参考系列文章:https://zhuanlan.zhihu.com/p/635152813
github链接:https://github.com/liguodongiot/llm-action
1 训练范式
下面这种instructive learning,在模型参数达到80亿后,会发生质的提升:
类似的还有手写prompt
然后可以推广到绿色的新任务上:
2 全量微调:fine-tuning
fine-tuning指的是预训练模型下游接新的MLP,然后全量参数微调。
例如2018年谷歌发布的BERT,只需要抛弃BERT原来的MLM,接上新的全连接层进行训练即可:
举个例子,使用如下数据:
from datasets import load_dataset
dataset = load_dataset("yelp_review_full")
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
加载训练参数类TrainingArguments,指定num_labels(自动添加MLP),并且要添加metrics
from transformers import TrainingArguments, Trainer
import numpy as np
from datasets import load_metric
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=5)
training_args = TrainingArguments(output_dir="test_trainer")
metric = load_metric("accuracy")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=small_train_dataset,
eval_dataset=small_eval_dataset,
compute_metrics=compute_metrics,
)
trainer.train()
对于GPT,也是在后面直接加一个MLP进行微调:
3 改变结构:prompt-learning
3.1 基本概念
现在比较流行的方法是prompt-learning,通过seq2seq的语言预测模型去完成任务,这样新的任务和原先Bert的任务一致(预测mask位置的词表分布 ),因此可以很有效的进行tuning。其范式如下,需要设计输入模版和标签映射:
我们使用Langchain等工具时,会用到template,用来制作prompt:
选模型的话,auto-regressive适用于mask在最后,而masked LM可以在任意位置设置mask。Encoder-decoder则两者皆可。
另外还要关注预训练模型的语料,比如Ernie等知识图谱类的预训练模型适用于做实体抽取等任务,使用金融/医学材料训练的大模型,更适用于各自相关领域的下游任务。
多模态也可以使用prompt-learning。例如图像编码后,可以跟文字使用同样的方式进行prompt learning。
3.2 zero-shot/few-shot
不用训练,直接给出任务描述(并且可以添加一些样例),然后使用prompt直接给出结果。
这种方式为何有效,至今仍是个迷。
3.3 template构造
3.4 Verbalizer构造
3.5 OpenPrompt工具包
4. 部分微调:delta-tuning
4.1 介绍
固定大部分参数,仅训练少量的参数来驱动大模型。下图是delta-tuning和fine-tuning的区别。
左边的fine-tuning中,每个任务都全量微调,得到一个新的模型,如果有100个任务,那么最终会给出100个数十G的大模型;但是在delta-tuning中,PLM参数大部分是固定的,每个任务只需要训练和记录有修改部分的参数(delta-object)即可。
delta-tuning分为以下三大类:增加额外参数(A)、选取一部分参数更新(S)、引入重参数化(R)。而在增加额外参数这类方法中,又主要分为类适配器(Adapter-like)方法和软提示(Soft prompts)两个小类。
4.2 OpenDelta工具包
可以使用枚举和正则表达式等方式指定要修改哪些模块:
使用时,只需要线attach,然后再detach即可。
5. Addition-based
5.1 Adapter Tuning:Transfomer中添加adapter模块
Adapter Tuning(论文:Parameter-Efficient Transfer Learning for NLP,2019 ),该方法设计了Adapter结构,并将其嵌入Transformer的结构里面,针对每一个Transformer层,增加了两个Adapter结构(分别是多头注意力的投影之后和第二个feed-forward层之后,Adapter本质就是一个双层MLP),在训练时,固定住原来预训练模型的参数不变,只对新增的 Adapter 结构和 Layer Norm 层进行微调,从而保证了训练的高效性。每当出现新的下游任务,通过添加Adapter模块来产生一个易于扩展的下游模型,从而避免全量微调与灾难性遗忘的问题。
下图左面是Adapter在transformer中的位置,右边的图是adapter内部的结构。Adapter通过引入0.5%~5%的模型参数可以达到不落后全量微调模型1%的性能:
还有一种结构,把adapter从主干网络降维后迁移到分支网络上,这样可以显著降低计算量:
5.2 Model/Prompt Tuning:添加前缀提示词
Prompt Tuning(论文:The Power of Scale for Parameter-Efficient Prompt Tuning),给每个任务定义了自己的Prompt,然后拼接到数据上作为输入,但只在输入层加入prompt tokens。
训练时冻结住pre-trained model,只训练prompt。Prompt token 的长度在20左右时的表现已经不错(超过20之后,提升Prompt token长度,对模型的性能提升不明显了)
下图表明,参数量增加时,prompt tuning接近model tuning的结果。
5.3 Prefix Tuning:prefix前添加MLP
Prefix Tuning(论文:Prefix-Tuning: Optimizing Continuous Prompts for Generation,2021)和prompt有些关系。在每一层的Prefix层前面加了MLP结构,训练完成后,只保留Prefix的参数。通过消融实验证实,只调整embedding层(也就是prompt-tuning)的表现力不够,将导致性能显著下降,因此,在每层都加了prompt的参数
在上图的例子中展示了不同模型添加前缀的方法:
- 针对自回归架构模型:在句子前面添加前缀,得到 z = [PREFIX; x; y],合适的上文能够在固定 LM 的情况下去引导生成下文(比如:GPT3的上下文学习)。
- 针对编码器-解码器架构模型:Encoder和Decoder都增加了前缀,得到 z = [PREFIX; x; PREFIX0; y]。Encoder端增加前缀是为了引导输入部分的编码,Decoder 端增加前缀是为了引导后续token的生成。
此外,还有实验对比位置对于生成效果的影响,Prefix-tuning要略优于Infix-tuning。其中,Prefix-tuning形式为 [PREFIX; x; y],Infix-tuning形式为 [x; INFIX; y]。
5.4 P-Tuning:添加prompt embedding+MLP层
P-Tuning(论文:GPT Understands, Too),该方法将Prompt转换为可以学习的Embedding层,并用MLP+LSTM的方式来对Prompt Embedding进行一层处理。P-Tuning对GPT的效果比较好。
对比prompt tuning,prompt和输入共享embedding层。
5.5 P-TuningV2:每一层都添加prompt
P-Tuning v2(论文: P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks),该方法在每一层都加入了Prompts tokens作为输入,而不是仅仅加在输入层。
P-Tuning v2可以看作是prefix tuning的升级,和prefix tuning的区别在于P-Tuning V2每一层的prompt是独立的,并不是由上一层计算得来。
6. Specification-based
6.1 BitFit:更新bias参数
BitFit(论文:BitFit: Simple Parameter-efficient Fine-tuning or Transformer-based Masked Language-models,2021)是一种稀疏的微调方法,它训练时只更新bias的参数或者部分bias参数。涉及到的bias参数有attention模块中计算query,key,value跟合并多个attention结果时涉及到的bias,MLP层中的bias,Layernormalization层的bias参数。
在Bert-Base/Bert-Large这种模型里,bias参数仅占模型全部参数量的0.08%~0.09%。但是通过在Bert-Large模型上基于GLUE数据集进行了 BitFit、Adapter和Diff-Pruning的效果对比发现,BitFit在参数量远小于Adapter、Diff-Pruning的情况下,效果与Adapter、Diff-Pruning想当,甚至在某些任务上略优于Adapter、Diff-Pruning。
另外,通过对比BitFit训练前后的参数,发现很多bias参数并没有太多变化(例如:跟计算key所涉及到的bias参数)。发现计算query和将特征维度从N放大到4N的FFN层(intermediate)的bias参数变化最为明显,只更新这两类bias参数也能达到不错的效果。
7. Reparameterization-based
7.1 Intrinsic prompt tuning
下图展示了如何从fine-tuning一步步转移到Intrinsic prompt tuning