HMM模型:馬爾可夫鏈、隱馬爾可夫模型

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度學習實戰(不定時更新)


 

4.1 馬爾科夫鏈

在機器學習算法中,馬爾可夫鏈(Markov chain)是個很重要的概念。馬爾可夫鏈(Markov chain),又稱離散時間馬爾可夫鏈(discrete-time Markov chain),因俄國數學家安德烈·馬爾可夫(俄語:Андрей Андреевич Марков)得名。

1 簡介

馬爾科夫鏈即爲狀態空間中從一個狀態到另一個狀態轉換的隨機過程。

  • 該過程要求具備“無記憶”的性質:
    • 下一狀態的概率分佈只能由當前狀態決定,在時間序列中它前面的事件均與之無關。這種特定類型的“無記憶性”稱作馬爾可夫性質
  • 馬爾科夫鏈作爲實際過程的統計模型具有許多應用。

  • 在馬爾可夫鏈的每一步,系統根據概率分佈,可以從一個狀態變到另一個狀態,也可以保持當前狀態。

  • 狀態的改變叫做轉移,與不同的狀態改變相關的概率叫做轉移概率。
  • 馬爾可夫鏈的數學表示爲:

  • 既然某一時刻狀態轉移的概率只依賴前一個狀態,那麼只要求出系統中任意兩個狀態之間的轉移概率,這個馬爾科夫鏈的模型就定了。

2 經典舉例

下圖中的馬爾科夫鏈是用來表示股市模型,共有三種狀態:牛市(Bull market), 熊市(Bear market)和橫盤(Stagnant market)。

每一個狀態都以一定的概率轉化到下一個狀態。比如,牛市以0.025的概率轉化到橫盤的狀態。

  • 這個狀態概率轉化圖可以以矩陣的形式表示。
  • 如果我們定義矩陣陣P某一位置P(i, j)的值爲P(j|i),即從狀態i變爲狀態j的概率。
  • 另外定義牛市、熊市、橫盤的狀態分別爲0、1、2,這樣我們得到了馬爾科夫鏈模型的狀態轉移矩陣爲:

當這個狀態轉移矩陣P確定以後,整個股市模型就已經確定!

3 小結

  • 馬爾科夫鏈即爲
    • 狀態空間中從一個狀態到另一個狀態轉換的隨機過程。
    • 該過程要求具備“無記憶”的性質:
      • 下一狀態的概率分佈只能由當前狀態決定,在時間序列中它前面的事件均與之無關

4.2 HMM簡介

隱馬爾可夫模型(Hidden Markov Model,HMM)是統計模型,它用來描述一個含有隱含未知參數的馬爾可夫過程。

其難點是從可觀察的參數中確定該過程的隱含參數。然後利用這些參數來作進一步的分析,例如模式識別。

1 簡單案例

下面我們一起用一個簡單的例子來闡述:

  • 假設我手裏有三個不同的骰子。
    • 第一個骰子是我們平常見的骰子(稱這個骰子爲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中的一個。不停的重複上述過程,我們會得到一串數字,每個數字都是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模型時候呢,往往是缺失了一部分信息的。

  • 有時候你知道骰子有幾種,每種骰子是什麼,但是不知道擲出來的骰子序列;
  • 有時候你只是看到了很多次擲骰子的結果,剩下的什麼都不知道。

如果應用算法去估計這些缺失的信息,就成了一個很重要的問題。這些算法我會在後面詳細講。

2 案例進階

2.1 問題闡述

和HMM模型相關的算法主要分爲三類,分別解決三種問題:

1)知道骰子有幾種(隱含狀態數量),每種骰子是什麼(轉換概率),根據擲骰子擲出的結果(可見狀態鏈),我想知道每次擲出來的都是哪種骰子(隱含狀態鏈)。

  • 這個問題呢,在語音識別領域呢,叫做解碼問題。
  • 這個問題其實有兩種解法,會給出兩個不同的答案。每個答案都對,只不過這些答案的意義不一樣。
    • 第一種解法求最大似然狀態路徑,說通俗點呢,就是我求一串骰子序列,這串骰子序列產生觀測結果的概率最大。
    • 第二種解法呢,就不是求一組骰子序列了,而是求每次擲出的骰子分別是某種骰子的概率。比如說我看到結果後,我可以求得第一次擲骰子是D4的概率是0.5,D6的概率是0.3,D8的概率是0.2。

2)還是知道骰子有幾種(隱含狀態數量),每種骰子是什麼(轉換概率),根據擲骰子擲出的結果(可見狀態鏈),我想知道擲出這個結果的概率。

  • 看似這個問題意義不大,因爲你擲出來的結果很多時候都對應了一個比較大的概率。
  • 問這個問題的目的呢,其實是檢測觀察到的結果和已知的模型是否吻合。
  • 如果很多次結果都對應了比較小的概率,那麼就說明我們已知的模型很有可能是錯的,有人偷偷把我們的骰子給換了。

3)知道骰子有幾種(隱含狀態數量),不知道每種骰子是什麼(轉換概率),觀測到很多次擲骰子的結果(可見狀態鏈),我想反推出每種骰子是什麼(轉換概率)。

  • 這個問題很重要,因爲這是最常見的情況。
  • 很多時候我們只有可見結果,不知道HMM模型裏的參數,我們需要從可見結果估計出這些參數,這是建模的一個必要步驟。

2.2 問題解決

2.2.1 一個簡單問題【對應問題二】

其實這個問題實用價值不高。由於對下面較難的問題有幫助,所以先在這裏提一下。

  • 知道骰子有幾種,每種骰子是什麼,每次擲的都是什麼骰子,根據擲骰子擲出的結果,求產生這個結果的概率。

2.2.2 看見不可見的,破解骰子序列【對應問題一】

這裏我說的是第一種解法,解最大似然路徑問題。

舉例來說,我知道我有三個骰子,六面骰,四面骰,八面骰。我也知道我擲了十次的結果

(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。


寫到這裏,大家應該看出點規律了。既然擲骰子一、二、三次可以算,擲多少次都可以以此類推。

我們發現,我們要求最大概率骰子序列時要做這麼幾件事情。

  • 首先,不管序列多長,要從序列長度爲1算起,算序列長度爲1時取到每個骰子的最大概率。
  • 然後,逐漸增加長度,每增加一次長度,重新算一遍在這個長度下最後一個位置取到每個骰子的最大概率。因爲上一個長度下的取到每個骰子的最大概率都算過了,重新計算的話其實不難。
  • 當我們算到最後一位時,就知道最後一位是哪個骰子的概率最大了。然後,我們要把對應這個最大概率的序列從後往前推出來。

2.2.3 誰動了我的骰子?【對應問題三】

比如說你懷疑自己的六面骰被賭場動過手腳了,有可能被換成另一種六面骰,這種六面骰擲出來是1的概率更大,是1/2,擲出來是2,3,4,5,6的概率是1/10。你怎麼辦麼?

  • 答案很簡單,算一算正常的三個骰子擲出一段序列的概率,再算一算不正常的六面骰和另外兩個正常骰子擲出這段序列的概率。如果前者比後者小,你就要小心了。

比如說擲骰子的結果是:

要算用正常的三個骰子擲出這個結果的概率,其實就是將所有可能情況的概率進行加和計算。

同樣,簡單而暴力的方法就是把窮舉所有的骰子序列,還是計算每個骰子序列對應的概率,但是這回,我們不挑最大值了,而是把所有算出來的概率相加,得到的總概率就是我們要求的結果。這個方法依然不能應用於太長的骰子序列(馬爾可夫鏈)。 我們會應用一個和前一個問題類似的解法,只不過前一個問題關心的是概率最大值,這個問題關心的是概率之和。解決這個問題的算法叫做前向算法(forward algorithm)。

首先,如果我們只擲一次骰子:

看到結果爲1.產生這個結果的總概率可以按照如下計算,總概率爲0.18:

同樣的,我們一步一步的算,有多長算多長,再長的馬爾可夫鏈總能算出來的。

用同樣的方法,也可以算出不正常的六面骰和另外兩個正常骰子擲出這段序列的概率,然後我們比較一下這兩個概率大小,就能知道你的骰子是不是被人換了。

3 小結

  • 隱馬爾可夫模型(Hidden Markov Model,HMM)是統計模型,它用來描述一個含有隱含未知參數的馬爾可夫過程。
  • 常見術語
    • 可見狀態鏈
    • 隱含狀態鏈
    • 轉換概率
    • 輸出概率

4.3 HMM模型基礎

1 什麼樣的問題需要HMM模型

首先我們來看看什麼樣的問題解決可以用HMM模型。使用HMM模型時我們的問題一般有這兩個特徵:

  • 1)我們的問題是基於序列的,比如時間序列,或者狀態序列。
  • 2)我們的問題中有兩類數據,
    • 一類序列數據是可以觀測到的,即觀測序列
    • 而另一類數據是不能觀察到的,即隱藏狀態序列,簡稱狀態序列

有了這兩個特徵,那麼這個問題一般可以用HMM模型來嘗試解決。這樣的問題在實際生活中是很多的。

  • 比如:我現在給大家寫課件,我在鍵盤上敲出來的一系列字符就是觀測序列,而我實際想寫的一段話就是隱藏狀態序列,輸入法的任務就是從敲入的一系列字符儘可能的猜測我要寫的一段話,並把最可能的詞語放在最前面讓我選擇,這就可以看做一個HMM模型了。
  • 再舉一個,假如我上課講課,我發出的一串連續的聲音就是觀測序列,而我實際要表達的一段話就是隱藏狀態序列,你大腦的任務,就是從這一串連續的聲音中判斷出我最可能要表達的話的內容。

從這些例子中,我們可以發現,HMM模型可以無處不在。但是上面的描述還不精確,下面我們用精確的數學符號來表述我們的HMM模型。

2 HMM模型的定義

對於HMM模型,首先我們假設Q是所有可能的隱藏狀態的集合,V是所有可能的觀測狀態的集合,即:

3 一個HMM模型實例

下面我們用一個簡單的實例來描述上面抽象出的HMM模型。這是一個盒子與球的模型。

例子來源於李航的《統計學習方法》。

假設我們有3個盒子,每個盒子裏都有紅色和白色兩種球,這三個盒子裏球的數量分別是:

盒子 1 2 3
紅球數 5 4 7
白球數 5 6 3

按照下面的方法從盒子裏抽球,開始的時候,

  • 從第一個盒子抽球的概率是0.2,
  • 從第二個盒子抽球的概率是0.4,
  • 從第三個盒子抽球的概率是0.4。

以這個概率抽一次球后,將球放回。

然後從當前盒子轉移到下一個盒子進行抽球。規則是:

  • 如果當前抽球的盒子是第一個盒子,則以0.5的概率仍然留在第一個盒子繼續抽球,以0.2的概率去第二個盒子抽球,以0.3的概率去第三個盒子抽球。
  • 如果當前抽球的盒子是第二個盒子,則以0.5的概率仍然留在第二個盒子繼續抽球,以0.3的概率去第一個盒子抽球,以0.2的概率去第三個盒子抽球。
  • 如果當前抽球的盒子是第三個盒子,則以0.5的概率仍然留在第三個盒子繼續抽球,以0.2的概率去第一個盒子抽球,以0.3的概率去第二個盒子抽球。

4 HMM觀測序列的生成

5 HMM模型的三個基本問題

3 小結

  • 什麼樣的問題可以用HMM模型解決
    • 基於序列的,比如時間序列;
    • 問題中包含兩類數據,一類是可以觀測到的觀測序列;另一類是不能觀察到的隱藏狀態序列。
  • HMM模型的兩個重要假設
    • 其次馬爾科夫鏈假設
    • 觀測獨立性假設
  • HMM模型的三個基本問題
    • 評估觀察序列概率—— 前向後向的概率計算
    • 預測問題,也稱爲解碼問題 ——維特比(Viterbi)算法
    • 模型參數學習問題 —— 鮑姆-韋爾奇(Baum-Welch)算法

4.4 前向後向算法評估觀察序列概率

本節我們就關注HMM第一個基本問題的解決方法,即已知模型和觀測序列,求觀測序列出現的概率。

1 回顧HMM問題一:求觀測序列的概率

2 用前向算法求HMM觀測序列的概率

前向後向算法是前向算法和後向算法的統稱,這兩個算法都可以用來求HMM觀測序列的概率。我們先來看看前向算法是如何求解這個問題的。

2.1 流程梳理

前向算法本質上屬於動態規劃的算法,也就是我們要通過找到局部狀態遞推的公式,這樣一步步的從子問題的最優解拓展到整個問題的最優解。

2.2 算法總結。

3 HMM前向算法求解實例

4 用後向算法求HMM觀測序列的概率

4.1 流程梳理

熟悉了用前向算法求HMM觀測序列的概率,現在我們再來看看怎麼用後向算法求HMM觀測序列的概率。

後向算法和前向算法非常類似,都是用的動態規劃,唯一的區別是選擇的局部狀態不同,後向算法用的是“後向概率”。

4.2 後向算法流程

4.5 維特比算法解碼隱藏狀態序列

在本篇我們會討論維特比算法解碼隱藏狀態序列,即給定模型和觀測序列,求給定觀測序列條件下,最可能出現的對應的隱藏狀態序列。

HMM模型的解碼問題最常用的算法是維特比算法,當然也有其他的算法可以求解這個問題。

同時維特比算法是一個通用的求序列最短路徑的動態規劃算法,也可以用於很多其他問題。

1 HMM最可能隱藏狀態序列求解概述

近似算法很簡單,但是卻不能保證預測的狀態序列整體是最可能的狀態序列,因爲預測的狀態序列中某些相鄰的隱藏狀態可能存在轉移概率爲0的情況。

維特比算法可以將HMM的狀態序列作爲一個整體來考慮,避免近似算法的問題,下面我們來看看維特比算法進行HMM解碼的方法。

2 維特比算法概述

維特比算法是一個通用的解碼算法,是基於動態規劃的求序列最短路徑的方法。

既然是動態規劃算法,那麼就需要找到合適的局部狀態,以及局部狀態的遞推公式。在HMM中,維特比算法定義了兩個局部狀態用於遞推。

3 維特比算法流程總結

4 HMM維特比算法求解實例

 

4.6 鮑姆-韋爾奇算法簡介

2 鮑姆-韋爾奇算法原理

鮑姆-韋爾奇算法原理既然使用的就是EM算法的原理,


4.7 HMM模型API介紹

1 API的安裝:

官網鏈接:https://hmmlearn.readthedocs.io/en/latest/

pip3 install hmmlearn

2 hmmlearn介紹

hmmlearn實現了三種HMM模型類,按照觀測狀態是連續狀態還是離散狀態,可以分爲兩類。

GaussianHMM和GMMHMM是連續觀測狀態的HMM模型,而MultinomialHMM是離散觀測狀態的模型,也是我們在HMM原理系列篇裏面使用的模型。

在這裏主要介紹我們前面一直講的關於離散狀態的MultinomialHMM模型。

對於MultinomialHMM的模型,使用比較簡單,裏面有幾個常用的參數:

3 MultinomialHMM實例

下面我們用我們在前面講的關於球的那個例子使用MultinomialHMM跑一遍。

import numpy as np
from hmmlearn import hmm
# 設定隱藏狀態的集合
states = ["box 1", "box 2", "box3"]
n_states = len(states)

# 設定觀察狀態的集合
observations = ["red", "white"]
n_observations = len(observations)

# 設定初始狀態分佈
start_probability = np.array([0.2, 0.4, 0.4])

# 設定狀態轉移概率分佈矩陣
transition_probability = np.array([
  [0.5, 0.2, 0.3],
  [0.3, 0.5, 0.2],
  [0.2, 0.3, 0.5]
])

# 設定觀測狀態概率矩陣
emission_probability = np.array([
  [0.5, 0.5],
  [0.4, 0.6],
  [0.7, 0.3]
])
# 設定模型參數
model = hmm.MultinomialHMM(n_components=n_states)
model.startprob_=start_probability  # 初始狀態分佈
model.transmat_=transition_probability  # 狀態轉移概率分佈矩陣
model.emissionprob_=emission_probability  # 觀測狀態概率矩陣

現在我們來跑一跑HMM問題三維特比算法的解碼過程,使用和之前一樣的觀測序列來解碼,代碼如下:

seen = np.array([[0,1,0]]).T  # 設定觀測序列
box = model.predict(seen)

print("球的觀測順序爲:\n", ", ".join(map(lambda x: observations[x], seen.flatten())))
# 注意:需要使用flatten方法,把seen從二維變成一維
print("最可能的隱藏狀態序列爲:\n", ", ".join(map(lambda x: states[x], box)))

我們再來看看求HMM問題一的觀測序列的概率的問題,代碼如下:

print(model.score(seen))
# 輸出結果是:-2.03854530992

要注意的是score函數返回的是以自然對數爲底的對數概率值,我們在HMM問題一中手動計算的結果是未取對數的原始概率是0.13022。對比一下:

import math

math.exp(-2.038545309915233)
# ln0.13022≈−2.0385
# 輸出結果是:0.13021800000000003

In [1]:

import numpy as np

from hmmlearn import hmm

In [2]:

# 設定隱藏狀態的集合
states = ["box 1", "box 2", "box3"]
n_states = len(states)

# 設定觀察狀態的集合
observations = ["red", "white"]
n_observations = len(observations)

# 設定初始狀態分佈
start_probability = np.array([0.2, 0.4, 0.4])

# 設定狀態轉移概率分佈矩陣
transition_probability = np.array([
  [0.5, 0.2, 0.3],
  [0.3, 0.5, 0.2],
  [0.2, 0.3, 0.5]
])

# 設定觀測狀態概率矩陣
emission_probability = np.array([
  [0.5, 0.5],
  [0.4, 0.6],
  [0.7, 0.3]
])

In [4]:

# 設定模型參數
model = hmm.MultinomialHMM(n_components=n_states)
# 設定初始狀態分佈
model.startprob_ = start_probability
# 設定狀態轉移概率矩陣
model.transmat_ = transition_probability
# 設定觀測狀態概率矩陣
model.emissionprob_ = emission_probability

In [5]:

# 設定觀測序列
seen = np.array([[0, 1, 0]]).T

In [6]:

seen

Out[6]:

array([[0],
       [1],
       [0]])

In [10]:

observations

Out[10]:

['red', 'white']

In [12]:

seen.flatten()

Out[12]:

array([0, 1, 0])

In [13]:

",".join(map(lambda x: observations[x], seen.flatten()))

Out[13]:

'red,white,red'

In [14]:

print("球的觀測順序爲:\n", ",".join(map(lambda x: observations[x], seen.flatten())))

球的觀測順序爲:
 red,white,red

In [15]:

# 維特比 - 模型訓練

box = model.predict(seen)

In [16]:

box

Out[16]:

array([2, 2, 2])

In [17]:

print("盒子最可能的隱藏狀態順序爲:\n", ",".join(map(lambda x: states[x], box)))

盒子最可能的隱藏狀態順序爲:
 box3,box3,box3

In [18]:

# 觀測序列的概率的問題

model.score(seen)

Out[18]:

-2.038545309915233

In [19]:

import math

math.exp(-2.038545309915233)

Out[19]:

0.13021800000000003

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章