隐马尔可夫模型针对是序列的解码问题而建立的一个生成式模型,它基于这样一个假设,在序列第n个编码给定的条件下,序列第n+1个编码与之前的编码具有条件独立性,如图1所示,观测
是可以观察到的,而编码
是需要预测的,记所有可能观测的集合为
,所有可能编码的集合
,A是转移概率矩阵:
B是观测概率矩阵:
是初始状态概率向量:
于是隐马尔可夫模型就可以用一个三元组
来表示
当A,B和
都确定下来的时候,就可以根据维特比算法预测观测的编码了,维特比算法实际上是用动态规划解马尔可夫模型的预测问题,其原理为:已知第T个编码为
,则求最大概率路径可以用递推的方法:
参数矩阵A,B和
的学习使用
算法,它是EM算法在HMM模型中的应用,参数的计算公式为:
其中的条件概率
和
的计算可使用前向算法和后向算法
更多细节可以参考李航的《统计学习方法》
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
考虑李航书中的一个习题:考虑盒子和球组成的隐马尔可夫模型
其中,
,计算概率
,
,预测
我们利用这个习题来测试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)
程序输出结果如下: