欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

hmm进行英文词性标注

发布时间:2023/12/16 编程问答 34 豆豆
生活随笔 收集整理的这篇文章主要介绍了 hmm进行英文词性标注 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

 

这里采用的手写维特比算法进行词性标注 

我们也可以选择第三方的库进行计算

https://github.com/jmschrei/yahmm

 

 

 

import nltk import sys from nltk.corpus import brown """ 做这个之前 首先我们要知道 我们知道什么 要求什么 第一步 我们需要根据 给定的语料库 进行学习hmm模型的 (A,B,π) A 是状态转移矩阵 在我们词库之中 就是tag之间转移的概率矩阵 对整个语料进行统计 P(ti | t{i-1}) = count(t{i-1}, ti) / count(t{i-1}) 计算t(i-1)到 ti的转移概率 就是ti在t(i-1)后面的个数/ T(i-1)tag的总数 B是每个隐藏状态到可见状态的概率分布 就是在tag下出现这个可见状态的概率 也就是在Tag下出现这个单词的概率 这个在整个语料库之中也可以统计 P(wi | ti) = count(wi, ti) / count(ti) 等于 tag1下所有的单词 /tag的数量的单词 π 是初始概率 就是在0时刻 statr 到每个tag的概率 这个在语料库中也可以统计 等于 start-tag1的数量 / 所有start到tag的数量根据语料库 计算出模型所有的参数 (A,B,π) 就可以利用hmm的模型求解三种问题 1.根据hmm模型 用遍历算法 求出 发生这个可见状态链条概率是多少 可以求出发生这个可见状态的可能性是多大2.第2个问题就是我们这里词性标注要解决的问题 给出一个模型(A,B,π) 可见状态链 也就一句话 求出 隐藏状态链 也就是单词的词性  维特比算法 动态规划求解  也可以用第三方的库 将参数 (A,b,π)带入求和 我们这里是自己实现这个算法 求和3。第三种问题这个例子中我们无关   说的是模型的参数(A.B.π)不确定  根据我们出现的可见状态 不断调整 求出发生这个可见状态最大可能性  这个模型的参数是什么 在词性标注的应用就是 给出一句话 然后给出一个隐藏状态链tag 判断一下匹配度 给出一句话  求出每个单词的词性 """""" 预处理词库 这里需要的预处理是 给词加上开始和结束符合 brown 里面的句子都是自己标注好了的,形式如下:(i.NOUN),(LOVE,VERB),(YOU,NOUN) 我们用 (START,START)(END.END) 的格式做开始和结束的标注 下载语料库    """ #nltk.download('brown')brown_tags_words=[] for sent in brown.tagged_sents():#加上开头brown_tags_words.append(("START","START"))#省略tag 只去tag前2个字母brown_tags_words.extend([(tag[:2],word) for (word,tag) in sent])#结尾加上结束符合brown_tags_words.append(("END","END"))print(brown_tags_words[0]) #('START', 'START')""" 词统计 这里计算的是 模型参数B 这个时候,我们要把我们所有的词库中拥有的单词与tag之间的关系,做个简单粗暴的统计。也就是我们之前说过的:P(wi | ti) = count(wi, ti) / count(ti)你可以自己一个个的loop全部的corpus,当然,这里NLTK给了我们做统计的工具, (这属于没有什么必要的hack,装起逼来也不X,所以,大家想自己实现,可以去实现,不想的话,就用这里提供的方法) """ # conditional frequency distribution cfd_tagwords = nltk.ConditionalFreqDist(brown_tags_words) # conditional probability distribution cpd_tagwords = nltk.ConditionalProbDist(cfd_tagwords, nltk.MLEProbDist) """ 好,现在我们看看平面统计下来的结果: 可以拿到每个tag到 单词的概率 也就是可见状态的分布概率  模型参数B 动词duck的概率  JJ new的概率  """ print("The probability of an adjective (JJ) being 'new' is", cpd_tagwords["JJ"].prob("new")) print("The probability of a verb (VB) being 'duck' is", cpd_tagwords["VB"].prob("duck")) """ The probability of an adjective (JJ) being 'new' is 0.01472344917632025 The probability of a verb (VB) being 'duck' is 6.042713350943527e-05 """""" 计算模型参数 A 隐藏状态的转移矩阵  这里面我们也把start 到各个tag的概率也一起计算了  也就是我们这次统计 计算了 2个参数  一个是A 一个是π初始概率   好,接下来,按照课上讲的,还有第二个公式需要计算:P(ti | t{i-1}) = count(t{i-1}, ti) / count(t{i-1})这个公式跟words没有什么卵关系。它是属于隐层的马科夫链。所以 我们先取出所有的tag来。 """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")) print( "If we have just seen 'VB', the probability of 'JJ' is", cpd_tags["VB"].prob("DT")) print( "If we have just seen 'VB', the probability of 'NN' is", cpd_tags["VB"].prob("NN")) """ DT 转移到NN的概率 VB到DT的概率 VB到NN的 概率  If we have just seen 'DT', the probability of 'NN' is 0.5057722522030194 If we have just seen 'VB', the probability of 'JJ' is 0.016885067592065053 If we have just seen 'VB', the probability of 'NN' is 0.10970977711020183 """""" 下面可以根据语料计算出来的Hmm 模型 进行词性标注求解第2类问题  可以根据维特比算法进行计算  也可以用第三方面实现的库进行计算  YAhMm进行计算 我们这里采用的是自己实现算法 https://github.com/jmschrei/yahmm 第三方实现库 """"""一些有趣的结果: 那么,比如, 一句话,"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) #The probability of the tag sequence 'START PP VB TO VB END' for 'I want to race' is: 1.0817766461150474e-14""" 根据hmm进行词性标注环节 Viterbi 的实现 如果我们手上有一句话,怎么知道最符合的tag是哪组呢? 首先,我们拿出所有独特的tags(也就是tags的全集) """ distinct_tags = set(brown_tags) sentence = ["I", "want", "to", "race" ] sentlen = len(sentence)""" 接下来,开始维特比:从1循环到句子的总长N,记为i每次都找出以tag X为最终节点,长度为i的tag链动态规划 从第一个开始算 """ viterbi = [ ] """ 同时,还需要一个回溯器:从1循环到句子的总长N,记为i把所有tag X 前一个Tag记下来。 """ backpointer = [ ] first_viterbi = { } first_backpointer = { } for tag in distinct_tags:# don't record anything for the START tagif tag == "START": continuefirst_viterbi[ tag ] = cpd_tags["START"].prob(tag) * cpd_tagwords[tag].prob( sentence[0] )first_backpointer[ tag ] = "START"print(first_viterbi) print(first_backpointer)""" 以上,是所有的第一个viterbi 和第一个回溯点。接下来,把楼上这些,存到Vitterbi和Backpointer两个变量里去 first_viterbi 这个变量记录的是 每个tag下是这个单词的概率 那么动态规划的原理 这里可以取出 当前概率最大的一个tag作为tag 然后存到backpoint之中 {'CS': 0.0, 'QL': 0.0, 'NP': 1.7319067623793952e-06, 'IN': 0.0, 'AP': 0.0, 'NI': 3.3324520848931064e-07, '(-': 0.0, ')-': 0.0, 'CC': 0.0, 'WD': 0.0, 'END': 0.0, 'NR': 0.0, 'CD': 0.0, '*': 0.0, '(': 0.0, 'DT': 0.0, 'RP': 0.0, 'WQ': 0.0, ':': 0.0, 'WP': 0.0, "''": 0.0, '--': 0.0, 'AT': 0.0, "'": 0.0, 'DO': 0.0, 'PN': 0.0, ',-': 0.0, 'NN': 1.0580313619573935e-06, ')': 0.0, 'AB': 0.0, 'RB': 0.0, 'HV': 0.0, '``': 0.0, '*-': 0.0, ',': 0.0, 'JJ': 0.0, 'FW': 0.0, 'PP': 0.014930900689060006, 'TO': 0.0, 'MD': 0.0, 'EX': 0.0, '.-': 0.0, '.': 0.0, 'UH': 0.0, 'RN': 0.0, 'WR': 0.0, 'VB': 0.0, ':-': 0.0, 'BE': 0.0, 'OD': 0.0} first_backpointer {'CS': 'START', 'QL': 'START', 'NP': 'START', 'IN': 'START', 'AP': 'START', 'NI': 'START', '(-': 'START', ')-': 'START', 'CC': 'START', 'WD': 'START', 'END': 'START', 'NR': 'START', 'CD': 'START', '*': 'START', '(': 'START', 'DT': 'START', 'RP': 'START', 'WQ': 'START', ':': 'START', 'WP': 'START', "''": 'START', '--': 'START', 'AT': 'START', "'": 'START', 'DO': 'START', 'PN': 'START', ',-': 'START', 'NN': 'START', ')': 'START', 'AB': 'START', 'RB': 'START', 'HV': 'START', '``': 'START', '*-': 'START', ',': 'START', 'JJ': 'START', 'FW': 'START', 'PP': 'START', 'TO': 'START', 'MD': 'START', 'EX': 'START', '.-': 'START', '.': 'START', 'UH': 'START', 'RN': 'START', 'WR': 'START', 'VB': 'START', ':-': 'START', 'BE': 'START', 'OD': 'START'} """viterbi.append(first_viterbi) backpointer.append(first_backpointer) #全部存起来 """ 我们可以先看一眼,目前最好的tag是啥: 也就是把第一个单词所有tag的情况下的概率都算出来 然后取最大值 作为第一个tag的值 """ currbest = max(first_viterbi.keys(), key = lambda tag: first_viterbi[ tag ]) print( "Word", "'" + sentence[0] + "'", "current best two-tag sequence:", first_backpointer[ currbest], currbest) #Word 'I' current best two-tag sequence: START PP""" 就是这样 我们一个单词一个单词往下算 每次都找到最大的那个概率 然后作为tag链 序列 好的一些都清晰了我们开始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)""" Word 'I' current best two-tag sequence: START PP Word 'want' current best two-tag sequence: PP VB Word 'to' current best two-tag sequence: VB TO Word 'race' current best two-tag sequence: IN NN The sentence was: I want to race The best tag sequence is: START PP VB IN NN END The probability of the best tag sequence is: 5.71772824864617e-14 """

 

总结

以上是生活随笔为你收集整理的hmm进行英文词性标注的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。