维特比算法—打字输入预测
這里首先說下隱馬爾可夫模型的相關(guān)知識。
1. 隱馬爾可夫模型(HMM)
在說隱馬爾可夫模型前還有一個概念叫做“馬爾科夫鏈”,既是在給定當前知識或信息的情況下,觀察對象過去的歷史狀態(tài)對于預(yù)測將來是無關(guān)的。也可以說在觀察一個系統(tǒng)變化的時候,他的下一個狀態(tài)如何的概率只需要觀察和統(tǒng)計當前的狀態(tài)即可正確得出。隱馬爾可夫鏈和貝葉斯網(wǎng)絡(luò)的模型思維有些接近,區(qū)別在于隱馬爾可夫的模型更為簡化。而且隱馬爾可夫鏈是一個雙重的隨機過程,不僅狀態(tài)轉(zhuǎn)移之間是一個隨機事件,狀態(tài)和輸出之間也是一個隨機過程。
如下圖:
在一個完整的觀察過程中有一些狀態(tài)的轉(zhuǎn)換,如X1到XT的轉(zhuǎn)換存在一個客觀的轉(zhuǎn)換規(guī)律,但是沒辦法直接觀測到,觀測到的只是每個X狀態(tài)下的輸出O,通過O1到OT這些輸出值來進行模型建立和計算狀態(tài)轉(zhuǎn)移的概率。
知乎上一個很好的例子:
https://www.zhihu.com/question/20962240/answer/33438846
對于HMM來說,如果前提知道所有的隱含狀態(tài)之間的轉(zhuǎn)換概率和所有隱含狀態(tài)到所有可見狀態(tài)之間的輸出概率,進行模擬是很容易的。但是往往會缺失一部分信息,如何應(yīng)用算法去估計缺失的信息,就成為了一個重要的問題。
和HMM模型相關(guān)的算法分為3類,分別解決3種問題。
- 知道隱含狀態(tài)數(shù)量、轉(zhuǎn)換概率、根據(jù)可見狀態(tài)鏈的結(jié)果想知道隱含狀態(tài)鏈。這個問題在語音識別領(lǐng)域叫做解碼問題,求解這個問題有兩種方法。第一種是求最大最大似然狀態(tài)路徑(一串骰子序列,使其產(chǎn)生的觀測結(jié)果的概率最大)。第二種解法是求每次擲出的骰子分別是某種骰子的概率。
- 知道隱含狀態(tài)數(shù)量、轉(zhuǎn)換概率、根據(jù)可見狀態(tài)鏈的結(jié)果想知道產(chǎn)生結(jié)果的概率。這個問題的實際意義在于檢測觀察到的模型是否和已知的模型吻合。如果很多次結(jié)果都是對應(yīng)了比較小的概率,那么就說明已知的模型很可能是錯的。
- 知道隱含狀態(tài)數(shù)量、不知道轉(zhuǎn)換概率、根據(jù)可見狀態(tài)鏈的結(jié)果想反推出轉(zhuǎn)換概率。這個問題是最重要的,因為是最為常見的。很多時候只有可見結(jié)果,不知道HMM模型中的參數(shù),需要從可見結(jié)果估計這些參數(shù)。
2. 維特比算法
當馬爾科夫鏈很長時,根據(jù)古典概型的分布特點來計算概率時窮舉的數(shù)量太大,就很難得到結(jié)果。這時就要用到維特比算法。維特比算法就是為了找出可能性最大的隱藏序列,即剛剛提到的第一個問題。這種算法是一種鏈的可能性問題,現(xiàn)在應(yīng)用最廣泛的是CDMA和打字提示功能。
維特比算法的整體思路是尋找上一段信息和它跟隨的下一段信息的轉(zhuǎn)換概率問題。
2.1 打字輸入提示應(yīng)用
這是常用的搜狗輸入法的示例。在本例中只是考慮最簡單的因素,下面是模擬輸入法的猜測方法給出一個算法示例,先給出各級轉(zhuǎn)換矩陣。如下:
其實這些概率矩陣都是由統(tǒng)計產(chǎn)生,而且每個雙字詞、三字詞等的輸入矩陣都由這種統(tǒng)計產(chǎn)生。在輸入雙字詞漢子拼音時會根據(jù)轉(zhuǎn)移概率表進行計算。多個詞相連就是多個轉(zhuǎn)移矩陣的概率相乘計算,從而得到概率最大的輸入可能項。
2.2 代碼:
# coding=utf-8 import numpy as npjin = ['近', '斤', '今', '金', '盡'] jin_per = [0.3, 0.2, 0.1, 0.06, 0.03] # 單字詞概率矩陣jintian = ['天', '填', '田', '甜', '添'] jintian_per = [[0.001, 0.001, 0.001, 0.001, 0.001], # 轉(zhuǎn)移矩陣[0.001, 0.001, 0.001, 0.001, 0.001],[0.990, 0.001, 0.001, 0.001, 0.001],[0.002, 0.001, 0.850, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001]]wo = ['我', '窩', '喔', '握', '臥'] wo_per = [0.400, 0.150, 0.090, 0.050, 0.030]women = ['們', '門', '悶', '燜', '捫'] women_per = [[0.970, 0.001, 0.003, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001]]qing = ['請', '晴', '清', '輕', '情'] qing_per = [0.2, 0.12, 0.09, 0.05, 0.02]qinghua = ['話', '畫', '化', '花', '華'] qinghua_per = [[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.950],[0.001, 0.001, 0.850, 0.001, 0.001],[0.003, 0.001, 0.001, 0.002, 0.001]]da = ['大', '打', '達', '搭', '噠'] da_per = [0.300, 0.130, 0.120, 0.110, 0.090]daxue = ['學', '雪', '血', '薛', '穴'] daxue_per = [[0.800, 0.140, 0.023, 0.004, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001],[0.001, 0.001, 0.001, 0.001, 0.001]]N = 5# 一個字的預(yù)測 def found_from_oneword(oneword_per):index = []values = []a = np.array(oneword_per)print 'a:',aprint 'argsort(a):',np.argsort(a)[::-1][:N]for v in np.argsort(a)[::-1][:N]: # 遍歷降序前5的概率索引,index.append(v)values.append(oneword_per[v])return index, values# 一個詞的預(yù)測 def found_from_twoword(oneword_per, twoword_per):last = 0for i in range(len(oneword_per)):#print oneword_per[i],'\n', twoword_per[i]current = np.multiply(oneword_per[i], twoword_per[i]) # 字概率乘以轉(zhuǎn)移概率if i == 0:last = currentelse:last = np.concatenate((last, current), axis=0) # 字符串拼接#print 'last:',last # 單個字概率矩陣乘以轉(zhuǎn)移概率矩陣index = []values = []#print np.argsort(last)[::-1][:N]for v in np.argsort(last)[::-1][:N]: # 概率從大到低的排列(前5個)index.append([v / 5, v % 5]) # 由數(shù)組的索引值得到其在矩陣中的坐標值values.append(last[v])print 'index:',index,'\n','values:',values return index, values# 字詞的預(yù)測 def predict(word):if word == 'jin':for i in found_from_oneword(jin_per)[0]: # 遍歷概率索引值print jin[i] # 輸出排序后的索引對應(yīng)的值print '.......................................'elif word == 'jintian':for i in found_from_twoword(jin_per, jintian_per)[0]: # 傳入的是概率矩陣和轉(zhuǎn)移矩陣print 'i:',i# jin[i[0]]:詞組在矩陣中的第一個字的下標;jintian[i[1]]:在矩陣中的列表示后一個字的下標print jin[i[0]] + jintian[i[1]] print '.......................................'elif word == 'wo':for i in found_from_oneword(wo_per)[0]:print wo[i]print '.......................................'elif word == 'women':for i in found_from_twoword(wo_per, women_per)[0]:print wo[i[0]] + women[i[1]]print '.......................................'elif word == 'jintianwo':index1, values1 = found_from_oneword(wo_per)print 'index1, values1:', index1, '\n',values1 # 先得到'wo'的排序index2, values2 = found_from_twoword(jin_per, jintian_per) # 再得到'jintian'排序print 'index2, values2:', index2, '\n',values2last = np.multiply(values1, values1) # 得到'jintian'和'wo'的最大概率乘積for i in np.argsort(last)[::-1][:N]:print jin[index2[i][0]], jintian[index2[i][1]], wo[i]print '.......................................'elif word == 'jintianwomen':index1, values1 = found_from_twoword(jin_per, jintian_per) # 得到'jintian'的排序index2, values2 = found_from_twoword(wo_per, women_per) # 得到'women'的排序last = np.multiply(values1, values1) # 得到'jintian'和'women'相乘的排序for i in np.argsort(last)[::-1][:N]:print jin[index1[i][0]], jintian[index1[i][1]], wo[index2[i][0]], women[index2[i][1]]print '.......................................'else:passif __name__ == '__main__':predict('jin')# 近# 斤# 今# 金# 盡predict('jintian')# 今天# 金田# 近天# 近填# 近田predict('wo')# 近# 斤# 今# 金# 盡predict('women')# 我們# 我悶# 我門# 我燜# 我捫predict('jintianwo')# 今 天 我# 金 田 窩# 近 天 喔# 近 填 握# 近 田 臥predict('jintianwomen')# 今 天 我 們# 金 田 我 悶# 近 田 我 捫# 近 填 我 燜# 近 天 我 門運行結(jié)果:
a: [ 0.3 0.2 0.1 0.06 0.03] argsort(a): [0 1 2 3 4] 近 斤 今 金 盡 ....................................... index: [[2, 0], [3, 2], [0, 0], [0, 1], [0, 2]] values: [0.099000000000000005, 0.050999999999999997, 0.00029999999999999997, 0.00029999999999999997, 0.00029999999999999997] i: [2, 0] 今天 i: [3, 2] 金田 i: [0, 0] 近天 i: [0, 1] 近填 i: [0, 2] 近田 ....................................... a: [ 0.4 0.15 0.09 0.05 0.03] argsort(a): [0 1 2 3 4] 我 窩 喔 握 臥 ....................................... index: [[0, 0], [0, 2], [0, 1], [0, 3], [0, 4]] values: [0.38800000000000001, 0.0012000000000000001, 0.00040000000000000002, 0.00040000000000000002, 0.00040000000000000002] 我們 我悶 我門 我燜 我捫 ....................................... a: [ 0.4 0.15 0.09 0.05 0.03] argsort(a): [0 1 2 3 4] index1, values1: [0, 1, 2, 3, 4] [0.4, 0.15, 0.09, 0.05, 0.03] index: [[2, 0], [3, 2], [0, 0], [0, 1], [0, 2]] values: [0.099000000000000005, 0.050999999999999997, 0.00029999999999999997, 0.00029999999999999997, 0.00029999999999999997] index2, values2: [[2, 0], [3, 2], [0, 0], [0, 1], [0, 2]] [0.099000000000000005, 0.050999999999999997, 0.00029999999999999997, 0.00029999999999999997, 0.00029999999999999997] 今 天 我 金 田 窩 近 天 喔 近 填 握 近 田 臥 ....................................... index: [[2, 0], [3, 2], [0, 0], [0, 1], [0, 2]] values: [0.099000000000000005, 0.050999999999999997, 0.00029999999999999997, 0.00029999999999999997, 0.00029999999999999997] index: [[0, 0], [0, 2], [0, 1], [0, 3], [0, 4]] values: [0.38800000000000001, 0.0012000000000000001, 0.00040000000000000002, 0.00040000000000000002, 0.00040000000000000002] 今 天 我 們 金 田 我 悶 近 田 我 捫 近 填 我 燜 近 天 我 門 .......................................從運行結(jié)果可以看出其實就是通過統(tǒng)計得到的概率矩陣和轉(zhuǎn)移矩陣的乘積之后的排序計算。
3. 筆記
3.1 for v in np.argsort(a)[::-1][:N]的用法:
In [7]: a=array([1, 2, 3])In [8]: a Out[8]: array([1, 2, 3])In [9]: a[::-1] Out[9]: array([3, 2, 1])In [10]: a[::-1][:3] Out[10]: array([3, 2, 1])3.2 last = np.concatenate((last, current), axis=0)的使用:
>>> import numpy as np >>> a = np.array([[1, 2], [3, 4]]) >>> a array([[1, 2],[3, 4]]) >>> b = np.array([[5, 6]]) >>> np.concatenate((a, b), axis=0) array([[1, 2],[3, 4],[5, 6]])參考:《白話大數(shù)據(jù)與機器學習》
總結(jié)
以上是生活随笔為你收集整理的维特比算法—打字输入预测的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建设银行怎么贷款 贷款流程及条件一览
- 下一篇: 朴素贝叶斯—疾病的预测