在这里插入图片描述
评估问题
隐马尔可夫模型中包含一个评估问题:已知模型参数,计算某一特定输出序列的概率。通常使用forward算法解决。
比如计算活动序列{读书,做清洁,散步,做清洁,散步}出现的概率,就属于评估问题。
如果穷举的话,观察序列会有2(晴or雨)^5种,需要分别计算它们出现的概率,然后找出概率最大的。
穷举法中有很多重复计算,向前算法就是利用已有的结果,减少重复计算。
向前算法借助于一个矩阵Q[LEN][M],其中M是所有隐藏状态的种数,Q[i][j]表示从第0天到第i天,满足观察序列,且第i天隐藏状态为Sj时的所有可能的隐藏序列的概率之和。最终所求结果为Q[LEN-1][0]+…+Q[LEN-1][M-1],即最后一天,所有隐藏状态下,分别满足观察序列的概率值之和。
向前算法
向前变量图示
向后算法
向后算法变量图示
前向-后向算法
解码问题 Viterbi算法
解码问题是:已知模型参数,寻找最可能的能产生某一特定输出序列O(LEN)的隐含状态的序列。通常使用Viterbi算法解决。
观察序列长度为LEN,隐藏状态序列长度是M,如果采用穷举法,就有M^LEN种可能的隐藏状态序列,我们要计算每一种隐藏状态到指定观察序列的概率,最终选择概率最大的。
穷举法中有很多重复计算,Viterbi算法就是利用已有的结果,减少重复计算。跟评估问题非常相似,不同点在于评估算的是和,解码算的是最大值。
Viterbi算法主要就是在计算一个矩阵Q[LEN][M],其中Q[i][j]表示从第0天到第i天,满足观察序列,且第i天隐藏状态为Sj的所有可能的隐藏序列的概率的最大值。另外还要建立一个矩阵Path[LEN][M],用来记录状态序列中某一状态之前最可能的状态。
举个例子,假如指定观察序列是{读书,做卫生,散步,做卫生,散步},求出现此观察序列最可能的状态序列是什么。
Q[0][0]=p(第一天读书 , 第一天晴)=p(天晴)*p(读书|天晴) Path[0][0]=-1;
Q[0][1]=p(第一天读书 , 第一天下雨)=p(下雨)*p(读书|下雨) Path[0][1]=-1;
关键是从第二天开始,Q[1][0]表示:满足“第一天读书 且 第二做清洁 且 第二天晴”的所有可能的隐藏序列的概率的最大值。那么满足“第一天读书 且 第二做清洁 且 第二天晴”的所有可能的隐藏序列有哪些呢?
第二天是必须满足晴天的,第二天之前的状态可以任意变。则所有可能的隐藏序列就是“晴 晴”和“雨 晴”。实际上考虑第三天(及第三天以后)时,并不需要考虑“所有”可能的隐藏序列,而只需要考虑第二天的不同状态取值,这是因为马氏过程有无后效性–t时刻所处状态的概率只和t-1时刻的状态有关,而与t-1时刻之前的状态无关。
Q[1][0]=
max{ p(第一天晴, 第一天读书 , 第二天晴, 第二天做卫生) ,p(第一天下雨 且 第一天读书 且 第二天晴, 第二天做卫生) }
=max{ p(第一天读书, 第一天晴)*p(天晴转天晴),p(第一天读书, 第一天下雨)*p(下雨转天晴) } * p(做卫生|天晴)
=max{ Q[0][0]*A[0][0],Q[0][1]*A[1][0] } * B[0][2]
假如Q[0][0]*A[0][0] < Q[0][1]*A[1][0],则Path[1][0]=1;假如Q[0][0]*A[0][0] > Q[0][1]*A[1][0],则Path[1][0]=0。
Q[1][1]= ……
…… ……
可以看到计算Q矩阵的每i行时都用到了第i-1行的结果。
Viterbi算法词性标注
问题:已知已标记好词性的语料库,现给定一个句子S,求这个句子的词性Z。
根据朴素贝叶斯:P(词性|单词) = P(单词|词性)P(词性) 得到公式:
我们称给定句子为观测序列,需要求的词性为状态序列,A为观测概率矩阵,B为初始状态,C为状态转移概率矩阵,根据如上公式知道参数 = (A,B,pi),即可求得状态序列。
向前算法代码
#-*-coding:utf-8-*-
__author__ = 'ZhangHe'
def forward(N,M,A,B,P,observed):
p = 0.0
#观测变量数目
LEN = len(observed)
#中间概率LEN*M
Q = [([0]*N) for i in range(LEN)]
#第一个观测变量的概率,隐藏状态的初始概率乘上隐藏状态到观测变量的条件概率。
for j in range(N):
Q[0][j] = P[j]*B[j][observation.index(observed[0])]
#第一个之后的观测变量,首先从前一天的每个隐藏状态,转移到当前隐藏状态的概率求和,然后乘上当前隐藏状态到观测变量的条件概率。
for i in range(1,LEN):
for j in range(N):
sum = 0.0
for k in range(N):
sum += Q[i-1][k]*A[k][j]
Q[i][j] = sum * B[j][observation.index(observed[i])]
for i in range(N):
p += Q[LEN-1][i]
return p
# 3 种隐藏层状态:sun cloud rain
hidden = []
hidden.append('sun')
hidden.append('cloud')
hidden.append('rain')
N = len(hidden)
# 4 种观察层状态:dry dryish damp soggy
observation = []
observation.append('dry')
observation.append('damp')
observation.append('soggy')
M = len(observation)
# 初始状态矩阵(1*N 第一天是sun,cloud,rain的概率)
P = (0.3,0.3,0.4)
# 状态转移矩阵A(N*N 隐藏层状态之间互相转变的概率)
A=((0.2,0.3,0.5),(0.1,0.5,0.4),(0.6,0.1,0.3))
# 混淆矩阵B(N*M 隐藏层状态对应的观测层状态的概率)
B=((0.1,0.5,0.4),(0.2,0.4,0.4),(0.3,0.6,0.1))
#假设观察到一组序列为observed,输出HMM模型(N,M,A,B,P)产生观察序列observed的概率
observed = ['dry']
print forward(N,M,A,B,P,observed)
observed = ['damp']
print forward(N,M,A,B,P,observed)
observed = ['dry','damp']
print forward(N,M,A,B,P,observed)
observed = ['dry','damp','soggy']
print forward(N,M,A,B,P,observed)
向后算法代码
#-*-coding:utf-8-*-
__author__ = 'ZhangHe'
def forward(N,M,A,B,P,observed):
p = 0.0
#观察到的状态数目
LEN = len(observed)
#中间概率LEN*M
Q = [([0]*N) for i in range(LEN)]
#令最后时刻所有状态的后向变量为1
for j in range(N):
Q[LEN-1][j] = 1
# 最后时刻之前的状态,Q[i][j]=sum(第i天的当前状态Sj转移到第i+1天的状态Sk的转移概率,
# 乘上第i+1天的状态Sk生成观测变量Oi+1观测概率,再乘上第i+1天Sk状态的概率 for k in N)
for i in range(LEN-2,-1,-1):
for j in range(N):
sum = 0.0
for k in range(N):
sum += A[j][k]*B[k][observation.index(observed[i+1])]*Q[i+1][k]
Q[i][j] = sum
for i in range(N):
p += Q[0][i]
return p
# 3 种隐藏层状态:sun cloud rain
hidden = []
hidden.append('sun')
hidden.append('cloud')
hidden.append('rain')
N = len(hidden)
# 4 种观察层状态:dry dryish damp soggy
observation = []
observation.append('dry')
observation.append('damp')
observation.append('soggy')
M = len(observation)
# 初始状态矩阵(1*N第一天是sun,cloud,rain的概率)
P = (0.3,0.3,0.4)
# 状态转移矩阵A(N*N 隐藏层状态之间互相转变的概率)
A=((0.2,0.3,0.5),(0.1,0.5,0.4),(0.6,0.1,0.3))
# 混淆矩阵B(N*M 隐藏层状态对应的观察层状态的概率)
B=((0.1,0.5,0.4),(0.2,0.4,0.4),(0.3,0.6,0.1))
#假设观察到一组序列为observed,输出HMM模型(N,M,A,B,P)产生观察序列observed的概率
observed = ['dry']
print(forward(N,M,A,B,P,observed))
observed = ['damp']
print(forward(N,M,A,B,P,observed))
observed = ['dry','damp']
print(forward(N,M,A,B,P,observed))
observed = ['dry','damp','soggy']
print(forward(N,M,A,B,P,observed))
viterbi算法代码
def viterbi(N,M,A,B,P,hidden,observed):
sta = []
LEN = len(observed)
Q = [([0]*N) for i in range(LEN)]
path = [([0]*N) for i in range(LEN)]
#第一天计算,状态的初始概率*隐藏状态到观察状态的条件概率
for j in range(N):
Q[0][j]=P[j]*B[j][observation.index(observed[0])]
path[0][j] = -1
# 第一天以后的计算
# 前一天的每个状态转移到当前状态的概率最大值
# *
# 隐藏状态到观察状态的条件概率
for i in range(1,LEN):
for j in range(N):
max = 0.0
index = 0
for k in range(N):
if(Q[i-1][k]*A[k][j] > max):
max = Q[i-1][k]*A[k][j]
index = k
Q[i][j] = max * B[j][observation.index(observed[i])]
path[i][j] = index
#找到最后一天天气呈现哪种观察状态的概率最大
max = 0.0
idx = 0
for i in range(N):
if(Q[LEN-1][i]>max):
max = Q[LEN-1][i]
idx = i
print "最可能隐藏序列的概率:"+str(max)
sta.append(hidden[idx])
#逆推回去找到每天出现哪个隐藏状态的概率最大
for i in range(LEN-1,0,-1):
idx = path[i][idx]
sta.append(hidden[idx])
sta.reverse()
return sta;
# 3 种隐藏层状态:sun cloud rain
hidden = []
hidden.append('sun')
hidden.append('cloud')
hidden.append('rain')
N = len(hidden)
# 4 种观察层状态:dry dryish damp soggy
observation = []
observation.append('dry')
observation.append('damp')
observation.append('soggy')
M = len(observation)
# 初始状态矩阵(1*N第一天是sun,cloud,rain的概率)
P = (0.3,0.3,0.4)
# 状态转移矩阵A(N*N 隐藏层状态之间互相转变的概率)
A=((0.2,0.3,0.5),(0.1,0.5,0.4),(0.6,0.1,0.3))
# 混淆矩阵B(N*M 隐藏层状态对应的观察层状态的概率)
B=((0.1,0.5,0.4),(0.2,0.4,0.4),(0.3,0.6,0.1))
#假设观察到一组序列为observed,输出HMM模型(N,M,A,B,P)产生观察序列observed的概率
observed = ['dry','damp','soggy']
print viterbi(N,M,A,B,P,hidden,observed)
参考:
http://www.cnblogs.com/CheeseZH/ 作者:ZH奶酪——张贺
博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang 作者:张朝阳