第 32 章:微调:把通才变成专才
本章问题:已经有了一个强大的基础模型——如何让它在我自己的特殊任务上表现最佳?
32.1 通才的局限
预训练出的基础模型是一个通才——它读过几十亿页文字,知道世界的大致轮廓。但当你面对一个具体的业务问题时——比如"帮我分类这些客服投诉"——模型的通用能力不够精确。
你的客户投诉里可能有特定的产品名("VX-9000 系列的显影液")、行业的特有缩写("SOP"在制造业和医疗中的含义完全不同)、或者你公司内部的特定格式要求。这些信息不在预训练语料中——模型不可能在"预测下一个词"的阶段自动学到你们公司的产品编号体系。
你需要让模型在已经具备了通用语言理解和生成能力的前提下,进一步学习你特定的领域数据和任务需求。这就是微调(fine-tuning)。
微调 = 拿一个预训练好的通用模型 + 在你自己的标注数据上继续训练少量步数。
32.2 微调为什么有效——冻结的底座和微调的表面
微调能够用极少数据达到极好效果的根本原因,可以通过一个类比理解:
一个精通英文的人要学写法文——需要大量时间。但一个已经精通意大利文和西班牙文的人去学写法文——可能几周就能基本掌握,因为她已经掌握了拉丁语系的语法结构、动词人称变位等"通用基底"。
基础模型在预训练阶段已经学到了:
- 语言的基本句法结构
- 大量世界知识和常识
- 基本推理模式
- 通用的文本生成能力
当你微调时——你不是在教模型从零学语言。你是在它已经具备的完整语言能力上,教会它在这个具体任务上的映射规则。因此只需少量标注数据——几百到几千条——经常就能得到很好的效果。
在这个框架下看,过去几年的各种"微调变体"其实都在处理同一个问题:在给定的计算和数据预算下,最优地调整模型的哪部分权重?
- 全参数微调:更新所有参数。效果最强——但需要存整个模型的梯度,GPU 内存消耗大。
- 冻结特征提取器:只微调最后一层(分类头等)。训练极快但通常效果差于全参数微调——因为高层语义特征也需要一些调整来适配新任务。
- 部分层微调:只微调最后几层。在全参数和特征提取之间有显著的折中——很多实际应用中只微调最后 2-4 层的效果已经非常接近全参数微调。
32.3 微调数据格式:回到指令三元组
微调数据的格式和指令微调(第 29 章)完全相同——指令(任务描述)+ 输入(材料)+ 输出(期望答案):
每条数据是同一个任务的不同实例。微调时——模型在这个任务上反复看、反复调整权重——最终学会"看到投诉→输出正确类别"的映射,同时保持它预训练学到的所有通用语言能力。
32.4 微调的关键工程直觉
学习率要小。预训练用大的学习率(模型从零开始,需要大步快走)。微调用小的学习率——通常是预训练学习率的 1/10 到 1/100——因为你只是想在"已有知识"的基础上做小的修正,而不是全盘改权重。
不要把预训练知识洗掉。微调一个 epoch 太少,模型还没学会你的任务模式。微调 50 个 epoch 太多——模型会过拟合到你的小数据集上,开始"死记"少数样本,导致泛化性能下降。这就是灾难性遗忘——模型为了在你提供的标注上继续降低损失,开始逐步覆盖预训练阶段学到的通用能力。在教科书例中观察到——微调太久后模型在你的任务上表现完美,但丧失了基本常识推理、语言连贯性和事实回忆能力。
数据质量 >> 数据量。在微调场景下,50 条高质量的、干净标注、覆盖真实边缘情况的样本经常比 5000 条嘈杂的自动标注数据更好。这不是在预训练——你不需要"尽可能多的数据去覆盖语言统计分布"。你需要精确的信号——每条错误的标注都在把模型推向错误的方向。
monitor 验证集 loss。如果你看到训练 loss 在降但验证 loss 在升——停下来,你已经过拟合了,模型已经进入灾难性遗忘的轨道。大部分成功的微调在 1-5 个 epoch 之间停止。
32.5 最小代码:用 HuggingFace 微调一个中文分类模型
以下约 40 行代码,演示微调一个预训练 BERT 做中文文本分类:
核心观察:
num_labels=3—— 在预训练 BERT 的基础上加了一个新的分类头(随机初始化)。微调会同时更新这个分类头和 BERT 的所有 Transformer 层。learning_rate=2e-5—— 远低于预训练时用的学习率。大型语言模型的微调通常的学习率在 1e-5 到 5e-5 之间。- 5 条训练数据显然不够实用——你需要至少 50-100 条来获得可靠的分类。但这段代码的接口对于 500 条和 5 条是一样的。
32.6 全参数微调的代价
对于像 GPT-3(175B 参数)或 LLaMA-70B 这样的模型——全参数微调意味着你需要存储所有 700 亿参数的梯度、优化器状态和中间激活值。对一个 175B 的模型做全参数微调——需要大约 1400 GB GPU 内存。即使你有 8 个 A100(每个 80 GB),也不够在大模型上做全参数微调。
这就是为什么在实际产业应用中,全参数微调往往是"理论上最优但工程上不可行"的——它更适合中小模型(10B 参数以下)或有大量 GPU 预算的团队。对于大部分想要微调大模型的开发者和公司——需要更轻量、更省显存的方法。
但核心训练逻辑没有变:你依然在做同一种事——把模型的通用能力通过少量有监督数据适配到你的具体任务上。只是实现方式需要调整到可以在消费级或单卡 GPU 上跑起来。
这就引出了下一章的核心问题:如何用极少的额外参数和极少的额外计算,达到接近全参数微调的效果?
下一章:LoRA——只训练"变化量"。
Discussion
留言区 · GitHub-powered comments via Giscus