全参数微调
全参数微调原理
全参数微调(Full Parameter Fine-Tuning)是指在预训练模型的基础上,对模型所有参数都进行更新以适配下游任务的微调方法。
核心思想:
- 预训练模型已经学到了丰富的通用知识,通过全参数微调可以让所有参数都适应下游任务的数据分布
- 所有层的参数都会参与反向传播和梯度更新
- 最终得到一个完全适配下游任务的完整模型
公式表示:
- 给定预训练参数 W0,在训练过程中更新所有参数得到 W=W0+ΔW
- 所有参数都参与梯度计算和更新
微调 vs 预训练区别
相同点
- 训练目标相同:都是下一个token预测(对于Decoder-only模型),本质都是最大化似然估计
不同点
| 维度 | 预训练 | 微调 |
|---|
| 数据规模 | 数十亿到数万亿token | 通常数千到数百万token,远小于预训练 |
| 数据标注 | 不需要人工标注,纯无监督 | 需要人工标注的指令数据,有监督学习 |
| 数据格式 | 原始连续文本 | 指令-回复对,特定格式 |
| 学习目标 | 学习通用语言知识和世界知识 | 对齐人类意图,适配特定任务 |
| 计算成本 | 极高,需要数千卡训练数月 | 相对较低,几十卡训练几天 |
| 参数更新 | 从随机初始化开始,全部参数更新 | 从预训练参数开始,全部参数更新 |
具体计算例子
预训练:对整句话每个token都计算损失
输入:描述计算机主板的功能[BOS]计算机主板是计算机中的主要电路板。它是系统的支撑。
损失计算:每个位置的token都计算损失,包括问句部分。
SFT微调:只计算回答部分的损失,问句部分损失被忽略
输入:描述计算机主板的功能[BOS]计算机主板是计算机中的主要电路板。它是系统的支撑。[EOS]
标签:[......][BOS]计算机主板是计算机中的主要电路板。它是系统的支撑。[EOS]
损失计算:只计算答句的损失,问句部分不贡献损失。
微调数据准备
指令微调(SFT)数据构建原则
- 代表性:选择多个有代表性的任务覆盖需求
- 控制数据量:每个任务实例数量不宜过多(通常数百个),避免过拟合
- 平衡数据分布:平衡不同任务的数据量比例,限制整个数据集容量(通常几千到几万条),防止大数据集压倒整个分布
- 数据质量优先:数据质量的重要性大于数据数量
领域模型数据选取
Continue PreTrain(增量预训练):
- 优先选择领域技术标准文档、专业书籍
- 领域网站、新闻资讯知识密度较低,作为补充
SFT指令微调:
- 数据要干净、具有代表性
- Prompt构造尽量多样化,提高模型鲁棒性
- 多任务训练时,保持各个任务数据量平衡
多轮对话数据处理
- 对历史对话做左截断,保留最新的对话记录,节省序列长度
- 去掉样本中无意义的语气词(如"嗯嗯"、"啊啊"等)
- 过滤不合适的内容
- 可以扩充用户特征标签(年龄、性别、地域等)
领域评测集构建
建议构建两份评测集:
- 选择题形式:可自动评测,用于快速初筛
- 开放问答形式:人工评测,用于精筛,更贴近真实场景
灾难性遗忘与缓解
什么是灾难性遗忘
灾难性遗忘指的是模型在学习新知识后,几乎彻底遗忘掉之前习得的内容。在大模型微调中表现为:微调特定任务后,模型原本的通用能力下降,甚至对原本能正确回答的问题也答非所问。
产生原因
- 微调数据量少且任务类型单一,参数更新方向集中,导致原模型通用知识被覆盖
- 学习率设置过高,参数改动过大
缓解方法
-
混合通用数据:在领域训练过程中加入一定比例的通用数据集
- 比例通常在领域数据:通用数据 = 1:5 到 1:10之间,具体取决于领域数据量
-
控制学习率:使用较小的学习率(如 2e-5 或更小),不要大于预训练时的学习率
-
参数高效微调:使用LoRA、Adapter等方法,只更新少量参数,原模型参数固定,从根本上避免遗忘
-
弹性权重巩固:对重要参数增加正则惩罚,限制其大幅更新
-
经验回放:在微调过程中混合一部分旧数据(通用数据)回放
显存需求计算
全参数微调对显存要求较高,经验公式:
- 一个nB参数的模型,最低需要 16~20n GB 显存(不开CPU offload的情况下)
举例:
- 7B模型:需要 112GB ~ 140GB 显存,通常需要4张A100 40GB
- 13B模型:需要 208GB ~ 260GB 显存
可以通过FSDP、梯度累积、梯度检查点等技术降低显存需求。
训练最佳实践
Batch Size 设置
- 太小:梯度估计方差大,更新方向噪声多,收敛不稳定
- 太大:收益递减,增加FLOPS但性能提升有限,达到临界点后性能反而下降
- 经验:存在临界点,找到临界点可以有效平衡训练效率和模型最终效果
- OpenAI建议:将学习率调整到接近最优值的前提下去寻找最优batch size
学习率调度
- 先warmup线性增长学习率,再余弦衰减到最大值的10%
- 最大值通常在 5e-5 到 1e-4 之间
优化器选择
- 推荐使用AdamW,权重衰减通常设置为0.1
- Sophia是值得关注的新选择,使用梯度曲率而非方差进行归一化,可能提高训练效率和模型性能
其他技巧
- 梯度裁剪:通常将梯度裁剪为1.0
- 混合精度训练:推荐使用bfloat16而非float16
- LayerNorm:使用Pre RMS Norm
- 激活函数:使用GeGLU或SwiGLU
- 位置编码:使用ROPE
- 去除偏置项:去除dense层和layer norm的偏置项,有助于提升稳定性
面试常见问题
Q: SFT之后为什么感觉模型变傻了?
A:
- 传统观点:SFT的作用是激发大模型已有能力,而非注入新知识。SFT数据量通常只有几万条,远小于预训练数据量。如果抱着灌注领域知识的想法做SFT,容易把模型"练傻"。
- 现代观点:指令微调的真正作用是增强/解锁大语言模型的能力,让模型能够泛化到未见过的任务,即使在多语言场景下也能有不错表现。
- 实践中,如果数据质量差、学习率太高,确实容易导致模型原有能力退化,出现变傻的现象。
Q: 想要领域知识注入,应该预训练还是微调?
A:
- 推荐结合使用:先用篇章数据做增量预训练获取广泛知识,再用问答数据做指令微调,让模型更好地适应特定领域
- 如果没有大量篇章文档数据,只做微调也能注入知识,不必纠结,因为预训练和微调从实现角度差别不大,都是更新参数
- 如果领域分布和预训练差别不大,也不需要二次预训练,直接微调即可
Q: SFT时基座选用Chat模型还是Base模型?
A:
- 资源有限(数据小于10k):选用Chat模型作为基座,因为它已经对齐过,效果更好
- 资源充足(数据大于100k):选用Base模型重新SFT,可以更好地拟合数据,不受原有Chat模板限制
- 注意:在Chat模型上SFT时,一定要遵循原模型的对话格式,建议不使用全量参数训练,避免遗忘太多原始能力。
Q: 全参数微调的优缺点?
A:
- 优点:效果最好,所有参数都可以适应下游任务,能充分利用数据学习新知识
- 缺点:
- 显存要求高,训练成本昂贵
- 每个任务需要保存一个完整模型副本,存储成本高
- 容易发生灾难性遗忘,通用能力下降
- 训练速度慢
Q: 大模型训练出现loss突刺(loss spike)是什么原因?怎么解决?
A:
- 原因:主要由Adam优化器导致。理论上,梯度变化不独立(浅层参数长时间不更新,batch太大后期梯度平稳),会导致参数更新进入非稳态,出现loss突然暴涨。
- 解决方法:
- 出现loss spike后,回退到最近的checkpoint,更换batch样本
- 减小learning rate
- 减小Adam中的epsilon参数,甚至设为0
- 对浅层梯度乘以缩放系数,减小更新幅度(智谱GLM-130B实践)
Q: 大模型领域词表扩增有必要吗?
A:
- 主要解决的是解码效率问题,对模型效果提升不一定很大。如果领域有大量专有词汇,词表过小会导致分词碎片化,增加序列长度,此时扩增是有帮助的。