课程链接《AI快车道PaddleNLP系列》、PaddleNLP项目地址、PaddleNLP文档
Taskflow文档、AI studio《PaddleNLP 一键预测功能 Taskflow API 使用教程》
百度同传:轻量级音视频同传字幕工具,一键开启,实时生成同传双语字幕。可用于英文会议、英文视频翻译等等。
Huggingface Hub
加载。Taskflow覆盖自然语言理解与生成两大场景:
词法分析:利用计算机对自然语言的形态(morphology) 进行分析,判断词的结构和类别等。简单而言,就是分词并对每个词进行分类,所以是包含了分词、词性标注、命名实体识别(NER)三个任务。NER相当于更细粒度的词性标注。
词法分析是自然语言处理基础且重要的任务。它是信息检索、信息抽取等应用的重要基础;可用于辅助其他自然语言任务,如句法分析、文本分类、问答对话等。
# 通过任务名word_segmentation实例化Taskflow分词任务
from paddlenlp import Taskflow
seg= Taskflow('word_segmentation')
seg('三亚是一个美丽的城市')
['三亚', '是', '一个', '美丽', '的', '城市']
from paddlenlp import Taskflow
seg= Taskflow('pos_tagging')
seg('三亚是一个美丽的城市')
[('三亚', 'LOC'), ('是', 'v'), ('一个', 'm'), ('美丽', 'a'), ('的', 'u'), ('城市', 'n')]
分词和词性标注都是使用BiGRUCRF模型训练的,输入文本经过双向GRU输出文本特征,然后以词性为标签进行训练。预测时使用CRF比直接softmax预测更准。
Taskflow(NER)
:基于解语框架的命名实体识别ner= Taskflow('ner')
ner('美人鱼是周星驰执导的电影')
[('美人鱼', '作品类_实体'),
('是', '肯定词'),
('周星驰', '人物类_实体'),
('执导', '场景事件'),
('的', '助词'),
('电影', '作品类_概念')]
传统NER方案有一些不足,比如:
PaddleNLP - 解语:解语(Text to Knowledge)是首个覆盖中文全词类的知识库(百科知识树)及知识标注框架,拥有可描述所有中文词汇的词类体系、中文知识标注工具集,以及更适用于中文挖掘任务的预训练语言模型。
1. 示例
文本纠错就是对语法错误的句子进行纠正。示例如下:
corrector= Taskflow('text_correction')
corrector(['人生就是如此,经过磨练才能让自己更加拙壮','遇到逆竟时,我们必须勇于面对'])
[{'source': '人生就是如此,经过磨练才能让自己更加拙壮',
'target': '人生就是如此,经过磨练才能让自己更加茁壮',
'errors': [{'position': 18, 'correction': {'拙': '茁'}}]},
{'source': '遇到逆竟时,我们必须勇于面对',
'target': '遇到逆境时,我们必须勇于面对',
'errors': [{'position': 3, 'correction': {'竟': '境'}}]}]
2. 文本纠错方案:
针对文本纠错任务,PaddleNLP使用的是2021年提出的专门针对中文文本纠错的模型ERNIE-CSC
。
Taskflow
句法分析任务,使用了百度建造的DuCTB1.0
数据库,是目前最大的中文依存句法数据库。
2. 模型方案
Taskflow
句法分析使用了Deep Biaffine Attention和百度依存句法分析工具DDParser。
DDParser训练数据不仅覆盖了多种输入形式的数据,如键盘输入query、语音输入query,还覆盖了多种场景的数据,如新闻、论坛。该工具在随机评测数据上取得了优异的效果。同时,该工具使用简单,一键完成安装及预测。
BiLSTM
、MLP
、Biaffine Attention
三个模块之后得到文本特征BiLSTM
替换为ERNIE
之后,模型精度从91.7%提升到95.6%,但是推理速度会有所下降,可以自行选择。from paddlenlp import Taskflow
ddp= Taskflow('dependency_parsing',model='ddparser-ernie-1.0')
ddp('百度是一家高科技公司')
[{'word': ['百度', '是', '一家', '高科技', '公司'],
'head': [2, 0, 5, 5, 2],
'deprel': ['SBV', 'HED', 'ATT', 'ATT', 'VOB']}]
定义:情感分析又称意见挖掘,是对带有情感色彩的主观性文本进行分析、处理、归纳和推理的过程,具体来说,对于给定的主观文本,输出如下五元组:
示例:
from paddlenlp import Taskflow
senta= Taskflow('sentiment_analysis')
senta('昨天我买了一台新的iphone手机,它的触摸屏做的非常精致酷炫')
[{'text': '昨天我买了一台新的iphone手机,它的触摸屏做的非常精致酷炫',
'label': 'positive',
'score': 0.969430685043335}]
from paddlenlp import Taskflow
# 智能问答
qa=Taskflow('question anwser')
qa('中国国土面积有多大')
[{'text': '中国国土面积有多大', 'answer': '960万平方公里。'}]
# 智能写诗
portry=Taskflow('poetry_generation')
portry('深山不见人')
[{'text': '深山不见人', 'answer': ',明月来相照。'}]
# 文生图
text_to_image = Taskflow("text_to_image", model="CompVis/stable-diffusion-v1-4")
image_list = text_to_image('"In the morning light,Chinese ancient buildings in the mountains,Magnificent and fantastic John Howe landscape,lake,clouds,farm,Fairy tale,light effect,Dream,Greg Rutkowski,James Gurney,artstation"')
文生可选参数:
model
:可选,默认为pai-painter-painting-base-zh。num_return_images
:返回图片的数量,默认为2。特例:disco_diffusion模型由于生成速度太慢,因此该模型默认值为1。Disco Diffusion-2.0-base-zh
模型,支持中文:
# 注意,该模型生成速度较慢,在32G的V100上需要10分钟才能生成图片,因此默认返回1张图片。
text_to_image = Taskflow("text_to_image", model="disco_diffusion_ernie_vil-2.0-base-zh")
image_list = text_to_image("一幅美丽的睡莲池塘的画,由Adam Paquette在artstation上所做。")
for batch_index, batch_image in enumerate(image_list):
for image_index_in_returned_images, each_image in enumerate(batch_image):
each_image.save(f"disco_diffusion_ernie_vil-2.0-base-zh-figure_{batch_index}_{image_index_in_returned_images}.png")
支持复现生成结果 (以Stable Diffusion
模型为例)
from paddlenlp import Taskflow
text_to_image = Taskflow("text_to_image", model="CompVis/stable-diffusion-v1-4")
prompt = [
"In the morning light,Chinese ancient buildings in the mountains,Magnificent and fantastic John Howe landscape,lake,clouds,farm,Fairy tale,light effect,Dream,Greg Rutkowski,James Gurney,artstation",
]
image_list = text_to_image(prompt)
for batch_index, batch_image in enumerate(image_list):
# len(batch_image) == 2 (num_return_images)
for image_index_in_returned_images, each_image in enumerate(batch_image):
each_image.save(f"stable-diffusion-figure_{batch_index}_{image_index_in_returned_images}.png")
# 如果我们想复现promt[0]文本的第二张返回的结果,我们可以首先查看生成该图像所使用的参数信息。
each_image.argument
# {'mode': 'text2image',
# 'seed': 2389376819,
# 'height': 512,
# 'width': 512,
# 'num_inference_steps': 50,
# 'guidance_scale': 7.5,
# 'latents': None,
# 'num_return_images': 1,
# 'input': 'In the morning light,Chinese ancient buildings in the mountains,Magnificent and fantastic John Howe landscape,lake,clouds,farm,Fairy tale,light effect,Dream,Greg Rutkowski,James Gurney,artstation'}
# 通过set_argument设置该参数。
text_to_image.set_argument(each_image.argument)
new_image = text_to_image(each_image.argument["input"])
# 查看生成图片的结果,可以发现最终结果与之前的图片相一致。
new_image[0][0]
$HOME/.paddlenlp
下,可以在任务初始化的时候通过home_path
自定义修改保存路径from paddlenlp import Taskflow
ner = Taskflow("ner", home_path="/workspace")
# 精确模式模型体积较大,可结合机器情况适当调整batch_size,采用批量样本输入的方式。
seg_accurate = Taskflow("word_segmentation", mode="accurate", batch_size=32)
# 批量样本输入,输入为多个句子组成的list,预测速度更快
texts = ["热梅茶是一道以梅子为主要原料制作的茶饮", "《孤女》是2010年九州出版社出版的小说,作者是余兼羽"]
seg_accurate(texts)
其它用法请参考Taskflow文档。
课程《自然语言处理中的小样本学习》、PaddleNLP小样本学习项目地址、Prompt Learning、《提示学习:Prompt API》
Few-Shot Learning
旨在研究如何从少量有监督的训练样本中学习出具有良好泛化性的模型,对训练数据很少或监督数据获取成本极高的应用场景有很大价值(比如每个类只有4/8/16个样本)。小样本学习和人类的学习场景是很相似的,可以认为是人类学习的一种模拟。只有搞定了小样本学习,才能说模型具有了一定的人类智能。Pre-train+Fine-Tune
- 预训练模型综述:《Pre-trained Models for Natural Language Processing: A Survey》
(2020.3.18)
自从BERT
发布以来,使用大规模预训练模型PTMs
(Large-scale pre-trained models )进行微调已经成为了NLP领域各种任务的标准解决方案,即Pre-train+Fine-Tune
范式。这种范式在数据密集场景大获成功,比如权威语言理解评测数据集GLUE
上,排名前50都是这种范式的模型。
prompt
综述:《Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing》(2021.7.28)
- 论文笔记:《Pre-train, Prompt, and Predict: 自然语言处理中prompting方法总结》、《Prompt统一NLP新范式Pre-train, Prompt, and Predict》
既然标准范式如此成功,那么为什么要提出Prompt Tuning呢?
主要就是因为Pre-train+Fine-Tune
的标准范式在小样本场景效果不好,很容易过拟合。由此,后续研究者提出了新的范式:PromptTuning
。PromptTuning
在小样本学习大放异彩,取得了更好的效果。
那到底什么是prompt呢?
预训练语言模型(比如BERT
),含有很全面的语言知识,并没有针对特定的下游任务,所以直接迁移学习的时候,可能模型不够明白你到底要解决什么问题,要提取哪部分知识,最后效果也就不够好。标准范式pre-train, fine-tune
转变为新范式pre-train, prompt, and predict
之后,不再是通过目标工程使语言模型(LMs)适应下游任务,而是在文本提示(prompt)的帮助下,重新制定下游任务,使其看起来更像原始LMs训练期间解决的任务。 所以说,Prompt
的核心思想,就是通过人工提示,把预训练模型中特定的知识挖掘出来。
传统的监督学习训练模型接收输入 x x x,并将输出 y y y预测为 P ( y ∣ x ) P(y|x) P(y∣x) ,
prompt-based learning
则是直接建模文本概率的语言模型。为了使用这些模型执行预测任务,使用模板将原始输入 x x x修改为具有一些未填充槽的文本字符串提示 x ′ {x}' x′,然后使用语言模型重构未填充信息以获得最终字符串 ,从中可以导出最终输出 。
下面就介绍新范式的三种主要算法:PET、P-Tuning、EFL。
论文《Exploiting Cloze Questions for Few Shot Text Classification and Natural Language Inference》
(2020.1.21
)
1. PET(PatternExploitingTraining)核心思想:
比如上新闻分类示例。如果是传统做法就是一个多分类任务。改用PET
方法之后,就人工设计一个PET模板下面是[MASK][MASK]新闻。
,然后把这个模板和原句子上拼接起来作为一个整体,让模型预测被MASK掉的字符。这样就将一个分类任务转变为一个MLM任务,和模型预训练的任务是一样的。而且通过这种人工设计的模板(明确的提示要进行新闻分类),可以充分挖掘出预训练模型的知识,做出更精准的预测。
2. 模型效果
下面是 RoBERTa (large)使用PET方法和传统方法在四个数据集上的精度对比:
画出来就是:
3. PET优缺点
优点:
局限性:
论文《GPT Understands, Too》
(2021.5.18)
1. 核心思想
针对PET方法中模板需要手工设计,精度不稳定且模板无法优化的问题, P-Tuning提出了可学习的伪模板。例如下图例子中的可学习模板——[u1][u2]...[MASK]...[uN]
。
2. 模型效果:普遍由于PET
下面是BER-large-cased
和GPT2-medium
两个模型在各种NLP任务上,微调、PET、PET微调和 P-Tuning的效果对比:
画出一部分就是:
蕴含任务:判断下面两句话的逻辑关系。
论文《Entailment as Few-Shot Learner》
(2021.4.29)
1. 核心思想
EFL
(Entailment as Few-Shot Learner):所有目标任务转化为2分类蕴含任务(Yes/No),不仅可以处理更多的任务,而且二分类问题大大简化了模型预测的难度。示例如下:
由于这条新闻是体育新闻,所以只有第一句话标签应该是1,其它都是0。也就是模型只需要判断每句话的逻辑关系是对的还是错的。
2. 模型效果
下面是RoBERTa-large
模型在7个任务上使用Fine-tuning
、PET
(LM-BFF )和EFL
方法的效果对比:
R-Drop
策略:显著提升模型效果(附代码示例)
- 论文:《R-Drop: Regularized Dropout for Neural Networks》(NeurIPS 2021 ),代码
- 论文笔记《R-Drop:提升有监督任务性能最简单的方法》
SimCSE(EMNLP 2021.4)通过简单的“Dropout两次”来构造正样本进行对比学习,达到了无监督语义相似度任务的全面SOTA,微软在六月底发布的论文《R-Drop: Regularized Dropout for Neural Networks》提出了R-Drop(
Regularized Dropout),它将“Dropout两次”的思想用到了有监督任务中,在神经机器翻译、摘要生成、语言理解、和图像分类等多个任务中被广泛证明有效,是一种通用的优化策略。
R-Drop
算法核心思想:通过drop两次这种隐式的数据增强,引入KL距离约束来提升模型效果(KL散度就是衡量两个分布之间的差异,在这里是减少了模型参数的自由度)
为了避免模型过拟合,我们通常使用诸如 Dropout 等较为成熟的正则化策略,而本文作者是将模型进行两次Dropout。具体来说,将同一个模型,同样的输入,进行两次Dropout 前向传播。由于Dropout的存在,同一个输入x会得到两个不同的输出特征,相当于进行了一次隐式的数据增强,效果一般是要更好的。
同时我们得到两个不同但差异很小的概率分布 P 1 ( y ∣ x ) P_{1}(y|x) P1(y∣x)和 P 2 ( y ∣ x ) P_{2}(y|x) P2(y∣x),并在这两个分布之间加了一个约束 D K L ( P 1 ∣ ∣ P 2 ) D_{KL}(P_{1}||P_{2} ) DKL(P1∣∣P2),使得这两个分布要尽可能的相似。通过在原来的交叉熵损失中加入这两个分布的KL散度损失,来共同进行反向传播,然后更新参数。(下面这张图右侧表示模型经过两次dropout之后相当于得到了两个网络)
模型的训练目标包含两个部分,一个是两次输出之间的KL散度,如下:
在训练过程中,为了节省训练时间,并不是将同一个输入输入两次,而是将输入句子复制一遍,然后拼接到一起,即 [ x , x ‘ ] [x,x`] [x,x‘] ,这样就相当于将batch size扩大了一倍,这个可以节省大量的训练时间,当然相比于原始的模型,这个会使得每个step的训练时间增加了,因为模型的训练复杂度提高了,所以需要更多的时间去收敛。训练如下:
1. 在GLUE稳定涨点
下面是BERT-base
和RoBERTa-Large
使用R-Drop策略后的效果对比,第一行是平均结果,可见使用这个策略后模型精度明显提升(如果是RoBERTa-Large从头预训练一次,大概要花6万美元)。
可视化:
2. Few-CLUE上明显涨点
另外,在P-Tuning、EFL、PET三种小样本算法基础上加入R-Drop
策略,Few-CLUE(中文小样本权威评测基准)精度明显提升。。
RDropLoss API
下面代码来自示例PaddleNLP/examples/few_shot/pet/pet.py,只需要加入三行代码就可以使用Rdrop:
mlm_loss_fn = ErnieMLMCriterion()
rdrop_loss = ppnlp.losses.RDropLoss() # 第一行导入
for epoch in range(1, args.epochs + 1):
model.train()
for step, batch in enumerate(train_data_loader, start=1):
......
prediction_scores = model(input_ids=src_ids,token_type_ids=token_type_ids,masked_positions=new_masked_positions)
......
if args.rdrop_coef > 0:
# 第二行,让模型进行第二次dropout得到另一个输出
prediction_scores_2 = model(input_ids=src_ids,token_type_ids=token_type_ids,
masked_positions=new_masked_positions)
ce_loss = (mlm_loss_fn(prediction_scores, masked_lm_labels) +
mlm_loss_fn(prediction_scores_2, masked_lm_labels)) * 0.5
# 第三、四行,计算两次结果的rdrop_loss,将这个loss加到总的损失中。
kl_loss = rdrop_loss(prediction_scores, prediction_scores_2)
loss = ce_loss + kl_loss * args.rdrop_coef
本质上来说,R-Drop与MixUp、Manifold-MixUp和Adversarial Training(对抗训练)一样,都是一种数据增强方法,这种方法可以套用到任何有监督/半监督的训练中,通用且实践意义很强,尤其在小样本学习场景中用的非常多。
另外,当R-Drop应用于微调大规模预训练模型(例如ViT、RoBERTa大型和BART)时,产生了显著的改进甚至超过了设计Transformer 的高级变体。
下面是一个电商平台评论的情感分类任务EPRSTMT,只有2个类别,每类16个样本 ,一共32个。在百度的ERNIE-1.0模型上使用各种范式的精度对比(Standard Finetune是标准的预训练+微调范式):
基于PaddleNLP Few-Shot可以轻松参加FewCLUE并获得好成绩。
PromptTuning新范式与StandardFine-tune范式通用技术结合有巨大潜力,比如数据增强(R-Drop)、知识蒸馏、
- PaddleNLP:FastGeneration 预测、《FastGeneration》、FasterTransformer API说明、《文本生成高性能加速教程》、 FasterTransformer完整源代码
- NVIDIA:FasterTransformer项目地址、视频《Faster Transformer介绍》
- 原始Transformer代码可参考我之前帖子《Transformer代码解读(Pytorch)》
Transformer计算量主要在paddle.nn.MultiHeadAttention部分的自注意力计算上。
由此可见,原生的self-attention实现,kernel计算粒度很细,而且还会引入一些功能性算子,导致整个计算模块中对kernel调用次数很多,所以需要优化(融合kernel,减少算子调用次数,减少GPU空闲时间)。
下面先放出FasterTransformer优化后,和Transformer的Nsight profiler
对比图,可见优化后CUDA kernel
执行粒度更紧密。
FasterTransformer(NVIDIA)算法结构和原版Transformer基本一致,但是从模型框架角度对Transformer进行了加速,主要方法有:
下图Faster Transformer结构中:
encoder
:下图左侧部分,由self attention+FFNN层组成decoder
:右侧黄色部分,由self attention+encoder-decoder-cross attention+FFNN三部分组成decoding
:右侧整个蓝色部分是Faster Transformer中提出的decoding
结构,包括embedding层、position encoding、decoder、log probability(计算logist概率分布)、解码策略层(Beam Search等)。 GEMM
(GeneralMatrixMultiplication):经过cuBLAS高度优化的一个用于矩阵乘的kernel,无需再做优化。剩下的就是对 除去GEMM的部分做最大限度的融合。FasterTransformer就是将两个GEMM之间的计算尽可能的融合成一个kernel。下面是FasterTransformer的encoder(虚线框部分)计算示意图,其中每个蓝色方块都是一个GEMM kernel。
FasterTransformer encoder
有6个(或8个,因为有三种bias kernel)GEMM kernel和6个自定义CUDA kernel,总共12个。而PaddlePaddle实现原生的paddle.nn.TransformerEncoderLayer需要调用38个算子,是做encoder融合之后的三倍。融合之后的encoder层,在某些配置下计算性能提升超过100%。decoder比encoder多了一个encoder-decoder-cross-attention层,其它结构都一样,所以优化策略也一样,都是讲GEMM之外的kernel尽可能的融合。这里以GPT举例,图中虚线框就是decoder部分:
pre-normalization
,PaddleNLP扩展使之同样支持post-normalization
。在transformer系列模型里面,这两种结构都能看到。1. 解码过程
decoing模块基于log probability层输出一个概率分布,然后使用解码策略输出最后的结果。下面详细介绍一下解码过程
下图中,解码时以[BOS]标识符作为解码的开始。[BOS]在第一个self attention
层的输出,加上encoder模块最后层的输出,一起输入encoder-decoder-cross-attention
层(图中粉红色)计算自注意力做交互,然后经过FFNN
层,最后得到一个softmax之后的输出概率,这个概率代表输出词表的概率分布情况。而解码策略,就是基于这个词表的概率分布,选择合适的单词作为输出。这个输出加入到下一个时间步的输入中,循环此过程,直至解码出[EOS],表示句子解码完成。
所以解码是一个循环过程,每次带入之前的解码输出结果,完成当前步的解码预测。而解码策略通过所有词的预测概率分布,选择最终的输出结果。
2. 解码策略
Greedy decoding/search
):
Beam Search
):每个时间步保留beam_size
(束宽)个最高概率的输出词,然后在下一个时间步,重复执行这个过程。最终,返回top_beams
个候选结果,并从中选择最优的输出结果。
T2T Beam Search
:提出了Alive Seq和Finished Seq的概念。假设beam_size=2
Topk Seq
。Alive Seq
和Finished Seq
两个候选序列,大小都为beam_size;并保证Alive Seq
中没有终止符[EOS],可以继续生成。Alive Seq
中最高得分的结果分支,是否已经低于Finished Seq
中的结果分支。如果是,则提前结束循环(Early Finish),否则Alive Seq
会代入到下一步的循环迭代中。
对比Beam Search和T2T Beam Search:
Beam Search
:每当某个分支生成终止符[EOS],则解码的分支会少一个,所以每个时间步生成的结果可能小于beam_size
T2T Beam Search
:Alive Seq
中是不包含终止符的,所以每个时间步都生成beam_size
个结果,其搜索空间更大,在一些任务中会有更好的效果。 在实际测试transformer过程中,会发现encoder
时间占比约10%
,而decoding
时间占比约90%
,比encoder
多得多,所以需要进一步优化。
除了融合kernel之外,Faster Transformer还做了一些其它的性能优化。
Faster Transformer优化总结:
__expf
替换expf
(前者是后者的数学近似),损失很小的精度但是有更快的速度(经过测试不影响一些机器翻译和对话的结果);classFasterTransformer(src_vocab_size, trg_vocab_size, max_length, num_encoder_layers,
num_decoder_layers, n_head, d_model, d_inner_hid, dropout,
weight_sharing,attn_dropout=None, act_dropout=None, bos_id=0,
eos_id=1, pad_id=None, decoding_strategy='beam_search', beam_size=4,
topk=1, topp=0.0, max_out_len=256, diversity_rate=0.0,
decoding_lib=None,use_fp16_decoding=False,alpha=0.6
enable_faster_encoder=False, use_fp16_encoder=False, rel_len=False)
定义好一些超参数,就可以得到FasterTransformer的一个实例模型。其中decoding_strategy
参数是字符串格式,可以是beam_search
、beam_search_v2
(也就是T2T beam search)、topk_sampling
和topp_sampling
。
Just InTime
自动编译:FasterTransformer本身是基于C++和CUDA C实现,在实例化时会检测对应的路径下是否有对应的动态库,如果没有的话,会进行自动编译(自动编译FasterTransformer、PaddlePaddle自定义op、载入编译的动态库)。下面是编译的部分截图:
第二、三列是分别使用fp32和fp16时
FasterTransformer
的性能(ms/batch),第四列是原生实现的transformer
。
PaddleNLP通过PaddlePaddle自定义op(算子)的形式接入了FasterTransformer,并支持多种transformer模型,用于各种NLP任务,详见《文本生成高性能加速》。
PaddleNLP用法: