☰
灾难性遗忘与持续学习:为什么大模型上线后要冻结参数
2026/10/15 5:38:29 网站建设 项目流程

1. 一个让人睡不着觉的悖论

你有没有想过一个挺拧巴的事儿:我们花了这么大力气,让机器能像人一样从数据里学东西,可一旦它学成了,我们做的第一件事往往是——把它冻起来,不让它再学了。

这不是段子。你去看任何一个真正落地的深度学习系统,几乎都是这个套路:训练阶段拼命喂数据、调参数,等模型收敛到一个满意的指标,立刻保存权重、锁死参数,然后拿去推理。上线之后,这个模型就进入了一种"冻结人生"——它再也不会因为遇到新数据而改变自己了。哪怕线上数据分布已经悄悄漂移,哪怕用户行为早就变了样,它还是那副老样子,顶多靠人工定期重新训练一版替换掉。

这事儿细想挺反直觉的。我们造机器的初衷,不就是希望它能持续进化、越用越聪明吗?为什么真到了这一步,反而不敢让它一直学了?

答案藏在一个听起来有点吓人的词里:灾难性遗忘(Catastrophic Forgetting)。这个词不是危言耸听,它描述的是一个非常具体的现象——当一个神经网络在学习新任务时,它会把之前学过的任务忘得一干二净,而且忘得非常彻底,几乎像是从来没学过一样。

这篇文章我想把这条线索捋清楚:从灾难性遗忘到底是怎么发生的,到持续学习(Continual Learning)这个领域为了解决它想了哪些招,再到大模型时代为什么"冻结"反而成了一种主流选择。中间会穿插一些我自己的理解、踩过的坑,以及如果你要动手做相关实验,具体该怎么设计、怎么避雷。适合对深度学习有一定基础、想搞清楚"模型为什么不能一直学"这个问题的朋友,也适合正在做增量训练、模型迭代相关工作的同学参考。

2. 灾难性遗忘到底是怎么发生的

2.1 从一个最朴素的实验说起

要理解灾难性遗忘,最好的办法不是看定义,而是自己动手跑一个最小实验。这个实验我在早期接触持续学习时反复做过,每次都能直观感受到那种"忘得干干净净"的震撼。

设想一个简单的全连接网络,先让它学任务A:识别手写数字0和1。数据很简单,网络很快就能达到很高的准确率。这时候你把任务A的测试集拿出来测一遍,准确率可能是99%以上。然后,你不动网络结构,直接拿任务B的数据继续训练——比如识别数字2和3。训练完之后,你再回头测任务A,会发生什么?

结果往往让人心里一凉:任务A的准确率可能直接掉到接近随机猜测的水平,比如50%左右(二分类瞎猜的水平)。网络并没有"变笨",它在任务B上表现很好,但它把任务A的知识几乎完全覆盖掉了。这就是灾难性遗忘最赤裸裸的样子。

我第一次看到这个结果的时候,第一反应是"是不是我代码写错了"。反复检查数据加载、标签映射、损失函数,都没问题。后来才明白,这不是bug,这是神经网络训练机制的内在天性。

2.2 为什么梯度更新会"覆盖"旧知识

要解释这个现象,得回到神经网络训练的本质:基于梯度的参数更新。

神经网络的"知识"并不像数据库那样,把每条信息存在一个独立的格子里。它是分布式地存储在成千上万个参数(权重和偏置)里的。一个参数往往同时参与多个任务、多个样本的预测。这种"共享参数"的设计是神经网络强大的原因,也是它脆弱的根源。

当你用任务B的数据做梯度下降时,优化器只关心一件事:怎么让当前这批数据上的损失更小。它根本不知道也不在乎这些参数之前承载了任务A的什么信息。于是,为了拟合任务B,参数会朝着对任务B有利的方向大幅移动。如果任务A和任务B需要的参数配置差异较大,那么这次移动就会把原本编码任务A知识的参数配置破坏掉。

打个比方。想象你有一块橡皮泥,任务A要求你把它捏成一只鸭子,你捏好了。现在任务B要求你把它捏成一辆汽车。你手上只有这一块泥,为了捏出汽车,你必须把鸭子拆了重捏。捏完汽车,鸭子自然就没了。神经网络面对的就是这种"只有一块泥"的困境——参数容量是共享的,新任务的重塑会摧毁旧任务的形状。

这里有个关键点容易被忽略:遗忘的严重程度和任务之间的相似性、参数更新的幅度都强相关。如果任务A和任务B非常相似(比如都是识别手写数字,只是类别不同),遗忘会轻一些;如果差异巨大(比如一个做图像分类,一个做文本情感分析),遗忘几乎是毁灭性的。另外,学习率越大、训练轮数越多,参数移动越剧烈,遗忘也越严重。

2.3 它和"过拟合""欠拟合"不是一回事

很多人第一次听到灾难性遗忘,会下意识把它和过拟合、欠拟合混在一起。这三者其实完全不同,我专门用一张表来区分,因为搞混了会导致你排查问题时方向跑偏。

现象本质典型表现触发条件
过拟合模型记住了训练集的噪声,泛化差训练集准、测试集差数据少、模型复杂、训练久
欠拟合模型容量不足或训练不充分训练集和测试集都差模型太简单、训练不足
灾难性遗忘学新任务时破坏旧任务参数新任务好、旧任务崩顺序训练多任务、共享参数

看出来了吧,灾难性遗忘的独特之处在于:它在当前任务上表现是好的,坏的是过去学过的任务。这是一种"时间维度"上的退化,而不是"泛化维度"上的问题。你如果只盯着当前任务的验证集,根本发现不了它,必须回头去测旧任务才能暴露。

提示:做增量训练时,一定要保留一份旧任务的验证集,并且每次训练后都跑一遍。我见过太多团队只监控新任务指标,上线后才发现老功能悄悄退化了,排查起来非常痛苦。

3. 持续学习:想让模型"边学边记"的几条路

既然灾难性遗忘是共享参数导致的,那持续学习(Continual Learning,也叫终身学习 Lifelong Learning)这个领域要解决的核心问题就很明确了:如何让模型在顺序学习多个任务时,既学好新任务,又不忘记旧任务。这个领域发展到现在,大致形成了三条主要技术路线,我一条条拆开讲,顺便说说各自的坑。

3.1 正则化路线:给重要参数"上锁"

这条思路的核心是:不是所有参数对旧任务都同等重要。有些参数是"关键先生",动了它们旧任务就崩;有些参数无关紧要,随便改。那我们在学新任务时,就给那些重要参数加一个惩罚项,让它们尽量别动。

最经典的方法是EWC(Elastic Weight Consolidation,弹性权重巩固)。它的做法是:在学完任务A之后,计算每个参数对任务A的重要性(用Fisher信息矩阵来估计),然后学任务B时,在损失函数里加一项,惩罚那些"重要参数"偏离原值的幅度。重要性越高的参数,越不允许动,就像给它们绑了根橡皮筋,拉得越远阻力越大。

这个方法的直觉很漂亮,但实操中有几个坑:

  • Fisher信息矩阵的计算成本不低,尤其是大模型,参数量动辄上亿,算一遍开销很大。
  • 重要性估计依赖旧任务数据,如果旧数据因为隐私或存储原因拿不到,这方法就受限。
  • 任务多了之后,约束会互相打架。学第三个任务时,既要尊重任务A的重要性,又要尊重任务B的,约束越叠越多,模型最后可能哪个任务都学不好,陷入"动弹不得"的僵局。

我实测下来的感受是:EWC在任务数量少(2到3个)、任务差异中等的情况下效果还行,但任务一多就力不从心。它更像是一个"温和的提醒",而不是"强力的保险"。

3.2 回放路线:把旧数据"掺"进来一起学

如果说正则化是"别动旧参数",那回放(Replay)路线就是另一个极端:既然怕忘,那就别让模型只学新的,把旧数据也拿出来一起学。

最直接的做法叫经验回放(Experience Replay):维护一个记忆缓冲区,存一部分旧任务的样本。每次学新任务时,从缓冲区采样一批旧样本,和新样本混在一起训练。这样模型在每个批次里都能同时看到新旧数据,参数更新就会兼顾两边,遗忘自然大大减轻。

这招效果通常是最好的,但代价也很明显:

  • 存储成本:你得存旧数据。数据量大或者任务多的时候,缓冲区怎么设计、存多少、存哪些,都是学问。
  • 隐私和合规问题:很多场景下旧数据不能长期保留,或者不能跨任务共享,这时候回放就用不了。
  • 数据不平衡:如果新任务数据远多于回放的旧数据,模型还是会被新任务带偏。需要仔细调回放比例。

为了绕开"必须存原始数据"的限制,后来出现了生成回放(Generative Replay):不存真实数据,而是训练一个生成模型,让它"回忆"出旧任务的样本。这个思路很优雅,但引入了新问题——生成模型本身也可能遗忘,而且生成质量不稳定,训练复杂度高。我试过用生成回放做实验,调参调到怀疑人生,最后效果还不如老老实实存一批真实样本。

3.3 架构路线:给每个任务"分房间"

前两条路线都是在"共享参数"这个前提下想办法,架构路线则干脆换个思路:既然共享参数会互相干扰,那我给不同任务分配不同的参数子集,各用各的,不就不打架了吗?

具体做法有很多种。比如参数隔离:学任务A时只用一部分网络分支,学任务B时再长出新分支,旧分支冻结不动。又比如动态扩展:每来一个新任务,就给网络增加一些容量,专门服务新任务。

这条路线理论上能彻底避免遗忘,因为旧任务的参数根本没被碰过。但它的硬伤是容量会无限膨胀。任务一多,网络越来越大,参数量爆炸,推理成本飙升,最后根本没法部署。而且不同任务之间的知识无法迁移共享,每个任务都从零学起,效率很低。

所以架构路线通常用在任务数量有限、且对遗忘零容忍的场景,比如某些工业质检的多阶段模型。放到通用大模型上,基本不现实。

3.4 三条路线的横向对比

把三条路线放一起对比,能更清楚地看到各自的取舍。这张表是我自己整理的经验总结,参数和结论基于常见实验设置,具体数值会因任务和数据集而异,但趋势是稳定的。

路线代表方法抗遗忘效果存储开销计算开销适用场景
正则化EWC、SI、MAS中等低中任务少、旧数据不可得
回放经验回放、生成回放高高中高旧数据可存、效果优先
架构参数隔离、动态扩展很高中高任务有限、零遗忘要求

实际工程中,很少纯用某一条路线,更多是混合使用。比如"回放 + 正则化":既存一小批旧样本,又对重要参数加约束,双保险。这种组合往往比单一路线更稳,也是我目前比较推荐的起步方案。

4. 大模型的"冻结人生":为什么我们主动选择不学

聊完持续学习的技术路线,回到开头那个悖论:既然有这么多方法对抗遗忘,为什么大模型时代,我们反而更倾向于把模型冻起来?

4.1 微调本身就是一次"温和的灾难"

先澄清一个误区:大模型并不是完全不学。我们常做的微调(Fine-tuning),本质上就是让预训练好的模型在新数据上继续学。但这里有个微妙的地方——微调往往只针对特定下游任务,而且通常用很小的学习率、很少的轮数,目的只是"微调"而不是"重塑"。

即便如此,微调也会带来遗忘。学术界有个现象叫灾难性遗忘在微调中的体现:一个大模型在通用能力上很强,但你拿某个垂直领域的数据猛训一通,它在垂直领域变强了,通用能力却可能下降。这就是为什么现在流行参数高效微调(PEFT),比如只训练一小部分额外参数(LoRA之类的思路),把主干网络冻住。冻结主干,本质上就是在保护预训练阶段学到的通用知识不被破坏。

所以你看,"冻结"不是偷懒,而是一种刻意的保护策略。预训练阶段花了海量算力和数据才得到的知识,太珍贵了,不敢让后续的微调随随便便把它覆盖掉。

4.2 全量持续训练的成本与风险

那为什么不干脆让大模型持续学习、天天更新呢?原因很现实:

第一,算力成本。大模型的全量训练动辄需要成千上万张加速卡跑很久。如果每次有新数据就全量重训,成本高到无法承受。就算只做增量更新,要保证不遗忘,就得掺入大量旧数据做回放,等于每次都在做接近全量的训练。

第二,稳定性风险。一个已经上线的模型,行为是经过大量测试和验证的。如果它每天都在变,今天和明天的输出可能不一样,这对依赖它的下游系统是灾难。你没法对一个"每天都在变"的模型做可靠的回归测试。

第三,可控性。冻结的模型,行为是可预测、可复现的。一旦允许它持续学习,就可能学到我们不希望它学的东西——数据里的偏见、噪声、甚至恶意注入的内容。冻结是一种安全兜底。

第四,评估困难。持续学习的模型,你怎么衡量它"整体上"变好了还是变坏了?旧任务和新任务此消彼长,没有一个简单的指标能说清楚。评估体系跟不上,就不敢放开让它学。

4.3 "冻结"其实是一种工程妥协

我个人对这件事的看法是:冻结不是技术上的最优解,而是工程上的最稳解。持续学习在学术上很热闹,但真正敢让线上模型"边跑边学"的生产系统少之又少。大多数团队选择的是一种折中方案——定期重训 + 灰度发布:每隔一段时间,用积累的新数据加上历史数据,重新训练一版模型,经过完整测试后替换线上版本。

这种做法牺牲了"实时进化"的能力,换来了可控、可测、可回滚。从工程角度,这笔账是划算的。毕竟,一个偶尔更新但稳定可靠的模型,比一个天天进化但时不时抽风的模型,对业务的价值大得多。

注意:如果你的业务确实需要模型快速适应新数据,别一上来就搞全量持续学习。先从"定期重训"做起,把数据管道、评估体系、回滚机制搭好,再考虑更激进的方案。跳过基础设施直接上持续学习,大概率会翻车。

5. 如果要做持续学习实验,具体怎么落地

前面讲的偏原理和思路,这一节给想动手的朋友一套可操作的方案。我按"数据准备—基线搭建—方法对比—评估"的顺序来讲,每一步都附上我踩过的坑。

5.1 数据集的切分方式决定了实验成败

持续学习实验和普通实验最大的区别在于数据的组织方式。普通实验是把所有数据混在一起打乱训练,持续学习则必须把数据切成有顺序的多个任务。

常见的切分协议有三种:

  • 任务增量(Task-Incremental):每个任务有明确的边界,测试时知道样本属于哪个任务,模型可以用任务ID辅助预测。这是最简单的设定。
  • 域增量(Domain-Incremental):任务之间输入分布不同但类别空间相同,比如不同光照下的同一批物体分类。
  • 类增量(Class-Incremental):最难的设定,每个任务引入新类别,测试时不知道样本属于哪个任务,模型必须自己区分所有见过的类别。

我的建议是:从任务增量开始,逐步过渡到类增量。类增量最接近真实场景,也最能暴露方法的缺陷。很多方法在任务增量下看着很美,一到类增量就原形毕露。

切分的时候有个细节要注意:任务之间的顺序会影响结果。同一组任务,换个学习顺序,遗忘程度可能差很多。所以严谨的实验应该跑多个随机顺序,取平均值,而不是只跑一个顺序就下结论。我早期就吃过这个亏,单次实验结论很漂亮,换了个顺序直接崩盘。

5.2 搭一个靠谱的基线

动手之前,先搭两个极端基线,它们是你判断任何新方法好坏的标尺:

基线一:朴素顺序训练(Naive / Fine-tuning)。就是不加任何抗遗忘手段,直接顺序训练。这个基线代表"最差情况",任何方法如果连它都打不过,就不用往下做了。

基线二:联合训练(Joint Training)。把所有任务的数据混在一起同时训练。这个基线代表"上限",因为它不存在遗忘问题(所有数据都在)。注意,联合训练在很多场景下是不允许的(旧数据不可得),但它能告诉你"如果不受限制,最好能做到什么程度"。

有了这两个基线,你的方法效果就有了参照系。一个方法如果接近联合训练,说明抗遗忘做得好;如果接近朴素顺序训练,说明基本没用。

5.3 关键参数与实操配置

下面给一份我常用的实验配置模板,基于图像分类任务,你可以按需调整。这里用伪代码风格描述,重点是参数选择的逻辑。

# 持续学习实验核心配置(示意) config = { "backbone": "小型卷积网络或预训练主干", # 小任务别用超大模型,训练慢且难调 "num_tasks": 5, # 任务数,建议5-10个,太少看不出趋势 "epochs_per_task": 10, # 每个任务训练轮数,太多会加剧遗忘 "batch_size": 64, "learning_rate": 0.01, # 持续学习里学习率要偏小,减小参数移动幅度 "optimizer": "SGD with momentum", # 实测SGD比Adam遗忘更轻,值得一试 "replay_buffer_size": 200, # 回放缓冲区容量,按任务数均分 "replay_ratio": 0.5, # 新旧样本比例,1:1起步 }

几个参数背后的逻辑值得展开说:

学习率为什么要偏小?因为参数移动幅度直接决定遗忘程度。学习率大,参数一步跨得远,旧知识被覆盖得就狠。把学习率调小,虽然新任务学得慢一点,但旧任务保得住。这是一个明确的权衡。

为什么推荐SGD而不是Adam?这是我实测的一个经验。Adam的自适应学习率会让某些参数更新得特别激进,反而更容易破坏旧知识。SGD虽然收敛慢,但更新更均匀、更温和,遗忘往往更轻。当然这不是绝对的,具体任务要试。

回放比例怎么定?如果新任务数据远多于回放数据,模型会被新任务主导。一般从1:1开始调,如果发现旧任务还是掉得厉害,就提高回放样本的比例。但回放太多又会拖慢新任务的学习,需要找平衡点。

5.4 评估指标不能只看准确率

持续学习的评估比普通任务复杂,因为你要同时看"新任务学得好不好"和"旧任务忘没忘"。常用的指标有这么几个:

  • 平均准确率(Average Accuracy):学完所有任务后,在所有任务上的平均表现。这是最直观的指标。
  • 遗忘率(Forgetting Measure):每个任务"刚学完时的准确率"减去"全部学完后的准确率",再取平均。这个指标专门衡量遗忘程度,非常关键。
  • 反向迁移(Backward Transfer):学新任务对旧任务的影响。正值表示新任务帮到了旧任务,负值表示拖累了旧任务。

我特别想强调遗忘率这个指标。很多人只看平均准确率,但平均准确率会被新任务的强势表现掩盖问题。一个模型可能平均准确率不错,但拆开看是"新任务满分、旧任务全崩",这种模型在实际场景里是没法用的。一定要把遗忘率单独拎出来看。

6. 常见问题与排查技巧实录

这一节整理我在做持续学习和增量训练时遇到的高频问题,配上排查思路。这些问题在标准文档里很少写,但实际动手时几乎都会碰到。

6.1 问题速查表

问题现象可能原因排查方向解决思路
旧任务准确率断崖式下跌学习率过大、无抗遗忘手段检查学习率、确认是否用了回放/正则降学习率、加回放、加正则
新任务怎么都学不好正则约束过强、回放比例过高检查正则权重、回放比例放松约束、降低回放比例
任务越多效果越差容量不足、约束互相冲突看参数量、看各任务约束叠加增大容量、改用回放为主
结果波动大、不可复现任务顺序敏感、随机种子未固定换顺序重跑、固定种子多顺序取平均、固定所有随机源
回放后仍遗忘严重回放样本太少或代表性差检查缓冲区大小和采样策略增大缓冲、用分层采样

6.2 几个容易忽视的坑

坑一:忘了固定随机种子。持续学习实验对随机性极其敏感,数据顺序、参数初始化、dropout都会影响结果。我见过同一份代码跑两次,遗忘率差了十几个百分点。做对比实验时,务必固定所有随机源,并且跑多次取平均。

坑二:用错了归一化统计量。如果用了批归一化(BatchNorm),不同任务的数据分布不同,统计量会互相污染。学新任务时,BatchNorm的均值和方差会被新数据带偏,间接导致旧任务性能下降。解决办法是给每个任务独立的归一化统计量,或者改用不受批次影响的归一化方式。这个坑很隐蔽,因为表面上看是"遗忘",实际是归一化在捣乱。

坑三:评估时用了训练模式。测试的时候一定要把模型切到评估模式,关掉dropout和BatchNorm的更新。这个听起来是常识,但在持续学习的复杂流程里,很容易在某个环节忘了切换,导致评估结果失真。

坑四:缓冲区采样策略太随意。回放缓冲区如果只是随机存、随机取,可能存了一堆相似样本,代表性差。更好的做法是分层采样或者基于多样性采样,保证缓冲区覆盖各个任务、各类别的典型样本。我试过用随机采样和分层采样对比,后者在类增量场景下明显更稳。

6.3 一个实用的调试顺序

当你发现实验结果不对劲时,别急着改方法,按这个顺序排查:

  1. 先确认基线正常:朴素顺序训练是不是真的遗忘了?联合训练是不是真的不遗忘?如果基线都不对,后面全白搭。
  2. 再确认数据切分正确:每个任务的类别、样本数、顺序是否符合预期?打印出来核对。
  3. 然后看单任务表现:模型在每个任务"刚学完"时的准确率是否正常?如果单任务都学不好,问题在训练本身,不在遗忘。
  4. 最后看遗忘曲线:把每个任务在后续所有阶段的准确率画成曲线,遗忘发生在哪一步、有多严重,一目了然。

这个顺序能帮你快速定位问题出在"训练"还是"遗忘",避免在错误的方向上瞎调参。

7. 我对这件事的一点个人看法

聊了这么多技术细节,最后说点偏感受的东西。

灾难性遗忘这个问题,本质上暴露的是当前神经网络学习范式的一个根本局限:它把知识压缩在共享参数里,却没有机制去保护旧知识。人类大脑不是这样工作的。我们学新东西时,大脑会通过复杂的机制巩固旧记忆,睡眠、记忆重放都参与其中。神经网络目前还没有这么精妙的机制。

持续学习这个领域研究了这么多年,方法出了一堆,但至今没有一个能像联合训练那样彻底解决问题。这让我觉得,可能真正的突破不在于"打补丁"式的正则或回放,而在于学习范式本身的改变。也许未来的模型会有更结构化的知识存储方式,也许会有类似大脑记忆巩固的机制,让新旧知识真正和谐共存。

至于大模型的"冻结人生",我现在的态度是理解并接受。在更好的方案成熟之前,冻结是一种负责任的工程选择。它不完美,但它稳定、可控、可回滚。与其追求一个天天进化但随时可能崩的模型,不如先把一个稳定的模型用扎实。

如果你正在做相关的工作,我的建议是:别被"持续学习"这个词的热度带偏,先想清楚你的业务到底需不需要模型实时进化。很多场景下,定期重训就够了,硬上持续学习反而增加了一堆复杂度和风险。技术选型永远要服务于实际需求,而不是追新逐热。

这个方向后续还有很多可以挖的,比如持续学习和参数高效微调的交叉、大模型时代如何做低成本的增量更新、评估体系怎么跟上。等我把手头的实验跑完,再来分享新的踩坑记录。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询