HMM-前向后向算法理解与实现(python)

HMM-前向后向算法理解与实现(python)
HMM-维特比算法理解与实现(python)

基本要素

  • 状态 N

  • 状态序列 S=s1,s2,...

  • 观测序列 O=O1,O2,...

  • λ(A,B,π)

    • 状态转移概率 A={aij}
    • 发射概率 B={bik}
    • 初始概率分布 π={πi}
  • 观测序列生成过程

    • 初始状态
    • 选择观测
    • 状态转移
    • 返回step2

HMM三大问题

  • 概率计算问题(评估问题)

给定观测序列 O=O1O2...OT,模型 λ(A,B,π),计算 P(O|λ),即计算观测序列的概率

  • 解码问题

给定观测序列 O=O1O2...OT,模型 λ(A,B,π),找到对应的状态序列 S

  • 学习问题

给定观测序列 O=O1O2...OT,找到模型参数 λ(A,B,π),以最大化 P(O|λ)

概率计算问题

给定模型 λ 和观测序列 O,如何计算P(O|λ)

暴力枚举每一个可能的状态序列 S

  • 对每一个给定的状态序列

    P(O|S,λ)=t=1TP(Ot|st,λ)=t=1TbstOt

  • 一个状态序列的产生概率

    P(S|λ)=P(s1)t=2TP(st|st1)=π1t=2Tast1st

  • 联合概率

    P(O,S|λ)=P(S|λ)P(O|S,λ)=π1t=2Tast1stt=1TbstOt

  • 考虑所有的状态序列

    P(O|λ)=Sπ1bs1O1t=2Tast1stbstOt

O 可能由任意一个状态得到,所以需要将每个状态的可能性相加。

这样做什么问题?时间复杂度高达 O(2TNT)。每个序列需要计算 2T 次,一共 NT 个序列。

前向算法

在时刻 t,状态为 i 时,前面的时刻观测到 O1,O2,...,Ot 的概率,记为 αi(t)

αi(t)=P(O1,O2,Ot,st=i|λ)

t=1 时,输出为 O1,假设有三个状态,O1 可能是任意一个状态发出,即

P(O1|λ)=π1b1(O1)+π2b2(O1)+π2b3(O1)=α1(1)+α2(1)+α3(1)

image-20200511202908264

t=2 时,输出为 O1O2O2 可能由任一个状态发出,同时产生 O2 对应的状态可以由 t=1 时刻任意一个状态转移得到。假设 O2 由状态 1 发出,如下图

image-20200511203749699

P(O1O2,s2=q1|λ)=π1b1(O1)a11b1(O2)+π2b2(O1)a21b1(O2)+π2b3(O1)a31b1(O2)=α1(1)a11b1(O2)+α2(1)a21b1(O2)+α3(1)a31b1(O2)=α1(2)

同理可得 α2(2),α3(2)

α2(2)=P(O1O2,s2=q2|λ)=α1(1)a12b2(O2)+α2(1)a22b2(O2)+α3(1)a32b2(O2)α3(2)=P(O1O2,s2=q3|λ)=α1(1)a13b3(O2)+α2(1)a23b3(O2)+α3(1)a33b3(O2)

所以

P(O1O2|λ)=P(O1O2,s2=q1|λ)+P(O1O2,s2=q2|λ)+P(O1O2,s2=q3|λ)=α1(2)+α2(2)+α3(2)

所以前向算法过程如下:

​ step1:初始化 αi(1)=πibi(O1)

​ step2:计算 αi(t)=(j=1Nαj(t1)aji)bi(Ot)

​ step3:P(O|λ)=i=1Nαi(T)

相比暴力法,时间复杂度降低了吗?

当前时刻有 N 个状态,每个状态可能由前一时刻 N 个状态中的任意一个转移得到,所以单个时刻的时间复杂度为 O(N2),总时间复杂度O(TN2)

代码实现

例子:

假设从三个 袋子 {1,2,3}中 取出 4 个球 O={red,white,red,white},模型参数λ=(A,B,π) 如下,计算序列O出现的概率

#状态 1 2 3
A = [[0.5,0.2,0.3],
	 [0.3,0.5,0.2],
	 [0.2,0.3,0.5]]

pi = [0.2,0.4,0.4]

# red white
B = [[0.5,0.5],
	 [0.4,0.6],
	 [0.7,0.3]]

​ step1:初始化 αi(1)=πibi(O1)

​ step2:计算 αi(t)=(j=1Nαj(t1)aji)bi(Ot)

​ step3:P(O|λ)=i=1Nαi(T)

#前向算法
def hmm_forward(A,B,pi,O):
    T = len(O)
    N = len(A[0])
    #step1 初始化
    alpha = [[0]*T for _ in range(N)]
    for i in range(N):
        alpha[i][0] = pi[i]*B[i][O[0]]

    #step2 计算alpha(t)
    for t in range(1,T):
        for i in range(N):
            temp = 0
            for j in range(N):
                temp += alpha[j][t-1]*A[j][i]
            alpha[i][t] = temp*B[i][O[t]]
            
    #step3
    proba = 0
    for i in range(N):
        proba += alpha[i][-1]
    return proba,alpha

A = [[0.5,0.2,0.3],[0.3,0.5,0.2],[0.2,0.3,0.5]]
B = [[0.5,0.5],[0.4,0.6],[0.7,0.3]]
pi = [0.2,0.4,0.4]
O = [0,1,0,1]
hmm_forward(A,B,pi,O)  #结果为 0.06009

结果

image-20200512195503450

后向算法

在时刻 t,状态为 i 时,观测到 Ot+1,Ot+2,...,OT 的概率,记为 βi(t)

βi(t)=P(Ot+1,Ot+2,...,OT|st=i,λ)

t=T 时,由于 T 时刻之后为空,没有观测,所以 βi(t)=1

t=T1 时,观测 OTOT 可能由任意一个状态产生

βi(T1)=P(OT|st=i,λ)=ai1b1(OT)β1(T)+ai2b2(OT)β2(T)+ai3b3(OT)β3(T)

image-20200511214910979

t=1 时,观测为 O2,O3,...,OT

β1(1)=P(O2,O3,...,OT|s1=1,λ)=a11b1(O2)β1(2)+a12b2(O2)β2(2)+a13b3(O2)β3(2)β2(1)=P(O2,O3,...,OT|s1=2,λ)=a21b1(O2)β1(2)+a22b2(O2)β2(2)+a23b3(O2)β3(2)β3(1)=P(O2,O3,...,OT|s1=3,λ)=a31b1(O2)β1(2)+a32b2(O2)β2(2)+a33b3(O2)β3(2)

所以

P(O2,O3,...,OT|λ)=β1(1)+β2(1)+β3(1)

后向算法过程如下:

​ step1:初始化 βi(T)=1

​ step2:计算 βi(t)=j=1Naijbj(Ot+1)βj(t+1)

​ step3:P(O|λ)=i=1Nπibi(O1)βi(1)

  • 时间复杂度 O(N2T)

代码实现

还是上面的例子

#后向算法
def hmm_backward(A,B,pi,O):
    T = len(O)
    N = len(A[0])
    #step1 初始化
    beta = [[0]*T for _ in range(N)]
    for i in range(N):
        beta[i][-1] = 1
        
    #step2 计算beta(t)
    for t in reversed(range(T-1)):
        for i in range(N):
            for j in range(N):
                beta[i][t]  += A[i][j]*B[j][O[t+1]]*beta[j][t+1]
            
    #step3
    proba = 0
    for i in range(N):
        proba += pi[i]*B[i][O[0]]*beta[i][0]
    return proba,beta

A = [[0.5,0.2,0.3],[0.3,0.5,0.2],[0.2,0.3,0.5]]
B = [[0.5,0.5],[0.4,0.6],[0.7,0.3]]
pi = [0.2,0.4,0.4]
O = [0,1,0,1]
hmm_backward(A,B,pi,O)  #结果为 0.06009

结果

image-20200512195526215

前向-后向算法

image-20200511201506794

回顾前向、后向变量:

  • ai(t) 时刻 t,状态为 i ,观测序列为 O1,O2,...,Ot 的概率
  • βi(t) 时刻 t,状态为 i ,观测序列为 Ot+1,Ot+2,...,OT 的概率

P(O,st=i|λ)=P(O1,O2,...,OT,st=i|λ)=P(O1,O2,...,Ot,st=i,Ot+1,Ot+2,...,OT|λ)=P(O1,O2,...,Ot,st=i|λ)P(Ot+1,Ot+2,...,OT|O1,O2,...,Ot,st=i,λ)=P(O1,O2,...,Ot,st=i|λ)P(Ot+1,Ot+2,...,OT,st=i|λ)=ai(t)βi(t)

即在给定的状态序列中,t 时刻状态为 i 的概率。

使用前后向算法可以计算隐状态,记 γi(t)=P(st=i|O,λ) 表示时刻 t 位于隐状态 i 的概率

P(st=i,O|λ)=αi(t)βi(t)

γi(t)=P(st=i|O,λ)=P(st=i,O|λ)P(O|λ)=αi(t)βi(t)P(O|λ)=αi(t)βi(t)i=1Nαi(t)βi(t)

references:

[1] https://www.cs.sjsu.edu/~stamp/RUA/HMM.pdf

[2]https://www.cnblogs.com/fulcra/p/11065474.html

[3] https://www.cnblogs.com/sjjsxl/p/6285629.html

[4] https://blog.csdn.net/xueyingxue001/article/details/52396494

posted @   鱼与鱼  阅读(5262)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示