赞
踩
命名实体识别(Named Entity Recognition,NER)是NLP领域中一项基础的信息抽取任务,也是热点的研究方向之一,NER往往是关系抽取、知识图谱、问答系统等其他诸多NLP任务的基础。
命名实体识别任务是指从给定的一个非结构化的文本中识别出其中的命名实体,并对实体进行分类,比如时间、人名、地名、机构名等类型的实体。
NER示意图
命名实体识别任务常常转化为序列标注问题,利用BIO、BIOES和BMES等常用的标注规则对经过分词的文本进行token标注。(浅析命名实体识别(NER)的三种序列标注方法)
以BIO标注模式为例,下图为对文本进行token-level的命名实体标注实例。
BIO标注示意图
通过构建模型对文本的每个token标签进行预测,进而进行实体识别。
序列标注的命名实体识别众多方法中将CNN、RNN和BERT等深度模型与条件随机场CRF结合已经成为最主流和普遍的方法,在本篇文章中我们仅关注基于CRF的序列标注模型。
基于序列标注的命名实体识别的发展大致经历了以下三个历程:
基于序列标注的命名实体识别方法往往利用CNN、RNN和BERT等模型对文本token序列进行编码表征,再利用一个全连接层对序列每个token进行分类,最后利用Softmax或CRF进行最终标签判断确定。
假设数据集的实体类别为 � 个,以BIO作为标注模式,命名实体识别的过程如下:
① Softmax实体标签判断
假设数据的实体类别有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正是通过数据学习标签转移关系和一些约束条件,帮助模型选择正确合理的实体标签序列,减少无效的实体标签序列的预测判断,这些模式约束可以是:
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
本文对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 |
实验结果如下:
方法 | 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 |
方法 | 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 |
从六个方法在OntoNotes 5.0和CLUENER两个数据集的实验结果(主要用F1作为对比分析的标准),我们可以得出以下的结论和启示:
Q1:BERT基础上是否有比较添加一层Bi-LSTM?
综合上述三组模型的对比: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倍进行对比来验证这一观点。
综合两组模型对,将CRF学习率扩大100倍能很明显的提升模型性能,并且BERT-CRF-0.001模型性能是最好的,甚至略微好于BERT-Bi-LSTM-CRF-0.001,这充分验证了CRF所需要的的学习率要比BERT大,设置更大的学习率能够为模型带来性能提升。
[2] BiLSTM上的CRF,用命名实体识别任务来解释CRF
[3] 你的CRF层的学习率可能不够大
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。