1. 程式人生 > >用scikit-learn學習LDA主題模型

用scikit-learn學習LDA主題模型

大小 href 房子 鏈接 size 目標 文本 訓練樣本 papers

    在LDA模型原理篇我們總結了LDA主題模型的原理,這裏我們就從應用的角度來使用scikit-learn來學習LDA主題模型。除了scikit-learn, 還有spark MLlib和gensim庫也有LDA主題模型的類庫,使用的原理基本類似,本文關註於scikit-learn中LDA主題模型的使用。

1. scikit-learn LDA主題模型概述

    在scikit-learn中,LDA主題模型的類在sklearn.decomposition.LatentDirichletAllocation包中,其算法實現主要基於原理篇裏講的變分推斷EM算法,而沒有使用基於Gibbs采樣的MCMC算法實現。

    而具體到變分推斷EM算法,scikit-learn除了我們原理篇裏講到的標準的變分推斷EM算法外,還實現了另一種在線變分推斷EM算法,它在原理篇裏的變分推斷EM算法的基礎上,為了避免文檔內容太多太大而超過內存大小,而提供了分步訓練(partial_fit函數),即一次訓練一小批樣本文檔,逐步更新模型,最終得到所有文檔LDA模型的方法。這個改進算法我們沒有講,具體論文在這:“Online Learning for Latent Dirichlet Allocation” 。

    下面我們來看看sklearn.decomposition.LatentDirichletAllocation類庫的主要參數。

2. scikit-learn LDA主題模型主要參數和方法

    我們來看看LatentDirichletAllocation類的主要輸入參數:

    1) n_topics: 即我們的隱含主題數$K$,需要調參。$K$的大小取決於我們對主題劃分的需求,比如我們只需要類似區分是動物,植物,還是非生物這樣的粗粒度需求,那麽$K$值可以取的很小,個位數即可。如果我們的目標是類似區分不同的動物以及不同的植物,不同的非生物這樣的細粒度需求,則$K$值需要取的很大,比如上千上萬。此時要求我們的訓練文檔數量要非常的多。

    2) doc_topic_prior:即我們的文檔主題先驗Dirichlet分布$\theta_d$的參數$\alpha$。一般如果我們沒有主題分布的先驗知識,可以使用默認值$1/K$。

    3) topic_word_prior:即我們的主題詞先驗Dirichlet分布$\beta_k$的參數$\eta$。一般如果我們沒有主題分布的先驗知識,可以使用默認值$1/K$。

    4) learning_method: 即LDA的求解算法。有 ‘batch’ 和 ‘online’兩種選擇。 ‘batch’即我們在原理篇講的變分推斷EM算法,而"online"即在線變分推斷EM算法,在"batch"的基礎上引入了分步訓練,將訓練樣本分批,逐步一批批的用樣本更新主題詞分布的算法。默認是"online"。選擇了‘online’則我們可以在訓練時使用partial_fit函數分布訓練。不過在scikit-learn 0.20版本中默認算法會改回到"batch"。建議樣本量不大只是用來學習的話用"batch"比較好,這樣可以少很多參數要調。而樣本太多太大的話,"online"則是首先了。

    5)learning_decay:僅僅在算法使用"online"時有意義,取值最好在(0.5, 1.0],以保證"online"算法漸進的收斂。主要控制"online"算法的學習率,默認是0.7。一般不用修改這個參數。

    6)learning_offset:僅僅在算法使用"online"時有意義,取值要大於1。用來減小前面訓練樣本批次對最終模型的影響。

    7) max_iter :EM算法的最大叠代次數。

    8)total_samples:僅僅在算法使用"online"時有意義, 即分步訓練時每一批文檔樣本的數量。在使用partial_fit函數時需要。

    9)batch_size: 僅僅在算法使用"online"時有意義, 即每次EM算法叠代時使用的文檔樣本的數量。

    10)mean_change_tol :即E步更新變分參數的閾值,所有變分參數更新小於閾值則E步結束,轉入M步。一般不用修改默認值。

    11) max_doc_update_iter: 即E步更新變分參數的最大叠代次數,如果E步叠代次數達到閾值,則轉入M步。

    從上面可以看出,如果learning_method使用"batch"算法,則需要註意的參數較少,則如果使用"online",則需要註意"learning_decay", "learning_offset",“total_samples”和“batch_size”等參數。無論是"batch"還是"online", n_topics($K$), doc_topic_prior($\alpha$), topic_word_prior($\eta$)都要註意。如果沒有先驗知識,則主要關註與主題數$K$。可以說,主題數$K$是LDA主題模型最重要的超參數。

3. scikit-learn LDA中文主題模型實例

    下面我們給一個LDA中文主題模型的簡單實例,從分詞一直到LDA主題模型。

    我們的有下面三段文檔語料,分別放在了nlp_test0.txt, nlp_test2.txt和 nlp_test4.txt:

    沙瑞金贊嘆易學習的胸懷,是金山的百姓有福,可是這件事對李達康的觸動很大。易學習又回憶起他們三人分開的前一晚,大家一起喝酒話別,易學習被降職到道口縣當縣長,王大路下海經商,李達康連連賠禮道歉,覺得對不起大家,他最對不起的是王大路,就和易學習一起給王大路湊了5萬塊錢,王大路自己東挪西撮了5萬塊,開始下海經商。沒想到後來王大路竟然做得風生水起。沙瑞金覺得他們三人,在困難時期還能以沫相助,很不容易。

    沙瑞金向毛婭打聽他們家在京州的別墅,毛婭笑著說,王大路事業有成之後,要給歐陽菁和她公司的股權,她們沒有要,王大路就在京州帝豪園買了三套別墅,可是李達康和易學習都不要,這些房子都在王大路的名下,歐陽菁好像去住過,毛婭不想去,她覺得房子太大很浪費,自己家住得就很踏實。

    347年(永和三年)三月,桓溫兵至彭模(今四川彭山東南),留下參軍周楚、孫盛看守輜重,自己親率步兵直攻成都。同月,成漢將領李福襲擊彭模,結果被孫盛等人擊退;而桓溫三戰三勝,一直逼近成都。

    首先我們進行分詞,並把分詞結果分別存在nlp_test1.txt, nlp_test3.txt和 nlp_test5.txt:

# -*- coding: utf-8 -*-

import jieba
jieba.suggest_freq(沙瑞金, True)
jieba.suggest_freq(易學習, True)
jieba.suggest_freq(王大路, True)
jieba.suggest_freq(京州, True)
#第一個文檔分詞#
with open(./nlp_test0.txt) as f:
    document = f.read()
    
    document_decode = document.decode(GBK)
    document_cut = jieba.cut(document_decode)
    #print  ‘ ‘.join(jieba_cut)
    result =  .join(document_cut)
    result = result.encode(utf-8)
    with open(./nlp_test1.txt, w) as f2:
        f2.write(result)
f.close()
f2.close()  

#第二個文檔分詞#
with open(./nlp_test2.txt) as f:
    document2 = f.read()
    
    document2_decode = document2.decode(GBK)
    document2_cut = jieba.cut(document2_decode)
    #print  ‘ ‘.join(jieba_cut)
    result =  .join(document2_cut)
    result = result.encode(utf-8)
    with open(./nlp_test3.txt, w) as f2:
        f2.write(result)
f.close()
f2.close() 

#第三個文檔分詞#
jieba.suggest_freq(桓溫, True)
with open(./nlp_test4.txt) as f:
    document3 = f.read()
    
    document3_decode = document3.decode(GBK)
    document3_cut = jieba.cut(document3_decode)
    #print  ‘ ‘.join(jieba_cut)
    result =  .join(document3_cut)
    result = result.encode(utf-8)
    with open(./nlp_test5.txt, w) as f3:
        f3.write(result)
f.close()
f3.close()  

    現在我們讀入分好詞的數據到內存備用,並打印分詞結果觀察:

with open(./nlp_test1.txt) as f3:
    res1 = f3.read()
print res1
with open(./nlp_test3.txt) as f4:
    res2 = f4.read()
print res2
with open(./nlp_test5.txt) as f5:
    res3 = f5.read()
print res3

    打印出的分詞結果如下:

    沙瑞金 贊嘆 易學習 的 胸懷 , 是 金山 的 百姓 有福 , 可是 這件 事對 李達康 的 觸動 很大 。 易學習 又 回憶起 他們 三人 分開 的 前一晚 , 大家 一起 喝酒 話別 , 易學習 被 降職 到 道口 縣當 縣長 , 王大路 下海經商 , 李達康 連連 賠禮道歉 , 覺得 對不起 大家 , 他 最 對不起 的 是 王大路 , 就 和 易學習 一起 給 王大路 湊 了 5 萬塊 錢 , 王大路 自己 東挪西撮 了 5 萬塊 , 開始 下海經商 。 沒想到 後來 王大路 竟然 做 得 風生水 起 。 沙瑞金 覺得 他們 三人 , 在 困難 時期 還 能 以沫 相助 , 很 不 容易 。
    沙瑞金 向 毛婭 打聽 他們 家 在 京州 的 別墅 , 毛婭 笑 著 說 , 王大路 事業有成 之後 , 要 給 歐陽 菁 和 她 公司 的 股權 , 她們 沒有 要 , 王大路 就 在 京州 帝豪園 買 了 三套 別墅 , 可是 李達康 和 易學習 都 不要 , 這些 房子 都 在 王大路 的 名下 , 歐陽 菁 好像 去 住 過 , 毛婭 不想 去 , 她 覺得 房子 太大 很 浪費 , 自己 家住 得 就 很 踏實 。
    347 年 ( 永和 三年 ) 三月 , 桓溫 兵至 彭模 ( 今 四川 彭山 東南 ) , 留下 參軍 周楚 、 孫盛 看守 輜重 , 自己 親率 步兵 直攻 成都 。 同月 , 成漢 將領 李福 襲擊 彭模 , 結果 被 孫盛 等 人 擊退 ; 而 桓溫 三 戰三勝 , 一直 逼近 成都 。

    我們接著導入停用詞表,這裏的代碼和中文文本挖掘預處理流程總結中一樣,如果大家沒有1208個的中文停用詞表,可以到之前的這篇文章的鏈接裏去下載。

#從文件導入停用詞表
stpwrdpath = "stop_words.txt"
stpwrd_dic = open(stpwrdpath, rb)
stpwrd_content = stpwrd_dic.read()
#將停用詞表轉換為list  
stpwrdlst = stpwrd_content.splitlines()
stpwrd_dic.close()

    接著我們要把詞轉化為詞頻向量,註意由於LDA是基於詞頻統計的,因此一般不用TF-IDF來做文檔特征。代碼如下:

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
corpus = [res1,res2,res3]
cntVector = CountVectorizer(stop_words=stpwrdlst)
cntTf = cntVector.fit_transform(corpus)
print cntTf

    輸出即為所有文檔中各個詞的詞頻向量。有了這個詞頻向量,我們就可以來做LDA主題模型了,由於我們只有三個文檔,所以選擇主題數$K$=2。代碼如下:

lda = LatentDirichletAllocation(n_topics=2,
                                learning_offset=50.,
                                random_state=0)
docres = lda.fit_transform(cntTf)

    通過fit_transform函數,我們就可以得到文檔的主題模型分布在docres中。而主題詞 分布則在lda.components_中。我們將其打印出來:

print docres
print lda.components_

    文檔主題的分布如下:

[[ 0.00950072  0.99049928]
 [ 0.0168786   0.9831214 ]
 [ 0.98429257  0.01570743]]

    可見第一個和第二個文檔較大概率屬於主題2,則第三個文檔屬於主題1.

    主題和詞的分布如下:

[[ 1.32738199  1.24830645  0.90453117  0.7416939   0.78379936  0.89659305
   1.26874773  1.23261029  0.82094727  0.87788498  0.94980757  1.21509469
   0.64793292  0.89061203  1.00779152  0.70321998  1.04526968  1.30907884
   0.81932312  0.67798129  0.93434765  1.2937011   1.170592    0.70423093
   0.93400364  0.75617108  0.69258778  0.76780266  1.17923311  0.88663943
   1.2244191   0.88397724  0.74734167  1.20690264  0.73649036  1.1374004
   0.69576496  0.8041923   0.83229086  0.8625258   0.88495323  0.8207144
   1.66806345  0.85542475  0.71686887  0.84556777  1.25124491  0.76510471
   0.84978448  1.21600212  1.66496509  0.84963486  1.24645499  1.72519498
   1.23308705  0.97983681  0.77222879  0.8339811   0.85949947  0.73931864
   1.33412296  0.91591144  1.6722457   0.98800604  1.26042063  1.09455497
   1.24696097  0.81048961  0.79308036  0.95030603  0.83259407  1.19681066
   1.18562629  0.80911991  1.19239034  0.81864393  1.24837997  0.72322227
   1.23471832  0.89962384  0.7307045   1.39429334  1.22255041  0.98600185
   0.77407283  0.74372971  0.71807656  0.75693778  0.83817087  1.33723701
   0.79249005  0.82589143  0.72502086  1.14726838  0.83487136  0.79650741
   0.72292882  0.81856129]
 [ 0.72740212  0.73371879  1.64230568  1.5961744   1.70396534  1.04072318
   0.71245387  0.77316486  1.59584637  1.15108883  1.15939659  0.76124093
   1.34750239  1.21659215  1.10029347  1.20616038  1.56146506  0.80602695
   2.05479544  1.18041584  1.14597993  0.76459826  0.8218473   1.2367587
   1.44906497  1.19538763  1.35241035  1.21501862  0.7460776   1.61967022
   0.77892814  1.14830281  1.14293716  0.74425664  1.18887759  0.79427197
   1.15820484  1.26045121  1.69001421  1.17798335  1.12624327  1.12397988
   0.83866079  1.2040445   1.24788376  1.63296361  0.80850841  1.19119425
   1.1318814   0.80423837  0.74137153  1.21226307  0.67200183  0.78283995
   0.75366187  1.5062978   1.27081319  1.2373463   2.99243195  1.21178667
   0.66714016  2.17440219  0.73626368  1.60196863  0.71547934  1.94575151
   0.73691176  2.02892667  1.3528508   1.0655887   1.1460755   4.17528123
   0.74939365  1.23685079  0.76431961  1.17922085  0.70112531  1.14761871
   0.80877956  1.12307426  1.21107782  1.64947394  0.74983027  2.03800612
   1.21378076  1.21213961  1.23397206  1.16994431  1.07224768  0.75292945
   1.10391419  1.26932908  1.26207274  0.70943937  1.1236972   1.24175001
   1.27929042  1.19130408]]

    在實際的應用中,我們需要對$K,\alpha,\eta$進行調參。如果是"online"算法,則可能需要對"online"算法的一些參數做調整。這裏只是給出了LDA主題模型從原始文檔到實際LDA處理的過程。希望可以幫到大家。

(歡迎轉載,轉載請註明出處。歡迎溝通交流: [email protected]

用scikit-learn學習LDA主題模型