前向算法(Forward Algorithm)
計算觀察序列的概率(Finding the probability of an observed sequence)
1.窮舉搜索( Exhaustive search for solution)
給定隱馬爾科夫模型,也就是在模型參數(pi, A, B)已知的情況下,我們想找到觀察序列的概率。還是考慮天氣這個例子,我們有一個用來描述天氣及與它密切相關的海藻溼度狀態的隱馬爾科夫模型(HMM),另外我們還有一個海藻的溼度狀態觀察序列。假設連續3天海藻溼度的觀察結果是(乾燥、溼潤、溼透)——而這三天每一天都可能是晴天、多雲或下雨,對於觀察序列以及隱藏的狀態,可以將其視爲網格:
網格中的每一列都顯示了可能的的天氣狀態,並且每一列中的每個狀態都與相鄰列中的每一個狀態相連。而其狀態間的轉移都由狀態轉移矩陣提供一個概率。在每一列下面都是某個時間點上的觀察狀態,給定任一個隱藏狀態所得到的觀察狀態的概率由混淆矩陣提供。
可以看出,一種計算觀察序列概率的方法是找到每一個可能的隱藏狀態,並且將這些隱藏狀態下的觀察序列概率相加。對於上面那個(天氣)例子,將有3^3
= 27種不同的天氣序列可能性,因此,觀察序列的概率是:
Pr(dry,damp,soggy | HMM) = Pr(dry,damp,soggy | sunny,sunny,sunny) + Pr(dry,damp,soggy | sunny,sunny ,cloudy) + Pr(dry,damp,soggy | sunny,sunny
,rainy) + . . . . Pr(dry,damp,soggy | rainy,rainy ,rainy)
用這種方式計算觀察序列概率極爲昂貴,特別對於大的模型或較長的序列,因此我們可以利用這些概率的時間不變性來減少問題的複雜度。
2.使用遞歸降低問題複雜度
給定一個隱馬爾科夫模型(HMM),我們將考慮遞歸地計算一個觀察序列的概率。我們首先定義局部概率(partial
probability),它是到達網格中的某個中間狀態時的概率。然後,我們將介紹如何在t=1和t=n(>1)時計算這些局部概率。
假設一個T-長觀察序列是:
2a.局部概率( 's)
考慮下面這個網格,它顯示的是天氣狀態及對於觀察序列乾燥,溼潤及溼透的一階狀態轉移情況:
我們可以將計算到達網格中某個中間狀態的概率作爲所有到達這個狀態的可能路徑的概率求和問題。
例如,t=2時位於“多雲”狀態的局部概率通過如下路徑計算得出:
我們定義t時刻位於狀態j的局部概率爲at(j)——這個局部概率計算如下:
t ( j )= Pr(觀察狀態 |
隱藏狀態j ) x Pr(t時刻所有指向j狀態的路徑)
對於最後的觀察狀態,其局部概率包括了通過所有可能的路徑到達這些狀態的概率——例如,對於上述網格,最終的局部概率通過如下路徑計算得出:
由此可見,對於這些最終局部概率求和等價於對於網格中所有可能的路徑概率求和,也就求出了給定隱馬爾科夫模型(HMM)後的觀察序列概率。
第3節給出了一個計算這些概率的動態示例。
2b.計算t=1時的局部概率
's
我們按如下公式計算局部概率:
t ( j )= Pr(觀察狀態 |
隱藏狀態j ) x Pr(t時刻所有指向j狀態的路徑)
特別當t=1時,沒有任何指向當前狀態的路徑。故t=1時位於當前狀態的概率是初始概率,即Pr(state|t=1)=P(state),因此,t=1時的局部概率等於當前狀態的初始概率乘以相關的觀察概率:
所以初始時刻狀態j的局部概率依賴於此狀態的初始概率及相應時刻我們所見的觀察概率。
2c.計算t>1時的局部概率
's
我們再次回顧局部概率的計算公式如下:
t ( j )= Pr(觀察狀態 |
隱藏狀態j ) x Pr(t時刻所有指向j狀態的路徑)
我們可以假設(遞歸地),乘號左邊項“Pr(觀察狀態 |
隱藏狀態j )”已經有了,現在考慮其右邊項“Pr(t時刻所有指向j狀態的路徑)”。
爲了計算到達某個狀態的所有路徑的概率,我們可以計算到達此狀態的每條路徑的概率並對它們求和,例如:
計算所需要的路徑數目隨着觀察序列的增加而指數級遞增,但是t-1時刻 's給出了所有到達此狀態的前一路徑概率,因此,我們可以通過t-1時刻的局部概率定義t時刻的
's,即:
故我們所計算的這個概率等於相應的觀察概率(亦即,t+1時在狀態j所觀察到的符號的概率)與該時刻到達此狀態的概率總和——這來自於上一步每一個局部概率的計算結果與相應的狀態轉移概率乘積後再相加——的乘積。
注意我們已經有了一個僅利用t時刻局部概率計算t+1時刻局部概率的表達式。
現在我們就可以遞歸地計算給定隱馬爾科夫模型(HMM)後一個觀察序列的概率了——即通過t=1時刻的局部概率
's計算t=2時刻的 's,通過t=2時刻的
's計算t=3時刻的 's等等直到t=T。給定隱馬爾科夫模型(HMM)的觀察序列的概率就等於t=T時刻的局部概率之和。
2d.降低計算複雜度
我們可以比較通過窮舉搜索(評估)和通過遞歸前向算法計算觀察序列概率的時間複雜度。
我們有一個長度爲T的觀察序列O以及一個含有n個隱藏狀態(列)的隱馬爾科夫模型l=(pi,A,B)。
窮舉搜索將包括計算所有可能的序列:
公式
對我們所觀察到的概率求和——注意其複雜度與T成指數級關係。相反的,使用前向算法我們可以利用上一步計算的信息,相應地,其時間複雜度與T成線性關係。
注:窮舉搜索的時間複雜度是2TN^T,前向算法的時間複雜度是N^2T,其中T指的是觀察序列長度,N指的是隱藏狀態數目。
3.總結
我們的目標是計算給定隱馬爾科夫模型HMM下的觀察序列的概率——Pr(observations
| )。
我們首先通過計算局部概率( 's)降低計算整個概率的複雜度,局部概率表示的是t時刻到達某個狀態s的概率。
t=1時,可以利用初始概率(來自於P向量)和觀察概率Pr(observation|state)(來自於混淆矩陣)計算局部概率;而t>1時的局部概率可以利用t-時的局部概率計算。
因此,這個問題是遞歸定義的,觀察序列的概率就是通過依次計算t=1,2,...,T時的局部概率,並且對於t=T時所有局部概率
's相加得到的。
注意,用這種方式計算觀察序列概率的時間複雜度遠遠小於計算所有序列的概率並對其相加(窮舉搜索)的時間複雜度。
我們使用前向算法計算T長觀察序列的概率:
其中y的每一個是觀察集合之一。局部(中間)概率( 's)是遞歸計算的,首先通過計算t=1時刻所有狀態的局部概率:
然後在每個時間點,t=2,...,T時,對於每個狀態的局部概率,由下式計算局部概率
:
也就是當前狀態相應的觀察概率與所有到達該狀態的路徑概率之積,其遞歸地利用了上一個時間點已經計算好的一些值。
最後,給定HMM, ,觀察序列的概率等於T時刻所有局部概率之和:
再重複說明一下,每一個局部概率(t > 2時)都由前一時刻的結果計算得出。
對於“天氣”那個例子,下面的圖表顯示了t
= 2爲狀態爲多雲時局部概率的計算過程。這是相應的觀察概率b與前一時刻的局部概率與狀態轉移概率a相乘後的總和再求積的結果:
(注:本圖及維特比算法4中的相似圖存在問題,具體請見文後評論,非常感謝讀者YaseenTA的指正)
總結(Summary)
我們使用前向算法來計算給定隱馬爾科夫模型(HMM)後的一個觀察序列的概率。它在計算中利用遞歸避免對網格所有路徑進行窮舉計算。
給定這種算法,可以直接用來確定對於已知的一個觀察序列,在一些隱馬爾科夫模型(HMMs)中哪一個HMM最好的描述了它——先用前向算法評估每一個(HMM),再選取其中概率最高的一個。
前向算法4
首先需要說明的是,本節不是這個系列的翻譯,而是作爲前向算法這一章的補充,希望能從實踐的角度來說明前向算法。除了用程序來解讀hmm的前向算法外,還希望將原文所舉例子的問題拿出來和大家探討。
文中所舉的程序來自於UMDHMM這個C語言版本的HMM工具包,具體見《幾種不同程序語言的HMM版本》。先說明一下UMDHMM這個包的基本情況,在linux環境下,進入umdhmm-v1.02目錄,“make
all”之後會產生4個可執行文件,分別是:
genseq:利用一個給定的隱馬爾科夫模型產生一個符號序列(Generates
a symbol sequence using the specified model sequence using the specified model)
testfor:利用前向算法計算log Prob(觀察序列|
HMM模型)(Computes log Prob(observation|model) using the Forward algorithm.)
testvit:對於給定的觀察符號序列及HMM,利用Viterbi算法生成最可能的隱藏狀態序列(Generates
the most like state sequence for a given symbol sequence, given the HMM, using Viterbi)
esthmm:對於給定的觀察符號序列,利用BaumWelch算法學習隱馬爾科夫模型HMM(Estimates
the HMM from a given symbol sequence using BaumWelch)。
這些可執行文件需要讀入有固定格式的HMM文件及觀察符號序列文件,格式要求及舉例如下:
HMM文件格式:
--------------------------------------------------------------------
M= number of symbols
N= number of states (列向量)
A:
a11 a12 ... a1N
a21 a22 ... a2N
. . . .
. . . .
. . . .
aN1 aN2 ... aNN
B:
b11 b12 ... b1M
b21 b22 ... b2M
. . . .
. . . .
. . . .
bN1 bN2 ... bNM
pi:
pi1 pi2 ... piN
--------------------------------------------------------------------
HMM文件舉例:
--------------------------------------------------------------------
M= 2
N= 3
A:
0.333 0.333 0.333
0.333 0.333 0.333
0.333 0.333 0.333
B:
0.5 0.5
0.75 0.25
0.25 0.75
pi:
0.333 0.333 0.333
--------------------------------------------------------------------
觀察序列文件格式:
--------------------------------------------------------------------
T=seqence length
o1 o2 o3 . . . oT
--------------------------------------------------------------------
觀察序列文件舉例:
--------------------------------------------------------------------
T= 10
1 1 1 1 2 1 2 2 2 2
--------------------------------------------------------------------
對於前向算法的測試程序testfor來說,運行:
testfor model.hmm(HMM文件)
obs.seq(觀察序列文件)
就可以得到觀察序列的概率結果的對數值,這裏我們在testfor.c的第58行對數結果的輸出下再加一行輸出:
fprintf(stdout, "prob(O| model) = %f\n", proba);
就可以輸出運用前向算法計算觀察序列所得到的概率值。至此,所有的準備工作已結束,接下來,我們將進入具體的程序解讀。
首先,需要定義HMM的數據結構,也就是HMM的五個基本要素,在UMDHMM中是如下定義的(在hmm.h中):
typedef struct
{
int N; /* 隱藏狀態數目;Q={1,2,...,N} */
int M; /* 觀察符號數目; V={1,2,...,M}*/
double **A; /* 狀態轉移矩陣A[1..N][1..N]. a[i][j]是從t時刻狀態i到t+1時刻狀態j的轉移概率
*/
double **B; /* 混淆矩陣B[1..N][1..M].b[j][k]在狀態j時觀察到符合k的概率。*/
double *pi; /* 初始向量pi[1..N],pi[i]是初始狀態概率分佈
*/
} HMM;
前向算法程序示例如下(在forward.c中):
/*
函數參數說明:
*phmm:已知的HMM模型;T:觀察符號序列長度;
*O:觀察序列;**alpha:局部概率;*pprob:最終的觀察概率
*/
void Forward(HMM *phmm, int T, int *O, double **alpha, double *pprob)
{
int i, j; /*
狀態索引 */
int t;
/* 時間索引 */
double sum; /*求局部概率時的中間值 */
/* 1.初始化:計算t=1時刻所有狀態的局部概率:
*/
for (i = 1; i <= phmm->N; i++)
alpha[1][i] = phmm->pi[i]* phmm->B[i][O[1]];
/* 2.歸納:遞歸計算每個時間點,t=2,...,T時的局部概率
*/
for (t = 1; t < T; t++)
{
for (j = 1; j <= phmm->N; j++)
{
sum = 0.0;
for (i = 1; i <= phmm->N; i++)
sum += alpha[t][i]* (phmm->A[i][j]);
alpha[t+1][j] = sum*(phmm->B[j][O[t+1]]);
}
}
/* 3.終止:觀察序列的概率等於T時刻所有局部概率之和*/
*pprob = 0.0;
for (i = 1; i <= phmm->N; i++)
*pprob += alpha[T][i];
}
原文地址:http://www.52nlp.cn/hmm-learn-best-practices-six-viterbi-algorithm-1