用bert4keras实现 span-level NER

用bert4keras实现 span-level NER

继续追随苏神的脚步~

什么是span-level? 

在阅读一些论文的时候,在做文本序列任务时,会看到span-level,token-level的说法。在中文任务中token-level就是指字级别的任务,span-level可以理解成词级别的任务。以NER任务来说传统的LSTM+CRF 就是token-level的任务 对每个位置上的字进行预测,然后通过标注的规则解码出符合要求的连续token串组成实体。span-level就是以词为单位直接进行相关的任务,这里的词应该理解为任意个连续token串组成的连续文本(单个token也符合要求,也是一个span),与我们理解的词语不同。

总结一下:

在中文任务中 token-level就是指每个字,而span-level 就是任意个连续的字串组成的连续文本

在英文任务中 token-level就是指每个单词,而span-level 就是任意个连续单词串组成的连续文本

参考阅读 

Span-Level Model for Relation Extraction

nlp中的实体关系抽取方法总结

什么是span-level NER?

span-level NER 是一种应对嵌套实体任务的方法,基于片段排列的方式,提取所有可能的片段排列,当某个片段排列是我们需要的正确排列作为正确实体,其他的作为负面实体,通过SoftMax对每一个Span进行实体类型判断,将原来的序列标注问题转化成分类问题。

举个例子,针对下面的case:

句子:《邪少兵王》是冰火未央写的网络小说连载于旗峰天下 

这句话中包含的实体:邪少兵王(作品名), 冰火未央(作者)

在span-level NER任务中, 这个句子中任何长度的子串都是一个实体

我们理论上可以得到

'《'

‘《邪’

‘《邪少’

‘《邪少兵’

‘《邪少兵王’

‘《邪少兵王》’

‘《邪少兵王》是’

‘《邪少兵王》是冰’

‘《邪少兵王》是冰火’

...

等等的很多的实体 ,在上面这些实体中,‘《邪少兵王》’就是属于小说名称的正面实体,其他都是负面实体。然后用分类模型识别出正面实体。

原始数据集 百度2020比赛的数据集 百度网盘 请输入提取码 提取码 vu02

处理后生成的数据集 部分包含在github项目中 GitHub - hgliyuhao/span_level_ner

思路

整个算法其实就是一个分类模型 ,复杂的地方在于构建合适的训练集,需要在众多负面实体中选择一部分作为负样本。

可以看到在对于含n个token的文本,理论上共有n(n+1)/2 种片段排列。比如长度为20的句子,就有20*(20+1)/2个潜在的实体

对于该数据集共有110000左右的样本,其中很多都是非常长的句子,数据量简直爆炸,而且包含了大量的负样本,计算量也异常的大。

所以在算力有限的情况下,尝试做了一些优化:

1 用分词的方式去分割句子 代替原来按字的方式分割句子,将大量减少样本数量,并且可以增加样本的对抗性和合理性

2 随机在负样本中选取一定量的样本

3 拓展正面实体的边界获取对抗性较高的负样本,比如‘《邪少兵王》’是正面实体,就需要生成‘《邪少兵王 ’,‘邪少兵王》’,‘《邪少兵王》  ’这些对抗性强的负样本。(在实验的时候开始并没有针对的制作负样本,结果观察结果的时候

会发现有大量边界错误的问题)

4 限制实体的最大长度,效果同1

5 可以考虑去掉带有标点符号的负样本

具体实现

原数据集的样子:{"text": "《邪少兵王》是冰火未央写的网络小说连载于旗峰天下", "spo_list": [{"predicate": "作者", "object_type": {"@value": "人物"}, "subject_type": "图书作品", "object": {"@value": "冰火未央"}, "subject": "邪少兵王"}]}

转换成分类模型 即 (《邪少兵王》,图书作品) ,(冰火未央,人物),(未央写的网,非实体),(络小说连载于,非实体)

模型使用实体 + 原句拼接后 使用bert + 分类。这样融入了更多的语境信息,准确率更高。

也可以在bert后接了两层隐藏单元数为150的FFNN,然后接softmax。

output = Lambda(lambda x: x[:, 0],
                    name='CLS-token')(bert.model.output)
output = Dense(units=100,
                activation='relu',
                kernel_initializer=bert.initializer)(output)

output = Dense(units=100,
                activation='relu',
                kernel_initializer=bert.initializer)(output)

output = Dense(units=27,
                activation='softmax')(output)                

model = keras.models.Model(bert.model.input, output)

总结

其实这种NER的做法 整体模型结构很简单也很好理解 。最复杂的部分就是正负样本的构建。如果负样本过少就会得到大量的错误实体,如果负样本过多,训练的代价会非常大。

这个过程需要根据算法的表现不断调整策略。

有兴趣的同学可以看陈丹琦大佬的一篇最新的关系抽取SOTA《A Frustratingly Easy Approach for Joint Entity and Relation Extraction》,里面使用了这种方式去做NER

还有腾讯AI团队的《Empirical Analysis of Unlabeled Entity Problem in Named Entity Recognition》这篇也论证了这种基于片段排列的数据标注方式,会使模型更有鲁棒性,同时可以减少因为实体漏标对模型造成的负面影响

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值