隐马尔可夫模型的python实现

隐马尔可夫模型针对是序列的解码问题而建立的一个生成式模型,它基于这样一个假设,在序列第n个编码给定的条件下,序列第n+1个编码与之前的编码具有条件独立性,如图1所示,观测 o 1 , o 2 ,   , o T o_1,o_2,\cdots,o_T 是可以观察到的,而编码 y 1 , y 2 ,   , y T y_1,y_2,\cdots,y_T 是需要预测的,记所有可能观测的集合为 V = { v 1 , v 2 ,   , v M } V=\left\{v_1,v_2,\cdots,v_M\right\} ,所有可能编码的集合 Q = { q 1 , q 2 ,   , q N } Q=\left\{q_1,q_2,\cdots,q_N\right\} ,A是转移概率矩阵:
A = [ a i j ] N × N A=\left[a_{ij}\right]_{N\times N}
a i j = P ( y t + 1 = q j y t = q i ) a_{ij}=P(y_{t+1}=q_j|y_t=q_i)
B是观测概率矩阵:
B = [ b j ( k ) ] N × M B=\left[b_j(k)\right]_{N\times M}
b j ( k ) = P ( o t = v k i t = q j ) b_j(k)=P(o_t=v_k|i_t=q_j)
π \pi 是初始状态概率向量:
π = ( π i ) \pi=(\pi_i)
π i = P ( i 1 = q i ) \pi_i=P(i_1=q_i)
于是隐马尔可夫模型就可以用一个三元组 ( A , B , π ) (A,B,\pi) 来表示
在这里插入图片描述
当A,B和 π \pi 都确定下来的时候,就可以根据维特比算法预测观测的编码了,维特比算法实际上是用动态规划解马尔可夫模型的预测问题,其原理为:已知第T个编码为 i T i^*_T ,则求最大概率路径可以用递推的方法:
(1) δ t ( i ) = max y 1 , y 2 ,   , y t 1 P ( y t = q i , y t 1 ,   , y 1 , o t ,   , o 1 A , B , π ) \delta_t(i)=\max\limits_{y_1,y_2,\cdots,y_{t-1}}P(y_t=q_i,y_{t-1},\cdots,y_1,o_t,\cdots,o_1|A,B,\pi)\tag{1}
(2) δ t + 1 ( i ) = max 1 j n [ δ t ( j ) a j i ] b i ( o t + 1 ) \delta_{t+1}(i)=\max\limits_{1\le j\le n}[\delta_{t}(j)a_{ji}]b_i(o_{t+1})\tag{2}
(3) Φ t + 1 ( i ) = arg max 1 j n [ δ t ( j ) a j i ] \Phi_{t+1}(i)=\arg\max\limits_{1\le j\le n}[\delta_{t}(j)a_{ji}]\tag{3}
参数矩阵A,B和 π \pi 的学习使用 Baum-Welch \text{Baum-Welch} 算法,它是EM算法在HMM模型中的应用,参数的计算公式为:
(4) γ t ( i ) = P ( O , y t = q i A ( n 1 ) , B ( n 1 ) , π ( n 1 ) ) \gamma_t(i)=P(O,y_t=q_i|A^{(n-1)},B^{(n-1)},\pi^{(n-1)})\tag{4}
(5) ξ t ( i , j ) = P ( O , y t = q i , y t + 1 = q j A ( n 1 ) , B ( n 1 ) , π ( n 1 ) ) \xi_t(i,j)=P(O,y_t=q_i,y_{t+1}=q_j|A^{(n-1)},B^{(n-1)},\pi^{(n-1)})\tag{5}
(6) a i j ( n ) = i = 1 T 1 ξ i ( i , j ) t = 1 T 1 γ t ( i ) a^{(n)}_{ij}=\cfrac{\sum_{i=1}^{T-1}\xi_{i}(i,j)}{\sum_{t=1}^{T-1}\gamma_{t}(i)}\tag{6}
(7) b j ( n ) ( k ) = t = 1 , o t = v k T γ t ( j ) t = 1 T γ t ( j ) b^{(n)}_j(k)=\cfrac{\sum_{t=1,o_t=v_k}^T\gamma_t(j)}{\sum_{t=1}^T\gamma_t(j)}\tag{7}
(8) π i ( n ) = γ t ( i ) \pi^{(n)}_i=\gamma_t(i)\tag{8}
其中的条件概率 δ t ( i ) \delta_t(i) ξ t ( i , j ) \xi_t(i,j) 的计算可使用前向算法和后向算法
在这里插入图片描述
更多细节可以参考李航的《统计学习方法》
HMM中所涉及的算法我用python实现如下,保存为文件HidenMarcov.py

import numpy as np
class HMM:
    def __init__(self,A,B,pi,Q,V):
        self.A=A
        self.B=B
        self.pi=pi
        self.Q=Q
        self.V=V
    def predict(self,O):
        delta=np.zeros((len(self.Q),len(O)))
        phi=np.zeros((len(self.Q),len(O)))
        c=np.zeros((1,len(O))).tolist()[0]
        #初始化
        indexer=self.V.index(O[0])
        for i in range(len(self.Q)):
            delta[i,0]=self.pi[i]*self.B[i,indexer]
        #递推
        for t in range(1,len(O)):
            indexer=self.V.index(O[t])
            for i in range(len(self.Q)):
                big=[]
                for j in range(len(self.Q)):
                    big.append(delta[j,t-1]*self.A[j,i])
                phi[i,t]=big.index(max(big))
                delta[i,t]=max(big)*self.B[i,indexer]
        #终止
        end=delta[:,len(O)-1].tolist()
        P=max(end)
        c[len(O)-1]=end.index(P)+1
        #最优路径回溯
        for t in reversed(range(len(O)-1)):
            c[t]=phi[int(c[t+1])-1,t+1]+1
        return c
    #前向算法
    def forward(self,O):
        alpha=np.zeros((len(self.Q),len(O)))
        indexer=self.V.index(O[0])
        for i in range(len(self.Q)):
            alpha[i,0]=self.pi[i]*self.B[i,indexer]
        for t in range(1,len(O)):
           indexer=self.V.index(O[t])
           alpha[:,t]=np.dot(self.A.T,alpha[:,t-1])*self.B[:,indexer]
        return alpha
    #后向算法
    def backward(self,O):
        beta=np.ones((len(self.Q),len(O)))
        for t in reversed(range(len(O)-1)):
            indexer=self.V.index(O[t+1])
            beta[:,t]=np.dot(self.A,self.B[:,indexer]*beta[:,t+1])
        return beta
    #计算概率
    def prob(self,O,t):
        alpha=self.forward(O)
        beta=self.backward(O)
        p=alpha[:,len(O)-1].sum()
        gamma=alpha[:,t-1]*beta[:,t-1]
        gamma=gamma/p
        indexer=self.V.index(O[t])
        beta_s,alpha_s=np.meshgrid(self.B[:,indexer]*beta[:,t],alpha[:,t-1])
        ksi=alpha_s*self.A*beta_s
        ksi=ksi/p
        return gamma,ksi
    def output_prob(self,O,t,i,j=None):
        gamma,ksi=self.prob(O,t)
        if j==None:
            print(gamma[i-1])
        else:
            print(ksi[i-1,j-1])
    #Baum-Welch算法更新步骤
    def update(self,O):
        gamma,ksi=self.prob(O,1)
        pi_up=gamma
        A_up=np.zeros((len(self.Q),len(self.Q)))
        B_up=np.zeros((len(self.Q),len(self.V)))
        gamma_up=np.zeros((len(self.Q),1))
        for t in range(1,len(O)):
            gamma,ksi=self.prob(O,t)
            A_up=A_up+ksi
            gamma_up=gamma_up+gamma
            for k in range(len(self.V)):
                if O[t-1]==self.V[k]:
                    B_up=B[:,k]+gamma
        gamma,ksi=self.prob(O,len(O))
        for k in range(len(self.V)):
                if O[len(O)-1]==self.V[k]:
                    B_up=B[:,k]+gamma
        gamma_up_1=gamma_up+gamma
        one_1=np.ones((1,len(self.Q)))
        one_2=np.ones((1,len(self.V)))
        gamma_up=np.dot(gamma_up,one_1)
        gamma_up_1=np.dot(gamma_up_1,one_2)
        self.A=A_up/gamma_up
        self.B=B_up/gamma_up_1
        self.pi=pi_up

考虑李航书中的一个习题:考虑盒子和球组成的隐马尔可夫模型 λ = ( A , B , π ) , \lambda=(A,B,\pi), 其中,
A = [ 0.5 0.1 0.4 0.3 0.5 0.2 0.2 0.2 0.6 ] , A=\left[\begin{matrix} 0.5 & 0.1 & 0.4 \\ 0.3 & 0.5 & 0.2 \\ 0.2 & 0.2 & 0.6 \end{matrix} \right], B = [ 0.5 0.5 0.4 0.6 0.7 0.3 ] , B=\left[\begin{matrix} 0.5 & 0.5 \\ 0.4 & 0.6 \\ 0.7 & 0.3 \end{matrix} \right], C = ( 0.2 , 0.5 , 0.8 ) T C=(0.2,0.5,0.8)^T
O = (red,white,red,red,white,red,white,white) O=\text{(red,white,red,red,white,red,white,white)} ,计算概率 P ( y 4 = q 3 O , λ ) P(y_4=q_3|O,\lambda) , P ( y 4 = q 3 , y 5 = q 2 O , λ ) P(y_4=q_3,y_5=q_2|O,\lambda) ,预测 Y Y^*
我们利用这个习题来测试HidenMarcov.py中的程序:

import numpy as np
from HiddenMarcov import HMM
A,B,pi=np.array([[0.5,0.1,0.4],[0.3,0.5,0.2],[0.2,0.2,0.6]]),np.array([[0.5,0.5],[0.4,0.6],[0.7,0.3]]),[0.2,0.3,0.5]
O,Q,V=['r','w','r','r','w','r','w','w'],[1,2,3],['r','w']
a=HMM(A,B,pi,Q,V)
b=a.predict(O)
c,d=a.prob(O,4)
print(c)
print(d)
print('_ '*20)
a.output_prob(O,4,3)
a.output_prob(O,4,3,2)
print(b)

程序输出结果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43614688/article/details/87891014