作者:jjyaoao, 欢迎各位佬来AiStudio和俺互关呀!
一、计划介绍
1.1 项目简介
中医文献阅览了解是一个将自然语言处理技能运用于中医药范畴的项目。它的目标是开发一个可以读取、了解和答复中医药常识的模型,以便更好地遍及和传播中医药常识。
为了完结这个目标,该项目需求树立一个大规模的中医药语料库,并运用自然语言处理技能对语料库进行处理,提取关键信息并树立模型。模型的输入可所以一个中医药相关的问题、模型的输出则是一个与问题相关的答案。
例如,如果输入的问题是”什么是中医证候学?“,则模型的输出可能是“中医证候学是中医药的一个重要理论,它旨在经过调查患者的症状和体征,揣度患者所患疾病的特点和发展趋势,为临床治疗供给理论指导。
此外,依据该项目还可以开发更多关于中医药常识的小运用,如中医药问诊系统、中医药辨证论治辅助工具等,以帮助更多人了解和运用中医药常识。
1.2 数据集介绍
本次标示数据源来自中医药范畴文本,包括【黄帝内经翻译版】、【名医百科中医篇】、【中成药用药卷】、【慢性病摄生保健科普常识】四个主要来源,共标示 13000对(问题、文档、答案),来源于5000篇文档,每篇文档由人工标示发生1~4对(问题, 答案)对。
例子:
{
2 "id": 98,
3 "text": "黄帝道:什麽叫重实?岐伯说:所谓重实,如大热患者,邪气甚热,而脉象又盛满,表里俱实,便叫重实",
4 "annotations": [
5 {
6 "Q": "重实是指什么?",
7 "A": "所谓重实,如大热患者,邪气甚热,而脉象又盛满,表里俱实,便叫重实"
8 },
9 {
10 "Q": "重实之人的脉象是什么样?",
11 "A": "脉象又盛满"
12 }
13 ],
14 "source": "黄帝内经翻译版"
15 }
以Json格局供给,包括:
id: 阶段id
text: 阶段文本
annotations: 包括(问题、答案)对
Q:问题 A:答案
来自于“万创杯”中医药天池大数据竞赛
1.3 技能点介绍
1、构建中医MRC数据集;
2、运用PaddleNLP开发库建立、练习并调优阅览了解模型;
3、动转静,完结静态图的推理,并用gradio完结可交互的布置。
1.3.1 PaddleNLP
PaddleNLP是飞桨自然语言处理开发库,具备易用的文本范畴API,多场景的运用示例、和高功能分布式练习三大特点,旨在提高飞桨开发者文本范畴建模功率,旨在提高开发者在文本范畴的开发功率,并供给丰厚的NLP运用示例。
-
易用的文本范畴API
- 供给丰厚的工业级预置使命才能Taskflow和全流程的文本范畴API:支撑丰厚中文数据集加载的Dataset API,可灵敏高效地完结数据预处理的Data API,预置60+预练习词向量的Embedding API,供给100+预练习模型的Transformer API等,可大幅提高NLP使命建模的功率。
-
多场景的运用示例
- 覆盖从学术到工业级的NLP运用示例,涵盖NLP根底技能、NLP系统运用以及相关拓宽运用。全面依据飞桨核心结构2.0全新API系统开发,为开发者供给飞桨文本范畴的最佳实践。
-
高功能分布式练习
- 依据飞桨核心结构领先的主动混合精度优化战略,结合分布式Fleet API,支撑4D混合并行战略,可高效地完结大规模预练习模型练习。
-
项目GitHub:github.com/PaddlePaddl…
1.3.2 Roberta阅览了解模型
阅览了解实质是一个答案抽取使命,PaddleNLP关于各种预练习模型现已内置了关于下游使命-答案抽取的Fine-tune网络。
以下项目以百度飞桨模型库中的Roberta模型为例,介绍怎么将预练习模型Fine-tune完结答案抽取使命。
答案抽取使命的实质便是依据输入的问题和文章,猜测答案在文章中的开始方位和完毕方位。依据BERT的答案抽取原理如下图所示:
Roberta模型主要是在BERT根底上做了几点调整:
1)练习时刻更长,batch size更大,练习数据更多;
2)移除了next predict loss;
3)练习序列更长;
4)动态调整Masking机制。
5) Byte level BPE RoBERTa is trained with dynamic masking
另外,运用其他类似模型,利用 PaddleNLP 开发库也能很快捷的完结。
paddlenlp.transformers.RobertaForQuestionAnswering()
一行代码即可加载预练习模型BERT用于答案抽取使命的Fine-tune网络。
paddlenlp.transformers.RobertaForQuestionAnswering.from_pretrained()
指定想要运用的模型称号和文本分类的类别数,一行代码完结网络构建。
1.3.3 Gradio算法可视化布置
怎么将你的AI算法敏捷共享给别人,让对方体会,一直是一件费事事儿。
首要大部分人都是在本地跑代码,让别人运用你的模型,以往有这三种计划:
- 上github
- 将代码打包或许封装成docker后,用QQ/百度云/U盘传输
- 学习前后端常识,写个前端界面,买个域名,用flask这样微服务结构快速布置,看情况结合一下内网穿透。 这些计划的问题在于——前两者需求对方会编程会装备环境(还得愿意),咱们的共享目标满意这个条件的寥寥无几;后者则需求你这个算法工程师升级成全栈,学习前后端开发,学习本钱太高。
总结起来:场景不匹配,需求不符合,费时又吃力!
那么有没有更好的解决计划呢?有!它便是我今日要给咱们安利的一个python开源库:Gradio。
Gradio是MIT的开源项目,GitHub 2k+ star。
运用gradio,只需在原有的代码中添加几行,就能主动化生成交互式web页面,并支撑多种输入输出格局,比如图像分类中的图>>标签,超分辨率中的图>>图等。
一起还支撑生成能外部网络拜访的链接,可以敏捷让你的朋友,搭档体会你的算法。
总结起来,它的优势有:
主动生成页面且可交互改动几行代码就能完结 支撑自界说多种输入输出 支撑生成可外部拜访的链接进行共享
想要了解更多,请见官网gradio
1.4 最终完结作用呈现
接下来进行详解与代码展现
二、详细规划流程展现
2.0 环境装备
本项目依据PaddlePaddle 2.0.2 与 PaddleNLP 2.0.7版本, 关于怎么下载此版本可以点击飞桨官网,检查下载方式,更加推荐在Aistudio上一键运行哦,项目链接
# 首要导入实验所需求用到的库包。
# base
import paddlenlp as ppnlp
from utils import prepare_train_features, prepare_validation_features
from functools import partial
from paddlenlp.metrics.squad import squad_evaluate, compute_prediction
import collections
import time
import json
# data preprocess:
from paddlenlp.datasets import load_dataset, MapDataset
from sklearn.model_selection import train_test_split
from paddle.io import Dataset
# Build the dataloader
import paddle
from paddlenlp.data import Stack, Dict, Pad
2.1 计划规划
阅览了解的计划如上图,首要是query表明的是问句,一般是用户的发问,passage表明的是文章,表明的是query的答案要从passage里边抽取出来,query和passage经过数据预处理,得到id方式的输入,然后把query,passage的id方式输入到Roberta模型里边,Roberta模型经过处理睬输出答案的方位,输出方位今后就可以得到相应的answer了。
2.2 数据处理
详细的使命界说为:关于一个给定的问题q和一个华章p,依据华章内容,给出该问题的答案a。数据集中的每个样本,是一个三元组<q, p, a>,例如:
问题 q: 草菇有什么功效?
华章 p: 草菇荠菜汤新鲜幽香、色味调配,具有清热和脾、益气平肝、降糖降压等功效,是夏季解暑祛热的良食佳品…….
参考答案 a: 草菇荠菜汤新鲜幽香、色味调配,具有清热和脾、益气平肝、降糖降压等功效,是夏季解暑祛热的良食佳品
咱们本次的数据集是以Json格局供给,包括:
- id: 阶段id
- text: 阶段文本
- annotations: 包括(问题、答案)对,共有
- Q:问题
- A:答案
将上述数据进行简略地数据清洗以及格局(sqaud格局)转化操作,为了便利读取,详细格局如下:
{
'id': 'xx', 'title': 'xxx',
'context': 'xxxx',
'question': 'xxxxx',
'answers': ['xxxx'],
'answer_starts': [xxx]
}
2.2.1 数据集加载与处理
PaddleNLP现已内置SQuAD,CMRC等中英文阅览了解数据集,运用paddlenlp.datasets.load_dataset()
API即可一键加载。本实例加载的是自行装配的中医阅览了解数据集,选用SQuAD数据格局读入,InputFeature运用滑动窗口的办法生成,即一个example可能对应多个InputFeature。
答案抽取使命即依据输入的问题和文章,猜测答案在文章中的开始方位和完毕方位。
由于文章加问题的文本长度可能大于max_seq_length,答案呈现的方位有可能呈现在文章最后,所以不能简略的对文章进行截断。
那么关于过长的文章,则选用滑动窗口将文章分红多段,别离与问题组合。再用对应的tokenizer转化为模型可承受的feature。doc_stride参数便是每次滑动的距离。滑动窗口生成InputFeature的进程如下图:
ppnlp.transformers.RobertaTokenizer
调用RobertaTokenizer进行数据处理。
预练习模型Roberta对中文数据的处理是以byte为单位的BPE编码。官方词表包括5w多的byte级其他token。merges.txt中存储了一切的token,而vocab.json则是一个byte到索引的映射,一般频率越高的byte索引越小。所以转化的进程是,先将输入的一切tokens转化为merges.txt中对应的byte,再经过vocab.json中的字典进行byte到索引的转化。
tokenizer的作用是将原始输入文本转化成模型可以承受的输入数据方式。关于Roberta,比如输入的文本是
What's up with the tokenizer?
首要运用merges.txt
转化为对应的Byte(类似于标准化的进程)
['What', "'s", 'up', 'with', 'the', 'token', 'izer', '?']
再经过vocab.json
文件存储的映射转化为对应的索引
[ 'What', "'s", 'up', 'with', 'the', 'token', 'izer', '?']
---- becomes ----
[ 2061, 338, 510, 351, 262, 11241, 7509, 30]
# 更多可选择模型:
# ['bert-base-uncased', 'bert-large-uncased', 'bert-base-multilingual-uncased', 'bert-base-cased', 'bert-base-chinese', 'bert-base-multilingual-cased'
# , 'bert-large-cased', 'bert-wwm-chinese', 'bert-wwm-ext-chinese', 'macbert-base-chinese', 'macbert-large-chinese', 'simbert-base-chinese']
# 界说运用paddleNLP内置的roberta中文预练习模型
MODEL_NAME = 'roberta-wwm-ext-large'
tokenizer = ppnlp.transformers.RobertaTokenizer.from_pretrained(MODEL_NAME)
2.2.2 数据转化
运用load_dataset()
API默许读取到的数据集是MapDataset
目标,MapDataset
是paddle.io.Dataset
的功能增强版本。其内置的map()
办法适合用来进行批量数据集处理。map()
办法传入的是一个用于数据处理的function。
以下是采纳的数据转化的用法:
max_seq_length = 512
doc_stride = 128
train_trans_func = partial(prepare_train_features,
max_seq_length=max_seq_length,
doc_stride=doc_stride,
tokenizer=tokenizer)
train_ds.map(train_trans_func, batched=True)
dev_trans_func = partial(prepare_validation_features,
max_seq_length=max_seq_length,
doc_stride=doc_stride,
tokenizer=tokenizer)
dev_ds.map(dev_trans_func, batched=True)
2.2.3 结构Dataloader
运用paddle.io.DataLoader接口多线程异步加载数据。一起运用paddlenlp.data中供给的办法把feature组成batch
# Build the dataloader
batch_size = 8
train_batch_sampler = paddle.io.DistributedBatchSampler(
train_ds, batch_size=batch_size, shuffle=True)
train_batchify_fn = lambda samples, fn=Dict({
"input_ids": Pad(axis=0, pad_val=tokenizer.pad_token_id),
"token_type_ids": Pad(axis=0, pad_val=tokenizer.pad_token_type_id),
"start_positions": Stack(dtype="int64"),
"end_positions": Stack(dtype="int64")
}): fn(samples)
train_data_loader = paddle.io.DataLoader(
dataset=train_ds,
batch_sampler=train_batch_sampler,
collate_fn=train_batchify_fn,
return_list=True)
dev_batch_sampler = paddle.io.BatchSampler(
dev_ds, batch_size=batch_size, shuffle=False)
dev_batchify_fn = lambda samples, fn=Dict({
"input_ids": Pad(axis=0, pad_val=tokenizer.pad_token_id),
"token_type_ids": Pad(axis=0, pad_val=tokenizer.pad_token_type_id)
}): fn(samples)
dev_data_loader = paddle.io.DataLoader(
dataset=dev_ds,
batch_sampler=dev_batch_sampler,
collate_fn=dev_batchify_fn,
return_list=True)
2.3 模型练习与战略选择
2.3.1设置Fine-Tune优化战略
适用于ERNIE/BERT这类Transformer模型的学习率为warmup的动态学习率。
# 参数装备
# 练习进程中的最大学习率
learning_rate = 3e-5
# 练习次序
epochs = 2
# 学习率预热比例
warmup_proportion = 0.1
# 权重衰减系数,类似模型正则项战略,防止模型过拟合
weight_decay = 0.01
num_training_steps = len(train_data_loader) * epochs
lr_scheduler = ppnlp.transformers.LinearDecayWithWarmup(learning_rate, num_training_steps, warmup_proportion)
# Generate parameter names needed to perform weight decay.
# All bias and LayerNorm parameters are excluded.
decay_params = [
p.name for n, p in model.named_parameters()
if not any(nd in n for nd in ["bias", "norm"])
]
optimizer = paddle.optimizer.AdamW(
learning_rate=lr_scheduler,
parameters=model.parameters(),
weight_decay=weight_decay,
apply_decay_param_fun=lambda x: x in decay_params)
2.3.2 规划loss function
由于BertForQuestionAnswering模型对将BertModel的sequence_output拆开成start_logits和end_logits进行输出,所以阅览了解使命的loss也由start_loss和end_loss组成,咱们需求自己界说loss function。关于答案其实方位和完毕方位的猜测可以别离成两个分类使命。所以规划的loss function如下:
class CrossEntropyLossForSQuAD(paddle.nn.Layer):
def __init__(self):
super(CrossEntropyLossForSQuAD, self).__init__()
def forward(self, y, label):
start_logits, end_logits = y # both shape are [batch_size, seq_len]
start_position, end_position = label
start_position = paddle.unsqueeze(start_position, axis=-1)
end_position = paddle.unsqueeze(end_position, axis=-1)
start_loss = paddle.nn.functional.softmax_with_cross_entropy(
logits=start_logits, label=start_position, soft_label=False)
start_loss = paddle.mean(start_loss)
end_loss = paddle.nn.functional.softmax_with_cross_entropy(
logits=end_logits, label=end_position, soft_label=False)
end_loss = paddle.mean(end_loss)
loss = (start_loss + end_loss) / 2
return loss
2.3.3 模型练习
模型练习的进程一般有以下过程:
- 从dataloader中取出一个batch data
- 将batch data喂给model,做前向核算
- 将前向核算成果传给损失函数,核算loss。
- loss反向回传,更新梯度。重复以上过程。
每练习一个epoch时,程序经过evaluate()调用paddlenlp.metric.squad中的squad_evaluate(), compute_predictions()评估当时模型练习的作用,其中:
-
compute_predictions()用于生成可提交的答案;
-
squad_evaluate()用于回来点评目标。
二者适用于一切符合squad数据格局的答案抽取使命。这类使命运用Rouge-L和exact来评估猜测的答案和实在答案的类似程度。
三、猜测布置
模型练习完结之后接下来咱们完结模型的猜测布置。尽管练习阶段运用的动态图方式有许多优点,包括Python风格的编程体会(运用RNN等包括控制流的网络时尤为显着)、友好的debug交互机制等。但Python动态图方式无法更好的满意猜测布置阶段的功能要求,一起也限制了布置环境。
静态图是猜测布置一般选用的方式。经过静态图中预先界说的网络结构,一方面无需像动态图那样履行开支较大的Python代码;另一方面,预先固定的图结构也为依据图的优化供给了可能,这些可以有用提高猜测布置的功能。常用的依据图的优化战略有内存复用和算子交融,这需求猜测引擎的支撑。下面是算子交融的一个示例(将Transformer Block的FFN中的矩阵乘->加bias->relu激活替换为单个算子):
高功能猜测布置需求静态图模型导出和猜测引擎两方面的支撑,这儿别离介绍。
3.1 动转静导出模型
依据静态图的猜测布置要求将动态图的模型转化为静态图方式的模型(网络结构和参数权重)。 Paddle静态图方式的模型(由变量和算子构成的网络结构)运用Program来存放,Program的结构可以经过Paddle的静态图方式说明,静态图方式下网络构建履行的各API会将输入输出变量和运用的算子添加到Program中。
3.2 运用推理库猜测
获得静态图模型之后,咱们运用Paddle Inference进行猜测布置。Paddle Inference是飞桨的原生推理库,作用于服务器端和云端,供给高功能的推理才能。
Paddle Inference选用 Predictor 进行猜测。Predictor 是一个高功能猜测引擎,该引擎经过对核算图的剖析,完结对核算图的一系列的优化(如OP的交融、内存/显存的优化、 MKLDNN,TensorRT 等底层加速库的支撑等),可以大大提高猜测功能。另外Paddle Inference供给了Python、C++、GO等多语言的API,可以依据实际环境需求进行选择,为了便于演示这儿运用Python API来完结,其已在安装的Paddle包中集成,直接运用即可。运用 Paddle Inference 开发 Python 猜测程序仅需以下过程:
3.3 gradio进行交互项布置
gradio布置有以下过程:
- 将模型拷贝到本地,并按照接口要求封装好办法
import gradio as gr
def question_answer(context, question):
pass # Implement your question-answering model here...
gr.Interface(fn=question_answer, inputs=["text", "text"], outputs=["textbox", "text"]).launch(share=True)
-
将用户输入的context, question加载,并利用模型回来answer
-
回来到gradio布置的框内, 进行页面展现
-
生成公开链接
进入public URL验证
四、总结与展望
4.1 模型作用对比
ernie-2.0-large-en | bert-base-chinese | bert-wwm-ext-chinese | roberta-wwm-ext-large | |
---|---|---|---|---|
Epoch | ||||
1 | 34.64 | 56.62 | 59.04 | 62.56 |
2 | 35.68 | 57.84 | 59.94 | 63.29 |
3 | 34.98 | 57.12 | 59.67 | 62.97 |
-
上述表格得分为 F1 值
-
最开始运用ernie2.0-large-en模型,成果发现好像该模型并不是针关于中文的预练习模型,因而导致作用比较差。
-
bert根本模型较为良好,可以根本完结使命。
-
可以发现随着bert基线拓宽而成的bert-wwm-ext-chinese模型与改良bert后的roberta-wwm-ext-large模型,可以获得较好的作用,随之支付的价值便是模型的体积变大,并且练习速度变迟缓。
-
由于选用预练习 + 微调的办法,处理数据,因而Epoch过大极易发生过拟合,这也是咱们今后应该防止的,尽量做到手工微调。
4.2 项目展望
-
数据:
-
寻觅更多优质中医语料数据集,进行简略增强
-
选用回译等数据增强办法,从无到有的构建文本类似数据集
-
-
项目布置:
-
gradio再优化
-
aistudio沙盒打包
-
flask生成api,运用docker打包flask制造小运用
-
-
模型
- 将模型微调的几步Epoch成果保存,进行模型均匀操作
- 选用模型Bagging战略,将练习质量好的模型进行交融处理
- 探寻更多优质模型,针对使命,寻觅运用更多医疗数据集练习好的与练习模型
-
更多:
- 学习RocketQA等端到端问答模型,加上检索条件,在机器阅览了解根底上,制造完好的依据检索的问答系统,并可为后续学习依据生成的问答模型打下根底(PS:励志做个类似chatgpt的通用问答模型(bushi))
4.3 参考文献:
- zhuanlan.zhihu.com/p/121787628
- link.zhihu.com/target=http…
- aistudio.baidu.com/aistudio/pr…
- aistudio.baidu.com/aistudio/pr…
- zhuanlan.zhihu.com/p/374238080