维特比算法 python_维特比算法 实现中文分词 python实现
最近我在學(xué)習(xí)自然語言處理,相信大家都知道NLP的第一步就是學(xué)分詞,但分詞≠自然語言處理。現(xiàn)如今分詞工具及如何使用網(wǎng)上一大堆。我想和大家分享的是結(jié)巴分詞核心內(nèi)容,一起探究分詞的本質(zhì)。
(1)、基于前綴詞典實(shí)現(xiàn)高效的詞圖掃描,生成句子中漢字所有可能成詞情況所構(gòu)成的有向無環(huán)圖
什么是DAG(有向無環(huán)圖)?
例如,句子“去北京大學(xué)玩”對應(yīng)的DAG為{0:[0], 1:[1,2,4], 2:[2], 3:[3,4], 4:[4], 5:[5]}。DAG中{0:[0]}就表示0位置對應(yīng)的是詞,就是說0~0,即“去”這個(gè)詞在Dict(詞典庫,里面記錄每個(gè)詞的頻次)中是詞條。DAG中{1:[1,2,4]},就是表示從1位置開始,在1,2,4位置都是詞,就是說1~1、1~2、1~4即“北”“北京”“北京大學(xué)”這三個(gè)也是詞,出現(xiàn)在Dict中。句子“去北京大學(xué)玩”的DAG畢竟比較短可以一眼看出來,現(xiàn)在來了另外一個(gè)句子“經(jīng)常有意見分歧”,如何得到它的DAG呢?這時(shí)候就得通過代碼來實(shí)現(xiàn)了。
Dict= {"經(jīng)常":0.1,"經(jīng)":0.05,"有":0.1, "常":0.001,"有意見":0.1, "歧":0.001,"意見":0.2,"分歧":0.2,"見":0.05,"意":0.05,"見分歧":0.05,"分":0.1}
def DAG(sentence):
DAG = {} #DAG空字典,用來構(gòu)建DAG有向無環(huán)圖
N = len(sentence)
for k in range(N):
tmplist = []
i = k
frag = sentence[k]
while i < N:
if frag in Dict:
tmplist.append(i)
i += 1
frag = sentence[k:i + 1]
if not tmplist:
tmplist.append(k)
DAG[k] = tmplist
return DAG
print(DAG("經(jīng)常有意見分歧"))
輸出:
{0: [0, 1], 1: [1], 2: [2, 4], 3: [3, 4], 4: [4, 6], 5: [5, 6], 6: [6]}
根據(jù)dict及其每個(gè)詞出現(xiàn)的概率,可以得到所有可能出現(xiàn)路徑(分詞情況),如下圖:
DAG的圖表示
只要出現(xiàn)的詞,就可以分,像“經(jīng)常有”這個(gè)詞沒出現(xiàn),就不能把它當(dāng)做單獨(dú)一個(gè)詞分開了,通過運(yùn)行代碼,我們可以得到“經(jīng)常有意見分歧”的DAG
{0: [0, 1], 1: [1], 2: [2, 4], 3: [3, 4], 4: [4, 6], 5: [5, 6], 6: [6]}
,便于直觀理解DAG,我們把問題轉(zhuǎn)化成尋找路徑的過程,就如上圖表示,從開始到結(jié)束,比如我照最上面紅線的路徑走,可以得到[經(jīng)常|有|意見|分歧]的分詞情況,如果把它們每一步的概率值加起來就是該路徑的得分S=0.1+0.1+0.2+0.2=0.6,同理我走其他的路徑[經(jīng)|常|有意見|分歧],它的得分就是S=0.05+.0.001+0.1+0.2=0.351。這就是我們的第一步,通過代碼構(gòu)建出一個(gè)sentence的DAG。
(2)采用動(dòng)態(tài)規(guī)劃查找最大概率路徑,找出基于詞頻的最大切分組合。
通過第一步,得到了DAG,同樣也可以得到每條路徑的得分S,從中找到得分最大的,也就是概率值最大的情況,就是我們要找的分詞情況。如果用遍歷所有路徑的話,找到每個(gè)路徑然后求出每個(gè)S,取出最大的S,當(dāng)然可以得到我們想要的,但比較蠻力。我們可以試著用動(dòng)態(tài)規(guī)劃的思路,維特比算法,直接上圖
維特比算法的順序解法
給每個(gè)節(jié)點(diǎn)編號1~8,開始到結(jié)束,f(a)代表該節(jié)點(diǎn)的所有得分值,每一步單個(gè)的箭頭都有其對應(yīng)的概率值,c(a~b)代表的是a節(jié)點(diǎn)到b節(jié)點(diǎn)的值,如c(1~3)是“經(jīng)常”的概率值,為什么有的節(jié)點(diǎn)如f(6)有三個(gè)值?那是因?yàn)?這個(gè)節(jié)點(diǎn)有三個(gè)箭頭指向它,也就是說有多少個(gè)箭頭指向該節(jié)點(diǎn),該節(jié)點(diǎn)就有多少個(gè)得分值,如分f(3)有2個(gè)值、f(4)有一個(gè)值......。按1~8的順序,計(jì)算出每個(gè)節(jié)點(diǎn)的所有得分值,計(jì)算后面節(jié)點(diǎn)的時(shí)候要用到前面節(jié)點(diǎn)得分值都取(max)最大的,以保證最后計(jì)算到f(8)時(shí)是全局的最大值,例如計(jì)算f(4)中f(3)取的就是0.1。算到最后,我們知道f(8)=f(6) +c(6~8) =0.4+0.2=0.6 (max),接著把f(6)展開,f(8)=f(4) +c(4~6) +c(6~8) ,同理,把所有的f()換成c(),f(8)=c(1~3) +c(3~4) +c(4~6) +c(6~8) 。直到等式右邊沒有f(),c(1~3)、c(3~4)、c(4~6)、c(6~8)分別代表啥各位看圖去吧。
回到開始,假如用蠻力一個(gè)一個(gè)列出所有路徑,不累死也得列的頭暈,用動(dòng)態(tài)規(guī)劃的思想可以把一個(gè)大問題拆分到每一步的小問題,下一步的小問題只需要在之前的小問題上再進(jìn)一步,動(dòng)態(tài)規(guī)劃的思想就像是小問題站巨人肩膀上,然后大問題莫名其妙就解決了。剛說的是從開始到結(jié)束的順序解法,要是從8節(jié)點(diǎn)到1節(jié)點(diǎn)逆序解法怎么解?
維特比算法的逆序解法
發(fā)現(xiàn)沒,最后最大都是0.6=f(1)=c(1~3) +c(3~4) +c(4~6) +c(6~8),而且直接都是看出來了,再一次說明了最大的路徑就是這條路徑。說了這么多,上代碼
sentence ="經(jīng)常有意見分歧"
N=len(sentence)
route={}
route[N] = (0, 0)
DAG={0: [0, 1], 1: [1], 2: [2, 4], 3: [3, 4], 4: [4, 6], 5: [5, 6], 6: [6]}
for idx in range(N - 1, -1, -1):
distance = (((Dict.get(sentence[idx:x + 1]) or 0) + route[x + 1][0], x) for x in DAG[idx])
route[idx] = max(distance)
# 列表推倒求最大概率對數(shù)路徑
# route[idx] = max([ (概率值,詞語末字位置) for x in DAG[idx] ])
# 以idx:(概率最大值,詞語末字位置)鍵值對形式保存在route中)
# route[x+1][0] 表示 詞路徑[x+1,N-1]的最大概率值,
# [x+1][0]即表示取句子x+1位置對應(yīng)元組(概率對數(shù),詞語末字位置)的概率對數(shù)
print(route)
輸出結(jié)果:
{7: (0, 0), 6: (0.001, 6), 5: (0.2, 6), 4: (0.25, 4), 3: (0.4, 4), 2: (0.5, 2), 1: (0.501, 1), 0: (0.6, 1)}
這是一個(gè)自底向上的動(dòng)態(tài)規(guī)劃(逆序的解法),從sentence的最后一個(gè)字開始倒序遍歷每個(gè)分詞方式的得分。然后將得分最高的情況以(概率得分值,詞語最后一個(gè)字的位置)這樣的tuple保存在route中。看route的0: (0.6, 1)中的0.6,不就是我們求到的f(1)的max, 1: (0.501, 1)中的0.501不就是f(2)......后面大家對著看圖找規(guī)律吧。最后小操作一波,就可以把我們要的分詞結(jié)果打印出來了,結(jié)果和手推的是一樣的c(1~3) +c(3~4) +c(4~6) +c(6~8)。
x = 0
segs = []
while x < N:
y = route[x][1] + 1
word = sentence[x:y]
segs.append(word)
x = y
print(segs)
#輸出結(jié)果:['經(jīng)常', '有', '意見', '分歧']
上面只是一些核心的思路,好多地方可以繼續(xù)優(yōu)化的,比如把概率值轉(zhuǎn)換成-log(概率值),目的是為了防止下溢問題,只是我舉例的概率值比較大,如果是一個(gè)超大的Dict,為了保證所有詞的概率之和約等于1,那每個(gè)詞對應(yīng)的概率值會(huì)特別小。
(3)中文分詞以后得攻克的難點(diǎn)
1、分詞的規(guī)范,詞的定義還不明確,沒有一個(gè)公認(rèn)的、權(quán)威的標(biāo)準(zhǔn)。
2、歧義詞的切分。這也從側(cè)面證實(shí)了中華文化博大精深。
3、未登錄的新詞。就是咱們的Dict里沒有的詞,對于3這個(gè)比2對分詞的影響大多了,目前結(jié)巴分詞對此采取的方法是:基于漢字成詞能力的HMM模型,使用維特比算法。
參考:
1、貪心學(xué)院nlp
2、自然語言處理理論與實(shí)戰(zhàn) 唐聃
總結(jié)
以上是生活随笔為你收集整理的维特比算法 python_维特比算法 实现中文分词 python实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ZEGO RTC Meetup 实时音视
- 下一篇: python pandas获取行号_py