jieba分詞原始碼解讀四

2021-07-12 02:52:11 字數 3021 閱讀 9518

在上一節中我們考察了結巴分詞對於未登入詞的分詞方法,它使用了hmm模型和用來解碼hmm的維特比演算法。較之基於語料庫打分的初步分詞結果,例句:

'喬治馬丁寫冰與火之歌拖了好久'

分詞情況變成了這樣:

'喬治/馬丁/寫冰/與/火之歌/拖/了/好久'

比原來有改進,但改進幅度可以忽略不計。。。

下一步我們就要除錯**了,目前可以知道程式會把連續的單個的字收集起來組成字串交由 finalseg 中的 cut 函式處理。而該函式把這個字串 寫冰與火之歌拖了 

標註成了 besbmess, 而相對比較正確的標註方式應該是 sbmmmess,那麼在程式內部這兩種標註方式計算得到的概率值相差多少呢?

這就要用到前向演算法來計算了。前向概率就是用來計算在已知hmm模型的全部引數的前提下,判斷某乙個輸出序列產生的概率,其本質仍是動態規劃,層層遞進。那麼在某一時刻,hmm呈現某種輸出狀態的概率值是多少呢?

這個公式表示 λ 時刻輸出為o的概率等於 λ 時刻 狀態為q輸出為o的概率對狀態q的遍歷求和,p(o,q|λ )是乙個聯合概率,表示λ 時刻狀態為q且輸出為o的概率,它等於

p(o,q|λ)=p(q | λ)*e(o | q)

e(o | q)即發射概率已知,而p(q|λ)等於前一時刻所有狀態概率對於轉移概率的加權和,又因為前一時刻的輸出已知(即概率為1),所以有p(q | λ-1)=p(o,q | λ-1)

所以有遞推公式 p(o | λ)=( σp(o,q|λ-1)*t(q->q) ) * e(o | q)

t(q->q)表示從前乙個狀態q轉移到q的轉移概率,其中(σp(o,q|λ-1)*t(q->q))即所謂前一時刻所有狀態概率對於轉移概率的加權和。

推薦一篇解釋地更好的文章:

下面一段**是我實現的前向演算法,把它新增到 jieba 分詞的 finalseg/__init__.py 檔案中執行就能得到在jieba分詞的hmm模型下產生「寫冰與火之歌拖了」這句輸出的概率值了。

[python]view plain

copy

defforward(obs, states):  

import

math  

sos={}  

fori,sw 

inenumerate(obs):  

ifi==

0:  

fors 

instates:  

sos[s]=start_p[s]+emit_p[s].get(obs[i],min_float)  

else

:  buf=dict(sos)  

fors 

instates:  

em_p = emit_p[s].get(sw,min_float)  

sos[s]=math.log(sum([math.e**(buf[s0]+trans_p[s0].get(s,min_float)+em_p) for

s0 in

prevstatus[s] ]))  

#print sos

return

math.log(sum([math.e**(sos[s]) 

fors 

insos.keys()  

ifs 

notin

('b'

,'m'

) ]))  

print

finalseg.forward(u

'寫冰與火之歌拖了'

,('b'

,'m'

,'e'

,'s'

)) #結果是-60.7515239889

前向演算法得到的是乙個概率總和,如果我們想要得到具體某一條鏈路的概率應該怎麼做呢?循著前向演算法的思路,可以推導出:

p(o,q|λ)=p(q | λ)*p(o | q,λ)=p(o,q|λ-1)*e(o | q)*t(q->q )

下面這段**就實現了這個公式計算出了特定的路徑的概率值:

[python]view plain

copy

defpathprobability(obs, test_path):  

rs=0.0

fori,sw 

inenumerate(obs):  

ifi==

0:  

rs += start_p[test_path[0

]]+emit_p[test_path[

0]].get(sw,min_float)  

else

:  s0 = test_path[i-1

]  s = test_path[i]  

em_p = emit_p[s].get(sw,min_float)  

tr_p = trans_p[s0][s]  

rs += tr_p+em_p  

return

rs  

total_val=finalseg.forward(u'寫冰與火之歌拖了'

,('b'

,'m'

,'e'

,'s'

))  

print

total_val 

#-60.7515239889

viterbi_val=finalseg.pathprobability(u'寫冰與火之歌拖了'

,'besbmess'

)  print

viterbi_val 

#-62.0665839518

right_val=finalseg.pathprobability(u'寫冰與火之歌拖了'

,'sbmmmess'

)  print

right_val 

#-66.1378460505

可以看到正確的分詞路徑的概率值比維特比演算法解碼得到的最優解差了4個指數級別,這個誤差很大,那麼應該怎麼修正呢?當然最直接的方法就是在語料庫裡加上「冰與火之歌」這個詞語,但是從hmm的角度有沒有什麼好辦法呢?

jieba分詞原始碼分析

jieba是乙個開源的中文分詞庫。posseg 自定義詞典 init.py jieba分詞的入口 compat.py dict.txt 總的詞庫,記錄乙個詞 詞頻和詞性 test 測試demo encoding utf 8 import jieba seg list jieba.cut 我來到北京清...

jieba分詞流程及部分原始碼解讀(一)

首先我們來看一下jieba分詞的流程圖 結巴中文分詞簡介 1 支援三種分詞模式 精確模式 將句子最精確的分開,適合文字分析 全模式 句子中所有可以成詞的詞語都掃瞄出來,速度快,不能解決歧義 搜尋引擎模式 在精確的基礎上,對長詞再次切分,提高召回 2 支援繁體分詞 3 支援自定義詞典 4 基於trie...

原始碼閱讀之jieba分詞

功能 計算每一步的最優路徑 輸入 sentence 我是來到北京清華大學 dag route defcalc self,sentence,dag,route n len sentence route n 0,0 logtotal log self.total for idx in xrange n ...