在传统的机器学习范式中,模型通常在固定的数据集上一次性训练完成。但现实世界里,数据往往是源源不断到来的:推荐系统每天迎来新用户,机器人不断遇到新环境,聊天机器人需要持续学习新话题。如果我们直接让模型在新数据上继续训练,很快就会发现一个尴尬的现象——模型学会了新任务,却把旧任务忘得干干净净,甚至性能不如随机初始化。这就是灾难性遗忘,也是持续学习领域要解决的头号难题。

灾难性遗忘为什么会产生
要理解灾难性遗忘,先要看清神经网络的参数共享机制。一个模型的容量是有限的,所有任务的知识都压缩在同一组权重里。当我们在新任务上训练时,梯度下降的唯一目标是降低新任务的损失,它完全不知道哪些参数承载着旧任务的关键信息。如果新旧任务对某个参数的“期望值”发生冲突,优化器会毫不犹豫地把参数拉向新任务需要的位置,旧任务的决策边界随之被破坏。
从数学角度看,假设模型参数为θ,旧任务训练结束后参数位于θ*,它是旧任务损失的一个极小值点。新任务的损失曲面在参数空间中有着不同的极小值区域,SGD会沿着新任务的梯度方向移动参数。一旦移动距离过大,偏离了旧任务损失的平坦区域,旧任务性能就会断崖式下跌。研究还发现,遗忘的严重程度与任务之间的分布差异呈正相关:两个任务的数据分布差异越大,参数空间的冲突越剧烈,遗忘越严重。
另一个加剧遗忘的因素是过拟合式的快速学习。采用随机梯度下降加小批量数据时,模型可以在极少的步数内拟合新任务的少量样本,这种“快速可塑性”在持续学习中反而是危险的——参数被剧烈改动,旧知识来不及得到任何保护。
Rehearsal方法:用旧数据温故知新
Rehearsal回放是最直观的抗遗忘方案:既然遗忘源于缺少旧数据,那就在训练新任务时,混入一部分旧任务的样本一起训练。思路简单的背后是坚实的理论支撑——经验回放等价于在新旧任务的联合分布上做近似优化,只要回放数据能代表旧分布,模型就不会单方面偏向新任务。
实际工程中,我们不可能保存全部历史数据,常见的做法是维护一个固定大小的记忆缓冲区,按类别均匀采样保留少量旧样本。下面是一个简化实现:
import torch
import numpy as np
class ReplayBuffer:
def __init__(self, capacity=2000, per_class=20):
self.capacity = capacity
self.per_class = per_class
self.images, self.labels = [], []
def update(self, imgs, labels):
# 每类只保留固定数量的样本,保证类别均衡
for x, y in zip(imgs, labels):
if len(self.images) < self.capacity:
self.images.append(x)
self.labels.append(y)
else:
idx = torch.randint(0, len(self.images), (1,)).item()
self.images[idx] = x
self.labels[idx] = y
def sample(self, batch_size=64):
idx = np.random.choice(len(self.images), batch_size, replace=False)
return torch.stack([self.images[i] for i in idx]), torch.tensor([self.labels[i] for i in idx])
# 训练新任务时的用法
for x_new, y_new in new_task_loader:
x_old, y_old = buffer.sample(batch_size=len(x_new))
x_mix = torch.cat([x_new, x_old])
y_mix = torch.cat([y_new, y_old])
logits = model(x_mix)
loss = criterion(logits, y_mix)
loss.backward()
optimizer.step()
optimizer.zero_grad()
buffer.update(x_new, y_new)
回放方法的效果通常非常好,因为旧数据直接参与了损失计算,约束力最强。但它有一个绕不开的软肋:隐私与存储。医疗、金融等场景下,旧数据可能因合规要求无法长期保留;边缘设备上存储几百兆的旧样本也不现实。为此,研究者提出了生成式回放,用GAN或扩散模型生成伪旧样本来代替真实数据,以及特征层面的回放,只保存中间层特征而非原始输入,都在一定程度上缓解了存储与隐私压力。
正则化方法:不动旧数据也能保住旧知识
如果场景不允许保留任何旧数据呢?正则化方法提供了另一条路:不回放数据,而是在损失函数中加一项约束,惩罚那些对旧任务重要的参数发生变化。其核心思想是,旧任务训练完后,模型已经隐含了“哪些参数重要”的信息,把这个信息量化出来,就能在新任务训练时保护关键权重。
最有代表性的是EWC,弹性权重巩固。它利用 Fisher 信息矩阵来估计每个参数对旧任务的重要性:Fisher 值越大,说明该参数的微小变动对旧任务损失影响越大。EWC将Fisher矩阵近似为对角矩阵,大幅降低计算量。新任务的损失函数变成如下形式:
import torch
class EWCLoss:
def __init__(self, model, old_params, fisher, lambda_=5000):
self.old_params = {n: p.detach().clone() for n, p in old_params.items()}
self.fisher = {n: f.detach() for n, f in fisher.items()}
self.lambda_ = lambda_
def __call__(self, model):
penalty = 0
for n, p in model.named_parameters():
penalty += (self.fisher[n] * (p - self.old_params[n]) ** 2).sum()
return self.lambda_ * 0.5 * penalty
# 计算对角Fisher信息:在旧任务数据上统计梯度平方的期望
def compute_fisher(model, data_loader, samples=1000):
fisher = {n: torch.zeros_like(p) for n, p in model.named_parameters()}
model.eval()
count = 0
for x, y in data_loader:
model.zero_grad()
out = model(x)
loss = torch.nn.functional.cross_entropy(out, y)
loss.backward()
for n, p in model.named_parameters():
if p.grad is not None:
fisher[n] += p.grad.detach() ** 2
count += x.size(0)
if count >= samples:
break
return {n: f / max(count, 1) for n, f in fisher.items()}
除了EWC,还有两条重要的分支。SI,突触智能,在旧任务训练过程中在线累积每个参数对损失下降的贡献,无需额外一轮Fisher计算,实现更轻量。LwF,Learning without Forgetting,则走了另一条路:它不约束参数本身,而是约束输出,训练新任务时要求模型在新数据上的旧任务输出(通过知识蒸馏)与旧模型的输出保持一致,相当于把旧知识蒸馏进了新模型。这种“软化”的约束对网络结构变化的容忍度更高。
正则化方法的优势是存储开销极小,只需保存一份参数副本和重要性权重,且天然规避隐私问题。缺点也很明显:它只能“减缓”参数漂移,当新任务与旧任务冲突剧烈、或任务序列很长时,正则化项会与新任务损失互相拉扯,导致两边都学不好,出现稳定性与可塑性的两难。
两类方法如何选择
Rehearsal与正则化并非非此即彼,实际选型要结合数据约束、任务数量和算力预算来权衡。可以参考下面的对比:
| 维度 | Rehearsal回放 | 正则化方法(EWC/SI/LwF) |
|---|---|---|
| 旧数据需求 | 需要保留少量原始样本或特征 | 不需要,只存参数和重要性权重 |
| 抗遗忘强度 | 强,旧数据直接参与训练 | 中等,长任务序列下会逐渐衰减 |
| 隐私合规 | 存在风险,需脱敏或生成式回放 | 友好,不涉及原始数据 |
| 实现复杂度 | 低,主要是缓冲区管理 | 中等,需计算Fisher或在线重要性 |
| 额外计算开销 | 每步多算一个批量的前向反向 | 只在任务边界一次性计算 |
如果数据合规允许、存储充足,优先选Rehearsal,它的性能上限更高;如果旧数据必须删除,正则化是首选起点,其中LwF适合输出空间稳定的分类任务,EWC和SI适合参数级保护。还有一种务实的混合策略:用正则化兜底,同时保留极少量旧特征做回放,在很多基准实验中,这种组合的抗遗忘效果接近联合训练的上界。
最后提醒一点工程细节:无论选择哪种方法,持续学习的评估一定不能只看最后一个任务的准确率,而要报告平均准确率和遗忘度量(旧任务性能的最大跌幅)。一个只在新任务上表现完美、把历史任务全部遗忘的模型,在持续学习场景下是失败的。选对评估指标,才能真正衡量抗遗忘方法的价值。