ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

显著性图引导的幻觉样本:小样本学习的数据增强新思路

显著性图引导的幻觉样本:小样本学习的数据增强新思路

小样本学习折腾久了,你会发现最头疼的问题往往不是模型跑得太慢,而是手里每类就那几十张图,怎么调都像在“螺蛳壳里做道场”。最近重读《Few-shot Learning via Saliency-guided Hallucination of Samples》这篇论文,触动我的是它没有把重心放在修改损失函数或设计元学习策略上,而是直接回答了一个我想了很久的问题:能不能在数据端“凭空”造出更多有效的样本?论文给出的方案很朴素——先用一个已经训练好的分类网络,把图像中真正决定类别判断的显著性区域(saliency map)提取出来,再把这个区域移植到另一张图像里,合成一批带正确标签的幻觉样本(hallucinated samples)。这个思路放在今天不算惊艳,但它让我第一次意识到,小样本场景里的数据增强不是简单翻转、裁剪就能糊弄过去的,关键是要把“语义”一起搬过去。

1. 小样本学习的老难题:不是模型不够强,而是数据本身太单薄

1.1 数据少带来的一系列连锁问题

小样本学习的设定说起来很“残忍”:训练时每个新类别只给你几到几十张带标注的图,测试时还要在完全没见过的新类别上做分类。很多人第一反应是“模型太小了,容量不够”,但实际跑过几个基准就会发现,问题往往出在数据分布上。

一个分类器在训练阶段见过的样本越少,它对类别边界的估计就越不稳定。假设把一张 224×224 的图像展开成特征空间里的一个点,本质上是在一个高维空间里做线性划分。支撑集只有 1 张或 5 张图时,这个空间的“锚点”太少,模型很容易找到一条在训练集上完美、但稍微挪一点点就翻车的判别面。这也是为什么小样本任务里经常见到训练准确率接近满分、测试准确率却很低的情况——过拟合不是因为你调参不够细心,而是样本量根本撑不起你想要的决策边界。

更麻烦的是,小样本任务通常采用“预训练 + 微调”的范式:先在数据充足的基类集合上训练一个通用特征提取器,再把这个特征提取器搬到新类别上微调。基类上训得好不代表迁移后好,因为新类别只有极少样本,微调阶段本质上是在“高维空间中用几个点去拟合一个未知分布”,几乎必然会过拟合。

1.2 常规增强手段为什么“治标不治本”

说到数据不足,很多人会立刻想到随机翻转、随机裁剪、颜色抖动这些常规增强手段。这些方法确实有效,但它们有一个共同的局限:它们都是在“同一张图像本身的邻域”里做变换。你翻转一张猫的图片,得到的还是一只猫;你把它颜色调深一点,它仍然只是一只猫。模型从这些变换里学到的,是这张图在平移、旋转、颜色扰动下的不变性,而不是“猫”这个类别在不同背景、不同姿态、不同组合下的泛化性。

举个例子,如果你手里只有一张“草地上坐着猫”的图片,常规增强能造出“草地上的猫往左歪头”“草地上的猫颜色偏暗”之类的样本,但永远造不出“厨房里的猫”“沙漠里的猫”。模型因此很容易学到一种偷懒的决策方式:看到绿色像素就倾向判断为猫,而不是真正去理解猫本身的形状纹理特征。在小样本场景里,这种“背景捷径”非常致命,因为测试阶段的新类别图像,背景分布和训练时几乎不可能完全一致。

Mixup、CutMix 这类基于样本混合的增强方法往前走了一大步,它们确实引入了跨样本的信息。但它们的混合区域是纯随机的,没有语义约束:CutMix 把一张图的某个矩形块贴到另一张图上,很可能把猫的头切掉一半、把另一张图的背景贴上另一半。对普通训练集来说,这种带噪声的样本可以增强鲁棒性;对样本极度稀缺的小样本任务来说,这种“噪声”会进一步污染本来就不够清晰的类簇结构。

所以,一个自然的问题就是:能不能让增强过程具备语义感知?既然分类器自己其实知道“图像的哪个区域对类别判断最重要”,为什么不让它来指导我们造样本?这正是这篇论文的出发点。

2. 核心机制拆解:显著性图引导的类间样本组合

2.1 显著性图怎么算:用分类器的梯度当“免费探测器”

论文的第一个关键思想,是把已经训练好的基类分类器当作一个免费的显著性探测器。给定一张输入图像和一个训练好的分类器,我们对模型输出的某类得分做反向传播,得到该得分对输入像素的梯度。梯度的绝对值越大,说明这个像素的变化对类别得分的影响越剧烈,也就是该像素在分类决策中的重要性越高。

用公式表示大概是:S = | ∂y_c / ∂I |,其中 y_c 是模型对类别 c 的得分,I 是输入图像。通常会对三通道的梯度取最大值或均值,把结果归一化到 [0,1] 区间,就得到一张和原图同尺寸的显著性图。

这里有个很容易忽略的细节:计算梯度时,用“预测得分最高的类别”和用“真实标签对应的类别”,结果差别很大。论文实际用的是分类器输出的类别得分,我个人复现时也倾向于用模型预测的那个类别,而非真实类别——因为我们要提取的正是“模型认为最能区分该类别的区域”,而预测类别往往就是模型最有信心的判断。如果基类分类器训练得足够好,两者差异不会太大,但在某些样本上,用真实标签反而会提取到不太稳定的区域。

2.2 从显著性图到二值掩码:阈值、插值和区域搬运

拿到显著性图后,下一步是把连续的热力图变成二值掩码。做法通常是在归一化后的显著性图上取一个阈值 τ,大于 τ 的像素置为 1,其余置为 0。这样一来,掩码所覆盖的区域就是模型认为“对分类最关键的像素集合”。

掩码生成之后,就是真正的“搬运”步骤。假设我们有两个样本:source(我们希望最终合成样本属于的类别)和 target(提供背景上下文的样本)。我们把 source 的显著性区域像素直接复制到 target 图像的对应位置上,合成的新样本拥有 target 的背景和上下文,但关键判别区域来自 source。新样本的标签取 source 的标签。

下面是我复现时用框架写的一个极简版本:

import torch def compute_saliency(model, img, label): # img shape: [C, H, W], 需要梯度 img = img.clone().requires_grad_(True) out = model(img.unsqueeze(0)) # 用预测类别得分,更接近模型真实的判别依据 pred_label = out[0].argmax() score = out[0, pred_label] model.zero_grad() score.backward() # 对三通道梯度取绝对值,再按通道取最大值 saliency = img.grad.abs().max(dim=0)[0] saliency = saliency / (saliency.max() + 1e-8) return saliency.detach() def hallucinate(source, target, saliency, tau=0.5): # source/target: [C, H, W] mask = (saliency > tau).float().unsqueeze(0) # [1, H, W] # 如果涉及尺寸变化,用最近邻插值 # mask = F.interpolate(mask, size=target.shape[1:], mode='nearest') new_img = source * mask + target * (1 - mask) return new_img

阈值 τ 的选择很讲究。τ 太高,搬运区域太小,合成样本几乎等于 target 原图,增强效果不明显;τ 太低,把目标区域的大块背景也搬过去了,合成样本的“目标类别”会混入大量噪声。论文里的消融实验也证实了这一点,后面我会专门展开。

2.3 为什么“不自然的样本”反而更有效

第一次看到生成的样本时,我的第一反应是“这图也太怪了”:一只猫的头出现在一片荒地的纹理里,或者一辆车的轮廓嵌在室内场景中。正常的机器学习直觉告诉我们,训练数据和测试数据应该服从同一分布,这些“拼贴”出来的样本看起来严重偏离自然图像分布,怎么会对分类有用?

关键在于,小样本任务里的测试数据本身就可能很“怪”。测试集里的新类别图像背景各异,如果模型在训练时只见过“自然背景下的目标”,它就学不会在陌生背景下提取目标特征。幻觉样本恰恰提供了一类刻意制造的正样本:目标类别语义被保留、上下文被替换,模型被迫放弃依赖背景捷径,转而认真学习目标类别本身的纹理和形状。

这个机制可以类比成我们人类学习认字:如果小时候只见过同样的教科书字体,换种手写体可能就不认识了;但如果老师把同一个字用各种奇怪的字体、颜色、背景展示给你,你对这个字的认知反而更稳固。幻觉样本做的就是这件事——它把“类别语义”和“上下文语境”解耦,逼模型抓住真正重要的判别信息。

在评估阶段,这个逻辑同样成立。测试时我们手里有少量支撑样本,如果直接拿去算原型向量,结果很容易被单张样本的偶发性背景干扰。把支撑样本也做一遍幻觉增强,相当于每个类别都获得了大量“同一语义、不同背景”的样本,原型向量的估计会稳定得多。

3. 训练-评估全流程:从基类分类器到幻觉样本的四个阶段

3.1 阶段一:在基类上训练一个“分类能力探测器”

整个方法的前提,是有一个对“什么是判别区域”有良好判断力的分类器。这个分类器不需要花哨,常见的小规模图像分类网络结构就够用。关键是在基类集合上把它训练到充分收敛,因为显著性图的质量直接取决于特征提取器是否已经学到了语义层面的知识。

基类集合通常包含几十个类别,每个类别样本数量充足。我在复现时会把分类器的输出层设计成和基类类别数一致的全连接层,训练时就用常规的交叉熵损失,没有加入特别的辅助损失。训练轮数宁多勿少,因为稀疏小样本任务对特征质量很敏感,一个半吊子的预训练特征提取器会让后面的显著性图完全不可用。

这里有个需要注意的点:预训练分类器的准确率并不是唯一指标。某些分类错误区域在梯度上可能呈现出不稳定的响应,但整体上,只要分类器在基类上超过一个合理的准确率阈值,显著性图的质量就足够用于样本幻觉。我在实验里发现,与其追求极致的分类准确率,不如关注特征图本身是否具备清晰的语义边界——后者对后续合成步骤的影响更大。

3.2 阶段二:批量生成幻觉样本

有了训练好的分类器,第二阶段就是批量构造幻觉样本。具体做法我在前面已经给出了核心函数,但在完整流程里,还需要解决两个工程问题:怎么采样配对,以及生成多少样本。

配对策略上,最简单的方式是随机采样两个不同类别的样本,一个作为 source,一个作为 target。需要尽量避免 source 和 target 同属一个类别,因为同类别配对生成出来的样本和原图差异太小,本质退化为一种“局部置换”增强,没有跨类别语义迁移的效果。更好的做法是维护一个类别索引,每次随机选一个类别作为 source,再从剩余类别里随机采样 target。

生成数量方面,我建议按类别扩增比例的思路来设置。比如原本每个类只有 5 张支撑样本,你可以为每张真实样本生成 3 到 5 个幻觉样本,让支撑集扩充到 20 到 30 个。数量太少,方差不够;数量太多,边际收益会下降,而且训练时间会明显拉长。论文里的实验也显示,幻觉样本数量从 0 开始增加时,准确率上升很快,但到某个临界点后曲线趋于平缓。

3.3 阶段三:用真实样本和幻觉样本共同微调分类器

生成完样本后,接下来的训练阶段很有意思——不是单独在幻觉样本上训练,而是把真实样本和幻觉样本混合在一起,共同微调整个分类器。这样做的目的是让模型在真实分布和增强分布之间找到一个平衡点,而不是过度适应合成数据。

训练目标仍然是交叉熵损失,标签按 source 类别设置。我习惯把幻觉样本的比例控制在 30% 到 50% 之间。比例太低,模型学不到增强带来的泛化收益;比例太高,模型会被合成样本的“不自然外观”带偏,甚至忘记真实样本的细节特征。

微调时要控制训练轮数。小样本场景下的规律是:微调过早,模型还没吸收新类别的判别信息;微调过晚,模型就开始死记硬背那几张某实验里我试过直接把微调轮数翻倍,结果准确率不仅没提升,反而掉了好几个点——教训就是,样本越少,越要小心别过拟合。

3.4 阶段四:评估阶段也做一次“临时增强”

很多增强方法只在训练阶段使用,评估时恢复原样回原样测试。但论文给我的一个重要启发是,评估阶段的小样本数据增强同样有效,甚至在某些设定下比训练阶段的增强收益更大。

具体做法是:在测试某个新类别任务时,先用预训练分类器对支撑集样本计算显著性图并生成幻觉样本,然后用扩充后的支撑集计算原型向量或训练一个简单的分类器,最后对查询集样本做预测。由于支撑集样本量本来就只有几个,扩充后的支撑集能让类别原型的估计方差大幅降低。我自己复现时,在 5-way 1-shot 设定下,仅这个测试期增强就能带来接近一个百分点的提升。

评估阶段还有个小细节:查询集样本不需要做幻觉增强,因为你不能利用未知样本的真实标签去生成样本。但如果查询集样本是图片,做常规的翻转、裁剪增强来投票,仍然可以叠加到这个框架上。

4. 实验规律:在标准基准上的增益与消融观察

4.1 在常用小样本基准上的效果观察

论文在标准的 5-way 1-shot 和 5-way 5-shot 设定下做了验证。从实验报告中能看到一个很典型的规律:5-way 1-shot 的提升幅度明显大于 5-way 5-shot。

这其实不难理解。5-way 1-shot 的支撑集每个类别只有一张图,类别原型的估计方差极大,幻觉增强相当于用多个合成样本来平滑这个估计,收益自然非常明显。到了 5-way 5-shot,每类已经有五张真实样本,原型估计本身相对稳定,增强带来的额外信息增益就会变小。

如果把这套增强模块加到一个朴素的预训练 + 微调基线上,1-shot 任务的准确率能提升两到三个百分点左右。这个幅度的提升在公开基准上已经相当可观,因为同一时期的很多先进方法花费大量精力修改训练目标,换来的也只是几个点的进步。

4.2 消融实验揭示的三个规律

关于阈值对结果的影响,消融实验的结论很清晰:阈值存在一个最优区间。阈值太低,比如 0.1 到 0.2 的时候,显著性区域占据图像的大部分面积,合成样本和“把两张图直接叠加”几乎没区别,类别语义被严重稀释;阈值太高,比如 0.8 以上,搬运的区域太小,样本多样性不足,增强效果趋近于零。

另一个规律是显著性图的来源层级。用网络浅层特征计算的梯度,更关注边缘、颜色等局部纹理;用深层特征计算的梯度,更能反映语义级的物体部件信息。论文实验显示,深层特征驱动的显著性图在后续增强上效果更好。这符合直觉,因为深层特征已经收敛到了“类别概念”层面。

还有一个关于幻觉样本数量的观察。生成样本数量太少起不到作用,但数量达到一定程度后继续增加,收益会边际递减。我在某次实验里看到过这种情况:从每类 10 个幻觉样本增加到 30 个时,准确率还在缓慢上升;但增加到 50 个以上,不仅训练时间翻倍,准确率反而出现了微小下降。这可能是因为同类别生成的幻觉样本之间高度相关,过多的合成样本会让模型反复看到相似的拼贴模式,反而束缚了它的泛化能力。

4.3 与各类小样本方法的兼容性

这篇论文的方法本质上是一个“即插即用”的增强模块,它不依赖特定的训练范式,可以很自然地叠加到当时主流的几类方法上。

和原型网络这一类的基于度量的方法结合时,思路最顺畅:先用幻觉增强扩充支撑集,再用扩充后的支撑集计算类别原型,最后做最近邻分类。和匹配网络这类注意力模型结合时,幻觉样本可以作为额外的支撑集输入,让注意力机制在更丰富的上下文中计算相似度。

即便是优化型的小样本方法,比如通过几步梯度更新来适配新任务的元学习范式,幻觉增强同样可以作用在支撑集的任务构造阶段。由于它不改动损失函数、不改动网络结构,所以接入成本很低。我当时在复现时是先把增强模块加到最简单的基线上,确认收益后,再把它和其它方法做组合对比。

5. 复现笔记:几个容易踩的坑和可选改造方向

5.1 复现最容易踩的五个坑

第一个坑是显存和反向传播。计算显著性图需要对输入图像求梯度,如果一不小心在批处理时把整个 batch 的梯度都打开了,显存占用会成倍增加。我的做法是:计算显著性图时用单样本或者极小的 mini-batch,算完立刻 detach 释放梯度图,后续的幻觉样本生成过程完全不需要梯度信息。

第二个坑是掩码插值方式。当显著性图和原图尺寸不一致时,要对掩码做缩放,这时候务必用最近邻插值,不要用双线性插值。双线性会把二值掩码变成带有中间灰度的模糊掩码,导致搬运区域出现半透明的混合带,新样本会出现明显的边界融合伪影。用最近邻插值可以保持掩码清晰,样本看起来更接近真实拼贴。

第三个坑是不要把随机增广叠加得过分。有些复现者喜欢在生成幻觉样本之后再叠加随机裁剪、随机颜色抖动,结果增广强度过大,很容易把搬运过去的显著性区域破坏掉,相当于你辛苦搬过去的“语义核心”又被随机噪声洗掉了。我的经验是,幻觉增强之后最多做一次轻度的随机水平翻转,其他重增广留给常规训练分支。

第四个坑与采样配对方式有关。如果代码里用随机 batch 采样,经常会出现 source 和 target 来自同一类别的情况。这会让大量生成的幻觉样本退化成“背景微调”,严重浪费计算资源。我在实现时专门写了一个类级别采样的逻辑,保证每个幻觉样本都来自两个不同类别。

第五个坑是 BatchNorm 统计量的漂移问题。训练好的分类器里如果有 BatchNorm 层,在计算显著性图和生成样本时,如果输入分布差异过大,统计量可能出现漂移。实际复现中,如果模型是在基类上充分训练过的,这个问题不太明显;但如果在微调阶段大量使用幻觉样本,推荐把这部分样本单独过一个前向,以稳定 BatchNorm 的统计量。

5.2 如果让我动手改,我会这么做

论文的原始实现是一个很好的起点,但它还有不少可以继续挖掘的空间。比如显著性图的计算,除了基于梯度的方式,还可以用 CAM 类的类激活映射来替代。梯度方式和 CAM 方式各有优劣,梯度方式更精细但需要反向传播,CAM 方式速度快、实现简单,但空间分辨率通常不如显著图细腻。如果你需要在大规模数据上快速批量生成幻觉样本,建议用 CAM 做初筛,再用梯度方式细化关键区域。

另一个改造方向是把二值掩码换成软掩码。硬掩码的边界在合成样本上非常明显,模型有可能学到“哪里边缘突兀,哪里就是关键区域”这样的捷径。给掩码加一圈高斯过渡,让搬运区域和背景之间平滑融合,可以在视觉上更接近真实图像,某些实验里能提高鲁棒性。

如果你正在做分类之外的小样本任务,比如小样本目标检测或小样本分割,这个思路也完全可以迁移过去:显著性图可以替换为检测框或分割掩码,搬运粒度从像素级变成区域级。本质上都是“把关键语义区域从源样本搬到上下文不同的目标样本上”,核心思想是一致的。

结尾的几句大实话

如果你只是想快速给现有小样本模型加一个增强模块,我的建议是别一上来就做花哨的版本。先用最朴素的预训练分类器生成显著性图,阈值设在让搬运区域大约占图像面积四分之一到三分之一的位置,训练和评估阶段同时用,跑出结果再决定要不要引入 CAM、软掩码这些变体。大多数情况下,朴素版本已经能带来相当可观的收益,真正麻烦的反而是数据配对、阈值调节这些工程细节。

我在复现过程中还有一个体会:测试阶段的增强比想象中更重要。很多人辛辛苦苦在训练阶段造了几百张幻觉样本,却忘了评估时只用那几张原始支撑样本,等于把最后的“冲刺”机会白白浪费了。先试着在测试阶段给支撑集扩充到十几张,再回头调训练阶段的生成数量,你会发现性价比完全不同。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进