# 中文NER的那些事儿6. NER新范式!你问我答之MRC
就像Transformer带火了"XX is all you need"的论文起名大法,最近也看到了好多"Unified XX Framework for XX"的paper,毕竟谁不喜欢写好一套框架然后哪里需要哪里搬凸-凸。这一章让我们来看下如何把NER的序列标注任务转换成阅读理解任务。论文本身把重点放在新的框架可以更好解决嵌套实体问题,但是实际应用中我碰到Nested NER的情况很少,不过在此之外MRC对小样本场景,以及细粒,层次化实体的识别任务也有一些启发意义,代码详见ChineseNER/mrc
Paper: A Unified MRC Framework for Named Entity Recognition
下面我们把MRC的模型框架分开成两部分来看。第一部分是阅读理解任务主要处理模型的输入,第二部分是entity Span抽取任务针对模型的输出。因为他们其实是针对不同问题的改良,可以在不同的场景下分开使用
阅读理解:Tag -> Q&A
样本生成
在之前的NER任务中,对不同的实体类型的处理就是在label中使用不同的tag,地点就是LOC,人物就是PER,机构就是ORG。这种标注方式的问题在于
- label本身没有任何先验信息(这里的先验信息既包括label本身的含义,也包括label之间关联性)
- 以及output层的复杂度会随着实体类别的增加呈线性增长
- label和文本的交互只发生在输出层
MRC的改良方式是把label信息用Query进行编码,然后用Query和文本间的交互提取对应的实体信息。针对MSRA数据集中LOC,ORG,PER三类实体,作者分别用了如下的Query作为三类实体的先验信息
Tag2Query = {
'LOC': '按照地理位置划分的国家,城市,乡镇,大洲',
'PER': '人名和虚构的人物形象',
'ORG': '组织包括公司,政府党派,学校,政府,新闻机构'
}
Bert模型的输入和QA任务相同是\([CLS]query[SEP]text\), 以PER为例,每个样本都被构建为以下形式。
[CLS]人名和虚构的人物形象[SEP]这是中国领导人首次在哈佛大学发表演讲
如果NER任务有N个实体,训练样本有M个,按以上QA样本的构建方式会得到N*M个样本。
Query构建
因为这里Query是对label先验信息的刻画,所以如何构建query对最终的模型效果有很大的影响,作者在paper中对比了不同的Query生成方式,已ORG为例,作者尝试了
- position: tag的数值编码1/2/3
- keyword: 组织
- Rule-based template filling: 文中提到了哪些结构?
- wikipedia:组织的百科描述
- synonyms: 组织的同义词
- keyword+synonyms: 组织+组织的同义词
- Annotation guide: 组织包括公司,政府党派,学校,政府,新闻机构
对比结果如上,position因为没啥信息所以效果最差,keyword+synonyms显著优于只使用关键词/同义词,所以编码信息其实是越全面越好。wiki因为包含一些无效信息所以效果不如Annotation。在实际应用时,其实可以进一步在query中引入结构化信息,帮助更细粒度的实体标注,例如[综艺节目]音乐综艺,[综艺节目]搞笑综艺,对同一领域的实体加上领域信息,帮助模型学习实体间的关联关系
总结
QA模型结构的优点
- 加入了label的先验信息,会在few-shot/zero-shot场景中有更好的效果,对于新实体的冷起会是个不错的选择
- Query里可以加入label之间的关联信息,例如同一领域内的细分实体,对细粒度实体识别可能会有帮助
- QA的模型结构增加了实体类型和文本的交互
- 增加新的实体类型,只会增加对应的训练样本,不会增加模型复杂度
哈哈天上从来不会掉馅饼,有优点肯定有缺点滴~这个paper中没有提,不过在使用中感觉有几点需要填坑
- QA的样本构建方式加剧了Data Imbalance,原先有实体/无实体的比例如果是1:10,3类实体的QA样本可能就是1:30,会出现大量无实体的sample
- QA的构建方式导致一次推理只能提取1种实体类型,会增加线上推理耗时。当然你可以加机器加并发。在github上看到有另一种操作是把query拼在一起\([cls1]q1[cls2]q2[cls3]q3[seq]text\),不过这个需要同时修改训练方式,有试过效果的小伙伴欢迎comment下效果~
- Query的构建有时候会需要多次尝试,因为如果先验信息有错或者不完整,反而会影响到模型效果。
Span抽取
span抽取部分paper中写的很简单,真的是读完觉得简单明了!结果噼里啪啦一同写,跑起来发现模型span F1不收敛。。。于是默默去读了MRC的源码,感觉paper中省略了几个对模型效果会有影响的细节,下面来聊聊踩过的坑~
Start/End Index
为了同时抽取nested Entitty,作者使用了start index+end index的方式来标记实体位置。举个例子,样本如下
样本:{"title": "我 会 邀 请 许 惠 佑 一 同 来 访 ",
"label": [{"span": "许惠佑", "tag": "PER", "start_pos": 4, "end_pos": 6}]}
start index:[0,0,0,0,1,0,0,0,0,0,0]
end index:[0,0,0,0,0,0,1,0,0,0,0]
span index: [11,11] matrix,[(4,6)]=1
start index标记实体开始的位置,end index标记实体结束的位置。这里和序列标注的方式相同,会在Bert模型输出后面接一个classifier,预测每个位置是否start/end=1
p_{start} = softmax(W_{start} \cdot Bert_{output})\\
p_{end} = softmax(W_{end} \cdot Bert_{output})\\
\hat{I}_{start} = \{i|argmax(p_{start}^i)==1, i=1,2,...n\}\\
\hat{I}_{end} = \{i|argmax(p_{end}^i)==1, i=1,2,...n\}
\end{align}
\]
Span Index
因为是nested entity,所以开始位置和结束位置可能会存在交叉,并不能唯一确定实体位置,所以需要一个span index。span index第i行第j列如果是1,就意味着i->j之间存在一个实体。
下面是paper中的公式,看公式我以为是把start end的logits进行了拼接,然后做一层线性变换,从start/end logits里面判断哪些是正确的组合。
\]
结果看到下面的源码发现是直接对Bert的embedding输出copy了两份进行拼接后,又加了一层非线性变换。。。这公式写的有些抽象。。。所以span logits的计算空间复杂度比较高,因为span本身是seq_len * seq_len二维的,所以embedding空间是O(seq_len * seq_len * emb_size)。
span_embedding = MultiNonLinearClassifier(config.hidden_size * 2, 1, config.mrc_dropout, intermediate_hidden_size=config.classifier_intermediate_hidden_size)
sequence_heatmap = bert_outputs[0] # [batch, seq_len, hidden]
# [batch, seq_len, seq_len, hidden]
start_extend = sequence_heatmap.unsqueeze(2).expand(-1, -1, seq_len, -1)
end_extend = sequence_heatmap.unsqueeze(1).expand(-1, seq_len, -1, -1)
# [batch, seq_len, seq_len, hidden*2]
span_matrix = torch.cat([start_extend, end_extend], 3)
# [batch, seq_len, seq_len]
span_logits = span_embedding(span_matrix).squeeze(-1)
计算Span Loss的时候,作者先做了个一个下半矩阵的MASK,这一步只是把所有start_index>end_index的位置直接MASK掉。但只有这一个MASK并不够,因为span logits存在很严重的Data-Imbalance的问题,就像上面在Query构造时就提到过的,QA的样本会成倍的提高正负样本比,这里的Span Loss也是同样的问题。1个实体在start index中的正负比是1vsSeq_len,在span index中就是1vs(Seq_len * Seq_len)。
考虑到Span Index只是为了对Start/End Index的输出结果进一步筛选,所以作者在计算Span Loss的时候加了Candidate Mask。Mask有几种构建方式,最终作者选择的主要是‘pred_and_gold’ MASK,也就是只针对Span Label=1 or Start/End预测=1的位置计算Span Loss。。。(*≧ω≦)
Loss & Inference
最终的模型loss是start loss, end loss, span loss的加权求和。看作者的参数设定span的权重一般是0.1,start&end的权重是1。感觉这里的trick可能在于差异化梯度更新,因为span的loss计算依赖上面提到的使用start/end的预测结果作为MASK。所以需要start/end先行更快的收敛,再来帮助span进行收敛。直接差异化learning rate可能也阔以??
L_{start} = CE(P_{start}, Y_{start})\\
L_{end} = CE(P_{end}, Y_{end})\\
L_{span} = CE(P_{span}, Y_{span})\\
L = \alpha L_{start} + \beta L_{end} + \gamma L_{span} \\
\end{align}
\]
模型的预测结果,需要分别对start,end, span index进行预测,然后取三个Index的交集作为实体预测的结果。
评估
实际应用中到我还没碰到必须使用嵌套实体的场景,所以还是更倾向于适配Flat NER的解决方案,所以在使用MRC的时候,我只使用了前半部分的Query构建,后面的start/end/span的抽取方式直接替换成了BIO的序列标注方式。
在MSRA上进行实验,因为这里没加CRF层,所以直接和第一章时提到的Bert+CrossEntropy的模型结构进行对比。(这里用的是google Bert Base,测试过和WWM Bert Base没啥差别,作者paper中用的是WWM Bert Large,然我并跑不动。。。所以结果没法比)上面是MRC的结果下面是Bert-CE的结果。会发现PER的预测结果两个模型差不多,但是ORG/LOC的结果中,MRC反而要略差一些,指标主要差在召回率都是显著低于Bert-CE的
搂了一眼MRC的召回结果,发现个比较有意思的点就是不太符合Query定义的存在一些争议的case,一般都没被召回。不确定这是否是由于query引入了较强的先验信息带来的效果~
- ORG中会议类的多数都没有召回,会议类是否属于ORG其实有一些模糊,但至少是不包含在ORG的Query定义里的
{"text": "致公党第十一次全国代表大会是致公党历史上一次重要的会议。", "tag": "ORG",
"pred_entity_list": ["致公党", "致公党"],
"true_entity_list": ["致公党第十一次全国代表大会", "致公党"]}
{"text": "不久前,召开了举世瞩目的第十五次全国代表大会。", "tag": "ORG",
"pred_entity_list": [],
"true_entity_list": [ "第十五次全国代表大会"]}
{"text": "不久前成功召开的第十五次全国代表大会最重要的历史贡献是。。。", "tag": "ORG",
"pred_entity_list": [],
"true_entity_list": [第十五次全国代表大会"]}
- 同样LOC中存在争议的case,例如月亮,太阳啥的是不是LOC(; ̄ェ ̄),MRC同样没有召回
{'text': '东方月正圆绍武圆,可是个绝妙好词!', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月']}
{'text': '窗台前花圃的那丛玫瑰正痴情地吻着如水的月辉,摇曳着美丽的花影。', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月']}
{'text': '此刻,我不知道彭丹是否也在月下徘徊,是否也在做着自己飞翔的梦?', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月']}
{'text': '“人有悲欢离合,月有阴晴圆缺,此事古难全!', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月']}
{'text': '”没什么,月亮碎了,心并没有碎!', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月亮']}
{'text': '从圆明园破碎那天起,炼石补月的工程,就启动了。', 'tag': 'LOC',
'pred_entity_list': ['圆明园'],
'true_entity_list': ['圆明园', '月']}
{'text': '月碎了有什么?', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月']}
{'text': '月碎了,只能造就出一批又一批、一代又一代的补月能手罢了!', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月', '月']}
{'text': '二十一世纪的月亮,正从东方升起来,圆不圆?', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['月亮']}
{'text': '让人类撕碎撒谎者遮羞的破布,让历史记住战争的创伤,让孩子和鸽群一起起飞,在蓝天托起和平的太阳。', 'tag': 'LOC',
'pred_entity_list': [],
'true_entity_list': ['太阳']}
除了Query先验信息的影响之外,不确定这里Data Imbalance的影响有多少,下一章我们试下调整Loss Function看看会不会有进一步的提升~
Reference
# 中文NER的那些事儿6. NER新范式!你问我答之MRC的更多相关文章
- 中文NER的那些事儿3. SoftLexicon等词汇增强详解&代码实现
前两章我们分别介绍了NER的基线模型Bert-Bilstm-crf, 以及多任务和对抗学习在解决词边界和跨领域迁移的解决方案.这一章我们就词汇增强这个中文NER的核心问题之一来看看都有哪些解决方案.以 ...
- uSID:SRv6新范式
摘要:本文介绍最新的SRv6创新uSID(Micro Segment).uSID兼容既有的SRv6框架,将极大地改变SRv6的设计.实现和部署方式,成为SRv6的新范式. 一.SRv6 101 Seg ...
- 中国数字化是怎么转型成新范式TOP 50的?
我不大认可"中国数字化转型成新范式TOP 50"的,确切的说,照着"中国数字化转型新范式TOP 50"做转型,大概率失败,对中国企业数字化转型的帮助甚微 ,尤其 ...
- 中文NER的那些事儿4. 数据增强在NER的尝试
这一章我们不聊模型来聊聊数据,解决实际问题时90%的时间其实都是在和数据作斗争,于是无标注,弱标注,少标注,半标注对应的各类解决方案可谓是百花齐放.在第二章我们也尝试通过多目标对抗学习的方式引入额外的 ...
- 中文NER的那些事儿1. Bert-Bilstm-CRF基线模型详解&代码实现
这个系列我们来聊聊序列标注中的中文实体识别问题,第一章让我们从当前比较通用的基准模型Bert+Bilstm+CRF说起,看看这个模型已经解决了哪些问题还有哪些问题待解决.以下模型实现和评估脚本,详见 ...
- 中文NER的那些事儿5. Transformer相对位置编码&TENER代码实现
这一章我们主要关注transformer在序列标注任务上的应用,作为2017年后最热的模型结构之一,在序列标注任务上原生transformer的表现并不尽如人意,效果比bilstm还要差不少,这背后有 ...
- 中文NER的那些事儿2. 多任务,对抗迁移学习详解&代码实现
第一章我们简单了解了NER任务和基线模型Bert-Bilstm-CRF基线模型详解&代码实现,这一章按解决问题的方法来划分,我们聊聊多任务学习,和对抗迁移学习是如何优化实体识别中边界模糊,垂直 ...
- 中文乱码问题(使用Servlet3.0新特性实现文件上传——上传文件名中文乱码问题)
问题描述:就是文件传送过来的文件名等是乱码 解决方法:将传送的JSP页面(即含有表单的页面)的页面编码方式改为: <%@ page contentType="text/html; ch ...
- 抛弃模板,一种Prompt Learning用于命名实体识别任务的新范式
原创作者 | 王翔 论文名称: Template-free Prompt Tuning for Few-shot NER 文献链接: https://arxiv.org/abs/2109.13532 ...
随机推荐
- Maven打包及场景
场景一 对当前项目打包并指定主类. <build> <plugins> <plugin> <artifactId>maven-compiler-plug ...
- [项目总结]论Android Adapter notifyDataSetChanged与notifyDataSetInvalidated无效原因
最近在开发中遇到一个问题,Adapter中使用notifyDataSetChanged 与notifyDataSetInvalidated无效,经过思考和网上查找,得出如下原因. 首先看一下notif ...
- jmeter设置参数化
设置参数化方法有3种 第一种: 1.打开 jmeter,导入badboy录制的脚本 导入后记得选择"step"右键选择change controller ->逻辑控制器-&g ...
- String.split()与StringUtils.split()的区别
import com.sun.deploy.util.StringUtils; String s =",1,,2,3,4,,"; String[] split1 = s.split ...
- 【Spring Framework】Spring入门教程(三)使用注解配置
本文主要介绍四个方面: (1) 注解版本IOC和DI (2) Spring纯注解 (3) Spring测试 (4) SpringJDBC - Spring对数据库的操作 使用注解配置Spring入门 ...
- Ajax异步更新网页(使用原生JavaScript)
一.页面代码 <!DOCTYPE html> <html> <head> <title>MyHtml.html</title> <me ...
- jdk1.7源码之-hashMap源码解析
背景: 笔者最近这几天在思考,为什么要学习设计模式,学些设计模式无非是提高自己的开发技能,但是通过这一段时间来看,其实我也学习了一些设计模式,但是都是一些demo,没有具体的例子,学习起来不深刻,所以 ...
- .net core容器添加时区和libgdi+和下载加速
国内.net core镜像下载加速 比如对于mcr.microsoft.com/dotnet/core/aspnet:3.1,下载是走的azure全球cdn,国内访问很慢. 国内访问可以把mcr.mi ...
- Jenkins 关闭和重启的实现方式
关闭jenkins 只需要在访问jenkins服务器的网址url地址后加上exit.例如我jenkins的地址http://localhost:8080/ , 那么我只需要在浏览器地址栏上敲下 htt ...
- CF173A Rock-Paper-Scissors 题解
Content 有 \(2\) 个人在玩石头剪刀布,已知他们的出手都有一定的规律,求 \(n\) 局之后两个人各输了几局. 数据范围:\(1\leqslant n\leqslant 2\times 1 ...