HMM的代码,这次用 Python 写了一遍,依据仍然是李航博士的《统计学习方法》

由于第一次用python,所以代码可能会有许多缺陷,但是所有代码都用书中的例题进行了测试,结果正确。

这里想说一下python,在编写HMM过程中参看了之前写的MATLAB程序,发现他们有太多相似的地方,用到了numpy库,在python代码过程中最让我头疼的是数组角标,和MATLAB矩阵角标从1开始不同,numpy库数组角标都是从0开始,而且数组的维数也需要谨慎,一不小心就会出现too many indices for array的错误。程序中最后是维特比算法,在运行过程中出现了__main__:190: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future的警告,还没有去掉这个警告,查了一下说不影响结果,后面会去解决这个问题,下面贴出我的代码

1. # -*- coding: utf-8 -*-  
2. """
3. Created on Thu Feb 16 19:28:39 2017
4. 2017-4-2
5.     ForwardBackwardAlg函数功能:实现前向算法
6.     理论依据:李航《统计学习方法》
7. 2017-4-5
8.     修改了ForwardBackwardAlg函数名称为ForwardAlgo以及输出的alpha数组形式
9.     完成了BackwardAlgo函数功能:后向算法
10.     以及函数FBAlgoAppli:计算在观测序列和模型参数确定的情况下,
11.     某一个隐含状态对应相应的观测状态的概率
12. 2017-4-6
13.     完成BaumWelchAlgo函数一次迭代
14. 2017-4-7
15.     实现维特比算法
16. @author: sgp
17. """  
18.   
19. import numpy as np  
20.   
21. #输入格式如下:  
22. #A = np.array([[.5,.2,.3],[.3,.5,.2],[.2,.3,.5]])  
23. #B = np.array([[.5,.5],[.4,.6],[.7,.3]])  
24. #Pi = np.array([[.2,.4,.4]])  
25. #O = np.array([[1,2,1]])  
26.   
27. #应用ndarray在数组之间进行相互运算时,一定要确保数组维数相同!  
28. #比如:  
29. #In[93]:m = np.array([1,2,3,4])  
30. #In[94]:m  
31. #Out[94]: array([1, 2, 3, 4])  
32. #In[95]:m.shape  
33. #Out[95]: (4,)  
34. #这里表示的是一维数组  
35. #In[96]:m = np.array([[1,2,3,4]])  
36. #In[97]:m  
37. #Out[97]: array([[1, 2, 3, 4]])  
38. #In[98]:m.shape  
39. #Out[98]: (1, 4)  
40. #而这里表示的就是二维数组  
41. #注意In[93]和In[96]的区别,多一对中括号!!  
42.   
43. #N = A.shape[0]为数组A的行数, H = O.shape[1]为数组O的列数  
44. #在下列各函数中,alpha数组和beta数组均为N*H二维数组,也就是横向坐标是时间,纵向是状态  
45.   
46. def ForwardAlgo(A,B,Pi,O):  
47. 0]#数组A的行数  
48. 1]#数组A的列数  
49. 1]#数组O的列数  
50.   
51.     sum_alpha_1 = np.zeros((M,N))  
52.     alpha = np.zeros((N,H))  
53. 1,N))  
54. 0,:], B[:,O[0,0]-1])  
55.       
56. 0] = np.array(alpha_1).reshape(1,N)#alpha_1是一维数组,在使用np.multiply的时候需要升级到二维数组。#错误是IndexError: too many indices for array


1.     for h in range(1,H):  
2. for i in range(N):  
3. for j in range(M):  
4. 1] * A[j,i]  
5. 1).reshape(1,N)#同理,将数组升级为二维数组  
6. 0,i] * B[i,O[0,h]-1]  
7. #print("alpha矩阵: \n %r" % alpha)      
8. 0).reshape(1,H)  
9. 0,H-1]  
10. #print("观测概率: \n %r" % P)  
11. #return alpha  
12. return alpha, P  
13.       
14. def BackwardAlgo(A,B,Pi,O):  
15. 0]#数组A的行数  
16. 1]#数组A的列数  
17. 1]#数组O的列数  
18.       
19. #beta = np.zeros((N,H))  
20. 1,N))  
21.     beta = np.zeros((N,H))  
22. 1] = 1  
23. 1,N))  
24.       
25. for h in range(H-1,0,-1):  
26. for i in range(N):  
27. for j in range(M):  
28. 0,j] = A[i,j] * B[j,O[0,h]-1] * beta[j,h]  
29. 1] = sum_beta.sum(1)  
30. #print("beta矩阵: \n %r" % beta)  
31. for i in range(N):  
32. 0,i] = Pi[0,i] * B[i,O[0,0]-1] * beta[i,0]  
33. 1).reshape(1,1)  
34. #print("观测概率: \n %r" % p[0,0])  
35. return beta, p[0,0]  
36.     
37. def FBAlgoAppli(A,B,Pi,O,I):  
38. #计算在观测序列和模型参数确定的情况下,某一个隐含状态对应相应的观测状态的概率  
39. #例题参考李航《统计学习方法》P189习题10.2  
40. #输入格式:  
41. #I为二维数组,存放所求概率P(it = qi,O|lambda)中it和qi的角标t和i,即P=[t,i]  
42.     alpha,p1 = ForwardAlgo(A,B,Pi,O)  
43.     beta,p2 = BackwardAlgo(A,B,Pi,O)  
44. 0,1]-1,I[0,0]-1] * beta[I[0,1]-1,I[0,0]-1] / p1  
45. return p  
46.       
47. def GetGamma(A,B,Pi,O):  
48. 0]#数组A的行数  
49. 1]#数组O的列数  
50.     Gamma = np.zeros((N,H))  
51.     alpha,p1 = ForwardAlgo(A,B,Pi,O)  
52.     beta,p2 = BackwardAlgo(A,B,Pi,O)  
53. for h in range(H):  
54. for i in range(N):  
55.             Gamma[i,h] = alpha[i,h] * beta[i,h] / p1  
56. return Gamma  
57.       
58. def GetXi(A,B,Pi,O):  
59. 0]#数组A的行数  
60. 1]#数组A的列数  
61. 1]#数组O的列数  
62. 1,N,M))  
63.     alpha,p1 = ForwardAlgo(A,B,Pi,O)  
64.     beta,p2 = BackwardAlgo(A,B,Pi,O)  
65. for h in range(H-1):  
66. for i in range(N):  
67. for j in range(M):  
68. 0,h+1]-1] * beta[j,h+1] / p1  
69. #print("Xi矩阵: \n %r" % Xi)  
70. return Xi  
71.       
72. def BaumWelchAlgo(A,B,Pi,O):  
73. 0]#数组A的行数  
74. 1]#数组A的列数  
75. 1]#数组B的列数  
76. 1]#数组O的列数  
77. 0  
78.     Gamma = GetGamma(A,B,Pi,O)  
79.     Xi = GetXi(A,B,Pi,O)  
80. 0)  
81.     a = np.zeros((N,M))  
82.     b = np.zeros((M,Y))  
83. 1,N))  
84. 1),Gamma[:,H-1]).reshape(1,N)  
85. for i in range(N):  
86. for j in range(M):  
87. 0,i]  
88. #print(a)  
89. for y in range(Y):  
90. for j in range(M):  
91. for h in range(H):  
92. if O[0,h]-1 == y:  
93.                     c = c + Gamma[j,h]  
94. 1).reshape(1,N)  
95. 0,j]  
96. 0  
97. #print(b)  
98. for i in range(N):  
99. 0,i] = Gamma[i,0]  
100. #print(pi)  
101. return a,b,pi  
102.       
103. def BaumWelchAlgo_n(A,B,Pi,O,n):#计算迭代次数为n的BaumWelch算法  
104. for i in range(n):  
105.         A,B,Pi = BaumWelchAlgo(A,B,Pi,O)  
106. return A,B,Pi  
107.       
108. def viterbi(A,B,Pi,O):  
109. 0]#数组A的行数  
110. 1]#数组A的列数  
111. 1]#数组O的列数  
112.     Delta = np.zeros((M,H))  
113.     Psi = np.zeros((M,H))  
114. 1))  
115. 1,H))  
116.       
117. for i in range(N):  
118. 0] = Pi[0,i] * B[i,O[0,0]-1]  
119.           
120. for h in range(1,H):  
121. for j in range(M):  
122. for i in range(N):  
123. 0] = Delta[i,h-1] * A[i,j] * B[j,O[0,h]-1]  
124.             Delta[j,h] = np.amax(Delta_1)  
125. 1  
126. print("Delta矩阵: \n %r" % Delta)  
127. print("Psi矩阵: \n %r" % Psi)  
128. 1])  
129. 1])  
130. 0,H-1] = psi + 1  
131. for h in range(H-1,0,-1):  
132. 0,h-1] = Psi[I[0,h]-1,h]  
133. print("最优路径概率: \n %r" % P_best)  
134. print("最优路径: \n %r" % I)

其实代码就是翻译的公式,李航博士的书中有比较详细的推理过程,或者去找一些专业的论文文献进一步了解,这里仅仅是实现了最简单的应用,其应用实例如下:


输入数据格式:

In[117]:A
 Out[117]: 
 array([[ 0.5,  0.2,  0.3],
        [ 0.3,  0.5,  0.2],
        [ 0.2,  0.3,  0.5]])

 In[118]:B
 Out[118]: 
 array([[ 0.5,  0.5],
        [ 0.4,  0.6],
        [ 0.7,  0.3]])

 In[119]:Pi
 Out[119]: array([[ 0.2,  0.4,  0.4]])

 In[120]:O
 Out[120]: array([[1, 2, 1]])



输出结果为:

In[101]:alpha,p = ForwardAlgo(A,B,Pi,O)

 In[102]:alpha
 Out[102]: 
 array([[ 0.1     ,  0.077   ,  0.04187 ],
        [ 0.16    ,  0.1104  ,  0.035512],
        [ 0.28    ,  0.0606  ,  0.052836]])

 In[103]:p
 Out[103]: 0.130218

 In[104]:beta,p1 = BackwardAlgo(A,B,Pi,O)

 In[105]:beta
 Out[105]: 
 array([[ 0.2451,  0.54  ,  1.    ],
        [ 0.2622,  0.49  ,  1.    ],
        [ 0.2277,  0.57  ,  1.    ]])

 In[106]:p1
 Out[106]: 0.130218

 In[107]:gamma = GetGamma(A,B,Pi,O)

 In[108]:gamma
 Out[108]: 
 array([[ 0.18822283,  0.31931069,  0.32153773],
        [ 0.32216744,  0.41542644,  0.27271191],
        [ 0.48960973,  0.26526287,  0.40575036]])

 In[109]:xi = GetXi(A,B,Pi,O)

 In[110]:xi
 Out[110]: 
 array([[[ 0.1036723 ,  0.04515505,  0.03939548],
         [ 0.09952541,  0.18062019,  0.04202184],
         [ 0.11611298,  0.1896512 ,  0.18384555]],


        [[ 0.14782903,  0.04730529,  0.12417638],
         [ 0.12717136,  0.16956181,  0.11869327],
         [ 0.04653735,  0.05584481,  0.16288071]]])

 In[111]:a,b,pi = BaumWelchAlgo_n(A,B,Pi,O,5)

 In[112]:a
 Out[112]: 
 array([[ 0.43972438,  0.15395857,  0.40631705],
        [ 0.309058  ,  0.45055446,  0.24038754],
        [ 0.3757005 ,  0.50361975,  0.12067975]])

 In[113]:b
 Out[113]: 
 array([[ 0.50277235,  0.49722765],
        [ 0.49524289,  0.50475711],
        [ 0.91925551,  0.08074449]])

 In[114]:pi
 Out[114]: array([[ 0.08435301,  0.18040718,  0.73523981]])

 In[115]:viterbi(A,B,Pi,O)
 Delta矩阵: 
  array([[ 0.1    ,  0.028  ,  0.00756],
        [ 0.16   ,  0.0504 ,  0.01008],
        [ 0.28   ,  0.042  ,  0.0147 ]])
 Psi矩阵: 
  array([[ 0.,  3.,  2.],
        [ 0.,  3.,  2.],
        [ 0.,  3.,  3.]])
 最优路径概率: 
  0.014699999999999998
 最优路径: 
  array([[ 3.,  3.,  3.]])
 __main__:192: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future


由于第一次用python,所以代码可能会有许多缺陷,但是所有代码都用书中的例题进行了测试,结果正确。

这里想说一下python,在编写HMM过程中参看了之前写的MATLAB程序,发现他们有太多相似的地方,用到了numpy库,在python代码过程中最让我头疼的是数组角标,和MATLAB矩阵角标从1开始不同,numpy库数组角标都是从0开始,而且数组的维数也需要谨慎,一不小心就会出现too many indices for array的错误。程序中最后是维特比算法,在运行过程中出现了__main__:190: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future的警告,还没有去掉这个警告,查了一下说不影响结果,后面会去解决这个问题,下面贴出我的代码


1. # -*- coding: utf-8 -*-  
2. """
3. Created on Thu Feb 16 19:28:39 2017
4. 2017-4-2
5.     ForwardBackwardAlg函数功能:实现前向算法
6.     理论依据:李航《统计学习方法》
7. 2017-4-5
8.     修改了ForwardBackwardAlg函数名称为ForwardAlgo以及输出的alpha数组形式
9.     完成了BackwardAlgo函数功能:后向算法
10.     以及函数FBAlgoAppli:计算在观测序列和模型参数确定的情况下,
11.     某一个隐含状态对应相应的观测状态的概率
12. 2017-4-6
13.     完成BaumWelchAlgo函数一次迭代
14. 2017-4-7
15.     实现维特比算法
16. @author: sgp
17. """  
18.   
19. import numpy as np  
20.   
21. #输入格式如下:  
22. #A = np.array([[.5,.2,.3],[.3,.5,.2],[.2,.3,.5]])  
23. #B = np.array([[.5,.5],[.4,.6],[.7,.3]])  
24. #Pi = np.array([[.2,.4,.4]])  
25. #O = np.array([[1,2,1]])  
26.   
27. #应用ndarray在数组之间进行相互运算时,一定要确保数组维数相同!  
28. #比如:  
29. #In[93]:m = np.array([1,2,3,4])  
30. #In[94]:m  
31. #Out[94]: array([1, 2, 3, 4])  
32. #In[95]:m.shape  
33. #Out[95]: (4,)  
34. #这里表示的是一维数组  
35. #In[96]:m = np.array([[1,2,3,4]])  
36. #In[97]:m  
37. #Out[97]: array([[1, 2, 3, 4]])  
38. #In[98]:m.shape  
39. #Out[98]: (1, 4)  
40. #而这里表示的就是二维数组  
41. #注意In[93]和In[96]的区别,多一对中括号!!  
42.   
43. #N = A.shape[0]为数组A的行数, H = O.shape[1]为数组O的列数  
44. #在下列各函数中,alpha数组和beta数组均为N*H二维数组,也就是横向坐标是时间,纵向是状态  
45.   
46. def ForwardAlgo(A,B,Pi,O):  
47. 0]#数组A的行数  
48. 1]#数组A的列数  
49. 1]#数组O的列数  
50.   
51.     sum_alpha_1 = np.zeros((M,N))  
52.     alpha = np.zeros((N,H))  
53. 1,N))  
54. 0,:], B[:,O[0,0]-1])  
55.       
56. 0] = np.array(alpha_1).reshape(1,N)#alpha_1是一维数组,在使用np.multiply的时候需要升级到二维数组。#错误是IndexError: too many indices for array


1.     for h in range(1,H):  
2. for i in range(N):  
3. for j in range(M):  
4. 1] * A[j,i]  
5. 1).reshape(1,N)#同理,将数组升级为二维数组  
6. 0,i] * B[i,O[0,h]-1]  
7. #print("alpha矩阵: \n %r" % alpha)      
8. 0).reshape(1,H)  
9. 0,H-1]  
10. #print("观测概率: \n %r" % P)  
11. #return alpha  
12. return alpha, P  
13.       
14. def BackwardAlgo(A,B,Pi,O):  
15. 0]#数组A的行数  
16. 1]#数组A的列数  
17. 1]#数组O的列数  
18.       
19. #beta = np.zeros((N,H))  
20. 1,N))  
21.     beta = np.zeros((N,H))  
22. 1] = 1  
23. 1,N))  
24.       
25. for h in range(H-1,0,-1):  
26. for i in range(N):  
27. for j in range(M):  
28. 0,j] = A[i,j] * B[j,O[0,h]-1] * beta[j,h]  
29. 1] = sum_beta.sum(1)  
30. #print("beta矩阵: \n %r" % beta)  
31. for i in range(N):  
32. 0,i] = Pi[0,i] * B[i,O[0,0]-1] * beta[i,0]  
33. 1).reshape(1,1)  
34. #print("观测概率: \n %r" % p[0,0])  
35. return beta, p[0,0]  
36.     
37. def FBAlgoAppli(A,B,Pi,O,I):  
38. #计算在观测序列和模型参数确定的情况下,某一个隐含状态对应相应的观测状态的概率  
39. #例题参考李航《统计学习方法》P189习题10.2  
40. #输入格式:  
41. #I为二维数组,存放所求概率P(it = qi,O|lambda)中it和qi的角标t和i,即P=[t,i]  
42.     alpha,p1 = ForwardAlgo(A,B,Pi,O)  
43.     beta,p2 = BackwardAlgo(A,B,Pi,O)  
44. 0,1]-1,I[0,0]-1] * beta[I[0,1]-1,I[0,0]-1] / p1  
45. return p  
46.       
47. def GetGamma(A,B,Pi,O):  
48. 0]#数组A的行数  
49. 1]#数组O的列数  
50.     Gamma = np.zeros((N,H))  
51.     alpha,p1 = ForwardAlgo(A,B,Pi,O)  
52.     beta,p2 = BackwardAlgo(A,B,Pi,O)  
53. for h in range(H):  
54. for i in range(N):  
55.             Gamma[i,h] = alpha[i,h] * beta[i,h] / p1  
56. return Gamma  
57.       
58. def GetXi(A,B,Pi,O):  
59. 0]#数组A的行数  
60. 1]#数组A的列数  
61. 1]#数组O的列数  
62. 1,N,M))  
63.     alpha,p1 = ForwardAlgo(A,B,Pi,O)  
64.     beta,p2 = BackwardAlgo(A,B,Pi,O)  
65. for h in range(H-1):  
66. for i in range(N):  
67. for j in range(M):  
68. 0,h+1]-1] * beta[j,h+1] / p1  
69. #print("Xi矩阵: \n %r" % Xi)  
70. return Xi  
71.       
72. def BaumWelchAlgo(A,B,Pi,O):  
73. 0]#数组A的行数  
74. 1]#数组A的列数  
75. 1]#数组B的列数  
76. 1]#数组O的列数  
77. 0  
78.     Gamma = GetGamma(A,B,Pi,O)  
79.     Xi = GetXi(A,B,Pi,O)  
80. 0)  
81.     a = np.zeros((N,M))  
82.     b = np.zeros((M,Y))  
83. 1,N))  
84. 1),Gamma[:,H-1]).reshape(1,N)  
85. for i in range(N):  
86. for j in range(M):  
87. 0,i]  
88. #print(a)  
89. for y in range(Y):  
90. for j in range(M):  
91. for h in range(H):  
92. if O[0,h]-1 == y:  
93.                     c = c + Gamma[j,h]  
94. 1).reshape(1,N)  
95. 0,j]  
96. 0  
97. #print(b)  
98. for i in range(N):  
99. 0,i] = Gamma[i,0]  
100. #print(pi)  
101. return a,b,pi  
102.       
103. def BaumWelchAlgo_n(A,B,Pi,O,n):#计算迭代次数为n的BaumWelch算法  
104. for i in range(n):  
105.         A,B,Pi = BaumWelchAlgo(A,B,Pi,O)  
106. return A,B,Pi  
107.       
108. def viterbi(A,B,Pi,O):  
109. 0]#数组A的行数  
110. 1]#数组A的列数  
111. 1]#数组O的列数  
112.     Delta = np.zeros((M,H))  
113.     Psi = np.zeros((M,H))  
114. 1))  
115. 1,H))  
116.       
117. for i in range(N):  
118. 0] = Pi[0,i] * B[i,O[0,0]-1]  
119.           
120. for h in range(1,H):  
121. for j in range(M):  
122. for i in range(N):  
123. 0] = Delta[i,h-1] * A[i,j] * B[j,O[0,h]-1]  
124.             Delta[j,h] = np.amax(Delta_1)  
125. 1  
126. print("Delta矩阵: \n %r" % Delta)  
127. print("Psi矩阵: \n %r" % Psi)  
128. 1])  
129. 1])  
130. 0,H-1] = psi + 1  
131. for h in range(H-1,0,-1):  
132. 0,h-1] = Psi[I[0,h]-1,h]  
133. print("最优路径概率: \n %r" % P_best)  
134. print("最优路径: \n %r" % I)

其实代码就是翻译的公式,李航博士的书中有比较详细的推理过程,或者去找一些专业的论文文献进一步了解,这里仅仅是实现了最简单的应用,其应用实例如下:


输入数据格式:

In[117]:A
 Out[117]: 
 array([[ 0.5,  0.2,  0.3],
        [ 0.3,  0.5,  0.2],
        [ 0.2,  0.3,  0.5]])

 In[118]:B
 Out[118]: 
 array([[ 0.5,  0.5],
        [ 0.4,  0.6],
        [ 0.7,  0.3]])

 In[119]:Pi
 Out[119]: array([[ 0.2,  0.4,  0.4]])

 In[120]:O
 Out[120]: array([[1, 2, 1]])


输出结果为:

In[101]:alpha,p = ForwardAlgo(A,B,Pi,O)

 In[102]:alpha
 Out[102]: 
 array([[ 0.1     ,  0.077   ,  0.04187 ],
        [ 0.16    ,  0.1104  ,  0.035512],
        [ 0.28    ,  0.0606  ,  0.052836]])

 In[103]:p
 Out[103]: 0.130218

 In[104]:beta,p1 = BackwardAlgo(A,B,Pi,O)

 In[105]:beta
 Out[105]: 
 array([[ 0.2451,  0.54  ,  1.    ],
        [ 0.2622,  0.49  ,  1.    ],
        [ 0.2277,  0.57  ,  1.    ]])

 In[106]:p1
 Out[106]: 0.130218

 In[107]:gamma = GetGamma(A,B,Pi,O)

 In[108]:gamma
 Out[108]: 
 array([[ 0.18822283,  0.31931069,  0.32153773],
        [ 0.32216744,  0.41542644,  0.27271191],
        [ 0.48960973,  0.26526287,  0.40575036]])

 In[109]:xi = GetXi(A,B,Pi,O)

 In[110]:xi
 Out[110]: 
 array([[[ 0.1036723 ,  0.04515505,  0.03939548],
         [ 0.09952541,  0.18062019,  0.04202184],
         [ 0.11611298,  0.1896512 ,  0.18384555]],


        [[ 0.14782903,  0.04730529,  0.12417638],
         [ 0.12717136,  0.16956181,  0.11869327],
         [ 0.04653735,  0.05584481,  0.16288071]]])

 In[111]:a,b,pi = BaumWelchAlgo_n(A,B,Pi,O,5)

 In[112]:a
 Out[112]: 
 array([[ 0.43972438,  0.15395857,  0.40631705],
        [ 0.309058  ,  0.45055446,  0.24038754],
        [ 0.3757005 ,  0.50361975,  0.12067975]])

 In[113]:b
 Out[113]: 
 array([[ 0.50277235,  0.49722765],
        [ 0.49524289,  0.50475711],
        [ 0.91925551,  0.08074449]])

 In[114]:pi
 Out[114]: array([[ 0.08435301,  0.18040718,  0.73523981]])

 In[115]:viterbi(A,B,Pi,O)
 Delta矩阵: 
  array([[ 0.1    ,  0.028  ,  0.00756],
        [ 0.16   ,  0.0504 ,  0.01008],
        [ 0.28   ,  0.042  ,  0.0147 ]])
 Psi矩阵: 
  array([[ 0.,  3.,  2.],
        [ 0.,  3.,  2.],
        [ 0.,  3.,  3.]])
 最优路径概率: 
  0.014699999999999998
 最优路径: 
  array([[ 3.,  3.,  3.]])
 __main__:192: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future