从UIE模型理解到UIE工业实战

从UIE模型理解到UIE工业实战

  • UIE: 信息抽取的大一统模型
    • 原始论文
    • 背景
    • 信息抽取语言
    • 损失函数定义
    • 实验与结论
    • 结论
  • UIE实战
    • Paddlenlp之UIE模型实战实体抽取任务【打车数据、快递单】
    • Paddlenlp之UIE模型实战实体抽取任务【产品编号、需求数量】

UIE: 信息抽取的大一统模型

原始论文

uie论文链接: https://arxiv.org/abs/2203.12277

背景

之前的信息抽取做法:子任务实体抽取、关系抽取、事件抽取和情感分析分开抽取,如a。本文要做的是联合抽取,如b
从UIE模型理解到UIE工业实战_第1张图片

信息抽取语言

作者自定义了结构化的信息抽取语言SEL:结构如下

((定位:实体或触发等
(关系或角色:实体或触发等)
(关系或角色:实体或触发等)))
从UIE模型理解到UIE工业实战_第2张图片
文章实例:输入句子“Steve became CEO of Apple in 1997.”,则蓝色表示关系抽取,红色表示事件抽取,剩余的是实体抽取。例如,红色部分事件的触发词是became,雇员是Steve,雇主是Apple,时间是1997年
从UIE模型理解到UIE工业实战_第3张图片
上图就是UIE模型的三大联合抽取任务,左边就是输入数据结构,右边就是预测数据结构,那么模型是如何训练和预测的呢?重点就是定义损失函数(见下小节)。

需要注意的是,这里的输入数据由SSI+Text表示,其中SSI(Structural Schema Instructor)是指结构模式指引,就是提示哪些类型的数据需要被定位和关联,例如“[spot] person [spot] company [asso] work for [text]”表示需要从句子中提取关系模式为“该人为公司工作”,此时,根据SSI提示,UIE模型会对文本test进行编码,再通过编码器-解码器式的架构生成SEL语言的目标记录y
从UIE模型理解到UIE工业实战_第4张图片

从UIE模型理解到UIE工业实战_第5张图片
在这里插入图片描述
在这里插入图片描述
注:(4)式中的h(d)(i)是解码器状态,就是用解码器生成y

损失函数定义

作者定义了三个重要的损失函数:
从UIE模型理解到UIE工业实战_第6张图片
从UIE模型理解到UIE工业实战_第7张图片
介绍损失函数之前,作者介绍了他们使用的数据集是知识库(结构化数据)、文本(非结构化数据)和维基百科(并行数据)来预训练UIE模型,并按需微调使预训练的UIE模型适应特定的下游IE任务。简单讲,就是UIE的通用性比较强,可以适应大多数场景的信息抽取任务,实在厉害。

详细结构一下数据集:

数据集1:Dpair是文本x-结构y对的并行数据集,通过维基百科搜集,用于预训练UIE对文本test转换为结构structure的能力。例如 “[spot] person [asso] work for [text]Steve became CEO of Apple in 1997.-((person: Steve(work for: Apple)))”;

数据集2:Drecord是结构化数据集,每一条数据就是结构y,用于预训练UIE的结构解码能力。例如“((person: Steve(work for: Apple)))”

数据集3:Dtext是非结构化数据集,来自英语维基百科所有纯文本,用于预训练UIE的语义编码能力

了解了数据集,下面就是重要的损失函数设计了。

1、第一个损失函数是:Lpair,用于训练模型对文本到结构的映射能力。
从UIE模型理解到UIE工业实战_第8张图片
简单理解就是对生成的token加个损失。作者这里还加了一个技巧,在已有正样本的Alt
的同时构造了负样本从UIE模型理解到UIE工业实战_第9张图片
ss是指spots,sa是指association,负样本的添加可以让模型学习一般的映射能力。

举个例子,正样本是“((person: Steve(work for: Apple)))”,负样本是“((vehicle: Steve(located in: Apple)))”

2、第二个损失函数是:Lrecord,用于训练模型对SEL结构的生成能力。
从UIE模型理解到UIE工业实战_第10张图片
这里用了UIE的decoder解码器作为结构化语言模型,在数据集2 Drecord做预训练,相当于一个自回归损失,通过对结构生成的预训练,解码器可以捕捉到SEL的规律性和不同标签之间的相互作用。

3、第三个损失函数是:Ltext,作者把这个损失叫做改善语义表征(Retrofitting Semantic Representation)。从UIE模型理解到UIE工业实战_第11张图片
这里就是一个MLM task,一个类似于bert的语言屏蔽模型任务,如果想具体了解参考论文:Exploring the limits of transfer learning with a unified text-to-text transformer
作者认为这个损失的加入可以缓解对SPOTNAME和ASSONAME标记语义的灾难性遗忘(可以参考bert MLM任务)

至此,总损失就是上面三个损失相加:

在这里插入图片描述

实验与结论

作者采用了13个典型的IE数据集进行了实验,涉及了4个具有代表性的IE任务(包括实体提取、关系提取、事件提取、结构化情感提取)及其组合(例如,联合实体-关系提取),实验结果很惊人(非常好了)
从UIE模型理解到UIE工业实战_第12张图片
上图是UIE-base模型在13个典型数据集上达到的效果。SEL指的是没有经过预训练的UIE,直接使用T5-v1.1-large的效果,而经过预训练的UIE效果基本上都提升了。

对小样本数据集的实验(Experiments on Low-resource Settings):目的是验证UIE的快速适应能力。作者对原始训练集进行了6种采样,前三种分别是1/5/10shot(就是在少量的实验中,我们对训练集中的每个实体/关系/事件/情感类型的1/5/10句子进行抽样),后三种是直接1/5/10%的比例下采样。
用上面6种方式分别对4个任务中低资源实验,为了避免随机抽样的影响,作者对不同的样本重复每个实验10次,结果如下:

从UIE模型理解到UIE工业实战_第13张图片
作者还比较了四种模型的效果。其中,1)T5-v1.1-base是UIE-base的初始化模型;2)Fine-tuned T5-base是微调的T5-base模型,这个模型是用与序列生成任务(如摘要)经过微调的模型,这在许多低资源的NLP任务中被证明是有效的(把它拿出来对比,说明UIE比这个通用的T5还要强);3)UIEbase w/o SSI是UIE的有监督版本,就是说在预训练阶段没有SSI(把它拿出来做对比是为了验证SSI在小样本低资源下对适应UIE的必要性)

直接看上图吧,很好的展示出了UIE-base的不错效果。

作者重点说明了UIE模型的优点:1)在与没有SSI的UIE模型相比,配备SSI的UIE在n-shot和n-ratio实验中平均取得了4.16和3.30的改进。2)我们的预训练算法可以学习一般的IE能力,而不是捕捉特定的任务信息。 与只有少量样本的baseline相比,即使UIE的预训练中没有事件和情感相关知识,UIE在这些任务上仍然取得了明显更好的性能。

表4显示了UIE-base在四个下游任务上的消融实验结果,目的是研究不同预训练任务的效果:
从UIE模型理解到UIE工业实战_第14张图片
上表说明1)预训练LPair和Lrecord对于UIE-base至关重要;在小数据集CoNLL04和16res上 2)使用掩码语言模型任务LText进行语义改造对于复杂的提取任务更为重要。33种类型的事件提取在去除LText文本任务后性能从72.63下降到70.89。3)用LPair进行映射预训练使模型能够学习提取的能力。消减LPair后,UIE的提取能力明显下降。

表5展示了生成错误标签与不生成错误标签(负样本)对模型的影响。
从UIE模型理解到UIE工业实战_第15张图片

结论

作者提出了一个统一的文本到结构生成框架 , 即 UIE , 可以普遍地模拟不同的 信息抽取(IE)任务 , 自适应地生成目标结构 , 并能不厌烦得从不同的知识数据源中学习通用的 IE 能力。作者认为,UIE在有监督和低资源环境下都取得了非常有竞争力的性能,这验证了其普遍性、有效性和可转移性。一个大规模的预训练文本-结构模型的发布有利于未来的研究。对于未来的工作 , 他们希望将 UIE 扩展到 KB 感知的 IE 任务种 , 如实体链接 ( Cao 等人 , 2021 ) , 以及文档感知的 IE 任务 , 如co-reference( Lee 等人 , 2017 ; Lu 等人 , 2022 ) 。

UIE实战

Paddlenlp之UIE模型实战实体抽取任务【打车数据、快递单】

本项目将演示如何通过小样本进行模型微调,快速且准确抽取快递单中的目的地、出发地、时间、打车费用等内容,形成结构化信息。辅助物流行业从业者进行有效信息的提取,从而降低客户填单的成本。其他信息抽取项目也可用此项目思想来做。

先看一下结果展示:

输入:

城市内交通费75日金额114广州至佛山
从百度大厦到龙泽苑东区打车费二十元
上海虹桥高铁到杭州时间是924日费用是73元
上周末坐动车从北京到上海花费五十块五毛
昨天北京飞上海话费一百元

输出:

{"出发地": [{"text": "广州", "start": 15, "end": 17, "probability": 0.9073772252165782}], "目的地": [{"text": "佛山", "start": 18, "end": 20, "probability": 0.9927365183877761}], "时间": [{"text": "7月5日", "start": 6, "end": 10, "probability": 0.9978010396512218}]}
{"出发地": [{"text": "百度大厦", "start": 1, "end": 5, "probability": 0.968825147409472}], "目的地": [{"text": "龙泽苑东区", "start": 6, "end": 11, "probability": 0.9877913072493669}]}
{"目的地": [{"text": "杭州", "start": 7, "end": 9, "probability": 0.9929172180094881}], "时间": [{"text": "9月24日", "start": 12, "end": 17, "probability": 0.9953342057701597}]}
{"出发地": [{"text": "北京", "start": 7, "end": 9, "probability": 0.973048366717471}], "目的地": [{"text": "上海", "start": 10, "end": 12, "probability": 0.988486130309397}], "时间": [{"text": "上周末", "start": 0, "end": 3, "probability": 0.9977407699595275}]}
{"出发地": [{"text": "北京", "start": 2, "end": 4, "probability": 0.974188953533556}], "目的地": [{"text": "上海", "start": 5, "end": 7, "probability": 0.9928200521486445}], "时间": [{"text": "昨天", "start": 0, "end": 2, "probability": 0.9731559534465504}]}

1、先升级paddlenlp

! pip install --upgrade paddlenlp

2、数据集加载(快递单数据、打车数据)

2.1 使用doccano进行数据标注,将标注好的数据放到data文件夹中

# ! wget https://paddlenlp.bj.bcebos.com/uie/datasets/waybill.jsonl
# ! mv waybill.jsonl ./data/
# ! mv doccano_ext.jsonl ./data/
# ! mv dev_test.jsonl ./data/

数据集情况:

waybill.jsonl文件是快递单信息数据集:

{"id": 57, "text": "昌胜远黑龙江省哈尔滨市南岗区宽桥街28号18618391296", "relations": [], "entities": [{"id": 111, "start_offset": 0, "end_offset": 3, "label": "姓名"}, {"id": 112, "start_offset": 3, "end_offset": 7, "label": "省份"}, {"id": 113, "start_offset": 7, "end_offset": 11, "label": "城市"}, {"id": 114, "start_offset": 11, "end_offset": 14, "label": "县区"}, {"id": 115, "start_offset": 14, "end_offset": 20, "label": "详细地址"}, {"id": 116, "start_offset": 20, "end_offset": 31, "label": "电话"}]} {"id": 58, "text": "易颖18500308469山东省烟台市莱阳市富水南路1号", "relations": [], "entities": [{"id": 118, "start_offset": 0, "end_offset": 2, "label": "姓名"}, {"id": 119, "start_offset": 2, "end_offset": 13, "label": "电话"}, {"id": 120, "start_offset": 13, "end_offset": 16, "label": "省份"}, {"id": 121, "start_offset": 16, "end_offset": 19, "label": "城市"}, {"id": 122, "start_offset": 19, "end_offset": 22, "label": "县区"}, {"id": 123, "start_offset": 22, "end_offset": 28, "label": "详细地址"}]}

doccano_ext.jsonl是打车数据集:

{"id": 1, "text": "昨天晚上十点加班打车回家58元", "relations": [], "entities": [{"id": 0, "start_offset": 0, "end_offset": 6, "label": "时间"}, {"id": 1, "start_offset": 11, "end_offset": 12, "label": "目的地"}, {"id": 2, "start_offset": 12, "end_offset": 14, "label": "费用"}]} {"id": 2, "text": "三月三号早上12点46加班,到公司54", "relations": [], "entities": [{"id": 3, "start_offset": 0, "end_offset": 11, "label": "时间"}, {"id": 4, "start_offset": 15, "end_offset": 17, "label": "目的地"}, {"id": 5, "start_offset": 17, "end_offset": 19, "label": "费用"}]} {"id": 3, "text": "8月31号十一点零四工作加班五十块钱", "relations": [], "entities": [{"id": 6, "start_offset": 0, "end_offset": 10, "label": "时间"}, {"id": 7, "start_offset": 14, "end_offset": 16, "label": "费用"}]} {"id": 4, "text": "5月17号晚上10点35分加班打车回家,36块五", "relations": [], "entities": [{"id": 8, "start_offset": 0, "end_offset": 13, "label": "时间"}, {"id": 1, "start_offset": 18, "end_offset": 19, "label": "目的地"}, {"id": 9, "start_offset": 20, "end_offset": 24, "label": "费用"}]} {"id": 5, "text": "2009年1月份通讯费一百元", "relations": [], "entities": [{"id": 10, "start_offset": 0, "end_offset": 7, "label": "时间"}, {"id": 11, "start_offset": 11, "end_offset": 13, "label": "费用"}]}

2.2 转换doccano标注数据集为uie格式的标注数据

!python doccano.py \
    --doccano_file ./data/doccano_ext.jsonl \
    --task_type 'ext' \
    --save_dir ./data \
    --splits 0.8 0.1 0.1 \
    --negative_ratio 5 \
    --seed 1000

参数解释:

doccano_file: 从doccano导出的数据标注文件。

save_dir: 训练数据的保存目录,默认存储在data目录下。

negative_ratio: 最大负例比例,该参数只对抽取类型任务有效,适当构造负例可提升模型效果。负例数量和实际的标签数量有关,最大负例数量 = negative_ratio * 正例数量。该参数只对训练集有效,默认为5。为了保证评估指标的准确性,验证集和测试集默认构造全负例。

splits: 划分数据集时训练集、验证集所占的比例。默认为[0.8, 0.1, 0.1]表示按照8:1:1的比例将数据划分为训练集、验证集和测试集。

task_type: 选择任务类型,可选有抽取和分类两种类型的任务。

options: 指定分类任务的类别标签,该参数只对分类类型任务有效。默认为[“正向”, “负向”]。

prompt_prefix: 声明分类任务的prompt前缀信息,该参数只对分类类型任务有效。默认为"情感倾向"。

is_shuffle: 是否对数据集进行随机打散,默认为True。

seed: 随机种子,默认为1000.

*separator: 实体类别/评价维度与分类标签的分隔符,该参数只对实体/评价维度级分类任务有效。默认为"##"。

3、训练UIE模型

!python finetune.py \
    --train_path "./data/train.txt" \
    --dev_path "./data/dev.txt" \
    --save_dir "./checkpoint" \
    --learning_rate 1e-5 \
    --batch_size 8 \
    --max_seq_len 512 \
    --num_epochs 100 \
    --model "uie-base" \
    --seed 1000 \
    --logging_steps 10 \
    --valid_steps 50 \
    --device "gpu"

使用标注数据进行小样本训练,模型参数保存在./checkpoint/目录。

推荐使用GPU环境,否则可能会内存溢出。CPU环境下,可以修改model为uie-tiny,适当调下batch_size。

增加准确率的话:–num_epochs 设置大点多训练训练

可配置参数说明:

train_path: 训练集文件路径。

dev_path: 验证集文件路径。

save_dir: 模型存储路径,默认为./checkpoint。

learning_rate: 学习率,默认为1e-5。

batch_size: 批处理大小,请结合显存情况进行调整,若出现显存不足,请适当调低这一参数,默认为16。

max_seq_len: 文本最大切分长度,输入超过最大长度时会对输入文本进行自动切分,默认为512。

num_epochs: 训练轮数,默认为100。

model 选择模型,程序会基于选择的模型进行模型微调,可选有uie-base和uie-tiny,默认为uie-base。

seed: 随机种子,默认为1000.

logging_steps: 日志打印的间隔steps数,默认10。

valid_steps: evaluate的间隔steps数,默认100。

device: 选用什么设备进行训练,可选cpu或gpu。

4、评估UIE模型

!python evaluate.py \
    --model_path ./checkpoint/model_best \
    --test_path ./data/test.txt \
    --batch_size 16 \
    --max_seq_len 512
!python evaluate.py \
    --model_path ./checkpoint/model_best \
    --test_path ./data/test.txt \
    --debug

可配置参数说明:

model_path: 进行评估的模型文件夹路径,路径下需包含模型权重文件model_state.pdparams及配置文件model_config.json。

test_path: 进行评估的测试集文件。

batch_size: 批处理大小,请结合机器情况进行调整,默认为16。

max_seq_len: 文本最大切分长度,输入超过最大长度时会对输入文本进行自动切分,默认为512。

model: 选择所使用的模型,可选有uie-base, uie-medium, uie-mini, uie-micro和uie-nano,默认为uie-base。

debug: 是否开启debug模式对每个正例类别分别进行评估,该模式仅用于模型调试,默认关闭。

5、结果预测

from pprint import pprint
import json
from paddlenlp import Taskflow

def openreadtxt(file_name):
    data = []
    file = open(file_name,'r',encoding='UTF-8')
    file_data = file.readlines()
    for row in file_data:
        data.append(row) 
    return data

data_input=openreadtxt('./input/text1.txt')

schema = ['出发地', '目的地','时间']
few_ie = Taskflow('information_extraction', schema=schema, batch_size=1, task_path='./checkpoint/model_best') #task_path为训练好的模型位置

results=few_ie(data_input)

with open("./output/test.txt", "w+",encoding='UTF-8') as f:
    for result in results:
        line = json.dumps(result, ensure_ascii=False)  #对中文默认使用的ascii编码,想输出真正的中文需要指定ensure_ascii=False
        f.write(line + "\n")

print("数据结果已导出")

text1输入文件展示:

城市内交通费75日金额114广州至佛山
从百度大厦到龙泽苑东区打车费二十元
上海虹桥高铁到杭州时间是924日费用是73元
上周末坐动车从北京到上海花费五十块五毛
昨天北京飞上海话费一百元

输出展示:

{"出发地": [{"text": "广州", "start": 15, "end": 17, "probability": 0.9073772252165782}], "目的地": [{"text": "佛山", "start": 18, "end": 20, "probability": 0.9927365183877761}], "时间": [{"text": "7月5日", "start": 6, "end": 10, "probability": 0.9978010396512218}]}
{"出发地": [{"text": "百度大厦", "start": 1, "end": 5, "probability": 0.968825147409472}], "目的地": [{"text": "龙泽苑东区", "start": 6, "end": 11, "probability": 0.9877913072493669}]}
{"目的地": [{"text": "杭州", "start": 7, "end": 9, "probability": 0.9929172180094881}], "时间": [{"text": "9月24日", "start": 12, "end": 17, "probability": 0.9953342057701597}]}
{"出发地": [{"text": "北京", "start": 7, "end": 9, "probability": 0.973048366717471}], "目的地": [{"text": "上海", "start": 10, "end": 12, "probability": 0.988486130309397}], "时间": [{"text": "上周末", "start": 0, "end": 3, "probability": 0.9977407699595275}]}
{"出发地": [{"text": "北京", "start": 2, "end": 4, "probability": 0.974188953533556}], "目的地": [{"text": "上海", "start": 5, "end": 7, "probability": 0.9928200521486445}], "时间": [{"text": "昨天", "start": 0, "end": 2, "probability": 0.9731559534465504}]}

Paddlenlp之UIE模型实战实体抽取任务【产品编号、需求数量】

你可能感兴趣的:(自然语言处理,人工智能,算法,深度学习)