
隐马尔科夫模型(hidden Markov model,HMM)是可用于标注问题的统计学习模型,描述由隐藏的马尔可夫链随机生成观测序列的过程,属于生成模型。隐马尔可夫模型在语音识别、自然语言处理、生物信息、模式识别等领域有着广泛的应用。
本文内容部分引用于 李航《统计学习方法》
一个不一定恰当的例子:你只知道一个人今天是散步了,逛街了,还是在家打扫卫生,推测今天的天气;这个人在下雨天可能在家打扫卫生,也有可能雨中散步,晴天可能去逛街,也有可能散步,或者打扫卫生 隐藏状态 Y(不可见):下雨,晴天 观察状态 X(可见): 散步,逛街,打扫房间
假设我们观测到的状态是
序列,
, 隐藏状态是
序列,
我们在知道观测序列
的情况下,求有多大概率是该隐藏状态序列
现在我们要求的就是
隐马尔可夫模型 两个假设
仅依赖于前一个状态
只跟他的隐含状态
相关
, 且
式忽略分母
隐马尔可夫模型由 三要素 决定
,(初始处于各个隐藏状态
的概率)
,(即式(1)中的
的各种项构成的矩阵)
, (即式(2)中的
的各种项构成的矩阵)
和
决定状态序列,
决定观测序列。隐马尔可夫模型
可以用三元符号表示,
与 初始状态概率向量
确定了隐藏的马尔可夫链,生成不可观测的状态序列。
确定了如何从隐藏状态
生成观测
,与状态序列综合确定了如何产生观测序列。
假设有4个盒子,每个盒子里都装有红、白两种颜色的球,盒子里的红、白球数由表列出。

按下面的方法抽球,产生一个球的颜色的观测序列
:

:
:
:
,观测序列长度
(该例子为 5)
, (红、白球)
随机产生一个
状态(盒子), 序列长度 t = 0
中,按照其观测概率
对应的行,产生一个观测序列
的元素
(红球 or 白球),计数 t++
的状态转移概率
对应的行,产生下一个状态
, while(t < T), 重复2,3步骤
和 观测序列
, 计算在模型
下,观测序列
出现的概率
,估计模型
的参数,使得在该模型下,观测序列概率
最大。极大似然估计的方法估计参数
和 观测序列
,求对给定观测序列条件概率
最大的状态序列
。即给定观测序列
,求最有可能的对应隐含状态序列
给定模型
和 观测序列
, 计算在模型
下,观测序列
出现的概率
的状态序列
与 观测序列
的联合概率
,得到
的概率是:
,观测序列
的概率是:
同时出现的联合概率为:
的情况下求和
,即可得到
但是上面计算量很大,复杂度
,不可行
,定义到时刻 t 部分观测序列
且 t 时刻的状态
为
的概率为前向概率,记为:
及 观测序列概率
输入:给定HMM模型
, 观测序列
输出:观测序列概率
,
为盒子个数
,
(看前向概率定义,全概率公式)
算法解释:
,观测到的是
, 可能的状态是盒子的编号
,概率为
,在
盒子发射出
颜色球的概率为
,所以
的 N 种情况下 都可能转移到
时的
状态,对应的前向概率乘以转移概率,并求和,得到 状态
的概率,再乘以发射概率
,就是
时的前向概率

时的所有前向概率求和就是
时刻,直接用
时刻的结果,时间复杂度

首先有公式联合概率
,对任意多个项都成立 递推公式证明:
第一个蓝色处:前
个观测序列,显然跟
时刻的状态
无关,第二个蓝色处:观测独立性
终止公式证明(全概率公式):
考虑盒子和球模型
,状态集合(盒子的编号)
,观测集合
设总的时间长度
, 观测序列
,用前向算法计算
解 :
,(1号盒子,时刻1摸出红色(第一列)的概率)
,(2号盒子,时刻1摸出红色的概率)
,(3号盒子,时刻1摸出红色的概率)
,
(时刻2时,从1号盒子摸出白色的概率)
(时刻2时,从2号盒子摸出白色的概率)
(时刻2时,从3号盒子摸出白色的概率)
(时刻3时,从1号盒子摸出红色的概率)
(时刻3时,从2号盒子摸出红色的概率)
(时刻3时,从3号盒子摸出红色的概率)
# -*- coding:utf-8 -*-
# python3.7
# @Time: 2019/12/17 22:33
# @Author: Michael Ming
# @Website: https://michael.blog.csdn.net/
# @File: hiddenMarkov.py
import numpy as np
class HiddenMarkov:
def forward(self, Q, V, A, B, X, PI): # 前向算法
N = len(Q) # 隐藏状态数量
M = len(X) # 观测序列大小
alphas = np.zeros((N, M)) # 前向概率 alphas 矩阵
T = M # 时刻长度,即观测序列长度
for t in range(T): # 遍历每个时刻,计算前向概率 alphas
indexOfXi = V.index(X[t]) # 观测值Xi对应的V中的索引
for i in range(N): # 对每个状态进行遍历
if t == 0: # 计算初值
alphas[i][t] = PI[t][i] * B[i][indexOfXi] # a1(i)=πi B(i,x1)
print("alphas1(%d)=p%dB%d(x1)=%f" %(i,i,i,alphas[i][t]))
else:
alphas[i][t] = np.dot(
[alpha[t-1] for alpha in alphas], # 取的alphas的t-1列
[a[i] for a in A]) *B[i][indexOfXi] # 递推公式
print("alpha%d(%d)=[sigma alpha%d(i)ai%d]B%d(x%d)=%f"
%(t, i, t-1, i, i, t, alphas[i][t]))
print(alphas)
P = sum(alpha[M-1] for alpha in alphas) # 最后一列概率的和
print("P=%f" % P)
Q = [1, 2, 3]
V = ['红', '白']
A = [[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]]
B = [[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]]
# X = ['红', '白', '红', '红', '白', '红', '白', '白']
X = ['红', '白', '红'] # 书上的例子
PI = [[0.2, 0.4, 0.4]]
hmm = HiddenMarkov()
hmm.forward(Q, V, A, B, X, PI)关于numpy的一些操作:
>>> a
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
>>> b
array([[ 7, 8],
[ 9, 10],
[11, 12]])
>>> np.dot(a[0],[B[1] for B in b])
64
>>> np.dot([A[0] for A in a],[B[1] for B in b])
132
>>> np.multiply(a[0],[B[1] for B in b])
array([ 8, 20, 36])
>>> np.multiply([A[0] for A in a],[B[1] for B in b])
array([ 8, 40, 84])运行结果:(跟上面计算一致)
alphas1(0)=p0B0(x1)=0.100000
alphas1(1)=p1B1(x1)=0.160000
alphas1(2)=p2B2(x1)=0.280000
alpha1(0)=[sigma alpha0(i)ai0]B0(x1)=0.077000
[[0.1 0.077 0. ]
[0.16 0. 0. ]
[0.28 0. 0. ]]
alpha1(1)=[sigma alpha0(i)ai1]B1(x1)=0.110400
[[0.1 0.077 0. ]
[0.16 0.1104 0. ]
[0.28 0. 0. ]]
alpha1(2)=[sigma alpha0(i)ai2]B2(x1)=0.060600
[[0.1 0.077 0. ]
[0.16 0.1104 0. ]
[0.28 0.0606 0. ]]
alpha2(0)=[sigma alpha1(i)ai0]B0(x2)=0.041870
[[0.1 0.077 0.04187]
[0.16 0.1104 0. ]
[0.28 0.0606 0. ]]
alpha2(1)=[sigma alpha1(i)ai1]B1(x2)=0.035512
[[0.1 0.077 0.04187 ]
[0.16 0.1104 0.035512]
[0.28 0.0606 0. ]]
alpha2(2)=[sigma alpha1(i)ai2]B2(x2)=0.052836
[[0.1 0.077 0.04187 ]
[0.16 0.1104 0.035512]
[0.28 0.0606 0.052836]]
P=0.130218,定义到时刻
状态
为
的条件下,从
到
的部分观测序列
的概率为后向概率,记为:
及 观测序列概率
输入:给定HMM模型
, 观测序列
输出:观测序列概率
,
为盒子个数
,
算法解释:

递推公式证明:
第一个蓝色处:观测独立性;第二个蓝色处:观测独立性(
都与
无关)
终止公式证明:
利用 前向 和 后向 概率的定义,可以将观测序列概率
写成:
证明如下:
import numpy as np
class HiddenMarkov:
def backward(self, Q, V, A, B, X, PI): # 后向算法
N = len(Q) # 隐藏状态数量
M = len(X) # 观测序列大小
betas = np.ones((N, M)) # 后向概率矩阵 betas
for i in range(N):
print("beta%d(%d)=1" %(M, i)) # 后向概率初始为1
for t in range(M-2, -1, -1):
indexOfXi = V.index(X[t+1]) # 观测值Xi对应的V中的索引
for i in range(N):
betas[i][t] = np.dot(
np.multiply(A[i], [b[indexOfXi] for b in B]), # A的i行 B的Xi列 = 行
[beta[t+1] for beta in betas]) # 递推公式
realT = t+1 # 真实的时间,t从1开始
realI = i+1 # 状态标号,从1开始
print("beta%d(%d)=[sigma A%djBj(x%d)beta%d(j)]=("
%(realT, realI, realI, realT+1, realT+1), end='') # end,表示以该符号空开,不换行
for j in range(N):
print("%.2f*%.2f*%.2f+" %(A[i][j], B[j][indexOfXi],betas[j][t+1]), end=' ')
print("0)=%.3f" % betas[i][t])
print(betas)
indexOfXi = V.index(X[0])
P = np.dot(
np.multiply(PI, [b[indexOfXi] for b in B]),
[beta[0] for beta in betas])
print("P(X|lambda)=", end=" ")
for i in range(N):
print("%.1f*%.1f*%.5f+" % (PI[0][i], B[i][indexOfXi], betas[i][0]), end=" ")
print("0=%f" % P)
Q = [1, 2, 3]
V = ['红', '白']
A = [[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]]
B = [[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]]
# X = ['红', '白', '红', '红', '白', '红', '白', '白']
X = ['红', '白', '红'] # 书上的例子
PI = [[0.2, 0.4, 0.4]]
hmm = HiddenMarkov()
hmm.backward(Q, V, A, B, X, PI)运行结果:
beta3(0)=1
beta3(1)=1
beta3(2)=1
beta2(1)=[sigma A1jBj(x3)beta3(j)]=(0.50*0.50*1.00+ 0.20*0.40*1.00+ 0.30*0.70*1.00+ 0)=0.540
beta2(2)=[sigma A2jBj(x3)beta3(j)]=(0.30*0.50*1.00+ 0.50*0.40*1.00+ 0.20*0.70*1.00+ 0)=0.490
beta2(3)=[sigma A3jBj(x3)beta3(j)]=(0.20*0.50*1.00+ 0.30*0.40*1.00+ 0.50*0.70*1.00+ 0)=0.570
beta1(1)=[sigma A1jBj(x2)beta2(j)]=(0.50*0.50*0.54+ 0.20*0.60*0.49+ 0.30*0.30*0.57+ 0)=0.245
beta1(2)=[sigma A2jBj(x2)beta2(j)]=(0.30*0.50*0.54+ 0.50*0.60*0.49+ 0.20*0.30*0.57+ 0)=0.262
beta1(3)=[sigma A3jBj(x2)beta2(j)]=(0.20*0.50*0.54+ 0.30*0.60*0.49+ 0.50*0.30*0.57+ 0)=0.228
[[0.2451 0.54 1. ]
[0.2622 0.49 1. ]
[0.2277 0.57 1. ]]
P(X|lambda)= 0.2*0.5*0.24510+ 0.4*0.4*0.26220+ 0.4*0.7*0.22770+ 0=0.130218
# 概率跟前向算法计算的是一致的结论1:
证明:
结论2: 给定模型
和观测序列
,在时刻
处于状态
的概率:
证明:
结论3: 给定模型
和观测序列
,在时刻
处于状态
且在时刻
处于
的概率 :
证明: 由上面可知:
结论4:
下状态
出现的期望值:
下由状态
转移的期望值:
下由状态
转移到状态
的期望值:
隐马尔可夫模型的学习,根据训练数据进行分类:
数据 | 学习方法 |
|---|---|
观测序列 XXX + 对应的状态序列 YYY | 监督学习 |
观测序列 XXX | 无监督学习(BaumBaumBaum-WelchWelchWelch / EMEMEM 算法) |
+ 对应的状态序列
监督学习 观测序列
无监督学习(
-
/
算法)
,
:
到
状态的频数
转移到所有状态的频数和
,
观测
:
状态观测到
的频数
状态观测到所有
的频数和
,
在样本中的初始概率
由于监督学习需要使用标注的训练数据,而人工标注训练数据往往代价很高,需要用无监督学习的方法。
-
算法
(先跳过)
根据 2.4 节 结论2:每个时刻 t 处于状态
的概率可以计算
那么每一时刻 t 的最有可能的状态就是概率最大的那个状态。
算法
维特比算法实际是用动态规划(dynamic programming)解隐马尔可夫模型预测问题,即用动态规划求概率最大路径(最优路径)。这时一条路径对应着一个状态序列。
2个定义:
的递推公式:
t-1 个结点为
viterbi 算法:
,观测序列
;
例题: 考虑盒子和球模型
,状态集合(盒子的编号)
,观测集合
已知观测序列
,求最优状态序列
解:
(1)初始化 在
时刻,对每一个状态 i ,i = 1,2,3, 求状态为 i 观测
为红的概率,记为
:

(2)在
时刻,对每个状态 i ,i = 1,2,3, 求在
时状态为
观测为 红 并在
时刻状态为 i 观测为
为白的路径的最大概率,记此最大概率为
同时,对每个状态
记录概率最大路径的前一个状态
:
计算:
在
时刻,
(3)以
表示最优路径的概率,则
最优路径的终点是
(4)由最优路径的终点
,逆向找到前面的路径
于是求得最优路径,最优状态序列
import numpy as np
class HiddenMarkov:
def viterbi(self, Q, V, A, B, X, PI): # 维特比算法,求最优状态序列
N = len(Q) # 隐藏状态数量
M = len(X) # 观测序列大小
deltas = np.zeros((N, M))
psis = np.zeros((N, M))
Y = np.zeros((1, M)) # 最优(概率最大)状态序列
for t in range(M):
realT = t+1 # 真实时刻,从1开始
indexOfXi = V.index(X[t]) # 观测值Xi对应的V中的索引
for i in range(N):
realI = i+1 # 状态序号,从1开始
if t == 0:
deltas[i][t] = PI[0][i]* B[i][indexOfXi] # 初始值
psis[i][t] = 0
print("delta1(%d)=pi_%d * B%d(x1)=%.2f * %.2f=%.2f" %
(realI, realI, realI, PI[0][i], B[i][indexOfXi], deltas[i][t]))
print('psis1(%d)=0' % realI)
else:
deltas[i][t] = np.max(
np.multiply([delta[t-1] for delta in deltas],[a[i] for a in A])) \
* B[i][indexOfXi] # 递推公式
print("delta%d(%d)=max[delta%d(j)Aj%d]B%d(x%d)=%.2f*%.2f=%.5f"
% (realT, realI, realT-1, realI, realI, realT,
np.max(np.multiply([delta[t-1] for delta in deltas], [a[i] for a in A])),
B[i][indexOfXi], deltas[i][t]))
psis[i][t] = np.argmax(
np.multiply([delta[t-1] for delta in deltas],[a[i] for a in A])) + 1 # 序号从1开始
print("psis%d(%d)=argmax[delta%d(j)Aj%d]=%d" %
(realT, realI, realT-1, realI, psis[i][t]))
print(deltas)
print(psis)
Y[0][M-1] = np.argmax([delta[M-1] for delta in deltas]) +1 # 序号从1开始
print("Y%d=argmax[deltaT(i)]=%d" %(M, Y[0][M-1])) # 最优路径的终点
for t in range(M-2, -1, -1): # 逆序推导前面的路径
Y[0][t] = psis[int(Y[0][t+1])-1][t+1] # 寻找前面路径
print("Y%d=psis%d(Y%d)=%d" %(t+1, t+2, t+2, Y[0][t]))
print("最大概率的状态序列Y是: ", Y)
Q = [1, 2, 3]
V = ['红', '白']
A = [[0.5, 0.2, 0.3], [0.3, 0.5, 0.2], [0.2, 0.3, 0.5]]
B = [[0.5, 0.5], [0.4, 0.6], [0.7, 0.3]]
# X = ['红', '白', '红', '红', '白', '红', '白', '白']
X = ['红', '白', '红'] # 书上的例子
PI = [[0.2, 0.4, 0.4]]
hmm = HiddenMarkov()
hmm.viterbi(Q, V, A, B, X, PI)运行结果:(与上面计算一致)
delta1(1)=pi_1 * B1(x1)=0.20 * 0.50=0.10
psis1(1)=0
delta1(2)=pi_2 * B2(x1)=0.40 * 0.40=0.16
psis1(2)=0
delta1(3)=pi_3 * B3(x1)=0.40 * 0.70=0.28
psis1(3)=0
delta2(1)=max[delta1(j)Aj1]B1(x2)=0.06*0.50=0.02800
psis2(1)=argmax[delta1(j)Aj1]=3
delta2(2)=max[delta1(j)Aj2]B2(x2)=0.08*0.60=0.05040
psis2(2)=argmax[delta1(j)Aj2]=3
delta2(3)=max[delta1(j)Aj3]B3(x2)=0.14*0.30=0.04200
psis2(3)=argmax[delta1(j)Aj3]=3
delta3(1)=max[delta2(j)Aj1]B1(x3)=0.02*0.50=0.00756
psis3(1)=argmax[delta2(j)Aj1]=2
delta3(2)=max[delta2(j)Aj2]B2(x3)=0.03*0.40=0.01008
psis3(2)=argmax[delta2(j)Aj2]=2
delta3(3)=max[delta2(j)Aj3]B3(x3)=0.02*0.70=0.01470
psis3(3)=argmax[delta2(j)Aj3]=3
[[0.1 0.028 0.00756]
[0.16 0.0504 0.01008]
[0.28 0.042 0.0147 ]]
[[0. 3. 2.]
[0. 3. 2.]
[0. 3. 3.]]
Y3=argmax[deltaT(i)]=3
Y2=psis3(Y3)=3
Y1=psis2(Y2)=3
最大概率的状态序列Y是: [[3. 3. 3.]]