《统计学习方法》 p.174
隐马尔科夫模型(HMM)有三个基本的问题
《统计学习方法》 p.186
问题类型:
(3)预测问题。已知模型 lambda = (A, B, Pi) 和观测序列,求最优的状态序列.
问题描述:
一共有三个盒子 <=> 状态集合 Q = {1,2,3}
盒子里边装着 红色 和 白色 两种颜色的球 <=> 观测集合 V = {红,白}
从盒子中有放回的取 3 次球,颜色分别为 O=(红,白,红),问取球的盒子顺序最可能是什么?
注意:代码中所有下标从 0 开始,而课本中问题描述是从 1 开始。
import numpy as np
# 模型参数
A = np.asarray([[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]]) # 转移矩阵
B = np.asarray([[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]])
Pi = np.asarray([0.2, 0.4, 0.4]).transpose()
O = np.asarray([0,1,0])
T = O.shape[0]
N = A.shape[0] # 状态数
p_nodes = Pi * B[:, O[0]] # 记录每个节点的路径概率
path_nodes = list() # 记录每个节点的路径
# 计初始化路径
for node in xrange(N):
path_nodes.append([node])
# T 个时刻
for step in xrange(1, T):
for this_node in xrange(N): # 计算每个节点的新概率
p_news = list()
for last_node in xrange(N):
p_trans = A[last_node, this_node] # 转移概率
p_out = B[this_node, O[step]] # 输出概率
p_new = p_nodes[last_node] * p_trans * p_out
p_news.append(p_new)
p_nodes[this_node] = np.max(p_news) # 更新节点路径概率
last_index = np.argmax(p_news) # 更新节点路径
temp = path_nodes[last_index][:]
temp.append(this_node)
path_nodes[this_node] = temp
print p_nodes # 最有一步每个节点的概率
print path_nodes
max_index = np.argmax(p_nodes)
max_path = path_nodes[max_index]
print max_path # 最优路径
[ 0.00756 0.01008 0.0147 ]
[[2, 2, 0], [2, 2, 1], [2, 2, 2]]
[2, 2, 2]
实际项目中,一般都是通过对概率取对数,将乘法转为加法进行求解的。但是,要注意转移概率为 0 的特殊情况,下面不考虑这种情况。
##############################
# 维特比译码
# 《统计学习方法》 p.186
#############################
# 模型参数
A = np.asarray([[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]]) # 转移矩阵
B = np.asarray([[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]])
Pi = np.asarray([0.2, 0.4, 0.4]).transpose()
A = np.log(A)
B = np.log(B)
Pi = np.log(Pi)
O = np.asarray([0,1,0]) # 观测序列
def Viterbi_decode(A, B, Pi, O):
"""维特比解码,所有概率为对数概率。
输入:
A: N×N 的转移矩阵
B: N×M 的输出矩阵
Pi: list, 初始状态概率分布
O: list, 观测序列
返回:
max_path: list, 最优路径。
"""
T = O.shape[0]
N = A.shape[0] # 状态数
p_nodes = Pi + B[:, O[0]] # 记录每个节点的路径概率
path_nodes = list() # 记录每个节点的路径
# 计初始化路径
for node in xrange(N):
path_nodes.append([node])
# T 个时刻
for step in xrange(1, T):
for this_node in xrange(N): # 计算每个节点的新概率
p_news = list()
for last_node in xrange(N):
p_trans = A[last_node, this_node] # 转移概率
p_out = B[this_node, O[step]] # 输出概率
p_new = p_nodes[last_node] + p_trans + p_out
p_news.append(p_new)
p_nodes[this_node] = np.max(p_news) # 更新节点路径概率
last_index = np.argmax(p_news) # 更新节点路径
temp = path_nodes[last_index][:]
temp.append(this_node)
path_nodes[this_node] = temp
max_index = np.argmax(p_nodes)
max_path = path_nodes[max_index]
return max_path
max_path = Viterbi_decode(A, B, Pi, O)
print max_path
[2, 2, 2]
在 TensorFlow入门(六) 双端 LSTM 实现序列标注(分词)最后部分使用了维特比解码。和这里有些区别: