NLP-Beginner 任务四:基于LSTM+CRF的序列标注+pytorch
传送门
主要参考论文:Neural Architectures for Named Entity Recognition
辅助参考论文:End-to-end Sequence Labeling via Bi-directional LSTM-CNNs-CRF
一. 介绍
1.1 任务简介
本次的NLP(Natural Language Processing)任务是对一个英文句子进行序列标注,例子如下
1.2 数据集
例子:
原数据:
EU NNP B-NP B-ORG
rejects VBZ B-VP O
German JJ B-NP B-MISC
call NN I-NP O
to TO B-VP O
boycott VB I-VP O
British JJ B-NP B-MISC
lamb NN I-NP O
. . O O
输入文本: EU rejects German call to boycott British lamb.
输出序列: B-ORG O B-MISC O O O B-MISC O O
原数据:
Peter NNP B-NP B-PER
Blackburn NNP I-NP I-PER
输入文本: Peter Blackburn
输出序列: B-PER I-PER
1.3 原数据解释
The first item on each line is a word, the second a part-of-speech (POS) tag, the third a syntactic chunk tag and the fourth the named entity tag.
每行第一项是单词,第二项是POS标签,第三项是语法块标签,第四项是命名实体标签,也就是我们本次的任务。
具体命名实体的每一个标签代表的意思,可以参考原数据集介绍。
数据集共有三个文件:train.txt, test.txt 和 dev.txt。由于文件数据的组织方式比较松散,因此需要预处理,并且需要去掉文件中所有的:-DOCSTART- -X- -X- O(代表某一个文档开始)
二. 特征提取——Word embedding(词嵌入)
请参考NLP任务二
本次实战除了给定的各种序列类别之外,还要另外多加3个类别,分别是: < pad >,< start >,< end >,分别代表padding(即补位,使句子达到同一个长度),句子开头和句子结尾,总共C类标签。
三. 神经网络(LSTM+CRF)
本部分详细内容可以参考论文:Neural Architectures for Named Entity Recognition
3.1 LSTM层
3.2 CRF层(条件随机场Conditional Random Field)
3.2.1 转移矩阵 T r a n Tran Tran 初始化
注意:
- 句子开头< start >不可能到达< start >
- < end >代表句子结尾,不能再转移
- -< pad > 表示padding(即补位,使句子达到同一个长度)也只能转移到< pad >。
3.2.2 CRF(最困难的部分)
首先我们先明确一下目标,在LSTM层过后我们得到了得分矩阵 S S S,我们还要从输入 X X X得到一个loss(损失),从而优化整个网络的参数,而从 X X X得到loss的中间部分则是CRF层。
对于每一个句子 X X X,需要计算的数值如下:
- 真实得分:对真正序列 y y y的得分
- 全部得分:对于所有序列 y ~ \tilde{y} y~的得分
- softmax得分: e T r u e _ s c o r e ( X , y ) ∑ y ~ e s c o r e ( X , y ~ ) = e T r u e _ s c o r e ( X , y ) T o t a l _ s c o r e \frac{e^{True\_score(X,y)}}{\sum_{\tilde{y}} e^{score(X,\tilde{y})}}=\frac{e^{True\_score(X,y)}}{Total\_score} ∑y~escore(X,y~)eTrue_score(X,y)=Total_scoreeTrue_score(X,y),这个得分越高,就证明真正序列y的得分占比越大,而其它所有序列的指数加和占比越小,也就是说我们的模型更加正确。
- Loss:loss一般是越小越好,而softmax得分是越大越好,且比值方式可以利用 l o g log log性质变成减法,因此取 l o s s = − log ( s o f t m a x ) = T r u e _ s c o r e ( X , y ) − log ( T o t a l _ s c o r e ) loss=-\log (softmax)=True\_score(X,y)-\log{(Total\_score)} loss=−log(softmax)=True_score(X,y)−log(Total_score)得分
3.2.2.1 真实得分——对应CRF.true_prob
有
S
S
S 和
T
r
a
n
Tran
Tran 可以直接计算,非常简单,求和即可。
3.2.2.2 全部得分——对应CRF.total_prob(难点)
我们可以看到,我们需要计算所有序列 y ~ \tilde{y} y~的得分,如果一个句子的长度为 T T T,一共 C C C类标签,那么我们就有 C T C^T CT种情况需要遍历,这个复杂度是非常高的,代价非常大,因此参考了网上的方法,采用动态规划(DP)的思想进行计算(非常难以理解)。
具体请参考代码。
3.2.2.3 Loss
直接计算 l o s s = T r u e _ s c o r e ( X , y ) − log ( T o t a l _ s c o r e ) loss=True\_score(X,y)-\log{(Total\_score)} loss=True_score(X,y)−log(Total_score)
3.2.2.4 预测序列——对应CRF.predict(难点)
除了训练网络参数,我们还需要进行预测,以在测试集上进行测试。
预测无非就是在所有序列得分中找到最大得分的序列,因此一开始的想法是遍历 C T C^T CT种情况,但显然复杂度非常高,因此采用非常常见且高效的动态规划算法——viterbi算法(建议先了解算法本身)。
具体请参考代码。
四. 代码及实现
4.1 实验设置
- 样本个数:train.txt与test.txt
- 训练集:测试集 : train.txt与test.txt
- 特征提取:Random / GloVe Embedding
- 学习率:10-3
- l h , l f l_h,\ l_f lh, lf:50
- Batch 大小:128
- 训练轮数:50
4.2 结果展示
指标:单个句子的标注正确率的平均
比如:一个句子无论多少个词,只有标全对了,才记为1分,否则0分。
可以看到GloVe初始化比随机初始化明显地好,标注正确率随机最多只有55%,而GloVe最高能到76.23%,性能比较好。
五. 总结
自己编写CRF难度是非常大的,因此建议多去网上寻找相关的讲解,还有一种“逃课”的方法是,直接调包,好像pytorch是有现成的CRF包的,直接调用可以解决许多问题。
标注正确率不到80%其实不太高,模型有待改进,比如增大 l h 和 l f l_h和l_f lh和lf,更精确的指标可以参考原本论文的指标 F 1 F_1 F1。
以上就是本次NLP-Beginner的任务四,全文难点在于全部得分和预测序列,需要多加琢磨。
谢谢各位的阅读,欢迎各位对本文章指正或者进行讨论,希望可以帮助到大家!