隐马尔科夫模型(hidden Markov model,HMM)是可用来解决标注问题的统计学模型。
HMM由初始状态概率分布π、状态转移矩阵A、状态观测概率矩阵B组成,称为HMM的三要素,表示为。此外,定义所有可能状态的几何Q,所有可能观测的集合V,以及一个随机生成的状态序列I和对应的观测序列O。
对这些变量怎么理解呢?下面举个例子。
假设有3个盒子( ),每个盒子装有红白两色的球(
),各个盒子的红白球数由下表列出:
由上表知,状态观测概率矩阵为:
其中盒子1传到盒子2概率为1,盒子2传到盒子1和3概率分别为0.4和0.6,盒子3停留在盒子3和传到盒子2概率均为0.5。因此状态转移矩阵为:
初始状态选择各个盒子的概率均等( ,π是隐藏的,并且随时间变化),由当前盒子取出一个球并记录颜色,放回,接着由转移概率确定下一状态的盒子,取出一个球并记录颜色,以此列推。由此得到的观测序列
(其中一种可观测状态)。
HMM有2个基本假设:
(1)齐次马尔科夫性假设,即任意时刻t的状态只与前一时刻 相关,与其他时刻的状态和观测无关。
(2)观测独立性假设,即任意时刻的观测只与当前时刻的状态有关,与其他时刻的状态和观测无关。
HMM有3个基本的问题:
(1)概率计算问题:计算给定模型 下观测序列O出现的概率
。(前向、后向算法求解,但本质都是穷举+概率运算)
(2)学习问题:给定观测序列O,求出模型 极大似然估计下的参数。(Baum-Welch算法,类似于EM算法)
(3)预测问题:也称解码问题,给定模型 和观测序列O,求概率
最大的状态序列(近似算法和维特比算法)。
近似算法是贪婪算法,在每一时刻找寻从当前到下一状态的最大概率,这样往往得到的只是局部最优解,因此较不合理。
维特比算法是用动态规划的方法计算概率最大路径(路径即状态序列)和观测值生成概率,解法类似于最短路算法。算法步骤:
(1)初始化,确定初始观测值(t=1)由各个状态生成的概率;
(2)递推,对观测t=2,3,…T分别计算每个状态产生观测值t的概率最大路径,并记录概率最大路径的前一状态。
(3)比较,在t=T的观测值对应的概率最大路径即为观测序列参数的最优路径,确定最优路径的终点。
(4)倒推,由最优路径终点倒推得到路径上记录的各个状态。
Python实例:将上述的盒子中取球的例子用python程序实现,下表是产生的实验结果表。
最后附上python程序:
# encoding: utf-8
import numpy as np
import pdb
def forward_viterbi(obs, states, start_p, trans_p, emit_p):
T = {}
for state in states:
T[state] = (start_p[state]*emit_p[state][obs[0]], [state], start_p[state]*emit_p[state][obs[0]])
for i in range(1, len(obs)):
output = obs[i]
U = {}
for next_state in states:
total = 0
argmax = None
valmax = 0
for source_state in states:
(prob, v_path, v_prob) = T[source_state]
p = emit_p[source_state][output] * trans_p[source_state][next_state]
prob *= p
v_prob *= p
total += prob
if v_prob > valmax:
argmax = v_path + [next_state]
valmax = v_prob
U[next_state] = (total, argmax, valmax)
T = U
total = 0
argmax = None
valmax = 0
for state in states:
(prob, v_path, v_prob) = T[state]
total += prob
if v_prob > valmax:
argmax = v_path
valmax = v_prob
return total, argmax, valmax
def main():
states_set = ('box1', 'box2', 'box3') #状态集合Q:盒子列表
observations_set = ('red', 'white') #观测集合V:小球颜色
start_probability = {'box1': 0.33,
'box2': 0.33,
'box3': 0.33} #初始概率分布pi
transition_probability = {
'box1' : {'box1': 0, 'box2': 1, 'box3':0},
'box2' : {'box1': 0.4, 'box2':0, 'box3':0.6},
'box3' : {'box1': 0, 'box2': 0.5, 'box3':0.5}
}
emission_probability = {
'box1' : {'red': 0.5, 'white': 0.5},
'box2' : {'red': 0.3, 'white': 0.7},
'box3' : {'red': 0.8, 'white': 0.2}
}
observations =['white', 'white', 'white']#观测集合V:小球颜色
ret = forward_viterbi(observations,
states_set,
start_probability,
transition_probability,
emission_probability)
print 'observations : %r' %observations
print 'obs_probability : %.3f' %float(ret[0])
print 'optimum_rounts : %r' %ret[1]
print 'OR_probability : %.3f' %float(ret[2])
if __name__ == '__main__':
main()