1. UIE 介绍
信息抽取(IE)是一个从文本到结构的转换过程。常见的实体、关系、事件分别采取Span、Triplet、Record形式的异构结构。具体表现为生成式统一建模,提出了面向信息抽取的统一文本到结构生成框架 UIE。
上面包含信息抽取的四个任务:实体识别、关系抽取、事件抽取、观点抽取。
信息抽取任务可以表述为“文本到结构”的问题,不同的 IE 任务对应不同的结构。UIE旨在通过单一框架统一建模不同IE任务的文本到结构的转换,也就是:不同的结构转换共享模型中 相同的底层操作 和 不同的转换能力。
主要有以下两个挑战:
1)IE任务的多样性,需要提取许多不同的目标结构,如实体、关系、事件等;
2)IE任务是通常是使用不同模式定义的特定需求(不同schema),需要自适应地控制提取过程;
因此,针对上述挑战,需要:
1)设计结构化抽取语言(SEL,Structured Extraction Language)来统一编码异构提取结构,即编码实体、关系、事件统一表示。
2)构建结构化模式提示器(SSI,Structural Schema Instructor),一个基于schema的prompt机制,用于控制不同的生成需求。
上图展示了UIE的整体框架,整体架构就是:SSI + Text -> SEL。一句话简单概括就是:SSI就是输入特定抽取任务的schema,SEL就是把不同任务的抽取结果统一用1种语言表示。
2. SEL:结构化抽取语言
SEL:本质上就是对输出进行统一化。不同的 IE 任务可以分解为 2 个原子操作:
- Spotting:找出Spot Name对应的Info Span,如某个实体或Trigger触发词;
- Associating:找出Asso Name对应的Info Span,链接Info Span片段间的关系:如两个实体pair的关系、论元和触发词间的关系;
如上图所示:
1)Spot Name:Spotting操作的Info Span的类别信息,如实体类型;
2)Asso Name: Associating操作的Info Span的类别信息,如关系类型、关系类型;
3)Info Span:Spotting或Associating操作相关的文本Span;
- 蓝色部分代表关系任务:person为实体类型Spot Name,work for为关系类型Asso Name;
- 红色部分代表事件任务:start-position为事件类型Spot Name,employee为论元类型Asso Name;
- 黑色部分代表实体任务:organization和time为实体类型Spot Name;
3. SSI:结构化模式提示器
SSI 的本质一个基于schema的prompt机制,用于控制不同的生成需求:在Text前拼接上相应的Schema Prompt,输出相应的SEL结构语言。
不同任务的的形式是:
1)实体抽取:[spot] 实体类别 [text]
2)关系抽取:[spot] 实体类别 [asso] 关系类别 [text]
3)事件抽取:[spot] 事件类别 [asso] 论元类别 [text]
4)观点抽取:[spot] 评价维度 [asso] 观点类别 [text]
不同数据集的详细形式见论文。
形式化表示如下:用 表示 SSI,用表示需要输入的原始句子,UIE 模型由 transformer 的 Encoder 和 Decoder 组成。
输出 就是采用 SEL 语法描述的结构化数据,其中
UIE 模型采用 Encoder 与 Decoder 结构进行生成式任务。
4. 预训练与微调
作者采用的模型是T5-v1.1-base和T5-v1.1-large作为UIE-base和UIE-large,模型的参数初始化直接使用了T5-v1.1的参数,也就是说直接基于其进行了二次预训练。
4.1 预训练数据
主要由Wikipedia、Wikidata和ConceptNet三部分组成,作者通过这三部分数据构造了如下三种形式的预训练数据:
1):数据表示为(),其中 是加在输入句子前面的prompt,是输入的原始句子,是需要生成的目标句子。
2):只有基于 SEL 语法的结构性 record,数据表示为()。
3):只有无结构的原始文本数据,进行 mask 语言模型训练,数据表示为()。
4.2 预训练任务
针对上述三种类型的数据,分别设计了三种预训练任务:
1)针对,输入数据为 SSI + 原始文本,使其生成结构化的数据 record,不同的是作者在 record 中不仅加入了原本存在的正样本,也加入了一些负样本作为噪音,比如。
2)针对,输入数据为 record,输入前面的部分,使其生成剩余部分,并且只训练 UIE 的 decoder 部分,使其学会 SEL 语法。
3)针对,做无监督的 mask 语言模型训练,在原始句子中 MASK 掉15%的 tokens,然后生成 MASK 的部分。
最终 Loss 为上述三个 Loss 相加。作者并不是分开做这三个预训练任务的,而是将其统一化,全部表示为三元组,在每个 batch 中随机抽取每个任务的数据去训练。
4.3 微调
微调与预训练任务的类似,数据形式是,微调部分依然加入了负样本。如下图所示:表格上部分为输入,下部分为第一行为正样本的输出,第二行为加入了负样本。随机插入一些原标签中没有的信息,即(Spot Name: [NULL])或(Asso Name: [NULL]),图中输入句子中并没有 facility 的实体,而标签中插入了(facility: [NULL])。
5. 实验
5.1 全监督实验
最右边的 SEL 列是指基于T5-v1.1-large进行微调得到的结果,UIE是指基于UIE-large进行微调的结果,可以看到几乎在全部数据集上都取得了SOTA的结果,但是通过对比SEL和UIE发现预训练部分对结果的提升并不大,通过这个可以看出作者设计的SEL语法和SSI还是很强大的,另一方面也说明T5本身的生成能力就很强大。
5.2 少样本实验
结论:
1)大规模异构监督预训练可以学习通用的信息抽取能力,使模型具有更好小样本学习能力。
2)当去掉SSI结构化模式提示器后,发现指标下降,因此:SSI 具有更好的定向迁移的能力。
5.3 消融实验
在小样本情景下,通过消融实验验证了插入负样本的有效性,结果如下图所示,这种插入负样本的方式在小样本情况下影响显著。
6. paddle 和 pytorch 代码解析
6.1 paddlenlp 解析
demo 代码:
from pprint import pprint
from paddlenlp import Taskflow
schema = [{"歌曲名称":["歌手", "所属专辑"]}, "人物"]
ie = Taskflow('information_extraction', schema=schema)
res = ie("告白气球是周杰伦在专辑周杰伦的床边故事里面的歌曲")
pprint(res)
输出结果如下:
[{'人物': [{'end': 8,
'probability': 0.9920330175461345,
'start': 5,
'text': '周杰伦'},
{'end': 14,
'probability': 0.9563452086659794,
'start': 11,
'text': '周杰伦'}],
'歌曲名称': [{'end': 4,
'probability': 0.9625632485885518,
'relations': {'所属专辑': [{'end': 19,
'probability': 0.8491942125644769,
'start': 11,
'text': '周杰伦的床边故事'}],
'歌手': [{'end': 8,
'probability': 0.9986735730139671,
'start': 5,
'text': '周杰伦'}]},
'start': 0,
'text': '告白气球'}]}]
经过 UIETask 类,继承自 Task 类,查看 Task 类的 call 函数:
def __call__(self, *args):
inputs = self._preprocess(*args)
outputs = self._run_model(inputs)
results = self._postprocess(outputs)
return results
查看 _run_model 方法在 UIETask 里面具体实现。
def _run_model(self, inputs):
raw_inputs = inputs['text']
results = self._multi_stage_predict(raw_inputs)
inputs['result'] = results
return inputs
查看 _multi_stage_predict 函数:遍历每个 schema,然后通过上一步的结果去进行预测。
while len(schema_list) > 0:
node = schema_list.pop(0)
...
...
if len(examples) == 0:
result_list = []
else:
result_list = self._single_stage_predict(examples)
结果如下:
[{'text': '告白气球是周杰伦在专辑周杰伦的床边故事里面的歌曲', 'prompt': '歌曲名称'}]
[{'text': '告白气球是周杰伦在专辑周杰伦的床边故事里面的歌曲', 'prompt': '人物'}]
[{'text': '告白气球是周杰伦在专辑周杰伦的床边故事里面的歌曲', 'prompt': '告白气球的歌手'}]
[{'text': '告白气球是周杰伦在专辑周杰伦的床边故事里面的歌曲', 'prompt': '告白气球的所属专辑'}]
从这些段代码可以看到,不管是训练还是推理,模型都未采用生成式。而是将prompt+text直接送入Ernie编码,然后编码送入指针网络,也就是一个全连接层预测实体的头,另一个预测实体的尾。
原始论文的实现方式,是通过生成式,一次性可以生成出SEL结构。而百度paddlenlp的做法,则是将SSI进行拆分,进行多步预测,需要先找出[SPOT]对应[Info Span],然后[Info Span]与[ASSO]拼接,生成新的prompt,然后再送入模型,找出[ASSO]。