教程:基于 UIE 的命名实体识别
本教程基于 PaddleNLP 的 Universal Information Extraction 进行命名实体识别任务,并展示通过标注少量数据进行微调获取模型效果快速提升。
完整的 Jupyter 笔记本、代码、标注数据在 https://openbayes.com/console/open-tutorials/containers/lWyxi1DwhJU 都可以找得到。
导入依赖库
from pprint import pprint
from paddlenlp import Taskflow
使用 uie-base 进行命名实体识别
首先直接使用预训练模型 uie-base 进行命名实体识别,不做任何的调优看看效果。
schema = [
'地名',
'人名',
'组织',
'时间',
'产品',
'价格',
'天气'
]
ie = Taskflow('information_extraction', schema=schema)
pprint(ie("2K 与 Gearbox Software 宣布,《小缇娜的奇幻之地》将于 6 月 24 日凌晨 1 点登录 Steam,此前 PC 平台为 Epic 限时独占。在限定期间内,Steam 玩家可以在 Steam 入手《小缇娜的奇幻之地》,并在 2022 年 7 月 8 日前享有获得黄金英雄铠甲包。"))
[{'产品': [{'end': 35,
'probability': 0.8594067882980987,
'start': 25,
'text': '《小缇娜的奇幻之地》'}],
'地名': [{'end': 117,
'probability': 0.5248250992968906,
'start': 109,
'text': '小缇娜的奇幻之地'},
{'end': 34,
'probability': 0.3007929716932729,
'start': 26,
'text': '小缇娜的奇幻之地'}],
'时间': [{'end': 52,
'probability': 0.87968346213556,
'start': 38,
'text': '6 月 24 日凌晨 1 点'}],
'组织': [{'end': 93,
'probability': 0.5977969768231866,
'start': 88,
'text': 'Steam'},
{'end': 2,
'probability': 0.6914769673274321,
'start': 0,
'text': '2K'},
{'end': 75,
'probability': 0.5848915911412256,
'start': 71,
'text': 'Epic'},
{'end': 60,
'probability': 0.5682100157587833,
'start': 55,
'text': 'Steam'},
{'end': 21,
'probability': 0.679590305138845,
'start': 5,
'text': 'Gearbox Software'},
{'end': 105,
'probability': 0.4573145431744834,
'start': 100,
'text': 'Steam'}]}]
pprint(ie("近日,量子计算专家、ACM 计算奖得主 Scott Aaronson 通过博客宣布,将于本周离开得克萨斯大学奥斯汀分校 (UT Austin) 一年,并加盟人工智能研究公司 OpenAI。"))
[{'人名': [{'end': 32,
'probability': 0.4801083732026494,
'start': 24,
'text': 'Aaronson'},
{'end': 23,
'probability': 0.6648137293130958,
'start': 18,
'text': 'Scott'}],
'时间': [{'end': 43,
'probability': 0.8425767345737043,
'start': 41,
'text': '本周'}],
'组织': [{'end': 87,
'probability': 0.5554367836811132,
'start': 81,
'text': 'OpenAI'}]}]
使用默认模型 uie-base 进行命名实体识别,效果还不错,大多数的命名实体被识别出来了,但依然存在部分实体未被识别出,部分文本被误识别等问题。比如 "Scott Aaronson" 被识别为了两个人名,比如 "得克萨斯大学奥斯汀分校" 没有被识别出来。
为提升识别效果,本教程尝试通过标注少量数据对模型进行微调。
数据标注
本教程使用数据标注平台 Label Studio 进行数据标注。所有的工作都是在一个开启了的 「OpenBayes 工作空间」 中完成的。
启动 Label Studio
如上所示,在 Jupyter 中打开终端并在终端中执行 openbayes-label-studio
即可在 OpenBayes Jupyter Workspace 中使用 LabelStudio 了。然后使用如下所示的命令行中生成的 url 启动 Label Studio:
在浏览器中打开链接,注册账户并登录,即可开始使用。
对于不同的 OpenBayes 算力容器,红框中的外部访问链接各不相同,直接使用本教程中的链接是无效的,需用终端中提示的链接进行替换。
标注数据
具体步骤如下:
- 创建项目。
- 导入数据。本教程使用的数据已上传到此算力容器,即
corpus.txt
。 - 配置标签界面。在 Natural Language Processing 中选择 Named Entity Recognition 模板,根据需要添加或修改标签。本教程中需要定义的实体标签有『地名』『人名』『组织』『时间』『产品』『价格』『天气』。
- 开始标注数据。
- 导出数据。标注完成后,从 label studio 导出 JSON 格式的结果文件。此算力容器中已有预先标注好的文件
label-studio.json
。
如果你懒得自己标注也没关系,这个教程里的 label-studio.json
就是已经标注好并导出的结果了。
模型微调
以下模型微调所需脚本已上传到此算力容器。
数据转换
在终端中执行以下脚本,将 label studio 导出的数据文件格式转换成 doccano 导出的数据文件格式。
python labelstudio2doccano.py --labelstudio_file label-studio.json
参数说明:
labelstudio_file
: label studio 的导出文件路径(仅支持 JSON 格式)。doccano_file
: doccano 格式的数据文件保存路径,默认为 "doccano_ext.jsonl"。task_type
: 任务类型,可选有抽取("ext")和分类("cls")两种类型的任务,默认为 "ext"。
PaddleNLP 默认并不提供将 labelstudio 的标注格式转换为其所支持的格式的工具,我们这里提供了一个 labelstudio2doccano.py
的脚本。
然后在终端中执行以下脚本,对 doccano 格式的数据文件进行处理,执行后会在 /home/data 目录下生成训练/验证/测试集文件。
python doccano.py \
--doccano_file ./doccano_ext.jsonl \
--task_type "ext" \
--save_dir ./data \
--splits 0.7 0.2 0.1
参数说明:
doccano_file
: doccano 格式的数据标注文件路径。task_type
: 选择任务类型,可选有抽取("ext")和分类("cls")两种类型的任务。save_dir
: 训练数据的保存目录,默认存储在 data 目录下。negative_ratio
: 最大负例比例,该参数只对抽取类型任务有效,适当构造负例可提升模型效果。负例数量和实际的标签数量有关,最大负例数量 = negative_ratio * 正例数量。该参数只对训练集有效,默认为 5。为了保证评估指标的准确性,验证集和测试集默认构造全负例。splits
: 划分数据集时训练集、验证集、测试集所占的比例。默认为 [0.8, 0.1, 0.1] 。options
: 指定分类任务的类别标签,该参数只对分类类型任务有效。默认为 ["正向", "负向"]。prompt_prefix
: 声明分类任务的 prompt 前缀信息,该参数只对分类类型任务有效。默认为 "情感倾向"。is_shuffle
: 是否对数据集进行随机打散,默认为 True。seed
: 随机种子,默认为 1000。separator
: 实体类别/评价维度与分类标签的分隔符,该参数只对实体/评价维度级分类任务有效。默认为 "##"。
每次执行 doccano.py 脚本,将会覆盖已有的同名数据文件。
Finetune
在终端中执行以下脚本进行模型微调。
python finetune.py \
--train_path "./data/train.txt" \
--dev_path "./data/dev.txt" \
--save_dir "./checkpoint" \
--learning_rate 1e-5 \
--batch_size 4 \
--max_seq_len 512 \
--num_epochs 50 \
--model "uie-base" \
--seed 1000 \
--logging_steps 10 \
--valid_steps 100 \
--device "gpu"
参数说明:
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-medium", "uie-mini", "uie-micro" 和 "uie-nano",默认为 "uie-base"。seed
: 随机种子,默认为 1000。logging_steps
: 日志打印的间隔 steps 数,默认为 10。valid_steps
: evaluate 的间隔 steps 数,默认为 100。device
: 选用什么设备进行训练,可选 "cpu" 或 "gpu"。init_from_ckpt
: 初始化模型参数的路径,可从断点处继续训练。
模型评估
在终端中执行以下脚本进行模型评估。
python evaluate.py \
--model_path ./checkpoint/model_best \
--test_path ./data/dev.txt \
--batch_size 16 \
--max_seq_len 512
输出:
[2022-07-15 03:18:19,157] [ INFO] - -----------------------------
[2022-07-15 03:18:19,157] [ INFO] - Class Name: all_classes
[2022-07-15 03:18:19,157] [ INFO] - Evaluation Precision: 0.95349 | Recall: 0.89130 | F1: 0.92135
可以看到 F1 已经达到了 0.92
,说明模型的效果较好。
参数说明:
model_path
: 进行评估的模型文件夹路径,路径下需包含模型权重文件 model_state.pdparams 及配置文件 model_config.json。test_path
: 进行评估的测试集文件。batch_size
: 批处理大小,请结合机器情况进行调整,默认为 16。max_seq_len
: 文本最大切分长度,输入超过最大长度时会对输入文本进行自动切分,默认为 512。debug
: 是否开启 debug 模式对每个正例类别分别进行评估,该模式仅用于模型调试,默认关闭。
debug
模式输出示例:
[2022-07-15 03:27:57,801] [ INFO] - -----------------------------
[2022-07-15 03:27:57,801] [ INFO] - Class Name: 组织
[2022-07-15 03:27:57,802] [ INFO] - Evaluation Precision: 1.00000 | Recall: 0.75000 | F1: 0.85714
[2022-07-15 03:27:57,913] [ INFO] - -----------------------------
[2022-07-15 03:27:57,913] [ INFO] - Class Name: 地名
[2022-07-15 03:27:57,913] [ INFO] - Evaluation Precision: 0.90476 | Recall: 0.82609 | F1: 0.86364
[2022-07-15 03:27:58,046] [ INFO] - -----------------------------
[2022-07-15 03:27:58,046] [ INFO] - Class Name: 时间
[2022-07-15 03:27:58,047] [ INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
[2022-07-15 03:27:58,098] [ INFO] - -----------------------------
[2022-07-15 03:27:58,098] [ INFO] - Class Name: 产品
[2022-07-15 03:27:58,098] [ INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
[2022-07-15 03:27:58,147] [ INFO] - -----------------------------
[2022-07-15 03:27:58,147] [ INFO] - Class Name: 价格
[2022-07-15 03:27:58,147] [ INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
[2022-07-15 03:27:58,176] [ INFO] - -----------------------------
[2022-07-15 03:27:58,176] [ INFO] - Class Name: 人名
[2022-07-15 03:27:58,177] [ INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
微调后效果
my_ie = Taskflow("information_extraction", schema=schema, task_path='./checkpoint/model_best') # task_path 指定模型权重文件的路径
pprint(my_ie("2K 与 Gearbox Software 宣布,《小缇娜的奇幻之地》将于 6 月 24 日凌晨 1 点登录 Steam,此前 PC 平台为 Epic 限时独占。在限定期间内,Steam 玩家可以在 Steam 入手《小缇娜的奇幻之地》,并在 2022 年 7 月 8 日前享有获得黄金英雄铠甲包。"))
[{'产品': [{'end': 148,
'probability': 0.9977381891196586,
'start': 141,
'text': '