Python+numpy实现隐马尔可夫模型的前向后向算法

一、两种算法

大致弄一下吧,如果你已经看到了我的博客,说明你已经对这个算法有了一定的了解,我就不介绍了,大致看下李航老师书上的吧,很简单的,下面的算法我是直接从这篇博客上截的图刘建平Pinard十分感谢,我不想打公式,如有侵权,立马删除
前向算法

后向算法

二、一点点思考:为什么可以使用这种递归计算

除了里面具有动态规划的思想 我觉得可以这样实现还得益于概率计算的链式法则
也就是 P ( a , b , c , d ) = p ( a ) p ( b ∣ a ) p ( c ∣ a , b ) p ( d ∣ a , b , c ) P(a,b,c,d)=p(a)p(b|a)p(c|a,b)p(d|a,b,c) P(a,b,c,d)=p(a)p(ba)p(ca,b)p(da,b,c)
如果将这个公式和 P ( O ∣ λ ) P(O| \lambda ) P(Oλ)对应起来的话就是 P ( O ∣ λ ) = P ( o 1 , o 2 , . . . , o T ∣ λ ) = P ( o 1 ∣ λ ) ∗ P ( o 2 ∣ λ , o 1 ) ∗ . . . ∗ P ( o T ∣ λ , o 1 , o 2 , . . . , o T − 1 ) P(O|\lambda )=P(o_{1},o_{2},...,o_{T}|\lambda )=P(o_{1}|\lambda )*P(o_{2}|\lambda ,o_{1})*...*P(o_{T}|\lambda ,o_{1},o_{2},...,o_{T-1}) P(Oλ)=P(o1,o2,...,oTλ)=P(o1λ)P(o2λ,o1)...P(oTλ,o1,o2,...,oT1)

不知道这样想对不对,反正都要有点自己的理解吧,先分享给大家,如果有什么不对之处,欢迎指教,也算是抛砖引玉吧,嘻嘻

三、代码

我脚的最最最应该注意的是怎么把数学公式理解透彻,就是实现从理论到应用实现的小飞跃。比如上面的那两个算法步骤里面其实隐含矩阵的乘法和矩阵点乘。矩阵乘法就是行乘列加起来,矩阵的点乘是对应位置的元素相乘。当然如果不使用这些,使用循环计算也很简单,但是当数据较多的时候,是高效的矩阵计算方法会降低很多时间复杂度。numpy本就是矩阵计算库,里面封装的都是高效方法,所以,可以使用矩阵运算的一定不要使用循环来做。这句话也是我本科老师上Matlab课的时候说的,我印象特别森

import numpy as np

def Bw_Recurrent(A,B,start_p,list_observation):
    '''
    Parameters
    ----------
    A : np.float32 matrix
        是隐马模型的状态转移矩阵.
    B : np.float32 matrix
        观测概率矩阵.
    start_p : np.float32 matrix
        初始状态概率分布.
    list_observation:list
        用于计算特定序列的概率

    Returns
    -------
    float32 .
    是P(O|lamda)

    '''
    #后向的递归计算需要初始化一个全一的大小为N*1的矩阵
    N=np.shape(A)[0]
    p=np.ones((N,1),dtype=np.float32)
    
    T=len(list_observation)#观测序列的长度
    for i in range(T-1):
        #将观测矩阵里面的第list_observation[T-i-1]列取出来
        v=np.transpose(np.array([B[:,list_observation[T-i-1]]],dtype=np.float32))
        p=np.matmul(A,v*p)#这行代码既有矩阵乘法,也有矩阵点乘
        
    o1=np.transpose(np.array([B[:,list_observation[0]]],dtype=np.float32))
    return np.sum(start_p*o1*p)#注意里面的矩阵乘法是点乘操作,也就是将对应位置的元素相乘

def Fw_Recurrent(A,B,start_p,list_observation):
    """
    Parameters
    ----------
    A : np.float32 matrix
        是隐马模型的状态转移矩阵.
    B : np.float32 matrix
        观测概率矩阵.
    start_p : np.float32 matrix
        初始状态概率分布.
    list_observation:list
        用于计算特定序列的概率

    Returns
    -------
    float32 .
    是P(O|lamda)

    """
    o1=np.transpose(np.array([B[:,list_observation[0]]],dtype=np.float32))
    p=start_p*o1
    T=len(list_observation)#观测序列的长度
    for i in range(T-1):
        p=np.matmul(np.transpose(A),p)*np.transpose(np.array([B[:,list_observation[i+1]]],dtype=np.float32))
        #要注意上面这行代码里面的矩阵乘法和矩阵点乘
        print(p,'\n')
    return np.sum(p)
if __name__=='__main__':
    
    A=np.array([[0.5,0.2,0.3],
                [0.3,0.5,0.2],
                [0.2,0.3,0.5]],dtype=np.float32)
    B=np.array([[0.5,0.5],
                [0.4,0.6],
                [0.7,0.3]],dtype=np.float32)
    
    start_p=np.array([[0.2],[0.4],[0.4]],dtype=np.float32)
    list_observation=[0,1,0]

    s_fw=Fw_Recurrent(A,B,start_p,list_observation)  
    s_bw=Bw_Recurrent(A,B,start_p,list_observation)

上面这个小栗子使用的是李航老师书上的,计算结果没啥问题,和书上一样。

最后,希望可以帮助大家!!!嘿嘿,这个代码是我理解算法之后完全自己写的,可能有瑕疵,希望大佬指教,万分感激

还有哈,转载注明出处哈!!!爱你们

猜你喜欢

转载自blog.csdn.net/qq_41626059/article/details/109012260