查看原文
其他

复现经典:《统计学习方法》第 10 章 隐马尔可夫模型

机器学习初学者 机器学习初学者 2022-05-16

本文是李航老师的《统计学习方法》[1]一书的代码复现。

作者:黄海广[2]

备注:代码都可以在github[3]中下载。

我将陆续将代码发布在公众号“机器学习初学者”,敬请关注。

代码目录

  • 第 1 章 统计学习方法概论
  • 第 2 章 感知机
  • 第 3 章 k 近邻法
  • 第 4 章 朴素贝叶斯
  • 第 5 章 决策树
  • 第 6 章 逻辑斯谛回归
  • 第 7 章 支持向量机
  • 第 8 章 提升方法
  • 第 9 章 EM 算法及其推广
  • 第 10 章 隐马尔可夫模型
  • 第 11 章 条件随机场
  • 第 12 章 监督学习方法总结

代码参考:wzyonggege[4],WenDesi[5],火烫火烫的[6]

第 10 章 隐马尔可夫模型

1.隐马尔可夫模型是关于时序的概率模型,描述由一个隐藏的马尔可夫链随机生成不可观测的状态的序列,再由各个状态随机生成一个观测而产生观测的序列的过程。

隐马尔可夫模型由初始状态概率向、状态转移概率矩阵和观测概率矩阵决定。因此,隐马尔可夫模型可以写成

隐马尔可夫模型是一个生成模型,表示状态序列和观测序列的联合分布,但是状态序列是隐藏的,不可观测的。

隐马尔可夫模型可以用于标注,这时状态对应着标记。标注问题是给定观测序列预测其对应的标记序列。

2.概率计算问题。给定模型和观测序列,计算在模型下观测序列出现的概率。前向-后向算法是通过递推地计算前向-后向概率可以高效地进行隐马尔可夫模型的概率计算。

3.学习问题。已知观测序列,估计模型参数,使得在该模型下观测序列概率最大。即用极大似然估计的方法估计参数。Baum-Welch 算法,也就是 EM 算法可以高效地对隐马尔可夫模型进行训练。它是一种非监督学习算法。

4.预测问题。已知模型和观测序列,求对给定观测序列条件概率最大的状态序列。维特比算法应用动态规划高效地求解最优路径,即概率最大的状态序列。

import numpy as np
class HiddenMarkov: def forward(self, Q, V, A, B, O, PI): # 使用前向算法 N = len(Q) #可能存在的状态数量 M = len(O) # 观测序列的大小 alphas = np.zeros((N, M)) # alpha值 T = M # 有几个时刻,有几个观测序列,就有几个时刻 for t in range(T): # 遍历每一时刻,算出alpha值 indexOfO = V.index(O[t]) # 找出序列对应的索引 for i in range(N): if t == 0: # 计算初值 alphas[i][t] = PI[t][i] * B[i][indexOfO] # P176(10.15) print( 'alpha1(%d)=p%db%db(o1)=%f' % (i, i, i, alphas[i][t])) else: alphas[i][t] = np.dot( [alpha[t - 1] for alpha in alphas], [a[i] for a in A]) * B[i][indexOfO] # 对应P176(10.16) print('alpha%d(%d)=[sigma alpha%d(i)ai%d]b%d(o%d)=%f' % (t, i, t - 1, i, i, t, alphas[i][t])) # print(alphas) P = np.sum([alpha[M - 1] for alpha in alphas]) # P176(10.17) # alpha11 = pi[0][0] * B[0][0] #代表a1(1) # alpha12 = pi[0][1] * B[1][0] #代表a1(2) # alpha13 = pi[0][2] * B[2][0] #代表a1(3)
def backward(self, Q, V, A, B, O, PI): # 后向算法 N = len(Q) # 可能存在的状态数量 M = len(O) # 观测序列的大小 betas = np.ones((N, M)) # beta for i in range(N): print('beta%d(%d)=1' % (M, i)) for t in range(M - 2, -1, -1): indexOfO = V.index(O[t + 1]) # 找出序列对应的索引 for i in range(N): betas[i][t] = np.dot( np.multiply(A[i], [b[indexOfO] for b in B]), [beta[t + 1] for beta in betas]) realT = t + 1 realI = i + 1 print( 'beta%d(%d)=[sigma a%djbj(o%d)]beta%d(j)=(' % (realT, realI, realI, realT + 1, realT + 1), end='') for j in range(N): print( "%.2f*%.2f*%.2f+" % (A[i][j], B[j][indexOfO], betas[j][t + 1]), end='') print("0)=%.3f" % betas[i][t]) # print(betas) indexOfO = V.index(O[0]) P = np.dot( np.multiply(PI, [b[indexOfO] for b in B]), [beta[0] for beta in betas]) print("P(O|lambda)=", end="") for i in range(N): print( "%.1f*%.1f*%.5f+" % (PI[0][i], B[i][indexOfO], betas[i][0]), end="") print("0=%f" % P)
def viterbi(self, Q, V, A, B, O, PI): N = len(Q) #可能存在的状态数量 M = len(O) # 观测序列的大小 deltas = np.zeros((N, M)) psis = np.zeros((N, M)) I = np.zeros((1, M)) for t in range(M): realT = t + 1 indexOfO = V.index(O[t]) # 找出序列对应的索引 for i in range(N): realI = i + 1 if t == 0: deltas[i][t] = PI[0][i] * B[i][indexOfO] psis[i][t] = 0 print('delta1(%d)=pi%d * b%d(o1)=%.2f * %.2f=%.2f' % (realI, realI, realI, PI[0][i], B[i][indexOfO], deltas[i][t])) print('psis1(%d)=0' % (realI)) else: deltas[i][t] = np.max( np.multiply([delta[t - 1] for delta in deltas], [a[i] for a in A])) * B[i][indexOfO] print( 'delta%d(%d)=max[delta%d(j)aj%d]b%d(o%d)=%.2f*%.2f=%.5f' % (realT, realI, realT - 1, realI, realI, realT, np.max( np.multiply([delta[t - 1] for delta in deltas], [a[i] for a in A])), B[i][indexOfO], deltas[i][t])) psis[i][t] = np.argmax( np.multiply( [delta[t - 1] for delta in deltas], [a[i] for a in A])) + 1 #由于其返回的是索引,因此应+1才能和正常的下标值相符合。 print('psis%d(%d)=argmax[delta%d(j)aj%d]=%d' % (realT, realI, realT - 1, realI, psis[i][t])) print(deltas) print(psis) I[0][M - 1] = np.argmax([delta[M - 1] for delta in deltas ]) + 1 #由于其返回的是索引,因此应+1才能和正常的下标值相符合。 print('i%d=argmax[deltaT(i)]=%d' % (M, I[0][M - 1])) for t in range(M - 2, -1, -1): I[0][t] = psis[int(I[0][t + 1]) - 1][t + 1] print('i%d=psis%d(i%d)=%d' % (t + 1, t + 2, t + 2, I[0][t])) print("状态序列I:", I)

习题 10.1

#习题10.1Q = [1, 2, 3]V = ['红', '白']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]]# O = ['红', '白', '红', '红', '白', '红', '白', '白']O = ['红', '白', '红', '白'] #习题10.1的例子PI = [[0.2, 0.4, 0.4]]
HMM = HiddenMarkov()# HMM.forward(Q, V, A, B, O, PI)# HMM.backward(Q, V, A, B, O, PI)HMM.viterbi(Q, V, A, B, O, PI)
delta1(1)=pi1 * b1(o1)=0.20 * 0.50=0.10
psis1(1)=0
delta1(2)=pi2 * b2(o1)=0.40 * 0.40=0.16
psis1(2)=0
delta1(3)=pi3 * b3(o1)=0.40 * 0.70=0.28
psis1(3)=0
delta2(1)=max[delta1(j)aj1]b1(o2)=0.06*0.50=0.02800
psis2(1)=argmax[delta1(j)aj1]=3
delta2(2)=max[delta1(j)aj2]b2(o2)=0.08*0.60=0.05040
psis2(2)=argmax[delta1(j)aj2]=3
delta2(3)=max[delta1(j)aj3]b3(o2)=0.14*0.30=0.04200
psis2(3)=argmax[delta1(j)aj3]=3
delta3(1)=max[delta2(j)aj1]b1(o3)=0.02*0.50=0.00756
psis3(1)=argmax[delta2(j)aj1]=2
delta3(2)=max[delta2(j)aj2]b2(o3)=0.03*0.40=0.01008
psis3(2)=argmax[delta2(j)aj2]=2
delta3(3)=max[delta2(j)aj3]b3(o3)=0.02*0.70=0.01470
psis3(3)=argmax[delta2(j)aj3]=3
delta4(1)=max[delta3(j)aj1]b1(o4)=0.00*0.50=0.00189
psis4(1)=argmax[delta3(j)aj1]=1
delta4(2)=max[delta3(j)aj2]b2(o4)=0.01*0.60=0.00302
psis4(2)=argmax[delta3(j)aj2]=2
delta4(3)=max[delta3(j)aj3]b3(o4)=0.01*0.30=0.00220
psis4(3)=argmax[delta3(j)aj3]=3
[[0.1 0.028 0.00756 0.00189 ]
[0.16 0.0504 0.01008 0.003024]
[0.28 0.042 0.0147 0.002205]]
[[0. 3. 2. 1.]
[0. 3. 2. 2.]
[0. 3. 3. 3.]]
i4=argmax[deltaT(i)]=2
i3=psis4(i4)=2
i2=psis3(i3)=2
i1=psis2(i2)=3
状态序列I:[[3. 2. 2. 2.]]

习题 10.2

Q = [1, 2, 3]V = ['红', '白']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]]O = ['红', '白', '红', '红', '白', '红', '白', '白']PI = [[0.2, 0.3, 0.5]]
HMM.forward(Q, V, A, B, O, PI)HMM.backward(Q, V, A, B, O, PI)
alpha1(0)=p0b0b(o1)=0.100000
alpha1(1)=p1b1b(o1)=0.120000
alpha1(2)=p2b2b(o1)=0.350000
alpha1(0)=[sigma alpha0(i)ai0]b0(o1)=0.078000
alpha1(1)=[sigma alpha0(i)ai1]b1(o1)=0.111000
alpha1(2)=[sigma alpha0(i)ai2]b2(o1)=0.068700
alpha2(0)=[sigma alpha1(i)ai0]b0(o2)=0.043020
alpha2(1)=[sigma alpha1(i)ai1]b1(o2)=0.036684
alpha2(2)=[sigma alpha1(i)ai2]b2(o2)=0.055965
alpha3(0)=[sigma alpha2(i)ai0]b0(o3)=0.021854
alpha3(1)=[sigma alpha2(i)ai1]b1(o3)=0.017494
alpha3(2)=[sigma alpha2(i)ai2]b2(o3)=0.033758
alpha4(0)=[sigma alpha3(i)ai0]b0(o4)=0.011463
alpha4(1)=[sigma alpha3(i)ai1]b1(o4)=0.013947
alpha4(2)=[sigma alpha3(i)ai2]b2(o4)=0.008080
alpha5(0)=[sigma alpha4(i)ai0]b0(o5)=0.005766
alpha5(1)=[sigma alpha4(i)ai1]b1(o5)=0.004676
alpha5(2)=[sigma alpha4(i)ai2]b2(o5)=0.007188
alpha6(0)=[sigma alpha5(i)ai0]b0(o6)=0.002862
alpha6(1)=[sigma alpha5(i)ai1]b1(o6)=0.003389
alpha6(2)=[sigma alpha5(i)ai2]b2(o6)=0.001878
alpha7(0)=[sigma alpha6(i)ai0]b0(o7)=0.001411
alpha7(1)=[sigma alpha6(i)ai1]b1(o7)=0.001698
alpha7(2)=[sigma alpha6(i)ai2]b2(o7)=0.000743
beta8(0)=1
beta8(1)=1
beta8(2)=1
beta7(1)=[sigma a1jbj(o8)]beta8(j)=(0.50*0.50*1.00+0.20*0.60*1.00+0.30*0.30*1.00+0)=0.460
beta7(2)=[sigma a2jbj(o8)]beta8(j)=(0.30*0.50*1.00+0.50*0.60*1.00+0.20*0.30*1.00+0)=0.510
beta7(3)=[sigma a3jbj(o8)]beta8(j)=(0.20*0.50*1.00+0.30*0.60*1.00+0.50*0.30*1.00+0)=0.430
beta6(1)=[sigma a1jbj(o7)]beta7(j)=(0.50*0.50*0.46+0.20*0.60*0.51+0.30*0.30*0.43+0)=0.215
beta6(2)=[sigma a2jbj(o7)]beta7(j)=(0.30*0.50*0.46+0.50*0.60*0.51+0.20*0.30*0.43+0)=0.248
beta6(3)=[sigma a3jbj(o7)]beta7(j)=(0.20*0.50*0.46+0.30*0.60*0.51+0.50*0.30*0.43+0)=0.202
beta5(1)=[sigma a1jbj(o6)]beta6(j)=(0.50*0.50*0.21+0.20*0.40*0.25+0.30*0.70*0.20+0)=0.116
beta5(2)=[sigma a2jbj(o6)]beta6(j)=(0.30*0.50*0.21+0.50*0.40*0.25+0.20*0.70*0.20+0)=0.110
beta5(3)=[sigma a3jbj(o6)]beta6(j)=(0.20*0.50*0.21+0.30*0.40*0.25+0.50*0.70*0.20+0)=0.122
beta4(1)=[sigma a1jbj(o5)]beta5(j)=(0.50*0.50*0.12+0.20*0.60*0.11+0.30*0.30*0.12+0)=0.053
beta4(2)=[sigma a2jbj(o5)]beta5(j)=(0.30*0.50*0.12+0.50*0.60*0.11+0.20*0.30*0.12+0)=0.058
beta4(3)=[sigma a3jbj(o5)]beta5(j)=(0.20*0.50*0.12+0.30*0.60*0.11+0.50*0.30*0.12+0)=0.050
beta3(1)=[sigma a1jbj(o4)]beta4(j)=(0.50*0.50*0.05+0.20*0.40*0.06+0.30*0.70*0.05+0)=0.028
beta3(2)=[sigma a2jbj(o4)]beta4(j)=(0.30*0.50*0.05+0.50*0.40*0.06+0.20*0.70*0.05+0)=0.026
beta3(3)=[sigma a3jbj(o4)]beta4(j)=(0.20*0.50*0.05+0.30*0.40*0.06+0.50*0.70*0.05+0)=0.030
beta2(1)=[sigma a1jbj(o3)]beta3(j)=(0.50*0.50*0.03+0.20*0.40*0.03+0.30*0.70*0.03+0)=0.015
beta2(2)=[sigma a2jbj(o3)]beta3(j)=(0.30*0.50*0.03+0.50*0.40*0.03+0.20*0.70*0.03+0)=0.014
beta2(3)=[sigma a3jbj(o3)]beta3(j)=(0.20*0.50*0.03+0.30*0.40*0.03+0.50*0.70*0.03+0)=0.016
beta1(1)=[sigma a1jbj(o2)]beta2(j)=(0.50*0.50*0.02+0.20*0.60*0.01+0.30*0.30*0.02+0)=0.007
beta1(2)=[sigma a2jbj(o2)]beta2(j)=(0.30*0.50*0.02+0.50*0.60*0.01+0.20*0.30*0.02+0)=0.007
beta1(3)=[sigma a3jbj(o2)]beta2(j)=(0.20*0.50*0.02+0.30*0.60*0.01+0.50*0.30*0.02+0)=0.006
P(O|lambda)=0.2*0.5*0.00698+0.3*0.4*0.00741+0.5*0.7*0.00647+0=0.003852
参考资料
[1] 《统计学习方法》: https://baike.baidu.com/item/统计学习方法/10430179
[2] 黄海广: https://github.com/fengdu78
[3] github: https://github.com/fengdu78/lihang-code
[4] wzyonggege: https://github.com/wzyonggege/statistical-learning-method
[5] WenDesi: https://github.com/WenDesi/lihang_book_algorithm
[6] 火烫火烫的: https://blog.csdn.net/tudaodiaozhale



往期精彩回顾


备注:加入本站微信群或者qq群,请回复“加群

加入知识星球(4300+用户,ID:92416895),请回复“知识星球

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存