文章目录
一、隐马尔可夫模型
隐马尔可夫模型(Hidden Markov Model,HMM)描述由隐藏的马尔可夫链随机生成观测序列的过程,属于生成模型。
名词解释:
-
马尔科夫链
马尔可夫链的提出来自俄国数学家安德雷·马尔可夫,指具有马尔可夫性质且存在于离散的指数集和状态空间内的随机过程。 -
马尔科夫模型
一种统计模型,广泛应用在语音识别,手语作为特征类型相似的语言,亦可采用此方法进行研究。 -
隐马尔可夫模型
由马尔可夫链生成随机不可观测的随机状态序列,再由各个状态生成可观测的随机序列。
听得云里雾里,没关系,wiki实例说明:
Alice 和Bob是好朋友,但是他们离得比较远,每天都是通过电话了解对方那天作了什么.Bob仅仅对三种活动感兴趣:公园散步,购物以及清理房间。他选择做什么事情只凭当天天气,Alice对于Bob所住的地方的天气情况并不了解,但是知道总的趋势。在Bob告诉Alice每天所做的事情基础上,Alice想要猜测Bob所在地的天气情况。
Alice认为天气的运行就像一个马尔可夫链。 其有两个状态 “雨”和”晴”,但是无法直接观察它们,也就是说,它们对于Alice是隐藏的。每天,Bob有一定的概率进行下列活动:”散步”, “购物”,或 “清理”。因为Bob会告诉Alice他的活动,所以这些活动就是Alice的观察数据。这整个系统就是一个隐马尔可夫模型HMM。
Alice关心Bob每天干了什么事情(观测序列)「散步,购物,大扫除」,想要通过Bob干的事情去推断出Bob处地的天气变化(隐藏序列)「晴天,雨天」:
Alice和Bob通了三天电话后发现第一天Bob去散步了,第二天他去购物了,第三天他清理房间了。Alice现在有两个问题:
- 这个观察序列“散步、购物、清理”的总的概率是多少?(注:这个问题对应于HMM的基本问题之一:已知HMM模型λ及观察序列O,如何计算P(O|λ)?)
- 最能解释这个观察序列的状态序列(晴/雨)又是什么?(注:这个问题对应HMM基本问题之二:给定观察序列O=O1,O2,…OT以及模型λ,如何选择一个对应的状态序列S = q1,q2,…qT,使得S能够最为合理的解释观察序列O?)
- 至于HMM的基本问题之三:如何调整模型参数, 使得P(O|λ)最大?这个问题事实上就是给出很多个观察序列值,来训练以上几个参数的问题。
结论:
隐藏序列:表示无法直接观测到的状态,就上述例子而言就是对方的天气状态;
观测序列:可以直接观测到的状态,就上述例子而言就是获取到的B执行的事情;
初始状态概率矩阵:表示B第一次告诉A的时候,得到的隐藏状态集合中每个状态发生的概率;
状态转移矩阵:表示从一个状态到另一个状态变化的概率,如在上述例子中,从雨天到晴天的概率为0.3;
发射状态矩阵:表示B在相应隐藏状态(天气)下,去执行得到观测状态(执行的动作)的概率,如在雨天,B去散步的概率为0.1;
综上五个要素:
- 两个序列:隐藏序列和观测序列
- 三个矩阵:初始状态矩阵,发射状态矩阵以及状态转移矩阵
class HMM:
def __init__(self):
self.A=array([(0.1,0.4,0.5),(0.6,0.3,0.1)])
self.B=array([(0.3,0.7),(0.6,0.4)])
self.pi=array([(0.4),(0.6)])
self.o=[0,1,0]
self.t=len(self.o)#观测序列长度
self.m=len(self.A)#状态集合个数
self.n=len(self.B[0])#观测集合个数
获取状态序列:
def get_stateprobability(self,t,p):
#打印在观测为self.o的前提下,t时刻,处于状态p的概率,
#self.x[t][p]表示到t时刻观测为o1,o2,ot,状态为p的概率
#self.y[t][p]表示在t时刻状态为p的前提下,接下来观测为ot+1,ot+2,oT的概率
#self.x[t][p]*self.y[t][p]即表示观测为self.o,且t时刻处于状态p的概率,
#利用贝叶斯公式,除以观测为self.o的概率即为所求
if(t>self.t or p>self.m):
print(u'输入数据超过范围')
return
print('在时刻'+str(t)+u'处于状态'+str(p)+u'的概率是:')
temp=self.x[t-1][p-1]*self.y[t-1][p-1]
total=0
for i in range(self.m):
total=total+self.x[t-1][i]*self.y[t-1][i]
print(temp/total)
计算方法:
- 直接法
- 前向法
def qianxiang(self):
#t时刻部分观测序列为o1,o2,ot,状态为qi的概率用矩阵x表示,
#则矩阵大小行数为观测序列数,列数为状态个数
self.x=array(zeros((self.t,self.m)))
#先计算出时刻1时,观测为o1,状态为qi的概率
for i in range(self.m):
self.x[0][i]=self.pi[i]*self.B[i][self.o[0]]
for j in range(1,self.t):
for i in range(self.m):
#前一时刻所有状态的概率乘以转移概率得到i状态概率
#i状态的概率*i状态到j观测的概率
temp=0
for k in range(self.m):
temp=temp+self.x[j-1][k]*self.A[k][i]
self.x[j][i]=temp*self.B[i][self.o[j]]
result=0
for i in range(self.m):
result=result+self.x[self.t-1][i]
print("前向概率矩阵及当前观测序列概率如下:")
print(self.x)
print(result)
- 后向法
def houxiang(self):
#t时刻状态为qi,从t+1到T观测为ot+1,ot+2,oT的概率用矩阵y表示
#则矩阵大小行数为观测序列数,列数为状态个数
self.y=array(zeros((self.t,self.m)))
#下面为对最终时刻的所有状态,接下来的观测序列概率初始化为1
#(可以理解为接下来没有观测所有为1)
for i in range(self.m):
self.y[self.t-1][i]=1
j=self.t-2
#j时刻为i状态,转移到k状态,k状态观测为oj+1,
#再乘以j+1时刻状态为k的B矩阵的值,对k遍历相加,
#即为j时刻i状态前提下,后面满足观测序列的概率
while(j>=0):
for i in range(self.m):
for k in range(self.m):
self.y[j][i]+=self.A[i][k]*self.B[k][self.o[j+1]]*self.y[j+1][k]
j=j-1
#第一个状态任意,观测为o1,所以对所有第一个状态概率相加
result=0
for i in range(self.m):
result=result+self.pi[i]*self.B[i][self.o[0]]*self.y[0][i]
print('后向概率矩阵及当前观测序列概率如下:')
print(self.y)
print(result)
- 维特比算法
def viterbi(self):
#利用模型和观测序列找出最优的状态序列
#时刻t时,很多路径可以到达状态i,且观测为self.o,
#每个路径都有自己的概率,最大的概率用矩阵z记录,前一个状态用d矩阵记录
self.z=array(zeros((self.t,self.m)))
self.d=array(zeros((self.t,self.m)))
for i in range(self.m):
self.z[0][i]=self.pi[i]*self.B[i][self.o[0]]
self.d[0][i]=0
for j in range(1,self.t):
for i in range(self.m):
maxnum=self.z[j-1][0]*self.A[0][i]
node=1
for k in range(1,self.m):
temp=self.z[j-1][k]*self.A[k][i]
if(maxnum<temp):
maxnum=temp
node=k+1
self.z[j][i]=maxnum*self.B[i][self.o[j]]
self.d[j][i]=node
#找到T时刻概率最大的路径
max_probability=self.z[self.t-1][0]
last_node=[1]
temp=0
for i in range(1,self.m):
if(max_probability<self.z[self.t-1][i]):
max_probability=self.z[self.t-1][i]
last_node[0]=i+1
temp=i
i=self.t-1
#self.d[t][p],t时刻状态为p的时候,t-1时刻的状态
while(i>=1):
last_node.append(self.d[i][temp])
i=i-1
temp=['o']
temp[0]=int(last_node[len(last_node)-1])
j=len(last_node)-2
while j>=0:
temp.append(int(last_node[j]))
j=j-1
print('路径节点分别为')
print(temp)
print('该路径概率为'+str(max_probability))
二、python实例
- 手写马尔科夫算法函数
完整代码:(源码参考链接)
#coding=utf-8
'''
https://github.com/Eilene/HMM-python/blob/master/HMM.py
实现隐马尔科夫模型的基本方法,
输入为状态转移矩阵,观测矩阵,初始状态概率向量,观测序列
实现前向算法和后向算法计算观测序列出现的概率
实现维特比算法找当前观测序列下最可能的状态序列
实现在给定模型和观测下,t时刻处于p状态的概率,t,p在main函数中指定
'''
from numpy import *
class HMM:
def __init__(self):
self.A=array([(0.1,0.4,0.5),(0.6,0.3,0.1)])
self.B=array([(0.3,0.7),(0.6,0.4)])
self.pi=array([(0.4),(0.6)])
self.o=[0,1,0]
self.t=len(self.o)#观测序列长度
self.m=len(self.A)#状态集合个数
self.n=len(self.B[0])#观测集合个数
def qianxiang(self):
#t时刻部分观测序列为o1,o2,ot,状态为qi的概率用矩阵x表示,
#则矩阵大小行数为观测序列数,列数为状态个数
self.x=array(zeros((self.t,self.m)))
#先计算出时刻1时,观测为o1,状态为qi的概率
for i in range(self.m):
self.x[0][i]=self.pi[i]*self.B[i][self.o[0]]
for j in range(1,self.t):
for i in range(self.m):
#前一时刻所有状态的概率乘以转移概率得到i状态概率
#i状态的概率*i状态到j观测的概率
temp=0
for k in range(self.m):
temp=temp+self.x[j-1][k]*self.A[k][i]
self.x[j][i]=temp*self.B[i][self.o[j]]
result=0
for i in range(self.m):
result=result+self.x[self.t-1][i]
print("前向概率矩阵及当前观测序列概率如下:")
print(self.x)
print(result)
def houxiang(self):
#t时刻状态为qi,从t+1到T观测为ot+1,ot+2,oT的概率用矩阵y表示
#则矩阵大小行数为观测序列数,列数为状态个数
self.y=array(zeros((self.t,self.m)))
#下面为对最终时刻的所有状态,接下来的观测序列概率初始化为1
#(可以理解为接下来没有观测所有为1)
for i in range(self.m):
self.y[self.t-1][i]=1
j=self.t-2
#j时刻为i状态,转移到k状态,k状态观测为oj+1,
#再乘以j+1时刻状态为k的B矩阵的值,对k遍历相加,
#即为j时刻i状态前提下,后面满足观测序列的概率
while(j>=0):
for i in range(self.m):
for k in range(self.m):
self.y[j][i]+=self.A[i][k]*self.B[k][self.o[j+1]]*self.y[j+1][k]
j=j-1
#第一个状态任意,观测为o1,所以对所有第一个状态概率相加
result=0
for i in range(self.m):
result=result+self.pi[i]*self.B[i][self.o[0]]*self.y[0][i]
print('后向概率矩阵及当前观测序列概率如下:')
print(self.y)
print(result)
def get_stateprobability(self,t,p):
#打印在观测为self.o的前提下,t时刻,处于状态p的概率,
#self.x[t][p]表示到t时刻观测为o1,o2,ot,状态为p的概率
#self.y[t][p]表示在t时刻状态为p的前提下,接下来观测为ot+1,ot+2,oT的概率
#self.x[t][p]*self.y[t][p]即表示观测为self.o,且t时刻处于状态p的概率,
#利用贝叶斯公式,除以观测为self.o的概率即为所求
if(t>self.t or p>self.m):
print(u'输入数据超过范围')
return
print('在时刻'+str(t)+u'处于状态'+str(p)+u'的概率是:')
temp=self.x[t-1][p-1]*self.y[t-1][p-1]
total=0
for i in range(self.m):
total=total+self.x[t-1][i]*self.y[t-1][i]
print(temp/total)
def viterbi(self):
#利用模型和观测序列找出最优的状态序列
#时刻t时,很多路径可以到达状态i,且观测为self.o,
#每个路径都有自己的概率,最大的概率用矩阵z记录,前一个状态用d矩阵记录
self.z=array(zeros((self.t,self.m)))
self.d=array(zeros((self.t,self.m)))
for i in range(self.m):
self.z[0][i]=self.pi[i]*self.B[i][self.o[0]]
self.d[0][i]=0
for j in range(1,self.t):
for i in range(self.m):
maxnum=self.z[j-1][0]*self.A[0][i]
node=1
for k in range(1,self.m):
temp=self.z[j-1][k]*self.A[k][i]
if(maxnum<temp):
maxnum=temp
node=k+1
self.z[j][i]=maxnum*self.B[i][self.o[j]]
self.d[j][i]=node
#找到T时刻概率最大的路径
max_probability=self.z[self.t-1][0]
last_node=[1]
temp=0
for i in range(1,self.m):
if(max_probability<self.z[self.t-1][i]):
max_probability=self.z[self.t-1][i]
last_node[0]=i+1
temp=i
i=self.t-1
#self.d[t][p],t时刻状态为p的时候,t-1时刻的状态
while(i>=1):
last_node.append(self.d[i][temp])
i=i-1
temp=['o']
temp[0]=int(last_node[len(last_node)-1])
j=len(last_node)-2
while j>=0:
temp.append(int(last_node[j]))
j=j-1
print('路径节点分别为')
print(temp)
print('该路径概率为'+str(max_probability))
if __name__ == '__main__':
test=HMM()
test.qianxiang()
test.houxiang()
test.get_stateprobability(3,3)
test.viterbi()
参考1:把隐马尔可夫模型彻底说清楚
参考2:标注——隐马尔科夫模型(HMM)以及Python实现
学习:李航《统计学习方法》第十章——用Python实现隐马尔科夫模型
-
另外是安装
hmmlearn
库pip install hmmlearn
原理部分参考: