隱馬爾可夫模型學習筆記(之二,學習算法)

隱馬爾可夫模型的學習,根據訓練數據是包括觀測序列和狀態序列還是隻有觀測序列,可以分別由監督學習與非監督學習實現。由於監督學習需要使用訓練數據,而人工標註訓練數據往往代價很高,有時就會利用非監督學習的方法,即Baum-Welch算法(也就是EM算法)。在介紹學習算法之前,先介紹一些概率和期望值的計算。這些計算會成爲Baum-Welch算法公式的基礎。

一些概率和期望值的計算

利用前向概率和後向概率,可以得到關於單個狀態和兩個狀態概率的計算公式。
1. 給定模型λ 和觀測O ,在時刻t 處於狀態qi 的概率。記爲

γt(i)=P(it=qi|O,λ)

先分解爲分數形式
(1)γt(i)=P(it=qi,O|λ)P(O|λ)

根據前向概率的定義可以做以下變換
αt(i)=P(o1,o2...ot,it=qt|λ)=P(it=qt|λ)P(o1,o2...ot|it=qt,λ)

後向概率的定義如下
βt(i)=P(ot+1,ot+2...,oT|it=qt,λ)

將這兩者相乘得到
(1)αt(i)βt(i)=P(it=qt|λ)P(o1,o2...ot|it=qt,λ)P(ot+1,ot+2...,oT|it=qt,λ)(2)=P(it=qt|λ)P(o1,o2...oT|it=qt,λ)(3)=P(it=qt|λ)P(O|it=qt,λ)(2)=P(it=qt,O|λ)

以上結果從兩者的定義上也很好理解。
對變量i 在範圍i=1,2,...N 上求和
(3)i=1NP(it=qt,O|λ)=P(O|λ)

將式(2),(3) 代入(1) 可以得到
(4)γt(i)=αt(i)βt(i)j=1Nαt(j)βt(j)

2. 給定模型λ 和觀測O ,在時刻t 處於狀態qi 且在時刻t+1 處於狀態qj 的概率。記爲
ξt(i,j)=P(it=qi,it+1=qj|O,λ)

通過前向後向概率計算:
ξt(i)=P(it=qi,it+1=qj,O|λ)P(O|λ)=P(it=qi,it+1=qj,O|λ)i=1Nj=1NP(it=qi,it+1=qj,O|λ)

分子可以用前向後向概率表示
P(it=qi,it+1=qj,O|λ)=αt(i)aijbj(ot+1)βt+1(j)

ξt(i) 可以表示爲
ξt(i)=αt(i)aijbj(ot+1)βt+1(j)i=1Nj=1Nαt(i)aijbj(ot+1)βt+1(j)

3. 將γt(i)ξt(i,j) 對各個時刻求和,可以得到一些有用的期望值。
(1) 觀測O 下,狀態i 出現的期望值
t=1Tγt(i)

將每一個時刻下,出現狀態i 的概率相加
(2) 觀測O 下,由狀態i 轉移的期望值
t=1T1γt(i)

能夠從狀態i 轉移的時刻是1,2...T1 ,比上一個求和公式少了時刻T
(3) 觀測O 下,由狀態i 轉移到狀態j 的期望值
t=1T1ξt(i,j)

Baum-Welch模型

參數估計公式

·推導的過程,尤其是拉格朗日對偶,我暫時還不十分理解,先直接給出訓練方法,公式和代碼。Baum-Welch算法(Baum-Welch algorithm),它是EM算法在隱馬爾可夫模型學習過程中的具體實現,由Baum和Welch提出。
(1)初始化
對n=0,選取aij0bj(k)0πi0 ,得到模型λ0=(aij0bj(k)0πi0)
(2)遞推。對n=1,2,...

aijn+1=t=1T1ξt(i,j)t=1T1γt(i)

bj(k)n+1=t=1,ot=vkTγt(j)t=1Tγt(j)

πin+1=γ1(i)

公式右端按照觀測O=(o1,o2,...oT) 和模型λn=(aijnbj(k)nπin) 代入計算
(3)終止,得到模型λn+1=(aijn+1bj(k)n+1πin+1)

Baum-Welch算法的Python實現

def baum_welch_train(self, observations, criterion=0.05):
    n_states = self.A.shape[0]
    n_samples = len(observations)

    done = False
    while not done:
        # alpha_t(i) = P(O_1 O_2 ... O_t, q_t = S_i | hmm)
        # Initialize alpha
        alpha = self._forward(observations)

        # beta_t(i) = P(O_t+1 O_t+2 ... O_T | q_t = S_i , hmm)
        # Initialize beta
        beta = self._backward(observations)

        xi = np.zeros((n_states,n_states,n_samples-1))
        for t in range(n_samples-1):
            denom = np.dot(np.dot(alpha[:,t].T, self.A) * self.B[:,observations[t+1]].T, beta[:,t+1])
            for i in range(n_states):
                numer = alpha[i,t] * self.A[i,:] * self.B[:,observations[t+1]].T * beta[:,t+1].T
                xi[i,:,t] = numer / denom

        # gamma_t(i) = P(q_t = S_i | O, hmm)
        gamma = np.sum(xi,axis=1)
        # Need final gamma element for new B
        prod =  (alpha[:,n_samples-1] * beta[:,n_samples-1]).reshape((-1,1))
        gamma = np.hstack((gamma,  prod / np.sum(prod))) #append one more to gamma!!!

        newpi = gamma[:,0]
        newA = np.sum(xi,2) / np.sum(gamma[:,:-1],axis=1).reshape((-1,1))
        newB = np.copy(self.B)

        num_levels = self.B.shape[1]
        sumgamma = np.sum(gamma,axis=1)
        for lev in range(num_levels):
            mask = observations == lev
            newB[:,lev] = np.sum(gamma[:,mask],axis=1) / sumgamma

        if np.max(abs(self.pi - newpi)) < criterion and \
                        np.max(abs(self.A - newA)) < criterion and \
                        np.max(abs(self.B - newB)) < criterion:
            done = 1

        self.A[:],self.B[:],self.pi[:] = newA,newB,newpi
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章