← 阅读室全部精读正在排版公式与图表…
PAPER ANALYZER · P20 · 跨场景方向

GPM:把新梯度投向不干扰旧知识的方向

用激活、SVD 与正交投影理解持续学习中的稳定性和容量

Saha, Garg & Roy · ICLR 2021 · arXiv:2103.09762

一句话总结

保存逐层输入的重要子空间,并从新梯度中去掉这些方向;有限记忆空间决定保护与适应的权衡。

GPM 保存的是方向,不是历史图像

Gradient Projection Memory for Continual Learning 由 Gobinda Saha、Isha Garg 和 Kaushik Roy 发表于 ICLR 2021。本页阅读完整论文与附录,并检查作者官方 PyTorch 仓库。它的出发点是:新任务参数更新如果沿着影响旧任务的方向移动,就会遗忘;如果能把这些方向从新梯度中移除,就可能保留旧能力。

名字里的 Memory 容易让人以为算法保存旧图片。实际上它主要保存逐层激活所张成的重要子空间的正交基;每个任务训练完后,用当前任务少量样本收集激活并做奇异值分解。后续训练用这些基约束梯度,不需要把历史样本持续放进每个批次重放。

与 EWC 的区别是约束对象不同。EWC 对每个参数偏离旧值的距离施加惩罚,GPM 直接修改这一步允许更新的方向。一个方向可能涉及许多参数协同变化,所以它不是把某一组重要权重简单锁死。理解这一点需要先会矩阵乘法、向量投影和奇异值分解。

为什么输入激活能告诉我们该保护什么

先考虑一个线性层,输出等于权重矩阵乘输入激活。权重梯度可以写成输出误差信号与输入激活的外积,因此输入出现过的方向会影响梯度的结构。如果未来权重变化对这些旧输入方向的乘积为零,那么在这个线性层上,相应旧输入的输出就不会因本次更新改变。

这是局部层级的解释,不是完整深层网络永不遗忘的保证。前面的层也可能变化,保存的激活只有有限样本,奇异值分解还会截断低能量方向。论文据此设计近似约束,并通过实验检验效果;不能把线性等式直接推广为所有旧视频的预测严格不变。

原文 Figure 1 展示卷积层如何把输入补丁展开为矩阵。卷积核权重可以看成输出通道乘以展开的输入通道和空间位置;每个局部补丁成为一列。全连接层则直接把不同样本的输入激活作为列。两种处理都让“保护输入方向”可以通过同一种线性代数操作实现。

$$a=Wx,\qquad \frac{\partial L}{\partial W}=\delta x^T$$

对应原文式(3)的思想,delta 是输出端误差信号。W 的形状为输出维乘输入维,梯度具有相同形状,输入 x 决定了梯度右侧的方向结构。

$$R=U\Sigma V^T,\quad \frac{\sum_{j=1}^k\sigma_j^2}{\sum_j\sigma_j^2}\ge\epsilon$$

原文式(5)的能量准则。R 是层输入激活矩阵,取满足阈值的最小 k,保留 U 的前 k 列。平方奇异值对应能量,不能把未平方的奇异值直接当成同一指标。

$$M=U_{[:,1:k]},\quad M^TM=I,\quad P=MM^T$$

教学定义。M 是输入空间的正交基,P 是到该子空间的投影矩阵。M 通常比 P 更省存储;是否显式构造 P 取决于实现,而不是方法必须保存一个完整大方阵。

$$G_{\perp}=G-GMM^T$$

原文式(6)按 PyTorch 权重形状的写法。G 是输出维乘输入维的权重梯度,右乘投影矩阵移除会作用在旧输入子空间上的分量。若矩阵方向取反,形状和投影侧也要一致改变。

$$G_{\perp}M=G(M-MM^TM)=0$$

教学验证:当 M 列正交时,投影后的梯度对已保存基方向的作用为零。实际计算存在浮点误差,可用相对范数测试是否接近零,而不是要求每个元素精确等于零。

$$\widehat R=R-MM^TR$$

原文式(8)的残差。新任务结束后,先去掉已被旧基解释的部分,再对残差做 SVD,补入必要新方向;这避免每次都把重复方向存一遍。

阈值与记忆容量如何互相制约

阈值越高,保留的激活能量越多,通常保护越强,但给未来任务留下的可更新方向越少。如果每层输入维度是固定的,基方向数量最终不可能无限增加;接近满秩时,投影可能把大部分新梯度都消掉,新任务难以学习。这是稳定性与可塑性的几何版本。

阈值并不直接表示准确率保留比例。保留百分之九十九的激活能量,不等于保留百分之九十九的旧任务准确率。低能量方向也可能对少数类别很重要,尤其表情数据长尾明显时;这是你将方法用于细粒度或稀有表情时应专门检查的假设。

每次任务结束后补入残差方向,要把旧空间已解释的能量与新方向能量一起计入阈值。若只对残差自身达到百分之九十九就全部加入,可能比原方法保存更多维度,导致不同的内存和适应能力。原文式(9)正是在说明这一总能量判断。

论文的训练细节还限制了部分偏置与批归一化参数的变化。若只投影卷积权重,却让归一化统计在新域任意漂移,旧输出仍会改变。实际代码应检查所有会改变函数的状态,而不仅是 named_parameters 中某几个矩阵。

flowchart TD
A[Task finished] --> R[Collect layer inputs]
R --> S[SVD]
S --> M[Subspace memory]
N[Next task loss] --> G[Raw gradient]
G --> P[Remove old components]
M --> P
P --> U[Parameter update]

实验必须先看任务身份是否已知

原文不同基准使用不同协议:置换 MNIST 采用单头,其余主要实验采用多头,并在推理时使用任务身份选择输出头。这样的任务增量分类比不知道任务身份、所有类别共享一个输出头的设定更容易。不能把这些数字与 EmWorld 同一七类场景增量结果直接放在一列比较高低。

下面第一张表摘录 Table 1,ACC 是最终各任务准确率的平均,BWT 衡量后学任务对先前任务的影响,负值意味着遗忘。不同数据集的任务数、架构和难度不一样,跨行数字不能解释为某种机制对一个数据集天然更强。

第二张表摘录 Table 2 的训练时间。GPM 在 CIFAR 设置较快,但在 miniImageNet 上并不是所有方法中最快。SVD 和表示收集也要计时;论文在不同实验使用不同 GPU,所以这些秒数只适合同一实验列内比较,不能当作你笔记本的预计耗时。

原文 Table 1:ACC % / BWT,保留报告波动
设置EWCHATGPM
10-split CIFAR-10068.80 ± 0.88 / −0.0272.06 ± 0.50 / −0.0072.48 ± 0.40 / −0.00
miniImageNet52.01 ± 2.53 / −0.1259.78 ± 0.57 / −0.0360.41 ± 0.61 / −0.00
5 datasets—91.32 ± 0.1891.22 ± 0.20
原文 Table 2:训练耗时秒,依原实验环境
方法CIFAR-100miniImageNet
EWC13524138
HAT12483077
ER11472775
GPM7703387

作者报告了什么局限

原文 Figure 2 展示记忆与时间开销,Figure 3 分析能量阈值如何影响干扰。这些图值得和 Table 1 一起看,因为一个方法可能用更多约束维度换来较少遗忘,却付出新任务学习速度或容量代价。只报告最终平均准确率会掩盖这个过程。

论文附录的类别增量实验中,GPM 的结果明显弱于某些专门针对该协议的方法,报告准确率约七十点六七,BWT 约负零点二六。这说明已知任务身份下的优势不能自然迁移到所有持续学习设置。作者也讨论子空间容量随着任务增加而受限,不能把“没有旧图像”写成“没有记忆增长”。

独立判断是:表情数据的高能量方向可能包含身份、光照和背景,而不全是表情语义。若直接保护全部主要激活方向,可能把应当适应的域偏差也保留下来。因此一个有价值的小研究是比较全特征记忆与任务相关子空间记忆,但必须先证明后者没有用测试标签选择方向。

官方代码中最该核对的两处

官方仓库 sahagobinda/GPM 当前提交包含置换 MNIST、CIFAR-100、超类任务及五数据集实验入口,run_experiments.sh 列出脚本。下列第一段位于梯度反传之后、优化器更新之前,按层把梯度减去投影分量;因此梯度投影不是损失函数里一个普通标量惩罚。

第二段从激活矩阵做 SVD 并截取基。值得注意的是初任务代码按累计能量小于阈值的数量计算 r,旁边的加一被注释,因此边界处与论文“达到阈值的最小秩”的文字规则可能相差一个方向。若你修正它,应明确标为实现差异,并同时报告原实现与修正版,不能悄悄改后宣称完全复现。

代码使用一百二十五个样本构造 CIFAR 的表示矩阵,学习率默认零点零一,并依据任务调整能量阈值。选择哪些样本同样影响保存空间,随机种子与抽样索引应记录。模型使用多头输出,训练和测试都根据 task_id 取对应头,这一行是确认实验协议的重要证据。

作者官方代码:反传后投影:main_cifar100.py:145–159;固定提交 1a238ec9d2ca。

        output = model(data)
        loss = criterion(output[task_id], target)         
        loss.backward()
        # Gradient Projections 
        kk = 0 
        for k, (m,params) in enumerate(model.named_parameters()):
            if k<15 and len(params.size())!=1:
                sz =  params.grad.data.size(0)
                params.grad.data = params.grad.data - torch.mm(params.grad.data.view(sz,-1),\
                                                        feature_mat[kk]).view(params.size())
                kk +=1
            elif (k<15 and len(params.size())==1) and task_id !=0 :
                params.grad.data.fill_(0)

        optimizer.step()

作者官方代码:初始子空间提取:main_cifar100.py:229–240;固定提交 1a238ec9d2ca。

def update_GPM (model, mat_list, threshold, feature_list=[],):
    print ('Threshold: ', threshold) 
    if not feature_list:
        # After First Task 
        for i in range(len(mat_list)):
            activation = mat_list[i]
            U,S,Vh = np.linalg.svd(activation, full_matrices=False)
            # criteria (Eq-5)
            sval_total = (S**2).sum()
            sval_ratio = (S**2)/sval_total
            r = np.sum(np.cumsum(sval_ratio)<threshold[i]) #+1  
            feature_list.append(U[:,0:r])

四个可以逐项验收的练习

第一项练习只用二维向量。令旧基为横轴方向,梯度包含横向与纵向分量,手算投影后只剩纵向。再把基换成斜向单位向量,用矩阵乘法验证结果与基正交。验收文件应包含输入、输出和内积,而不只是画一根看起来垂直的箭头。

第二项练习生成一个已知秩为二的激活矩阵并添加少量噪声,做 SVD 后画累计能量曲线。分别用较低与较高阈值记录选中秩,并验证重建误差。此时你会看到噪声也能占能量,从而理解阈值为什么是需要选择的超参数,而不是固定的自然常数。

第三项练习给一个两层分类器接入激活缓存。先仅投影一层,检查梯度形状、基正交误差和投影后梯度在旧基上的相对范数;然后再接第二层。把新任务损失下降与旧任务表现一起记录,确认不是因为错误把全部梯度清零才“没有遗忘”。

第四项练习使用两个表情场景的同一标签空间,明确采用共享分类头还是场景专用头。比较普通微调、EWC、GPM,记录每阶段每层基的维度比例、额外存储和耗时。如果共享头条件下效果差于多头,先把任务身份这一条件解释清楚,再讨论算法优劣。

验收结果还应包含一个失败诊断:当某层基接近满秩,计算被移除的梯度范数占比,观察新任务损失是否停滞。这样你可以用可测量的量说明“可塑性不足”,而不是泛泛说模型容量不够。后续若尝试减少记忆、放松投影或仅保护特定层,必须在同样预算下重新检查旧新任务权衡。

进一步理解投影后的优化器与误差

投影修改的是当前梯度,而优化器实际应用的更新还可能含动量、权重衰减或逐坐标缩放。即使当前梯度与旧子空间正交,历史动量或 Adam 的非均匀缩放也可能让最终参数更新重新含有旧方向分量。因此最小教学实验先用不带这些额外项的 SGD,再直接检查参数差乘以旧基的范数;不要只检查修改后的梯度就宣布严格保护成立。

保存基向量时还需检查正交性。多阶段将新残差方向拼接到旧基后,有限精度会积累误差;若 M 的列不再近似正交,MM 转置就不再是理想正交投影。可以记录 M 转置乘 M 与单位矩阵的差,并在教学实现中研究 QR 重正交化的影响,但应注明它相对作者代码的改动。

作者局限包括有限子空间容量和类别增量场景中的不足。独立判断是,能量阈值未必对应语义重要性:高方差的人物身份方向可能比低强度的细微表情方向更容易进入记忆。一个具体研究起点是固定记忆维度,对比随机取样与按表情类别均衡取样,查看少数类别是否获得更稳定的保护。

验收时同时保存三种比例:记忆基维数占输入维数、梯度被移除的范数比例、旧场景指标下降量。它们分别描述容量使用、当前更新受限程度和实际遗忘,不能互相替代。若空间占用很高但遗忘仍严重,应检查前层激活漂移和归一化状态,而不是继续把阈值调到更高。

另一个边界测试是空记忆与满记忆。空记忆应使投影后的梯度等于原梯度;若基覆盖整个输入空间,理想矩阵梯度应全部被消除。把这两个极端写成断言,可以检查左右乘方向和转置位置,也能直观解释为什么固定维度网络无法无限保留不断增加的独立方向。

可执行研究记录与评价纪律

把阅读成果变成可检查的实验记录。先建立一张样本清单,每行至少包括样本标识、原始视频或说话人标识、类别、场景、训练验证测试角色。先检查这些标识的交集,再开始训练。视频切成不同短片,并不意味着它们在语义、人物或拍摄来源上相互独立;随机划分得到的高分也不能自动证明跨场景泛化。

所有比较都应固定输入帧数、预训练权重、数据增强、优化步数和评价脚本。先用一个种子验证数据流、梯度和保存恢复,再用预先约定的多个种子做正式比较。记录每个种子的结果,报告均值和波动,而不是只保留最高一次。若为了显存缩小模型或批量,把它写入兼容性实验说明,不把改动后的结果称为原论文的严格复现。

检查分类效果时,同时保存逐样本预测和混淆矩阵。总体准确率上升可能只是多数类别变好;逐类召回率能揭示少数表情被牺牲的问题。跨场景实验还要报告每个场景的指标,防止样本量大的场景掩盖小场景退化。一个漂亮的特征可视化只能辅助观察,不能替代独立测试集、明确指标和误差分析。

模型选择必须与最终测试分开。用验证集选择学习率、训练轮数和新增模块的强度,先写清选择规则,再运行最终测试;不要根据测试集反复挑选超参数。若研究设定禁止访问旧数据,就不能为了选择防遗忘系数反复查看旧任务测试结果。可以预先固定系数,或另行声明验证数据的可访问范围,让比较遵循同一规则。

每次改动只回答一个问题,保存配置文件、代码提交、随机种子、数据清单哈希和检查点路径。最后交付一页实验说明,按“问题、对照、改动、结果、解释、失败案例”组织。若结果没有提高,先确认机制是否确实生效,再判断假设是否错误;未运行的实验只能写成计划,不能填上看似合理的数字。本文表格均为原文报告或明确注明的教学计算,并非本机复现实测。

原文短引与证据入口

原论文与完整上下文。以下三个短引仅定位作者用语,解释与判断见上文:“Gradient Projection Memory”;“Singular Value Decomposition”;“orthogonal direction”。

自己的解释与验收记录

写出一条输入到输出的链路、一个关键公式的含义,以及你完成的最小检查。笔记仅保存在当前浏览器,可从主站「我的学习笔记」导出。