python实现的基于hmm模型的词性标注系统
来源:互联网 发布:高性能计算 知乎 编辑:程序博客网 时间:2024/05/02 01:17
python实现的基于hmm模型的词性标注系统
任务定义
实现一个词性标注系统,输入分好词的单词序列,输出一个词性标注后的结果序
使用的语料库为人民日报98年公开语料库,一共约18000行语料。在用户交互模式下,所有语料库均用作训练。在文件读写模式下,前3000行语句用来做测试,后面的语句用来做训练。
方法描述
隐马尔科夫模型理解
隐马尔科夫模型是结构最简单的动态贝叶斯网络。描述由一个隐藏的马尔科夫链随机生成不可观测的状态随机序列,再由各个状态生成一个观测而产生随机序列的过程。隐藏的马尔科夫链随机生成的状态的序列称为状态序列,每个状态生成一个观测,称为观测序列。
隐马尔科夫做了两个基本的假设
- 齐次马尔科夫假设,即假设隐藏的马尔科夫链在任意时刻t的状态只依赖于前一时刻的状态,去其他观测状态无关。
- 观测独立性假设,即假设任意时刻的观测只依赖于该时刻的马尔科夫链的状态,与其他观测以及状态无关。
隐马尔科夫模型由初始状态概率向量π,状态转移概率矩阵A,以及观测概率矩阵B决定。
在词性标注问题中,初始状态概率为每个语句序列开头出现的词性的概率,状态转移概率矩阵由相邻两个单词的词性得到,观测序列为分词后的单词序列,状态序列为每个单词的词性,观测概率矩阵B也就是一个词性到单词的概率矩阵。
隐马尔科夫模型有三个基本问题:
- 概率计算问题,给出模型和观测序列,计算在模型
λ 下观测序列O出现的概率 - 学习问题,估计模型
λ=(A,B,π) 参数,使得该模型下观测序列P(0|λ) 最大,也就是用极大似然的方法估计参数 - 观测问题,已知模型
λ 和观测序列O,求对给的观测序列条件概率P(I|O)最大的状态序列I,即给的观测序列,求最可能的状态序列。
在词性标注问题中,需要解决的是学习问题和观测问题。学习问题即转移矩阵的构建,观测问题即根据单词序列得到对应的词性标注序列
- 概率计算问题,给出模型和观测序列,计算在模型
语料库的处理
在原始语料库中,存在多个连续空格以及空行等不便处理的字符。所以先用正则表达式对原始语料库进行处理。
由init.py文件实现,实现代码如下。
fin=codecs.open("语料.txt","r","utf-8")strl=fin.read()strl = re.sub("\[","",strl)strl = re.sub("]nt","",strl)strl = re.sub("]ns","",strl)strl = re.sub("]nz","",strl)strl = re.sub("]l","",strl)strl = re.sub("]i","",strl)strl = re.sub("\n", "@", strl)strl = re.sub("\s+"," ", strl)strl = re.sub("@","\n",strl)strl = re.sub(" \n","\n",strl)strl = re.sub(" ","@",strl)strl = re.sub("\s+","\n",strl)strl = re.sub("@"," ",strl)s=strl.encode("utf-8")fout=open("处理语料.txt","w")fout.write(s)fout.close()fin.close()
学习问题
三个概率的计算是该算法的核心。
转移概率
aij 的计算aij=Aij∑Nj=1Aij 其中
Aij 表示从t时刻到t+1时刻,从状态i变成状态j的频数观测概率
bj(k) 的计算bj(k)=Bjk∑Mk=1Bjk 其中
Bjk 表示状态为j,观测为k的概率初始状态概率
πi 的计算估计为多个序列中,初始状态
qi 出现的频率
在python语言的实现中,各个矩阵的表示方法如下。
# 先验概率矩阵pi = {}# 状态转移概率矩阵A = {}# 观测概率矩阵B = {}
先验概率矩阵是一个一维字典。状态转移概率和观测概率矩阵都是一个二维字典。统计出各个矩阵的值的方法如下。
# 所有词语ww = []# 所有的词性pos = []# 每个词性出现的频率fre = {}# 先验概率矩阵pi = {}# 状态转移概率矩阵A = {}# 观测概率矩阵B = {}# dp概率dp = []# 路径记录pre = []zz = {}fin = codecs.open("处理语料.txt", "r", "utf-8")while (True): text = fin.readline() if (text == ""): break tmp = text.split(" ") n = len(tmp) for i in range(0, n - 1): word = tmp[i].split('/') if (word[1] not in pos): pos.append(word[1])fin = codecs.open("分词语料.txt", "r", "utf-8")text = fin.read()ww = text.split("\n")n=len(pos)#初始化概率矩阵for i in pos: pi[i]=0 fre[i]=0 A[i]={} B[i]={} for j in pos: A[i][j]=0 for j in ww: B[i][j]=0#计算概率矩阵line=0#总行数fin=codecs.open("处理语料.txt","r","utf-8")while(True): text=fin.readline() if(text=="\n"): continue if(text==""): break tmp=text.split(" ") n=len(tmp) line+=1 for i in range(0,n-1): word=tmp[i].split('/') pre=tmp[i-1].split('/') fre[word[1]]+=1 if(i==1): pi[word[1]]+=1 elif(i > 0): A[pre[1]][word[1]]+=1 B[word[1]][word[0]]+=1cx={}cy={}for i in pos: cx[i]=0 cy[i]=0 pi[i]=pi[i]*1.0/line for j in pos: if(A[i][j]==0): cx[i]+=1 A[i][j]=0.5 for j in ww: if(B[i][j]==0): cy[i]+=1 B[i][j]=0.5for i in pos: pi[i]=pi[i]*1.0/line for j in pos: A[i][j]=A[i][j]*1.0/(fre[i]+cx[i]) for j in ww: B[i][j]=B[i][j]*1.0/(fre[i]+cy[i])print "训练结束"
为了解决概率矩阵稀疏的问题,最后一部分代码采用了Add-delta平滑方法,对概率矩阵进行了平滑处理。
观测问题
通常采用维比特算法来解决观测问题。本质上是一个非常简单的动态规划问题。利用
具体代码如下:
num=len(text) for i in range(0,num): text[i]=unicode(text[i]) dp=[{} for i in range(0,num)] pre=[{} for i in range(0,num)] #初始化概率 for k in pos: for j in range(0,num): dp[j][k]=0 pre[j][k]="" n=len(pos) for c in pos: if(B[c].has_key(text[0])): dp[0][c]=pi[c]*B[c][text[0]]*1000 else: dp[0][c]=pi[c]*0.5*1000/(cy[c]+fre[c]) for i in range(1,num): for j in pos: for k in pos: tt=0 if(B[j].has_key(text[i])): tt=B[j][text[i]]*1000 else: tt=0.5*1000/(cy[j]+fre[j]) if(dp[i][j]<dp[i-1][k]*A[k][j]*tt): dp[i][j]=dp[i-1][k]*A[k][j]*tt pre[i][j]=k
一些问题和思考
- 尽管python默认支持高精度小数,但是由于转移概率矩阵很多数值太小,当遇到很长的语句时,还是会出现精度不够的情况。更好的做法是把概率取对数相加,这里我选择了简单粗暴得乘以一千,也算是简单解决了问题。
- 对于未见词选择了类似平滑的处理方式。增强了对未见词的处理能力。
性能评价
由于输入输出序列一定等长,所以准确率和回归率相等。测出数据如下所示
运行环境
python 2.7
python代码
# -*- coding: UTF-8 -*-# 打开一个文件# coding:utf-8import sysreload(sys)sys.setdefaultencoding('utf8')import codecsimport reimport mathfrom operator import itemgetter, attrgetter# 所有词语ww = []# 所有的词性pos = []# 每个词性出现的频率fre = {}# 先验概率矩阵pi = {}# 状态转移概率矩阵A = {}# 观测概率矩阵B = {}# dp概率dp = []# 路径记录pre = []zz = {}fin = codecs.open("处理语料.txt", "r", "utf-8")while (True): text = fin.readline() if (text == ""): break tmp = text.split(" ") n = len(tmp) for i in range(0, n - 1): word = tmp[i].split('/') if (word[1] not in pos): pos.append(word[1])fin = codecs.open("分词语料.txt", "r", "utf-8")text = fin.read()ww = text.split("\n")n=len(pos)#初始化概率矩阵for i in pos: pi[i]=0 fre[i]=0 A[i]={} B[i]={} for j in pos: A[i][j]=0 for j in ww: B[i][j]=0#计算概率矩阵line=0#总行数fin=codecs.open("处理语料.txt","r","utf-8")while(True): text=fin.readline() if(text=="\n"): continue if(text==""): break tmp=text.split(" ") n=len(tmp) line+=1 for i in range(0,n-1): word=tmp[i].split('/') pre=tmp[i-1].split('/') fre[word[1]]+=1 if(i==1): pi[word[1]]+=1 elif(i > 0): A[pre[1]][word[1]]+=1 B[word[1]][word[0]]+=1cx={}cy={}for i in pos: cx[i]=0 cy[i]=0 pi[i]=pi[i]*1.0/line for j in pos: if(A[i][j]==0): cx[i]+=1 A[i][j]=0.5 for j in ww: if(B[i][j]==0): cy[i]+=1 B[i][j]=0.5for i in pos: pi[i]=pi[i]*1.0/line for j in pos: A[i][j]=A[i][j]*1.0/(fre[i]+cx[i]) for j in ww: B[i][j]=B[i][j]*1.0/(fre[i]+cy[i])print "训练结束"while(True): tmp=raw_input("请输入需要词性标注的句子,以空格分割: ") if(tmp=="-1"): break text=tmp.split(" ") num=len(text) for i in range(0,num): text[i]=unicode(text[i]) dp=[{} for i in range(0,num)] pre=[{} for i in range(0,num)] #初始化概率 for k in pos: for j in range(0,num): dp[j][k]=0 pre[j][k]="" n=len(pos) for c in pos: if(B[c].has_key(text[0])): dp[0][c]=pi[c]*B[c][text[0]]*1000 else: dp[0][c]=pi[c]*0.5*1000/(cy[c]+fre[c]) for i in range(1,num): for j in pos: for k in pos: tt=0 if(B[j].has_key(text[i])): tt=B[j][text[i]]*1000 else: tt=0.5*1000/(cy[j]+fre[j]) if(dp[i][j]<dp[i-1][k]*A[k][j]*tt): dp[i][j]=dp[i-1][k]*A[k][j]*tt pre[i][j]=k res={} MAX="" for j in pos: if(MAX=="" or dp[num-1][j]>dp[num-1][MAX]): MAX=j if(dp[num-1][MAX]==0): print "您的句子超出我们的能力范围了" continue i=num-1 while(i>=0): res[i]=MAX MAX=pre[i][MAX] i-=1 for i in range(0,num): print text[i].decode('utf-8')+"\\"+res[i].decode('utf-8'), print ""
- python实现的基于hmm模型的词性标注系统
- 转载 基于HMM模型的词性标注
- 基于一阶HMM的中文词性标注(Java实现)
- 词性标注的python实现-基于平均感知机算法
- 词性标注的python实现-基于平均感知机算法
- 基于隐马尔可夫模型的有监督词性标注
- 基于隐马尔可夫模型的有监督词性标注
- 基于隐马尔可夫模型的有监督词性标注
- 基于隐马尔可夫模型的有监督词性标注
- HMM模型用在词性标注、分词
- HMM算法-viterbi算法的实现及与分词、词性标注、命名实体识别的引用
- 一阶HMM词性标注
- python的jieba分词词性标注
- 基于HMM的中文分词模型实现
- 中文词性标注的简单实现
- 隐马尔可夫模型 HMM 的python实现
- 中科院的分词系统使用的词性标注标准
- 中科院的分词系统使用的词性标注标准
- tensorflow下GoogLeNet的实现
- Android关于极光推送收到空白通知的问题
- JS——动态正则
- fork函数man手册原文
- spring data jpa
- python实现的基于hmm模型的词性标注系统
- IReport 使用记录 主报表和子报表数据传递
- 基本算法-冒泡排序
- #781 – 多个变换执行的顺序问题(Transform Order Matters)
- (笔记)类的组合,是如何析构的,及领悟
- 记录unterminated string literal报错解决
- POJ3020 Antenna Placement(二分图最小路径覆盖)
- selenium + python环境搭建
- static 相关的知识点