基本要素
-
狀態 \(N\)個
-
狀態序列 \(S = s_1,s_2,...\)
-
觀測序列 \(O=O_1,O_2,...\)
-
\(\lambda(A,B,\pi)\)
- 狀態轉移概率 \(A = \{a_{ij}\}\)
- 發射概率 \(B = \{b_{ik}\}\)
- 初始概率分佈 \(\pi = \{\pi_i\}\)
-
觀測序列生成過程
- 初始狀態
- 選擇觀測
- 狀態轉移
- 返回step2
HMM三大問題
- 概率計算問題(評估問題)
給定觀測序列 \(O=O_1O_2...O_T\),模型 \(\lambda (A,B,\pi)\),計算 \(P(O|\lambda)\),即計算觀測序列的概率
- 解碼問題
給定觀測序列 \(O=O_1O_2...O_T\),模型 \(\lambda (A,B,\pi)\),找到對應的狀態序列 \(S\)
- 學習問題
給定觀測序列 \(O=O_1O_2...O_T\),找到模型引數 \(\lambda (A,B,\pi)\),以最大化 \(P(O|\lambda)\),
概率計算問題
給定模型 \(\lambda\) 和觀測序列 \(O\),如何計算\(P(O| \lambda)\)?
暴力列舉每一個可能的狀態序列 \(S\)
-
對每一個給定的狀態序列
\[P(O|S,\lambda) = \prod^T_{t=1} P(O_t|s_t,\lambda) =\prod^T_{t=1} b_{s_tO_t} \] -
一個狀態序列的產生概率
\[P(S|\lambda) = P(s_1)\prod^T_{t=2}P(s_t|s_{t-1})=\pi_1\prod^T_{t=2}a_{s_{t-1}s_t} \] -
聯合概率
\[P(O,S|\lambda) = P(S|\lambda)P(O|S,\lambda) =\pi_1\prod^T_{t=2}a_{s_{t-1}s_t}\prod^T_{t=1} b_{s_tO_t} \] -
考慮所有的狀態序列
\[P(O|\lambda)=\sum_S\pi_1b_{s_1O_1}\prod^T_{t=2}a_{s_{t-1}s_t}b_{s_tO_t} \]
\(O\) 可能由任意一個狀態得到,所以需要將每個狀態的可能性相加。
這樣做什麼問題?時間複雜度高達 \(O(2TN^T)\)。每個序列需要計算 \(2T\) 次,一共 \(N^T\) 個序列。
前向演算法
在時刻 \(t\),狀態為 \(i\) 時,前面的時刻觀測到 \(O_1,O_2, ..., O_t\) 的概率,記為 \(\alpha _i(t)\) :
當 \(t=1\) 時,輸出為 \(O_1\),假設有三個狀態,\(O_1\) 可能是任意一個狀態發出,即
當 \(t=2\) 時,輸出為 \(O_1O_2\) ,\(O_2\) 可能由任一個狀態發出,同時產生 \(O_2\) 對應的狀態可以由 \(t=1\) 時刻任意一個狀態轉移得到。假設 \(O_2\) 由狀態 1
發出,如下圖
同理可得 \(\alpha_2(2),\alpha_3(2)\)
所以
所以前向演算法過程如下:
step1:初始化 \(\alpha_i(1)= \pi_i*b_i(O_1)\)
step2:計算 \(\alpha_i(t) = (\sum^{N}_{j=1} \alpha_j(t-1)a_{ji})b_i(O_{t})\)
step3:\(P(O|\lambda) = \sum^N_{i=1}\alpha_i(T)\)
相比暴力法,時間複雜度降低了嗎?
當前時刻有 \(N\) 個狀態,每個狀態可能由前一時刻 \(N\) 個狀態中的任意一個轉移得到,所以單個時刻的時間複雜度為 \(O(N^2)\),總時間複雜度為 \(O(TN^2)\)
程式碼實現
例子:
假設從三個 袋子 {1,2,3}
中 取出 4 個球 O={red,white,red,white}
,模型引數\(\lambda = (A,B,\pi)\) 如下,計算序列O
出現的概率
#狀態 1 2 3
A = [[0.5,0.2,0.3],
[0.3,0.5,0.2],
[0.2,0.3,0.5]]
pi = [0.2,0.4,0.4]
# red white
B = [[0.5,0.5],
[0.4,0.6],
[0.7,0.3]]
step1:初始化 \(\alpha_i(1)= \pi_i*b_i(O_1)\)
step2:計算 \(\alpha_i(t) = (\sum^{N}_{j=1} \alpha_j(t-1)a_{ji})b_i(O_{t})\)
step3:\(P(O|\lambda) = \sum^N_{i=1}\alpha_i( T)\)
#前向演算法
def hmm_forward(A,B,pi,O):
T = len(O)
N = len(A[0])
#step1 初始化
alpha = [[0]*T for _ in range(N)]
for i in range(N):
alpha[i][0] = pi[i]*B[i][O[0]]
#step2 計算alpha(t)
for t in range(1,T):
for i in range(N):
temp = 0
for j in range(N):
temp += alpha[j][t-1]*A[j][i]
alpha[i][t] = temp*B[i][O[t]]
#step3
proba = 0
for i in range(N):
proba += alpha[i][-1]
return proba,alpha
A = [[0.5,0.2,0.3],[0.3,0.5,0.2],[0.2,0.3,0.5]]
B = [[0.5,0.5],[0.4,0.6],[0.7,0.3]]
pi = [0.2,0.4,0.4]
O = [0,1,0,1]
hmm_forward(A,B,pi,O) #結果為 0.06009
結果
後向演算法
在時刻 \(t\),狀態為 \(i\) 時,觀測到 \(O_{t+1},O_{t+2}, ..., O_T\) 的概率,記為 \(\beta _i(t)\) :
當 \(t=T\) 時,由於 \(T\) 時刻之後為空,沒有觀測,所以 \(\beta_i(t)=1\)
當 \(t = T-1\) 時,觀測 \(O_T\) ,\(O_T\) 可能由任意一個狀態產生
當 \(t=1\) 時,觀測為 \(O_{2},O_{3}, ..., O_T\)
所以
後向演算法過程如下:
step1:初始化 \(\beta_i(T)=1\)
step2:計算 \(\beta_i(t) = \sum^N_{j=1}a_{ij}b_j(O_{t+1})\beta_j(t+1)\)
step3:\(P(O|\lambda) = \sum^N_{i=1}\pi_ib_i(O_1)\beta_i(1)\)
- 時間複雜度 \(O(N^2T)\)
程式碼實現
還是上面的例子
#後向演算法
def hmm_backward(A,B,pi,O):
T = len(O)
N = len(A[0])
#step1 初始化
beta = [[0]*T for _ in range(N)]
for i in range(N):
beta[i][-1] = 1
#step2 計算beta(t)
for t in reversed(range(T-1)):
for i in range(N):
for j in range(N):
beta[i][t] += A[i][j]*B[j][O[t+1]]*beta[j][t+1]
#step3
proba = 0
for i in range(N):
proba += pi[i]*B[i][O[0]]*beta[i][0]
return proba,beta
A = [[0.5,0.2,0.3],[0.3,0.5,0.2],[0.2,0.3,0.5]]
B = [[0.5,0.5],[0.4,0.6],[0.7,0.3]]
pi = [0.2,0.4,0.4]
O = [0,1,0,1]
hmm_backward(A,B,pi,O) #結果為 0.06009
結果
前向-後向演算法
回顧前向、後向變數:
- \(a_i(t)\) 時刻 \(t\),狀態為 \(i\) ,觀測序列為 \(O_1,O_2, ..., O_t\) 的概率
- \(\beta_i(t)\) 時刻 \(t\),狀態為 \(i\) ,觀測序列為 \(O_{t+1},O_{t+2}, ..., O_T\) 的概率
即在給定的狀態序列中,\(t\) 時刻狀態為 \(i\) 的概率。
使用前後向演算法可以計算隱狀態,記 \(\gamma_i(t) = P(s_t=i|O,\lambda)\) 表示時刻 \(t\) 位於隱狀態 \(i\) 的概率
references:
[1] https://www.cs.sjsu.edu/~stamp/RUA/HMM.pdf
[2]https://www.cnblogs.com/fulcra/p/11065474.html
[3] https://www.cnblogs.com/sjjsxl/p/6285629.html
[4] https://blog.csdn.net/xueyingxue001/article/details/52396494