跳到主要内容大模型微调常用方法详解 | 极客日志PythonAI算法
大模型微调常用方法详解
本文详细介绍了大模型微调中的常用 PEFT 技术,包括 Adapter Tuning、Prefix Tuning、Prompt Tuning、P-Tuning v1/v2 以及 AdaLoRA。文章阐述了各方法的原理、结构特点及适用场景,对比了它们在参数量、推理速度和精度上的差异,并提供了统一视角下的框架分析。内容涵盖从理论推导到代码实现的完整流程,旨在帮助开发者在资源受限情况下高效完成模型微调与迁移学习。
最近,深度学习的研究中出现了许多大型预训练模型,例如 GPT-3、ChatGPT、GPT4、ChatGLM-130B 等,这些模型可以在多种自然语言处理任务中取得优异的性能表现。然而,这些大型预训练模型的训练成本非常高昂,需要庞大的计算资源和大量的数据,一般人难以承受。这也导致了一些研究人员难以重复和验证先前的研究成果。
为了解决这个问题,研究人员开始研究 Parameter-Efficient Fine-Tuning (PEFT) 技术。PEFT 技术旨在通过最小化微调参数的数量和计算复杂度,来提高预训练模型在新任务上的性能,从而缓解大型预训练模型的训练成本。这样一来,即使计算资源受限,也可以利用预训练模型的知识来迅速适应新任务,实现高效的迁移学习。
在上一篇文章中,介绍了 PEFT 技术中的常用方法 LoRA,使得百亿(10B)参数的大模型可以在单卡上训练(显存大小>=40G)。今天介绍下另外几种常用的方法,包括 Adapter Tuning、Prompt Tuning、Prefix Tuning、P-Tuning、P-Tuning v2 和 AdaLoRA。
1. Adapter Tuning
2019 年谷歌的研究人员首次在论文《Parameter-Efficient Transfer Learning for NLP》提出针对 BERT 的 PEFT 微调方式,拉开了 PEFT 研究的序幕。他们指出,在面对特定的下游任务时,如果进行 Full-Finetuning(即预训练模型中的所有参数都进行微调),太过低效;而如果采用固定预训练模型的某些层,只微调接近下游任务的那几层参数,又难以达到较好的效果。
于是他们设计了如下图所示的 Adapter 结构,将其嵌入 Transformer 的结构里面,在训练时,固定住原来预训练模型的参数不变,只对新增的 Adapter 结构进行微调。同时为了保证训练的高效性(也就是尽可能少的引入更多参数),他们将 Adapter 设计为这样的结构:
首先是一个 down-project 层将高维度特征映射到低维特征
然后过一个非线性层之后,再用一个 up-project 结构将低维特征映射回原来的高维特征
同时也设计了 skip-connection 结构,确保了在最差的情况下能够退化为 identity(类似残差结构)。
从实验结果来看,该方法能够在只额外对增加的 3.6% 参数规模(相比原来预训练模型的参数量)的情况下取得和 Full-Finetuning 接近的效果(GLUE 指标在 0.4% 以内)。
优点: 通用性强,可应用于各种 Transformer 架构。
缺点: 增加了推理延迟,因为需要额外的前向传播步骤。
2. Prefix Tuning
2021 年斯坦福的研究人员在论文《Prefix-Tuning: Optimizing Continuous Prompts for Generation》中提出了 Prefix Tuning 方法。与 Full-finetuning 更新所有参数的方式不同,该方法是在输入 token 之前构造一段任务相关的 virtual tokens 作为 Prefix,然后训练的时候只更新 Prefix 部分的参数,而 Transformer 中的其他部分参数固定。该方法其实和构造 Prompt 类似,只是 Prompt 是人为构造的'显式'的提示,并且无法更新参数,而 Prefix 则是可以学习的'隐式'的提示。
同时,为了防止直接更新 Prefix 的参数导致训练不稳定的情况,他们在 Prefix 层前面加了 MLP 结构 (相当于将 Prefix 分解为更小维度的 Input 与 MLP 的组合后输出的结果),训练完成后,只保留 Prefix 的参数。
embedding = torch.nn.Embedding(num_virtual_tokens, token_dim)
transform = torch.nn.Sequential(
torch.nn.Linear(token_dim, encoder_hidden_size),
torch.nn.Tanh(),
torch.nn.Linear(encoder_hidden_size, num_layers * 2 * token_dim),
)
3. Prompt Tuning
Prompt Tuning 是 2021 年谷歌在论文《The Power of Scale for Parameter-Efficient Prompt Tuning》中提出的微调方法。
该方法可以看作是 Prefix Tuning 的简化版本,只在输入层加入 prompt tokens,并不需要加入 MLP 进行调整来解决难训练的问题,主要在 T5 预训练模型上做实验。似乎只要预训练模型足够强大,其他的一切都不是问题。作者也做实验说明随着预训练模型参数量的增加,Prompt Tuning 的方法会逼近 Fine-tune 的结果。
固定预训练参数,为每一个任务额外添加一个或多个 embedding,之后拼接 query 正常输入 LLM,并只训练这些 embedding。左图为单任务全参数微调,右图为 Prompt tuning。
作者做了一系列对比实验,都在说明:随着预训练模型参数的增加,一切的问题都不是问题,最简单的设置也能达到极好的效果。
- Prompt 长度影响: 模型参数达到一定量级时,Prompt 长度为 1 也能达到不错的效果,Prompt 长度为 20 就能达到极好效果。
- Prompt 初始化方式影响: Random Uniform 方式明显弱于其他两种,但是当模型参数达到一定量级,这种差异也不复存在。
- 预训练的方式: LM Adaptation 的方式效果好,但是当模型达到一定规模,差异又几乎没有了。
- 微调步数影响: 模型参数较小时,步数越多,效果越好。同样随着模型参数达到一定规模,zero shot 也能取得不错效果。
- 结论: 当参数达到 100 亿规模与全参数微调方式效果无异。
from peft import PromptTuningConfig, get_peft_model
peft_config = PromptTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=10)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
4. P-Tuning v1
P-Tuning 方法的提出主要是为了解决这样一个问题:大模型的 Prompt 构造方式严重影响下游任务的效果。
P-Tuning 提出将 Prompt 转换为可以学习的 Embedding 层,只是考虑到直接对 Embedding 参数进行优化会存在这样两个挑战:
- Discreteness: 对输入正常语料的 Embedding 层已经经过预训练,而如果直接对输入的 prompt embedding 进行随机初始化训练,容易陷入局部最优。
- Association: 没法捕捉到 prompt embedding 之间的相关关系。作者在这里提出用 MLP + LSTM 的方式来对 prompt embedding 进行一层处理。
P-tuning 依然是固定 LLM 参数,利用多层感知机和 LSTM 对 Prompt 进行编码,编码之后与其他向量进行拼接之后正常输入 LLM。注意,训练之后只保留 Prompt 编码之后的向量即可,无需保留编码器。
self.lstm_head = torch.nn.LSTM(
input_size=self.input_size,
hidden_size=self.hidden_size,
num_layers=num_layers,
dropout=lstm_dropout,
bidirectional=True,
batch_first=True,
)
self.mlp_head = torch.nn.Sequential(
torch.nn.Linear(self.hidden_size * 2, self.hidden_size * 2),
torch.nn.ReLU(),
torch.nn.Linear(self.hidden_size * 2, self.output_size),
)
self.mlp_head(self.lstm_head(input_embeds)[0])
4.1 与 Prefix-Tuning 的区别
P-Tuning 和 Prefix-Tuning 差不多同时提出,做法其实也有一些相似之处,主要区别在:
- 位置: Prefix Tuning 是将额外的 embedding 加在开头,看起来更像是模仿 Instruction 指令;而 P-Tuning 的位置则不固定。
- 初始化: Prefix Tuning 通过在每个 Attention 层都加入 Prefix Embedding 来增加额外的参数,通过 MLP 来初始化;而 P-Tuning 只是在输入的时候加入 Embedding,并通过 LSTM+MLP 来初始化。
5. P-Tuning v2
P-Tuning 的问题是在小参数量模型上表现差。
于是就有了 v2 版本:《P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks》。
从标题就可以看出,P-Tuning v2 的目标就是要让 Prompt Tuning 能够在不同参数规模的预训练模型、针对不同下游任务的结果上都达到匹敌 Fine-tuning 的结果。
那也就是说当前 Prompt Tuning 方法在这两个方面都存在局限性。
- 不同模型规模: Prompt Tuning 和 P-tuning 这两种方法都是在预训练模型参数规模够足够大时,才能达到和 Fine-tuning 类似的效果,而参数规模较小时效果则很差。
- 不同任务类型: Prompt Tuning 和 P-tuning 这两种方法在 sequence tagging 任务上表现都很差。
5.1 主要结构
相比 Prompt Tuning 和 P-tuning 的方法,P-tuning v2 方法在多层加入了 Prompts tokens 作为输入,带来两个方面的好处:
- 带来更多可学习的参数(从 P-tuning 和 Prompt Tuning 的 0.1% 增加到 0.1%-3%),同时也足够 parameter-efficient。
- 加入到更深层结构中的 Prompt 能给模型预测带来更直接的影响。
v1 到 v2 的可视化:蓝色部分为参数冻结,橙色部分为可训练部分。
5.2 几个关键设计因素
- Reparameterization: Prefix Tuning 和 P-tuning 中都有 MLP 来构造可训练的 embedding。本文发现在自然语言理解领域,面对不同的任务以及不同的数据集,这种方法可能带来完全相反的结论。
- Prompt Length: 不同的任务对应的最合适的 Prompt Length 不一样,比如简单分类任务下 length=20 最好,而复杂的任务需要更长的 Prompt Length。
- Multi-task Learning: 多任务对于 P-Tuning v2 是可选的,但可以利用它提供更好的初始化来进一步提高性能。
- Classification Head: 使用 LM head 来预测动词是 Prompt Tuning 的核心,但我们发现在完整的数据设置中没有必要这样做,并且这样做与序列标记不兼容。P-tuning v2 采用和 BERT 一样的方式,在第一个 token 处应用随机初始化的分类头。
5.3 实验结果
不同预训练模型大小下的表现,在小模型下取得与 Full-finetuning 相近的结果,并远远优于 P-Tuning。
不同任务下的 P-Tuning v2 效果都很好,而 P-Tuning 和 Prompt Learning 效果不好;同时,采用多任务学习的方式能在多数任务上取得最好的结果。
6. AdaLoRA
预训练语言模型中的不同权重参数对下游任务的贡献是不同的。因此需要更加智能地分配参数预算,以便在微调过程中更加高效地更新那些对模型性能贡献较大的参数。
具体来说,通过奇异值分解将权重矩阵分解为增量矩阵,并根据新的重要性度量动态地调整每个增量矩阵中奇异值的大小。这样可以使得在微调过程中只更新那些对模型性能贡献较大或必要的参数,从而提高了模型性能和参数效率。
7. Towards a Unified View of PETL
这篇 ICLR2022 的文章研究了典型的 PEFT 方法,试图将 PEFT 统一到一个框架下,找出它们起作用的具体原因,并进行改进。主要研究了三个问题:
- 典型的 PEFT 方法有什么联系?
- 典型的 PEFT 方法中是哪些关键模块在起作用?
- 能否对这些关键模块进行排列组合,找出更有用的 PEFT 方法?
7.1 通用形式
通过对 Prefix Tuning 的推导,得出了和 Adapter Tuning 以及 LoRA 形式一致的形式。
- 形式: 具体的数学表达形式。
- 嵌入 Transformer 结构的方式: 分为 Parallel 和 Sequential 两种。Parallel 指的是在输入层嵌入,这样与原有结构可以并行计算;Sequential 指的是在输出层嵌入,相当于增加了网路的深度,与原有结构存在依赖关系。
- 修改表示层: 主要指对 attention 层的修改还是对 ffn 层的修改。
- 组合方式: 怎么与原有的参数组合,包括简单相加(Adapter)、门控式(Prefix Tuning)、缩放式(LoRA)三种)。
根据这个统一的框架,还另外设计了三种变体 Parallel Adapter、Multi-head Parallel Adapter、Scaled Parallel Adapter。
8. 方法对比与选择指南
为了帮助开发者更好地选择微调策略,以下总结了各方法的特性对比:
| 方法 | 参数量占比 | 推理速度 | 适用场景 | 稳定性 |
|---|
| Full Finetuning | 100% | 快 | 数据充足,资源无限 | 高 |
| Adapter Tuning | ~3-5% | 慢 (增加层) | 多任务,通用性强 | 高 |
| Prefix Tuning | <1% | 快 | 生成任务,Instruction Following | 中 |
| Prompt Tuning | <0.1% | 快 | 大规模模型,简单任务 | 中 |
| P-Tuning v2 | <1% | 快 | 各类任务,小模型友好 | 高 |
| AdaLoRA | 动态 | 快 | 资源受限,需高精度 | 高 |
8.1 实际选型建议
- 资源极其有限: 优先选择 Prompt Tuning 或 P-Tuning v2,显存占用最低。
- 追求极致精度且数据量大: 如果显存允许,Full Finetuning 仍是基准线;若受限,LoRA 或 AdaLoRA 是最佳平衡点。
- 多任务学习: Adapter Tuning 因其模块化特性,适合在不同任务间共享主干网络。
- 生成类任务: Prefix Tuning 和 P-Tuning v2 在文本生成方面表现更为出色。
9. 总结
PEFT 技术的核心在于如何在保持模型性能的同时降低计算和存储成本。从早期的 Adapter 到后来的 Prompt 系列,再到基于低秩分解的 LoRA 及其变体,PEFT 方法不断演进,逐渐解决了小模型适配难、多任务冲突等问题。在实际工程中,建议根据具体任务类型、数据规模及硬件资源,灵活组合上述方法。未来,随着模型规模的进一步扩大,参数效率将成为衡量算法优劣的关键指标之一。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online