隐马尔可夫模型
隐马尔可夫模型(Hidden Markov Model,HMM)是描述两个时序序列联合分布p(x,y)的概率模型。
x序列外界可见(外界指的是观测者),称为观测序列( observation sequence ) ; y序列外界不可见,称为状态序列( state sequence )。
隐马尔可夫模型之所以称为“马尔可夫模型”,是因为它满足马尔可夫假设。
马尔可夫假设:每个事件的发生概率只取决于前一个事件。
隐马尔可夫模型利用了三个要素来模拟时序序列的发生,分别为初始状态概率向量、状态转移概率矩阵和发射概率矩阵(观测概率矩阵)
初始状态概率向量
P(B) = 0.7\ P(M) = 0\ P(E) = 0\ P(S) = 0.3
p(y_1=B)=0.7\
p(y_1=M)=0\
p(y_1=E)=0\
p(y_1=S)=0.3\
此时的隐马尔可夫模型的初始状态概率向量为:[0.7,0,0,0.3]

发射概率矩阵
有了状态y之后那么怎么确定对应的观测x的概率分布呢?
这时根据隐马尔可夫假设②任意时刻观测x只依赖于该时刻的y,于其他时刻的状态和观测无关。若观测x一共有M种可能,那么x的概率分布参数向量的维度为M。由于y一共有N种,那么这些参数构成了N*M矩阵(size(状态) * size(x)),成为发射概率矩阵B。


初始状态概率向量、状态转移概率矩阵与发射概率矩阵成为隐马尔可夫模型的三元组。三元组确定后,隐马尔可夫模型就确定了。
隐马尔可夫模型的三个基本用法
- 样本生成问题:给定模型三元组,生成满足模型约束的样本。
- 模型训练问题:给定训练集,估计模型三元组参数
- 序列预测问题:已知模型三元组参数,给定观测序列x,求最可能的状态y
样本生成问题实例
设想如下案例:
某医院招标开发“智能”医疗诊断系统,用来辅助感冒诊断。
- ①来诊者只有两种状态:要么健康,要么发烧。
- ②来诊者不确定自己到底是哪种状态,只能回答感觉头晕、体寒或正常。
- ③感冒这种病,只跟病人前一天的状态有关。
- ④当天的病情决定当天的身体感觉。
有位来诊者的病历卡上完整地记录了最近T天的身体感受(头晕、体寒或正常),请预测这T天的身体状态(健康或发烧)。
由于医疗数据属于机密隐私,医院无法提供训练数据,但根据医生经验,感冒发病的规律如图所示。

也就是说,在初始状态下有0.4的概率第二天发烧,然后有0.1的概率正常。
我们可以写出隐马尔可夫模型:
初始概率向量π:
【0.6,0.4】
状态转移概率矩阵A:
| 0.7 | 0.3 |
|---|---|
| 0.4 | 0.6 |
发射概率矩阵B:
| 0.5 | 0.4 | 0.1 |
|---|---|---|
| 0.1 | 0.3 | 0.6 |
样本生成:
生成过程就是沿着隐马尔可夫链走T步。
代码:
import random
pi=[0.6,0.4]
y=['健康','发烧']
x=['头晕','体寒','正常']
A=[[0.7,0.3],
[0.4,0.6]]
B=[[0.5,0.4,0.1],
[0.1,0.3,0.6]]
size=4
def generateHMM(status,size):
symList=[0 for i in range(size)]
symList[0]=x[status]
initSym=0 if random.random()<=pi[0] else 1
for i in range(1,size):
symList[i],initSym=continueGen(initSym,i)
print(symList)
def continueGen(now,i):
if i==size-1:
return y[now],0
else:
flag=random.random()
genx= 1 if flag>=A[now][0] else 0#下一代的病情
flag=random.random()
if flag<=B[now][0]:
return str(y[now]+" "+x[0]),genx
elif flag<=(B[now][1]+B[now][0]):
return str(y[now]+" "+x[1]),genx
else:
return str(y[now]+" "+x[2]),genx
generateHMM(0,4)
generateHMM(1,4)
generateHMM(2,4)
输出如下:
['头晕', '发烧 正常', '健康 体寒', '健康']
['体寒', '发烧 正常', '发烧 正常', '发烧']
['正常', '健康 头晕', '发烧 正常', '发烧']
由于随机数的原因,每次运行的结果都是随机的。
模型训练问题实例
通过上述样本来进行监督学习来估计模型参数。
在监督学习中,通过利用极大似然法来估计隐马尔科夫模型的参数。
初始状态概率向量的估计:
转移概率矩阵的估计:
发射概率矩阵的估计:
p(y_1=S_i)=\pi_i\ …①
p(y_t=S_j|y_{t-1}=s_i)=A_{i,j}\ …②
p(y)=p(y_1,…,y_T)=p(y_1)\prod_{t=2}^{T}p(y_t|y_{t-1})
p(x_t=O_j|y_t=s_i)=B_{i,j}\ …③
p(x|y)=\prod_{t=1}^{T}p(x_t|y_{t})\ …④
p(x,y)=p(y_1)\prod_{t=2}^{T}p(y_t|y_{t-1})\prod_{t=1}^{T}p(x_t|y_{t})
首先求:\
P(晴天,干燥)=\pi(晴天)B(晴天,干燥)=0.32
\p(阴天,干燥)=\pi(阴天)B(阴天,干燥)=0.18\
p(雨天,干燥)=\pi(雨天)B(雨天,干燥)=0.09\
接下来求:\P(潮湿,晴天|T_1=干燥)=(0.320.4+0.180.30.090.1)0.2=0.382\
P(潮湿,阴天|T_1=干燥)=(0.320.5+0.180.4+0.090.5)0.4=0.1108\
P(潮湿,雨天|T_1=干燥)=(0.320.1+0.180.3+0.09*0.4)*0.7=0.0854
\依此类推:\
P(干燥,晴天|T_1=干燥,T_2=潮湿)=0.0456\
…
P=0.0456+0.0637+0.0214=0.1307
#### 维特比算法 在构成了图后,接下来使用维特比算法寻找最优的状态序列:  在上面的表格例子中:  ## HMM应用于中文分词 大体步骤为: * 用{B,M,E,S}进行标注 * 使用HMM训练 在hanlp中已经实现了HMM分词器,因此可以直接使用API进行调用: ```python FirstOrderHiddenMarkovModel = JClass('com.hankcs.hanlp.model.hmm.FirstOrderHiddenMarkovModel') def train(model): segmenter.train('./msr_training.utf8') return segmenter.toSegment() segment = train(msr_train, FirstOrderHiddenMarkovModel()) [商品, 和, 服务]#output 2. 计算转移概率矩阵 4. 使用 Viterbi 算法解码 如果没有标注语料,则问题的解决过程: 1. 获取词的个数 3. 参数学习(利用 EM 迭代算法获取初始状态概率、状态转移概率和输出概率)