本文主要用于理解主題模型LDA(Latent Dirichlet Allocation)其背后的數學原理及其推導過程。本菇力求用簡單的推理來論證LDA背后復雜的數學知識,苦于自身數學基礎不夠,因此文中還是大量引用了各方大神的數學推導細節,既是為了方便自己以后回顧,也方便讀者追本溯源,當然喜歡直接看應用的讀者可直接翻到第二章~
基本目錄如下:
LDA的原理
1.1 先導數學知識準備
1.2 文本模型 - Unigram Model
1.3 主題模型 - PLSA Model
1.4 主題模型 - LDA ModelLDA的應用與擴展
2.1 LDA的Python實現
2.2 LDA模型中的主題個數
------------------第一菇 - LDA的原理------------------
1.1 先導數學知識準備
LDA之所以很難懂,跟其涉及大量的數學統計知識有關,因此,為了更好的理解LDA,還是先鋪墊一些數學知識。本段力求少擺公式,用更通俗的話來闡述其背后的數學思想~
1.1.1 Gamma函數
認真學過高數的應該都對這個函數有印象,其基本公式如下:
再貼一張Gamma函數圖,方便大家對其有一個概覽性的認識:
該函數有一個很好的遞歸性質(利用分步積分法可證)如下:
當然該函數還有更重要的一點,即其可以表述為階乘在實數集上的延拓:
(PS.至于為何不定義一個函數滿足,大家可以移步LDA數學八卦第一章,看完還蠻有趣的,順便感嘆一下偉大數學家們對完美數學公式的追求~)
1.1.2 二項分布
高中學概率論的時候,還記得拋硬幣的例子么?每一次拋硬幣都會有倆個結果,正面OR背面,那么拋一次硬幣就滿足伯努利分布(又叫0-1分布),是一個離散型的隨機分布。二項分布無非就是重復N次的伯努利實驗,其概率密度函數可以表示為:
其中
看到C(n,k)大部分人應該都能想起這個在高中就學過的式子的意思,最簡單的排列組合,從n個事件中挑出k個事件的組合方式,因此上面的式子應該不難理解了。
1.1.3 多項分布
高中學習概率論的時候,我們除了仍硬幣還仍什么?還會仍骰子哈哈~但這個時候仍一次骰子就不滿足伯努利分布了,因為仍一次骰子會有6或更多種結果。因此,多項分布就是二項分布擴展到高維的情況,其概率密度函數可以表示為:
想細究該公式怎么來的,可以先移步這篇博文多項式分布的理解概率公式的理解,本質思想跟二項分布幾乎沒差,只不過涉及到了一些多項式定理。
1.1.4 Beta分布
通俗理解,當我們不知道一個概率是什么,但又有一些合理的猜測時,Beta分布能很好的作為一個表示概率的概率分布。舉個例子,我們要統計預測一下窩火琦琦新賽季的三分投籃命中率,假如琦琦上賽季投了一個三分,命中一個,則按照我們熟悉的思路,我們可以預測琦琦新賽季命中率100%(想想美滋滋)!但仔細一想不對啊,就算是頂級的三分手(比如湯神)也就40%超一點,難道我大琦琦比湯神還牛了?顯然這里大家的思考就會出現分歧,于是就引申出了一個統計學知識點,或者說是倆個學派 - 頻率學派和貝葉斯學派!這倆個學派的思路差異體現在:
- 頻率學派:他們把需要推斷的參數
(本例就是琦琦的三分命中率)看做是固定的未知常數,即雖然這個概率是未知的,但卻是確定的一個值,同時,樣本
是隨機的,因此頻率學派重點研究樣本空間,大部分的概率計算都是針對樣本
的分布!
- 貝葉斯學派:他們與頻率學派認知剛好相反,他們認為參數都不是固定值,而是服從一個概率分布,樣本
卻是固定的,因此他們重點研究的是參數的分布。
因此,回到琦琦的例子上來,用傳統頻率學派的觀點,那琦琦的命中率確實就是100%,而用貝葉斯學派的觀點,首先琦琦的命中率有一個先驗分布,而根據樣本信息,我們可以對其進行更新,得到后驗分布。而數學家們為二項分布選取的先驗分布就是Beta分布!
Beta分布在概率論中指一組分布在區間的連續概率分布,其中參數為
,
,概率密度函數為:
其中,
至于代表的物理意義,可以簡單理解這倆一起控制著Beta分布的形狀,貼一張圖方便理解:
當然如果這個時候新賽季已經開打,那我們又會多得到一點信息,比如琦琦在揭幕戰怒投5三分,并且命中2個,錯失3個,那這個時候,揭幕戰中投出的5個三分,肯定能為我所用(樣本信息為二項分布),用于更新我對琦琦一開始命中率預測的分布。而這個時候就要用到Beta分布另一個重要的性質了,即Beta-Binomial共軛,對琦琦的預測分布可更新為
。
先講明,“共軛分布” 援引wiki的定義即為:在貝葉斯統計中,如果后驗分布與先驗分布屬于同類,則先驗分布與后驗分布被稱為共軛分布,而先驗分布被稱為似然函數的共軛先驗【1】。參照定義,Beta-Binomial 共軛意味著,如果我們為二項分布的參數p選取的先驗分布是Beta分布,那么以p為參數的二項分布用貝葉斯估計得到的后驗分布仍然服從Beta分布,那么Beta分布就是二項分布的共軛先驗分布,用數學公式表述就是:
這種形式不變的好處是,我們能夠在先驗分布中賦予參數很明確的物理意義,這個物理意義可以延續到后續分布中進行解釋,同時從先驗變換到后驗過程中從數據中補充的知識也容易有物理解釋【2】。另外再多說一點,關于Beta分布的期望可推導表示為:
不知大家能否一眼看出這倆個參數的物理意義,實際上就是琦琦三分投籃時候,命中與未命中的次數!(居然與頻率學派對P值的估計統一起來了~妙!)
1.1.5 Dirichlet分布
終于涉及到本文的主角,Dirichlet分布了~靈光的讀者研究到這應該大致能猜到該分布是干嘛的了。類比于多項分布是二項分布的推廣,Dirichlet分布也確實是Beta分布的推廣,其概率密度函數為:
其中
且類比于Beta分布,Dirichlet也是多項分布的共軛先驗分布:
相對應于Beta的期望,Dirichlet分布的期望也可以如下:(其物理意義也就是每個參數的估計值是其對應事件的先驗的參數(也叫偽計數)和數據中的計數的和在整體計數中的比例):
對以上倆個分布的推導過程感興趣的可以移步LDA數學八卦。
1.2 文本模型 - Unigram Model
假設一篇文檔由若干個詞語構成,可以不考慮順序,就你像看這句話的時候也沒現發他序順亂了~因此我們不妨就把一篇文章看作是一些詞的集合。那么最簡單的文本生成模型就是,確定文本的長度為N,然后選出N個詞來。選詞的過程與我們仍骰子一模一樣,無非就是這個骰子的面比較多,但其仍然服從多項分布。這個簡單的文本生成模型就叫做Unigram Model。當然少不了貝葉斯估計,每一個面朝上的概率也會有一個先驗分布為Dirichlet分布,表示為,而我們也可以根據樣本信息
來估計后驗分布
,其概率圖模型可以表示為:
該記圖方式為plate notation,有興趣的可以了解一下。此時該文本的生成概率就等于:
我們推導可以計算得到:
1.3 主題模型 - PLSA Model
上面講的文本生成模型足夠簡單,但是對文本的表達能力還是不夠,且不太符合我們日常的寫作習慣!試想,我們寫一篇文章的時候,肯定都會事先擬定一個主題,然后再從這個主題中去尋找相應的詞匯,因此這么來看,只扔一個有N面的骰子似乎還不夠,我們似乎應該提前再準備m個不同類型的骰子,作為我們的主題,然后確定了主題以后,再選中那個骰子,來決定詞匯。由此,便引申出了我們的主題模型!別急,還沒輪到LDA登場,我們先介紹一個簡單的PLSA模型!
簡單來說PLSA是用一個生成模型(生成模型與判別式模型的區別大家還是要懂的)來建模文章的生成過程!它有幾個前提假設【3】如下:
- 一篇文章可以由多個主題構成
- 每一個主題由一組詞的概率分布來表示
- 一篇文章中的每一個具體的詞都來自于一個固定的主題
其概率圖模型【4】可以表示為:
用自己的話翻譯一下上述過程就是:
- 按照概率
選中一篇文檔
- 從主題分布中按照概率
選中一個主題
- 從詞分布中按照概率
選中一個詞
則整個語料庫中的文本生成概率可以用似然函數表示為:
其中表示單詞
在文檔
中出現的次數。
其對數似然函數可以寫成:
其中主題分布和定義在主題上的詞分布
就是待估計的參數,一般會用EM算法(值得另開一篇來單獨講的機器學習基礎算法)來求解。有興趣考究推導細節的讀者請移步July老師的博客
1.3 主題模型 - LDA Model
堅持看到這里的讀者,其實內心應該對LDA也有了自己的猜想,看看PLSA漏了什么?沒錯,就是一個貝葉斯框架!如果我們給主題分布加一個Dirichlet分布,再給主題上的詞分布再加一個Dirichlet分布
,那就是LDA!因此實際上LDA就是PLSA的貝葉斯版本。我們直接來看模型圖【4】:
用自己的話翻譯一下上述過程就是:
- 按照概率
選中一篇文檔
- 從Dirichlet分布
中抽樣生成文檔
的主題分布
- 從主題分布
中抽取文檔
第j個詞的主題
- 從Dirichlet分布
中抽樣生成主題
對應的詞分布
- 從詞分布
中抽樣生成詞
為了方便求解,我們通常會將上述過程順序交換一下,即我們先生成完全部的主題,再由這些主題去生成完每一個詞。這樣,第一,二個過程的推導就可以用到Unigram Model的結論,即我們整個語料庫下所有詞的主題編號的生成概率為:
對于詞的生成過程(主題編號的選擇并不會改變K個主題下的詞分布),即可表示為:
因此,LDA模型的語料庫的生成概率可以表述為:
至此,整個LDA模型的文檔生成過程介紹完了,而實際我們運用求解的時候,我們的任務也就是去估計隱含變量主題分布和詞分布
的值了。實際求解的時候,一般會采用Gibbis Sampleing (又是可以單獨寫一篇的MCMC采樣的其中一種,詳情可見我的另一篇文章)。這里簡單介紹一下,就是首先隨機給定每個單詞的主題,然后在其他變量保持不變的情況下,根據轉移概率抽樣生成每個單詞的新主題,反復迭代以后,收斂后的結果就是主題分布和詞分布的期望。
寫到這里,基本上整個LDA模型算是從最簡單的二項分布到Dirichlet分布梳理明白了,對于不追求數學細節的讀者來說,至少可以在實際運用中不像無頭蒼蠅一樣當個“調包俠”,當然這些都還只是個入門基礎,研究人員對LDA做了非常多的擴展應用(這里推薦一篇文章是專門講當初三位大佬LDA論文的,看完也是收獲頗多,推薦一波),而我們只有打好了扎實的基礎,才能在實際應用中面對各種不同的變化得心應手!接下來,我就舉幾個實際的實戰案例,讓大家體驗一把其實際的應用。
------------------第二菇 - LDA的應用與擴展------------------
2.1 LDA的Python實現
在實際的運用中,LDA可以直接從gensim調,主要的一些參數有如下幾個:
- corpus:語料數據,需要包含單詞id與詞頻
- num_topics:我們需要生成的主題個數(重點調節)
- id2word:是一種id到單詞的映射(gensim也有包生成)
- passes:遍歷文本的次數,遍歷越多越準備
- alpha:主題分布的先驗
- eta:詞分布的先驗
接下來,我們實戰一把,直接用其官方的示例
from gensim.test.utils import common_texts
from gensim.corpora.dictionary import Dictionary
# Create a corpus from a list of texts
common_dictionary = Dictionary(common_texts)
common_corpus = [common_dictionary.doc2bow(text) for text in common_texts]
# Train the model on the corpus.
lda = LdaModel(common_corpus, num_topics=10)
一步步拆解來看,首先common_texts是list形式,里面的每一個元素都可以認為是一篇文檔也是list結構:
>>> print type(common_texts)
<type 'list'>
>>> common_texts[0]
['human', 'interface', 'computer']
第二步,doc2bow這個方法用于將文本轉化為詞袋形式,看一個官方的示例大家應該就能明白了,
>>> from gensim.corpora import Dictionary
>>> dct = Dictionary(["máma mele maso".split(), "ema má máma".split()])
>>> dct.doc2bow(["this", "is", "máma"])
[(2, 1)]
>>> dct.doc2bow(["this", "is", "máma"], return_missing=True)
([(2, 1)], {u'this': 1, u'is': 1})
初始化的時候對每一個詞都會生成一個id,新的文本進去的時候,返回該文本每一個詞的id,和對應的頻數,對于那些不存在原詞典的,可以控制是否返回。此時生成的corpus就相當于是LDA訓練模型的輸入了,讓我們檢查一下:
>>>common_corpus[0]
[(0, 1), (1, 1), (2, 1)]
# human單詞的id為0,且在第一個文檔中只出現了一次
最后一步,我們只需調用LDA模型即可,這里指定了10個主題。
from gensim.models import LdaModel
lda = LdaModel(common_corpus, num_topics=10)
讓我們檢查一下結果(還有很多種方法大家可以看文檔),比如我們想看第一個主題由哪些單詞構成:
>>>lda.print_topic(1, topn=2)
'0.500*"9" + 0.045*"10"
可以看出第一個模型的詞分布,9號10號占比較大(這里topn控制了輸出的單詞個數,對應的單詞可以通過之前生成dict找出)
我們還可以對剛才生成的lda模型用新語料去進行更新,
# 能更新全部參數
lda.update(other_corpus)
#還能單獨更新主題分布, 輸入為之前的參數,其中rho指學習率
lda.update_alpha(gammat, rho)
#還能單獨更新詞分布
lda.update_eta(lambdat, rho)
大家可以根據自己的實際業務需求,來具體查驗所需函數,這里就不一一展開了,官方文檔上也寫的比較細,總的來說,感謝大神們把輪子都造好了,我們只要會用即可,最好還能參透一點其背后的原理。
2.2 LDA模型中的主題個數
這里擴展開來談一點,我們如何確定LDA模型中的主題個數,因為這也是我們調參的重點,該參數選取的恰當,那我們模型的效果往往會變好。首先還是熟悉的套路,交叉驗證,28,37分看數據量,而我們的評估指標,我翻了一圈,大家都是用的困惑度(perplexity),其定義為:
其中為文檔的總數,
為文檔
中單詞所組成的詞袋向量,
為模型所預測的文檔
的生成概率,
為文檔
中單詞的總數。簡單理解一下就是,對于一篇文章,我們的模型有多不確定它是屬于哪一個主題的。很自然,主題越多,肯定困惑度越小,但是不要忘了,計算性能也扛不住啊,因此,一般也是會在合理的主題范圍內去挑選一個最佳的主題個數,比如畫topic_number-perplexity曲線(跟K-means)去找最佳的K一樣的理念吧。還有其他大佬,融入了分層狄利克雷過程(HDP),構成一種非參數主題模型,好處就是不需要預先指定個數,模型可以隨著文檔的變化而自動的對主題個數進行調整(復雜程度有點高,本菇還未深入涉足這一塊)。這里還要再提醒一點,也是看知乎上小伙伴上提醒的,千萬不要用gensim中的log_perplexity()計算的perplexity指標來比較topic數量的好壞!因為這個函數沒有對主題數目做歸一化,因此不同的topic數目不能直接比較!【5】(然后發現一票人最后說,都是自己拍的一個主題個數哈哈哈)
至此運用這一塊也算是簡單的,大家還是需要根據自己的業務再做調整~畢竟也是一門實驗學科,大家多做實驗,多試驗試驗自然經驗就有了。
簡單總結一下本文,先是介紹了一些LDA背后的統計分布,重點有Beta分布,及Beta-Binomial 共軛,然后引出了Dirichlet分布,最后引出了LDA模型,雖然省略了一些數學推導細節,但基本不影響讀者理解。最后還附上了一些Python的實戰應用,并重點討論了一下如何選取合適的主題數目。希望大家讀完本文后對機器學習主題模型這一塊有全新的認識。有說的不對的地方也請大家指出,多多交流,大家一起進步~??
參考文獻:
【1】https://zh.wikipedia.org/wiki/%E5%85%B1%E8%BD%AD%E5%85%88%E9%AA%8C
【2】https://zhuanlan.zhihu.com/p/31470216
【3】http://aroslin.com/2017/11/07/LDA-From-Zero/
【4】https://clyyuanzi.gitbooks.io/julymlnotes/content/lda.html
【5】https://www.zhihu.com/question/32286630