隱馬模型的評估問題即,在已知一個觀察序列O=O1O2...OT,和模型μ=(A,B,π}的條件下,觀察序列O的概率,即P(O|μ}
如果窮盡所有的狀態組合,即S1S1...S1, S1S1...S2, S1S1...S3, ..., S3S3...S3。這樣的話t1時刻有N個狀態,t2時刻有N個狀態...tT時刻有N個狀態,這樣的話一共有N*N*...*N= NT種組合,時間復雜度為O(NT),計算時,就會出現“指數爆炸”,當T很大時,簡直無法計算這個值。為解決這一問題,Baum提出了前向算法。
歸納過程
首先引入前向變量αt(i):在時間t時刻,HMM輸出序列為O1O2...OT,在第t時刻位於狀態si的概率。
當T=1時,輸出序列為O1,此時計算概率為P(O1|μ):假設有三個狀態(如下圖)1、2、3,輸出序列為O1,有三種可能一是狀態1發出,二是從狀態2發出,三是從狀態3發出。另外從狀態1發出觀察值O1得概率為b1(O1),從狀態2發出觀察值O1得概率為b2(O1),從狀態3發出觀察值O1得概率為b3(O1)。因此可以算出
P(O1|μ)= π1*b1(O1)+π2*b2(O1) + π3*b3(O1)= α1(1) + α1(2) + α1(3)
當T=2時,輸出序列為O1O2,此時計算概率為P(O1O2|μ):假設有三個狀態(如下圖)1、2、3,輸出序列為O1,有三種可能一是狀態1發出,二是從狀態2發出,三是從狀態3發出。另外從狀態1發出觀察值O2得概率為b1(O2),從狀態2發出觀察值O2得概率為b2(O2),從狀態3發出觀察值O2得概率為b3(O2)。
要是從狀態1發出觀察值O2,可能從第一時刻的1、2或3狀態裝換過來,要是從狀態1轉換過來,概率為α1(1)*a11*b1(O2),要是從狀態2轉換過來,概率為α1(2)*a21*b1(O2),要是從狀態3轉換過來,概率為α1(3)*a31*b1(O2),因此
P(O1O2,q2=s1|μ)= α1(1)*a11*b1(O2) + α1(2)*a21*b1(O2) + α1(3)*a31*b1(O2)=α2(1)
同理:P(O1O2,q2=s1|μ)= α1(1)*a12*b2(O2) + α1(2)*a22*b2(O2) + α1(3)*a32*b2(O2)=α2(2)
P(O1O2,q2=s1|μ)= α1(1)*a13*b1(O2) + α1(2)*a23*b3(O2) + α1(3)*a33*b3(O2)=α2(3)
所以:P(O1O2|μ)=P(O1O2,q2=s1|μ)+ P(O1O2,q2=s1|μ)+ P(O1O2,q2=s1|μ)
=α2(1) + α2(2) + α2(3)
以此類推。。。
前向算法
step1 初始化:α1(i) = πi*bi(O1), 1≤i≤N
step2 歸納計算:
step3 終結:
P(O|μ)=
時間復雜度
計算某時刻的某個狀態的前向變量需要看前一時刻的N個狀態,此時時間復雜度為O(N),每個時刻有N個狀態,此時時間復雜度為N*O(N)=O(N2),又有T個時刻,所以時間復雜度為T*O(N2)=O(N2T)。
程序例證
前向算法計算P(O|M):
step1:α1(1) =π1*b1(red)=0.2*0.5=0.1 α1(2)=π2*b2(red)==0.4*0.4= 0.16 α1(3)=π3*b3(red)==0.4*0.7=0.21
step2:α2(1)=α1(1)*a11*b1(white) + α1(2)*a21*b1(white) + α1(3)*a31*b1(white)
...
step3:P(O|M) = α3(1)+α3(2)+α3(3)
程序代碼
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { float a[3][3] = {{0.5,0.2,0.3},{0.3,0.5,0.2},{0.2,0.3,0.5}}; float b[3][2] = {{0.5,0.5},{0.4,0.6},{0.7,0.3}}; float alpha[4][3]; int i,j,k, count = 1; //output list int list[4] = {0,1,0,1}; //step1:Initialization alpha[0][0] = 0.2 * 0.5; alpha[0][1] = 0.4 * 0.4; alpha[0][2] = 0.4 * 0.7; //step2:iteration for (i=1; i<=3; i++) { for(j=0; j<=2; j++) { alpha[i][j] = 0; for(k=0; k<=2; k++) { alpha[i][j] += alpha[i-1][k] * a[k][j] * b[j][list[count]]; } } count += 1; } for (i=0; i<=3; i++) { for(j=0; j<=2; j++) { printf("a[%d][%d]=%f\n",i+1,j+1,alpha[i][j]); } } //step3:end printf("Forward:%f\n", alpha[3][0]+alpha[3][1]+alpha[3][2]); return 0; }
運行結果