文章目录
问题描述
给定词库traindata.txt(提取码为r9px),预测字符串x = 'The ad is increasing .'的词性。
问题分析
根据博客https://www.cnblogs.com/pinking/p/8531405.html的内容可知,这是一个典型的hmm预测问题。因此我们需要先基于词库,计算出hmm模型的三个参数(A, B, π),然后使用viterbi算法进行词性预测。
求解步骤
1.读取词库traindata.txt,统计出四个字典:tag2id,id2tag,word2id,id2word
代码如下:
import numpy as np
tag2id,id2tag={},{}
word2id,id2word={},{}
with open('traindata.txt','r') as f:
for line in f.readlines():
items=line.split('/')
word,tag=items[0],items[1].rstrip()
if word not in word2id.keys():
word2id[word]=len(word2id)
id2word[len(id2word)]=word
if tag not in tag2id.keys():
tag2id[tag]=len(tag2id)
id2tag[len(id2tag)]=tag
得到的四个字典的形式如下图所示:
2.计算hmm模型的三个参数(A, B, π)
word_number=len(word2id)
tag_number=len(tag2id)
A = np.zeros((tag_number, word_number)) #发射矩阵
B=np.zeros((tag_number,tag_number))#状态转移矩阵
L=np.zeros(tag_number) #初始状态矩阵
pre_tag=''
with open('traindata.txt','r') as f:
for line in f.readlines():
items=line.split('/')
wordId,tagId=word2id[items[0]],tag2id[items[1].rstrip()]
if pre_tag=='': #表示是在句首位置,此时统计发射矩阵和初始状态矩阵
L[tagId]+=1
A[tagId][wordId]+=1
else: #表示在句中位置,此时统计发射矩阵和状态转移矩阵
A[tagId][wordId] += 1
B[tag2id[pre_tag]][tagId]+=1
if items[0]=='.': #说明这个句子结束,到了下一个句子
pre_tag=''
else:
pre_tag=items[1].rstrip()
#将矩阵中的值转换成概率
L/=sum(L)
for i in range(tag_number):
A[i] /=sum(A[i])
B[i] /=sum(B[i])
3. viterbi算法预测句中词的词性
3.1 分析
viterbi算法是一种动态规划的算法,其核心思想是:先求出起始位置到下一状态的最优路径,然后基于这个最优路径的值,继续求起始位置到下下一状态的最优路径,依此类推。当然每个最优化问题中“优”的评判标准不同。在词性标注问题中,其评判条件如下图箭头位置所示:
(图片引用于早期保存的某博客的学习笔记,链接忘记存了)
从上图可以看到,为了简化运算、并且放大概率值,公式对矩阵增加了log运算,这也是编码中需要注意的点。
3.2 代码示例
def log(v):
if v==0: #为了避免np.log函数报错
return np.log(0.00001)
else:
return np.log(v)
def viterbi(x,L,A,B):
seq2id=[word2id[i] for i in x.split()]
lay_number=len(seq2id)
P=np.zeros((lay_number,tag_number))
D=np.zeros((lay_number,tag_number))
for i in range(tag_number):
P[0][i]=log(L[i])+log(A[i][seq2id[0]])
D[0][i]=-1
for layer in range(1,lay_number,1):
for i in range(tag_number): #当前节点
probilities = []
for j in range(tag_number):#前一个节点
probilities.append(P[layer-1][j]+log(B[j][i])+log(A[i][seq2id[layer]]))
P[layer][i] = np.max(probilities)
D[layer][i] = np.argmax(probilities)
best_seq=[0]*lay_number
best_seq[lay_number-1]=np.argmax(P[lay_number-1])
#反推出最优路径
for layer in range(lay_number-2,-1,-1):
best_seq[layer]=int(D[layer+1][int(best_seq[layer+1])])
#求出最终的词性标注结果
best_seq=[id2tag[best_seq[i]] for i in range(len(best_seq))]
return best_seq
测试:
if __name__ == '__main__':
x = 'The ad is increasing .'
print("标注结果为:",viterbi(x,L,A,B))
运行结果如下:
4 推荐文章
[1] viterbi算法词性标注
[2] 维特比算法实现词性标注
[3] 课程讲解