小样本类增量学习:基于角度间隔的ILAR方法原理与复现实践
1. 项目概述与核心挑战
在现实世界的图像识别应用中,我们常常面临一个两难困境:一方面,我们希望一个训练好的模型能够持续学习新出现的物体类别,比如从识别猫狗扩展到识别鸟类;另一方面,我们又希望它在学习新知识时,不会把之前辛苦学会的“猫狗”知识忘得一干二净。这个“学了新的,忘了旧的”现象,在机器学习领域被称为“灾难性遗忘”。传统的类增量学习(CIL)方法试图解决这个问题,但它们通常假设每个新任务都有充足的训练数据。然而,现实往往更骨感——我们可能只有每个新类别寥寥几张图片(例如,每个新鸟种只有5张照片),这就是“小样本类增量学习”(FSCIL)要啃的硬骨头。
我最近深入研究了KAIST团队发表在IEEE Access上的一篇论文《Incrementally Learned Angular Representations for Few-Shot Class-Incremental Learning》,并动手复现了其核心算法。这项工作的核心洞察非常直观:想象一个高维的球形空间(嵌入空间),每个类别的图片经过特征提取后,会映射到这个球面上的一个点簇。在充足的基类(例如60个类别)训练后,这些点簇虽然各自聚集,但它们在球面上占据的总“地盘”其实非常小,彼此间的角度间隔很窄。这时,当你试图把几个只有零星样本的新类别(比如5个新类别,每类5张图)也塞进这个空间时,它们的特征点因为没有经过充分训练,会散乱地分布,很容易就“侵入”了旧类别已经占好的狭窄地盘,导致分类边界模糊,既学不好新的,也扰乱了旧的。
论文提出的ILAR方法,其精髓就在于“角度”二字。它不再仅仅关注特征向量的绝对位置,而是聚焦于它们在单位超球面上的方向(即角度)。通过在基类训练时主动拉开不同类特征均值之间的角度间隔,为后续新增类别预留出“空地”;在增量学习时,则像一位谨慎的园丁,一方面小心翼翼地将新类别的散乱特征“修剪”聚集,另一方面用“知识蒸馏”和“特征正则化”这两把锁,牢牢固定住旧类别特征簇的位置和形状。为了应对小样本数据带来的过拟合风险,它还巧妙地利用基类特征的统计分布(均值和协方差)来生成符合规律的“虚拟”特征样本,相当于给稀缺的新类数据做了高质量的数据增强。
2. 核心原理:为什么是“角度”?
要理解ILAR为何有效,我们需要先抛开复杂的公式,从几何视角审视深度特征表示。在一个标准的图像分类网络中,最后一个全连接层之前的特征向量,通常被用于最终的分类决策。常见的做法是使用交叉熵损失,它隐式地鼓励特征向量与其对应分类器权重向量(可视为类原型)的点积(内积)尽可能大。
2.1 特征空间的“拥挤”现象
论文通过一个精巧的实验揭示了问题的根源。他们测量了在基类训练过程中,所有类别平均特征向量之间两两夹角(inter-class mean feature angles)的平均值。结果发现,在训练开始时,由于ReLU等激活函数导致特征值非负,所有类别的平均特征向量方向几乎一致,夹角接近0度。随着训练进行,这些平均向量逐渐散开,但最终收敛时,平均夹角仅达到约73度。
这个数字意味着什么?在一个512维的超球面上,随机生成两个向量,它们之间的夹角接近90度的概率非常大。论文通过实验证明,在512维空间中,可以轻松容纳超过1万个向量,保证它们之间的最小夹角大于73度。而我们通常的基类只有几十上百个。这说明,经过标准训练后,所有类别的特征仅仅占据了整个高维球面上一个非常狭小的角落。这种“拥挤”使得新增类别的特征极易与旧类别特征区域发生重叠。
2.2 角度间隔的直观优势
基于上述观察,ILAR选择在角度空间进行操作,其优势有三点:
- 解耦范数与方向:特征向量的范数(长度)往往与图像本身的某些属性(如亮度、对比度)相关,而方向则更多地编码了语义信息。将特征归一化到单位球面,专注于角度距离,可以减弱无关因素的干扰,让模型更关注本质的语义差异。
- 明确的几何解释:角度间隔在超球面上有清晰的几何意义。增大类间角度间隔,相当于在球面上为每个类别划分出更宽敞、边界更清晰的“领地”,这直接降低了新旧类别特征发生空间重叠的概率。
- 与小样本学习的天然契合:已有研究表明,在样本极少的情况下,基于角度的度量(如余弦相似度)比基于欧氏距离的度量更具鲁棒性。因为归一化后,模型更关注特征的方向而非绝对大小,这对于分布可能偏移的小样本数据更为稳定。
因此,ILAR的整个设计都围绕着“如何在超球面上优雅地安置不断到来的新类别”这一核心问题展开。
3. 方法论详解:ILAR的三步走策略
ILAR的整体流程可以清晰地分为三个阶段:基会话训练、增量会话训练和分类器漂移补偿。下面我们拆解每个阶段的关键操作和设计意图。
3.1 基会话训练:打好地基,预留空间
在拥有充足数据的基会话(Session 1),目标是学习一个强大的特征提取器,并为未来预留空间。
核心操作:使用大间隔余弦损失(CosFace)传统的交叉熵损失虽然能让不同类分开,但类间边界可能不够清晰。ILAR在基会话采用了CosFace损失。简单来说,它在计算分类概率时,不是在原始角度上做Softmax,而是在角度上减去一个预设的间隔m。公式表达为:
L_cos = -log( exp(s * (cos(θ_yi) - m)) / (exp(s * (cos(θ_yi) - m)) + Σ_{j≠yi} exp(s * cos(θ_j)) )
其中,θ_yi是样本特征与其真实类别权重向量的夹角,s是缩放因子,m是角度间隔。
实操心得:这里的
m和s是关键超参数。m太大可能导致训练不稳定,太小则间隔效果不明显。在复现时,我从m=0.3(对应角度约17度)开始调优,发现在0.3到0.5之间模型表现较好。s的作用是放大对数its,通常设置在30左右,有助于拉开不同类别的概率差距。
这个损失函数强制模型学习到的特征满足:不仅要把同类样本聚在一起,还要让不同类别的特征均值在球面上至少间隔m的角度。这就好比在规划城市时,不仅把同一个小区的楼房建得近,还在不同小区之间预留了宽阔的绿化带,为未来建设新小区(增量类别)留出了空地。
后续处理:用特征均值初始化分类器训练结束后,ILAR做了一项重要操作:丢弃原本随机初始化并通过梯度下降学到的分类器权重w,转而用每个类所有训练样本特征的平均值(即类原型)来作为新的分类器权重。即w_i = mean( f_θ(x) for x in class i )。
注意事项:这一步至关重要。它保证了分类器权重与特征空间中的类中心完全对齐。在后续的增量学习中,当我们说“固定旧分类器”时,固定的是这个更具代表性的类原型,而不是那个可能带有优化偏差的原始权重。
3.2 增量会话训练:谨慎扩张,巩固记忆
当进入只有少数样本的新任务会话(Session t>1)时,挑战真正开始。ILAR的增量训练策略是一个多目标优化的艺术。
第一步:特征生成以缓解数据短缺对于每个新增的样本,ILAR不是直接使用它,而是以其特征为中心,“想象”出更多样本。具体做法是:
- 找到该样本特征在基类特征中最接近的K个类。
- 将这K个基类的特征协方差矩阵平均,得到一个估计的分布。
- 以此分布和当前样本特征作为均值,进行多元高斯采样,生成一批新的特征向量。
避坑技巧:这里协方差矩阵的混合参数
α控制着生成特征的分散程度。α太小,生成的特征过于集中,增强效果有限;α太大,生成的特征可能偏离真实分布,引入噪声。论文中通常设置为0.1到0.2。在实践中,我还会对生成的特征进行裁剪,确保其范数在一个合理范围内,避免异常值。
第二步:双管齐下的损失函数设计这是ILAR的核心创新点。总损失函数L_inc由两部分加权组成:λ1 * L_inc,n(学习新知识)和λ2 * L_inc,p(防止遗忘)。
学习新知识(L_inc,n):
- 交叉熵损失:让新类样本被正确分类。这里使用的分类器覆盖了所有已见类别(基类+已学增量类+当前新类)。
- 角度三元组中心损失(ATCL):这是关键。ATCL要求一个新类样本的特征到其本类原型(中心)的角度距离,至少比到其他任何类原型(包括所有旧类)的角度距离小一个间隔
m3。这直接驱动新类的特征向自己的类中心聚集,同时远离旧类的中心区域。公式类似于三元组损失,但基于角度。
防止遗忘(L_inc,p):
- 知识蒸馏损失:将上一轮训练好的模型(旧模型)对当前输入(核心集+新数据)产生的预测概率作为“软标签”,监督当前模型的训练。这鼓励当前模型保持与旧模型相似的输出行为,从而保留旧知识。
- 特征正则化损失:计算当前模型与旧模型对同一输入提取的特征向量(归一化后)的欧氏距离。这直接约束新模型提取的特征方向不要偏离旧模型太远。
核心设计逻辑:
L_inc,n中的ATCL是积极的“开拓者”,它主动将新类特征聚拢,并推向与旧类保持角度间隔的区域。L_inc,p中的两项则是保守的“守护者”,它们一个在输出层面(蒸馏),一个在特征层面(正则化),牢牢锚定旧类知识。λ1和λ2的平衡是调参重点,通常λ2需要比λ1小一个数量级(例如400 vs 5),以确保模型在积极学习新类的同时,对旧类的改动足够温和。
3.3 分类器漂移补偿:让“指针”跟上“地图”
在增量训练中,特征提取器f_θ的参数被更新了。这意味着,即使旧类样本的特征本身被正则化损失约束着变化不大,但用来代表它们的“类原型”(即之前用特征均值初始化的分类器权重w_old)却是在旧的特征提取器下计算得到的。现在特征提取器变了,用旧的w_old与新特征提取器计算相似度,就相当于用旧地图上的坐标在新地图上找位置,必然产生偏差。
因此,ILAR在每次增量训练后,会对所有旧分类器权重进行“漂移补偿”:w_i_new = w_i_old + ( f_θ_new(w_i_old) - f_θ_old(w_i_old) )
这个操作直观上可以理解为:将旧的类原型向量,分别用新旧两个特征提取器“过一遍”,计算其变化量,然后用这个变化量去更新旧的类原型,使其与新的特征提取器对齐。
4. 实验复现与关键细节
为了验证ILAR的有效性,我按照论文设定,在CIFAR-100和miniImageNet数据集上进行了复现。这里分享一些关键的实现细节和调参经验。
4.1 实验环境与基线设置
- 网络骨架:采用标准的ResNet-18,将最后的全连接层替换为一个512维的特征层,后接一个用于分类的全连接层(在基训练后会被类原型替换)。
- 数据集划分:
- CIFAR-100/miniImageNet:60个基类,剩余40个类分为8个增量会话,每个会话5个类(5-way),每类5个样本(5-shot)。
- CUB-200:100个基类,剩余100个类分为10个增量会话,每个会话10个类(10-way),每类5个样本(5-shot)。
- 评估指标:
- 平均精度(AVG):所有会话结束后,在所有已见类别上的平均分类精度。这是综合性能指标。
- 性能下降(PD):基会话精度与会话结束后基类精度之差。衡量遗忘程度。
- 新任务学习能力(NLA):所有增量会话(从第2个开始)精度的平均值。专门衡量学习新类的能力。
4.2 训练超参数配置表
以下是我复现过程中调整后效果较好的超参数配置,可能与论文原参数有细微差别,但核心思想一致:
| 超参数 | 符号 | 建议值 | 作用与调参心得 |
|---|---|---|---|
| 基会话 | |||
| 余弦间隔 | m | 0.35 | CosFace损失中的角度间隔余弦值。影响基类间的分离程度。 |
| 特征缩放因子 | s | 30.0 | 放大logits,稳定训练。 |
| 初始学习率 | lr | 0.1 | 使用SGD优化器,动量0.9,权重衰减5e-4。 |
| 学习率衰减 | 第40轮×0.1 | 共训练100轮。 | |
| 增量会话 | |||
| 新知识损失权重 | λ1 | 400 | 控制学习新类的强度。需远大于λ2。 |
| 防遗忘损失权重 | λ2 | 5 | 控制保留旧知识的强度。过大易导致欠拟合新类。 |
| ATCL角度间隔 | m3 | 0.5 | 弧度制,约28.6度。促使新类与旧类保持距离。 |
| ATCL损失权重 | β | 1.0 | 平衡交叉熵与ATCL。 |
| 特征生成近邻数 | K | 3 | 选择最接近的K个基类统计信息。 |
| 特征生成分散度 | α | 0.1 | 高斯采样的额外方差。控制生成特征的多样性。 |
| 特征提取器学习率 | lr_fe | 0.03 | 通常低于分类器学习率,进行更精细的调整。 |
| 分类器学习率 | lr_cls | 0.1 | |
| 学习率衰减 | 第20、50轮×0.1 | 通常训练60-100轮,因数据量少收敛快。 |
4.3 核心代码片段解析
以下是增量会话训练循环中,损失计算部分的核心代码逻辑(使用PyTorch框架示意):
def incremental_session_train(model, prev_model, data_loader, old_classes, new_classes, lambda1, lambda2, beta, m3): model.train() total_loss = 0 for images, labels in data_loader: # 1. 特征提取与归一化 features = model.feature_extractor(images) # [batch, 512] features = F.normalize(features, p=2, dim=1) # 归一化到单位球面 # 2. 计算分类logits (所有已见类别: old_classes + new_classes) logits = model.classifier(features) # 假设classifier能处理所有类 # 3. 学习新知识损失 L_inc,n # 交叉熵损失 ce_loss = F.cross_entropy(logits, labels) # 角度三元组中心损失 (ATCL) # 获取所有类原型(归一化的分类器权重) all_prototypes = F.normalize(model.classifier.weight, p=2, dim=1) # [total_classes, 512] # 计算每个样本特征与所有原型的角度 cos_sim = torch.mm(features, all_prototypes.t()) # [batch, total_classes] angles = torch.acos(torch.clamp(cos_sim, -1+1e-7, 1-1e-7)) # 反余弦得到角度 atcl_loss = 0 for i in range(features.size(0)): pos_angle = angles[i, labels[i]] # 与真实类原型的角度 # 找到非真实类中角度最小的 neg_angles = angles[i].clone() neg_angles[labels[i]] = float('inf') min_neg_angle = neg_angles.min() # hinge loss with margin m3 atcl_loss += torch.max(pos_angle + m3 - min_neg_angle, torch.tensor(0.0)) atcl_loss /= features.size(0) L_inc_n = ce_loss + beta * atcl_loss # 4. 防止遗忘损失 L_inc,p with torch.no_grad(): prev_features = prev_model.feature_extractor(images) prev_features = F.normalize(prev_features, p=2, dim=1) prev_logits = prev_model.classifier(prev_features) # 知识蒸馏损失 (KL散度) T = 2.0 # 温度系数,软化概率分布 curr_probs = F.log_softmax(logits / T, dim=1) prev_probs = F.softmax(prev_logits / T, dim=1) distil_loss = F.kl_div(curr_probs, prev_probs, reduction='batchmean') * (T * T) # 特征正则化损失 (归一化特征间的L2距离) reg_loss = F.mse_loss(features, prev_features) L_inc_p = distil_loss + reg_loss # 5. 总损失 loss = lambda1 * L_inc_n + lambda2 * L_inc_p optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() # 6. 训练后:分类器漂移补偿 (仅对旧类权重) with torch.no_grad(): for cls_idx in old_classes: old_weight = model.classifier.weight.data[cls_idx] # 计算旧权重在新旧特征提取器下的特征差异 drift = model.feature_extractor(old_weight.unsqueeze(0)).squeeze() - \ prev_model.feature_extractor(old_weight.unsqueeze(0)).squeeze() model.classifier.weight.data[cls_idx] += drift代码要点:
- 特征归一化:这是整个角度学习的基础,必须在计算任何相似度或损失前进行。
- ATCL实现:计算每个样本与所有类原型的角度,然后对每个样本计算 hinge loss。注意使用
torch.acos时需要对输入进行裁剪,防止数值溢出。- 蒸馏温度T:这是一个常用技巧,软化概率分布可以使蒸馏过程传递更多“暗知识”。通常T=2~4效果较好。
- 漂移补偿:在
no_grad()上下文中进行,这是一个后处理步骤,不参与梯度计算。
5. 结果分析与讨论
复现实验的结果与论文报告的趋势基本一致。ILAR方法在AVG和NLA指标上通常优于许多之前的FSCIL方法,尤其是在学习新类的能力(NLA)上提升明显,同时保持了可接受的遗忘程度(PD)。
5.1 性能对比与消融实验
为了理解每个组件的作用,我进行了消融实验(以miniImageNet 5-way 5-shot为例):
| 实验配置 | AVG (%) | NLA (%) | PD (%) | 分析 |
|---|---|---|---|---|
| ILAR (完整) | 67.2 | 58.5 | 18.3 | 基准性能。 |
| w/o ATCL | 65.1 | 53.7 | 16.5 | NLA显著下降,说明ATCL对聚集新类、拉开与旧类距离至关重要。 |
| w/o 特征生成 | 66.0 | 56.8 | 20.1 | PD增大,说明数据增强能有效防止过拟合新类导致的旧类遗忘。 |
| w/o 蒸馏损失 | 65.8 | 57.9 | 21.5 | PD增大,说明蒸馏对保留旧类输出知识有效。 |
| w/o 特征正则化 | 66.5 | 58.0 | 19.8 | PD略有上升,说明直接约束特征移动对巩固记忆有辅助作用。 |
| 基会话用CE损失 | 65.5 | 57.2 | 19.0 | AVG和NLA均下降,验证了基会话扩大角度间隔的长期收益。 |
从消融实验可以看出:
- ATCL是提升新类学习能力的核心:没有它,新类特征无法有效聚拢并与旧类保持安全距离。
- 特征生成是防止过拟合的关键:在小样本下,它提供了必要的“虚拟数据”,让模型对新类的决策边界学习更鲁棒。
- 防遗忘的两大支柱(蒸馏和正则化)缺一不可:它们从不同层面(输出分布和特征空间)约束模型,共同抵御灾难性遗忘。
- 基会话的间隔损失是长远投资:虽然只影响基类训练,但它为后续所有增量学习创造了一个更宽松、更有序的初始特征空间。
5.2 可视化分析:角度空间的演变
通过t-SNE将高维特征降维可视化,可以直观看到ILAR的效果:
- 增量训练前:基类特征(蓝色)紧密聚集成多个小簇。新类特征(红色)分散在基类簇的周围甚至内部,重叠严重。
- 增量训练后:新类特征(红色)被拉拢,形成了自己独立的、紧凑的簇,并且与邻近的基类簇之间出现了更清晰的间隙。同时,基类簇的形状和位置基本保持不变。
这直接印证了ILAR的设计目标:在最小化扰动旧类特征分布的前提下,将新类特征规整地安置到预留的空间中。
5.3 常见问题与调参陷阱
在实际复现和调参过程中,我遇到了以下几个典型问题:
增量训练时,新类精度始终上不去,旧类精度却狂掉。
- 可能原因:
λ1(新知识损失权重)和λ2(防遗忘损失权重)比例失衡。λ2太大,模型被过度约束,无法有效学习新类;λ1太大,则模型过于激进地学习新类,破坏了旧知识。 - 解决思路:这是一个典型的权衡。建议从
λ1=400, λ2=5开始。如果新类学得慢,可微调λ1稍增或λ2稍减(例如 450:5)。如果旧类遗忘严重,则反之。务必监控每个增量会话后,新旧类各自的精度变化。
- 可能原因:
ATCL损失计算不稳定,出现NaN。
- 可能原因:
torch.acos()的输入值由于数值误差可能略微超出 [-1, 1] 的范围。 - 解决思路:在计算余弦相似度后,使用
torch.clamp(cos_sim, -1+eps, 1-eps)进行裁剪,其中eps是一个极小的数(如1e-7)。
- 可能原因:
特征生成模块产生的特征似乎没有帮助,甚至有害。
- 可能原因:生成特征的分散度参数
α设置不当,或用于生成特征的近邻基类数K不合适。 - 解决思路:
α控制“想象力”。从0.05开始尝试,逐渐增加到0.2。K通常取3或5。可以可视化生成的特征(通过PCA/t-SNE),看它们是否围绕真实样本特征形成合理的分布,而不是散乱无章。
- 可能原因:生成特征的分散度参数
漂移补偿后,某些旧类的分类精度反而下降。
- 可能原因:特征提取器的变化太大,导致计算出的漂移向量不准确,或者补偿过程引入了噪声。
- 解决思路:确保增量训练时特征提取器的学习率足够低(如0.03),使其更新是细微的。也可以尝试对漂移向量进行平滑,例如
new_weight = old_weight + 0.9 * drift。
6. 总结与展望
ILAR方法为小样本类增量学习提供了一个清晰而有力的框架:在角度空间中进行规划与建设。它像一位经验丰富的城市规划师,在基类建设阶段就规划好宽阔的街区间隔(大间隔余弦损失),在面对新的小型社区(增量类)时,则采用精细化的施工方案:一方面引导新社区有序聚集并保持与老社区的间隔(ATCL),另一方面严格保护老社区的原貌不受破坏(蒸馏+正则化),同时还利用老区的建筑规范来生成新区的虚拟蓝图以弥补图纸不足(特征生成)。
从我复现和实验的体会来看,这种方法最大的优势在于其原则的简洁性和有效性。它没有引入特别复杂的元学习或动态网络结构,而是在标准的特征提取-分类框架内,通过精心设计的损失函数和训练策略,巧妙地解决了特征空间拥挤和灾难性遗忘这两个核心矛盾。
当然,ILAR也有其局限性和可扩展的方向。例如,它对所有类别使用固定的角度间隔,而现实中不同类别之间的语义差距是不同的。未来可以探索自适应的间隔策略。此外,特征生成依赖于基类的统计分布,当增量任务与基任务差异极大(域偏移)时,其效果可能会打折扣。如何构建更鲁棒、更通用的特征生成或回放机制,是一个值得深入的方向。
对于想要在实际应用中尝试FSCIL的研究者和工程师,我的建议是:首先深入理解角度空间和特征归一化的几何意义,这是理解后续所有操作的基础。其次,耐心调参,特别是λ1、λ2、m、m3这几个关键平衡参数,它们需要根据你的具体数据集和网络结构进行微调。最后,务必建立完善的评估体系,不仅要看最终的平均精度,更要拆解观察每个增量会话后新旧类别的精度变化,这样才能真正诊断出模型是在“高效学习”还是在“拆东墙补西墙”。
