大模型微调技术 --> P-Tuning v1和 P-Tuning v2
P-Tuning 是一种通过引入可学习的 提示 向量来增强预训练语言模型能力的技术,属于提示学习的一种。
1.背景
GPT 在 NLP 领域经过 finetuning 之后很难击败 BERT,主要是因为现在预训练模型的方法有很多种(主要是 MLM),但在 finetune 的时候,常常和预训练不一致。于是 P-tuning: GPT Understands, Too 这篇论文被提出。
2.P-Tuning v1
1.摘要
手动的离散提示通常导致一个不稳定的性能——比如,在提示中改变一个词可能导致性能大幅度下降。于是提出了新颖的方法 —— P-Tuning,它采用连续的提示 embedding 与离散提示 结合在一起。P-Tuning 不仅能够通过最小化各种离散提示之间的差距来稳定训练,还能在很多基准测试中提高性能。
2.介绍
预训练模型一般都可以通过手动编写提示模版进一步的提升性能。
但是,手动的离散提示有很大程度的不稳定性。
如图所示,仅仅改变一个单词,就导致了准确率大幅度的变化,但是 P-Tuning 基本稳定。
为了减少不稳定性,我们利用可训练的连续的提示 embeddings 与离散提示串联(和离散提示结合起来)。
具体来说,给定离散提示作为输入,P-Tuning 将 连续的提示嵌入 和 离散的提示 token 连接起来,并将它们作为 LM 的输入。通过反向传播更新连续提示以优化任务目标。
直觉上,连续的提示将一定程度的可学习性融入到了输入中,这抵消了离散提示中微小变化的影响。
3.方法
-
传统方法
Prompting 被提出用一种离散 token 的形式。每个体式被描述成一个模板 T = { [ D 0 : i ] , x , [ D ( i + 1 ) : j ] , y , [ D ( j + 1 ) : k ] } T=\{[D_{0:i}], x, [D_{(i+1):j}], y, [D_{(j+1):k}]\} T={[D0:i],x,[D(i+1):j],y,[D(j+1):k]},他能组织标注数据成文本 token 的序列,以便任务能被重新阐述为一个文本填空问题。
“The capital of [INPUT] is [LABEL].” → “The capital of Britain is [MASK].”
离散的提示和输入一起被映射成输入的嵌入。
但是由上述表格发现,这样不稳定。所以提出了 P-Tuning ,使用连续的提示嵌入去提升和稳定 prompting
-
P-Tuning
定义 [ P i ] [P_i] [Pi] 是第 i 个连续提示嵌入。P-Tuning 的提示模版变成了如下
T = { [ P 0 : i ] , x , [ P ( i + 1 ) : j ] , y , [ P ( j + 1 ) : k ] } T = \{[P_{0:i}], x, [P_{(i+1):j}], y, [P_{(j+1):k}]\} T={[P0:i],x,[P(i+1):j],y,[P(j+1):k]}
P-Tuning 利用额外的嵌入函数 f : [ p i ] → h i f:[p_i] → h_i f:[pi]→hi 将模版映射到如下。
最好,更新 embedding { P i } i = 1 k \{P_i\}^k_{i=1} {Pi}i=1k 去优化任务损失函数。
-
Prompt Encoder
在上述框架中,我们使用映射函数 f 将可训练嵌入 {P} 映射到模型输入 {h}。
我们的直觉是,与使用独立的可学习嵌入式相比,使用映射函数可以更方便地模拟不同提示嵌入式之间的依赖关系。
-
**总结(借鉴该链接)**
Prompt 其实就是将微调任务换成了填空任务,来贴近 MLM 任务,效果很取决于模版好不好。
作者为了解决这个问题,提出了 P-Tuning ,把原本由模版产生的提示 token ,改成用 embedding 的形式,那这些 embedding 怎么来的呢?其实就是直接创造了新的词 [prompt1] - [prompt10] ,然后输入一个神经网络。这些词原本的预训练模型也没有看过,也不在乎他是不是自然语言,因为我们希望的是利用这些模版学习到怎样完成下游任务,而这些模版就是经过参数化后,找到的一群参数,至于它本身是什么样的字符或符号并不需要太在意,背后学到的参数才是最重要的。
不同于 finetune 的方法差别在哪里呢?P-Tuning 让预训练模型参数固定,只学习橙色部分的 prompt token,这样做的原因是利用之前学习到的大量文本中的知识,达到更好的效果。而 finetune 要调整整个模型参数,常常因为预训练模型和下游任务不一样,导致灾难性遗忘问题,打乱原本学习到的参数。
4.实验结果
-
知识探究准确率,P-Tuning 比 手动提示和离散提示的方法都好
-
P-Tuning 在 BERT 和 GPT 中表现优异,看数据,貌似对于GPT系列提升更大
3.P-Tuning V2
在实践中,V2 比 V1 更加常用
1.引言
当预训练语言模型的参数很少时,P-tuning效果显然很差,而只有当参数规模达到100亿时,才勉强达到Fine-tuning效果。
传统的方法是 fine-tune ,很费内存。
Prompting 虽然不需要训练,但是离散的提示可能导致较低的性能(与 fine-tune 相比)
prompt tuning,他思想是只微调连续的提示,即增加可训练的连续嵌入在原始的输入序列列,只有连续的提示嵌入会被更新。
尽管P-Tuning 改善了很多任务,但是他仍然不如 fine-tune ,尤其是低于 10B 的模型中。
而且在一些困难的任务中,性能可能更差
基于以上考虑,作者又提出了 P-Tuning v2
他是一个深度提示微调的一个优化和适应实现,他最显著的改善源自于应用连续的 prompt 给预训练模型的每一层,而不仅仅是在输入层
2.P-Tuning v2
首先分析了一下 之前的 P-Tuning 性能不够好的原因,作者的猜想是缺乏普遍性。
- 缺乏跨尺度的普遍性
- 缺乏跨任务的普遍性
所以提出了 P-tuning v2
-
深度 Prompt Tuning
之前的方法是只在输入序列加上提示嵌入。这导致了两个挑战:
首先,大量的可调整的参数是有限的,因为序列长度的限制
其次,输入嵌入相对来说不是直接影响模型精度的。
为了处理这些挑战,P-tuning v2 如图二右侧的深度提示微调的方法。在不同层的提示被加入作为前缀 token
一方面,P-tuning v2 有了更多的可调整的任务特定参数(从 0.01% 到 0.1%-3%),这允许更多的任务能力
另一方面,提示增加到了更深的层中,可以更直接的影响模型的精度
-
优化和实现
-
重参数化
以前的工作通常利用一个重参数化编码器,比如 MLP来转换可训练的嵌入。然而对于 NLU,作者发现他的有用性取决于任务和数据集。对某些数据集,MLP 可能对结果影响很小甚至是负面影响
-
提示长度
提示长度是一个重要的角色在 P-Tuning v2 中。作者发现不同的 NLU 任务通常以不同的提示长度实现最佳性能。
一般来说,简单的分类任务可以使用短的提示长度(少于 20),复杂的序列标注任务可以是更长的(大于 100)
-
多任务学习
可选的,
-
分类头
使用语言建模头来预测 言语者 一直是提示调整的核心,但我们发现它在全数据设置中是不必要的,并且与序列标记不兼容。相反,P-tuning v2 在 token 之上应用随机初始化的分类头,如 BERT 中那样。
表 1:P-tuning v2 和现有 Prompt Tuning 方法之间的概念比较(KP:知识探测;SeqTag:序列标记;Re-param.:重新参数化;No verb.:无语言器)。
-
3.实验
挑选部分 NLU task,包括:
GLUE(SST-2、MNLI-m);
SuperGLUE(RTE、BoolQ、CB);
实体识别(CoNLL03、OntoNotes 5.0、CoNLL04);
ExtractiveQA(SQuAD1.1、SQuAD2.0);
Semantic Role Labelling(CoNLL05、CoNLL12)。
Baseline选择:
PT-2:本文提出的P-tuning V2;
MPT-2:本文提出的P-tuning V2,并采用Multi-task learning方法;
PT:P-tuning;
FT:传统的Fine-tuning方法;
表 2:SuperGLUE 开发集的结果。 P-tuning v2 超越了 P-tuning 在小于 10B 的模型上,匹配不同模型尺度的微调性能。 (FT:微调;PT:Lester et al. (2021) & P-tuning;PT-2:P-tuning v2;粗体:最好;下划线:第二好)。
表 3: 命名实体识别(NER)、问题解答(提取式 QA)和语义角色标记(SRL)的结果。
NER 和 SRL 中的所有指标均为 micro-fl 分数。(FT:微调;PT:P-tuning & Lester 等人(2021 年);PT-2:P-tuning v2;MPT-2:多任务 P-tuning v2;粗体:最佳;下划线:次佳)。
4.结论
- P-tuning在复杂的NLU任务上效果很差;
- P-tuning V2可以与传统Fine-tuning有差不多的效果;
- Multi-task P-tuning V2效果更好,分析认为可能是变相的数据增强带来的影响。
4.P-Tuning 超参数微调
这两种方法中,P-Tuning v2更常用,因为它的性能更接近全参数微调,且应用场景更广泛。v1主要用于分类等简单任务,v2则可以应用于生成、理解等各类任务。
1.超参数
P-Tuning v1的关键超参数:
- prompt长度(prompt_length)
- 含义:可训练的连续提示标记数量
- 建议值:
- 简单任务:10-20
- 中等任务:20-50
- 复杂任务:50-100
- 调优策略:
- 从小值开始(如20)
- 如果效果不好可以逐步增加
- 注意增加prompt长度会增加训练难度
- 提示编码器结构(prompt encoder)
- LSTM隐藏维度
- 建议值:512-2048
- 一般设为基座模型隐藏维度的1/2到1/1
- LSTM层数
- 建议值:2-3层
- 更多层可能导致训练不稳定
- MLP层数和维度
- 层数:通常2层
- 维度:可以逐层递减(如2048->1024)
- LSTM隐藏维度
- 初始化策略(init_method)
- 随机初始化
- 从词表采样
- 基于任务相关词的embedding
- 建议:先尝试随机初始化,如果效果不好再尝试其他方法
P-Tuning v2的关键超参数:
- 每层prompt长度(prefix_length)
- 建议值:
- 小模型(7B以下):32-64
- 中型模型(7B-70B):64-128
- 大模型(70B+):128-256
- 调优建议:
- 任务复杂度越高,需要更长的prefix
- 模型越大,可以使用更长的prefix
- 建议值:
- prefix添加的层(target_layers)
- 选项:
- 全部层
- 部分层(如前1/3或前1/2)
- 间隔添加(如每隔2层)
- 建议:
- 默认全部层
- 如果显存不足,可以选择性添加
- 通常前面的层更重要
- 选项:
- prefix投影(prefix_projection)
- 投影维度
- 建议值:原始维度的1/4到1/2
- 例如:原始1024->投影256
- 是否使用投影
- 默认开启
- 可以显著减少参数量
- 通常对性能影响不大
- 投影维度
典型配置示例:
- P-Tuning v1:
v1_config = {
"prompt_length": 20,
"encoder": {
"lstm_hidden": 1024,
"lstm_layers": 2,
"mlp_hidden": [1024, 512],
"dropout": 0.1
},
"init_from_vocab": True
}
- P-Tuning v2:
v2_config = {
"prefix_length": 64,
"num_target_layers": "all",# 或具体层数
"prefix_projection": True,
"projection_dim": 256,
"dropout": 0.1
}
2.调参建议
- 优先级顺序:
- v1: prompt长度 > 编码器结构 > 初始化策略
- v2: 层的选择 > prefix长度 > 投影维度
- 调优策略:
- 从保守配置开始
- 逐步增加复杂度
- 注意监控训练稳定性
- 常见问题和解决方案:
- 训练不稳定:
- 减小prompt/prefix长度
- 降低学习率
- 增加预热步数
- 效果不好:
- 增加长度
- 尝试不同的初始化(v1)
- 添加更多层(v2)
- 训练不稳定:
5.总结
- v2通常是更好的选择,除非:
- 计算资源极其有限
- 任务非常简单
- 只需要处理分类任务
- 资源受限时的简化建议:
- v1:使用更短的prompt,简化编码器
- v2:减少目标层数,使用更激进的投影