图 1:本文封面,微调中目标任务上升、通用能力下滑的双曲线。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料,与任何公司的当前招聘无关。「微调后通用能力下降」是每个做大模型微调的人都会撞上的现象,面试里它的正式名字叫灾难性遗忘(catastrophic forgetting):模型学会新任务的同时,把预训练阶段积累的通用能力「挤掉」了。
成因:同一份参数,被新数据的梯度改写。
预训练参数之所以有通用能力,是因为它们在万亿级 token 上被调到了一个对「语言整体分布」都不错的位置。微调时,梯度只按新任务数据的分布计算,参数朝着新任务的低损失方向移动——而新数据里没有任何信号阻止它远离旧任务的好位置。网络里没有「这块参数管通用问答、那块管拼写纠错」的隔断,所有能力共享同一套权重,新梯度覆盖到哪里,哪里的旧能力就劣化。任务越窄、数据越单一、训练越久,覆盖得越狠。
四种缓解手段,各管一段。
- 数据混合:在微调数据里按比例回混通用指令数据或旧任务数据。这是最直接的手段——旧分布的信号重新出现在梯度里,参数就没有理由单方面漂移。代价是要准备和维护回混数据,比例也得调。
- 小学习率:微调学习率明显小于预训练末期,每一步对参数的改写量小,旧能力被覆盖得慢。代价是收敛慢,新任务要学更久。
- 正则约束:给损失加一项「不许重要参数动太多」的惩罚。经典做法是 2016 年提出的 EWC(Elastic Weight Consolidation,arXiv:1612.00796):用费雪信息估计每个参数对旧任务的重要性,越重要的参数改动惩罚越大,相当于给旧能力上了一圈松紧不一的锚。
- 参数冻结:干脆不让原权重动。LoRA 这类参数高效微调只训练一小撮增量参数,预训练权重全程冻结,通用能力几乎不掉——这是它在实践中大受欢迎的重要原因之一。
没有免费的午餐:缓解越强,新任务学得越慢。
四种手段本质上是同一个权衡的不同切面:参数离旧位置越远,新任务学得越透、旧能力掉得越多。工程上的目标不是「零遗忘」,而是「遗忘控制在够用线以内」——上线前用通用能力评测集(而不是只看新任务指标)卡住下限,是基本的自保动作。
用代码把遗忘和缓解跑一次最小复现。
下面这段 Node.js 22.15.0 程序(node forget.js)把问题压缩到极限:一个单参数线性模型 y = w·x,任务 A(模拟通用能力)的最优 w 是 2,任务 B(微调目标)的最优 w 是 1.5。先充分学好 A,再用三种策略各微调 40 步。
const mse = (w, data) => data.reduce((a, [x, y]) => a + (w * x - y) ** 2, 0) / data.length;
const grad = (w, data) => data.reduce((a, [x, y]) => a + 2 * (w * x - y) * x, 0) / data.length;
const A = [[1, 2], [2, 4], [3, 6], [-1, -2], [-2, -4]]; // y = 2x
const B = [[1, 1.5], [2, 3], [3, 4.5], [-1, -1.5], [-2, -3]]; // y = 1.5x
const report = (tag, w) =>
console.log(`${tag}:w = ${w.toFixed(3)} | 任务A误差 = ${mse(w, A).toFixed(4)} | 任务B误差 = ${mse(w, B).toFixed(4)}`);
let w = 0;
for (let i = 0; i < 2000; i++) w -= 0.05 * grad(w, A); // 充分学好任务 A
report('学完任务A(微调前)', w);
let w1 = w;
for (let i = 0; i < 40; i++) w1 -= 0.05 * grad(w1, B); // 大学习率 + 纯新数据
report('大学习率纯B数据', w1);
let w2 = w;
for (let i = 0; i < 40; i++) w2 -= 0.005 * grad(w2, B); // 学习率缩小 10 倍
report('小学习率纯B数据', w2);
let w3 = w;
for (let i = 0; i < 40; i++) w3 -= 0.05 * grad(w3, [...B, ...A]); // 回混 50% 旧数据
report('大学习率+回混旧数据', w3);
实测输出:
学完任务A(微调前):w = 2.000 | 任务A误差 = 0.0000 | 任务B误差 = 0.9500
大学习率纯B数据:w = 1.500 | 任务A误差 = 0.9500 | 任务B误差 = 0.0000
小学习率纯B数据:w = 1.606 | 任务A误差 = 0.5894 | 任务B误差 = 0.0428
大学习率+回混旧数据:w = 1.750 | 任务A误差 = 0.2375 | 任务B误差 = 0.2375
三行数字正好对应三种权衡:大学习率纯新数据把 B 学到满分、A 彻底遗忘;小学习率保住了大半 A,但同样步数下 B 没学透;回混旧数据让参数停在两个任务的折中点,两边误差都不归零但都可控。真实大模型里发生的就是这件事的高维版本。
图 2:遗忘成因链与四种缓解手段的力度对比。
面试现场的答题结构。
先定义现象——微调后通用能力劣化叫灾难性遗忘;再讲成因——共享参数被新分布梯度单方面覆盖,任务越窄、训练越久越严重;然后按「数据混合、小学习率、正则约束(EWC)、参数冻结(LoRA 类)」四种手段逐一讲机制与代价;最后落到权衡:目标是控制在够用线以内,用通用评测集卡下限。你做微调时遇到过遗忘吗?最后是靠调学习率还是回混数据救回来的?欢迎聊聊。