隐马尔科夫

一、马尔科夫链

1.马尔科夫链:指数学中具有马尔可夫性质的离散时间随机过程。即给定当前知识或信息的情况下,过去对于未来预测无关,的这样一种前后关系。

2.马尔科夫性质:初始状态确定的情况下,给定不变的状态转移矩阵,n次循环之后最终会达到稳态的分布。下面例子中,达到稳态后,在很久的未来,每一天的天气都是q=(0.833 0.167)的一个样本点。下面一阶马尔科夫过程定义了三个部分:(1)状态:晴天、阴天。(2)初始向量 :定义系统在时间0的时候的状态的概率。比如晴天,阴天是(1,0)(3)状态转移矩阵:每种天气转换的概率。

3.马尔科夫链的缺陷很明显,前后关系的缺失,带来了信息的缺失。比如股市,如果观测市场,我们只能知道当天的成交价格、成交量等信息,我们并不知道当前股市处于什么样的状态(牛市、熊市、震荡、反弹等等)。在这种情况下,我们有两个状态集合,一个是可以观察的状态集合(股市价格成交量状态等)和一个隐藏的状态集合(股市状况)。这时候需要多一层马科夫链,隐马尔可夫HMM。

 

二、隐马尔科夫链

1.HMM是关于时序的概率模型,描述由一个隐藏的马尔科夫链生成不可观察的状态随机序列(z),再由各个状态生成观测随机序列(x)的过程。与LDA模型的假设不同(文档中的词都是独立的),HMM中x1, x2…之间是不独立的(在z1,z2…不可知的情况下)。

2.HMM的参数描述

首先我们来看如何描述Zn-1到Zn的状态,假设Zn-1由n个取值,Z n也有n个取值,当Zn-1取值为i时,Z n等于j的概率(状态转移的条件概率矩阵An*n)。

Zn到Xn的状态,当Zn取值为i时,Xn等于j的概率(观测概率分布Bn*m)。如果m=n且为单位阵时,则HMM模型就退化为了MM模型(z1, z2…zn),相当于B的存在把我们这些未观测的值z1 z2…做了混淆,所以有些教程将B称为混淆矩阵。另外,观察变量X也可能是连续的变量服从N(x| μ,xigema)。

其次,我们还差一个初始Z1的分布,取第一个不可观察状态Z1的概率为π1,取第二个不可观察状态Z2的概率为π2…

因此把上面三个罗列后我们可知,对于隐马尔可夫模型想去确定参数,就是来计算初始概率分布π、状态转移概率分布A(一定是方正)以及观测概率分布B(不一定是方正),简写记做lamda。

λ=(A,B,π)

3.隐马尔科夫链的三大问题:
evaluation:知道隐含状态的数量、转换概率和可见状态链,求掷出这个结果的概率。

Recognition:知道隐含状态的数量、转换概率和可见状态链,求隐含状态链,比如你说出来的是东北话,我们想知道的是你表达的含义是什么。
Training:知道隐含状态的数量和可见状态链,反推出每个隐含状态是什么和转换概率,模型lambda本身不确定,需要观测序列就是这样,则需要隐马尔可夫的模型是什么样子。

解决方法:

最后可观测状态是Walk Shop Clean
问题一,forward algorithm,向前算法,或者Backward Algo向后算法。时间序列的形式,以第一个时间为基础往后推。(动态规划)

问题二,Viterbi Algo,维特比算法。暴力排列所有的情况,那个排列使得到达终结点情况的概率最大。(动态规划)空间换时间,把每次能达到最大概率的中间点记录下来。

问题三,Baum-Welch Algo, 鲍姆-韦尔奇算法。只能得到局部最优解。(EM算法)。

ε_ij的意思是,从t到t+1,状态从i到j发生的最大概率。

 

三、 应用马尔科夫链做词性标准
HMM可用标注问题,在语音识别、NLP、生物信息、模式识别等领域,被实践证明是有效的算法。应用比较多的就是用于中文分词,同时它还具有发现新词的功能。在没有给定词库的情况下HMM也可以进行分词(参数斯塔),(https://www.jianshu.com/p/f140c3a44ab6 )只是会把很多词都作为新词处理。Jieba分词默认的分词模型就是HMM模型
NLTK有自带的hmm,YAHMM等库可以直接应用。但下面主要是应用viterber自己写出词性标准的算法。
根据语法的规则,我们会知道各词性之间的转移。基于这个语法Model,就可以画出双层的隐马尔科夫结构。

通过语料库corpus,总结出模型后(λ=(π,A, B)),再给新的句子做词性标注。求在单词w1…w2确定的情况下,出这个t1…tn词性标注的概率。

链式法则求解,后面取决于前面状态的条件概率形式。


怎么计算出上面表达式呢,Aij的计算是把所有i在前j在后的tag的次数/所有出现i的tag的次数。B是在这个tag下能出单词w的概率,其计算方法是tag是k的情况下出的单词是w的次数/tag等于k的次数。在所有句子的开头加一个start,πinit就等于从start到所有tags的概率分布。

给出一句话,找出最符合的tags,HMM的问题二,要用到vertibi算法。找出最优的tags路径,使得下面这四个单词出现的概率最大。

#代码 NLTK有自带的hmm,YAHMM等库可以直接应用。但下面主要是应用viterber自己写出词性标准的算法。

import nltk
import sys
from nltk.corpus import brown
# 给词们加上开始和结束的符号
brown_tags_words = [ ]
for sent in brown.tagged_sents():
    # 先加开头
    brown_tags_words.append( ("START", "START") )
    # 为了省事儿,我们把tag都省略成前两个字母
    brown_tags_words.extend([ (tag[:2], word) for (word, tag) in sent ])
    # 加个结尾
    brown_tags_words.append( ("END", "END") )
#词统计
#B 计算P(wi | ti) = count(wi, ti) / count(ti)
# conditional frequency distribution
cfd_tagwords = nltk.ConditionalFreqDist(brown_tags_words)
# conditional probability distribution
cpd_tagwords = nltk.ConditionalProbDist(cfd_tagwords, nltk.MLEProbDist)
#看下统计结果
print("The probability of an adjective (JJ) being 'new' is", cpd_tagwords["JJ"].prob("new"))

#A 计算P(ti | t{i-1}) = count(t{i-1}, ti) / count(t{i-1})
brown_tags = [tag for (tag, word) in brown_tags_words ]
# count(t{i-1} ti)
# bigram的意思是 前后两个一组,联在一起
cfd_tags= nltk.ConditionalFreqDist(nltk.bigrams(brown_tags))
# P(ti | t{i-1})
cpd_tags = nltk.ConditionalProbDist(cfd_tags, nltk.MLEProbDist)
#查看效果
print("If we have just seen 'DT', the probability of 'NN' is", cpd_tags["DT"].prob("NN"))
#那么,比如, 一句话,"I want to race", 一套tag,"PP VB TO VB"
#他们之间的匹配度有多高呢?
#其实就是:P(START) * P(PP|START) * P(I | PP) *P(VB | PP) * P(want | VB) *P(TO | VB) * P(to | TO) *P(VB | TO) * P(race | VB) *P(END | VB)
prob_tagsequence = cpd_tags["START"].prob("PP") * cpd_tagwords["PP"].prob("I") * \
    cpd_tags["PP"].prob("VB") * cpd_tagwords["VB"].prob("want") * \
    cpd_tags["VB"].prob("TO") * cpd_tagwords["TO"].prob("to") * \
    cpd_tags["TO"].prob("VB") * cpd_tagwords["VB"].prob("race") * \
    cpd_tags["VB"].prob("END")

print( "The probability of the tag sequence 'START PP VB TO VB END' for 'I want to race' is:", prob_tagsequence)

#viterbi实现,如果我们手上有一句话,怎么知道最符合的tag是哪一组。
#首先,我们拿出所有独特的tags(也就是tags的全集)
distinct_tags = set(brown_tags)
# 初始π计算 把跟start相连的第一组都计算出来
viterbi = [ ]
backpointer = [ ]
first_viterbi = { }
first_backpointer = { }
for tag in distinct_tags:
    # don't record anything for the START tag
    if tag == "START": continue
    first_viterbi[ tag ] = cpd_tags["START"].prob(tag) * cpd_tagwords[tag].prob( sentence[0] )
    first_backpointer[ tag ] = "START"
print(first_viterbi)
print(first_backpointer)
#接下来,把楼上这些,存到Vitterbi和Backpointer两个变量里去
viterbi.append(first_viterbi)
backpointer.append(first_backpointer)

# 开始Loop
for wordindex in range(1, len(sentence)):
    this_viterbi = { }
    this_backpointer = { }
    prev_viterbi = viterbi[-1]
    for tag in distinct_tags:
        # START没有卵用的,我们要忽略
        if tag == "START": continue
# 如果现在这个tag是X,现在的单词是w,
        # 我们想找前一个tag Y,并且让最好的tag sequence以Y X结尾。
        # 也就是说
        # Y要能最大化:
        # prev_viterbi[ Y ] * P(X | Y) * P( w | X)
        best_previous = max(prev_viterbi.keys(),key = lambda prevtag: prev_viterbi[ prevtag ] * cpd_tags[prevtag].prob(tag) * cpd_tagwords[tag].prob(sentence[wordindex]))
        this_viterbi[ tag ] = prev_viterbi[ best_previous] * cpd_tags[ best_previous ].prob(tag) * cpd_tagwords[ tag].prob(sentence[wordindex])
        this_backpointer[ tag ] = best_previous
    # 每次找完Y 我们把目前最好的 存一下
    currbest = max(this_viterbi.keys(), key = lambda tag: this_viterbi[ tag ])
print( "Word", "'" + sentence[ wordindex] + "'", "current best two-tag sequence:", this_backpointer[ currbest], currbest)
# 完结
# 全部存下来
viterbi.append(this_viterbi)
backpointer.append(this_backpointer)

#找end结束
# 找所有以END结尾的tag sequence
prev_viterbi = viterbi[-1]
best_previous = max(prev_viterbi.keys(), key = lambda prevtag: prev_viterbi[ prevtag ] * cpd_tags[prevtag].prob("END"))
prob_tagsequence = prev_viterbi[ best_previous ] * cpd_tags[ best_previous].prob("END")
# 我们这会儿是倒着存的。。。。因为。。好的在后面
best_tagsequence = [ "END", best_previous ]
# 同理 这里也有倒过来
backpointer.reverse()

#最终:回溯所有的回溯点,此时,最好的tag就是backpointer里面的current best
current_best_tag = best_previous
for bp in backpointer:
    best_tagsequence.append(bp[current_best_tag])
current_best_tag = bp[current_best_tag]
#显示结果
best_tagsequence.reverse()
print( "The sentence was:", end = " ")
for w in sentence: print( w, end = " ")
print("\n")
print( "The best tag sequence is:", end = " ")
for t in best_tagsequence: print (t, end = " ")
print("\n")
print( "The probability of the best tag sequence is:", prob_tagsequence)

 

posted @ 2018-01-08 14:48  fionaplanet  阅读(1546)  评论(0编辑  收藏  举报