第10章 隐马尔可夫模型
1.隐马尔可夫模型是关于时序的概率模型,描述由一个隐藏的马尔可夫链随机生成不可观测的状态的序列,再由各个状态随机生成一个观测而产生观测的序列的过程。
隐马尔可夫模型由初始状态概率向量、状态转移概率矩阵
和观测概率矩阵
决定。因此,隐马尔可夫模型可以写成
。
隐马尔可夫模型是一个生成模型,表示状态序列和观测序列的联合分布,但是状态序列是隐藏的,不可观测的。
隐马尔可夫模型可以用于标注,这时状态对应着标记。标注问题是给定观测序列预测其对应的标记序列。
2.概率计算问题。
给定模型和观测序列
,计算在模型
下观测序列
出现的概率
。前向-后向算法是通过递推地计算前向-后向概率可以高效地进行隐马尔可夫模型的概率计算。
3.学习问题。
已知观测序列,估计模型
参数,使得在该模型下观测序列概率
最大。即用极大似然估计的方法估计参数。Baum-Welch算法,也就是EM算法可以高效地对隐马尔可夫模型进行训练。它是一种非监督学习算法。
4.预测问题。
已知模型和观测序列
,求对给定观测序列条件概率
最大的状态序列
。维特比算法应用动态规划高效地求解最优路径,即概率最大的状态序列。
import numpy as npclass HiddenMarkov:def __init__(self):self.alphas = Noneself.forward_P = Noneself.betas = Noneself.backward_P = None# 前向算法def forward(self, Q, V, A, B, O, PI):# 状态序列的大小N = len(Q)# 观测序列的大小M = len(O)# 初始化前向概率alpha值alphas = np.zeros((N, M))# 时刻数=观测序列数T = M# 遍历每一个时刻,计算前向概率alpha值for t in range(T):# 得到序列对应的索引indexOfO = V.index(O[t])# 遍历状态序列for i in range(N):# 初始化alpha初值if t == 0:# P176 公式(10.15)alphas[i][t] = PI[t][i] * B[i][indexOfO]print('alpha1(%d) = p%db%db(o1) = %f' %(i + 1, i, i, alphas[i][t]))else:# P176 公式(10.16)alphas[i][t] = np.dot([alpha[t - 1] for alpha in alphas],[a[i] for a in A]) * B[i][indexOfO]print('alpha%d(%d) = [sigma alpha%d(i)ai%d]b%d(o%d) = %f' %(t + 1, i + 1, t - 1, i, i, t, alphas[i][t]))# P176 公式(10.17)self.forward_P = np.sum([alpha[M - 1] for alpha in alphas])self.alphas = alphas# 后向算法def backward(self, Q, V, A, B, O, PI):# 状态序列的大小N = len(Q)# 观测序列的大小M = len(O)# 初始化后向概率beta值,P178 公式(10.19)betas = np.ones((N, M))#for i in range(N):print('beta%d(%d) = 1' % (M, i + 1))# 对观测序列逆向遍历for t in range(M - 2, -1, -1):# 得到序列对应的索引indexOfO = V.index(O[t + 1])# 遍历状态序列for i in range(N):# P178 公式(10.20)betas[i][t] = np.dot(np.multiply(A[i], [b[indexOfO] for b in B]),[beta[t + 1] for beta in betas])realT = t + 1realI = i + 1print('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])# 取出第一个值indexOfO = V.index(O[0])self.betas = betas# P178 公式(10.21)P = np.dot(np.multiply(PI, [b[indexOfO] for b in B]),[beta[0] for beta in betas])self.backward_P = Pprint("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)# 初始化daltasdeltas = np.zeros((N, M))# 初始化psispsis = np.zeros((N, M))# 初始化最优路径矩阵,该矩阵维度与观测序列维度相同I = np.zeros((1, M))# 遍历观测序列for t in range(M):# 递推从t=2开始realT = t + 1# 得到序列对应的索引indexOfO = V.index(O[t])for i in range(N):realI = i + 1if t == 0:# P185 算法10.5 步骤(1)deltas[i][t] = PI[0][i] * B[i][indexOfO]psis[i][t] = 0print('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:# # P185 算法10.5 步骤(2)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]))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])print('i%d = argmax[deltaT(i)] = %d' % (M, I[0][M - 1] + 1))# 递归由后向前得到其他结点for t in range(M - 2, -1, -1):I[0][t] = psis[int(I[0][t + 1])][t + 1]print('i%d = psis%d(i%d) = %d' %(t + 1, t + 2, t + 2, I[0][t] + 1))# 输出最优路径print('最优路径是:', "->".join([str(int(i + 1)) for i in I[0]]))
