本文重點講解隱馬爾科夫(HMM)模型的模型原理,以及與模型相關的三個最重要問題:求解、解碼和模型學習。python
爲了方便,下文統一用HMM代替隱馬爾科夫模型。HMM其實是一種圖機率模型。之因此叫作隱馬爾科夫模型,是由於模型與普通的馬爾科夫模型不一樣的是,HMM含有隱變量空間,而且遵循馬爾科夫假設。這樣說太抽象,咱們看下圖:算法
其中\(Y_i\in Q={q_1,q_2,...,q_N}\),\(X_i \in V={v_1,v_2,...,v_M}\)。而且記觀測序列\(X = X_1,X_2,...,X_T\)網絡
隱狀態序列\(Y = Y_1,Y_2,...,Y_T\)。咱們首先介紹HMM三個重要參數,而後下面再解釋這些參數的具體物理含義。學習
也就是說,狀態轉移機率決定了狀態間的單步轉移狀況,而觀測發射矩陣決定了從隱狀態到觀測狀態的轉移狀況。而初始分佈決定了模型的狀態初始分佈。爲了敘述方便,咱們記上面的參數爲\(\lambda=(A,B,\pi)\)優化
HMM有以下兩個假設,齊次性質假設和觀測獨立性假設,分別敘述以下:spa
也就是任什麼時候刻的隱藏狀態只與上一時刻的隱藏狀態有關,與其餘的隱藏狀態和觀測狀態無關。code
即任意時刻的觀測只由其同時刻的隱狀態所決定,與其餘隱藏狀態和觀測狀態無關。blog
爲了更形象的理解這個模型,下面舉一個我以爲比較貼切的例子。遞歸
假設有兩我的分別記爲P1和P2,他們中間隔了一堵牆。其中P1被關在了牆裏面,P2在外面。這兩我的僅僅能經過一個窗子交流。兩我的分別有如下的特色:P1記性特別差,只能記住上一次說過的單詞。而且每次說話時也只受上一次說話內容所影響。而P2能夠當作一個特殊的轉錄機,他每次說的單詞都只受P1當前說的單詞所影響。而且P2有一個很長的紙帶,會把每次說的單詞記錄下來。而且假設P1和P2的詞彙表都是有限的。深度學習
若是給定P2的文本片斷,咱們可否給出該文本片斷的機率(已知模型參數\(\lambda\))
給定P2的文本片斷,假設咱們已經知道了P1的詞彙表,咱們可否給出P1最可能說了哪段話(已知模型參數\(\lambda\))
給定大量的(P2,P1)文本對片斷,可否推測出模型參數\(\lambda\),或者僅僅給出大量的P2文本片斷,可否學出模型參數\(\lambda\)
實際上面的三個問題就對應着預測,解碼和模型學習的三個問題。而且若是P1的詞彙表是分詞標記標籤,P2的詞彙表是漢語,那麼所對應的就是分詞問題。把P1的詞彙表是命名實體標籤,P2的詞彙表是漢語,那麼所對應的問題實際上就是命名實體識別(NER)問題。
若是咱們不關心模型的具體使用,只是關心HMM是一個什麼樣的模型,那麼到這裏徹底能夠將模型說明白。可是咱們要更細緻的瞭解HMM的話(從代碼層面上)那麼仍是逐一解決上述三個重要的問題。
所謂的預測問題,其實是求下面的序列機率:
根據全機率公式咱們有:
其中\(Y\)是隱狀態空間的內全部組合,顯然當隱狀態空間特別大時,若是經過簡單的枚舉加和方式,面臨着指數爆炸這一難題(簡單的分析可知,上述的求和複雜度是\(O(N^T)\))。
經過上述分析可知,高效的求解序列機率的關鍵在於求解聯合機率。根據鏈式法則及HMM的假設咱們能夠獲得以下公式:
其中前兩步分別是觀測獨立假設和齊次轉移假設,最後一步是咱們的遞歸定義。實際上上面的公式還能進一步拆分(根據遞歸定義拆解下去便可),這裏再也不展開說明。
根據鏈式法則,咱們能夠有如下結論,\(T\)時刻的機率計算僅僅依賴於\(T-1\)時刻,又涉及到指數狀態空間枚舉,那麼實際上必定會存在着大量的重複計算,這樣咱們就會想用動態規劃來進行優化。(這樣的思考確實有些跳躍,實際上我在剛剛接觸HMM時,也對於其中反覆運用的動態規劃感到跳躍,可是等我刷過一些算法題目以後,再回頭看HMM,其中運用的動態規劃便瓜熟蒂落了。
首先咱們定義\(\alpha_{t,j}\) 爲\(t\)時刻對應的\(Y_{t}=q_j且觀測序列爲X={X_1,...,X_t}\)的全(路徑)機率。那麼顯然根據上面的定義,咱們有:
而且,特別的,對於\(t=1\)時,咱們引入上面的初始分佈:
總結上面的算法,僞代碼敘述以下:
輸入:觀測序列X,模型參數 lambda = (A,B,pi) 輸出:序列X所對應的機率p
對應的python代碼(片斷)以下:
def forward_alpha(self,label_seq): T = len(label_seq) label_seq_idx = self.label_to_idx(label_seq) init_label_idx = label_seq_idx pre_alpha = [self.pi[j] * self.b[j][init_label_idx] for j in range(self.N)] for t in range(1,T): tmp_label_idx = label_seq_idx[t] tmp_alpha = [self.sum_vector([pre_alpha[i]*self.a[i][j] * b[j][tmp_label_idx]]) for j in range(self.N)] pre_alpha = tmp_alpha return self.sum_vecotr(tmp_alpha)
《統計學習方法》第二版,李航,隱馬爾科夫模型
《神經網絡與深度學習》,邱錫鵬,機率圖模型