機器學習-python文本主題提取

你在工作、學習中是否曾因信息過載叫苦不迭?有一種方法能夠替你讀海量文章,並將不同的主題和對應的關鍵詞抽取出來,讓你談笑間觀其大略。本文使用Python對超過1000條文本做主題抽取,一步步帶你體會非監督機器學習LDA方法的魅力。想不想試試呢?


機器學習-python文本主題提取


淹沒

每個現代人,幾乎都體會過信息過載的痛苦。文章讀不過來,音樂聽不過來,視頻看不過來。可是現實的壓力,使你又不能輕易放棄掉。

假如你是個研究生,教科書和論文就是你不得不讀的內容。現在有了各種其他的閱讀渠道,微信、微博、得到App、多看閱讀、豆瓣閱讀、Kindle,還有你在RSS上訂閱的一大堆博客……情況就變得更嚴重了。

因為對數據科學很感興趣,你訂閱了大量的數據科學類微信公眾號。雖然你很勤奮,但你知道自己依然遺漏了很多文章。

學習了 Python爬蟲課 以後,你決定嘗試一下自己的屠龍之術。依仗著爬蟲的威力,你打算採集到所有數據科學公眾號文章。

你仔細分析了微信公眾號文章的檢索方式,制定了關鍵詞列表。巧妙利用搜狗搜索引擎的特性,你編寫了自己的爬蟲,並且成功地於午夜放到了雲端運行。

開心啊,激動啊……

第二天一早,天光剛亮,睡眠不足的你就興沖沖地爬起來去看爬取結果。居然已經有了1000多條!你欣喜若狂,導出成為csv格式,存儲到了本地機器,並且打開瀏覽。


機器學習-python文本主題提取


興奮了10幾分鐘之後,你冷卻了下來,給自己提出了2個重要的問題。

  • 這些文章都值得讀嗎?
  • 這些文章我讀得過來嗎?

一篇數據科學類公眾號,你平均需要5分鐘閱讀。這1000多篇……你拿出計算器認真算了一下。


機器學習-python文本主題提取


讀完這一宿採集到的文章,你不眠不休的話,也需要85個小時。

在你閱讀的這85個小時裡面,許許多多的數據科學類公眾號新文章還會源源不斷湧現出來。

你感覺自己快被文本內容淹沒了,根本透不過氣……

學了這麼長時間Python,你應該想到——我能否用自動化工具來分析它?

好消息,答案是可以的。

但是用什麼樣的工具呢?

翻了翻你自己的武器庫,你發現了詞雲、情感分析和決策樹。

然而,在幫你應對信息過載這件事兒上,上述武器好像都不大合適。

詞雲你打算做幾個?全部文章只做一個的話,就會把所有文章的內容混雜起來,沒有意義——因為你知道這些文章談的就是數據科學啊!如果每一篇文章都分別做詞雲,1000多張圖瀏覽起來,好像也沒有什麼益處。

你閱讀數據科學類公眾號文章是為了獲得知識和技能,分析文字中蘊含的情感似乎於事無補。

決策樹是可以用來做分類的,沒錯。可是它要求的輸入信息是結構化標記數據,你手裡握著的這一大堆文本,卻剛好是非結構化標記數據。

全部武器都啞火了。

沒關係。本文幫助你在數據科學武器庫中放上一件新式兵器。它能夠處理的,就是大批量的非結構無標記數據。在機器學習的分類裡,它屬於非監督學習(unsupervised machine learning)範疇。具體而言,我們需要用到的方法叫主題建模(topic model)或者主題抽取(topic extraction)。

主題

既然要建模,我們就需要弄明白建立什麼樣的模型。

根據維基百科的定義,主題模型是指:

在機器學習和自然語言處理等領域是用來在一系列文檔中發現抽象主題的一種統計模型。

這個定義本身好像就有點兒抽象,咱們舉個例子吧。

還是維基百科上,對一條可愛的小狗有這樣一段敘述。

阿博(Bo;2008年10月9日-) 是美國第44任總統巴拉克·奧巴馬的寵物狗,也是奧巴馬家族的成員之一。阿博是一隻已閹割的雄性黑色長毛葡萄牙水犬。奧巴馬一家本來沒有養狗,因為他的大女兒瑪麗亞對狗過敏。但為了延續白宮主人歷年均有養狗的傳統,第一家庭在入主白宮後,花了多個月去觀察各種犬種,並特地選擇了葡萄牙水犬這一種掉毛少的低敏狗。

我們來看看這條可愛的小狗照片:


機器學習-python文本主題提取


問題來了,這篇文章的主題(topic)是什麼?

你可能脫口而出,“狗啊!”

且慢,換個問法。假設一個用戶讀了這篇文章,很感興趣。你想推薦更多他可能感興趣的文章給他,以下2段文字,哪個選項更合適呢?

選項1:

阿富汗獵狗(Afghan Hound)是一種獵犬,也是最古老的狗品種。阿富汗獵狗外表厚實,細膩,柔滑,它的尾巴在最後一環捲曲。阿富汗獵狗生存於伊朗,阿富汗東部的寒冷山上,阿富汗獵狗最初是用來狩獵野兔和瞪羚。阿富汗獵狗其他名稱包含巴爾赫塔子庫奇獵犬,獵犬,俾路支獵犬,喀布爾獵犬,或非洲獵犬。

選項2:

1989年夏天,奧巴馬在西德利·奧斯汀律師事務所擔任暑期工讀生期間,結識當時已是律師的米歇爾·魯濱遜。兩人於1992年結婚,現有兩個女兒——大女兒瑪麗亞在1999年於芝加哥芝加哥大學醫療中心出生,而小女兒薩沙在2001年於芝加哥大學醫療中心出生。

給你30秒,思考一下。

你的答案是什麼?

我的答案是——不確定。

人類天生喜歡把複雜問題簡單化。我們恨不得把所有東西劃分成具體的、互不干擾的分類,就如同藥鋪的一個個抽屜一樣。然後需要的時候,從對應的抽屜裡面取東西就可以了。


機器學習-python文本主題提取


這就像是職業。從前我們說“三百六十行”。隨便拿出某個人來,我們就把他歸入其中某一行。

現在不行了,反例就是所謂的“斜槓青年”。

主題這個事情,也同樣不那麼涇渭分明。介紹小狗Bo的文章雖然不長,但是任何單一主題都無法完全涵蓋它。

如果用戶是因為對小狗的喜愛,閱讀了這篇文章,那麼顯然你給他推薦選項1會更理想;但是如果用戶關注的是奧巴馬的家庭,那麼比起選項2來,選項1就顯得不是那麼合適了。

我們必須放棄用一個詞來描述主題的嘗試,轉而用一系列關鍵詞來刻畫某個主題(例如“奧巴馬”+“寵物“+”狗“+”第一家庭“)。

在這種模式下,以下的選項3可能會脫穎而出:

據英國《每日郵報》報道,美國一名男子近日試圖綁架總統奧巴馬伕婦的寵物狗博(Bo),不惜由二千多公里遠的北達科他州驅車往華盛頓,但因為走漏風聲,被特勤局人員逮捕。奧巴馬伕婦目前養有博和陽光(Sunny)兩隻葡萄牙水犬。

講到這裡,你大概弄明白了主題抽取的目標了。可是面對浩如煙海的文章,我們怎麼能夠把相似的文章聚合起來,並且提取描述聚合後主題的重要關鍵詞呢?

主題抽取有若干方法。目前最為流行的叫做隱含狄利克雷分佈(Latent Dirichlet allocation),簡稱LDA。

LDA相關原理部分,置於本文最後。下面我們先用Python來嘗試實踐一次主題抽取。如果你對原理感興趣,不妨再做延伸閱讀。

準備

準備工作的第一步,還是先安裝Anaconda套裝。詳細的流程步驟請參考《 如何用Python做詞雲 》一文。

從微信公眾平臺爬來的datascience.csv文件,請從 這裡 下載。你可以用Excel打開,看看下載是否完整和正確。


機器學習-python文本主題提取


如果一切正常,請將該csv文件移動到咱們的工作目錄demo下。

到你的系統“終端”(macOS, Linux)或者“命令提示符”(Windows)下,進入我們的工作目錄demo,執行以下命令。

<code>

pip

install jieba

pip

install pyldavis

/<code>

運行環境配置完畢。

在終端或者命令提示符下鍵入:

<code>

jupyter

notebook /<code>


機器學習-python文本主題提取


Jupyter Notebook已經正確運行。下面我們就可以正式編寫代碼了。

代碼

我們在Jupyter Notebook中新建一個Python 2筆記本,起名為topic-model。


機器學習-python文本主題提取


為了處理表格數據,我們依然使用數據框工具Pandas。先調用它。

<code>

import

pandas

as

pd /<code>

然後讀入我們的數據文件datascience.csv,注意它的編碼是中文GB18030,不是Pandas默認設置的編碼,所以此處需要顯式指定編碼類型,以免出現亂碼錯誤。

<code>

df

= pd.read_csv(

"datascience.csv"

, encoding=

'gb18030'

) /<code>

我們來看看數據框的頭幾行,以確認讀取是否正確。

<code>

df

.head

() /<code>

顯示結果如下:


機器學習-python文本主題提取


沒問題,頭幾行內容所有列都正確讀入,文字顯式正常。我們看看數據框的長度,以確認數據是否讀取完整。

<code>

df

.shape

/<code>

執行的結果為:

<code>

(1024,

3

)

/<code>

行列數都與我們爬取到的數量一致,通過。

下面我們需要做一件重要工作——分詞。這是因為我們需要提取每篇文章的關鍵詞。而中文本身並不使用空格在單詞間劃分。此處我們採用“結巴分詞”工具。這一工具的具體介紹和其他用途請參見《如何用Python做中文分詞?》一文。

我們首先調用jieba分詞包。

<code>

import

jieba /<code>

我們此次需要處理的,不是單一文本數據,而是1000多條文本數據,因此我們需要把這項工作並行化。這就需要首先編寫一個函數,處理單一文本的分詞。

<code>

def

chinese_word_cut

(mytext)

:

return

" "

.join(jieba.cut(mytext)) /<code>

有了這個函數之後,我們就可以不斷調用它來批量處理數據框裡面的全部文本(正文)信息了。你當然可以自己寫個循環來做這項工作。但這裡我們使用更為高效的apply函數。如果你對這個函數有興趣,可以點擊這段教學視頻查看具體的介紹。

下面這一段代碼執行起來,可能需要一小段時間。請耐心等候。

<code>

df

[

"content_cutted"

] = df.content.apply(chinese_word_cut) /<code>

執行過程中可能會出現如下提示。沒關係,忽略就好。

<code>Building prefix dict 

from

the

default

dictionary ... Loading model

from

cache /

var

/folders/

8

s/k8yr4zy52q1dh107gjx280mw0000gn/T/jieba.cache Loading model cost

0.406

seconds. Prefix dict has been built succesfully. /<code>

執行完畢之後,我們需要查看一下,文本是否已經被正確分詞。

<code>

df

.content_cutted

.head

() /<code>

結果如下:

<code>

0

數據

產業

發展

受到

國家

重視

數據

已經

上升

國家

戰略

未...

1

點擊

上方

硅谷

周邊

關注

收到

最新

文章

昨天

Goo...

2

國務院

總理

李克強

當地

時間

20

上午

紐約

下榻

飯店

美國

經濟

...

3

2016

全峰

集團

持續

挖掘

數據

計算

互聯網

+

等...

4

貴州

理工學院

召開

數據分析

應用

專題

分享

創響

中國

貴...

Name:

content_cutted,

dtype:

object

/<code>

單詞之間都已經被空格區分開了。下面我們需要做一項重要工作,叫做文本的向量化。

不要被這個名稱嚇跑。它的意思其實很簡單。因為計算機不但不認識中文,甚至連英文也不認識,它只認得數字。我們需要做的,是把文章中的關鍵詞轉換為一個個特徵(列),然後對每一篇文章數關鍵詞出現個數。

假如這裡有兩句話:

I love the game.I hate the game.

那麼我們就可以抽取出以下特徵:

  • I
  • love
  • hate
  • the
  • game

然後上面兩句話就轉換為以下表格:


機器學習-python文本主題提取


第一句表示為[1, 1, 0, 1, 1],第二句是[1, 0, 1, 1, 1]。這就叫向量化了。機器就能看懂它們了。

原理弄清楚了,讓我們引入相關軟件包吧。

<code>

from

sklearn.feature_extraction.text

import

TfidfVectorizer, CountVectorizer /<code>

處理的文本都是微信公眾號文章,裡面可能會有大量的詞彙。我們不希望處理所有詞彙。因為一來處理時間太長,二來那些很不常用的詞彙對我們的主題抽取意義不大。所以這裡做了個限定,只從文本中提取1000個最重要的特徵關鍵詞,然後停止。

<code>

n_features

=

1000

/<code>

下面我們開始關鍵詞提取和向量轉換過程:

<code>

tf_vectorizer

=

CountVectorizer(strip_accents = 'unicode',

max_features

=

n_features,

stop_words

=

'english',

max_df

=

0.5,

min_df

=

10)

tf

=

tf_vectorizer.fit_transform(df.content_cutted)

/<code>

到這裡,似乎什麼都沒有發生。因為我們沒有要求程序做任何輸出。下面我們就要放出LDA這個大招了。

先引入軟件包:

<code>

from

sklearn.decomposition

import

LatentDirichletAllocation /<code>

然後我們需要人為設定主題的數量。這個要求讓很多人大跌眼鏡——我怎麼知道這一堆文章裡面多少主題?!

彆著急。應用LDA方法,指定(或者叫瞎猜)主題個數是必須的。如果你只需要把文章粗略劃分成幾個大類,就可以把數字設定小一些;相反,如果你希望能夠識別出非常細分的主題,就增大主題個數。

對劃分的結果,如果你覺得不夠滿意,可以通過繼續迭代,調整主題數量來優化。

這裡我們先設定為5個分類試試。

<code>

n_topics

=

5

lda

=

LatentDirichletAllocation(n_topics=n_topics, max_iter=50,

learning_method

=

'online',

learning_offset

=

50.,

random_state

=

0)

/<code>

把我們的1000多篇向量化後的文章扔給LDA,讓它歡快地找主題吧。

這一部分工作量較大,程序會執行一段時間,Jupyter Notebook在執行中可能暫時沒有響應。等待一會兒就好,不要著急。

<code>

lda

.fit

(

tf

) /<code>

程序終於跑完了的時候,你會看到如下的提示信息:

<code> =

128, doc_topic_prior=None,

evaluate_every

=

-1, learning_decay=0.7,

learning_method

=

'online', learning_offset=50.0,

max_doc_update_iter

=

100, max_iter=50, mean_change_tol=0.001,

n_jobs

=

1, n_topics=5, perp_tol=0.1, random_state=0,

topic_word_prior

=

None, total_samples=1000000.0, verbose=0)

/<code>

可是,這還是什麼輸出都沒有啊。它究竟找了什麼樣的主題?

主題沒有一個確定的名稱,而是用一系列關鍵詞刻畫的。我們定義以下的函數,把每個主題裡面的前若干個關鍵詞顯示出來:

<code>

def

print_top_words

(model, feature_names, n_top_words)

:

for

topic_idx, topic

in

enumerate(model.components_): print(

"Topic #%d:"

% topic_idx) print(

" "

.join([feature_names[i]

for

i

in

topic.argsort()[:-n_top_words -

1

:

-1

]])) print() /<code>

定義好函數之後,我們暫定每個主題輸出前20個關鍵詞。

<code>

n_top_words

=

20

/<code>

以下命令會幫助我們依次輸出每個主題的關鍵詞表:

<code>tf_feature_names = tf_vectorizer.get_feature_names()
print_top_words(lda, tf_feature_names, n_top_words)
/<code>

執行效果如下:

<code>

Topic

學習

模型

使用

算法

方法

機器

可視化

神經網絡

特徵

處理

計算

系統

不同

數據庫

訓練

分類

基於

工具

一種

深度

Topic

這個

就是

可能

如果

他們

沒有

自己

很多

什麼

不是

但是

這樣

因為

一些

時候

現在

用戶

所以

非常

已經

Topic

企業

平臺

服務

管理

互聯網

公司

行業

數據分析

業務

用戶

產品

金融

創新

客戶

實現

系統

能力

產業

工作

價值

Topic

中國

2016

電子

增長

10

市場

城市

2015

關注

人口

檢索

30

或者

其中

閱讀

應當

美國

全國

同比

20

Topic

人工智能

學習

領域

智能

機器人

機器

人類

公司

深度

研究

未來

識別

已經

醫療

系統

計算機

目前

語音

百度

方面

()

/<code>

在這5個主題裡,可以看出主題0主要關注的是數據科學中的算法和技術,而主題4顯然更注重數據科學的應用場景。

剩下的幾個主題可以如何歸納?作為思考題,留給你花時間想一想吧。

到這裡,LDA已經成功幫我們完成了主題抽取。但是我知道你不是很滿意,因為結果不夠直觀。

那咱們就讓它直觀一些好了。

執行以下命令,會有有趣的事情發生。

<code>

import

pyLDAvis

import

pyLDAvis

.sklearn

pyLDAvis

.enable_notebook

()

pyLDAvis

.sklearn

.prepare

(

lda

,

tf

,

tf_vectorizer

) /<code>

對,你會看到如下的一張圖,而且還是可交互的動態圖哦。


機器學習-python文本主題提取


需要說明的是,由於pyLDAvis這個包兼容性有些問題。因此在某些操作系統和軟件環境下,你執行了剛剛的語句後,沒有報錯,卻也沒有圖形顯示出來。

沒關係。這時候請你寫下以下語句並執行:

<code>

data

= pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer) pyLDAvis.show(

data

) /<code>

Jupyter會給你提示一些警告。不用管它。因為此時你的瀏覽器會彈出一個新的標籤頁,結果圖形會在這個標籤頁里正確顯示出來。

如果你看完了圖後,需要繼續程序,就回到原先的標籤頁,點擊Kernel菜單下的第一項Interrupt停止繪圖,然後往下運行新的語句。

圖的左側,用圓圈代表不同的主題,圓圈的大小代表了每個主題分別包含文章的數量。

圖的右側,列出了最重要(頻率最高)的30個關鍵詞列表。注意當你沒有把鼠標懸停在任何主題之上的時候,這30個關鍵詞代表全部文本中提取到的30個最重要關鍵詞。

如果你把鼠標懸停在1號上面:


機器學習-python文本主題提取


右側的關鍵詞列表會立即發生變化,紅色展示了每個關鍵詞在當前主題下的頻率。

以上是認為設定主題數為5的情況。可如果我們把主題數量設定為10呢?

你不需要重新運行所有代碼,只需要執行下面這幾行就可以了。

這段程序還是需要運行一段時間,請耐心等待。

<code>

n_topics

=

10

lda

=

LatentDirichletAllocation(n_topics=n_topics, max_iter=50,

learning_method

=

'online',

learning_offset

=

50.,

random_state

=

0)

lda.fit(tf)

tf_feature_names, n_top_words)

tf, tf_vectorizer)

/<code>

程序輸出給我們10個主題下最重要的20個關鍵詞。

<code>

Topic

這個

就是

如果

可能

用戶

一些

什麼

很多

沒有

這樣

時候

但是

因為

不是

所以

不同

如何

使用

或者

非常

Topic

中國

孩子

增長

市場

2016

學生

10

2015

城市

自己

人口

大眾

關注

其中

教育

同比

沒有

美國

投資

這個

Topic

data

變量

距離

http

樣本

com

www

檢驗

方法

分佈

計算

聚類

如下

分類

之間

兩個

一種

差異

表示

序列

Topic

電子

採集

應當

或者

案件

保護

規定

信用卡

收集

是否

提取

設備

法律

申請

法院

系統

記錄

相關

要求

無法

Topic

系統

檢索

交通

平臺

專利

智能

監控

採集

海量

管理

搜索

智慧

出行

視頻

車輛

計算

實現

基於

數據庫

存儲

Topic

可視化

使用

工具

數據庫

存儲

hadoop

處理

圖表

數據倉庫

支持

查詢

開發

設計

sql

開源

用於

創建

用戶

基於

軟件

Topic

學習

算法

模型

機器

深度

神經網絡

方法

訓練

特徵

分類

網絡

使用

基於

介紹

研究

預測

迴歸

函數

參數

圖片

Topic

企業

管理

服務

互聯網

金融

客戶

行業

平臺

實現

建立

社會

政府

研究

資源

安全

時代

利用

傳統

價值

醫療

Topic

人工智能

領域

機器人

智能

公司

人類

機器

學習

未來

已經

研究

他們

識別

可能

計算機

目前

語音

工作

現在

能夠

Topic

用戶

公司

企業

互聯網

平臺

中國

數據分析

行業

產業

產品

創新

項目

2016

服務

工作

科技

相關

業務

移動

市場

()

/<code>

附帶的是可視化的輸出結果:


機器學習-python文本主題提取


如果不能直接輸出圖形,還是按照前面的做法,執行:

<code>

data

= pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer) pyLDAvis.show(

data

) /<code>

你馬上會發現當主題設定為10的時候,一些有趣的現象發生了——大部分的文章抱團出現在右上方,而2個小部落(8和10)似乎離群索居。我們查看一下這裡的8號主題,看看它的關鍵詞構成。


通過高頻關鍵詞的描述,我們可以猜測到這一主題主要探討的是政策和法律法規問題,難怪它和那些技術、算法與應用的主題顯得如此格格不入。

說明

前文幫助你一步步利用LDA做了主題抽取。成就感爆棚吧?然而這裡有兩點小問題值得說明。

首先,信息檢索的業內專家一看到剛才的關鍵詞列表,就會哈哈大笑——太粗糙了吧!居然沒有做中文停用詞(stop words)去除!沒錯,為了演示的流暢,我們這裡忽略了許多細節。很多內容使用的是預置默認參數,而且完全忽略了中文停用詞設置環節,因此“這個”、“如果”、“可能”、“就是”這樣的停用詞才會大搖大擺地出現在結果中。不過沒有關係,完成比完美重要得多。知道了問題所在,後面改進起來很容易。有機會我會寫文章介紹如何加入中文停用詞的去除環節。

另外,不論是5個還是10個主題,可能都不是最優的數量選擇。你可以根據程序反饋的結果不斷嘗試。實際上,可以調節的參數遠不止這一個。如果你想把全部參數都搞懂,可以繼續閱讀下面的“原理”部分,按圖索驥尋找相關的說明和指引。

原理

前文我們沒有介紹原理,而是把LDA當成了一個黑箱。不是我不想介紹原理,而是過於複雜。

只給你展示其中的一個公式,你就能管窺其複雜程度了。


機器學習-python文本主題提取


透露給你一個秘密:在計算機科學和數據科學的學術講座中,講者在介紹到LDA時,都往往會把原理這部分直接跳過去。

好在你不需要把原理完全搞清楚,再去用LDA抽取主題。

這就像是學開車,你只要懂得如何加速、剎車、換擋、打方向,就能讓車在路上行駛了。即便你通過所有考試並取得了駕駛證,你真的瞭解發動機或電機(如果你開的是純電車)的構造和工作原理嗎?

但是如果你就是希望瞭解LDA的原理,那麼我給你推薦2個學起來不那麼痛苦的資源吧。

首先是教程幻燈。slideshare是個尋找教程的好去處。 這份教程 瀏覽量超過20000,內容深入淺出,講得非常清晰。


機器學習-python文本主題提取


但如果你跟我一樣,是個視覺學習者的話,我更推薦你看 這段 Youtube視頻。


機器學習-python文本主題提取


講者是Christine Doig,來自Continuum Analytics。咱們一直用的Python套裝Anaconda就是該公司的產品。

Christine使用的LDA原理解釋模型,不是這個LDA經典論文中的模型圖(大部分人覺得這張圖不易懂):


機器學習-python文本主題提取


她深入閱讀了各種文獻後,總結了自己的模型圖出來:


機器學習-python文本主題提取


用這個模型來解釋LDA,你會立即有豁然開朗的感覺。


鏈接:https://zhuanlan.zhihu.com/p/28992175


分享到:


相關文章: