??首先抄下《統(tǒng)計(jì)學(xué)習(xí)方法》中HMM的定義和相關(guān)知識(shí)點(diǎn):隱馬爾科夫模型是關(guān)于時(shí)序的概率模型,描述由一個(gè)隱藏的馬爾科夫鏈隨機(jī)生成不可觀測的狀態(tài)隨機(jī)序列,再由各個(gè)狀態(tài)生成一個(gè)觀測而產(chǎn)生觀測隨機(jī)序列的過程。隱馬爾科夫模型由初始狀態(tài)概率向量、狀態(tài)轉(zhuǎn)移概率矩陣
和觀測概率矩陣
(也可稱為發(fā)射概率矩陣)組成。隱馬爾科夫模型做了兩個(gè)基本假設(shè)——一是任意時(shí)刻的隱狀態(tài)只與前一時(shí)刻的隱狀態(tài)有關(guān),二是任意時(shí)刻的觀測只與當(dāng)前時(shí)刻的隱狀態(tài)有關(guān)。隱馬爾科夫模型如下圖所示:

其中,
- 概率計(jì)算問題。給定模型參數(shù)
和觀測序列
,計(jì)算在模型
下觀測序列
出現(xiàn)的概率
- 學(xué)習(xí)問題。也就是模型的訓(xùn)練,已知觀測序列
,估計(jì)模型參數(shù)
- 預(yù)測問題。也稱為decode,給定觀測序列
,求最有可能的對(duì)應(yīng)的隱狀態(tài)
這3個(gè)問題都需要計(jì)算概率并進(jìn)行對(duì)比得到結(jié)果,當(dāng)數(shù)據(jù)很龐大的時(shí)候,算法的關(guān)鍵就是如何對(duì)概率的計(jì)算進(jìn)行優(yōu)化。首先,我們將模型中的隱狀態(tài)轉(zhuǎn)移在時(shí)間上展開,得到隱狀態(tài)的晶格圖,如下:

??對(duì)于概率預(yù)測問題,我們假設(shè)前向概率為
- 初值,
![]()
- 遞歸,對(duì)t=1,2,...,T-1有,
![]()
- 終止,
![]()
完整的后向算法也是分為3步:
- 初值,
![]()
- 遞歸,對(duì)t=T-1,T-2,...,1有,
![]()
- 終止,
![]()
??對(duì)于學(xué)習(xí)問題,可以分為兩種情況進(jìn)行計(jì)算。一種是已知隱狀態(tài),那么直接使用極大似然估計(jì)算法就可以得到;一種是不知隱狀態(tài),這種是常見情況,此時(shí)就沒法用極大似然估計(jì)算法了,似然函數(shù)是,但是
是未知的,
是沒法得到,所以也就沒法直接使用極大似然估計(jì)算法了。此時(shí)就應(yīng)該用EM算法,依靠EM算法的迭代訓(xùn)練達(dá)到一個(gè)比較好的效果。EM算法中不是對(duì)
直接最大似然估計(jì),而是最優(yōu)化完全數(shù)據(jù)的似然函數(shù)
,所以新似然函數(shù)可寫成
(這里的公式推導(dǎo)請看PRML的9.4節(jié)),然后就可以利用極大似然估計(jì)算法得到新的模型參數(shù)
,接著將
作為新一輪的
,并迭代訓(xùn)練模型,這樣就可以得到最終的模型參數(shù)
。這種方法稱為Baum-Welch算法。具體每一輪如何得到
,《統(tǒng)計(jì)學(xué)習(xí)方法》中181頁有著詳細(xì)的講解,這里就不抄上了。
??對(duì)于預(yù)測問題,也是分為兩種方法。一種是近似算法,計(jì)算公式為:但是這種算法預(yù)測的狀態(tài)序列可能有不發(fā)生的部分,即對(duì)某些
,
有
。分析一下,根據(jù)定義來看,
,
,
中考慮了1到t的連續(xù)性,
中考慮了t+1到T的連續(xù)性,但是它們都沒有考慮t和t+1之間的連續(xù)性。所以最好就是求一條完整的路徑,此時(shí)就可以采用第二種方法——維特比算法。維特比算法是采用了動(dòng)態(tài)規(guī)劃來求解最優(yōu)路徑,每個(gè)時(shí)刻都保存了每個(gè)隱狀態(tài)的最大可能概率和對(duì)應(yīng)的上一時(shí)刻的最優(yōu)隱狀態(tài)。詳細(xì)的維特比算法偽代碼如下圖:


??hmmlearn源碼中實(shí)現(xiàn)3種模型——GaussianHMM、GMMHMM和MultinomialHMM。其中GaussianHMM假設(shè)觀測狀態(tài)是連續(xù)型且滿足高斯分布,GMMHMM假設(shè)觀測狀態(tài)是連續(xù)型且滿足混合高斯分布,MultinomialGMM假設(shè)觀測狀態(tài)是離散型。
??在代碼實(shí)現(xiàn)上,hmmlearn在base.py中定義了基本HMM模型baseHMM類,類中定義了2個(gè)重要參數(shù)——self.startprob(HMM的初始狀態(tài),維度是[n_components])和self.transmat_(HMM的狀態(tài)轉(zhuǎn)移矩陣
,維度是[n_components, n_components]),基類中并沒有定義發(fā)射矩陣
,因?yàn)橹挥杏^測狀態(tài)是離散型的HMM模型才有發(fā)射矩陣(代碼在MultinomialGMM類中單獨(dú)定義了self.emissionprob_)。而HMM中的3個(gè)基本問題則對(duì)應(yīng)了類中的3個(gè)函數(shù):
- 概率計(jì)算問題,對(duì)應(yīng)了_baseHMM類中的score函數(shù)。代碼中利用前向算法來計(jì)算概率,核心部分如下:
for i, j in iter_from_X_lengths(X, lengths):
framelogprob = self._compute_log_likelihood(X[i:j])
logprobij, _fwdlattice = self._do_forward_pass(framelogprob)
logprob += logprobij
其中,_do_forward_pass函數(shù)實(shí)現(xiàn)了前向算法中的,代碼在https://github.com/hmmlearn/hmmlearn/blob/master/lib/hmmlearn/_hmmc.pyx中。具體核心代碼如下:
for t in range(1, n_samples):
for j in range(n_components):
for i in range(n_components):
work_buffer[i] = fwdlattice[t - 1, i] + log_transmat[i, j] # 由于取log操作,這里的*運(yùn)算變成了+運(yùn)算
fwdlattice[t, j] = _logsumexp(work_buffer) + framelogprob[t, j]
hmmlearn在計(jì)算概率時(shí)都對(duì)結(jié)果進(jìn)行了取log操作,是為了防止序列過長而導(dǎo)致計(jì)算過程中出現(xiàn)數(shù)值下溢的情況,這在PRML的13.2.4節(jié)中有提到。當(dāng)然,代碼中也是實(shí)現(xiàn)了后向算法,可見_do_backward_pass函數(shù):
for t in range(n_samples - 2, -1, -1):
for i in range(n_components):
for j in range(n_components):
work_buffer[j] = (log_transmat[i, j] + framelogprob[t + 1, j] + bwdlattice[t + 1, j])
bwdlattice[t, i] = _logsumexp(work_buffer)
- 學(xué)習(xí)問題,對(duì)應(yīng)了_baseHMM類中的fit函數(shù)。核心代碼如下:
for i, j in iter_from_X_lengths(X, lengths):
framelogprob = self._compute_log_likelihood(X[i:j])
logprob, fwdlattice = self._do_forward_pass(framelogprob)
curr_logprob += logprob
bwdlattice = self._do_backward_pass(framelogprob)
posteriors = self._compute_posteriors(fwdlattice, bwdlattice)
self._accumulate_sufficient_statistics(stats, X[i:j], framelogprob, posteriors, fwdlattice, bwdlattice)
其中,framelogprob就是發(fā)射概率(如果觀測狀態(tài)是離散型就是矩陣B,如果觀測狀態(tài)是連續(xù)型就是高斯對(duì)數(shù)密度),fwdlattice和bwdlattice分別是前向概率和后向概率
,posteriors就是
。
for i, j in iter_from_X_lengths(X, lengths):
logprobij, state_sequenceij = decoder(X[i:j])
logprob += logprobij
state_sequence[i:j] = state_sequenceij
其中最重要的維特比算法實(shí)現(xiàn)代碼部分如下:
for t in range(1, n_samples):
for i in range(n_components):
for j in range(n_components):
work_buffer[j] = (log_transmat[j, i] + viterbi_lattice[t - 1, j])
viterbi_lattice[t, i] = _max(work_buffer) + framelogprob[t, i]
其中,viterbi_lattice保存的就是每個(gè)中間時(shí)刻的隱狀態(tài)的最大累積概率。