hmm 求隐藏序列_结巴分词3--基于汉字成词能力的HMM模型识别未登录词
1 算法簡介
在結巴分詞2--基于前綴詞典及動態規劃實現分詞 博文中,博主已經介紹了基于前綴詞典和動態規劃方法實現分詞,但是如果沒有前綴詞典或者有些詞不在前綴詞典中,jieba分詞一樣可以分詞,那么jieba分詞是如何對未登錄詞進行分詞呢?這就是本文將要講解的,基于漢字成詞能力的HMM模型識別未登錄詞。
利用HMM模型進行分詞,主要是將分詞問題視為一個序列標注(sequence labeling)問題,其中,句子為觀測序列,分詞結果為狀態序列。首先通過語料訓練出HMM相關的模型,然后利用Viterbi算法進行求解,最終得到最優的狀態序列,然后再根據狀態序列,輸出分詞結果。
2 實例
2.1 序列標注
序列標注,就是將輸入句子和分詞結果當作兩個序列,句子為觀測序列,分詞結果為狀態序列,當完成狀態序列的標注,也就得到了分詞結果。
以“去北京大學玩”為例,我們知道“去北京大學玩”的分詞結果是“去 / 北京大學 / 玩”。對于分詞狀態,由于jieba分詞中使用的是4-tag,因此我們以4-tag進行計算。4-tag,也就是每個字處在詞語中的4種可能狀態,B、M、E、S,分別表示Begin(這個字處于詞的開始位置)、Middle(這個字處于詞的中間位置)、End(這個字處于詞的結束位置)、Single(這個字是單字成詞)。具體如下圖所示,“去”和“玩”都是單字成詞,因此狀態就是S,“北京大學”是多字組合成的詞,因此“北”、“京”、“大”、“學”分別位于“北京大學”中的B、M、M、E。
2.2 HMM模型
關于HMM模型的介紹,網絡上有很多的資源,比如 52nlp整理的 HMM相關文章索引 。博主在此就不再具體介紹HMM模型的原理,但是會對分詞涉及的基礎知識進行講解。
HMM模型作的兩個基本假設:
- 1.齊次馬爾科夫性假設,即假設隱藏的馬爾科夫鏈在任意時刻t的狀態只依賴于其前一時刻的狀態,與其它時刻的狀態及觀測無關,也與時刻t無關;
P(states[t] | states[t-1],observed[t-1],...,states[1],observed[1]) = P(states[t] | states[t-1]) t = 1,2,...,T- 2.觀測獨立性假設,即假設任意時刻的觀測只依賴于該時刻的馬爾科夫鏈的狀態,與其它觀測和狀態無關,
P(observed[t] | states[T],observed[T],...,states[1],observed[1]) = P(observed[t] | states[t]) t = 1,2,...,THMM模型有三個基本問題:
- 1.概率計算問題,給定模型
和觀測序列 ,怎樣計算在模型 下觀測序列O出現的概率 ,也就是Forward-backward算法;- 2.學習問題,已知觀測序列
,估計模型 ,使得在該模型下觀測序列的概率 盡可能的大,即用極大似然估計的方法估計參數;- 3.預測問題,也稱為解碼問題,已知模型
和觀測序列 ,求對給定觀測序列條件概率 最大的狀態序列 ,即給定觀測序列。求最有可能的對應的狀態序列;其中,jieba分詞主要中主要涉及第三個問題,也即預測問題。
*HMM模型中的五元組表示:*
- 1.觀測序列;
- 2.隱藏狀態序列;
- 3.狀態初始概率;
- 4.狀態轉移概率;
- 5.狀態發射概率;
這里仍然以“去北京大學玩”為例,那么“去北京大學玩”就是觀測序列。
而“去北京大學玩”對應的“SBMMES”則是隱藏狀態序列,我們將會注意到B后面只能接(M或者E),不可能接(B或者S);而M后面也只能接(M或者E),不可能接(B或者S)。
狀態初始概率表示,每個詞初始狀態的概率;jieba分詞訓練出的狀態初始概率模型如下所示。其中的概率值都是取對數之后的結果(可以讓概率相乘轉變為概率相加),其中-3.14e+100代表負無窮,對應的概率值就是0。這個概率表說明一個詞中的第一個字屬于{B、M、E、S}這四種狀態的概率,如下可以看出,E和M的概率都是0,這也和實際相符合:開頭的第一個字只可能是每個詞的首字(B),或者單字成詞(S)。這部分對應jieba/finaseg/prob_start.py,具體可以進入源碼查看。
P={'B': -0.26268660809250016,'E': -3.14e+100,'M': -3.14e+100,'S': -1.4652633398537678}狀態轉移概率是馬爾科夫鏈中很重要的一個知識點,一階的馬爾科夫鏈最大的特點就是當前時刻T = i的狀態states(i),只和T = i時刻之前的n個狀態有關,即{states(i-1),states(i-2),...,states(i-n)}。
再看jieba中的狀態轉移概率,其實就是一個嵌套的詞典,數值是概率值求對數后的值,如下所示,
P={'B': {'E': -0.510825623765990, 'M': -0.916290731874155},'E': {'B': -0.5897149736854513, 'S': -0.8085250474669937},'M': {'E': -0.33344856811948514, 'M': -1.2603623820268226},'S': {'B': -0.7211965654669841, 'S': -0.6658631448798212}}P['B']['E']代表的含義就是從狀態B轉移到狀態E的概率,由P['B']['E'] = -0.5897149736854513,表示當前狀態是B,下一個狀態是E的概率對數是-0.5897149736854513,對應的概率值是0.6,相應的,當前狀態是B,下一個狀態是M的概率是0.4,說明當我們處于一個詞的開頭時,下一個字是結尾的概率要遠高于下一個字是中間字的概率,符合我們的直覺,因為二個字的詞比多個字的詞更常見。這部分對應jieba/finaseg/prob_trans.py,具體可以查看源碼。
狀態發射概率,根據HMM模型中觀測獨立性假設,發射概率,即觀測值只取決于當前狀態值,也就如下所示,
P(observed[i],states[j]) = P(states[j]) * P(observed[i] | states[j])其中,P(observed[i] | states[j])就是從狀態發射概率中獲得的。
P={'B': {'一': -3.6544978750449433,'丁': -8.125041941842026,'七': -7.817392401429855,...'S': {':': -15.828865681131282,'一': -4.92368982120877,'丁': -9.024528361347633,...P['B']['一']代表的含義就是狀態處于'B',而觀測的字是‘一’的概率對數值為P['B']['一'] = -3.6544978750449433。這部分對應jieba/finaseg/prob_emit.py,具體可以查看源碼。
2.3 Viterbi算法
Viterbi算法實際上是用動態規劃求解HMM模型預測問題,即用動態規劃求概率路徑最大(最優路徑)。這時候,一條路徑對應著一個狀態序列。
根據動態規劃原理,最優路徑具有這樣的特性:如果最優路徑在時刻t通過結點 $i_{t}^{*}$ ,那么這一路徑從結點 $i_{t}^{*}$ 到終點 $i_{T}^{*}$ 的部分路徑,對于從 $i_{t}^{*}$ 到 $i_{T}^{*}$ 的所有可能的部分路徑來說,必須是最優的。因為假如不是這樣,那么從 $i_{t}^{*}$ 到 $i_{T}^{*}$ 就有另一條更好的部分路徑存在,如果把它和從 $i_{t}^{*}$ 到達 $i_{T}^{*}$ 的部分路徑連接起來,就會形成一條比原來的路徑更優的路徑,這是矛盾的。依據這個原理,我們只需要從時刻t=1開始,遞推地計算在時刻t狀態i的各條部分路徑的最大概率,直至得到時刻t=T狀態為i的各條路徑的最大概率。時刻t=T的最大概率就是最優路徑的概率 $P^{*}$ ,最優路徑的終結點 $i_{T}^{*}$ 也同時得到。之后,為了找出最優路徑的各個結點,從終結點 $i_{T}^{*}$ 開始,由后向前逐步求得結點 $i_{T-1}^{*},...,i_{1}^{*}$ ,最終得到最優路徑 $I^{*}=(i_{1}^{*},i_{2}^{*},...,i_{T}^{*})$ 。
2.4 輸出分詞結果
由Viterbi算法得到狀態序列,也就可以根據狀態序列得到分詞結果。其中狀態以B開頭,離它最近的以E結尾的一個子狀態序列或者單獨為S的子狀態序列,就是一個分詞。以”去北京大學玩“的隱藏狀態序列”SBMMES“為例,則分詞為”S / BMME / S“,對應觀測序列,也就是”去 / 北京大學 / 玩”。
3 源碼分析
jieba分詞中HMM模型識別未登錄詞的源碼目錄在jieba/finalseg/下,
__init__.py 實現了HMM模型識別未登錄詞;
prob_start.py 存儲了已經訓練好的HMM模型的狀態初始概率表;
prob_trans.py 存儲了已經訓練好的HMM模型的狀態轉移概率表;
prob_emit.py 存儲了已經訓練好的HMM模型的狀態發射概率表;
3.1 HMM模型參數訓練
HMM模型的參數是如何訓練出來,此處可以參考jieba中Issue 模型的數據是如何生成的? ,如下是jieba的開發者的解釋:
來源主要有兩個,一個是網上能下載到的1998人民日報的切分語料還有一個msr的切分語料。另一個是我自己收集的一些txt小說,用ictclas把他們切分(可能有一定誤差),然后用python腳本統計詞頻。要統計的主要有三個概率表:1)位置轉換概率,即B(開頭),M(中間),E(結尾),S(獨立成詞)四種狀態的轉移概率;2)位置到單字的發射概率,比如P("和"|M)表示一個詞的中間出現”和"這個字的概率;3) 詞語以某種狀態開頭的概率,其實只有兩種,要么是B,要么是S。
3.2 基于HMM模型的分詞流程
jieba分詞會首先調用函數cut(sentence),cut函數會先將輸入句子進行解碼,然后調用__cut函數進行處理。__cut函數就是jieba分詞中實現HMM模型分詞的主函數。__cut函數會首先調用viterbi算法,求出輸入句子的隱藏狀態,然后基于隱藏狀態進行分詞。
def __cut(sentence):global emit_P# 通過viterbi算法求出隱藏狀態序列prob, pos_list = viterbi(sentence, 'BMES', start_P, trans_P, emit_P)begin, nexti = 0, 0# print pos_list, sentence# 基于隱藏狀態序列進行分詞for i, char in enumerate(sentence):pos = pos_list[i]# 字所處的位置是開始位置if pos == 'B':begin = i# 字所處的位置是結束位置elif pos == 'E':# 這個子序列就是一個分詞yield sentence[begin:i + 1]nexti = i + 1# 單獨成字elif pos == 'S':yield charnexti = i + 1# 剩余的直接作為一個分詞,返回if nexti < len(sentence):yield sentence[nexti:]3.3 Viterbi算法
首先先定義兩個變量, $delta,psi$,定義在時刻t狀態i的所有單個路徑 $(i_{1},i_{2},...,i_{t})$ 中概率最大值為
由此可得變量 $delta$ 的遞推公式為,
定義在時刻t狀態i的所有單個路徑
中概率最大的路徑的第t-1個結點為,Viterbi算法的大致流程:
輸入:模型
和觀測序列 ;輸出:最優路徑
;(1)初始化
(2)遞推
(3)終止
(4)最優路徑回溯,對于t=T-1,T-2,...,1,
最終求得最優路徑
;jieba分詞實現Viterbi算法是在viterbi(obs, states, start_p, trans_p, emit_p)函數中實現。viterbi函數會先計算各個初始狀態的對數概率值,然后遞推計算,每時刻某狀態的對數概率值取決于上一時刻的對數概率值、上一時刻的狀態到這一時刻的狀態的轉移概率、這一時刻狀態轉移到當前的字的發射概率三部分組成。
def viterbi(obs, states, start_p, trans_p, emit_p):V = [{}] # tabularpath = {}# 時刻t = 0,初始狀態for y in states: # initV[0][y] = start_p[y] + emit_p[y].get(obs[0], MIN_FLOAT)path[y] = [y]# 時刻t = 1,...,len(obs) - 1for t in xrange(1, len(obs)):V.append({})newpath = {}# 當前時刻所處的各種可能的狀態for y in states:# 獲取發射概率對數em_p = emit_p[y].get(obs[t], MIN_FLOAT)# 分別獲取上一時刻的狀態的概率對數,該狀態到本時刻的狀態的轉移概率對數,本時刻的狀態的發射概率對數# 其中,PrevStatus[y]是當前時刻的狀態所對應上一時刻可能的狀態(prob, state) = max([(V[t - 1][y0] + trans_p[y0].get(y, MIN_FLOAT) + em_p, y0) for y0 in PrevStatus[y]])V[t][y] = prob# 將上一時刻最優的狀態 + 這一時刻的狀態newpath[y] = path[state] + [y]path = newpath# 最后一個時刻(prob, state) = max((V[len(obs) - 1][y], y) for y in 'ES')# 返回最大概率對數和最優路徑return (prob, path[state])相關優化:
- 1.將每一時刻最優概率路徑記錄下來,避免了第4步的最優路徑回溯;
- 2.提前建立一個當前時刻的狀態到上一時刻的狀態的映射表,記錄每個狀態在前一時刻的可能狀態,降低計算量;如下所示,當前時刻的狀態是B,那么前一時刻的狀態只可能是(E或者S),而不可能是(B或者M);
PrevStatus = {'B': 'ES','M': 'MB','S': 'SE','E': 'BM'}4 Reference
李航,統計學習方法.?book.douban.comjieba 分詞源代碼研讀(3)?blog.csdn.netjieba中文分詞源碼分析(四)?blog.csdn.net總結
以上是生活随笔為你收集整理的hmm 求隐藏序列_结巴分词3--基于汉字成词能力的HMM模型识别未登录词的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bocketmq 多个消费者同时_菜鸟开
- 下一篇: 盐城大数据产业园人才公寓_住在永川大数据