基于PaddleNLP信息抽取,uie微调打造自己专属的信息抽取模型

 

上一篇<<基于PaddleOCR + NLP实现证件识别>> 用PaddleOCR实现OCR文字识别,再通过PaddleNLP实现文字提取关键信息

#关键代码
schema = ['姓名', '毕业院校', '职位', '月收入', '身体状况']
ie = Taskflow('information_extraction', schema=schema)
ie('你需要信息提取的文章字段')
 

在这里插入图片描述

UIE模型简介

UIE(Universal Information Extraction):Yaojie Lu等人在ACL-2022中提出了通用信息抽取统一框架UIE。该框架实现了实体抽取、关系抽取、事件抽取、情感分析等任务的统一建模,并使得不同任务间具备良好的迁移和泛化能力。为了方便大家使用UIE的强大能力,PaddleNLP借鉴该论文的方法,基于ERNIE 3.0知识增强预训练模型,训练并开源了首个中文通用信息抽取模型UIE。该模型可以支持不限定行业领域和抽取目标的关键信息抽取,实现零样本快速冷启动,并具备优秀的小样本微调能力,快速适配特定的抽取目标

在这里插入图片描述

UIE优势

  • 使用简单:用户可以使用自然语言自定义抽取目标,无需训练即可统一抽取输入文本中的对应信息。实现开箱即用,并满足各类信息抽取需求。

  • 降本增效:以往的信息抽取技术需要大量标注数据才能保证信息抽取的效果,为了提高开发过程中的开发效率,减少不必要的重复工作时间,开放域信息抽取可以实现零样本(zero-shot)或者少样本(few-shot)抽取,大幅度降低标注数据依赖,在降低成本的同时,还提升了效果。

  • 效果领先:开放域信息抽取在多种场景,多种任务上,均有不俗的表现。

应用示例

UIE不限定行业领域和抽取目标,以下是一些零样本行业示例

  • 医疗场景-专病结构化
    在这里插入图片描述
  • 法律场景-判决书抽取
    在这里插入图片描述
  • 金融场景-收入证明、招股书抽取
    在这里插入图片描述
  • 公安场景-事故报告抽取
    在这里插入图片描述
  • 旅游场景-宣传册、手册抽取
    在这里插入图片描述

UIE开箱即用

paddlenlp.Taskflow提供通用信息抽取、评价观点抽取等能力,可抽取多种类型的信息,包括但不限于命名实体识别(如人名、地名、机构名等)、关系(如电影的导演、歌曲的发行时间等)、事件(如某路口发生车祸、某地发生地震等)、以及评价维度、观点词、情感倾向等信息。用户可以使用自然语言自定义抽取目标,无需训练即可统一抽取输入文本中的对应信息。实现开箱即用,并满足各类信息抽取需求

UIE适用抽取示例

命名实体识别(Named Entity Recognition,简称NER)

例如抽取的目标实体类型是"时间"、“选手"和"赛事名称”, schema构造如下

>>> from pprint import pprint
>>> from paddlenlp import Taskflow

>>> schema = ['时间', '选手', '赛事名称'] # Define the schema for entity extraction
>>> ie = Taskflow('information_extraction', schema=schema)
>>> pprint(ie("2月8日上午北京冬奥会自由式滑雪女子大跳台决赛中中国选手谷爱凌以188.25分获得金牌!")) # Better print results using pprint
[{'时间': [{'end': 6,
          'probability': 0.9857378532924486,
          'start': 0,
          'text': '2月8日上午'}],
  '赛事名称': [{'end': 23,
            'probability': 0.8503089953268272,
            'start': 6,
            'text': '北京冬奥会自由式滑雪女子大跳台决赛'}],
  '选手': [{'end': 31,
          'probability': 0.8981548639781138,
          'start': 28,
          'text': '谷爱凌'}]}]

在上例中我们已经实例化了一个Taskflow对象,这里可以通过set_schema方法重置抽取目标。


>>> schema = ['肿瘤的大小', '肿瘤的个数', '肝癌级别', '脉管内癌栓分级']
>>> ie.set_schema(schema)
>>> pprint(ie("(右肝肿瘤)肝细胞性肝癌(II-III级,梁索型和假腺管型),肿瘤包膜不完整,紧邻肝被膜,侵及周围肝组织,未见脉管内癌栓(MVI分级:M0级)及卫星子灶形成。(肿物1个,大小4.2×4.0×2.8cm)。"))
[{'肝癌级别': [{'end': 20,
            'probability': 0.9243267447402701,
            'start': 13,
            'text': 'II-III级'}],
  '肿瘤的个数': [{'end': 84,
            'probability': 0.7538413804059623,
            'start': 82,
            'text': '1个'}],
  '肿瘤的大小': [{'end': 100,
            'probability': 0.8341128043459491,
            'start': 87,
            'text': '4.2×4.0×2.8cm'}],
  '脉管内癌栓分级': [{'end': 70,
              'probability': 0.9083292325934664,
              'start': 67,
              'text': 'M0级'}]}]
 
 

关系抽取(Relation Extraction,简称RE)

关系抽取是指从文本中识别实体并抽取实体之间的语义关系,进而获取三元组信息,即<主体,谓语,客体>。

  • 例如以"竞赛名称"作为抽取主体,抽取关系类型为"主办方"、“承办方"和"已举办次数”, schema构造如下:
{
  '竞赛名称': [
    '主办方',
    '承办方',
    '已举办次数'
  ]
}

>>> schema = {'竞赛名称': ['主办方', '承办方', '已举办次数']} # Define the schema for relation extraction
>>> ie.set_schema(schema) # Reset schema
>>> pprint(ie('2022语言与智能技术竞赛由中国中文信息学会和中国计算机学会联合主办,百度公司、中国中文信息学会评测工作委员会和中国计算机学会自然语言处理专委会承办,已连续举办4届,成为全球最热门的中文NLP赛事之一。'))
[{'竞赛名称': [{'end': 13,
            'probability': 0.7825402622754041,
            'relations': {'主办方': [{'end': 22,
                                  'probability': 0.8421710521379353,
                                  'start': 14,
                                  'text': '中国中文信息学会'},
                                  {'end': 30,
                                  'probability': 0.7580801847701935,
                                  'start': 23,
                                  'text': '中国计算机学会'}],
                          '已举办次数': [{'end': 82,
                                    'probability': 0.4671295049136148,
                                    'start': 80,
                                    'text': '4届'}],
                          '承办方': [{'end': 39,
                                  'probability': 0.8292706618236352,
                                  'start': 35,
                                  'text': '百度公司'},
                                  {'end': 72,
                                  'probability': 0.6193477885474685,
                                  'start': 56,
                                  'text': '中国计算机学会自然语言处理专委会'},
                                  {'end': 55,
                                  'probability': 0.7000497331473241,
                                  'start': 40,
                                  'text': '中国中文信息学会评测工作委员会'}]},
            'start': 0,
            'text': '2022语言与智能技术竞赛'}]}]
 
 

事件抽取 (Event Extraction, 简称EE)

事件抽取 ,是指从自然语言文本中抽取预定义的事件触发词(Trigger)和事件论元(Argument),组合为相应的事件结构化信息。

  • 例如抽取的目标是"地震"事件的"地震强度"、“时间”、"震中位置"和"震源深度"这些信息,schema构造如下
{
  '地震触发词': [
    '地震强度',
    '时间',
    '震中位置',
    '震源深度'
  ]
}

#触发词的格式统一为`触发词`或``XX触发词`,`XX`表示具体事件类型,上例中的事件类型是`地震`,则对应触发词为`地震触发词`。

>>> schema = {'地震触发词': ['地震强度', '时间', '震中位置', '震源深度']} # Define the schema for event extraction
>>> ie.set_schema(schema) # Reset schema
>>> ie('中国地震台网正式测定:5月16日06时08分在云南临沧市凤庆县(北纬24.34度,东经99.98度)发生3.5级地震,震源深度10千米。')
[{'地震触发词': [{'text': '地震', 'start': 56, 'end': 58, 'probability': 0.9987181623528585, 'relations': {'地震强度': [{'text': '3.5级', 'start': 52, 'end': 56, 'probability': 0.9962985320905915}], '时间': [{'text': '5月16日06时08分', 'start': 11, 'end': 22, 'probability': 0.9882578028575182}], '震中位置': [{'text': '云南临沧市凤庆县(北纬24.34度,东经99.98度)', 'start': 23, 'end': 50, 'probability': 0.8551415716584501}], '震源深度': [{'text': '10千米', 'start': 63, 'end': 67, 'probability': 0.999158304648045}]}}]}]
 
 

评论观点抽取

评论观点抽取,是指抽取文本中包含的评价维度、观点词。
例如抽取的目标是文本中包含的评价维度及其对应的观点词和情感倾向,schema构造如下:

{
  '评价维度': [
    '观点词',
    '情感倾向[正向,负向]'
  ]
}

>>> schema = {'评价维度': ['观点词', '情感倾向[正向,负向]']} # Define the schema for opinion extraction
>>> ie.set_schema(schema) # Reset schema
>>> pprint(ie("店面干净,很清静,服务员服务热情,性价比很高,发现收银台有排队")) # Better print results using pprint
[{'评价维度': [{'end': 20,
            'probability': 0.9817040258681473,
            'relations': {'情感倾向[正向,负向]': [{'probability': 0.9966142505350533,
                                          'text': '正向'}],
                          '观点词': [{'end': 22,
                                  'probability': 0.957396472711558,
                                  'start': 21,
                                  'text': '高'}]},
            'start': 17,
            'text': '性价比'},
          {'end': 2,
            'probability': 0.9696849569741168,
            'relations': {'情感倾向[正向,负向]': [{'probability': 0.9982153274927796,
                                          'text': '正向'}],
                          '观点词': [{'end': 4,
                                  'probability': 0.9945318044652538,
                                  'start': 2,
                                  'text': '干净'}]},
            'start': 0,
            'text': '店面'}]}]
 
 

情感倾向分类

句子级情感倾向分类,即判断句子的情感倾向是“正向”还是“负向”,schema构造如下:

>>> schema = '情感倾向[正向,负向]' # Define the schema for sentence-level sentiment classification
>>> ie.set_schema(schema) # Reset schema
>>> ie('这个产品用起来真的很流畅,我非常喜欢')
[{'情感倾向[正向,负向]': [{'text': '正向', 'probability': 0.9988661643929895}]}]
 

跨任务抽取

[
  "法院",
  {
      "原告": "委托代理人"
  },
  {
      "被告": "委托代理人"
  }
]


>>> schema = ['法院', {'原告': '委托代理人'}, {'被告': '委托代理人'}]
>>> ie.set_schema(schema)
>>> pprint(ie("北京市海淀区人民法院\n民事判决书\n(199x)建初字第xxx号\n原告:张三。\n委托代理人李四,北京市 A律师事务所律师。\n被告:B公司,法定代表人王五,开发公司总经理。\n委托代理人赵六,北京市 C律师事务所律师。")) # Better print results using pprint
[{'原告': [{'end': 37,
          'probability': 0.9949814024296764,
          'relations': {'委托代理人': [{'end': 46,
                                  'probability': 0.7956844697990384,
                                  'start': 44,
                                  'text': '李四'}]},
          'start': 35,
          'text': '张三'}],
  '法院': [{'end': 10,
          'probability': 0.9221074192336651,
          'start': 0,
          'text': '北京市海淀区人民法院'}],
  '被告': [{'end': 67,
          'probability': 0.8437349536631089,
          'relations': {'委托代理人': [{'end': 92,
                                  'probability': 0.7267121388225029,
                                  'start': 90,
                                  'text': '赵六'}]},
          'start': 64,
          'text': 'B公司'}]}]
 

模型选择

在这里插入图片描述

>>> from paddlenlp import Taskflow

>>> ie = Taskflow('information_extraction',
                  schema="",
                  schema_lang="zh",
                  batch_size=1,
                  model='uie-base',
                  position_prob=0.5,
                  precision='fp32',
                  use_fast=False)
  • schema:定义任务抽取目标,可参考开箱即用中不同任务的调用示例进行配置。
  • schema_lang:设置schema的语言,默认为zh, 可选有zhen。因为中英schema的构造有所不同,因此需要指定schema的语言。该参数只对uie-m-baseuie-m-large模型有效。
  • batch_size:批处理大小,请结合机器情况进行调整,默认为1。
  • model:选择任务使用的模型,默认为uie-base,可选有uie-baseuie-mediumuie-miniuie-microuie-nanouie-medical-baseuie-base-en
  • position_prob:模型对于span的起始位置/终止位置的结果概率在0~1之间,返回结果去掉小于这个阈值的结果,默认为0.5,span的最终概率输出为起始位置概率和终止位置概率的乘积。
  • precision:选择模型精度,默认为fp32,可选有fp16fp32fp16推理速度更快,支持GPU和NPU硬件环境。如果选择fp16,在GPU硬件环境下,请先确保机器正确安装NVIDIA相关驱动和基础软件,确保CUDA>=11.2,cuDNN>=8.1.1,初次使用需按照提示安装相关依赖。其次,需要确保GPU设备的CUDA计算能力(CUDA Compute Capability)大于7.0,典型的设备包括V100、T4、A10、A100、GTX 20系列和30系列显卡等。更多关于CUDA Compute Capability和精度支持情况请参考NVIDIA文档:GPU硬件与支持精度对照表
  • use_fast: 使用C++实现的高性能分词算子FastTokenizer进行文本预处理加速。需要通过pip install fast-tokenizer-python安装FastTokenizer库后方可使用。默认为False。更多使用说明可参考FastTokenizer文档

UIE定制训练

对于简单的抽取目标可以直接使用paddlenlp.Taskflow实现零样本(zero-shot)抽取,对于细分场景我们推荐使用轻定制功能(标注少量数据进行模型微调)以进一步提升效果。下面通过来往内地通行证(非中国籍)的例子展示如何通过5条训练数据进行UIE模型微调
在这里插入图片描述

代码结构:

paddleNLP x2.6 uie微调官方文档

├── utils.py          # 数据处理工具
├── model.py          # 模型组网脚本
├── doccano.py        # 数据标注脚本
├── doccano.md        # 数据标注文档
├── finetune.py       # 模型微调、压缩脚本
├── evaluate.py       # 模型评估脚本
└── README.md
 

数据标注

我们推荐使用数据标注平台doccano(doccano github社区)进行数据标注,本示例也打通了从标注到训练的通道,即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,保持窗口
doccano webserver --port 8000
#在另一个窗口启动doccano的任务队列
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从文件导入。
最上边Span表示实体标签,Relation表示关系标签,需要分别设置。
导入数据。在Datasets一栏点击Actions、Import Dataset从文件导入文本数据。
~根据文件格式(File format)给出的示例,选择适合的格式导入自定义数据文件。TextLine每一行为一条数据,Text是一个文件一个数据
导入成功后即跳转至数据列表。
导入文件:gatText.txt
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

~标注数据。点击每条数据最右边的Annotate按钮开始标记。标记页面右侧的标签类型(Label Types)开关可在实体标签和关系标签之间切换。
在这里插入图片描述
~实体标注:直接用鼠标选取文本即可标注实体。
在这里插入图片描述
~关系标注:首先点击待标注的关系标签,接着依次点击相应的头尾实体可完成关系标注。
~导出数据: 在Datasets一栏点击Actions、Export Dataset导出已标注的数据。
导出dev_doccaon_ext.jsonl文件 文件名称改为json doccano_ext.json

在这里插入图片描述

将标注数据转化成UIE训练所需数据

将doccano平台的标注数据保存在./data/目录。对于快递单信息抽取的场景,可以直接下载标注好的数据。

python doccano.py --doccano_file ./data/doccano_ext.json --splits 1 0 0

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.

执行成功后会在data文件夹生成3个文件

dev.txt,test.txt,train.txt
 

在这里插入图片描述

在这里插入图片描述
进行数据微调:

python finetune.py  --device cpu --logging_steps 100 --save_steps 100 --eval_steps 100 --seed 42 --model_name_or_path uie-base --output_dir ./checkpoint/model_best --train_path ./data/train.txt --dev_path ./data/dev.txt  --max_seq_length 512  --per_device_eval_batch_size 100 --per_device_train_batch_size  100 --num_train_epochs 100 --learning_rate 1e-5 --label_names "start_positions" "end_positions" --do_train --do_eval --do_export --export_model_dir ./checkpoint/model_best --overwrite_output_dir --disable_tqdm True --metric_for_best_model eval_f1 --load_best_model_at_end  True --save_total_limit 1
 

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

在这里插入图片描述save_dir: 模型存储路径,默认为./checkpoint。
在这里插入图片描述

调用方式:

 schema = ["英文名", "中文名", "出生日期", "性别", "国籍", "证件号码"]
    ie = Taskflow('information_extraction', schema=schema, task_path='./checkpoint/model_best')
    res = ie(allStr)

效果还是可以的,目前英文名和证件号码提取还需要在调整下。
在这里插入图片描述

最终效果:
在这里插入图片描述
很完美!!!

记录下过程中bug处理思路

问题1:提示该问题是转换dcocan文件,3个文件中数据为空的问题,需要检查下生成文件dev.txt/test.txt/train.txt是否没有数据问题。在执行finetune会指定训练文件这个要注意。这个还是灵感一闪找出问题所在
问题2:如果你下载的转换代码不是对应paddleNLP版本的就会提示这个。因为不匹配的版本会生成uie-base你删掉他重新执行就可以了

【已解决】问题1:ValueError: num_samples should be a positive integer value, but got num_samples=0
【已解决】问题2:ValueError: The state dictionary of the model you are trying to load is corr
 

辅助文档
doccano部署文档:https://github.com/doccano/doccano

需要更多的模型工具使用示例,部署问题解决方案同学请移步谢谢支持!!!!
回复 paddle-demo
在这里插入图片描述

posted @ 2024-08-13 16:19  去你的鸟命  阅读(30)  评论(0编辑  收藏  举报