6

概率图模型系列(3):隐马尔可夫模型(HMM)

 2 years ago
source link: https://allenwind.github.io/blog/7681/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

HMM是描述时间序列生成的概率模型,属于概率生成模型,由一个隐藏的马尔可夫链随机生成不可观测的状态序列,再由各个状态随机生成一个可观测状态,进而构成观测序列的过程。

由于 HMM 涉及到的内容比较多,需要马尔科夫链的基础知识,可以参考过去文章马尔可夫链及其Python实现。有了马尔科夫链的知识后,HMM的定义就相对简单了。HMM是由一条不可观察的马尔科夫链生成不可观察的状态序列,再由个状态生成可观察变量的生成模型。

HMM及其参数描述

HMM把马尔可夫链作为隐变量(离散的不可观察变量),通过隐变量生成不可观察的序列(隐状态序列),再由其序列中的各个时间点的状态生成可观察的变量。假设隐变量状态空间为,

S={s1,s2,…,sn}

例如,在中文分词中,常用BMES作为状态空间,

S={B,M,E,S}

假设可观察的变量的状态空间(观察空间)为,

V={v1,v2,…,vm}

例如,在中文分词中,所有的汉字作为观察空间,

V={中文词汇组成的集合}

HMM生成的隐状态序列与可观察序列是一一对应的,例如隐状态序列为,

I=(st1,st2,…,stk)

对应的观察序列为,

O=(ot1,ot2,…,otk)

例如,SSBEBME对应“我爱北京天安门”。示意图如下,

上图对应的隐状态序列为,

S→S→B→E→B→M→E

对应的观察序列为,

我 爱 北 京 天 安 门

以上这些都很好理解吧。

事实上,注意到,HMM的每个时间步都可以看做是朴素贝叶斯模型,

任意一个时间步的y(此处对应BMES标签集)对应x1,…,xn(此处为汉字集),因此HMM可以看成是朴素贝叶斯模型在时间维度上的扩展,并使用马尔科可假设约定相邻状态的约束。

HMM的隐状态序列I总有个开头吧?比如上述的“SSBEBME”,开头为“S”状态。开头出现不同的状态有不同的概率,我们称它为初始状态分布,表示为,

π=(π1,…,πn)

其中πi=P(i1=si)​表示初始时刻t=1​处于状态si​​的概率。就是统计大量初始分布的概率。

HMM的隐状态的转换由状态转移矩阵决定,

A=[aij]n×n

矩阵中的每个元素为,

aij=P(St+1=sj|St=si)

表示在时刻t状态为si转移到时刻t+1状态为sj的概率是aij。可以看到时刻t+1状态为sj只由时刻t状态为si​决定,和更久远时刻的状态无关,这称为马尔科可假设。注意到由于概率的归一性,有

∑jaij=1

HMM的隐状态对应的可观察状态由观察概率矩阵(发射矩阵)决定,

B=[bjk]n×m

矩阵中的每个元素为,

bjk=P(Vt=vk|St=sj)

表示在时刻t状态为sj生成对应时刻的可观察状态vk的概率是bjk,有时候我们也用bj(k)来说表示。这里也可以看到,观察状态vk之由当前时刻的状态为sj​​决定,这称为观察独立性假设。注意到,由于概率的归一性,有

∑kbjk=1

初始状态分布、状态转移矩阵和观察矩阵决定了HMM的所有参数,即

λ=(A,B,π)

总之,HMM考虑两点假设:

  • 隐状态的状态转移受到马尔科可假设约束
  • 观察状态只由当前时刻的隐状态决定,即观察独立性假设

HMM的概率化描述

以上我们使用基于矩阵的形式描述HMM及其参数,其实它还有基于概率的描述,两种是等价的。首先HMM可以解决序列标注问题,以中文分词为例。例如输入中文序列(x1,x2,…,xn),HMM要预测其标注序列(y1,y2,…,yn),可以简单表示为,

(y1,y2,…,yn)←(x1,x2,…,xn)

基于HMM的两点约束,HMM的联合概率分布为,

ˆy=argmaxyP(y|x)=argmaxyP(x,y)P(x)=argmaxyP(x,y)=argmaxyn∏i=1P(xi|yi)P(yi|yi−1)=argmaxyn∑i=1(logP(xi|yi)+logP(yi|yi−1))

其中x=(x1,x2,…,xn),y=(y1,y2,…,yn),P(y1|y0)=P(y1),P(xi|yi)称为发射概率,所有可能组成观察矩阵,P(yi|yi−1)称为转移概率,所有的可能组成状态转移矩阵。

于是,P(y1|y0)=P(y1)对应上一节中HMM的参数π,P(xi|yi)对应观察矩阵B中相应的元素,P(yi|yi−1)对应状态矩阵A​中相应的元素。

因此,描述HMM有两套数学语言,一套是概率语言、一套是矩阵的语言,其实它们是等价的。

HMM四个基本问题

HMM作为概率图模型涉及四个基本问题。

采样与生成:已知HMM模型,生成观察序列O和隐状态序列I

概率计算问题:已知HMM模型,计算观察序列的概率P(O|λ)

参数学习问题:已知观察序列O,状态序列I​已知或未知,计算HMM的参数λ

隐状态序列预测问题:已知HMM模型,以及观察序列O,推断隐状态序列I

采样和生成

HMM是生成模型,典型的贝叶斯网络,即有向无环图(DAG)模型。其生成样本的过程:

  • (1)首先根据初始状态分布采样以隐状态
  • (2)根据当前隐状态和状态转移矩阵获得下一时间步隐状态的分布
  • (3)根据当前隐状态获得观察状态分布,从该分布中采样一个观察样本并输出
  • (4)迭代下一个时间步

观察序列概率计算

首先我们来解决观察序列概率计算,假定我们已经知道HMM的参数λ=(A,B,π),计算观察序列的概率P(O|λ)

最直接的思路是枚举所有的隐状态序列,

p(O|λ)=∑IP(O,I|λ)=∑IP(I|λ)P(O|I,λ)

这里I=(i1,…,iT)是一个排列组合的求和,计算量非常大,是 O(TNT) 时间复杂度。这里有一个技巧,为避免连续乘积的数值下溢,可以取对数然后求和。

既然直接计算算法复杂度大,那么可以使用间接计算,以下示意图作为计算的参考辅助,

有与对观察序列的计算是从左到右,因此成为前向计算方法

αt(i)=P(o1,…,ot,it=qi)

容易计算初始值,

α1(i)=P(o1,i1=qi)=πiP(o1|i1=qi)=πibi(o1)

向前递归计算

αt+1(i)=P(o1,…,ot,ot+1,it+1=qi)=N∑j=1P(o1,…,ot,ot+1,it=qj,it+1=qi)=N∑j=1P(o1,…,ot,it=qj,it+1=qi)P(ot+1|it+1=qi)=N∑j=1P(o1,…,ot,it=qj)P(it+1=qi|it=qj)P(ot+1|it+1=qi)=[N∑j=1αt(i)aji]bi(ot+1)

这个过程的推导技巧是还原出αt(i),这样就可以利用上一步的计算结果递归计算。一直递推下去可获得αT(i)​的结果,

αT(i)

迭代到第T​步,所有的隐状态构成结果,

P(O)=N∑i=1αT(i)

类似的思路,后向计算,定义

βt(i)=P(ot+1,ot+2,…,oT|it=qi)

容易计算βT(i),

βT(i)=1

向后递归计算,

βt−1(i)=P(ot,ot+1,ot+2,…,oT|it−1=qi)=N∑j=1P(ot,ot+1,ot+2,…,oT,it=qj|it−1=qi)=N∑j=1P(it=qj|it−1=qi)P(ot|it=qj)P(ot+1,ot+2,…,oT|it=qi)=N∑j=1aijbj(ot)βt(i)

其思路依旧是展开后能够还原出βt(i)​以便利用上一步计算的结果,如此迭代下去获得,β1(i)的结果,

β1(i)=P(o2,o3,…,oT|i1=qi)

于是获得观察序列概率,

P(O)=N∑i=1πibi(o1)β1(i)

间接计算尽管看起来有点复杂,但是其思路很简单,就是构造概率计算的递归关系,并逐时间步迭代。

HMM参数学习

HMM参数学习分两类:

  • 已知观察序列O,状态序列I​已知
  • 已知观察序列O,状态序列I未知

对于序列标注问题,状态序列I已知相当于标签已知,所以也称为带标签的学习问题。于之对应的是状态序列I未知,即无标签的学习问题。

带标签的学习问题

状态转移矩阵参数估计,aij=P(it+1=qj|it=qi)

ˆaij=AijN∑j=1Aij

其中i,j取值均在[1,N]范围内。

观察矩阵的参数估计,

ˆbj(k)=BjkM∑k=1Bjk

其中j∈[1,N],k∈[1,M]​,通常在实现的时候使用稀疏形式,如字典来存储参数。如果遇到OOV问题,可以统一使用一个来处理。

初始状态π=(π1,…,πN),其中

πi=P(i1=si)

即所有待学习样本的初始状态的概率。不过初始状态在这里用不上,在生成任务上才需要。

无标签的学习问题

无标签的学习问题,即无监督序列建模,一般使用Baum-Welch,实则是EM算法在HMM中的应用。

隐状态序列预测

隐状态序列预测就是已知HMM模型,以及观察序列O,推断隐状态序列I​。有两种策略:

  • 基于动态规划的维特比算法

贪心策略是每个时间步取概率最大的状态而不考虑前后时间步的约束。假设给定模型参数λ=(π,A,B)​以及观察序列O=(o1,o2,…,oT)​,令

γt(i)=P(it=qi|O;λ)

表示在时间步t​观察到O​条件下处于状态qi​​的概率。

于是,贪心策略下,第t个时间步的解为,

ˆit=argmax[γt(1),γ2(2),…,γT(N)]

这里N为隐状态数量。依次解得t=1,2,…,T,获得隐状态序列ˆI=(ˆi1,ˆi2,…,ˆiT)。

贪心策略计算十分简单,但是并没有考虑到状态转移的约束,因此不能保证解得的状态序列是最优序列。该算法通常用在考虑解码速度(计算性能)而不过分强调模型性能的场景上。至于对模型性能的考虑,则要使用维特比算法。

维特比算法

以上分析我们知道HMM求隐状态序列就是求,

ˆy=argmaxyn∑i=1(logP(xi|yi)+logP(yi|yi−1))

中y=(y1,y2,…,yn)的最大概率,称为最优解码。最优解码不光是P(xi|yi)的最大值,还要考虑P(yi|yi−1)。

其中,P(yi|yi−1)​表示相邻两个标签间的转移概率,比如,

P(Bt|Mt−1)

表示时间步t−1标签为M转移到时间步t标签为B的概率,当然正确的标注下这样的标签转移是不存在的。这里还需要注意一个边界条件,就是P(y1|y0)表示初始状态分布。

而,P(xi|yi)​​表示标签概率分布,对于给定的时间步i​​,x​​的取值是固定的,比如观察变量“北”,但是其对应的标签是有不同的概率,一般HMM、CRF、CNN、RNN或者Dense+softmax网络都可以计算。比如“北”字,

P(北|yi)={P(北|B)P(北|M)P(北|E)P(北|S)

类似地,“京”字,

P(京|yi)={P(京|B)P(京|M)P(京|E)P(京|S)

综合上述,那么沿着所有时间步前进,上式就构成篱笆(Lattice)网络的有向无环图(DAG)。如下图所示,

于是求解ˆy=argmaxyP(y|x)就成了篱笆(Lattice)网络上的最大概率路径或最短路问题。它的计算复杂度是O(T⋅N2),T是时间步数,N是状态数量。其实抛开HMM看,维特比算法解决的就是使用动态规划解决方阵型DAG的最短路算法,使用CNN、RNN、Dense+softmax等模型解决序列标注问题同样可以使用维特比解码最优序列。

维特比算法要做的是在这些众多的路径组合中找到概率最大的路径。上图路径长度为7,每个时间步有4种可选择的状态,因此路径总共有47​种。对于更一般的情况,路径长度为l​,每个时间步状态数量为n​,那么所有路径组合有nl​​种。直接枚举所有路径搜索是不可能的。

最短路中有一个简单朴素的特性:如果(i1,…,ij,…,in)是所有路径中的最短路,那么任意ij切分出的两段分路径(i1,…,ij)和(ij,…,in)都是这两段路径的端点所构成的所有可能路径中最优的,否则如果还有更优的分路径,就会有比原来更短的路径,这与一开始的最优路径假设相悖。这个特性用来排除在前行搜索最优路径时把不符合以上特性的分路径过滤掉。

例如,上图的最优路径为S→S→B→E→B→M→E。最优路径的分路径(最优路径的某一点把它切成两段)也是最优,否则分路径可以找到更优的分路径,那么合并到最优路径就得到一条比原来最优路径更好的路径,这与假设矛盾。

因此,维特比算法的核心思想是过滤掉不符合最短路思想的路径

定义δt(i)表示时刻t状态为i的最优路径概率,

δt(i)=maxi1,…,it−1P(it=i,it−1,…,i1,ot,…,o1|λ)

于是有地推关系,

δt+1(i)=maxi1,…,itP(it+1=i,it,…,i1,ot+1,…,o1|λ)=max1≤j≤N[δt(j)aji]bi(ot+1)

时刻t+1​​状态为i​​的所有路径中概率最大路径在t​​时刻的状态为,

ˆit+1=ψt+1(i)=argmax1≤j≤N[δt(j)aji]

以上这些递推过程的中间参数都可以使用矩阵来存储。对于时刻T,容易求得,

ˆiT=argmax1≤j≤N[δT(i)]

于是获得的ˆiT容易递推回溯,令ˆit+1=ˆiT​,有

ˆit=ψt+1(ˆit+1)ˆit−1=ψt(ˆit)ˆit−2=ψt−1(ˆit−1)…ˆi1=ψ2(ˆi2)

于是就获得最优解码,

I=[ˆi1,…,ˆiT]

需要注意一点,在实现时为避免数值溢出,一般都是取log后求和避免逐时间步连续乘积。

基于numpy的实现如下,

import numpy as np

def viterbi_decode(scores, trans, return_score=False):
# 使用viterbi算法求最优路径
# scores.shape = (seq_len, num_tags)
# trans.shape = (num_tags, num_tags)
dp = np.zeros_like(scores)
backpointers = np.zeros_like(scores, dtype=np.int32)
dp[0] = scores[0]
for t in range(1, scores.shape[0]):
v = np.expand_dims(dp[t-1], axis=1) + trans
dp[t] = scores[t] + np.max(v, axis=0)
backpointers[t] = np.argmax(v, axis=0)

viterbi = [np.argmax(dp[-1])]
for bp in reversed(backpointers[1:]):
viterbi.append(bp[viterbi[-1]])
viterbi.reverse()
if return_score:
viterbi_score = np.max(dp[-1])
return viterbi, viterbi_score
return viterbi

HMM .vs. Navie bayes

朴素贝叶斯分类器的目标函数是后验概率最大化,分类器可以写成,

ˆk=argmaxk∈{1,…,K}p(Ck)∏ip(x(i)|Ck)

HMM是对序列建模,最大概率序列输出可以表示为,

ˆy=argmaxyn∏i=1P(xi|yi)P(yi|yi−1)

两者都是生成模型,直接对P(x,y)建模,HMM可以看成是Navie bayes在时间维度上的拓展,同时考虑相邻状态的马尔科夫约束。

HMM的不足之处

HMM两个基本假设:

  • 观察值之间严格独立,观察独立性假设
  • 状态转移过程中,当前状态仅依赖于前一个状态(一阶马尔科夫模型)

这两个假设换成中文分词语境来说就是,在隐状态集为{B,M,E,S}下,当前标注的取值取决于前一时间步的标注,而当前时刻的观察值(中文字)只由当前时间步的标注决定。显然,这样的约束导致模型忽视上下文特征表达能力不够。

后期补充了实现,项目实现源码地址:hmm-ner-cws

本文从两套数学语言出发描述HMM,然后简单梳理一下HMM的四个问题,包括:HMM的观察序列生成、观察序列相关的概率计算、参数学习以及维特比算法。

[1] 《统计自然语言处理》

[2] 《统计机器学习》

[3] https://en.wikipedia.org/wiki/Maximum-entropy_Markov_model


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK