一. 命名实体识别
命名实体识别(Named Entity Recognition,NER)是NLP领域中一项基础的信息抽取任务,也是热点的研究方向之一,NER往往是关系抽取、知识图谱、问答系统等其他诸多NLP任务的基础。
命名实体识别任务是指从给定的一个非结构化的文本中识别出其中的命名实体,并对实体进行分类,比如时间、人名、地名、机构名等类型的实体。
NER示意图
命名实体识别任务常常转化为序列标注问题,利用BIO、BIOES和BMES等常用的标注规则对经过分词的文本进行token标注。(浅析命名实体识别(NER)的三种序列标注方法)
以BIO标注模式为例,下图为对文本进行token-level的命名实体标注实例。
BIO标注示意图
通过构建模型对文本的每个token标签进行预测,进而进行实体识别。
二. 基于序列标注的命名实体识别
1. 方法概述
序列标注的命名实体识别众多方法中将CNN、RNN和BERT等深度模型与条件随机场CRF结合已经成为最主流和普遍的方法,在本篇文章中我们仅关注基于CRF的序列标注模型。
基于序列标注的命名实体识别的发展大致经历了以下三个历程:
- [机器学习]:早期传统机器学习时代,除了利用人工规则的方法外,往往利用隐马尔科夫链HMM和条件随机场CRF进行实体标注;
- [深度学习]:随着深度学习的发展,将CNN和RNN做为基本的文本特征编码器,更好的学习token或word的隐层表示,再利用CRF进行实体标签分类,Bi-LSTM-CRF是最常用和普遍的实体识别模型;
- [预训练模型]:最近BERT为代表的预训练模型表现出了强大的文本表示和理解能力,目前最流行的方法是将BERT或BERT-Bi-LSTM作为底层的文本特征编码器,再利用CRF进行实体标签预测。现在,对于许多命名实体识别任务可以将BERT-Softmax、BERT-CRF、BERT-Bi-LSTM-CRF这几个模型作为baseline,而且能达到很好的效果,这几乎得益于BERT模型的强大文本表征建模能力。
2. CRF与NER
基于序列标注的命名实体识别方法往往利用CNN、RNN和BERT等模型对文本token序列进行编码表征,再利用一个全连接层对序列每个token进行分类,最后利用Softmax或CRF进行最终标签判断确定。
假设数据集的实体类别为 � 个,以BIO作为标注模式,命名实体识别的过程如下:
- 给定一个文本 ����="�1�2...��" 的token序列为: �=[′�1′,′�2′,...,′��′] , � 为文本序列长度;
- � 经过嵌入层(Embedding Layer)获得序列的嵌入表示 �∈��×� , � 表示向量维度;
- � 经过文本编码层(Encoder Layer)对token序列建模获得序列的隐层表示 �∈��×ℎ,ℎ 表示隐层向量维度;
- � 经过一个全连接的分类层(Classification Layer)对每个token进行实体标签的预测,得到分类结果 ������∈��×� ,其中每一行 ������∈������∈�� 表示文本中 �� 为各个实体标签的预测分数;
① Softmax实体标签判断
- 将 ������ 经过Softmax计算,将每个��对应概率最大的实体类别作为该��的实体标签,即对 � 个token部分进行 � 分类
假设数据的实体类别有2类:人名(P)和国家(C), label_set = {B-C, I-C, B-P, I-P, O}。以“朝鲜领导人和普京举行会晤”句子为例,下图为命名实体识别的整个过程。
Encoder+Softmax的命名实体识别示意图
② CRF实体标签判断
由下图可知softmax预测实体标签时是独立的,它只由其对应token的输出所决定,同一序列中判断预测的多个标签也是独立的,没有关联和影响。而CRF是以标签路径为预测目标,可以在������基础上为最终的预测标签序列添加一些约束,以确保预测的实体标签序列是有效的,这些约束可以由CRF层在训练过程中从训练数据集自动学习。
实际上最终的输出可能会产生多种标签序列组合,如下图所示列举了三个标签路径组合,红色路径标签序列为 [B-C,I-C, ...,,B-P,I-P,...,O],蓝色标签序列为 [O,B-P,...,I-P,O,..., O],绿色标签序列为 [I-C,O,...,O,I-P,...,B-C],其中红色路径为真实正确的,其他两条为可能预测产生的路径。
Softmax实体序列标注缺点示意图
其实很多标签路径预测出来后很明显是错误的,比如绿色路径中,I-C不可能作为序列的起始标签,标签O后面不可能是I-P标签,所以标签之间的转移关系和标签本身的属性对实体标签预测是有很大作用的。
CRF正是通过数据学习标签转移关系和一些约束条件,帮助模型选择正确合理的实体标签序列,减少无效的实体标签序列的预测判断,这些模式约束可以是:
- 文本第一个单词的实体标签应该以 'B-' 、'O' 开头,而不是 'I-' ;
- ' B-label1 I-label2 I-label3 I-…'模式中,label1、label2、label3等应该是相同的实体标签;
- 一个命名实体的第一个标签应该以 'B-' 而不能是 'I-' 开头
- ......
CRF在训练过程中通过数据学习一个标签转移关系关系矩阵 ������������∈��×� ,这个矩阵是CRF的参数,通过数据集训练学习,得到标签之间的关系和标签约束。
Start | B-C | I-C | B-P | I-P | O | End | |
Start | 0 | 0.7 | 0.004 | 0.8 | -0.21 | 0.9 | 0.02 |
B-C | 0 | 0.5 | 0.9 | 0.6 | 0.001 | 0.6 | 0.05 |
I-C | 0 | 0.4 | 0.65 | 0.7 | -0.84 | 0.7 | 0.21 |
B-P | -1 | 0.5 | 0.0001 | 0.5 | 0.9 | 0.5 | 0.31 |
I-P | 0 | 0.3 | 0 | 0.3 | 0.7 | 0.6 | 0.12 |
O | 0 | 0.6 | -0.12 | 0.8 | 0.7 | 0.9 | 0.4 |
End | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Encoder+CRF的命名实体识别示意图
推荐一篇文章: BiLSTM上的CRF,用命名实体识别任务来解释CRF,该文章系列讲述了CRF如何进行命名实体识别、识别预测实体标签的过程、CRF损失的计算、CRF的参数学习优化等详细内容。
class TransformerNER(BasicModule):
def __init__(self, enocder=None, rnn=None, crf=False, hidden_dim=768, dropout=0.5, tag_num=None) -> None:
"""
A entity recognized tagging model use transformer(bert) as encoder.
Args:
encoder: A pretrained transformer, e.g., BERT.
rnn: rnn type, RNN、GRU、LSTM
crf: True or False, whether to use CRF
tag_num: Size of tagset.
"""
super().__init__()
self.tag_num = tag_num
self.encoder = enocder
self.rnn = RNNModel(input_size=hidden_dim, hidden_size=hidden_dim, rnn_type=rnn) if rnn else None
self.crf = CRF(num_tags=tag_num, batch_first=True) if crf else None
self.classifier = nn.Linear(hidden_dim, tag_num)
self.dropout = nn.Dropout(p=dropout)
self.loss_fct = CrossEntropyLoss()
def forward(self, input_ids, token_type_ids=None, attention_mask=None, labels=None, label_mask=None, **kwargs):
encoder_outputs = self.encoder(input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask)
# [ batch_size * seq_length * hidden_dim ]
sequence_output = encoder_outputs[0]
if self.rnn:
sequence_output, sequence_hn = self.rnn(sequence_output, attention_mask)
sequence_output = self.dropout(sequence_output)
# [ batch_size * seq_length * tag_num ]
logits = self.classifier(sequence_output)
outputs = ()
if labels is not None:
if self.crf is not None:
loss = self.crf(emissions=logits, tags=labels, mask=label_mask.eq(1)) * (-1)
else:
if label_mask is not None:
active_loss = label_mask.view(-1) == 1
active_logits = logits.view(-1, self.tag_num)[active_loss]
active_labels = labels.view(-1)[active_loss]
loss = self.loss_fct(active_logits, active_labels)
else:
loss = self.loss_fct(logits.view(-1, self.tag_num), labels.view(-1))
outputs = (loss, )
if self.crf is not None:
# [ batch_size * seq_length ]
logits = self.crf.decode(emissions=logits, mask=label_mask.eq(1))
else:
# [ batch_size * seq_length ]
logits = torch.argmax(F.log_softmax(logits, dim=2), dim=2)
outputs = outputs + (logits, )
return outputs
三. 实验及分析
1. 实验设置
本文对BERT-Softmax、BERT-Bi-LSTM-Softmax、BERT-CRF、BERT-Bi-LSTM-CRF这几个模型做了一系列相关实验和分析,基础的BERT使用的是哈工大开源的中文预训练模型BERT-wwm-ext。实验相关代码和数据集可参考Github:
https://github.com/guolipa/nlp-algorithmgithub.com/guolipa/nlp-algorithm
实验使用了OntoNotes 5.0和CLUENER两个中文命名实体识别数据集,具体信息如下表所示,其中,CLUENER公开的数据集的测试数据没有标签,因此实验中将验证集同时也作为测试集。
数据集 | 训练样本数 | 验证样本数 | 测试样本数 | 实体类别数 |
---|---|---|---|---|
OntoNotes | 37557 | 6217 | 4293 | 18 |
CLUENER | 10748 | 1343 | 1343 | 10 |
2. 实验结果
实验结果如下:
- OntoNotes数据集的实验结果
方法 | Precision | Recall | F1 |
---|---|---|---|
BERT | 73.48 | 76.85 | 75.13 |
BERT-Bi-LSTM | 74.24 | 76.79 | 75.49 |
BERT-CRF | 73.76 | 77.41 | 75.54 |
BERT-CRF-0.001 | 76.76 | 77.74 | 77.25 |
BERT-Bi-LSTM-CRF | 75.81 | 76.82 | 76.31 |
BERT-Bi-LSTM-CRF-0.001 | 77.13 | 76.97 | 77.05 |
- CLUENER数据集的实验结果
方法 | Precision | Recall | F1 |
---|---|---|---|
BERT | 72.93 | 78.18 | 75.47 |
BERT-Bi-LSTM | 73.36 | 76.78 | 75.03 |
BERT-CRF | 73.39 | 76.53 | 74.93 |
BERT-CRF-0.001 | 76.47 | 77.01 | 76.73 |
BERT-Bi-LSTM-CRF | 72.14 | 78.18 | 75.04 |
BERT-Bi-LSTM-CRF-0.001 | 75.69 | 77.44 | 76.56 |
3. 结果分析
从六个方法在OntoNotes 5.0和CLUENER两个数据集的实验结果(主要用F1作为对比分析的标准),我们可以得出以下的结论和启示:
Q1:BERT基础上是否有比较添加一层Bi-LSTM?
-
- [BERT和BERT-Bi-LSTM对比]:BERT-Bi-LSTM相对BERT在OntoNotes数据集上有非常小量的提升(75.13->75.49),性能相差不大;而在CLUENER数据集上,性能反而下降(75.47->75.03)
- [BERT-CRF和BERT-Bi-LSTM-CRF对比]:BERT-Bi-LSTM-CRF相比BERT-CRF在两个数据集上有提升(75.54->76.31, 74.93->75.04)
- [BERT-CRF-0.001和BERT-Bi-LSTM-CRF-0.001对比]:BERT-Bi-LSTM-CRF-0.001相比BERT-CRF-0.001在两个数据集上性能均有一定的下降(77.25->77.05, 76.73->76.56)
综合上述三组模型的对比:BERT基础上是没有必要再添加一层Bi-LSTM,BERT基础上添加Bi-LSTM并不会带来模型性能的显著提升甚至会影响BERT原本的性能。这是因为BERT双向的深层结构和强大的文本拟合能力使得其本身就能够学习文本中token序列关系,甚至比Bi-LSTM学的更好,因此在BERT基础上增加Bi-LSTM不一定能带来性能提升,反而增加模型的复杂度导致过拟合甚至错误的拟合。
Q2:CRF是否需要设置不同的学习率?
在文章你的CRF层的学习率可能不够大中作者通过实验说明CRF需要设置比BERT更大的学习率,正常BERT微调的学习率为1e-5,通过将CRF的学习率设置为1e-3,调大100倍进行对比来验证这一观点。
- [BERT-CRF和BERT-CRF-0.001对比]:扩大学习率的BERT-CRF-0.001相对BERT-CRF性能在两个实验数据集上都有明显提升(75.54->77.25, 74.93->76.73)
- [BERT-Bi-LSTM-CRF和BERT-Bi-LSTM-CRF-0.001对比]:BERT-Bi-LSTM-CRF-0.001相对BERT-Bi-LSTM-CRF性能在两个实验数据集上都也有明显提升(76.31->77.05, 75.04->76.56)
综合两组模型对,将CRF学习率扩大100倍能很明显的提升模型性能,并且BERT-CRF-0.001模型性能是最好的,甚至略微好于BERT-Bi-LSTM-CRF-0.001,这充分验证了CRF所需要的的学习率要比BERT大,设置更大的学习率能够为模型带来性能提升。
参考文献
[2] BiLSTM上的CRF,用命名实体识别任务来解释CRF
[3] 你的CRF层的学习率可能不够大