第10章 隐马尔可夫模型

1.隐马尔可夫模型是关于时序的概率模型,描述由一个隐藏的马尔可夫链随机生成不可观测的状态的序列,再由各个状态随机生成一个观测而产生观测的序列的过程
隐马尔可夫模型由初始状态概率向量隐马尔可夫模型 - 图1状态转移概率矩阵隐马尔可夫模型 - 图2观测概率矩阵隐马尔可夫模型 - 图3决定。因此,隐马尔可夫模型可以写成隐马尔可夫模型 - 图4
隐马尔可夫模型是一个生成模型,表示状态序列和观测序列的联合分布,但是状态序列是隐藏的,不可观测的。
隐马尔可夫模型可以用于标注,这时状态对应着标记。标注问题是给定观测序列预测其对应的标记序列

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

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

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

  1. import numpy as np
  2. class HiddenMarkov:
  3. def __init__(self):
  4. self.alphas = None
  5. self.forward_P = None
  6. self.betas = None
  7. self.backward_P = None
  8. # 前向算法
  9. def forward(self, Q, V, A, B, O, PI):
  10. # 状态序列的大小
  11. N = len(Q)
  12. # 观测序列的大小
  13. M = len(O)
  14. # 初始化前向概率alpha值
  15. alphas = np.zeros((N, M))
  16. # 时刻数=观测序列数
  17. T = M
  18. # 遍历每一个时刻,计算前向概率alpha值
  19. for t in range(T):
  20. # 得到序列对应的索引
  21. indexOfO = V.index(O[t])
  22. # 遍历状态序列
  23. for i in range(N):
  24. # 初始化alpha初值
  25. if t == 0:
  26. # P176 公式(10.15)
  27. alphas[i][t] = PI[t][i] * B[i][indexOfO]
  28. print('alpha1(%d) = p%db%db(o1) = %f' %
  29. (i + 1, i, i, alphas[i][t]))
  30. else:
  31. # P176 公式(10.16)
  32. alphas[i][t] = np.dot([alpha[t - 1] for alpha in alphas],
  33. [a[i] for a in A]) * B[i][indexOfO]
  34. print('alpha%d(%d) = [sigma alpha%d(i)ai%d]b%d(o%d) = %f' %
  35. (t + 1, i + 1, t - 1, i, i, t, alphas[i][t]))
  36. # P176 公式(10.17)
  37. self.forward_P = np.sum([alpha[M - 1] for alpha in alphas])
  38. self.alphas = alphas
  39. # 后向算法
  40. def backward(self, Q, V, A, B, O, PI):
  41. # 状态序列的大小
  42. N = len(Q)
  43. # 观测序列的大小
  44. M = len(O)
  45. # 初始化后向概率beta值,P178 公式(10.19)
  46. betas = np.ones((N, M))
  47. #
  48. for i in range(N):
  49. print('beta%d(%d) = 1' % (M, i + 1))
  50. # 对观测序列逆向遍历
  51. for t in range(M - 2, -1, -1):
  52. # 得到序列对应的索引
  53. indexOfO = V.index(O[t + 1])
  54. # 遍历状态序列
  55. for i in range(N):
  56. # P178 公式(10.20)
  57. betas[i][t] = np.dot(
  58. np.multiply(A[i], [b[indexOfO] for b in B]),
  59. [beta[t + 1] for beta in betas])
  60. realT = t + 1
  61. realI = i + 1
  62. print('beta%d(%d) = sigma[a%djbj(o%d)beta%d(j)] = (' %
  63. (realT, realI, realI, realT + 1, realT + 1),
  64. end='')
  65. for j in range(N):
  66. print("%.2f * %.2f * %.2f + " %
  67. (A[i][j], B[j][indexOfO], betas[j][t + 1]),
  68. end='')
  69. print("0) = %.3f" % betas[i][t])
  70. # 取出第一个值
  71. indexOfO = V.index(O[0])
  72. self.betas = betas
  73. # P178 公式(10.21)
  74. P = np.dot(np.multiply(PI, [b[indexOfO] for b in B]),
  75. [beta[0] for beta in betas])
  76. self.backward_P = P
  77. print("P(O|lambda) = ", end="")
  78. for i in range(N):
  79. print("%.1f * %.1f * %.5f + " %
  80. (PI[0][i], B[i][indexOfO], betas[i][0]),
  81. end="")
  82. print("0 = %f" % P)
  83. # 维特比算法
  84. def viterbi(self, Q, V, A, B, O, PI):
  85. # 状态序列的大小
  86. N = len(Q)
  87. # 观测序列的大小
  88. M = len(O)
  89. # 初始化daltas
  90. deltas = np.zeros((N, M))
  91. # 初始化psis
  92. psis = np.zeros((N, M))
  93. # 初始化最优路径矩阵,该矩阵维度与观测序列维度相同
  94. I = np.zeros((1, M))
  95. # 遍历观测序列
  96. for t in range(M):
  97. # 递推从t=2开始
  98. realT = t + 1
  99. # 得到序列对应的索引
  100. indexOfO = V.index(O[t])
  101. for i in range(N):
  102. realI = i + 1
  103. if t == 0:
  104. # P185 算法10.5 步骤(1)
  105. deltas[i][t] = PI[0][i] * B[i][indexOfO]
  106. psis[i][t] = 0
  107. print('delta1(%d) = pi%d * b%d(o1) = %.2f * %.2f = %.2f' %
  108. (realI, realI, realI, PI[0][i], B[i][indexOfO],
  109. deltas[i][t]))
  110. print('psis1(%d) = 0' % (realI))
  111. else:
  112. # # P185 算法10.5 步骤(2)
  113. deltas[i][t] = np.max(
  114. np.multiply([delta[t - 1] for delta in deltas],
  115. [a[i] for a in A])) * B[i][indexOfO]
  116. print(
  117. 'delta%d(%d) = max[delta%d(j)aj%d]b%d(o%d) = %.2f * %.2f = %.5f'
  118. % (realT, realI, realT - 1, realI, realI, realT,
  119. np.max(
  120. np.multiply([delta[t - 1] for delta in deltas],
  121. [a[i] for a in A])), B[i][indexOfO],
  122. deltas[i][t]))
  123. psis[i][t] = np.argmax(
  124. np.multiply([delta[t - 1] for delta in deltas],
  125. [a[i] for a in A]))
  126. print('psis%d(%d) = argmax[delta%d(j)aj%d] = %d' %
  127. (realT, realI, realT - 1, realI, psis[i][t]))
  128. #print(deltas)
  129. #print(psis)
  130. # 得到最优路径的终结点
  131. I[0][M - 1] = np.argmax([delta[M - 1] for delta in deltas])
  132. print('i%d = argmax[deltaT(i)] = %d' % (M, I[0][M - 1] + 1))
  133. # 递归由后向前得到其他结点
  134. for t in range(M - 2, -1, -1):
  135. I[0][t] = psis[int(I[0][t + 1])][t + 1]
  136. print('i%d = psis%d(i%d) = %d' %
  137. (t + 1, t + 2, t + 2, I[0][t] + 1))
  138. # 输出最优路径
  139. print('最优路径是:', "->".join([str(int(i + 1)) for i in I[0]]))