写点什么

UIE Slim 满足工业应用场景,解决推理部署耗时问题,提升效能

作者:汀丶
  • 2022-10-21
    浙江
  • 本文字数:9141 字

    阅读完需:约 1 分钟

项目链接:fork 一下即可UIE Slim满足工业应用场景,解决推理部署耗时问题,提升效能!如果有图片缺失查看原项目

UIE Slim 满足工业应用场景,解决推理部署耗时问题,提升效能

在 UIE 强大的抽取能力背后,同样需要较大的算力支持计算。在一些工业应用场景中对性能的要求较高,若不能有效压缩则无法实际应用。因此,基于数据蒸馏技术构建了 UIE Slim 数据蒸馏系统。其原理是通过数据作为桥梁,将 UIE 模型的知识迁移到封闭域信息抽取小模型,以达到精度损失较小的情况下却能达到大幅度预测速度提升的效果。


FasterTokenizer 是一款简单易用、功能强大的跨平台高性能文本预处理库,集成业界多个常用的 Tokenizer 实现,支持不同 NLP 场景下的文本预处理功能,如文本分类、阅读理解,序列标注等。结合 PaddleNLP Tokenizer 模块,为用户在训练、推理阶段提供高效通用的文本预处理能力。use_faster: 使用 C++实现的高性能分词算子 FasterTokenizer 进行文本预处理加速


UIE 数据蒸馏三步


  • Step 1: 使用 UIE 模型对标注数据进行 finetune,得到 Teacher Model。

  • Step 2: 用户提供大规模无标注数据,需与标注数据同源。使用 Taskflow UIE 对无监督数据进行预测。

  • Step 3: 使用标注数据以及步骤 2 得到的合成数据训练出封闭域 Student Model。


效果展示:


测试硬件情况:


1 点算力卡对应的:V100 32GBGPUTesla V100Video Mem32GBCPU4 CoresRAM32GBDisk100GB



模型计算运行时间:



Archive: data.zip


  • inflating: ./data/unlabeled_data.txt

  • inflating: ./data/doccano_ext.json


示例数据包含以下两部分:


1. 进行预训练微调,得到 Teacher Model

具体参数以及 doccano 标注细节参考文档:


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


PaddleNLP之UIE信息抽取小样本进阶(二)[含doccano详解]


Paddlenlp之UIE分类模型【以情感倾向分析新闻分类为例】含智能标注方案)


Paddlenlp之UIE关系抽取模型【高管关系抽取为例】


UIE:模型



实际模型大小解释:


base 模型 118M parameters 是指 base 模型的参数个数,因为同一个模型可以被不同的精度来表示,例如 float16,float32,下载下来是 450M 左右(存储空间大小),是因为下载的模型是 float32,118M * 4 大概是存储空间的量级。


!python finetune.py \    --train_path "./data/train.txt" \    --dev_path "./data/dev.txt" \    --save_dir "./checkpoint" \    --learning_rate 5e-6  \    --batch_size 16 \    --max_seq_len 512 \    --num_epochs 10 \    --model "uie-base" \    --seed 1000 \    --logging_steps 10 \    --valid_steps 50 \    --device "gpu"
复制代码


base 模型部分结果展示:


[2022-09-08 17:26:55,701] [    INFO] - Evaluation precision: 0.69375, recall: 0.69811, F1: 0.69592[2022-09-08 17:27:01,145] [    INFO] - global step 260, epoch: 9, loss: 0.00172, speed: 1.84 step/s[2022-09-08 17:27:06,448] [    INFO] - global step 270, epoch: 9, loss: 0.00168, speed: 1.89 step/s[2022-09-08 17:27:12,102] [    INFO] - global step 280, epoch: 10, loss: 0.00165, speed: 1.77 step/s[2022-09-08 17:27:17,607] [    INFO] - global step 290, epoch: 10, loss: 0.00162, speed: 1.82 step/s[2022-09-08 17:27:22,899] [    INFO] - global step 300, epoch: 10, loss: 0.00159, speed: 1.89 step/s[2022-09-08 17:27:26,577] [    INFO] - Evaluation precision: 0.69277, recall: 0.72327, F1: 0.70769[2022-09-08 17:27:26,577] [    INFO] - best F1 performence has been updated: 0.69841 --> 0.70769
复制代码

2.离线蒸馏

2.1 通过训练好的 UIE 定制模型预测无监督数据的标签

用户提供大规模无标注数据,需与标注数据同源。使用 Taskflow UIE 对无监督数据进行预测。


References:


GlobalPointer:用统一的方式处理嵌套和非嵌套NER:


GPLinker:基于GlobalPointer的实体关系联合抽取


GPLinker_pytorch


CBLUE


%cd /home/aistudio/data_distill!python data_distill.py \    --data_path /home/aistudio/data \    --save_dir student_data \    --task_type relation_extraction \    --synthetic_ratio 10 \    --model_path /home/aistudio/checkpoint/model_best
复制代码


可配置参数说明:


  • data_path: 标注数据(doccano_ext.json)及无监督文本(unlabeled_data.txt)路径。

  • model_path: 训练好的 UIE 定制模型路径。

  • save_dir: 学生模型训练数据保存路径。

  • synthetic_ratio: 控制合成数据的比例。最大合成数据数量=synthetic_ratio*标注数据数量。

  • task_type: 选择任务类型,可选有 entity_extraction,relation_extraction,event_extraction 和 opinion_extraction。因为是封闭域信息抽取,需指定任务类型。

  • seed: 随机种子,默认为 1000。


 parser.add_argument("--data_path", default="../data", type=str, help="The directory for labeled data with doccano format and the large scale unlabeled data.")    parser.add_argument("--model_path", type=str, default="../checkpoint/model_best", help="The path of saved model that you want to load.")    parser.add_argument("--save_dir", default="./distill_task", type=str, help="The path of data that you wanna save.")    parser.add_argument("--synthetic_ratio", default=10, type=int, help="The ratio of labeled and synthetic samples.")    parser.add_argument("--task_type", choices=['relation_extraction', 'event_extraction', 'entity_extraction', 'opinion_extraction'], default="entity_extraction", type=str, help="Select the training task type.")    parser.add_argument("--seed", type=int, default=1000, help="Random seed for initialization")
复制代码


可配置参数说明:


  • model_path: 训练好的 UIE 定制模型路径。

  • test_path: 测试数据集路径。

  • label_maps_path: 学生模型标签字典。

  • batch_size: 批处理大小,默认为 8。

  • max_seq_len: 最大文本长度,默认为 256。

  • task_type: 选择任务类型,可选有 entity_extraction,relation_extraction,event_extraction 和 opinion_extraction。因为是封闭域信息抽取的评估,需指定任务类型。


parser.add_argument("--model_path", type=str, default=None, help="The path of saved model that you want to load.")    parser.add_argument("--test_path", type=str, default=None, help="The path of test set.")    parser.add_argument("--encoder", default="ernie-3.0-base-zh", type=str, help="Select the pretrained encoder model for GP.")    parser.add_argument("--label_maps_path", default="./ner_data/label_maps.json", type=str, help="The file path of the labels dictionary.")    parser.add_argument("--batch_size", type=int, default=16, help="Batch size per GPU/CPU for training.")    parser.add_argument("--max_seq_len", type=int, default=128, help="The maximum total input sequence length after tokenization.")    parser.add_argument("--task_type", choices=['relation_extraction', 'event_extraction', 'entity_extraction', 'opinion_extraction'], default="entity_extraction",
复制代码

2.3 学生模型训练

底座模型可以参考下面进行替换!


!python train.py \    --task_type relation_extraction \    --train_path student_data/train_data.json \    --dev_path student_data/dev_data.json \    --label_maps_path student_data/label_maps.json \    --num_epochs 200 \    --encoder ernie-3.0-mini-zh
复制代码


# %cd /home/aistudio/data_distill!python train.py \    --task_type relation_extraction \    --train_path student_data/train_data.json \    --dev_path student_data/dev_data.json \    --label_maps_path student_data/label_maps.json \    --num_epochs 100 \    --encoder ernie-3.0-mini-zh\    --device "gpu"\    --valid_steps 100\    --logging_steps 10\    --save_dir './checkpoint2'\    --batch_size 16
复制代码

3.Taskflow 部署学生模型以及性能测试

通过 Taskflow 一键部署封闭域信息抽取模型,task_path 为学生模型路径。


demo 测试


from pprint import pprintfrom paddlenlp import Taskflow
ie = Taskflow("information_extraction", model="uie-data-distill-gp", task_path="checkpoint2/model_best/") # Schema 在闭域信息抽取中是固定的pprint(ie("登革热@结果 升高 ### 血清白蛋白水平 检查 结果 检查 在资源匮乏地区和富足地区,对有症状患者均应早期检测。"))
复制代码


[{'疾病': [{'end': 3,          'probability': 0.9995957,          'relations': {'实验室检查': [{'end': 21,                                   'probability': 0.99892455,                                   'relations': {},                                   'start': 14,                                   'text': '血清白蛋白水平'}],                        '影像学检查': [{'end': 21,                                   'probability': 0.99832386,                                   'relations': {},                                   'start': 14,                                   'text': '血清白蛋白水平'}]},          'start': 0,          'text': '登革热'}]}]
复制代码


from pprint import pprintimport jsonfrom paddlenlp.taskflow import Taskflowimport pandas as pd#运行时间import time

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) #将每行数据插入data中 return data
# 时间1old_time = time.time()data_input=openreadtxt('/home/aistudio/数据集/unlabeled_data.txt')

few_ie = Taskflow("information_extraction", model="uie-data-distill-gp", task_path="/home/aistudio/data_distill/checkpoint2/model_best",batch_size=32) # Schema 在闭域信息抽取中是固定的

# 时间1current_time = time.time()print("数据模型载入运行时间为" + str(current_time - old_time) + "s")
#时间2old_time1 = time.time()results=few_ie(data_input)current_time1 = time.time()print("模型计算运行时间为" + str(current_time1 - old_time1) + "s")#时间2
#时间三old_time3 = time.time()test = pd.DataFrame(data=results)test.to_csv('/home/aistudio/output/reslut.txt', sep='\t', index=False,header=False) #本地
# with open("/home/aistudio/output/reslut.txt", "w+",encoding='UTF-8') as f: #a : 写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾# for result in results:# line = json.dumps(result, ensure_ascii=False) #对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False# f.write(line + "\n")current_time3 = time.time()print("数据导出运行时间为" + str(current_time3 - old_time3) + "s")
# for idx, text in enumerate(data):# print('Data: {} \t Lable: {}'.format(text[0], results[idx]))print("数据结果已导出")
复制代码


**mini运行时间:**
数据模型载入运行时间为0.8430757522583008s
模型计算运行时间为6.839258909225464s
数据导出运行时间为0.008304595947265625s
**nano运行时间:**数据模型载入运行时间为0.5164840221405029s
模型计算运行时间为6.6231770515441895s
数据导出运行时间为0.023623943328857422s
**micro运行时间:**
数据模型载入运行时间为0.5323500633239746s
模型计算运行时间为6.77699007987976s
数据导出运行时间为0.04320549964904785s
复制代码

4 进行预训练模型 UIE-mini 并测试推理时间

封闭域 UIE 的 schema 是固定的,可以在 label_maps.json 查看


0:"手术治疗"1:"实验室检查"2:"影像学检查"
复制代码


from pprint import pprintimport jsonfrom paddlenlp import Taskflowimport pandas as pd#运行时间import time

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) #将每行数据插入data中 return data
# 时间1old_time = time.time()data_input=openreadtxt('/home/aistudio/数据集/unlabeled_data.txt')
schema = {'疾病': ['手术治疗', '实验室检查', '影像学检查']}# few_ie = Taskflow('information_extraction', schema=schema, batch_size=32,task_path='/home/aistudio/checkpoint_mini/model_best') #自行切换few_ie = Taskflow('information_extraction', schema=schema, batch_size=32,task_path='/home/aistudio/checkpoint_micro/model_best')# 时间1current_time = time.time()print("数据模型载入运行时间为" + str(current_time - old_time) + "s")
#时间2old_time1 = time.time()results=few_ie(data_input)current_time1 = time.time()print("模型计算运行时间为" + str(current_time1 - old_time1) + "s")#时间2
#时间三old_time3 = time.time()test = pd.DataFrame(data=results)test.to_csv('/home/aistudio/output/reslut.txt', sep='\t', index=False,header=False) #本地
# with open("/home/aistudio/output/reslut.txt", "w+",encoding='UTF-8') as f: #a : 写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾# for result in results:# line = json.dumps(result, ensure_ascii=False) #对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False# f.write(line + "\n")current_time3 = time.time()print("数据导出运行时间为" + str(current_time3 - old_time3) + "s")
# for idx, text in enumerate(data):# print('Data: {} \t Lable: {}'.format(text[0], results[idx]))print("数据结果已导出")
复制代码


通过上述程序自行切换:加载对应模型
记录推理时间:**uie-nano**数据模型载入运行时间为0.3770780563354492s模型计算运行时间为24.893776178359985s数据导出运行时间为0.01157689094543457s
**uie-micro**数据模型载入运行时间为0.39632749557495117s模型计算运行时间为26.367019176483154s数据导出运行时间为0.012260198593139648s
**uie-mini**
数据模型载入运行时间为0.5642790794372559s
模型计算运行时间为28.93251943588257s
数据导出运行时间为0.01435089111328125s
**uie-base**
数据模型载入运行时间为1.4756040573120117s
模型计算运行时间为68.61049008369446s
数据导出运行时间为0.02205801010131836s
复制代码

5.提前尝鲜 UIE FasterTokenizer 加速,提升推理性能

FasterTokenizer 是一款简单易用、功能强大的跨平台高性能文本预处理库,集成业界多个常用的 Tokenizer 实现,支持不同 NLP 场景下的文本预处理功能,如文本分类、阅读理解,序列标注等。结合 PaddleNLP Tokenizer 模块,为用户在训练、推理阶段提供高效通用的文本预处理能力。


use_faster: 使用 C++实现的高性能分词算子 FasterTokenizer 进行文本预处理加速。需要通过pip install faster_tokenizer安装 FasterTokenizer 库后方可使用。默认为False。更多使用说明可参考[FasterTokenizer 文档]


https://github.com/PaddlePaddle/PaddleNLP/blob/develop/faster_tokenizer/README.md


特性


  • 高性能。由于底层采用 C++实现,所以其性能远高于目前常规 Python 实现的 Tokenizer。在文本分类任务上,FasterTokenizer 对比 Python 版本 Tokenizer 加速比最高可达 20 倍。

  • 跨平台。FasterTokenizer 可在不同的系统平台上使用,目前已支持 Windows x64,Linux x64 以及 MacOS 10.14+平台上使用。

  • 多编程语言支持。FasterTokenizer 提供在 C++、Python 语言上开发的能力。

  • 灵活性强。用户可以通过指定不同的 FasterTokenizer 组件定制满足需求的 Tokenizer。


FAQ


Q:我在 AutoTokenizer.from_pretrained 接口上已经打开 use_faster=True 开关,为什么文本预处理阶段性能上好像没有任何变化?


A:在有三种情况下,打开 use_faster=True 开关可能无法提升性能:


  • 没有安装 faster_tokenizer。若在没有安装 faster_tokenizer 库的情况下打开 use_faster 开关,PaddleNLP 会给出以下 warning:"Can't find the faster_tokenizer package, please ensure install faster_tokenizer correctly. "。

  • 加载的 Tokenizer 类型暂不支持 Faster 版本。目前支持 4 种 Tokenizer 的 Faster 版本,分别是 BERT、ERNIE、TinyBERT 以及 ERNIE-M Tokenizer。若加载不支持 Faster 版本的 Tokenizer 情况下打开 use_faster 开关,PaddleNLP 会给出以下 warning:"The tokenizer XXX doesn't have the faster version. Please check the map paddlenlp.transformers.auto.tokenizer.FASTER_TOKENIZER_MAPPING_NAMES to see which faster tokenizers are currently supported."

  • 待切词文本长度过短(如文本平均长度小于 5)。这种情况下切词开销可能不是整个文本预处理的性能瓶颈,导致在使用 FasterTokenizer 后仍无法提升整体性能。

5.1 方案一

把 paddlenlp 直接装到指定路径然后修改对应文件;详情参考这个 PR:


Add use_faster flag for uie of taskflow.

5.2 方案二

直接找到 pr 修改后的版本,从 giuhub 拉去过来:链接参考


https://github.com/joey12300/PaddleNLP/tree/add_ft_requirements


from pprint import pprintimport jsonfrom paddlenlp.taskflow import Taskflowimport pandas as pd#运行时间import time

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) #将每行数据插入data中 return data
# 时间1old_time = time.time()data_input=openreadtxt('/home/aistudio/数据集/unlabeled_data-Copy1.txt')
few_ie = Taskflow("information_extraction", model="uie-data-distill-gp", task_path="/home/aistudio/data_distill/checkpoint2/model_best",use_faster=True,batch_size=32) # Schema 在闭域信息抽取中是固定的# few_ie = Taskflow("information_extraction", model="uie-data-distill-gp", task_path="/home/aistudio/data_distill/checkpoint2/model_best",batch_size=32) # Schema 在闭域信息抽取中是固定的
# schema = {'疾病': ['手术治疗', '实验室检查', '影像学检查']}# few_ie = Taskflow('information_extraction', schema=schema, batch_size=32,use_faster=True,task_path='/home/aistudio/checkpoint/model_best')# few_ie = Taskflow('information_extraction', schema=schema, batch_size=32,task_path='/home/aistudio/checkpoint/model_best')
# 时间1current_time = time.time()print("数据模型载入运行时间为" + str(current_time - old_time) + "s")
#时间2old_time1 = time.time()results=few_ie(data_input)current_time1 = time.time()print("模型计算运行时间为" + str(current_time1 - old_time1) + "s")#时间2
#时间三old_time3 = time.time()test = pd.DataFrame(data=results)test.to_csv('/home/aistudio/output/reslut.txt', sep='\t', index=False,header=False) #本地
# with open("/home/aistudio/output/reslut.txt", "w+",encoding='UTF-8') as f: #a : 写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾# for result in results:# line = json.dumps(result, ensure_ascii=False) #对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False# f.write(line + "\n")current_time3 = time.time()print("数据导出运行时间为" + str(current_time3 - old_time3) + "s")
# for idx, text in enumerate(data):# print('Data: {} \t Lable: {}'.format(text[0], results[idx]))print("数据结果已导出")
复制代码

5.3UIE FasterTokenizer 加速,提升推理性能

数据样本增大为原来的三倍:unlabeled_data-Copy1.txt


UIE base


数据模型载入运行时间为 1.6006419658660889s


模型计算运行时间为 203.95947885513306s


数据导出运行时间为 0.07103896141052246s


UIE base + FasterTokenizer


数据模型载入运行时间为 1.6196515560150146s


模型计算运行时间为 177.17986011505127s


数据导出运行时间为 0.07898902893066406s


UIE 蒸馏 mini


数据模型载入运行时间为 0.8441095352172852s


模型计算运行时间为 21.979790925979614s


数据导出运行时间为 0.02339339256286621s


UIE 蒸馏 mini + FasterTokenizer


数据模型载入运行时间为 0.7269768714904785s


模型计算运行时间为 20.155770540237427s


数据导出运行时间为 0.012202978134155273s

6.总结

测试硬件情况:


1 点算力卡对应的:V100 32GBGPUTesla V100Video Mem32GBCPU4 CoresRAM32GBDisk100GB



模型计算运行时间:





1.可以看出 UIE 蒸馏在小网络下,性能差不多可以按需选择。可能会在更大任务性能会更好点


2.这里 uie-base 等只简单运行了 10 个 epoch,可以多训练会提升性能


3.一般学生模型会选择参数量比较小的,UIE 蒸馏版是 schema 并行推理的,速度会比 UIE 快很多,特别是 schema 比较多以及关系抽取等需要多阶段推理的情况




1.FasterTokenizer 加速,paddlenlp2.4.0 版本目前还不支持,只要参考 PR 改下源码


2.封闭域 UIE 的话 schema 是固定的,可以在 label_maps.json 查看,目前支持实体抽取、关系抽取、观点抽取和事件抽取,句子级情感分类目前蒸馏还不支持


3.想要更快的推理换下学生模型的 backbone 就行


感谢


感谢 paddlenlp 工作人员 @linjieccc 的支持,接受了 issue 并创建了 pull request:fix data distill for UIE #3231 https://github.com/PaddlePaddle/PaddleNLP/pull/3231


Add use_faster flag for uie of taskflow. #3194


展望:


后续对 FasterTokenizer 进行补充;以及研究一下 UIE 模型的量化、剪枝、NAS


项目链接:fork 一下即可 UIE Slim 满足工业应用场景,解决推理部署耗时问题,提升效能!如果有图片缺失查看原项目

用户头像

汀丶

关注

还未添加个人签名 2022-01-06 加入

还未添加个人简介

评论

发布
暂无评论
UIE Slim满足工业应用场景,解决推理部署耗时问题,提升效能_汀丶_InfoQ写作社区