我是一个深度学习爱好者,目前对自然语言处理感兴趣,热衷于了解一些人工智能中的数学推导和经典论文复现,正在成长的“小趴菜”一枚,在PPDE指导计划中,创作了中医文献阅读理解项目,下面将由我介绍在项目创作过程中的一些思考。
中医文献阅读理解是一个将自然语言处理技术应用于中医药领域的任务,其目标是使用能够读取、理解和回答中医药知识的模型普及和传播中医药知识。该任务需要建立一个大规模的中医药语料库,并使用自然语言处理技术对语料库进行处理,提取关键信息并建立模型。
模型的输入可以是一个中医药相关的问题,模型的输出则是问题的答案。例如,如果输入的问题是“什么是中医证候学?”,则模型的输出可能是“中医证候学是中医药的一个重要理论,它旨在通过观察患者的症状和体征,推断患者所患疾病的特点和发展趋势,为临床治疗提供理论指导。”
此外,基于该任务还可以开发更多关于中医药知识的小应用,如中医药问诊系统、中医药辨证论治辅助工具等,以帮助更多人了解和应用中医药知识。
https://aistudio.baidu.com/aistudio/projectdetail/5166465
本项目标注数据源来自四个中医药领域文本,包括《黄帝内经翻译版》《名医百科中医篇》《中成药用药卷》《慢性病养生保健科普知识》。共计5000篇文档,每篇文档人工标注产生 1~4对(问题, 答案),共标注13000对(文档、问题、答案)。
{
2
"id": 98,
3
"text":
"黄帝道:什麽叫重实?岐伯说:所谓重实,如大热病人,邪气甚热,而脉象又盛满,内外俱实,便叫重实",
4
"annotations": [
5 {
6
"Q":
"重实是指什么?",
7
"A":
"所谓重实,如大热病人,邪气甚热,而脉象又盛满,内外俱实,便叫重实"
8 },
9 {
10
"Q":
"重实之人的脉象是什么样?",
11
"A":
"脉象又盛满"
12 }
13 ],
14
"source":
"黄帝内经翻译版"
15 }
随着中医文献资源的不断增加, 如何高效地阅读和理解中医文献已经成为了一个重要课题。为了解决这个问题,我们需要利用一些自然语言处理、机器学习、可视化等相关技术。本项目的主要技术点包括:
-
构建中医MRC数据集,使用
PaddleNLP
搭建、训练并调优阅读理解模
型;
-
采用多种内置模型进行实验,最终确定使用
Roberta阅读理解模型
;
-
动转静,完成静态图的推理,并用
Gradio
实现可交互的部署。
PaddleNLP是飞桨自然语言处理模型库,具备易用的文本领域API、丰富的预训练模型、多场景的应用示例和高性能分布式训练与部署能力,旨在提升开发者在文本领域的开发效率。
阅读理解本质是一个答案抽取任务,PaddleNLP对于各种预训练模型已经内置了对于下游任务-答案抽取的Fine-tune网络。以PaddleNLP中的RoBERTa模型为例,将模型Fine-tune完成答案抽取任务。
答案抽取任务的本质就是根据输入的问题和文章,预测答案在文章中的起始位置和结束位置。RoBERTa模型主要是在BERT基础上做了几点调整:
在BERT中,mask是在数据预处理过程中完成,这意味着,在每个epoch中,训练的每个句子的mask是固定的、静态的。RoBERTa提出一种动态mask的方法,将训练数据每个句子复制10次,采用不同的mask方法,这样在epoch为40的时候,平均每条mask的序列会出现4次。
对中文数据的处理中,预训练模型RoBERTa使用BPE (Byte-Pair Encoding,字节对编码)处理文本顺序。官方词表包含5万多的byte级别的token。其中,merges.txt文件存储了所有的token,vocab.json文件是一个byte到索引的映射,通常频率越高的byte索引越小。BPE编码转换的过程是,先将输入的所有tokens转化为merges.txt中对应的byte,再通过vocab.json中的字典进行byte到索引的映射。Tokenizer的作用是将原始输入文本转化成模型可以接受的输入数据形式。
如何将你的AI算法迅速分享给别人让对方体验,一直是一件麻烦事儿。Gradio算法可视化部署可以自动生成页面,形成交互,改动几行代码就能完成项目,支持自定义多种输入输出,支持生成可外部访问的链接,从而实现分享。
本项目基于PaddlePaddle2.0.2与PaddleNLP2.0.7版本。关于如何下载此版本可以点击飞桨官网,查看下载方式。更加推荐在AI Studio上一键运行哦。
https://aistudio.baidu.com/aistudio/projectdetail/5166465
阅读理解的方案如上图,query表示问句,一般是用户的提问,passage表示文章,query的答案要从passage里面抽取出来。query和passage经过数据预处理,生成其id形式的输入,然后经过RoBERTa模型,得到答案的位置,从而得到相应的answer。
具体的任务定义为:给定问题q和一个篇章p,根据篇章内容,给出该问题的答案a。数据集中的每个样本,是一个三元组<q, p, a>,例如:
篇章 p:
草菇荠菜汤鲜嫩清香、色味搭配,具有清热和脾、益气平肝、降糖降压等功效,是夏季解暑祛热的良食佳品…….
参考答案 a:
草菇荠菜汤鲜嫩清香、色味搭配,具有清热和脾、益气平肝、降糖降压等功效。
-
-
-
lannotations:
每个段落拥有一个annotations,其中包含1~4对(问题、答案)
-
-
将上述数据进行简单地数据清洗以及格式(sqaud格式)转换操作。为了方便读取,具体格式如下:
{
'id':
'xx',
'title':
'xxx',
'context':
'xxxx',
'question':
'xxxxx',
'answers': [
'xxxx'],
'answer_starts': [xxx]
}
# 参数配置
# 训练过程中的最大学习率
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)
由于BertForQuestionAnswering模型将BERT模型的sequence_output拆成start_logits和end_logits,所以阅读理解任务的loss也由start_loss和end_loss组成。答案起始位置和结束位置的预测可以分成两个分类任务,我们需要自己定义损失函数。
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
-
从dataloader中取出一个
batch data
;
-
将batch data喂给
model
,做前向计算;
-
-
每训练一个epoch时,程序通过evaluate()调用paddlenlp.metric.squad中的squad_evaluate()返回评价指标,compute_predictions() 评估当前模型训练的效果,用于生成可提交的答案。
模型训练完成之后,我们实现模型的预测部署。虽然训练阶段使用的动态图模式有诸多优点,包括Python风格的编程体验(使用RNN等包含控制流的网络时尤为明显)、友好的debug交互机制等。但Python动态图模式无法更好地满足预测部署阶段的性能要求,同时也限制了部署环境。
高性能预测部署需要静态图模型导出和预测引擎两方面的支持。
基于静态图的预测部署要求将动态图的模型转换为静态图形式的模型(网络结构和参数权重)。飞桨静态图形式的模型(由变量和算子构成的网络结构)使用Program来存放,Program的构造可以通过飞桨的静态图模式说明,静态图模式下网络构建执行的各API会将输入输出变量和使用的算子添加到Program中。
获得静态图模型之后,我们使用Paddle Inference进行预测部署。Paddle Inference是飞桨的原生推理库,作用于服务器端和云端,提供高性能的推理能力。使用 Paddle Inference 开发 Python 预测程序仅需以下步骤:
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;
-
-
总结与展望
本项目尝试了12L768H的bert-base-chinese、bert-wwm-ext-chinese和24L1024H的roberta-wwm-ext-large模型,效果如下,可以看到roberta-wwm-ext-large模型能够取得较好的效果,随之付出的代价就是模型的体积变大,并且训练速度变迟缓。除此之外,如果epoch过大,极易发生过拟合,可以调整学习率等参数避免过拟合。
与roberta-wwm-ext-large相比,同规模的ERNIE 1.0-Large-cw、ERNIE 2.0-Large-zh在CLUE各项任务上平均精度更高,后续计划尝试ERNIE系列模型。
https://github.com/PaddlePaddle/PaddleNLP/tree/develop/model_zoo/ernie-3.0
https://aistudio.baidu.com/aistudio/projectdetail/2017189
寻找更多优质中医语料数据集,进行简单增强,采用回译等数据增强方法,从无到有的构建文本相似数据集。
利用AI Studio部署趣味项目或将模型打包后使用flask框架,配合前端页面制作体验更好的小应用。
将模型微调的几步epoch结果保存,进行模型平均操作。采用模型Bagging策略,将训练质量好的模型融合处理,探寻更多优质模型。针对任务,使用更多医疗数据集训练好的预训练模型。
学习RocketQA等端到端问答模型,加上检索条件,在机器阅读理解基础上制作完整的基于检索的问答系统,并可为后续学习基于生成的问答模型打下基础。(PS:励志做个类似ChatGPT的通用问答模型)
https://github.com/PaddlePaddle/PaddleNLP
[1]
https://aistudio.baidu.com/aistudio/projectdetail/2017189
[2]
https://aistudio.baidu.com/aistudio/projectdetail/4113678