寫在前面
最近在寫論文過程中,研究了一些關於概率統計的算法,也從網上收集了不少資料,在此整理一下與各位朋友分享。
隱馬爾可夫模型,簡稱HMM(Hidden Markov Model), 是一種基於概率的統計分析模型,用來描述一個系統隱性狀態的轉移和隱性狀態的表現概率。
本文適用於對HMM感興趣的入門讀者,為了讓文章更加通俗易懂,我會多闡述數學思想,盡可能的撇開公式,撇開推導。結合實際例子,爭取做到雅俗共賞,童叟無欺。沒有公式,就沒有傷害。
建議看一下吳軍博士的《數學之美》,里面有簡單的說明。然后看下HMM的三個計算問題和對應的解答,你會發現基本就是動態規划的思想。
本文非完全原創,部分內容來自互聯網,自己在此基礎上加入了個人的理解,如有侵權還請告知!
從擲骰子說起
假設我手里有三個不同的骰子:
- 第一個骰子是我們平常見的骰子(稱這個骰子為D6),6個面,每個面(1,2,3,4,5,6)出現的概率是1/6。
- 第二個骰子是個四面體(稱這個骰子為D4),每個面(1,2,3,4)出現的概率是1/4。
- 第三個骰子有八個面(稱這個骰子為D8),每個面(1,2,3,4,5,6,7,8)出現的概率是1/8。
現在開始擲骰子:
- 挑骰子:從三個骰子里挑一個(挑到每一個骰子的概率都是1/3)
- 擲骰子:將得到一個數字(1,2,3,4,5,6,7,8中的一個)
不停的重復上述過程,我們會得到一串數字,例如我們可能得到這么一串數字(擲骰子10次):
1 6 3 5 2 7 3 5 2 4
我們稱這串數字叫做可見狀態鏈。
在隱馬爾可夫模型中,不僅僅有這么一串可見狀態鏈,還有一串隱含狀態鏈。在這個例子里,這串隱含狀態鏈就是你用的骰子的序列。比如,隱含狀態鏈有可能是:
D6 D8 D8 D6 D4 D8 D6 D6 D4 D8
一般來說,HMM中說到的馬爾可夫鏈其實是指隱含狀態鏈,因為隱含狀態(骰子)之間存在轉換概率(transition probability)。
在這個例子里,D6的下一個狀態是D4,D6,D8的概率都是1/3。D4,D8的下一個狀態是D4,D6,D8的轉換概率也都一樣是1/3。我們其實可以隨意設定轉換概率的。比如,我們可以這樣定義:D6后面不能接D4,D6后面是D6的概率是0.9,是D8的概率是0.1。這樣就是一個新的HMM。
同樣的,盡管可見狀態之間沒有轉換概率,但是隱含狀態和可見狀態之間有一個概率叫做輸出概率(emission probability)。就我們的例子來說,六面骰(D6)產生1的輸出概率是1/6。產生2,3,4,5,6的概率也都是1/6。我們同樣可以對輸出概率進行其他定義。比如,我有一個被賭場動過手腳的六面骰子,擲出來是1的概率更大,是1/2,擲出來是2,3,4,5,6的概率是1/10。
其實對於HMM來說,如果提前知道所有隱含狀態之間的轉換概率和所有隱含狀態到所有可見狀態之間的輸出概率,做模擬是相當容易的。但在實際運用中,往往會缺失一部分信息:有時候你知道骰子有幾種,每種骰子是什么,但是不知道擲出來的骰子序列;有時候你知道骰子序列,剩下的什么都不知道。如何應用算法去估計這些缺失的信息,就成了一個很有研究價值的問題。這些算法我會在下面詳細講。
和HMM模型相關的算法主要分為三類,分別解決三種問題:
- 知道骰子有幾種(隱含狀態數量),每種骰子是什么(轉換概率),根據擲骰子擲出的結果(可見狀態鏈),我想知道每次擲出來的都是哪種骰子(隱含狀態鏈)。這個問題,在語音識別領域,叫做解碼問題。這個問題其實有兩種解法,會給出兩個不同的答案。每個答案都對,只不過這些答案的意義不一樣。第一種解法求最大似然狀態路徑,說通俗點,就是我求一串骰子序列,這串骰子序列產生觀測結果的概率最大。第二種解法,就不是求骰子序列了,而是求每次擲出的骰子分別是某種骰子的概率。比如說我看到結果后,我可以求得第一次擲骰子是D4的概率是0.5,D6的概率是0.3,D8的概率是0.2。第一種解法我會在下面說到,但是第二種解法我就不寫在這里了。
- 還是知道骰子有幾種(隱含狀態數量),每種骰子是什么(轉換概率),根據擲骰子擲出的結果(可見狀態鏈),我想知道擲出這個結果的概率。看似這個問題意義不大,因為你擲出來的結果很多時候都對應了一個比較大的概率。問這個問題的目的,其實是檢測觀察到的結果和已知的模型是否吻合。如果很多次結果都對應了比較小的概率,那么就說明我們已知的模型很有可能是錯的,有人偷偷把我們的骰子給換了。
- 知道骰子有幾種(隱含狀態數量),不知道每種骰子是什么(轉換概率),觀測到很多次擲骰子的結果(可見狀態鏈),我想反推出每種骰子是什么(轉換概率)。這個問題很重要,因為這是最常見的情況。很多時候我們只有可見結果,不知道HMM模型里的參數,我們需要從可見結果估計出這些參數,這是建模的一個必要步驟。
問題闡述完了,下面就開始說解法。
一個簡單問題
其實這個問題實用價值不高。由於對下面較難的問題有幫助,所以先在這里提一下。
知道骰子有幾種,每種骰子是什么,每次擲的都是什么骰子,給出一串數字序列,求產生這個序列的概率。
解法無非就是概率相乘:
破解骰子序列
這里我說的是第一種解法,解最大似然路徑問題。
舉例來說,我知道我有三個骰子,六面骰,四面骰,八面骰。我也知道我擲了十次的結果(1 6 3 5 2 7 3 5 2 4),我不知道每次用了哪種骰子,我想知道最有可能的骰子序列。
其實最簡單而暴力的方法就是窮舉所有可能的骰子序列,然后依照第零個問題的解法把每個序列對應的概率算出來。然后我們從里面把對應最大概率的序列挑出來就行了。如果馬爾可夫鏈不長,當然可行。如果長的話,窮舉的數量太大,就很難完成了。
另外一種很有名的算法叫做Viterbi algorithm. 要理解這個算法,我們先看幾個簡單的列子。
首先,如果我們只擲一次骰子:

看到結果為1,對應的最大概率骰子序列就是D4,因為D4產生1的概率是1/4,高於1/6和1/8.
把這個情況拓展,我們擲兩次骰子:

結果為1,6,這時問題變得復雜起來,我們要計算三個值,分別是第二個骰子是D6,D4,D8的最大概率。顯然,要取到最大概率,第一個骰子必須為D4。這時,第二個骰子取到D6的最大概率是
同樣的,我們可以計算第二個骰子是D4或D8時的最大概率。我們發現,第二個骰子取到D6的概率最大。而使這個概率最大時,第一個骰子為D4。所以最大概率骰子序列就是D4 D6。
繼續拓展,我們擲三次骰子:
同樣,我們計算第三個骰子分別是D6,D4,D8的最大概率。我們再次發現,要取到最大概率,第二個骰子必須為D6。這時,第三個骰子取到D4的最大概率是
同上,我們可以計算第三個骰子是D6或D8時的最大概率。我們發現,第三個骰子取到D4的概率最大。而使這個概率最大時,第二個骰子為D6,第一個骰子為D4。所以最大概率骰子序列就是D4 D6 D4。
寫到這里,大家應該看出點規律了,這其實就是概率DP問題(Dynamic Programming with Probability)。既然擲骰子一二三次可以算,擲多少次都可以以此類推。我們發現,我們要求最大概率骰子序列時要做這么幾件事情:
- 首先,不管序列多長,要從序列長度為1算起,算序列長度為1時取到每個骰子的最大概率。
- 然后,逐漸增加長度,每增加一次長度,重新算一遍在這個長度下最后一個位置取到每個骰子的最大概率。因為上一個長度下的取到每個骰子的最大概率都算過了,重新計算的話其實不難。當我們算到最后一位時,就知道最后一位是哪個骰子的概率最大了。
- 最后,我們把對應這個最大概率的序列從后往前推出來。
誰動了我的骰子
如果你懷疑自己的六面骰被賭場動過手腳了,有可能被換成另一種六面骰,這種六面骰擲出來是1的概率更大,是1/2,擲出來是2,3,4,5,6的概率是1/10。怎么辦?答案很簡單,算一算正常的三個骰子擲出一段序列的概率,再算一算不正常的六面骰和另外兩個正常骰子擲出這段序列的概率。如果前者比后者小,你就要小心了。比如說擲骰子的結果是:
要算用正常的三個骰子擲出這個結果的概率,其實就是將所有可能情況的概率進行加和計算。同樣,簡單而暴力的方法就是把窮舉所有的骰子序列,還是計算每個骰子序列對應的概率,但是這回,我們不挑最大值了,而是把所有算出來的概率相加,得到的總概率就是我們要求的結果。這個方法依然不能應用於太長的骰子序列(馬爾可夫鏈)。我們會應用一個和前一個問題類似的解法,只不過前一個問題關心的是概率最大值,這個問題關心的是概率之和。解決這個問題的算法叫做前向算法(forward algorithm)。首先,如果我們只擲一次骰子:
看到結果為1.產生這個結果的總概率可以按照如下計算,總概率為0.18:
把這個情況拓展,我們擲兩次骰子:
看到結果為1,6.產生這個結果的總概率可以按照如下計算,總概率為0.05:
繼續拓展,我們擲三次骰子:
看到結果為1,6,3.產生這個結果的總概率可以按照如下計算,總概率為0.03:
同樣的,我們一步一步的算,有多長算多長,再長的馬爾可夫鏈總能算出來的。用同樣的方法,也可以算出不正常的六面骰和另外兩個正常骰子擲出這段序列的概率,然后我們比較一下這兩個概率大小,就能知道你的骰子是不是被人換了。
HMM 的應用
以上例子是用HMM對擲骰子進行建模與分析。當然還有很多HMM經典的應用,能根據不同的應用需求,對問題進行建模。
但是使用HMM進行建模的問題,必須滿足以下條件:
- 隱性狀態的轉移必須滿足馬爾可夫性(狀態轉移的馬爾可夫性:一個狀態只與前一個狀態有關)
- 隱性狀態必須能夠大概被估計
在滿足條件的情況下,確定問題中的隱性狀態是什么,隱性狀態的表現可能又有哪些。
HMM適用的問題:真正的狀態(隱態)難以被估計,而狀態與狀態之間又存在聯系。
語音識別
語音識別問題就是將一段語音信號轉換為文字序列的過程。
在個問題里面,隱性狀態就是: 語音信號對應的文字序列。而顯性狀態就是: 語音信號。
MM模型的學習(Learning): 語音識別的模型學習和上文中通過觀察骰子序列建立起一個最有可能的模型不同。
語音識別的HMM模型學習有三個步驟:
- 統計文字的發音概率,建立隱性表現概率矩陣
- 統計字詞之間的轉換概率(不需要考慮語音,直接統計字詞之間的轉移概率即可)
- 語音模型的估計(Evaluation): 計算"是十四”,"四十四"等等的概率,比較得出最有可能出現的文字序列。
由此可見,其原理和上面的破解骰子序列是一樣的。
手寫識別
手寫識別(HandWriting Recognition)是指將在手寫設備上書寫時產生的有序軌跡信息化轉化為文字的過程。
原理和語音差不多,只不過手寫識別的過程是將字的圖像當成了顯性序列。
中文分詞
總所周知,在漢語中,詞與詞之間不存在分隔符,詞本身也缺乏明顯的形態標記(英文中,詞與詞之間用空格分隔,這是天然的分詞標記)。因此,中文信息處理的特有問題就是如何將漢語的字串分割為合理的詞語序。
例如,英文句子:you should go to kindergarten now. 天然的空格已然將詞分好,只需去除其中的介詞“to”即可;而“你現在應該去幼兒園了”這個句子表達同樣的意思卻沒有明顯的分隔符,中文分詞的目的是得到“你/現在/應該/去/幼兒園/了”。那么如何進行分詞呢?
主流的方法有三種:
- 第1類是基於語言學知識的規則方法,如:各種形態的最大匹配、最少切分方法。
- 第2類是基於大規模語料庫的機器學習方法,這是目前應用比較廣泛、效果較好的解決方案。用到的統計模型有N元語言模型、信道—噪聲模型、最大期望、HMM等。
- 第3類也是實際的分詞系統中用到的,即規則與統計等多類方法的綜合。
更多中文分詞相關介紹,參考我的另一篇博客:一周亂談 - 中文分詞
拼音輸入法
拼音輸入法,是一個估測拼音字母對應想要輸入的文字(隱性狀態)的過程(比如, ‘pingyin’ -> 拼音)。
很明顯,拼音輸入法的觀察序列就是用戶的輸入拼音,比如”wo shi zhong guo ren”,我們要推測出用戶想要輸入的是“我 是 中 國 人”,這是個很典型的隱馬爾科夫模型。
如上圖所示,我們根據給定的觀察對象O,獲得一個概率最大的序列S*。我們所知道的數據有:
- 所有觀察對象的值
- 隱藏序列的馬爾科夫模型概率,這是通過統計獲得的
- 隱藏狀態到觀察狀態的概率,比如 “晴天”(隱藏狀態) 到 “出去玩”(觀察狀態)的概率
我們要求的是S*各個狀態的連續概率最大的那個序列,和上面同理。
有關隱馬爾可夫模型的教學視頻,請參看coursera課程:Lecture 17 - 隱馬爾可夫模型
結語
隱馬爾可夫模型是可用於標注問題的統計學習的模型,描述由隱藏的馬爾可夫鏈隨機生成觀測序列的過程,屬於生成模型。
本文以一個例子為主線,用理論結合實際的方法講解了HMM的基本原理和三個基本問題,以及三個問題的求解方法。最后,綜述了一些HMM在人類的行為分析、網絡安全和信息抽取中的最新應用。
參考文獻
1. 數學之美 - 吳軍 - 隱馬爾科夫模型
2. 統計學習方法 - 李航 - 隱馬爾科夫模型
3. HMM學習最佳范例一:介紹 - 52nlp
4. HMM學習最佳范例二:生成模式 - 52nlp
5. POS - Stanford NLP