★★★ 本文源自AlStudio社区精品项目,【点击此处】查看更多精品内容 >>>

  • 人工标注的缺点主要有以下几点:

    • 产能低:人工标注需要大量的人力物力投入,且标注速度慢,产能低,无法满足大规模标注的需求。
    • 受限条件多:人工标注受到人力、物力、时间等条件的限制,无法适应所有的标注场景,尤其是一些复杂的标注任务。
    • 易受主观因素影响:人工标注受到人为因素的影响,如标注人员的专业素养、标注态度、主观判断等,易受到人为误差的干扰,导致标注结果不准确。
    • 难以满足个性化需求:人工标注无法满足所有标注场景和个性化需求,无法精确地标注出所有的关键信息,需要使用者自行选择和判断。
  • 相比之下,智能标注的优势主要包括:

    • 效率更高:智能标注可以自动化地进行标注,能够快速地生成标注结果,减少了人工标注所需的时间和精力,提高了标注效率。
    • 精度更高:智能标注采用了先进的人工智能技术,能够对图像进行深度学习和处理,能够生成更加准确和精细的标注结果,特别是对于一些细节和特征的标注,手动标注往往存在误差较大的问题。
    • 自动纠错:智能标注可以自动检测标注结果中的错误,并进行自动修正,能够有效地避免标注错误带来的影响,提高了标注的准确性。
    • 灵活性更强:智能标注可以根据不同的应用场景和需求,生成不同类型的标注结果,能够满足用户的多样化需求,提高了标注的适用性。

总之,智能标注相对于人工标注有着更高的效率、更高的精度、更强的灵活性和更好的适用性,可以更好地满足用户的需求。


自然语言处理信息抽取智能标注方案包括以下几种:

  1. 基于规则的标注方案:通过编写一系列规则来识别文本中的实体、关系等信息,并将其标注。

    • 基于规则的标注方案是一种传统的方法,它需要人工编写规则来识别文本中的实体、关系等信息,并将其标注。
    • 这种方法的优点是易于理解和实现,但缺点是需要大量的人工工作,并且规则难以覆盖所有情况。
  2. 基于机器学习的标注方案:通过训练模型来自动识别文本中的实体、关系等信息,并将其标注。

    • 基于机器学习的标注方案是一种自动化的方法,它使用已经标注好的数据集训练模型,并使用模型来自动标注文本中的实体、关系等信息。
    • 这种方法的优点是可以处理大量的数据,并且可以自适应地调整模型,但缺点是需要大量的标注数据和计算资源,并且模型的性能受到标注数据的质量和数量的限制。
  3. 基于深度学习的标注方案:通过使用深度学习模型来自动识别文本中的实体、关系等信息,并将其标注。

    • 基于深度学习的标注方案是一种最新的方法,它使用深度学习模型来自动从文本中提取实体、关系等信息,并将其标注。
    • 这种方法的优点是可以处理大量的数据,并且具有较高的准确性,但缺点是需要大量的标注数据和计算资源,并且模型的训练和调试需要专业的知识和技能。
  4. 基于半监督学习的标注方案:通过使用少量的手工标注数据和大量的未标注数据来训练模型,从而实现自动标注。

    • 基于半监督学习的标注方案是一种利用少量的手工标注数据和大量的未标注数据来训练模型的方法。
    • 这种方法的优点是可以利用未标注数据来提高模型的性能,但缺点是需要大量的未标注数据和计算资源,并且模型的性能受到标注数据的质量
  5. 基于远程监督的标注方案:利用已知的知识库来自动标注文本中的实体、关系等信息,从而减少手工标注的工作量。

本次项目主要讲解的是基于半监督深度学习的标注方案。

1.UIE-base预训练模型进行命名实体识别

from pprint import pprint
from paddlenlp import Taskflow

schema = ['地名', '人名', '组织', '时间', '产品', '价格', '天气']
ie = Taskflow('information_extraction', schema=schema)

pprint(ie("2K 与 Gearbox Software 宣布,《小缇娜的奇幻之地》将于 6 月 24 日凌晨 1 点登录 Steam,此前 PC 平台为 Epic 限时独占。在限定期间内,Steam 玩家可以在 Steam 入手《小缇娜的奇幻之地》,并在 2022 年 7 月 8 日前享有获得黄金英雄铠甲包。"))
[2023-03-27 16:11:00,527] [    INFO] - Downloading model_state.pdparams from https://bj.bcebos.com/paddlenlp/taskflow/information_extraction/uie_base_v1.0/model_state.pdparams
100%|██████████| 450M/450M [00:45<00:00, 10.4MB/s] 
[2023-03-27 16:11:46,996] [    INFO] - Downloading model_config.json from https://bj.bcebos.com/paddlenlp/taskflow/information_extraction/uie_base/model_config.json
100%|██████████| 377/377 [00:00<00:00, 309kB/s]
[2023-03-27 16:11:47,074] [    INFO] - Downloading vocab.txt from https://bj.bcebos.com/paddlenlp/taskflow/information_extraction/uie_base/vocab.txt
100%|██████████| 182k/182k [00:00<00:00, 1.27MB/s]
[2023-03-27 16:11:47,292] [    INFO] - Downloading special_tokens_map.json from https://bj.bcebos.com/paddlenlp/taskflow/information_extraction/uie_base/special_tokens_map.json
100%|██████████| 112/112 [00:00<00:00, 99.6kB/s]
[2023-03-27 16:11:47,364] [    INFO] - Downloading tokenizer_config.json from https://bj.bcebos.com/paddlenlp/taskflow/information_extraction/uie_base/tokenizer_config.json
100%|██████████| 172/172 [00:00<00:00, 192kB/s]
W0327 16:11:47.478449   273 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 11.2, Runtime API Version: 11.2
W0327 16:11:47.481654   273 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.
[2023-03-27 16:11:50,518] [    INFO] - Converting to the inference model cost a little time.
[2023-03-27 16:11:57,379] [    INFO] - The inference model save in the path:/home/aistudio/.paddlenlp/taskflow/information_extraction/uie-base/static/inference
[2023-03-27 16:11:59,489] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load '/home/aistudio/.paddlenlp/taskflow/information_extraction/uie-base'.


[{'产品': [{'end': 35,
          'probability': 0.8595664902550801,
          'start': 25,
          'text': '《小缇娜的奇幻之地》'}],
  '地名': [{'end': 34,
          'probability': 0.30077351606695757,
          'start': 26,
          'text': '小缇娜的奇幻之地'},
         {'end': 117,
          'probability': 0.5250433327469182,
          'start': 109,
          'text': '小缇娜的奇幻之地'}],
  '时间': [{'end': 52,
          'probability': 0.8796518890642702,
          'start': 38,
          'text': '6 月 24 日凌晨 1 点'}],
  '组织': [{'end': 2,
          'probability': 0.6914450625760651,
          'start': 0,
          'text': '2K'},
         {'end': 93,
          'probability': 0.5971815528872604,
          'start': 88,
          'text': 'Steam'},
         {'end': 75,
          'probability': 0.5844303540013343,
          'start': 71,
          'text': 'Epic'},
         {'end': 105,
          'probability': 0.45620707081511114,
          'start': 100,
          'text': 'Steam'},
         {'end': 60,
          'probability': 0.5683007420326334,
          'start': 55,
          'text': 'Steam'},
         {'end': 21,
          'probability': 0.6797917390407271,
          'start': 5,
          'text': 'Gearbox Software'}]}]
pprint(ie("近日,量子计算专家、ACM计算奖得主Scott Aaronson通过博客宣布,将于本周离开得克萨斯大学奥斯汀分校(UT Austin)一年,并加盟人工智能研究公司OpenAI。"))
[{'人名': [{'end': 23,
          'probability': 0.664236391748247,
          'start': 18,
          'text': 'Scott'},
         {'end': 32,
          'probability': 0.479811241610971,
          'start': 24,
          'text': 'Aaronson'}],
  '时间': [{'end': 43,
          'probability': 0.8424644728072508,
          'start': 41,
          'text': '本周'}],
  '组织': [{'end': 87,
          'probability': 0.5550909248934985,
          'start': 81,
          'text': 'OpenAI'}]}]

使用默认模型 uie-base 进行命名实体识别,效果还不错,大多数的命名实体被识别出来了,但依然存在部分实体未被识别出,部分文本被误识别等问题。比如 “Scott Aaronson” 被识别为了两个人名,比如 “得克萨斯大学奥斯汀分校” 没有被识别出来。为提升识别效果,将通过标注少量数据对模型进行微调。

2.基于Label Studio的数据标注

在将智能标注前,先讲解手动标注,通过手动标注后才会感知到智能标注的提效和交互性。

由于AI studio不支持在线标注,这里大家在本地端进行标注,标注完毕后上传数据集即可

2.1 Label Studio安装

以下标注示例用到的环境配置:

  • Python 3.8+
  • label-studio == 1.7.1
  • paddleocr >= 2.6.0.1

在终端(terminal)使用pip安装label-studio:

pip install label-studio==1.7.1

安装完成后,运行以下命令行:

label-studio start

在浏览器打开http://localhost:8080/,输入用户名和密码登录,开始使用label-studio进行标注。

2.2 实体抽取任务标注

  • 项目创建
    点击创建(Create)开始创建一个新的项目,填写项目名称、描述,然后选择Object Detection with Bounding Boxes。

填写项目名称、描述

  • 命名实体识别任务选择

  • 添加标签(也可跳过后续在Setting/Labeling Interface中配置)

  • 数据上传
    先从本地上传txt格式文件,选择List of tasks,然后选择导入本项目。

  • 实体抽取标注

  • 数据导出
    勾选已标注文本ID,选择导出的文件类型为JSON,导出数据:

3. 模型微调

3.1 数据转换

在终端中执行以下脚本,将 label studio 导出的数据文件格式转换成 doccano 导出的数据文件格式。

python labelstudio2doccano.py --labelstudio_file dataset/label-studio.json

参数说明:

  • labelstudio_file: label studio 的导出文件路径(仅支持 JSON 格式)。
  • doccano_file: doccano 格式的数据文件保存路径,默认为 “doccano_ext.jsonl”。
  • task_type: 任务类型,可选有抽取(“ext”)和分类(“cls”)两种类型的任务,默认为 “ext”。
!python doccano.py \
    --doccano_file dataset/doccano_ext.jsonl \
    --task_type "ext" \
    --save_dir ./data \
    --splits 0.8 0.2 0
[2023-03-27 16:43:33,438] [    INFO] - Converting doccano data...
100%|████████████████████████████████████████| 40/40 [00:00<00:00, 29794.38it/s]
[2023-03-27 16:43:33,440] [    INFO] - Adding negative samples for first stage prompt...
100%|███████████████████████████████████████| 40/40 [00:00<00:00, 118650.75it/s]
[2023-03-27 16:43:33,441] [    INFO] - Converting doccano data...
100%|████████████████████████████████████████| 10/10 [00:00<00:00, 38095.40it/s]
[2023-03-27 16:43:33,442] [    INFO] - Adding negative samples for first stage prompt...
100%|███████████████████████████████████████| 10/10 [00:00<00:00, 130257.89it/s]
[2023-03-27 16:43:33,442] [    INFO] - Converting doccano data...
0it [00:00, ?it/s]
[2023-03-27 16:43:33,442] [    INFO] - Adding negative samples for first stage prompt...
0it [00:00, ?it/s]
[2023-03-27 16:43:33,444] [    INFO] - Save 274 examples to ./data/train.txt.
[2023-03-27 16:43:33,445] [    INFO] - Save 70 examples to ./data/dev.txt.
[2023-03-27 16:43:33,445] [    INFO] - Save 0 examples to ./data/test.txt.
[2023-03-27 16:43:33,445] [    INFO] - Finished! It takes 0.01 seconds

参数说明:

  • 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 脚本,将会覆盖已有的同名数据文件。

3.2 Finetune

在终端中执行以下脚本进行模型微调。

# 然后在终端中执行以下脚本,对 doccano 格式的数据文件进行处理,执行后会在 /home/data 目录下生成训练/验证/测试集文件。
!python finetune.py \
    --train_path "./data/train.txt" \
    --dev_path "./data/dev.txt" \
    --save_dir "./checkpoint" \
    --learning_rate 1e-5 \
    --batch_size 32 \
    --max_seq_len 512 \
    --num_epochs 100 \
    --model "uie-base" \
    --seed 1000 \
    --logging_steps 100 \
    --valid_steps 100 \
    --device "gpu"
[2023-03-27 16:47:58,806] [    INFO] - Downloading resource files...
[2023-03-27 16:47:58,810] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load 'uie-base'.
W0327 16:47:58.836591 13399 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 11.2, Runtime API Version: 11.2
W0327 16:47:58.839186 13399 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.
[2023-03-27 16:48:30,349] [    INFO] - global step 100, epoch: 12, loss: 0.00060, speed: 3.46 step/s
[2023-03-27 16:48:30,794] [    INFO] - Evaluation precision: 0.93878, recall: 0.85185, F1: 0.89320
[2023-03-27 16:48:30,794] [    INFO] - best F1 performence has been updated: 0.00000 --> 0.89320
[2023-03-27 16:48:58,054] [    INFO] - global step 200, epoch: 23, loss: 0.00032, speed: 3.82 step/s
[2023-03-27 16:48:58,500] [    INFO] - Evaluation precision: 0.95918, recall: 0.87037, F1: 0.91262
[2023-03-27 16:48:58,500] [    INFO] - best F1 performence has been updated: 0.89320 --> 0.91262
[2023-03-27 16:49:25,664] [    INFO] - global step 300, epoch: 34, loss: 0.00022, speed: 3.83 step/s
[2023-03-27 16:49:26,107] [    INFO] - Evaluation precision: 0.90385, recall: 0.87037, F1: 0.88679
[2023-03-27 16:49:52,155] [    INFO] - global step 400, epoch: 45, loss: 0.00017, speed: 3.84 step/s
[2023-03-27 16:49:52,601] [    INFO] - Evaluation precision: 0.93878, recall: 0.85185, F1: 0.89320
[2023-03-27 16:50:18,632] [    INFO] - global step 500, epoch: 56, loss: 0.00014, speed: 3.84 step/s
[2023-03-27 16:50:19,075] [    INFO] - Evaluation precision: 0.92157, recall: 0.87037, F1: 0.89524
[2023-03-27 16:50:45,077] [    INFO] - global step 600, epoch: 67, loss: 0.00012, speed: 3.85 step/s
[2023-03-27 16:50:45,523] [    INFO] - Evaluation precision: 0.93478, recall: 0.79630, F1: 0.86000
[2023-03-27 16:51:11,546] [    INFO] - global step 700, epoch: 78, loss: 0.00010, speed: 3.84 step/s
[2023-03-27 16:51:11,987] [    INFO] - Evaluation precision: 0.93750, recall: 0.83333, F1: 0.88235
[2023-03-27 16:51:38,013] [    INFO] - global step 800, epoch: 89, loss: 0.00009, speed: 3.84 step/s
[2023-03-27 16:51:38,457] [    INFO] - Evaluation precision: 0.93617, recall: 0.81481, F1: 0.87129
[2023-03-27 16:52:04,361] [    INFO] - global step 900, epoch: 100, loss: 0.00008, speed: 3.86 step/s
[2023-03-27 16:52:04,808] [    INFO] - Evaluation precision: 0.95745, recall: 0.83333, F1: 0.89109

结果展示:

参数说明:

  • 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: 初始化模型参数的路径,可从断点处继续训练。

3.3 模型评估

在终端中执行以下脚本进行模型评估。

输出示例:

参数说明:

  • model_path: 进行评估的模型文件夹路径,路径下需包含模型权重文件 model_state.pdparams 及配置文件 model_config.json。
  • test_path: 进行评估的测试集文件。
  • batch_size: 批处理大小,请结合机器情况进行调整,默认为 16。
  • max_seq_len: 文本最大切分长度,输入超过最大长度时会对输入文本进行自动切分,默认为 512。
  • debug: 是否开启 debug 模式对每个正例类别分别进行评估,该模式仅用于模型调试,默认关闭。

debug 模式输出示例:

!python evaluate.py \
    --model_path ./checkpoint/model_best \
    --test_path ./data/dev.txt \
    --batch_size 16 \
    --max_seq_len 512
[2023-03-27 16:56:21,832] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint/model_best'.
W0327 16:56:21.863559 15278 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 11.2, Runtime API Version: 11.2
W0327 16:56:21.866312 15278 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.
[2023-03-27 16:56:27,409] [    INFO] - -----------------------------
[2023-03-27 16:56:27,409] [    INFO] - Class Name: all_classes
[2023-03-27 16:56:27,409] [    INFO] - Evaluation Precision: 0.95918 | Recall: 0.87037 | F1: 0.91262
!python evaluate.py \
    --model_path ./checkpoint/model_best \
    --test_path ./data/dev.txt \
    --debug
[2023-03-27 16:56:31,824] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint/model_best'.
W0327 16:56:31.856709 15361 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 11.2, Runtime API Version: 11.2
W0327 16:56:31.859668 15361 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.
[2023-03-27 16:56:37,039] [    INFO] - -----------------------------
[2023-03-27 16:56:37,039] [    INFO] - Class Name: 时间
[2023-03-27 16:56:37,039] [    INFO] - Evaluation Precision: 1.00000 | Recall: 0.90000 | F1: 0.94737
[2023-03-27 16:56:37,092] [    INFO] - -----------------------------
[2023-03-27 16:56:37,092] [    INFO] - Class Name: 地名
[2023-03-27 16:56:37,092] [    INFO] - Evaluation Precision: 0.95833 | Recall: 0.85185 | F1: 0.90196
[2023-03-27 16:56:37,113] [    INFO] - -----------------------------
[2023-03-27 16:56:37,113] [    INFO] - Class Name: 产品
[2023-03-27 16:56:37,113] [    INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
[2023-03-27 16:56:37,139] [    INFO] - -----------------------------
[2023-03-27 16:56:37,139] [    INFO] - Class Name: 组织
[2023-03-27 16:56:37,139] [    INFO] - Evaluation Precision: 1.00000 | Recall: 0.50000 | F1: 0.66667
[2023-03-27 16:56:37,161] [    INFO] - -----------------------------
[2023-03-27 16:56:37,161] [    INFO] - Class Name: 人名
[2023-03-27 16:56:37,161] [    INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
[2023-03-27 16:56:37,181] [    INFO] - -----------------------------
[2023-03-27 16:56:37,181] [    INFO] - Class Name: 天气
[2023-03-27 16:56:37,181] [    INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
[2023-03-27 16:56:37,198] [    INFO] - -----------------------------
[2023-03-27 16:56:37,198] [    INFO] - Class Name: 价格
[2023-03-27 16:56:37,198] [    INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000

3.4 微调后效果

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 日前享有获得黄金英雄铠甲包。"))
[2023-03-27 16:59:31,064] [    INFO] - Converting to the inference model cost a little time.
[2023-03-27 16:59:38,171] [    INFO] - The inference model save in the path:./checkpoint/model_best/static/inference
[2023-03-27 16:59:40,364] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint/model_best'.


[{'产品': [{'end': 118,
          'probability': 0.9860373472963602,
          'start': 108,
          'text': '《小缇娜的奇幻之地》'},
         {'end': 35,
          'probability': 0.9870597349192849,
          'start': 25,
          'text': '《小缇娜的奇幻之地》'},
         {'end': 148,
          'probability': 0.9075982731610566,
          'start': 141,
          'text': '黄金英雄铠甲包'}],
  '时间': [{'end': 52,
          'probability': 0.9998029564426645,
          'start': 38,
          'text': '6 月 24 日凌晨 1 点'},
         {'end': 137,
          'probability': 0.9876786236837809,
          'start': 122,
          'text': '2022 年 7 月 8 日前'}],
  '组织': [{'end': 2, 'probability': 0.988802896329716, 'start': 0, 'text': '2K'},
         {'end': 93,
          'probability': 0.9500440898664806,
          'start': 88,
          'text': 'Steam'},
         {'end': 75,
          'probability': 0.9819772965571794,
          'start': 71,
          'text': 'Epic'},
         {'end': 105,
          'probability': 0.7921079762008958,
          'start': 100,
          'text': 'Steam'},
         {'end': 60,
          'probability': 0.9829542747088276,
          'start': 55,
          'text': 'Steam'},
         {'end': 21,
          'probability': 0.9994613042455924,
          'start': 5,
          'text': 'Gearbox Software'}]}]
pprint(my_ie("近日,量子计算专家、ACM计算奖得主Scott Aaronson通过博客宣布,将于本周离开得克萨斯大学奥斯汀分校(UT Austin)一年,并加盟人工智能研究公司OpenAI。"))
[{'人名': [{'end': 32,
          'probability': 0.9990170436659866,
          'start': 18,
          'text': 'Scott Aaronson'}],
  '时间': [{'end': 2,
          'probability': 0.9998477751029782,
          'start': 0,
          'text': '近日'},
         {'end': 43,
          'probability': 0.9995671774285029,
          'start': 41,
          'text': '本周'}],
  '组织': [{'end': 66,
          'probability': 0.9900270615638647,
          'start': 57,
          'text': 'UT Austin'},
         {'end': 87,
          'probability': 0.9993388552686611,
          'start': 81,
          'text': 'OpenAI'},
         {'end': 56,
          'probability': 0.9968586409231648,
          'start': 45,
          'text': '得克萨斯大学奥斯汀分校'},
         {'end': 13,
          'probability': 0.8437228020724348,
          'start': 10,
          'text': 'ACM'}]}]

基于 50 条标注数据进行模型微调后,效果有所提升。

4.基于Label Studio的智能标注(含自动训练)

在基于UIE的命名实体识别的基础上,进一步通过集成 Label Studio 的 Machine Learning Backend 实现交互式预注释和模型训练等功能。

环境安装:

pip install label_studio_ml

pip uninstall attr

4.1 Machine Learning Backend 编写教学

完整的 Machine Learning Backend 见 my_ml_backend.py 文件。更多有关自定义机器学习后端编写的内容可参考 Write your own ML backend

简单来讲,my_ml_backend.py 内主要包含一个继承自 LabelStudioMLBase 的类,其内容可以分为以下三个主要部分:

  • init 方法,包含模型的加载和基本配置的初始化
  • predict 方法,用于为标注数据生成新的预测结果,其关键参数 tasks 就是 label studio 传递的原始数据
  • fit 方法,用于模型的训练,当点击页面上的 Train 按钮时,会调用此方法(具体的位置在下文会提到),其关键参数 annotations 就是 label studio 传递的已经标注了的数据

4.1.1 init 初始化方法

  • 导入依赖库
import numpy as np
import os  
import json
from paddlenlp import Taskflow
from label_studio_ml.model import LabelStudioMLBase
  • 声明并初始化一个类

首先创建一个类声明,通过继承 LabelStudioMLBase 创建一个与 Label Studio 兼容的 ML 后端服务器。

class MyModel(LabelStudioMLBase):

然后,在 __init__ 方法中定义和初始化需要的变量。LabelStudioMLBase 类提供了以下几个可供使用的特殊变量

  • self.label_config: 原始标签配置。
  • self.parsed_label_config: 为项目提供结构化的 Label Studio 标签配置。
  • self.train_output: 包含之前模型训练运行的结果,与训练调用部分中定义的 fit() 方法的输出相同。

如本教程的例子中,标签配置为:

<View>
  <Labels name="label" toName="text">
  <Label value="地名" background="#FFA39E"/>
  <Label value="人名" background="#D4380D"/>
  <Label value="组织" background="#FFC069"/>
  <Label value="时间" background="#AD8B00"/>
  <Label value="产品" background="#D3F261"/>
  <Label value="价格" background="#389E0D"/>
  <Label value="天气" background="#5CDBD3"/>
  </Labels>
  <Text name="text" value="$text"/>
</View>

相对应的 parsed_label_config 如下所示:

{
	'label': {
		'type': 'Labels',
		'to_name': ['text'],
		'inputs': [{
			'type': 'Text',
			'value': 'text'
		}],
		'labels': ['地名', '人名', '组织', '时间', '产品', '价格', '天气'],
		'labels_attrs': {
			'地名': {
				'value': '地名',
				'background': '#FFA39E'
			},
			'人名': {
				'value': '人名',
				'background': '#D4380D'
			},
			'组织': {
				'value': '组织',
				'background': '#FFC069'
			},
			'时间': {
				'value': '时间',
				'background': '#AD8B00'
			},
			'产品': {
				'value': '产品',
				'background': '#D3F261'
			},
			'价格': {
				'value': '价格',
				'background': '#389E0D'
			},
			'天气': {
				'value': '天气',
				'background': '#5CDBD3'
			}
		}
	}
}

根据需要从 self.parsed_label_config 变量中提取需要的信息,并通过 PaddleNLP 的 Taskflow 加载用于预标注的模型。

def __init__(self, **kwargs):
    # don't forget to initialize base class...
    super(MyModel, self).__init__(**kwargs)

    # print("parsed_label_config:", self.parsed_label_config)
    self.from_name, self.info = list(self.parsed_label_config.items())[0]

    assert self.info['type'] == 'Labels'
    assert self.info['inputs'][0]['type'] == 'Text'

    self.to_name = self.info['to_name'][0]
    self.value = self.info['inputs'][0]['value']
    self.labels = list(self.info['labels'])
    self.model = Taskflow("information_extraction", schema=self.labels, task_path= './checkpoint/model_best')

4.1.2 使用ML Backend predict 预测方法(自动标注)

编写代码覆盖 predict(tasks, **kwargs) 方法。predict() 方法接受 JSON 格式的 Label Studio 任务 并以 Label Studio 接受的格式 返回预测。此外,还可以包含和自定义可用于主动学习循环的预测分数。

tasks 参数包含了有关要进行预注释的任务的详细信息。具体的 task 格式如下所示:

{
	'id': 16,
	'data': {
		'text': '新华社都柏林6月28日电(记者张琪)第二届“汉语桥”世界小学生中文秀爱尔兰赛区比赛结果日前揭晓,来自都柏林市的小学五年级学生埃拉·戈尔曼获得一等奖。'
	},
	'meta': {},
	'created_at': '2022-07-12T07:05:06.793411Z',
	'updated_at': '2022-07-12T07:05:06.793424Z',
	'is_labeled': False,
	'overlap': 1,
	'inner_id': 6,
	'total_annotations': 0,
	'cancelled_annotations': 0,
	'total_predictions': 0,
	'project': 2,
	'updated_by': None,
	'file_upload': 2,
	'annotations': [],
	'predictions': []
}

通过 Taskflow 进行预测需要从 ['data']['text'] 字段提取出原始文本,返回的 uie 预测结果格式如下所示:

{
	'地名': [{
		'text': '爱尔兰',
		'start': 34,
		'end': 37,
		'probability': 0.9999107139090313
	}, {
		'text': '都柏林市',
		'start': 50,
		'end': 54,
		'probability': 0.9997840536235998
	}, {
		'text': '都柏林',
		'start': 3,
		'end': 6,
		'probability': 0.9999684097596173
	}],
	'人名': [{
		'text': '埃拉·戈尔曼',
		'start': 62,
		'end': 68,
		'probability': 0.9999879598978225
	}, {
		'text': '张琪',
		'start': 15,
		'end': 17,
		'probability': 0.9999905824882092
	}],
	'组织': [{
		'text': '新华社',
		'start': 0,
		'end': 3,
		'probability': 0.999975681447097
	}],
	'时间': [{
		'text': '6月28日',
		'start': 6,
		'end': 11,
		'probability': 0.9997071721989244
	}, {
		'text': '日前',
		'start': 43,
		'end': 45,
		'probability': 0.9999804497706464
	}]
}

从 uie 预测结果中提取相应的字段,构成 Label Studio 接受的预注释格式。命名实体识别任务的具体预注释示例可参考 Import span pre-annotations for text

更多其他类型任务的具体预注释示例可参考 Specific examples for pre-annotations

def predict(self, tasks, **kwargs):
    from_name = self.from_name
    to_name = self.to_name
    model = self.model

    predictions = []
    for task in tasks:
        # print("predict task:", task)
        text = task['data'][self.value]
        uie = model(text)[0]
        # print("uie:", uie)

        result = []
        scores = []
        for key in uie:
            for item in uie[key]:
                result.append({
                    'from_name': from_name,
                    'to_name': to_name,
                    'type': 'labels',
                    'value': {
                        'start': item['start'],
                        'end': item['end'],
                        'score': item['probability'],
                        'text': item['text'],
                        'labels': [key]
                    }
                })
                scores.append(item['probability'])
        result = sorted(result, key=lambda k: k["value"]["start"])
        mean_score = np.mean(scores) if len(scores) > 0 else 0

        predictions.append({
            'result': result,
            # optionally you can include prediction scores that you can use to sort the tasks and do active learning
            'score': float(mean_score),
            'model_version': 'uie-ner'
        })
    return predictions

4.1.3 使用 ML Backend fit 训练方法(根据标注好的数据再次优化训练模型)

基于新注释更新模型。

编写代码覆盖 fit() 方法。fit() 方法接受 JSON 格式的 Label Studio 注释 并返回任意一个可以存储模型相关信息的 JSON 字典。

def fit(self, annotations, workdir=None, **kwargs):
    """ This is where training happens: train your model given list of annotations, 
        then returns dict with created links and resources
    """
    # print("annotations:", annotations)
    dataset = convert(annotations)


    with open("./doccano_ext.jsonl", "w", encoding="utf-8") as outfile:
        for item in dataset:
            outline = json.dumps(item, ensure_ascii=False)
            outfile.write(outline + "\n")

    os.system('python doccano.py \
        --doccano_file ./doccano_ext.jsonl \
        --task_type "ext" \
        --save_dir ./data \
        --splits 0.8 0.2 0')

    os.system('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" \
        --init_from_ckpt "./checkpoint/model_best/model_state.pdparams" \
        --seed 1000 \
        --logging_steps 10 \
        --valid_steps 100 \
        --device "gpu"')

    return {
        'path': workdir
    }

4.2 Machine Learning 集成

4.2.1 启动 Label Studio

本环节在OpenBayes进行演示,AIStuodio暂不支持labelstudio云端侧交互。

  • 在 OpenBayes 启动一个「模型训练」的容器,环境选择 paddlepaddle-2.3 资源选择 vgpu 或其他 GPU 容器

  • 在 Jupyter 中打开一个 Terminal 窗口,然后执行命令 openbayes-label-studio 启动 label-studio

2023-03-28 07:53:14,345 - tasks.functions - INFO - Finished filling project field for Annotation model
2023-03-28 07:53:14,345 - tasks.functions - INFO - Finished filling project field for Annotation model
[2023-03-28 07:53:14,508] [label_studio.server::check_port_in_use::230] [INFO] Checking if host & port is available :: localhost:8080
2023-03-28 07:53:14,508 - label_studio.server - INFO - Checking if host & port is available :: localhost:8080
2023-03-28 07:53:14,508 - label_studio.server - INFO - Checking if host & port is available :: localhost:8080
[2023-03-28 07:53:14,510] [label_studio.core.utils.common::start_browser::313] [INFO] Start browser at URL: https://openbayes.com/jobs-auxiliary/Ting/cmnyo88l6nlb
2023-03-28 07:53:14,510 - label_studio.core.utils.common - INFO - Start browser at URL: https://openbayes.com/jobs-auxiliary/Ting/cmnyo88l6nlb
2023-03-28 07:53:14,510 - label_studio.core.utils.common - INFO - Start browser at **URL: https://openbayes.com/jobs-auxiliary/Ting/cmnyo88l6nlb**
Performing system checks...

[2023-03-28 07:53:14,563] [django::register_actions_from_dir::97] [INFO] No module named 'data_manager.actions.__pycache_'
[2023-03-28 07:53:14,563] [django::register_actions_from_dir::97] [INFO] No module named 'data_manager.actions.__pycache_'
2023-03-28 07:53:14,563 - django - INFO - No module named 'data_manager.actions.__pycache_'
2023-03-28 07:53:14,563 - django - INFO - No module named 'data_manager.actions.__pycache_'
System check identified no issues (1 silenced).
March 28, 2023 - 07:53:14
Django version 3.2.16, using settings 'label_studio.core.settings.label_studio'
Starting development server at http://0.0.0.0:8080/
Quit the server with CONTROL-C.

在浏览器中打开加粗的链接,注册账户并登录

注意: 对于不同的 OpenBayes 算力容器,红框中的外部访问链接各不相同,直接使用本教程中的链接是无效的,需用终端中提示的链接进行替换。

4.2.2 启动 Machine Learning Backend

参考链接:https://labelstud.io/guide/ml.html

在终端中依次执行下列命令:

# 初始化自定义机器学习后端
label-studio-ml init my_ml_backend --script my_ml_backend.py

# 开启机器学习后端服务
label-studio-ml startmy_ml_backend

成功启动后,在终端中可以看到 ML 后端的 URL。

注意: 对于不同的 OpenBayes 算力容器,红框中的外部访问链接各不相同,直接使用本教程中的链接是无效的,需用终端中提示的链接进行替换。也可以使用 localhost 替换其中的 IP 地址。

在启动自定义机器学习后端之后,就可以将其添加到 Label Studio 项目中。

具体步骤如下:

  1. 点击 Settings - Machine Learning - Add Model

  1. 填入标题、ML 后端的 URL、描述(可选)等内容

  1. 选择 Use for interactive preannotations 打开交互式预注释功能(可选)

  2. 点击 Validate and Save

如果出现报错,可查看 机器学习疑难解答

除了通过 Label Studio 的 UI 界面添加 ML 后端以外,还可以 使用 API 添加 ML 后端

4.2.3 获取交互式预注释

若要使用交互式预注释功能,需在添加 ML Backend 时打开 Use for interactive preannotations 选项。如未打开,可点击 Edit 进行编辑。然后随便点击一个数据,label studio 就会悄悄运行刚才的 ml backend 生成新的标注了。

查看预标注好的数据,如有必要,对标注进行修改。

  • 本例中,预标注的结果中『NBA』没有被识别出来,手动添加实体将其标注为『组织』。

  • 本例中,预标注的结果中将『人名』实体『三月』错标注为『时间』实体,手动进行修改。

修改完成后,或预标注的结果已经符合预期,点击 Submit 提交标注结果。

4.2.4 智能标注(自动再训练模型)

在标注了至少一项任务之后,就可以开始训练模型了。

点击 Settings - Machine Learning - Start Training 开始训练。

动态图为引用方便展示这个流程。

**然后返回启动 label-studio-ml-backend 的窗口可以看到训练的流程启动了。 **

除此之外,还可以 使用 API 训练模型使用 webhooks 触发训练

4.3 ML Backend 相关技巧提升

4.3.1 Import pre-annotated data into Label Studio

参考链接: https://labelstud.io/guide/predictions.html#Prepare-pre-annotations-for-Label-Studio

4.3.2 使用API训练

不适用UI界面更加丝滑

参考链接:https://labelstud.io/api/#operation/api_ml_create

4.3.3 使用 webhooks 触发训练

参考链接:https://labelstud.io/api/#operation/api_ml_train_create

4.3.4 使用 Label Studio 设置主动学习

这个是 Label Studio Enterprise Edition 企业版含有功能

参考链接:https://docs.heartex.com/guide/active_learning.html

5.模型部署

以下是 UIE Python 端的部署流程,包括环境准备、模型导出和使用示例。

5.1 UIE Python 端的部署流程

  • 模型导出
    模型训练、压缩时已经自动进行了静态图的导出以及 tokenizer 配置文件保存,保存路径${finetuned_model} 下应该有 .pdimodel、.pdiparams 模型文件可用于推理。

  • 模型部署
    以下示例展示如何基于 FastDeploy 库完成 UIE 模型完成通用信息抽取任务的 Python 预测部署。先参考 UIE 模型部署安装FastDeploy Python 依赖包。 可通过命令行参数–device以及–backend指定运行在不同的硬件以及推理引擎后端,并使用–model_dir参数指定运行的模型。模型目录为 model_zoo/uie/checkpoint/model_best(用户可按实际情况设置)。

FastDeploy提供各平台预编译库,供开发者直接下载安装使用。当然FastDeploy编译也非常容易,开发者也可根据自身需求编译FastDeploy。

详情参考链接:https://github.com/PaddlePaddle/FastDeploy/blob/develop/docs/cn/build_and_install/download_prebuilt_libraries.md

GPU端

为了在GPU上获得最佳的推理性能和稳定性,请先确保机器已正确安装NVIDIA相关驱动和基础软件,确保CUDA >= 11.2,cuDNN >= 8.1.1,并使用以下命令安装所需依赖

python部署比较常规就不展开:参考模型部署

5.2 Serving 服务编写

编写 predictor.py 文件:

  • 导入依赖库:除了业务中用到的库之外,需要额外依赖serving。

  • 后处理(可选):根据需要对模型返回的结果进行处理,以更好地展示。本教程中通过 format() 函数和 add_o() 函数修改命名实体识别结果的形式。

  • Predictor 类: 不需要继承其他的类,但是至少需要提供 __init__predict 两个接口。

    • __init__ 中定义实体抽取结构,通过 Taskflow 加载模型。
    • predict 中进行预测,返回后处理的结果。
class Predictor:
    def __init__(self):
        self.schema = ['地名', '人名', '组织', '时间', '产品', '价格', '天气']
        self.ie = Taskflow("information_extraction", schema=self.schema, task_path='./checkpoint/model_best')
    

    def predict(self, json):
        text = json["input"]
        uie = self.ie(text)[0]
        result = format(text, uie)
        return result
  • 运行:启动服务。
if __name__ == '__main__':
    serv.run(Predictor)

在项目根目录下已经提供了编写好的 predictor.py 可以直接在后续使用。

# !paddlenlp server server:app --workers 1 --host 0.0.0.0 --port 8189
result
  • 运行:启动服务。
if __name__ == '__main__':
    serv.run(Predictor)

在项目根目录下已经提供了编写好的 predictor.py 可以直接在后续使用。

# !paddlenlp server server:app --workers 1 --host 0.0.0.0 --port 8189
# !pip install --upgrade paddlenlp 
# import json

# import requests

# url = "http://0.0.0.0:8189/taskflow/uie"
# headers = {"Content-Type": "application/json"}
# texts = ["近日,量子计算专家、ACM计算奖得主Scott Aaronson通过博客宣布,将于本周离开得克萨斯大学奥斯汀分校(UT Austin)一年,并加盟人工智能研究公司OpenAI"]
# data = {
#     "data": {
#         "text": texts,
#     }
# }
# r = requests.post(url=url, headers=headers, data=json.dumps(data))
# datas = json.loads(r.text)
# print(datas)

note:部署环节请在本地上调试,更多详情参考:https://github.com/PaddlePaddle/PaddleNLP/tree/develop/model_zoo/uie

6.总结

  • Label Studio 所提供的 Machine Learning Backend 提供了一个比较灵活的辅助人工标注的框架,我们通过它确实可以加快 nlp 数据的标注
  • Label Studio 的 enterprise 版本提供了 Active Learning 的流程,不过从其描述看这个流程并不完美,尤其是 fit 部分,由于 Label Studio 低估了「Train」所花费的时间,所以每次标注都自动训练的流程可能并不会那么顺滑(会在链接时候等待一段时间)
  • 这次项目并没有使用 Label Studio 所提供的「Auto-Annotation」的功能,因为它存在重复标注的问题
  • 既然 Label Studio 提供了它的 api 那其实可玩的东西还是很多的,配合 webhook 等内容可能会让这个标注和训练的流程做的更加高效

此外目前使用的UIE码源是前几个版本的,最新官网更新了一些训练升级API,后续再重新优化现有项目。

本人对容器相关技术不太了解,所以在一些容器化技术操作上更多就是借鉴使用了,如有疑问评论区留言即可。

更多详情请参考Label Studio官网:

参考链接:

https://openbayes.com/console/Ting/containers/sDvsVp0CCFJ

https://docs.heartex.com/guide/active_learning.html

https://github.com/PaddlePaddle/PaddleNLP/tree/develop/model_zoo/uie


此文章为转载
原文链接

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐