4

序列标注之NER、CWS经典模型HMM实现

 2 years ago
source link: https://allenwind.github.io/blog/8750/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

序列标注之NER、CWS经典模型HMM实现

序列标注之NER经典模型HMM实现

在文章NLP任务:序列标注提及很多解决NER和中文分词任务的方案,唯独经典的HMM没有详细展开。

三类常见的词法分析:

  • 实体命名识别

简单来说,中文分词负责语义的基本间隔,词性标注进行浅层的标注,实体命名识别识别更粗粒度的专有名词。

分词中BMES序列标注示意图,

HMM的参数学习包括状态转移矩阵和观察矩阵,由于数据带标注,都可以使用最大似然估计获得,这些内容在过去的文章:

已经有所提及,不过当时并没有拿来做序列标注上的实践。今天这里补充HMM的实现和实践。

状态转移矩阵参数估计,aij=P(it+1=qj|it=qi)aij=P(it+1=qj|it=qi)

^aij=AijN∑j=1Aija^ij=Aij∑j=1NAij

其中i,ji,j取值均在[1,N][1,N]范围内。

观察矩阵的参数估计,

^bj(k)=BjkM∑k=1Bjkb^j(k)=Bjk∑k=1MBjk

其中j∈[1,N],k∈[1,M]j∈[1,N],k∈[1,M],通常在实现的时候使用稀疏形式,如字典来存储参数。HMM需要考虑OOV问题,即测试集有的字并没有出现在训练集里, 这种情况下,通过观测概率矩阵是无法获得OOV对应的各种状态的概率分布。HMM的OOV问题的解决方案是,把OOV对应的状态的概率分布设为均匀分布。

初始状态π=(π1,…,πN)π=(π1,…,πN),其中

πi=P(i1=qi)πi=P(i1=qi)

即所有待学习样本的初始状态的频率。不过初始状态在这里用不上,在生成任务上才需要。

实现和效果(更新)

源码地址:hmm-ner-cws,包括HMM模型用于中文分词和命名实体识别例子。通过监督学习计算到参数后,输入观察序列到模型中,模型可以计算每个时间步的观察值所对应的各个状态的scores,整个观察序列如此计算即可获得path scores,在观察到第jj个时间步ojoj情况下,各个状态ii的概率sijsij为,

sij=p(i|oj)sij=p(i|oj)

然后使用状态转移矩阵和viterbi解码即可获得最大分值路径。

命名实体识别例子,

$ python3 task_ner.py

[('桂林', 'LOC'), ('李中文', 'PER'), ('九届『银荔杯』全国象棋冠军', 'ORG')]
[('桂林', 'LOC'), ('李中文', 'PER'), ('银荔', 'ORG')]

[('中国', 'LOC'), ('江泽民', 'PER'), ('中央', 'ORG')]
[('中国', 'LOC'), ('江泽民', 'PER'), ('党中央', 'ORG')]

NER任务有规则性较强的实体甚至连HMM都不需要,如ISBN、商品编号网址、E-mail、电话号码可以直接使用正则表达式来提取。但是对于命名更灵活复杂的实体,则需要更复杂的模型,如实体类别为ORG,中央党中央都是实体。

BIOES 释义如下:

BIOES 解释

B Begin,表示开始

O Other,表示其他,用于标记无关字符

E End,表示结尾

S Single,表示单个字符

I Intermediate,表示中间

可视化状态转移矩阵可以检验模型是否学习充分,下面可视化一下NER中HMM的状态转移矩阵:

可以看到学习到的状态转移矩阵整体上算合理的。注意到不同类别的实体也存在连续的情况,如I-LOCB-ORG的转移概率有1%。

中文分词例子,

$ python task_cws.py

['守得', '云开', '见月', '明']
['守得', '云开', '见月', '明']
['乒乓球', '拍卖', '完', '了']
['乒乓球', '拍卖', '完', '了']
['无线', '电法', '国别', '研究']
['无线', '电法', '国别', '研究']
['广东', '省长', '假', '成绩', '单']
['广东', '省长', '假', '成绩', '单']
['欢迎', '新', '老师', '生前', '来', '就', '餐']
['欢迎', '新', '老师', '生前', '来', '就', '餐']
['上海', '浦东', '开发', '与', '建设', '同步']
['上海', '浦东', '开发', '与', '建设', '同步']
['独立', '自主', '和平', '等', '互利', '的', '原则']
['独立', '自主', '和平', '等', '互利', '的', '原则']
['黑天', '鹅', '和', '灰犀牛', '是', '两', '个突', '发性', '事件']
['黑天', '鹅', '和', '灰犀牛', '是', '两', '个突', '发性', '事件']

以上分词效果还算可以,能够把新词灰犀牛识别出来,但是并没有把新词黑天鹅识别出来。

BEMS 解释

B Begin,表示开始

M Intermediate,表示中间

E End,表示结尾

S Single,表示单个字符

状态矩阵的可视化,

可以看到学习到的状态转移矩阵是合理的。

项目实现源码地址:hmm-ner-cws

可能根据需要持续更新~

对比jieba分词

这里的HMM使用ctb6的标注数据训练参数,jieba的HMM自带状态矩阵和观察矩阵,以及一个非常大的词表。

import jieba
from task_cws import tokenizer
import dataset

for text in dataset.load_sentences():
print(jieba.lcut(text, HMM=True))
print(tokenizer.cut(text))

部分分词结果如下,

$ python3 compare_jieba.py

['守得云', '开见', '月', '明']
['守得', '云开', '见月', '明']
['乒乓球', '拍卖', '完', '了']
['乒乓球', '拍卖', '完', '了']
['无线电', '法国', '别', '研究']
['无线', '电法', '国别', '研究']
['广东省', '长假', '成绩单']
['广东', '省长', '假', '成绩', '单']
['欢迎', '新', '老师', '生前', '来', '就餐']
['欢迎', '新', '老师', '生前', '来', '就', '餐']
['上海浦东', '开发', '与', '建设', '同步']
['上海', '浦东', '开发', '与', '建设', '同步']
['独立自主', '和', '平等互利', '的', '原则']
['独立', '自主', '和平', '等', '互利', '的', '原则']
['黑天鹅', '和', '灰', '犀牛', '是', '两个', '突发性', '事件']
['黑天', '鹅', '和', '灰犀牛', '是', '两', '个突', '发性', '事件']
['黄马', '与', '黑马', '是', '马', ',', '黄马', '与', '黑马', '不是', '白马', ',', '因此', '白马', '不是', '马', '。']
['黄马', '与', '黑马', '是', '马', ',', '黄马', '与', '黑马', '不', '是', '白马', ',', '因此', '白马', '不', '是', '马', '。']

对于新词,黑天鹅这里的HMM能够识别出来,而jieba并没有,而灰犀牛则正好相反。整体上可以看到,这里HMM分词效果不错,且没有带额外的词表。

HMM的实现很简单,只需要用统计的方法计算状态转移矩阵和观察矩阵即可,后者往往是稀疏的,可以使用字典来存储参数。对于状态序列的预测,通过观察序列计算path scores,然后viterbi解码即可获得最大分值路径。

转载请包括本文地址:https://allenwind.github.io/blog/8750
更多文章请参考:https://allenwind.github.io/blog/archives/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK