针对以上难题,中科院软件所和百度共同提出了一个大一统诸多任务的通用信息抽取技术 UIE(Unified Structure Generation for Universal Information Extraction),发表在ACL‘22。UIE在实体、关系、事件和情感等4个信息抽取任务、13个数据集的全监督、低资源和少样本设置下,UIE均取得了SOTA性能。
PaddleNLP结合文心大模型中的知识增强NLP大模型ERNIE 3.0,发挥了UIE在中文任务上的强大潜力,开源了首个面向通用信息抽取的产业级技术方案,不需要标注数据(或仅需少量标注数据),即可快速完成各类信息抽取任务。
喜欢这个功能的朋友,欢迎点个⭐️star⭐支持我们(链接指路:https://github.com/PaddlePaddle/PaddleNLP/tree/develop/model_zoo/uie )一起进步❤️❤️
也欢迎加入PaddleNLP的技术交流群(微信),一起交流NLP技术!入群有PaddleNLP官方直播课链接,以及10G重磅NLP学习大礼包哦!
直播课内容:
✅ ACL’22 顶会Paper,产业级通用信息抽取技术UIE
✅ ERNIE 3.0轻量级模型讲解
NLP学习礼包:
! pip install --upgrade paddlenlp
! pip show paddlenlp
from paddlenlp import Taskflow
schema = ['名称', '毕业院校', '职位', '月收入', '身体状况']
ie = Taskflow('information_extraction', schema=schema)
ie('兹证明凌霄为本单位职工,已连续在我单位工作5 年。学历为嘉利顿大学毕业,目前在我单位担任总经理助理 职位。近一年内该员工在我单位平均月收入(税后)为 12000 元。该职工身体状况良好。本单位仅此承诺上述表述是正确的,真实的。')
[{'毕业院校': [{'text': '嘉利顿大学', 'start': 28, 'end': 33, 'probability': 0.9927976418453142}], '职位': [{'text': '总经理助理', 'start': 44, 'end': 49, 'probability': 0.9922573060948707}], '月收入': [{'text': '12000 元', 'start': 77, 'end': 84, 'probability': 0.9788572282436334}], '身体状况': [{'text': '良好', 'start': 92, 'end': 94, 'probability': 0.9939773306889848}]}]
# Jupyter Notebook默认做了格式化输出,如果使用其他代码编辑器,可以使用Python原生包pprint进行格式化输出
from pprint import pprint
pprint(ie('兹证明凌霄为本单位职工,已连续在我单位工作5 年。学历为嘉利顿大学毕业,目前在我单位担任总经理助理 职位。近一年内该员工在我单位平均月收入(税后)为 12000 元。该职工身体状况良好。本单位仅此承诺上述表述是正确的,真实的。'))
[{'月收入': [{'end': 84, 'probability': 0.9788572282436334, 'start': 77, 'text': '12000 元'}], '毕业院校': [{'end': 33, 'probability': 0.9927976418453142, 'start': 28, 'text': '嘉利顿大学'}], '职位': [{'end': 49, 'probability': 0.9922573060948707, 'start': 44, 'text': '总经理助理'}], '身体状况': [{'end': 94, 'probability': 0.9939773306889848, 'start': 92, 'text': '良好'}]}]
schema = ['肿瘤部位', '肿瘤大小']
ie.set_schema(schema)
ie('胃印戒细胞癌,肿瘤主要位于胃窦体部,大小6*2cm,癌组织侵及胃壁浆膜层,并侵犯血管和神经。')
[{'肿瘤部位': [{'text': '胃窦体部', 'start': 13, 'end': 17, 'probability': 0.96023779265877}], '肿瘤大小': [{'text': '6*2cm', 'start': 20, 'end': 25, 'probability': 0.9671645096499759}]}]
# 实体抽取
schema = ['时间', '赛手', '赛事名称']
ie.set_schema(schema)
ie('2月8日上午北京冬奥会自由式滑雪女子大跳台决赛中中国选手谷爱凌以188.25分获得金牌!')
[{'时间': [{'text': '2月8日上午', 'start': 0, 'end': 6, 'probability': 0.9857490371462205}], '赛手': [{'text': '中国选手谷爱凌', 'start': 24, 'end': 31, 'probability': 0.7236420795636178}], '赛事名称': [{'text': '北京冬奥会自由式滑雪女子大跳台决赛', 'start': 6, 'end': 23, 'probability': 0.8501073600559224}]}]
# 关系抽取
schema = {'歌曲名称': ['歌手', '所属专辑']}
ie.set_schema(schema)
ie('《告别了》是孙耀威在专辑爱的故事里面的歌曲')
[{'歌曲名称': [{'text': '告别了', 'start': 1, 'end': 4, 'probability': 0.6280605279757197, 'relations': {'歌手': [{'text': '孙耀威', 'start': 6, 'end': 9, 'probability': 0.9988395302966424}], '所属专辑': [{'text': '爱的故事', 'start': 12, 'end': 16, 'probability': 0.9968425180391449}]}}, {'text': '爱的故事', 'start': 12, 'end': 16, 'probability': 0.28189926607866056, 'relations': {'歌手': [{'text': '孙耀威', 'start': 6, 'end': 9, 'probability': 0.9951328819836576}]}}]}]
# 事件抽取
schema = {'地震触发词': ['地震强度', '时间', '震中位置', '震源深度']} # 事件需要通过xxx触发词来选择触发词
ie.set_schema(schema)
ie('中国地震台网正式测定:5月16日06时08分在云南临沧市凤庆县(北纬24.34度,东经99.98度)发生3.5级地震,震源深度10千米。')
[{'地震触发词': [{'text': '地震', 'start': 56, 'end': 58, 'probability': 0.9977414839118168, 'relations': {'地震强度': [{'text': '3.5级', 'start': 52, 'end': 56, 'probability': 0.9980784309576691}], '时间': [{'text': '5月16日06时08分', 'start': 11, 'end': 22, 'probability': 0.9853360715134016}], '震中位置': [{'text': '云南临沧市凤庆县(北纬24.34度,东经99.98度)', 'start': 23, 'end': 50, 'probability': 0.7877027859608745}], '震源深度': [{'text': '10千米', 'start': 63, 'end': 67, 'probability': 0.9938026135573423}]}}]}]
# 情感倾向分类
schema = '情感倾向[正向,负向]' # 分类任务需要[]来设置分类的label
ie.set_schema(schema)
ie('这个产品用起来真的很流畅,我非常喜欢')
[{'情感倾向[正向,负向]': [{'text': '正向', 'probability': 0.9990022866755055}]}]
# 评价抽取
schema = {'评价维度': '观点词'} # 评价抽取的schema是固定的,后续直接按照这个schema进行观点抽取
ie.set_schema(schema) # Reset schema
ie('个人觉得管理太混乱了,票价太高了')
[{'评价维度': [{'text': '管理', 'start': 4, 'end': 6, 'probability': 0.9110394498307599, 'relations': {'观点词': [{'text': '混乱', 'start': 7, 'end': 9, 'probability': 0.9730200313675681}]}}, {'text': '票价', 'start': 11, 'end': 13, 'probability': 0.954214410019727, 'relations': {'观点词': [{'text': '高', 'start': 14, 'end': 15, 'probability': 0.9699452364742456}]}}]}]
# 跨任务跨领域抽取
schema = ['寺庙', {'丈夫': '妻子'}] # 抽取的任务中包含了实体抽取和关系抽取
ie.set_schema(schema)
ie('李治即位后,让身在感业寺的武则天续起头发,重新纳入后宫。')
[{'寺庙': [{'text': '感业寺', 'start': 9, 'end': 12, 'probability': 0.9888500550951385}], '丈夫': [{'text': '李治', 'start': 0, 'end': 2, 'probability': 0.9896618131065296, 'relations': {'妻子': [{'text': '武则天', 'start': 13, 'end': 16, 'probability': 0.9987624795214423}]}}]}]
schema = ['才人']
ie.set_schema(schema)
ie('李治即位后,让身在感业寺的武则天续起头发,重新纳入后宫。')
[{}]
schema = ['妃子']
ie.set_schema(schema)
ie('李治即位后,让身在感业寺的武则天续起头发,重新纳入后宫。')
[{'妃子': [{'text': '武则天', 'start': 13, 'end': 16, 'probability': 0.9976351549483411}]}]
from paddlenlp import Taskflow
schema = ['费用']
ie = Taskflow('information_extraction', schema=schema, batch_size=2)
ie(['二十号21点49分打车回家46块钱', '8月3号往返机场交通费110元', '2019年10月17日22点18分回家打车46元', '三月三0号23点10分加班打车21元'])
[2022-05-17 17:59:48,338] [ INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load 'ernie-3.0-base-zh'. [2022-05-17 17:59:48,347] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/ernie-3.0-base-zh/ernie_3.0_base_zh_vocab.txt
[{'费用': [{'text': '46块钱', 'start': 13, 'end': 17, 'probability': 0.9782020045014761}]}, {'费用': [{'text': '110元', 'start': 11, 'end': 15, 'probability': 0.9503916347146628}]}, {'费用': [{'text': '46元', 'start': 21, 'end': 24, 'probability': 0.9753546414929133}]}, {'费用': [{'text': '21元', 'start': 15, 'end': 18, 'probability': 0.9761186341737371}]}]
Taskflow中的UIE基线版本我们是通过大量的有标签样本进行训练,但是UIE抽取的效果面对部分子领域的效果也不是令人满意,UIE可以通过小样本就可以快速提升效果。
为什么UIE可以通过小样本来提升效果呢?UIE的建模方式主要是通过 Prompt
方式来建模, Prompt
在小样本上进行微调效果非常有效,下面我们通过一个具体的case
来展示UIE微调的效果。
在某公司内部可以通过语音输入来报销打车费用,通过语音ASR模型可以将语音识别为文字,同时对文字信息进行信息抽取,抽取的信息主要是包括了4个方面,时间、出发地、目的地、费用,通过对文字4个方面的信息进行抽取就可以完成一个报销工单的填写。
目前Taskflow UIE任务对于这种非常垂类的任务效果没有完全达到工业使用水平,因此需要一定的微调手段来完成UIE模型的微调来提升模型的效果,下面是一些case的展现
ie.set_schema(['时间', '出发地', '目的地', '费用'])
ie('10月16日高铁从杭州到上海南站车次d5414共48元') # 无法准确抽取出发地、目的地
[{'时间': [{'text': '10月16日', 'start': 0, 'end': 6, 'probability': 0.9885428493720383}], '出发地': [{'text': '杭州', 'start': 9, 'end': 11, 'probability': 0.6861097354006951}], '目的地': [{'text': '杭州', 'start': 9, 'end': 11, 'probability': 0.31515575235882665}], '费用': [{'text': '48元', 'start': 24, 'end': 27, 'probability': 0.9643074997657948}]}]
我们推荐使用数据标注平台doccano 进行数据标注,本案例也打通了从标注到训练的通道,即doccano导出数据后可通过doccano.py脚本轻松将数据转换为输入模型时需要的形式,实现无缝衔接。为达到这个目的,您需要按以下标注规则在doccano平台上标注数据:
Step 1. 本地安装doccano(请勿在AI Studio内部运行,本地测试环境python=3.8)
$ pip install doccano
Step 2. 初始化数据库和账户(用户名和密码可替换为自定义值)
$ doccano init
$ doccano createuser --username my_admin_name --password my_password
Step 3. 启动doccano
$ doccano webserver --port 8000
$ doccano task
Step 4. 运行doccano来标注实体和关系
打开浏览器(推荐Chrome),在地址栏中输入http://0.0.0.0:8000/
后回车即得以下界面。
登陆账户。点击右上角的LOGIN
,输入Step 2中设置的用户名和密码登陆。
创建项目。点击左上角的CREATE
,跳转至以下界面。
Sequence Labeling
)Project name
)等必要信息Allow overlapping entity
)、使用关系标注(Use relation labeling
)设置标签。在Labels一栏点击Actions
,Create Label
手动设置或者Import Labels
从文件导入。
导入数据。在Datasets一栏点击Actions
、Import Dataset
从文件导入文本数据。
标注数据。点击每条数据最右边的Annotate
按钮开始标记。标记页面右侧的标签类型(Label Types)开关可在实体标签和关系标签之间切换。
导出数据。在Datasets一栏点击Actions
、Export Dataset
导出已标注的数据。
./data/
目录。对于语音报销工单信息抽取的场景,可以直接下载标注好的数据。https://github.com/PaddlePaddle/PaddleNLP/blob/develop/model_zoo/uie/doccano.md
! wget https://paddlenlp.bj.bcebos.com/datasets/erniekit/speech-cmd-analysis/audio-expense-account.jsonl
! mv audio-expense-account.jsonl ./data/
! python preprocess.py --input_file ./data/audio-expense-account.jsonl --save_dir ./data/ --negative_ratio 5 --splits 0.2 0.8 0.0 --seed 1000
! python finetune.py --train_path ./data/train.txt --dev_path ./data/dev.txt --save_dir ./checkpoint --model uie-base --learning_rate 1e-5 --batch_size 16 --max_seq_len 512 --num_epochs 50 --seed 1000 --logging_steps 10 --valid_steps 10
[2022-05-17 17:59:54,779] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/ernie-3.0-base-zh/ernie_3.0_base_zh_vocab.txt [2022-05-17 17:59:54,805] [ INFO] - We are using <class 'paddlenlp.transformers.ernie.modeling.ErnieModel'> to load 'ernie-3.0-base-zh'. [2022-05-17 17:59:54,805] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/ernie-3.0-base-zh/ernie_3.0_base_zh.pdparams W0517 17:59:54.806599 2725 device_context.cc:447] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 11.2, Runtime API Version: 11.2 W0517 17:59:54.809562 2725 device_context.cc:465] device: 0, cuDNN Version: 8.2. [2022-05-17 17:59:59,736] [ INFO] - Weights from pretrained model not used in ErnieModel: ['cls.predictions.transform.weight', 'cls.predictions.layer_norm.weight', 'cls.predictions.transform.bias', 'cls.predictions.layer_norm.bias', 'cls.predictions.decoder_bias'] Init from: uie-base/model_state.pdparams global step 10, epoch: 4, loss: 0.00265, speed: 3.82 step/s Evaluation precision: 0.59091, recall: 0.60465, F1: 0.59770 best F1 performence has been updated: 0.00000 --> 0.59770 global step 20, epoch: 7, loss: 0.00203, speed: 8.73 step/s Evaluation precision: 0.74783, recall: 0.66667, F1: 0.70492 best F1 performence has been updated: 0.59770 --> 0.70492 global step 30, epoch: 10, loss: 0.00168, speed: 9.18 step/s Evaluation precision: 0.80000, recall: 0.71318, F1: 0.75410 best F1 performence has been updated: 0.70492 --> 0.75410 global step 40, epoch: 14, loss: 0.00138, speed: 8.32 step/s Evaluation precision: 0.85714, recall: 0.79070, F1: 0.82258 best F1 performence has been updated: 0.75410 --> 0.82258 global step 50, epoch: 17, loss: 0.00116, speed: 8.81 step/s Evaluation precision: 0.87603, recall: 0.82171, F1: 0.84800 best F1 performence has been updated: 0.82258 --> 0.84800 global step 60, epoch: 20, loss: 0.00099, speed: 9.05 step/s Evaluation precision: 0.90164, recall: 0.85271, F1: 0.87649 best F1 performence has been updated: 0.84800 --> 0.87649 global step 70, epoch: 24, loss: 0.00086, speed: 8.35 step/s Evaluation precision: 0.91057, recall: 0.86822, F1: 0.88889 best F1 performence has been updated: 0.87649 --> 0.88889 global step 80, epoch: 27, loss: 0.00076, speed: 8.87 step/s Evaluation precision: 0.91870, recall: 0.87597, F1: 0.89683 best F1 performence has been updated: 0.88889 --> 0.89683 global step 90, epoch: 30, loss: 0.00068, speed: 9.32 step/s Evaluation precision: 0.92623, recall: 0.87597, F1: 0.90040 best F1 performence has been updated: 0.89683 --> 0.90040 global step 100, epoch: 34, loss: 0.00061, speed: 8.55 step/s Evaluation precision: 0.92683, recall: 0.88372, F1: 0.90476 best F1 performence has been updated: 0.90040 --> 0.90476 global step 110, epoch: 37, loss: 0.00056, speed: 8.78 step/s Evaluation precision: 0.92683, recall: 0.88372, F1: 0.90476 global step 120, epoch: 40, loss: 0.00051, speed: 9.26 step/s Evaluation precision: 0.93548, recall: 0.89922, F1: 0.91700 best F1 performence has been updated: 0.90476 --> 0.91700 global step 130, epoch: 44, loss: 0.00047, speed: 8.54 step/s Evaluation precision: 0.94309, recall: 0.89922, F1: 0.92063 best F1 performence has been updated: 0.91700 --> 0.92063 global step 140, epoch: 47, loss: 0.00044, speed: 8.71 step/s Evaluation precision: 0.93548, recall: 0.89922, F1: 0.91700 global step 150, epoch: 50, loss: 0.00041, speed: 9.28 step/s Evaluation precision: 0.93548, recall: 0.89922, F1: 0.91700
from paddlenlp import Taskflow
schema = ['时间', '出发地', '目的地', '费用']
few_ie = Taskflow('information_extraction', schema=schema, task_path='./checkpoint/model_best')
few_ie(['10月16日高铁从杭州到上海南站车次d5414共48元',
'6月7日苏州街地铁站到海淀黄庄',
'10月22日从公司到首都机场38元过路费'])
[2022-05-17 18:01:01,416] [ INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load 'ernie-3.0-base-zh'. [2022-05-17 18:01:01,418] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/ernie-3.0-base-zh/ernie_3.0_base_zh_vocab.txt
[{'时间': [{'text': '10月16日', 'start': 0, 'end': 6, 'probability': 0.9885207726645504}], '出发地': [{'text': '杭州', 'start': 9, 'end': 11, 'probability': 0.6859755825723255}], '目的地': [{'text': '杭州', 'start': 9, 'end': 11, 'probability': 0.3147510209338975}], '费用': [{'text': '48元', 'start': 24, 'end': 27, 'probability': 0.9642829208266122}]}, {'时间': [{'text': '6月7日', 'start': 0, 'end': 4, 'probability': 0.9898184582365275}]}, {'时间': [{'text': '10月22日', 'start': 0, 'end': 6, 'probability': 0.9809597519680544}], '出发地': [{'text': '首都机场', 'start': 10, 'end': 14, 'probability': 0.31341815956524144}], '目的地': [{'text': '首都机场', 'start': 10, 'end': 14, 'probability': 0.3607466193800164}], '费用': [{'text': '38元', 'start': 14, 'end': 17, 'probability': 0.9444483717023218}]}]
相信大家已经掌握了如何使用UIE技术完成信息抽取任务,快快动手体验吧!
如有疑问,可加入PaddleNLP的技术交流群(微信),一起交流NLP技术!入群有PaddleNLP官方直播课链接,以及10G重磅NLP学习大礼包哦!
本项目基于PaddleNLP。