在講隱馬模型之前,首先要了解下,啥是馬爾可夫模型。
馬爾可夫模型
幾個條件
- 當前狀態(tài)只與前一個狀態(tài)相關(guān)
- 一個狀態(tài)到所有狀態(tài)的轉(zhuǎn)移概率和為1
- 概率大于等于0小于等于1
- 狀態(tài)起始概率和為1
舉個例子:
在文本中,假設(shè)有三個狀態(tài),名詞n,動詞v,形容詞adj,三者之間的轉(zhuǎn)移概率為(瞎編):
初始化概率為p(adj) = 0.5,p(n)=0.3,p(v)=0.2.
那么,一個文本詞性為 形容詞,名詞,動詞,名詞的概率為:
相當于n-gram語言模型中,n = 2 的情況。
隱馬爾可夫模型
在上述例子中,文本的詞性是明確的,而隱馬模型中,不知道所經(jīng)歷的狀態(tài)序列,觀察到的序列是隨機的。所以,狀態(tài)轉(zhuǎn)換是隨機的,觀測序列也是隨機的,雙重隨機過程。
舉個例子:我 吃 飯。這里我們能夠觀測到的就是,‘我’,‘吃’,‘飯’。
那么‘我’對應(yīng)的狀態(tài)有‘動詞’,‘名詞’,‘形容詞’,‘吃’,‘飯’同理。那么他們的背后詞性序列,到底是 名動名,還是動動名,還是形動名不得而知。這種未知序列稱之為隱狀態(tài)。
所以:隱馬模型三要素,一個是狀態(tài)轉(zhuǎn)移矩陣(同上),另一個是觀測狀態(tài)到隱藏狀態(tài)的概率矩陣。還有一個起始狀態(tài)概率。
隱馬模型的三個問題
- 如何計算 觀測序列O 的概率
- 如和計算 觀測序列O 最優(yōu)的隱狀態(tài)序列
- 參數(shù)如何學習
問題一:
這樣相當于是窮舉所有可能。即,O1的狀態(tài)概率O2的狀態(tài)概率狀態(tài)之間的概率轉(zhuǎn)移。這樣,狀態(tài)一多,觀測長度一長,計算將會指數(shù)增長。
為了優(yōu)化求解時間,采用動態(tài)規(guī)劃的思想,即O1,到O2,在計算O2,到O3,一次類推,最終計算到Ot.
那么復雜度即,O1有N個狀態(tài),O2有N個狀態(tài),那么計算O1到O2,就需要計算N*N 次,最終算得N個路徑,通往O3,解釋有點繞。總共t次。隨意復雜度為。具體算法可以查看 前向算法。大概樣子就是如下所示:
問題二:
一般在問題二在NLP的應(yīng)用中較多,需要找到當前觀測序列最優(yōu)的標注。
如何快速的找到最優(yōu)的序列。
維特比算法:
算法原理這里不做闡述。這篇博客講的非常通俗易懂~
https://blog.csdn.net/athemeroy/article/details/79339546
算法實現(xiàn):
首先需要一個隱馬模型:
寫了個隨機函數(shù),生成隱馬模型
def hmmRamdon():
#隱狀態(tài)個數(shù)
yin_num = 5
#觀測概率個數(shù)
guan_num = 10
A = np.zeros((yin_num,yin_num))
for i in range(yin_num):
rows_h = np.random.random(yin_num)
rows_v = np.random.random(yin_num)
for k in range(i):
rows_h[k] = A[i][k]
rows_h[i:] = rows_h[i:] / rows_h[i:].sum() * (1 - rows_h[:i].sum())
A[i] = rows_h
for k in range(i+1):
rows_v[k] = A[k][i]
rows_v[i+1:] = rows_v[i+1:] / rows_v[i+1:].sum() * (1 - rows_v[:i+1].sum())
for j in range(yin_num):
A[j][i] = rows_v[j]
B = np.zeros((yin_num,guan_num))
for i in range(yin_num):
row = np.random.random(guan_num)
B[i] = row / row.sum()
init_yin = np.random.random(yin_num)
pai = init_yin / init_yin.sum()
return A,B,pai
函數(shù)沒有做大于0校驗~,隨出負數(shù)的話記得多隨幾次哈~
看過上述講解維特比算法原理博客的同鞋,應(yīng)該記得下圖:
在HMM,中A->B相當于,觀測序列的中 第一個序列,B1->B3是隱狀態(tài),
所以當前A->B的路徑可以初始化為
這里展示方便,假設(shè)HMM模型為
A = np.array([[0.5,0.2,0.3],
[0.3,0.5,0.2],
[0.2,0.3,0.5]])
B = np.array([[0.5,0.5],
[0.4,0.6],
[0.7,0.3]])
pai = np.array([[0.2,0.4,0.4]])
observe = [0,1,0]
a_ex = B[:,observe[0]] * pai
a_ex = a_ex.T
all_way = np.full((yin_num, 1), -1)
print(a_ex)
print(all_way)
[[0.1 ]
[0.16]
[0.28]]
===========
[[-1]
[-1]
[-1]]
a_ex記錄到達上一個觀測狀態(tài)每個隱狀態(tài)最優(yōu)的概率值。
observe[0]觀測序列第一個,觀測概率各自乘于初始化概率。all_way 用于記錄每條路徑經(jīng)過的隱狀態(tài)節(jié)點。0.1 = 0.5 * 0.2,0.16 = 0.4 * 0.4,0.28 = 0.7 * 0.4.
當計算由B->C時,C1,C2,C3 各自有三條路徑,,結(jié)合A-B的路徑,計算出A->C1概率最高的一條路徑,同理計算到C2,C3的路徑。
a_new = A1 *a_ex* B1[:,observe[1]]#計算圖中9個路線
a_ex = np.amax(a_new,axis=0) #獲取每個隱狀態(tài)三個路徑中最高的那一個
a_ex = a_ex.reshape(a_ex.shape[0],-1)
way = np.argmax(a_new,axis=0)#找出概率最高的三條路線
way = way.reshape(way.shape[0],-1)
all_way = np.hstack((all_way,way))#路徑更新
a_new:
[[0.025 0.012 0.009 ]
[0.024 0.048 0.0096]
[0.028 0.0504 0.042 ]]
a_ex:
[[0.028 ]
[0.0504]
[0.042 ]]
all_way:
[[-1 2]
[-1 2]
[-1 2]]
所以對應(yīng)圖中的三條路徑為A-B3-C1,A-B3-C2,A-B3-C3。
即博客中對應(yīng)的圖
如此循環(huán)直到最后一個。完整算法代碼如下:
import numpy as np
def viterbi_search(A,B,pai,observe):
yin_num = A.shape[0]
for j in range(len(observe)):
if j == 0:
a_ex = B[:,observe[j]] * pai
a_ex = a_ex.T
all_way = np.full((yin_num, 1), -1)
else:
a_new = A1 *a_ex* B1[:,observe[j]]
a_ex = np.amax(a_new,axis=0)
a_ex = a_ex.reshape(a_ex.shape[0],-1)
way = np.argmax(a_new,axis=0)
way = way.reshape(way.shape[0],-1)
all_way = np.hstack((all_way,way))
result = np.append(all_way[a_ex.argmax()],a_ex.argmax())
return result, a_ex.max()
實驗環(huán)境jupyter,實驗代碼下載鏈接:
鏈接:https://pan.baidu.com/s/1W9zckhKG3yssFeZtahl-5g 密碼:78y5
問題三:參數(shù)估計
最大似然估計:
轉(zhuǎn)移概率:aij = ai 到aj的次數(shù) / ai到所有狀態(tài)的次數(shù)。
觀測概率:bi = ai 到 bi 觀測的次數(shù) / ai到所有觀測的次數(shù)。
一般隱狀態(tài)未知的情況下,可以用最大期望EM,對轉(zhuǎn)移概率進行估計。