章节导航 第 15 章 / 共 30 章
灾难性遗忘与持续学习
连续训练中的灾难性遗忘机理与数据侧缓解(重放/正则/数据混合)。
“学习新事物最困难的部分不是记住新东西,而是不忘掉旧东西。”
这句话对大模型同样成立——甚至更为残酷:模型不会告诉你它忘了什么。
15.1 灾难性遗忘(Catastrophic Forgetting)
什么是遗忘?
在中训练的语境下,灾难性遗忘指的是:模型在新数据上训练后,在之前已经掌握的任务或知识上表现显著退化。
这不是一个理论风险——它是每个做过中训练的团队都会遇到的实际问题。
一个典型的场景:
- 你有一个通用 7B 模型,MMLU 分数 55%,C-Eval 50%
- 你在 200B 的代码+数学数据上做中训练
- 中训练后:HumanEval 从 25% 飙升到 50% ✓
- 但是:MMLU 从 55% 跌到 45%,C-Eval 从 50% 跌到 38% ✗
你增强了代码能力,但付出了丢失通用知识的代价。
遗忘的机制
从参数更新的角度理解遗忘:
-
参数覆盖:模型的权重是有限的。当在新分布上训练时,参数会被更新以适应新分布,但这些更新可能覆盖了之前学到的模式
-
表示漂移:中间层的表示会向新数据的分布漂移。如果新数据和旧数据的分布差异大,漂移就大,遗忘就严重
-
注意力重分配:注意力头可能从关注”通用模式”转向关注”领域特定模式”,导致通用能力下降
遗忘的函数空间理论:为什么参数空间正则化器会失效
上节描述的”参数覆盖/表示漂移/注意力重分配”是参数空间视角的解释。但 2026 年 6 月 Tel Aviv University 的 Hidekel & Raviv(ICML 2026 Workshop)从函数空间(NTK 框架)给出了更根本的解释,并直接挑战了 EWC/SI/MAS/RWalk 等参数空间正则化器的基础假设。
NTK 视角的核心洞察:
参数空间视角(传统):
遗忘 = 参数 θ 偏离 θ_old → 用 Fisher 矩阵 / SI / MAS 惩罚 θ 漂移
└── 问题:参数空间是高维的,惩罚"哪些方向"难以精确瞄准输出空间的脆弱点
函数空间视角(NTK):
遗忘 = 输出 f(x) 偏离 f_old(x) → 跨任务核函数 K_AB 决定漂移方向
└── 优势:遗忘集中在旧任务 NTK 特征模态的少数方向上
闭式遗忘向量预测器:
Hidekel & Raviv 的核心定理是:在 NTK 体制下,新任务训练尚未执行任何梯度步之前,就能通过跨任务核函数精确预测遗忘向量的方向和大小:
其中关键观察是:
- 在冻结骨干+线性头 PEFT-CL 设定下,预测器在 float32 数值精度内精确
- 在非线性适配器/全模型微调下,作为局部 NTK 近似(cosine similarity > 0.994)
- 遗忘能量集中在旧任务 NTK 矩阵 的少数特征模态上
- Split-MNIST/CIFAR-10 实验:1-6 个模态承载 50-90% 的遗忘能量
Kronecker 缩放规则(冻结线性头下的解析结论):
在冻结骨干+任务共享可训练线性头的设定下, 可分解为 Kronecker 积:
其中 是输出维度, 是特征 Gram 矩阵的有效秩。这意味着:
- 脆弱秩 = 输出维度 × 特征秩——这是冻结线性头下可计算的精确公式
- 保护所有参数既无必要(大部分方向不脆弱),也徒增成本
为什么参数空间正则化器会失效:
EWC / SI / MAS / RWalk 的共同盲区:
L2 参数正则化(Fisher 加权):
└── 惩罚方向:参数空间高维
└── 实际脆弱方向:NTK 特征模态空间低秩(k=1~6)
└── 后果:在参数空间广撒胡椒面 → 在脆弱方向上可能漏防
共享头基准(shared-head benchmarks)上的失败模式:
└── L2 正则化假设"所有参数同等重要"
└── 实际:仅 NTK 前 k 个模态占脆弱性的 50-90%
└── 结果:在共享头设定下,参数空间正则化器效果远低于函数空间方法
靶向谱正则化器(Targeted Spectral Regularizer):
基于上述理论,Hidekel & Raviv 提出了只在 NTK 脆弱子空间上加正则化的方案:
"""
靶向谱正则化器(伪代码)
"""
import torch
def targeted_spectral_regularizer(
model,
old_task_ntk_eigvecs: torch.Tensor, # 旧任务 NTK 前 k 个特征向量
lambda_spec: float = 1.0,
) -> torch.Tensor:
"""
只在 NTK 脆弱子空间(old_task_ntk_eigvecs 张成)上惩罚参数漂移
其他方向(占参数空间绝大多数)不加惩罚
"""
penalty = 0.0
for name, param in model.named_parameters():
if name in old_task_ntk_eigvecs:
# 将参数漂移投影到脆弱子空间
drift = param - old_task_ntk_eigvecs[name]['anchor']
projection = old_task_ntk_eigvecs[name]['eigvecs'] @ \
old_task_ntk_eigvecs[name]['eigvecs'].T @ \
drift.flatten()
penalty += projection.norm() ** 2
return lambda_spec * penalty
对章节理论的补充意义:
| 章节方法 | 参数空间视角下的解释 | 函数空间视角下的局限性 |
|---|---|---|
| EWC | Fisher 对角加权 L2 | 惩罚方向 ≠ 脆弱方向,参数空间高维浪费 |
| SI | 路径积分 ω 累积 | 同上,ω 高维但脆弱方向低秩 |
| MAS | 输出敏感度 |∂f/∂θ| | 接近正确方向,但仍分散在参数空间 |
| OPLoRA | 奇异方向正交投影 | 部分对齐——仅在 SVD 主导方向上对齐,未必在 NTK 模态上对齐 |
对工程实践的指导:
- 诊断优先:在设计正则化器之前,先计算旧任务的 NTK 矩阵并定位前 k 个脆弱模态(k=1~6 通常足够)
- 保护什么:基于脆弱模态设计保护机制,而不是基于参数幅值
- 避免冗余保护:参数空间大部分方向对遗忘贡献微弱,不需要保护
遗忘的模式
2025 年 ACL 的论文《Exploring Forgetting in LLM Pre-Training》首次系统性地研究了预训练/中训练过程中的遗忘模式,发现遗忘不是均匀的:
选择性遗忘:
- 低频知识比高频知识更容易被遗忘
- 与新训练数据分布差异大的知识更容易被遗忘
- 跨语言知识比同语言知识更脆弱
遗忘的阶段性:
- 遗忘在中训练初期最严重(前 10% 的训练步)
- 之后遗忘速率逐渐放缓
- 但总遗忘量随训练量增加而持续增长
模型规模的影响:
- 令人意外的是,更大的模型遗忘更严重
- 假说:大模型的参数有更多”冗余”用于适应新分布,因此更容易发生参数覆盖
- 但大模型的绝对能力基数也更高,所以遗忘后的绝对水平可能仍然高于小模型
虚假遗忘:性能下降 ≠ 知识丢失
上面讨论的遗忘机制默认了一个假设:评估指标下降就意味着模型丢失了知识。 但这个假设并不总是成立。
2025 年 ICLR 上发表的 Spurious Forgetting 研究首次揭示了一种被长期忽视的现象——虚假遗忘(Spurious Forgetting):模型在持续学习后,虽然在旧任务上的评估分数下降了,但知识并未真正丢失。真正发生的是模型丧失了对旧任务格式的任务对齐(task alignment)。
真实遗忘 vs 虚假遗忘:
真实遗忘(Genuine Forgetting):
└── 知识确实从参数中被覆盖/丢失
└── 无论怎么提示,模型都无法恢复
虚假遗忘(Spurious Forgetting):
└── 知识仍存在于参数中
└── 但模型"忘记了"如何按旧格式输出
└── 通过少量 re-alignment(如 few-shot 提示或轻量微调)可以恢复
这一发现的实践意义很大:
- 评估遗忘前先做 re-alignment 验证:在判定模型”遗忘”之前,先尝试用少量 in-context examples 或轻量提示工程恢复表现。如果能恢复,就是虚假遗忘,不需要加大回放比例
- 避免对虚假遗忘过度反应:如果把虚假遗忘当真实遗忘处理,可能会不必要地增加回放数据量,浪费训练容量
- 监控体系需要升级:仅看 benchmark 分数变化不够,需要区分两类遗忘。实操上可以在评估 pipeline 中增加一个”re-alignment 验证”步骤
可访问性失败:根叙事的重大更新(Accessibility Collapse)
虚假遗忘揭示了”性能下降 ≠ 知识丢失”的一个边界,但 2026 年 6 月 Trivedi & Melwani 提出的 Accessibility Collapse(三层框架) 给出了一个更具颠覆性的视角:灾难性遗忘应被描述为”可访问性失败”而非”完全表征擦除”。
知识的三层分离(Accessibility Collapse 框架):
Layer 1: 知识存储(Storage)
└── 任务解决方案在 checkpoint 中是否被物理保留?
└── 实证:所有任务 checkpoint 都能保持其原始准确率
└── 结论:✅ 存储完整,未被擦除
Layer 2: 知识表征(Representation)
└── 任务信息是否在网络特征空间中被编码?
└── 实证:行为性塌缩到 0%,但线性探针保留 76% 信息
└── 结论:✅ 表征仍在,但可访问性丧失
Layer 3: 知识可访问性(Accessibility)
└── 模型能否在标准推理路径下访问到这些表征?
└── 实证:54.8% → 0% 的行为性塌缩
└── 结论:❌ 访问失败,是真正的失效点
关键实验证据(基于 CIFAR-100 sequential + ResNet-18):
| 实验 | 测量目标 | 结果 | 含义 |
|---|---|---|---|
| 行为性任务准确率 | 输出是否正确 | 54.8% → 0% | 表面看完全遗忘 |
| 线性探针准确率 | 末层特征是否含信息 | 0.468(= checkpoint 的 76%) | 表征基本保留 |
| 重训最终分类器 | 仅恢复最后一层 | 恢复 75.7% 原始任务性能 | 骨干未被破坏 |
| 分层保留率 | 哪一层丢失 | Layer 1-3 保持 100-104%;Layer 4 和分类器层严重退化 | 遗忘高度局部化 |
对章节根叙事的颠覆:
之前的章节叙事隐含一个假设——灾难性遗忘 = 知识擦除。Accessibility Collapse 框架将这一假设拆解为三个独立维度,并实证表明:
- 知识本身没有被擦除
- 知识表征大部分被保留
- 真正的失败发生在输出层——即”可访问性”维度
对实践的指导意义:
之前认知:模型"忘了"旧任务 → 需要完整回放保护
现在认知:模型"够不到"旧任务 → 仅重训最后一层即可恢复 75.7%
新的轻量级恢复方案:
1. 保留预训练/前一阶段模型的特征提取器(frozen)
2. 仅重训输出层分类器/输出头
3. 在保留的表征上 + 少量标注 → 性能快速回升
与虚假遗忘的关系:Accessibility Collapse 是”虚假遗忘”的根本性解释——虚假遗忘本质上就是可访问性失败的体现。两者在诊断上互补:虚假遗忘是”可恢复 vs 不可恢复”的二分,Accessibility Collapse 则是”知识还在但访问失败”的连续谱。
哪些能力最容易被遗忘?
基于多个研究和实践经验的总结:
| 能力维度 | 遗忘风险 | 原因 | 保护策略 |
|---|---|---|---|
| 世界知识(MMLU 类) | 高 | 知识点分散,中训练不覆盖就会退化 | 回放通用数据 |
| 语言理解(阅读理解) | 中 | 基础语言能力较鲁棒 | 保留自然语言数据 |
| 数学推理 | 中-高 | 依赖特定的推理模式 | 保留数学数据 |
| 代码生成 | 中 | 依赖语法和模式记忆 | 保留代码数据 |
| 多语言能力 | 极高 | 非目标语言的数据减少后快速退化 | 多语言回放 |
| 创意写作 | 高 | 格式化数据训练压制创造性 | 保留文学数据 |
| 安全对齐 | 中 | 中训练可能冲淡安全训练 | 重新做安全对齐 |
15.2 数据层面的遗忘防控
先识别遗忘源:合成数据微调本身就会造成遗忘
在讨论”怎么防”之前,先补一个常被忽略的遗忘源:合成数据微调本身。生产级 skill router 场景的研究(arXiv:2609.10750,2026-09-09,覆盖 34,396 个 skill)给出明确结论:合成数据微调提升分布内检索,但会在真实数据与 OOD 数据上造成灾难性遗忘。
缓解手段正是本节后面要讲的那几类持续学习方法——embedding-anchor 正则化、LwF(Learning without Forgetting)、EWC、L2-initialization。值得注意的是结果的另一面:这些方法不仅保住了 OOD 性能,还把分布内合成数据的检索进一步提升 13.98%(0.6B Qwen retriever + reranker)。
实践含义:抗遗忘措施不总是纯成本——在这个案例里它同时带来了分布内收益。这对说服团队投入回放/正则预算很有帮助。(与 Ch14 §14.3 中训练合成数据一节互为参照。需注意该研究场景是检索型 skill router,与本书的预训练/中训练场景不同,结论迁移时应保留场景限定;该工作目前为预印本。)
回放策略(Experience Replay)
回放是对抗遗忘最有效且最直接的方法。 核心思路:在中训练数据中混入一定比例的预训练数据(或其高质量子集),让模型在学习新知识的同时”复习”旧知识。
"""
回放数据的配置框架
"""
from dataclasses import dataclass
from typing import Dict
@dataclass
class ReplayConfig:
"""回放策略配置"""
# 回放数据占中训练总数据的比例
replay_ratio: float = 0.3
# 回放数据的来源分布(应该近似预训练的分布)
replay_distribution: Dict[str, float] = None
# 是否按能力维度精选回放数据
targeted_replay: bool = True
def __post_init__(self):
if self.replay_distribution is None:
self.replay_distribution = {
"high_quality_web": 0.40,
"code": 0.15,
"math": 0.10,
"academic": 0.10,
"books": 0.10,
"multilingual": 0.10,
"creative_writing": 0.05,
}
def design_replay_strategy(
midtraining_objective: str,
midtraining_tokens: float,
forgetting_tolerance: str = "low"
) -> ReplayConfig:
"""
根据中训练目标设计回放策略
forgetting_tolerance:
- "low": 不能接受任何能力退化 → 回放比例高
- "medium": 可以接受 1-2% 的退化 → 回放比例中等
- "high": 可以接受 3-5% 的退化 → 回放比例低
"""
# 回放比例
ratio_map = {
"low": 0.40,
"medium": 0.25,
"high": 0.15,
}
replay_ratio = ratio_map.get(forgetting_tolerance, 0.25)
# 根据中训练目标调整回放分布
if midtraining_objective == "code":
# 代码中训练时,回放重点保留数学和自然语言
replay_dist = {
"high_quality_web": 0.30,
"math": 0.20, # 保护数学能力
"academic": 0.15,
"books": 0.15,
"multilingual": 0.15,
"creative_writing": 0.05,
}
elif midtraining_objective == "domain_medical":
# 医学领域中训练,回放重点保留通用能力
replay_dist = {
"high_quality_web": 0.35,
"code": 0.15,
"math": 0.10,
"academic": 0.15,
"books": 0.10,
"multilingual": 0.10,
"creative_writing": 0.05,
}
elif midtraining_objective == "long_context":
# 长上下文扩展,回放保持原有能力
replay_dist = {
"high_quality_web": 0.30,
"code": 0.20,
"math": 0.15,
"academic": 0.15,
"books": 0.10,
"multilingual": 0.10,
}
else:
replay_dist = None # 使用默认分布
return ReplayConfig(
replay_ratio=replay_ratio,
replay_distribution=replay_dist,
targeted_replay=True,
)
# 示例
config = design_replay_strategy("code", 500e9, "medium")
print(f"回放比例: {config.replay_ratio*100}%")
print(f"回放数据量: {config.replay_ratio * 500e9 / 1e9:.0f}B tokens")
for domain, ratio in config.replay_distribution.items():
print(f" {domain}: {ratio*100:.0f}%")
回放比例的选择
这是中训练中最关键的超参数之一。以下是经验性的指导:
| 中训练场景 | 经验回放比例 | 理由 |
|---|---|---|
| 长上下文扩展 | 30-40% | 数据分布变化小,但能力要求高 |
| 代码/数学增强 | 25-35% | 数据分布变化中等 |
| 单一领域注入 | 35-50% | 数据分布变化大,遗忘风险高 |
| 多语言增强 | 30-40% | 需要保护原语言能力 |
| 综合中训练 | 20-30% | 目标数据本身已较多样 |
⚠️ 注意:上表是基于 2023-2024 年工程经验的经验性指导,适合作为快速参考的起点。但 2025-2026 年的研究表明,回放比例可以通过 Scaling Laws 更精确地计算。
从经验值到 Scaling Law 预测
两项研究从根本上改变了回放比例的设定方式:
Apple Forgetting Scaling Laws(ICLR 2026)推导出遗忘量与回放比例之间存在幂律关系,并量化了目标域、可用目标数据量、模型规模三个维度的交互。关键发现:在特定条件下,仅需混入 1% 的预训练数据即可有效防止遗忘——远低于传统经验的 20-40%。
CMR Scaling Law(EMNLP 2024)提出了 Critical Mixture Ratio 的概念:给定目标域和模型规模,可以用小规模实验预测”恰好不遗忘”的最小回放比例。这使得回放比例可以从”经验猜测”走向”数学预测”。
CPT Scaling Law(Wang et al., 2025)则将视角从”终点状态”扩展到”训练过程动态”——不再只关心训练结束时的遗忘量,而是试图预测持续预训练(Continual Pre-Training, CPT)过程中每一步的损失轨迹。其核心贡献在于将 CPT 的损失变化解耦为两个独立驱动因素:
- 学习率退火效应:随训练推进、学习率逐步衰减,模型在任意数据上的损失自然下降——这与数据内容无关,纯粹是优化动力学的结果
- 数据分布偏移效应:从预训练数据切换到新领域数据,模型需要适应新的分布,这一过程导致的损失变化取决于新旧分布的差异程度
CPT Scaling Law 将持续预训练概念化为一条从”初始预训练轨迹”到”新领域特定轨迹”的过渡:模型在训练初期仍沿预训练轨迹运行,随着训练推进逐渐过渡到新轨迹。两个因素各自贡献可分解、可预测的损失分量,使得研究者能够在训练开始前就绘制出完整的 CPT 损失曲线——而不仅仅是终点的单一数值。
遗忘缩放定律的三代演进:
Apple Forgetting Scaling Laws:
└── 终点度量:训练结束后,遗忘量 vs 回放比例的幂律关系
└── 回答:"最终会忘多少?"
CMR Scaling Law:
└── 临界比例:恰好不遗忘的最小回放比例
└── 回答:"至少要回放多少?"
CPT Scaling Law:
└── 训练动力学:解耦 LR 退火与分布偏移,预测全程损失轨迹
└── 回答:"训练过程中每一步表现如何?"
这意味着遗忘不仅可以从终点状态衡量,还可以从训练过程中的动力学理解——例如,在训练早期出现的大幅损失上升,究竟是因为学习率尚未充分退火,还是因为分布偏移过于剧烈?CPT Scaling Law 的分解框架为这类诊断提供了定量工具。
三项研究的实践指导可以归纳为:
回放比例的设定路线:
经验时代(2023-2024):
└── 查表 → 设定 20-40% → 观察效果 → 手动调整
Scaling Law 时代(2025-):
└── 小规模实验 → 拟合幂律关系 → 计算 CMR → 精确设定
└── 关键变量:目标域分布差异越大 → 需要回放越多
模型规模越大 → 需要回放越少
回放数据质量越高 → 需要回放越少
需要注意的是,1% 是理想条件下的下界。实际工程中通常需要更高比例来覆盖多个能力维度(语言、推理、知识等)。但核心变化是:不再盲目设定 30%,而是根据具体场景计算最优比例。
💡 反直觉发现
回放数据不需要和预训练数据一模一样。实际上,用预训练数据的高质量子集做回放效果更好——因为回放的目标不是”重现预训练”,而是”保持关键能力”。高质量子集在更少的 token 中包含了更密集的知识信号。这也解释了为什么 Scaling Law 预测的比例远低于经验值——质量上去了,数量可以下来。
智能回放选择:从静态到动态
上面讨论的回放配比假设回放数据是预先选好的静态子集。但回放的效果还取决于另一个维度:选什么样本来回放。
SuRe(Surprise-prioritised Replay)提出将回放从”静态选择”升级为”动态优先级排序”,核心思想是将回放效果拆解为两个独立维度:
- 选择策略(What to replay):不是随机选,而是用模型当前参数对候选回放数据计算 NLL(负对数似然),优先回放模型最”惊讶”的样本——这些正是模型正在遗忘的内容
- 整合方式(How to consolidate):使用 EMA(指数移动平均)维护一个慢权重(slow weights),快学习器适应新数据,慢学习器保持旧知识的表示
实践定位:静态均匀回放仍然是合理且简单的默认策略。惊奇度驱动的回放更适用于回放预算受限、需要最大化每个回放样本效率的场景。这种方法目前主要在持续学习研究中得到验证,尚未在大规模中训练(>100B token)中被广泛采用。
配比策略:新域数据 vs 旧域数据
"""
中训练数据配比的决策框架
"""
def midtraining_mix_calculator(
target_abilities: list, # 要增强的能力列表
ability_priorities: dict, # 每个能力的优先级 (1-10)
total_tokens: float, # 总训练 token 数
available_data: dict, # 各类数据的可用量
forgetting_tolerance: str = "low" # 遗忘容忍度
) -> dict:
"""
计算中训练的数据配比
算法:
1. 先分配回放数据(保底)
2. 按优先级分配目标能力数据
3. 用高质量通用数据填充剩余
"""
# Step 1: 回放分配
replay_ratios = {"low": 0.35, "medium": 0.25, "high": 0.15}
replay_ratio = replay_ratios[forgetting_tolerance]
replay_tokens = total_tokens * replay_ratio
# Step 2: 目标能力数据分配
remaining = total_tokens - replay_tokens
total_priority = sum(ability_priorities.values())
target_allocation = {}
for ability in target_abilities:
priority = ability_priorities.get(ability, 5)
allocation = remaining * (priority / total_priority)
# 不能超过可用量
available = available_data.get(ability, float('inf'))
allocation = min(allocation, available)
target_allocation[ability] = allocation
# Step 3: 剩余用通用高质量数据填充
allocated = sum(target_allocation.values()) + replay_tokens
filler = total_tokens - allocated
result = {
"total_tokens": f"{total_tokens/1e9:.0f}B",
"replay": {
"tokens": f"{replay_tokens/1e9:.0f}B",
"ratio": f"{replay_ratio*100:.0f}%",
},
"target_abilities": {
ability: {
"tokens": f"{tokens/1e9:.0f}B",
"ratio": f"{tokens/total_tokens*100:.1f}%",
}
for ability, tokens in target_allocation.items()
},
"filler_high_quality": {
"tokens": f"{filler/1e9:.0f}B",
"ratio": f"{filler/total_tokens*100:.1f}%",
}
}
return result
数据排列顺序对遗忘的影响
中训练数据的排列顺序不是无关紧要的。
三种策略:
-
均匀混合(Uniform Mixing)
- 每个 batch 中均匀混合新旧数据
- 优点:简单,遗忘最少
- 缺点:新知识的学习效率可能不是最优
- 推荐作为默认策略
-
渐进式过渡(Progressive Transition)
- 开始时回放比例高,逐渐降低;新数据比例逐渐升高
- 优点:平滑过渡,减少初期遗忘
- 缺点:实现稍复杂
-
先新后旧(New First, Then Review)
- 先大量训练新数据,然后用旧数据”恢复”遗忘
- 优点:新知识学习效率高
- 缺点:遗忘恢复可能不完全;如果旧数据不够,恢复效果差
- 不推荐
"""
中训练数据排列策略
"""
import numpy as np
def generate_mixing_schedule(
total_steps: int,
strategy: str = "uniform",
new_data_final_ratio: float = 0.6, # 最终新数据占比
) -> list:
"""
生成每个 step 的数据混合比例
返回每个 step 的 new_data_ratio
"""
if strategy == "uniform":
# 均匀混合
return [new_data_final_ratio] * total_steps
elif strategy == "progressive":
# 渐进式:从 0.3 线性增长到 final_ratio
start_ratio = 0.3
return [start_ratio + (new_data_final_ratio - start_ratio) * i / total_steps
for i in range(total_steps)]
elif strategy == "warmup_stable":
# warmup 后稳定
warmup_steps = int(total_steps * 0.1)
schedule = []
for i in range(total_steps):
if i < warmup_steps:
ratio = 0.2 + (new_data_final_ratio - 0.2) * i / warmup_steps
else:
ratio = new_data_final_ratio
schedule.append(ratio)
return schedule
else:
raise ValueError(f"Unknown strategy: {strategy}")
无 Replay 无 Task-ID 的架构新范式:TFGN
上面讨论的回放策略、正则化方法(EWC/SI/MAS)、参数隔离方法(O-LoRA/InfLoRA/SD-LoRA/OPLoRA)都有共同的依赖假设:要么需要 replay buffer 保留旧域数据,要么需要 task 标签来识别任务身份,要么需要 Fisher 矩阵等额外结构。这意味着在真实的开放持续预训练场景(无 task 边界、无数据保留、无任务元信息)下,这些方法都无法直接应用。
2026 年 5 月 Anurup Ganguli 提出的 TFGN(Task-Free, Replay-Free Gradient Network) 是第一个在 LLM 规模上同时满足”无 replay + 无 task-ID + 无 Fisher 惩罚”的架构突破。
核心思想:Read/Write 分解
传统方法的隐含假设:
稳定性 = 不写既往参数(参数空间加约束)
└── 代价:需要 task ID / replay buffer / Fisher 信息
TFGN 的洞察:
稳定性是"写"问题,不是"读"问题
读路径(前向)保持密集
写路径(参数更新)结构化地"避开既往子空间"
└── 代价:仅在写路径上加架构约束
架构设计要点:
- TFGN 是一个架构叠加层(architectural overlay),嵌入在 Transformer 每个 block 内部
- 不改变前向传播路径——前向完全密集(dense),无 MoE 风格的稀疏路由
- 仅结构化”写”路径:跨域参数更新被强制构造为”不写既往子空间”的形式
- 可训练参数仅占总参数 1-14%(parameter-efficient)
关键实验结果(6 个异构文本域 × 3 模型规模 × 2 范式):
| 指标 | 数值 | 含义 |
|---|---|---|
| 反向迁移(BWT, LLaMA-3.1 8B Retrofit) | -0.007 | 几乎完美保留旧域 |
| HellaSwag 保留(3 阶段) | 0.506 / 0.504 / 0.510 | 跨阶段漂移仅 0.006 |
| 跨域梯度 L2-orthogonal 分离 | ≥99.59% | 写路径结构化分离 |
| 跨域正向迁移(Python→JavaScript PPL 下降) | 26.8%(LLaMA-8B)/ 62.0%(GPT-2 Medium) | 旧域训练帮助新域 |
| Extension A(元控制层)额外减少遗忘 | 81% | 闭环元控制器 |
| Extension B(算子级计划向量)cosine fidelity | 99.96%(30 对) | 算子级潜在规划器 |
为什么是根本性突破:
传统持续学习方法谱系(章节前面讨论的):
├── Replay 类:需要保留旧域数据
│ ├── 经验回放(10.2.1)
│ └── SuRe 惊奇度回放
├── 正则化类:需要 Fisher / SI / MAS
│ ├── EWC
│ ├── SI / MAS / RWalk
│ └── Targeted Spectral Regularizer(NTK 视角)
├── 参数隔离类:需要 task 标签路由
│ ├── O-LoRA / InfLoRA / SD-LoRA
│ ├── OPLoRA(正交投影)
│ ├── KeepLoRA(SVD 子空间)
│ └── Merge before Forget(正交初始化)
└── 架构覆盖类:TFGN
├── 无 replay buffer ✅
├── 无 task IDs ✅
├── 无 Fisher 惩罚 ✅
└── 在 LLM 规模上闭解 ✅
与 NTK 理论的关系:TFGN 的写路径约束在经验上产生了 ≥99.59% 的梯度正交性,这与 10.1 节讨论的 NTK 函数空间理论形成呼应——TFGN 实际上是在架构层实现了 NTK 视角推荐的”靶向谱保护”。
对工程实践的影响:
- 真正的”持续预训练”成为可能:不需要保留任何历史数据,可应用于隐私敏感或合规要求严格的场景
- 多域预训练成本降低:不再需要精心设计的数据回放配比
- 元控制器与潜在规划器的结合:Extension A + B 提供了”自主学习”和”算子级规划”的 LLM 规模实现
- 代码/模型已开源:https://arxiv.org/abs/2605.15053v2(独立研究者 Anurup Ganguli)
适用边界:
- TFGN 在6 个异构文本域上验证(Prose/Python/Math/Biomedical/Chinese/JavaScript),但未在视觉或多模态上验证
- Extension A/B 是”工作演示”级别,工程化程度(训练稳定性、收敛速度)尚需大规模实践检验
- 与现有 EWC/O-LoRA/OPLoRA 等的对比基准尚未广泛建立
章节影响:
- 10.2 的”数据层面防控”和 10.3 的”训练策略层面”分类需要补充”架构层面”作为第三类
- 章节开头”灾难性遗忘 = 参数覆盖”叙事需要增加”架构约束可结构性闭解”的补充
- 章节不应再将”需要 replay”作为持续学习的硬性前提
15.3 训练策略层面的稳定性
学习率的选择
学习率是影响遗忘程度的最重要训练超参数。
经验法则:
- 中训练的起始学习率通常是预训练峰值学习率的 1/5 到 1/3
- 学习率越高,学习新知识越快,但遗忘也越严重
- 学习率越低,遗忘越少,但学习效率也越低
预训练峰值 lr = 3e-4
├── 激进中训练: lr = 1e-4 → 学得快,忘得多
├── 平衡中训练: lr = 5e-5 → 推荐默认值
└── 保守中训练: lr = 2e-5 → 忘得少,学得慢
三种学习率策略的选择:
| 策略 | 学习率方案 | 适用场景 | 遗忘程度 |
|---|---|---|---|
| 直接退火 | 从预训练末期 lr 开始余弦退火 | 目标与预训练相近 | 最低 |
| 短暂 warmup 后退火 | 先 warmup 到中等 lr,然后退火 | 通用中训练(推荐) | 低-中 |
| 重新 warmup | warmup 到较高 lr,然后退火 | 大幅领域转换 | 中-高 |
为什么”小学习率”只是启发式:来自 PaLoRA 的理论形式
上面这套经验法则有一个从未被论证的前提:用固定小学习率来限制梯度幅度。LoRA 持续学习的方法几乎全都这么干——OPLoRA、KeepLoRA、Janus-LoRA、JANUS 概莫能外——但这个限制的强度应该如何随着任务累积而演化,此前没有理论指导。
PaLoRA(arXiv:2610.04226,2026-10-03,NeurIPS 2026 已录用)把这件事说清楚了,分三步:
- 方向约束拦不住泄漏。 即便已经施加了 nullspace 投影这类方向性约束,有限精度的更新仍会沿多个方向泄漏进已累积历史知识的子空间。注意这里的关键在于”有限精度”——泄漏不是约束没设计好,而是数值上不可避免。
- 小学习率只能衰减,不能阻止。 小 lr 降低泄漏的量级,但无法阻止累积遗忘随历史知识的有效秩(effective rank) 增长而加剧。这解释了为什么”lr 调小一点”在长 horizon 上总是越来越不够用。
- 最优幅度限制应随 自适应增强。 在各向异性泄漏模型下推导出 pacing law:
其中 是历史更新的有效秩, 是每步遗忘预算相对于平均每方向泄漏下界的系数。论文的表述是: 刻画的是梯度步的分母的最优缩放——实现上就是把 LoRA 因子 的梯度除以 。方向读清楚: 越大 → 分母越大 → 步长越小 → 限制越强。 在给定预算下最大化一阶新任务进展:限制太弱会违反预算,限制太强则牺牲超出预算所需的进展。
⚠️ 两条必须同时标注的边界(均出自论文 Limitations 一节):① 该 pacing law 在单层线性模型 + 各向异性泄漏 + 局部二次敏感度假设下推导;用于深度 ViT 时提供的是近似的局部缩放原则,而非对完整非线性网络的形式化保证。② 该工作的评测仅限图像分类(CIFAR-100、ImageNet-R、ImageNet-A,10/20/25/50 任务分割),向 LLM / MLLM 的推广被作者明列为未来工作。因此对本书以 LLM 中训练为主的读者,PaLoRA 目前提供的是一条待本地验证的缩放原则,不是一个可直接套用的超参公式。
工程落地:如果你的流程已经在用 JANUS / KeepLoRA 类的投影,那么加一个随有效秩增长的步长收缩(自适应 SVD 截断 + rank-aware pacing)是低侵入的叠加项,不是替换项——因为这两者作用在正交的两根轴上(见 §15.3 末尾的总结)。报告的增益在 50-task ImageNet-A 与 ImageNet-R 上为 +4% 准确率,且论文明确”长 horizon 尤其明显”,这与其理论预测的”随 增长而差异放大”是一致的。
权重平均(Weight Averaging / Model Merging)
一种优雅的遗忘缓解方案是权重平均:将中训练后的模型权重与中训练前的模型权重做加权平均。
"""
权重平均缓解遗忘
"""
import torch
def weight_averaging(
pretrained_state_dict: dict,
midtrained_state_dict: dict,
alpha: float = 0.7, # 中训练权重的比例
) -> dict:
"""
简单的线性权重平均
alpha=1.0: 完全使用中训练权重(无回退)
alpha=0.5: 等权平均
alpha=0.7: 偏向中训练(推荐起点)
"""
merged = {}
for key in pretrained_state_dict:
if key in midtrained_state_dict:
merged[key] = (
alpha * midtrained_state_dict[key] +
(1 - alpha) * pretrained_state_dict[key]
)
else:
merged[key] = midtrained_state_dict[key]
return merged
def find_optimal_alpha(
pretrained_state_dict: dict,
midtrained_state_dict: dict,
eval_fn, # 评估函数,输入 state_dict,返回 (new_task_score, old_task_score)
alpha_range: list = None,
) -> dict:
"""
搜索最优的 alpha 值
通过在多个 alpha 上评估,找到新旧能力的最佳平衡点
"""
if alpha_range is None:
alpha_range = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
results = []
for alpha in alpha_range:
merged = weight_averaging(pretrained_state_dict, midtrained_state_dict, alpha)
new_score, old_score = eval_fn(merged)
results.append({
"alpha": alpha,
"new_task_score": new_score,
"old_task_score": old_score,
"harmonic_mean": 2 * new_score * old_score / max(new_score + old_score, 1e-8),
})
# 选择调和平均最高的 alpha
best = max(results, key=lambda x: x["harmonic_mean"])
return {
"optimal_alpha": best["alpha"],
"all_results": results,
"recommendation": f"alpha={best['alpha']}: new={best['new_task_score']:.1f}%, "
f"old={best['old_task_score']:.1f}%"
}
权重平均的优势:
- 零额外训练成本——只需做一次权重合并
- 可以在不同 alpha 上快速实验
- 效果出奇地好:通常能恢复 50-80% 的遗忘
权重平均的限制:
- 线性平均假设参数空间是凸的,但实际并非如此
- 对于差异很大的模型(中训练很长时间),效果会下降
- 不能完全替代数据回放——两者配合使用效果最好
更高级的合并方法
除了简单的线性平均,还有多种更高级的模型合并方法:
- SLERP(球面线性插值):在球面上进行插值,理论上更适合高维参数空间
- TIES-Merging:只合并”重要”的参数变化,消除冲突
- DARE:随机删除小的参数变化,然后重新缩放
- Task Arithmetic:用”任务向量”(中训练后减去中训练前的参数差)进行加减运算
- RECALL(表示对齐合并):利用隐层表示(hidden representations)的相似度指导逐层合并(EMNLP 2025)
前四种方法的合并系数都是全局统一的——所有层使用同一个 alpha。RECALL 的关键升级在于引入了逐层自适应:
全局统一合并 vs 逐层自适应合并:
全局统一(方法 1-4):
所有层统一 alpha=0.7
└── 简单,但忽略了不同层的漂移差异
逐层自适应(RECALL):
Layer 0: alpha=0.9 ← 表示漂移大,保留更多旧权重
Layer 5: alpha=0.6 ← 表示漂移小,可以用更多新权重
Layer 10: alpha=0.8 ← 中等漂移
...
└── 只需在少量聚类样本上计算表示相似度,成本很低
RECALL 不需要访问历史训练数据,可以和 TIES/DARE 组合使用——先用 TIES 消除参数冲突,再用 RECALL 决定逐层合并比例。
EWC 与参数高效方案
弹性权重巩固(Elastic Weight Consolidation, EWC)是经典的遗忘防控方法:
"""
EWC 的核心思想(简化版)
"""
def ewc_loss(
model_params: dict,
pretrained_params: dict,
fisher_information: dict, # 预训练参数的 Fisher 信息矩阵
lambda_ewc: float = 1.0, # EWC 正则化强度
standard_loss: float = 0.0, # 正常训练损失
) -> float:
"""
EWC 损失 = 标准损失 + λ * Σ F_i * (θ_i - θ*_i)²
核心思想:
- Fisher 信息矩阵的对角元素 F_i 衡量参数 θ_i 对旧任务的重要性
- 重要的参数(F_i 大)变化被惩罚得更重
- 不重要的参数(F_i 小)可以自由变化去适应新任务
"""
ewc_penalty = 0.0
for name in model_params:
if name in fisher_information:
ewc_penalty += (
fisher_information[name] *
(model_params[name] - pretrained_params[name]) ** 2
).sum()
total_loss = standard_loss + lambda_ewc * ewc_penalty
return total_loss
EWC 在大模型中训练中的实际应用有限,原因包括:
- Fisher 信息矩阵的计算对大模型来说成本很高
- 只用对角近似效果有限
- 在实践中,数据回放 + 学习率调整通常就够了
LoRA/QLoRA 作为遗忘缓解方案:
一种越来越流行的做法是在中训练中使用参数高效微调(PEFT)方法。2026 年的大规模实证研究表明,标准 LoRA(无任何额外正则化)本身就是持续学习中强大的遗忘缓解基线——低秩约束提供了选择性可塑性(selective plasticity):只允许模型在低秩子空间内调整,天然限制了参数空间中的干扰范围。
LoRA 的遗忘缓解技术路线:
标准 LoRA(基线):
├── 低秩约束 → 天然限制参数干扰 → 遗忘小
├── 但仍可能干扰预训练权重的主要奇异方向
└── 适用:轻量级领域适应
OPLoRA(正交投影 LoRA):
├── 双边正交投影约束:更新限制在主要奇异方向的正交补空间
├── 理论保证:微调不会干扰已学知识的编码
└── 适用:遗忘敏感场景(LLaMA-2 7B / Qwen2.5 7B 上已验证)
OPLoRA 揭示了标准 LoRA 的一个盲区:虽然只更新低秩矩阵、主干参数不变,但低秩更新仍然可能干扰预训练权重的主要奇异方向——这些方向编码了模型最重要的已学知识。OPLoRA 的双边正交投影将更新约束在这些方向的正交补空间内,从理论上保证微调不会覆盖已有知识。
事后整流:JANUS 与”子空间正交条件过强”
上面几类方法(OPLoRA / KeepLoRA / Janus-LoRA)的共同思路是在训练过程中施加正交约束。2026-09 的一条工作给出了一个更上位的判据与一条不同的路线(arXiv:2609.19985,2026-09-17):
- 判据升级:现有 PEFT 类抗遗忘方法采用的子空间正交(Subspace Orthogonality)条件是过强的;一阶保持历史性能的充要条件是参数空间正交(Parameter Space Orthogonality);
- 做法:把参数更新投影进 Jacobian 零空间(JANUS = JAcobian NUll Space);
- 定位:纯事后(post-hoc)且与微调方式无关(tuning-agnostic)——不侵入微调过程本身,因此可以叠加在已有的 LoRA / 全参微调流程之外;
- 配套 Multi-step Adaptive Rectification(应对 Jacobian 近似的局部有效性)与 ghost projection / sequence-level SVD 压缩(时空效率)。
对工程的意义:如果已经有一套跑通的微调流程、不想为了抗遗忘去改训练代码,事后整流是一条低侵入路径。同时它也提示:判断一个”正交约束”是否合适,要看它是否满足参数空间正交这一充要条件,而不是只看”约束听起来严不严密”。
⚠️ 命名区分:此处的 JANUS(Jacobian Null Space,arXiv:2609.19985)与后文的 Janus-LoRA(arXiv:2605.28495)是两篇不同的论文,缩写相近但方法与出处均不同,引用时请各自带编号。(该结论来自 2026-09-17 预印本,“显著恢复历史知识”属作者自述。)
ICLR 2026 LoRA 持续学习三大新方法
OPLoRA 之后,2026 年 ICLR 涌现了三个重要的 LoRA 持续学习方法,分别从子空间硬约束、正交基提取、双机制参数-特征分离三个角度推进。
KeepLoRA(ICLR 2026):SVD 子空间硬约束
KeepLoRA(ICLR 2026,东南大学+浙江大学)通过 SVD 分解预训练权重,发现了清晰的双子空间结构:
SVD 分解 W = U S V^T 的发现:
主子空间(W_p = U[:, 1:p],大奇异值方向):
└── 编码通用知识(general knowledge)
└── 置零低能量分量时,ImageNet 等通用数据集性能几乎不变
└── 关键:必须严格保护
残差子空间(小奇异值方向):
└── 编码任务/领域特定知识
└── 移除后,特定数据集性能急剧下降
└── 关键:可作为新任务更新的"领地"
方法:将新任务的 LoRA 更新投影到主子空间的正交补(残差子空间),同时用首步梯度信息初始化 LoRA 以保持可塑性。
三重平衡:
KeepLoRA 解决的三个竞争目标:
前向稳定性(Forward stability):
└── 保护主子空间 → 不破坏预训练通用知识
后向稳定性(Backward stability):
└── 累积提取历史任务的"主导特征方向" → 不遗忘旧任务
可塑性(Plasticity):
└── 在残差子空间内自由更新 → 新任务学习能力强
与现有方法对比:
| 方法 | 保护对象 | 初始化策略 | 推理成本 |
|---|---|---|---|
| O-LoRA | 通过正交约束 | 随机 | 不变 |
| InfLoRA | 统一神经正切核空间 | 随机 | 不变 |
| SD-LoRA | 知识蒸馏 | 随机 | 不变 |
| OPLoRA | 主要奇异方向 | 随机 | 不变 |
| KeepLoRA | 主子空间 + 历史主导方向 | 梯度投影 | 不变 |
Merge before Forget(ICLR 2026):单 LoRA 持续学习
Merge before Forget / SLAO(ICLR 2026,Pennsylvania State University)解决了另一个根本问题:持续累积 LoRA 适配器导致的存储爆炸。
传统 LoRA 持续学习方法的存储复杂度:
O-LoRA / InfLoRA / SD-LoRA:
└── 每个任务保存一个 LoRA 适配器
└── 总内存 = T × (m+n) × r,T 为任务数
└── 问题:长期部署成本不可接受
Merge before Forget:
└── 维护单一统一 LoRA
└── 总内存 = (m+n) × r,与任务数无关
└── 优势:常数内存复杂度
核心方法:
-
正交基提取(Orthogonal Basis Extraction):
- 从前一个任务的 LoRA 矩阵 通过 QR 分解提取正交基
- 用正交基初始化新任务 LoRA 的
- 公式:,其中
- 保证
-
时感知非对称合并(Time-aware Asymmetric Merging):
- 利用 LoRA 组件 A/B 的内在不对称性——A 矩阵跨任务相似度高,B 矩阵差异大
- A 直接覆盖:
- B 时感知缩放:,
理论支撑(NTK 体制下的分析):
论文证明在 NTK 体制下,最小化遗忘和 intransigence 错误等价于最小化 ——这正是非对称合并所实现的目标。
Janus-LoRA:参数级正交 + 特征级分离的双机制
Janus-LoRA(2026-05-27)识别了 LoRA 持续学习 SOTA 仍未解决的”复合更新违反正交性”问题:
传统 LoRA 优化的盲区:
优化器独立更新 A 和 B → 复合更新 ΔW = sBA
但 ΔW 在参数空间的方向 ≠ 在历史子空间正交的方向
└── 后果:所谓"正交 LoRA"在数学上不成立
└── 表现:重新引入干扰,破坏稳定性
双机制设计:
| 机制 | 作用层级 | 方法 | 解决什么问题 |
|---|---|---|---|
| Gradient Rectification (GR) | 参数级 | 闭式解耦 A/B 因子更新,强制对历史子空间正交(Online Estimation 识别) | 复合更新违反正交性 |
| Decoupled Margin Loss (DML) | 特征级 | 将新特征推离旧特征 | 特征空间被新类”侵蚀” |
关键洞察:
仅参数级正交 → 牺牲可塑性(新类无法找到”空地”) 仅特征级分离 → 牺牲稳定性(旧类表征被新类干扰) 双重机制协同 → 在多个 CL 基准上达到 SOTA
与 KeepLoRA/Merge before Forget 的对比:
KeepLoRA: 子空间硬约束(主子空间保护) + 梯度初始化
Merge before: 单 LoRA 持续合并(常数内存) + 正交基提取
Janus-LoRA: 复合更新正交矫正(GR) + 特征级分离(DML)
MuLoRA: 容量分配(历史白化) + 容量利用(近似极坐标正交化)
四种方法覆盖 LoRA 持续学习的四个不同设计维度。
第四维:容量分配与利用(MuLoRA)。 上面三种关心的都是”更新落在哪里”或”怎么合并”,没有一种关心”标称秩之下还剩多少真实可用的适应容量”。MuLoRA(arXiv:2610.02283,2026-10-01)指出 LoRA 的标称秩会掩盖有效适应容量的损失:序贯适配过程中,更新能量逐渐集中到少数几个奇异模态上,大量可用的低秩空间被闲置。作者把这个现象命名为 谱塑性崩溃(spectral plasticity collapse)。
这一点直接暴露了”只做干扰规避”的局限:保护住历史表征,并不保证剩下的适应容量对新任务仍有响应、或被有效利用。MuLoRA 的解法是两手同时管:
- 历史白化(管分配):识别那些”当前任务响应强、而累积历史响应弱”的输入方向,得到一个任务自适应的基,训练期间固定;
- 近似极坐标正交化(管利用):对动量更新做近似极坐标正交化,降低所选空间内的谱集中度。
两者由一个正交基连接起来——它把因子更新的谱精确转移到诱发的权重更新上。论文另给出了精确子空间选择的 max–min 刻画,以及在受控跨步各向异性下的累积谱界。结果层面:在 5 个 class-incremental 基准、8 个增量设置上,MuLoRA 在 16 项报告指标中的 15 项取得最高平均准确率。
与 KeepLoRA 的分工一句话:KeepLoRA 回答”保护哪些方向”,MuLoRA 回答”剩下的容量够不够、用尽了没有”。前者是防守,后者是产能。
LoRA 适配器公共仓的审计协议
以上 LoRA 合并/持续学习方法都有一个隐含前提:输入的 LoRA 适配器是”可信的”。但 Aharon/Glazer/Aharon(OpenReview 2025)的 Audit Before You Merge 研究表明,公共 LoRA 适配器公共仓存在严重的可信度问题,直接挑战了章节当前 LoRA 合并实证的”基准不严谨”问题。
核心发现:
公共 LoRA 适配器的两类主要问题:
1. 基础模型不匹配(Base model mismatch):
└── 上传的 LoRA 声明基于 "LLaMA-3-8B",实际基于 LLaMA-3-8B 的某个微调版本
└── 后果:与声称的基础模型 SVD 谱差异大,合并后基线偏置
2. 近零 delta 范数(Near-zero delta norm):
└── LoRA 权重变化 $\Delta W$ 极小(接近空操作)
└── 后果:合并时这些 LoRA 贡献接近零,但合并方法会"虚高"干扰度量
└── 量化影响:单纯合并造成 ~10pp 干扰度量虚高
两阶段审计协议:
"""
Audit Before You Merge 协议(伪代码)
"""
def audit_lora_adapter(adapter, claimed_base_model) -> AuditResult:
# Stage 1: 基础模型匹配(必须通过才能继续)
base_spectrum = compute_top_k_singular_values(claimed_base_model)
adapter_spectrum = compute_top_k_singular_values(adapter.base_weights)
base_match_score = cosine_similarity(
base_spectrum[:k], adapter_spectrum[:k]
)
if base_match_score < 0.95:
return AuditResult(
status="REJECTED",
reason=f"Base model mismatch (cosine={base_match_score:.3f})"
)
# Stage 2: 非平凡 delta 范数
delta_norm = compute_lora_delta_norm(adapter)
if delta_norm < 1e-4: # 经验阈值
return AuditResult(
status="REJECTED",
reason=f"Trivial delta (norm={delta_norm:.2e})"
)
return AuditResult(status="VERIFIED", delta_norm=delta_norm)
无泄漏探测协议(Leakage-Free Probing Protocol):
作为遗忘研究的预条件——若 LoRA 适配器在测试集上已经”见过”答案,则任何”抗遗忘”评估都是不可靠的。该协议要求评估时使用 LoRA 训练集严格隔离的 holdout 集合。
关键实验结果(4 适配器审计集合):
| 方法 | GSM8K | HumanEval |
|---|---|---|
| 朴素合并(未审计) | -10.5pp | -18.3pp |
| Task Arithmetic (λ=0.5) | 落在未合并基线标准误差内 | - |
| 负系数 math 适配器 (λ=-0.5) | 探测最优 | - |
对章节当前实证的反思:
章节当前 LoRA 合并实证的潜在问题:
├── 缺乏基础模型匹配验证
│ └── 不同 LoRA 可能基于不同基础模型,合并无意义
├── 缺乏 delta 范数过滤
│ └── 空操作 LoRA 贡献被高估
├── 缺乏 holdout 隔离
│ └── 训练集泄漏导致"抗遗忘"假象
└── 缺乏负系数探索
└── 章节未发现"negation-aware merging"可被利用
建议章节未来的 LoRA 合并实证都应通过该审计协议。
设备端 LoRA 持续融合:K-Merge 系列
章节前面的 LoRA 合并方法主要针对服务器端多任务离线合并。但用户级个性化场景(移动设备、边缘端)需要在线持续融合——K-Merge / K-Merge++(K-Merge: Online Continual Merging of Adapters for On-device Large Language Models,Shenaj 等,2025-10-15)给出了工业级方案。
三段式管线:
K-Merge 在线持续融合流程:
1. 相似性聚类(Similarity Clustering)
└── 计算新 LoRA 与已存储 LoRA 的余弦相似度
└── 匹配兼容性 → 决定是新增还是合并
2. 历史感知融合(History-aware Merging)
└── 动态加权平衡新旧任务
└── 保持每个任务贡献的可追溯性
3. 存储决策(Storage Decision)
└── 在存储预算内选择新增槽或融合现有
└── 自动化"添加"与"融合"的权衡
实验配置:Llama-3.2-1B / Qwen-2.5-1.5B × 5 任务 × 8 语言。结果显示 K-Merge 优于 Linear / TIES / DARE 合并基线,K-Merge++ 对任务到达顺序鲁棒性更强。
对工业实践的意义:
- 设备端部署:每个用户设备可独立运行 K-Merge 持续学习
- 隐私保护:无需回传数据到中心服务器
- 个性化:用户级 LoRA 适配器持续累积并自动融合
- 存储高效:自动决策减少冗余存储
易感区定位:从”层冻结”到”基于激活态子空间的精准保护”
章节前面的”权重平均 / LoRA / OPLoRA”主要处理参数空间的稳定性,但哪些层/参数最易感一直缺乏精细化定位。2026 年的 Mechanistic Analysis of Catastrophic Forgetting 通过对 20 个最先进模型(10 闭源 + 10 开源)的大规模比较分析,提供了易感区定位的方法学框架。
关键发现:最高遗忘易感区位于中段网络 MLP 和 MoE 路由投影。
20 个模型的大规模定位方法学:
1. CKA(Centered Kernel Alignment):
└── 比较不同训练阶段的层表征相似度
2. Activation Patching:
└── 干预特定层的激活,观察下游任务性能变化
3. Frobenius 漂移跟踪:
└── 量化参数更新矩阵的 Frobenius 范数变化
4. MoE 路由门稳定性测量:
└── 追踪 router gate 决策的稳定性
5. 闭源模型用 SOD(Semantic Output Drift)间接度量:
└── 通过语义相似度估计输出空间漂移
位置精确定位:
| 层/模块 | 遗忘易感度 | 主要机制 |
|---|---|---|
| Embedding 层 | 低 | 输入分布稳定 |
| 浅层 Attention(Layer 1-25%) | 中低 | 主要编码浅层语法 |
| 中层 MLP(Layer 25-75%) | 极高 | 编码任务特定模式 |
| MoE 路由投影 | 极高 | 路由决策对分布敏感 |
| 深层 Attention | 中 | 与中层 MLP 协同 |
| 末层 Norm | 中 | 输出归一化 |
| LM Head | 低 | 任务无关 |
LRCP(Low-Rank Circuit Projection)方法:
基于上述定位,论文提出 LRCP 方法——将梯度更新限制在历史任务激活态张成的子空间的正交补:
"""
LRCP(Low-Rank Circuit Projection)伪代码
"""
def lrcp_gradient(
grad: torch.Tensor, # 当前任务梯度
historical_activations: torch.Tensor, # 历史任务激活态矩阵
rank_threshold: int = 16, # 历史子空间保留秩
) -> torch.Tensor:
"""
1. 对历史激活态矩阵做 SVD 分解
2. 取前 rank_threshold 个右奇异向量作为历史子空间基 U_hist
3. 将当前梯度投影到 U_hist 的正交补
"""
# 历史子空间基(来自历史任务激活态)
U_hist = torch.svd_lowrank(historical_activations, q=rank_threshold)[0]
# 投影到 U_hist 张成子空间
projection = U_hist @ (U_hist.T @ grad)
# 取正交补(保留不干扰历史子空间的部分)
orthogonal_projection = grad - projection
return orthogonal_projection
与已有方法的关系:
参数空间方法(章节前面讨论):
└── 保护对象:参数本身(θ)
└── 保护依据:Fisher / SI / MAS / OPLoRA SVD
└── 保护粒度:参数级别
LRCP:
└── 保护对象:激活态子空间
└── 保护依据:历史任务激活态的 SVD 低秩近似
└── 保护粒度:子空间级别(更精细)
两者关系:LRCP 是从"参数空间保护"到"激活态空间保护"的升级
对工程实践的指导:
- 诊断顺序:先用 CKA/activation patching 定位易感层/模块,再针对性保护
- LRCP 比层冻结更精细:层冻结粒度太粗(layer 25-75% 全部冻结会牺牲可塑性)
- 历史激活态采集成本:需要少量历史数据(小 buffer)计算 SVD,不要求完整 replay
置信度提示:LRCP 在 20 模型上有大规模证据,但工程化程度(如何高效采集历史激活态、如何与 LoRA 集成)尚需验证。
全参数中训练 vs LoRA 中训练
全参数中训练:
├── 优点:学习能力强,能做大幅度调整
├── 缺点:遗忘风险高,需要大量回放数据
└── 适用:大规模中训练(>100B token)
LoRA 中训练:
├── 优点:低秩约束天然缓解遗忘(选择性可塑性)
├── 注意:学习容量受秩限制,大幅调整需要更高的秩或多阶段策略
└── 适用:轻量级领域适应(10B-50B token)
折中方案:阶段性 LoRA → 合并 → 再 LoRA
├── 第 1 阶段:LoRA 训练一段(遗忘小)
├── 合并 LoRA 参数到主干
├── 第 2 阶段:在合并后的模型上再加 LoRA
└── 优点:分步累积变化,每步遗忘都可控
机制是”可组合”的:来自 100 任务长程记忆基准的证据
15.2 与 15.3 分头叙述了回放、正则、LoRA 合并等机制,但读者仍会问一个工程上绕不开的问题:这些机制该叠加还是二选一?2026-09 的一项工作用析因设计给出了量化答案(arXiv:2609.06986,Johns Hopkins,2026-09-07)。
设定(long-horizon memorization):100 个 QA 任务顺序做持续 SFT,不保留早期样本、推理时不给 task ID——比常规 CL 基准更贴近”模型要记住很久以前学到的东西”。
结果:
- 没有任何单一机制能在这个 horizon 上维持强保留——朴素顺序微调的最终平均保留率仅 1.2%;
- 把三类锚(data / function / weight)+ merged LoRA 全部组合,保留率提到 34.9%(28×),三个数据集上都进 top-3;
- data anchor 与 merged LoRA 的主效应最大,且在三个数据集上都表现出超加性交互(先用 task-level successive halving 从 90→45→23→10 漏斗式搜设计空间,再用 2⁴ 析因实验分离主效应与交互效应——因此”超加性”是量化结论,不是”这个组合最好”的排名式说法)。
⚠️ 现实边界:34.9% 意味着仍有约三分之二的知识丢失。长程记忆远未解决——不要把它当作检索式记忆(外部存储)的替代品。
工程侧:O-LoRA / OSRM 的 state 随任务数线性增长,而 shared / merged LoRA 保持常数——持续滚动部署的生产模型应选后者。(骨干为 Qwen3-4B-Base,并在第 100 个任务后评估 GSM8K / MATH / MGSM / MMLU-Redux 的通用能力;代码与三个数据集已开源。工程侧细节来自公开报道,置信度中等。)
“凭什么可以共享一个子空间”:Share 的机制证据
上面那句”shared / merged LoRA 保持常数”是一个结论,但它依赖一个更强的假设:不同任务真的能被压进同一个低秩子空间。这个假设此前在本章没有被论证过。
Share(arXiv:2602.06043,2026-02-05,Kaushik、Vaidya、Chaudhari、Chellappa、Yuille)给出的正是这个证据。它维护并动态更新单一共享低秩子空间:初始化时用 SVD 从少量 LoRA 适配器提取主基向量;新任务到来时冻结共享基、只训练少量临时因子与极小任务系数;周期性用 SVD 把新方向解析合并回基础子空间。
机制证据:线性 CKA(Centered Kernel Alignment) 分析显示,从 GLUE 不同任务子集出发的三条独立轨迹,在序贯学习中单调收敛到同一个权重子空间,到 任务 T=5 时达到 >0.95 的近完美对齐。收敛过程还伴随知识的前向迁移。这条曲线的意义在于:共享子空间不是一种工程妥协(“为了省内存只好共用”),而是序贯适配本身就会自然涌现的结构。
量级(Table 1,GLUE):逐个任务保存 LoRA 为 1.2M × 6 任务 / 81.6 MB,Share 为 0.012M / 0.29 MB。两个倍率要分开验算,且口径不同:100× 是单适配器口径(1.2M → 0.012M = 100×),281× 取自内存列(81.6 MB ÷ 0.29 MB ≈ 281.4×)。若按六任务合计参数对 Share 的单一共享子空间计算(1.2M × 6 = 7.2M → 0.012M)则为 600×——论文摘要取的不是这个口径。引用时必须连同口径一起给出,只报倍率会让人按合计口径复算出一个对不上的数字。质量上,GLUE 平均 Share-full 83.44 对非持续 LoRA 83.43(持平略优),联合训练上界为 83.90。覆盖图像分类、NLU、3D 姿态估计、文生图四类任务。
⚠️ 需要说明的口径:论文摘要对削减倍率的表述是”up to 100× / 281ד,即为上界而非典型值;且本条工作为 2026-02 的预印本,距今已 8 个月,属”机制证据”而非”最新进展”。另外,提案提到的”正向后向迁移”在本轮回源中未能在正文确认,故不写入。
15.3 小结:两根正交的设计轴
把 15.3 全节的方法摊开看,会发现它们其实分布在两根正交的轴上:
| 方向约束(更新可以落在哪里) | 幅度约束(步长该多大) | |
|---|---|---|
| 代表 | OPLoRA(主要奇异方向正交补)、KeepLoRA(主子空间 + 历史主导方向)、JANUS(Jacobian 零空间)、Janus-LoRA(A/B 解耦正交 + GR 矫正)、Share(共享基冻结) | PaLoRA(rank-aware pacing,) |
| 回答的问题 | ”往哪儿走不会踩到旧知识" | "每一步走多大才不超预算” |
| 单独使用的失效模式 | 有限精度下泄漏不可避免,随有效秩增长而累积(PaLoRA 的核心发现) | 失去”更新可以落在哪里”的瞄准能力——步长再小,方向错了照样踩 |
| 容量视角 | MuLoRA:剩下的容量够不够、用尽了没有 | — |
评估任何一个抗遗忘方案,都应该同时问这两句:它约束了更新的方向吗?它约束了更新的幅度吗?只答一句的方案,在长 horizon 上几乎一定会露出缺口——这正是 PaLoRA 用理论形式说清、而此前只停留在”大家都用小学习率”这一经验共识里的东西。
15.4 遗忘检测与评估
遗忘监控指标体系
"""
中训练遗忘监控系统
"""
from typing import Dict, List, Optional
from dataclasses import dataclass
@dataclass
class ForgettingMetrics:
"""遗忘监控指标"""
# 绝对分数
current_scores: Dict[str, float] # 当前各 benchmark 分数
baseline_scores: Dict[str, float] # 中训练前的分数
# 遗忘量
forgetting: Dict[str, float] = None # 各维度的遗忘量
# 汇总
max_forgetting: float = 0.0 # 最大遗忘量
avg_forgetting: float = 0.0 # 平均遗忘量
num_degraded: int = 0 # 退化的维度数量
def __post_init__(self):
self.forgetting = {}
for key in self.baseline_scores:
if key in self.current_scores:
delta = self.current_scores[key] - self.baseline_scores[key]
self.forgetting[key] = round(delta, 2)
degraded = [v for v in self.forgetting.values() if v < -0.5]
self.max_forgetting = min(self.forgetting.values()) if self.forgetting else 0
self.avg_forgetting = sum(degraded) / max(len(degraded), 1) if degraded else 0
self.num_degraded = len(degraded)
def report(self) -> str:
lines = ["遗忘监控报告", "=" * 50]
# 汇总
status = "🟢 正常" if self.max_forgetting > -2 else (
"🟡 轻微遗忘" if self.max_forgetting > -5 else "🔴 严重遗忘"
)
lines.append(f"状态: {status}")
lines.append(f"退化维度数: {self.num_degraded}/{len(self.forgetting)}")
lines.append(f"最大遗忘: {self.max_forgetting:+.2f}%")
lines.append(f"平均遗忘: {self.avg_forgetting:+.2f}%")
lines.append("")
# 详细
lines.append(f"{'维度':<20} {'基线':>8} {'当前':>8} {'变化':>8} {'状态':>4}")
lines.append("-" * 52)
for key in sorted(self.forgetting.keys()):
baseline = self.baseline_scores[key]
current = self.current_scores[key]
delta = self.forgetting[key]
status = "✅" if delta > -1 else ("⚠️" if delta > -3 else "❌")
lines.append(f"{key:<20} {baseline:>7.1f}% {current:>7.1f}% {delta:>+7.1f}% {status}")
return "\n".join(lines)
# 使用示例
metrics = ForgettingMetrics(
baseline_scores={
"MMLU": 55.0, "C-Eval": 50.0, "HellaSwag": 72.0,
"ARC": 48.0, "HumanEval": 25.0, "GSM8K": 40.0,
},
current_scores={
"MMLU": 52.0, "C-Eval": 46.0, "HellaSwag": 71.5,
"ARC": 47.0, "HumanEval": 50.0, "GSM8K": 58.0,
},
)
print(metrics.report())
实时评估 vs 离线评估
| 评估方式 | 频率 | 成本 | 覆盖面 | 适用场景 |
|---|---|---|---|---|
| 实时评估 | 每 1-5B token | 低 | 2-3 个 benchmark | 日常监控 |
| 定期评估 | 每 20-50B token | 中 | 6-10 个 benchmark | 里程碑检查 |
| 离线评估 | 完成后 | 高 | 全部 benchmark + 人工 | 最终验证 |
实时评估的关键:选择对遗忘最敏感的 benchmark 作为”金丝雀”指标。
推荐的”金丝雀” benchmark:
- MMLU:对世界知识遗忘最敏感
- HellaSwag:对常识推理遗忘敏感
- C-Eval/CMMLU:对中文能力遗忘敏感(如果是中文模型)
快速遗忘检测的 Proxy 方法
完整的 benchmark 评估耗时较长。以下是几种快速检测遗忘的方法:
方法 1:训练数据子集的 Perplexity
def quick_forgetting_check(
model,
pretrain_sample: list, # 预训练数据的小样本(1000-5000 条)
midtrain_sample: list, # 中训练数据的小样本
) -> dict:
"""
快速遗忘检测
如果模型在预训练数据上的 PPL 显著升高,说明遗忘正在发生
"""
pretrain_ppl = compute_perplexity(model, pretrain_sample)
midtrain_ppl = compute_perplexity(model, midtrain_sample)
return {
"pretrain_ppl": pretrain_ppl,
"midtrain_ppl": midtrain_ppl,
"ppl_ratio": pretrain_ppl / max(midtrain_ppl, 1),
"warning": pretrain_ppl > midtrain_ppl * 1.5,
# 如果预训练数据的 PPL 比中训练数据高 50% 以上,可能有遗忘
}
方法 2:隐层表示的漂移检测
监控模型中间层输出的变化,如果特定层的表示发生大幅漂移,可能预示着遗忘。
方法 3:生成样本的定性检查
给模型一组固定的 prompt,在中训练过程中定期生成回复,人工检查是否出现退化。这是最直观但也最难自动化的方法。
两类隐性灾难性遗忘:超越准确率指标的监测
上面所有遗忘监控方法都基于准确率/PPL/benchmark 分数等表层指标。但 SITS2026 综述 揭示了两类被准确率指标漏掉的隐性遗忘模式——这些模式在分数上看似正常,但模型实际能力已发生退化。
隐性遗忘 1:语义锚点漂移(Semantic Anchor Drift)
现象定义:
词向量空间中稳定的高频概念锚点("patient"、"consent"、"liability")
在持续训练过程中 cosine 相似度系统性下降
关键数字:
└── 10K 步内 cosine 相似度下降 18.7%
└── 同期分类 loss 仅微增 0.02(表面上"没遗忘")
└── 但下游概念检索 / 关系推理任务性能下降
为什么准确率指标漏掉了:
准确率 = 输入匹配到正确标签的概率
└── 表面看:分类 loss 微增 0.02 = 模型还能正确分类
└── 实际:词向量的几何结构已偏移
└── "patient" 在向量空间里和"医生"距离变远
└── 但分类层可能已经过拟合到"表面模式"
└── 后果:OOD 检索 / 关系推理 / 抽象语义任务表现差
诊断方法:
└── 锚点校准损失 L_anchor = Σ ||emb_t(token_i) − emb_0(token_i)||²
└── 监控 token_i 在 t 时刻的 embedding 与初始 embedding 的 L2 距离
隐性遗忘 2:分布感知钝化(Distribution Awareness Dulling)
现象定义:
即使输入 token 分布熵提升 35%(OOD 输入增加)
各层 attention entropy 变化幅度衰减 >62%
└── 模型对"陌生输入"不再敏感
└── OOD 鲁棒性系统性下降
关键数字:
└── 输入分布熵提升 35%(训练数据多样性增加)
└── attention entropy 变化幅度衰减 >62%
└── 分类 / 推理准确率可能不变或微增
└── 但 OOD 检测 / 抗对抗样本能力下降
为什么准确率指标漏掉了:
准确率在 in-distribution 上稳定 = 模型"还行"
└── 但 attention entropy 衰减 = 模型对"意外"的反应能力下降
└── 后果:
├── OOD 检测阈值失准(高置信度错误预测)
├── 对抗样本防御能力下降
└── 鲁棒性指标(certified accuracy)下降
诊断方法:
└── 动态分布感知正则(DDAR):KL 散度约束 attention entropy
└── 监控"输入分布熵 - attention 熵响应"的传递函数
缓解方案
"""
两类隐性遗忘的缓解(伪代码)
"""
import torch
import torch.nn.functional as F
def anchor_calibration_loss(
model, anchor_tokens: list, initial_embeddings: dict
) -> torch.Tensor:
"""
锚点校准损失:维持高频概念锚点的 embedding 稳定
"""
loss = 0.0
current_embeddings = model.get_input_embeddings()
for token in anchor_tokens:
token_id = model.tokenizer.convert_tokens_to_ids(token)
cur_emb = current_embeddings(token_id)
init_emb = initial_embeddings[token]
loss += (cur_emb - init_emb).pow(2).sum()
return loss / len(anchor_tokens)
def dynamic_distribution_aware_regularizer(
model,
input_entropy: torch.Tensor,
target_attention_entropy: torch.Tensor,
decay: float = 0.999,
) -> torch.Tensor:
"""
动态分布感知正则(DDAR)
通过 KL 散度约束 attention entropy 跟踪输入分布变化
"""
# 使用 batch-wise moving average(不更新全局 LayerNorm EMA)
# decay=0.999 控制历史平均的窗口
predicted_entropy = target_attention_entropy.mean(dim=-1)
kl_div = F.kl_div(
F.log_softmax(input_entropy, dim=-1),
F.softmax(predicted_entropy, dim=-1),
reduction='batchmean',
)
return kl_div
关键工程决策:
训练配置的隐性遗忘防护选项:
1. 启用锚点校准损失 L_anchor(权重 0.01-0.1)
2. 启用 DDAR(KL 散度约束,权重 0.001-0.01)
3. 禁用全局 LayerNorm EMA 更新
└── 改用 batch-wise moving average with decay=0.999
4. 增加 OOD 鲁棒性指标到监控 dashboard
└── 不仅看 benchmark,还要看 certified accuracy
对章节评估体系的扩展:
| 评估维度 | 传统指标 | 隐性遗忘新指标 |
|---|---|---|
| 任务准确率 | MMLU/C-Eval/HumanEval | — |
| 困惑度 | 预训练数据 PPL | — |
| 词向量几何 | — | 锚点校准 L_anchor |
| 注意力响应 | — | DDAR / 熵传递函数 |
| OOD 鲁棒性 | — | certified accuracy |
置信度提示:SITS2026 的两类隐性遗忘提供了新视角,但原始论文主要来自 CSDN 二手报告,学术来源溯源(待补充 arXiv 链接)和大规模实验验证尚在进行中。
15.5 🔬 显微镜案例:一次遗忘问题的发现与修复
背景
某团队在一个 13B 中英双语模型上做中训练,目标是增强代码和数学能力。
配置
预训练模型:13B, 在 5T token 上预训练
中训练数据:300B token
- 代码数据: 40% (120B)
- 数学数据: 15% (45B)
- 通用回放: 20% (60B)
- 高质量网页: 15% (45B)
- 中文数据: 10% (30B)
学习率:6e-5, 余弦退火到 1e-6
发现问题
中训练进行到 150B token(一半)时,实时监控报告了异常:
监控告警:
MMLU: 55.0 → 51.2 (-3.8%) ⚠️ 超过阈值
C-Eval: 50.0 → 43.5 (-6.5%) ❌ 严重退化
HellaSwag: 72.0 → 70.8 (-1.2%) ✅ 可接受
HumanEval: 25.0 → 42.0 (+17.0%) ✅ 增长如期
GSM8K: 40.0 → 55.0 (+15.0%) ✅ 增长如期
代码和数学能力的增长符合预期,但中文知识(C-Eval)出现了严重退化。
分析原因
团队进行了详细的排查:
-
查看中文数据的实际配比
- 发现中文数据只有 10%,但预训练中中文数据占 25%
- 这意味着中训练中中文的”浓度”大幅降低
-
检查回放数据的语言分布
- 20% 的通用回放数据中,中文只占 15%(约 3% 的总训练量)
- 总的中文数据比例:10% + 20%×15% = 13%,远低于预训练的 25%
-
对比不同能力的遗忘速率
- 英文知识(MMLU)遗忘较慢(-3.8%)
- 中文知识(C-Eval)遗忘较快(-6.5%)
- 验证了”非目标语言遗忘更快”的规律
修复方案
方案:调整后半段训练的数据配比
后 150B token 的数据配比(修正后):
- 代码数据: 35% (52.5B) ← 略降
- 数学数据: 12% (18B) ← 略降
- 通用回放: 18% (27B) ← 略降
- 高质量网页: 10% (15B) ← 降
- 中文数据: 25% (37.5B) ← 大幅增加
中文数据细分:
- 中文百科+知乎: 10B
- 中文学术论文: 5B
- 中文书籍: 5B
- 中文代码相关: 7.5B ← 中文代码注释+文档
- 中文数学: 5B ← 中文数学题+教材
- 中文通用网页: 5B
结果
中训练完成后的最终评估:
| Benchmark | 中训练前 | 修复前(150B) | 修复后(300B) | 变化 |
|---|---|---|---|---|
| MMLU | 55.0 | 51.2 | 53.8 | -1.2% ✅ |
| C-Eval | 50.0 | 43.5 | 48.2 | -1.8% ✅ |
| HellaSwag | 72.0 | 70.8 | 71.0 | -1.0% ✅ |
| HumanEval | 25.0 | 42.0 | 48.0 | +23.0% ✅ |
| GSM8K | 40.0 | 55.0 | 60.0 | +20.0% ✅ |
通过及时发现和修复,C-Eval 的退化从 -6.5% 恢复到了 -1.8%,在可接受范围内。
教训
- 中文数据(或目标语言数据)在回放中的比例应该至少等于预训练中的比例
- 实时监控是关键——如果到训练结束才发现遗忘,已经来不及了
- 数据配比的调整可以在训练中途进行——这比重新训练划算得多
- 不同语言的遗忘速率不同——需要分别监控
15.6 中训练稳定性的其他考量
📌 三阶段视角
2026 年最新综述将 LLM 持续学习方法按训练阶段分为三类,不同阶段的遗忘模式和最优缓解策略截然不同:
- 持续预训练/中训练(本章主题):数据回放 + 学习率控制为核心
- 持续微调:参数高效方法(LoRA/正交约束)+ 正则化为核心
- 持续对齐:偏好保持 + 安全性维护为核心
本章聚焦第一阶段。持续微调和持续对齐的数据策略分别在后续的 SFT(第 11-13 章)和对齐(第 14-16 章)中讨论。
Loss Spike 的预防
中训练中的 loss spike(训练损失突然飙升)是另一个常见问题,通常由以下原因引起:
- 数据分布突变:从一个 epoch 切换到另一个 epoch 时,数据分布可能突然变化
- 上下文长度切换:从 8K 突然变到 32K
- 异常数据批次:少量极端样本导致梯度爆炸
预防措施:
"""
中训练稳定性的关键配置
"""
stability_config = {
# 梯度裁剪(必须设置)
"gradient_clip_norm": 1.0,
# 学习率 warmup(中训练开始时)
"warmup_steps": 200,
# 数据混合的平滑性
"batch_mixing": "uniform", # 每个 batch 都均匀混合各类数据
# 异常检测
"loss_spike_threshold": 2.0, # loss 突然升高超过 2 倍时告警
"auto_rollback": True, # loss spike 时自动回退到上一个检查点
# 检查点频率
"save_every_steps": 500, # 频繁保存,方便回退
}
训练过程中的数据配比动态调整
更高级的方案是根据模型的实时评估结果动态调整数据配比:
训练过程中的反馈循环:
每 N 步 → 快速评估(金丝雀 benchmark)
↓
检测到某维度退化?
↓ 是 ↓ 否
增加该维度的回放数据比例 保持当前配比
↓
调整后继续训练
这种方法在理论上最优,但在实践中有挑战:
- 评估有延迟,可能不够及时
- 频繁调整配比可能引入训练不稳定
- 需要足够灵活的数据加载基础设施
15.7 认知演变时间线
| 时间 | 认知阶段 | 代表性发现 |
|---|---|---|
| 2019 | ”遗忘是微调的问题” | 主要在任务级微调中讨论 |
| 2022 | ”继续预训练也会遗忘” | Code Llama 等实践暴露问题 |
| 2023 | ”回放数据是关键” | 经验性地发现 20-30% 回放效果好 |
| 2024 | ”系统化的遗忘研究” | ACL/EMNLP 上多篇论文量化遗忘;CMR Scaling Law 首次将回放比例形式化 |
| 2025 前半 | ”虚假遗忘被识别” | ICLR 2025 Spurious Forgetting——部分遗忘是评测假象而非真实知识丢失 |
| 2025 后半 | ”遗忘可数学建模” | Apple Forgetting Scaling Laws(ICLR 2026)——回放比例可精确计算;OPLoRA 正交投影 LoRA 提供理论级遗忘防护 |
| 2026 上半 | ”根叙事被颠覆” | NTK 函数空间理论解释参数空间正则化器失效;Accessibility Collapse 证明遗忘是可访问性失败而非知识擦除 |
| 2026 | ”架构层面闭解” | TFGN 首个无 replay 无 task-ID 闭解 LLM 规模灾难性遗忘;KeepLoRA/Merge before Forget/Janus-LoRA(ICLR 2026)三路径 LoRA 持续学习 |
| 2026 | ”易感区精准定位” | Mechanistic Analysis + LRCP——中层 MLP/MoE 路由投影是最高易感区,基于激活态子空间的精准保护 |
| 2026 | ”隐性遗忘被识别” | SITS2026 揭示语义锚点漂移(cosine -18.7%)和分布感知钝化(attn entropy -62%)两类表层指标漏掉的失效模式 |
| 2026 | ”遗忘管理全栈成熟” | 三层防线:Scaling Law 预测回放比例 + 智能回放选择 + 表示对齐逐层合并;方法按训练阶段(预训练/微调/对齐)分化 |
本章要点回顾
- 灾难性遗忘在中训练中是真实且严重的风险——不是理论问题
- 区分虚假遗忘和真实遗忘:性能下降不一定意味着知识丢失,先尝试 re-alignment 验证
- 遗忘是选择性的:低频知识和非目标语言最脆弱
- 回放是最有效的防控手段:经验值 20-40%,但 Scaling Laws 可以针对具体场景精确计算最优比例
- 学习率控制是第二道防线:中训练 lr 应为预训练峰值的 1/5 到 1/3
- 权重平均是零成本的补救方案:能恢复 50-80% 的遗忘;逐层自适应合并(RECALL)效果更优
- LoRA 的低秩约束天然缓解遗忘:正交投影(OPLoRA)提供更严格的理论保证
- 实时监控不可或缺:发现越早,修复成本越低
- 数据配比可以在训练中途调整——不需要完全重来
- 分别监控不同语言和不同能力维度的遗忘
🛠 动手环节
练习 1:遗忘监控 Dashboard
设计一个遗忘监控 dashboard 的 schema:
- 定义需要监控的 5-8 个维度
- 设定每个维度的告警阈值
- 设计监控频率和上报策略
练习 2:权重平均实验
如果你有一个微调前和微调后的模型检查点:
- 在不同的 alpha 值(0.3, 0.5, 0.7, 0.9)上做权重平均
- 在 2-3 个 benchmark 上评估每个 alpha 值的效果
- 画出”alpha vs 新能力分数”和”alpha vs 旧能力分数”的帕累托前沿
练习 3:回放策略设计
假设你要在一个通用模型上做法律领域中训练:
- 法律数据:50B token
- 总训练预算:150B token
- 回放数据来源:预训练数据的高质量子集(可用 500B token)
设计完整的数据配比方案,包括:回放比例、各领域分布、中文/英文比例、评估方案。