python+HMM之维特比解码

来源:互联网 发布:手风琴特效js 编辑:程序博客网 时间:2024/04/29 15:30

HMM 回顾

《统计学习方法》 p.174

隐马尔科夫模型(HMM)有三个基本的问题

  • (1)概率计算问题。给定模型 λ=(A,B,Pi) 和观测序列 O(o1,o2,...,oT),计算在模型 λ 下观测序列 O 的概率 P(O|λ)
  • (2)学习问题。已知观测序列 O(o1,o2,...,oT),估计模型 λ=(A,B,Pi) 的参数,使得在该模型下观测序列概率 P(O|λ) 最大。即用极大似然估计的方法估计参数。
  • (3)预测问题,也称为解码问题。已知模型 λ=(A,B,Pi) 和观测序列 O(o1,o2,...,oT),求对给定观测序列条件概率 P(I|O) 最大的状态序列 I=(i1,i2,...,iT),即给定观测序列,求最有可能的对应的状态序列。

维特比译码

《统计学习方法》 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] = tempprint 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_pathmax_path = Viterbi_decode(A, B, Pi, O)print max_path
[2, 2, 2]

在 TensorFlow入门(六) 双端 LSTM 实现序列标注(分词)最后部分使用了维特比解码。和这里有些区别:

  • 初始路径概率 pnodes=Pi+B[:,O[0]], 改成使用 Bi-LSTM 模型输出的分类概率
  • 输出概率 pout=B[thisnode,O[step]], 改成使用 Bi-LSTM 模型输出的分类概率
原创粉丝点击