
少样本图像识别Few-Shot Image Recognition这个方向我前前后后看过不少论文大部分工作都在“度量学习”和“元学习”这两条老路上翻来覆去。但《Few-Shot Image Recognition by Predicting Parameters from Activations》这篇是少有的让我读完有“原来还能这么干”感觉的文章。它的核心思路一句话能说清不去学一个固定的分类器也不指望测试时再微调而是训练一个“参数预测器”让它直接根据支持集图片在骨干网络上的激活特征生成新任务分类器的权重。听起来有点绕但想通之后非常自然——骨干网络在不同任务之间共享那分类器参数本质上就是支持集信息的一个函数把这个函数学出来就行了。这篇论文解决的是少样本场景下的核心痛点新类别只有一两张标注图怎么才能快速得到一个稳定可用的分类器。它的优势在于推理快、不需要测试时反向传播、对类别数量不敏感既适合做研究复现也适合想在生产环境里快速适配新类别的工程团队。下面我按自己复现时的思路把论文的核心设计、关键技术细节、训练套路和踩过的坑一次性讲清楚。1. 核心思路拆解为什么不走微调非要“预测参数”1.1 少样本场景下的两难困境先把问题定义说清楚。少样本图像识别通常是这么设置的先有一批数据充足的基类base classes用它们训练出一个通用的特征提取网络。到了新任务里出现一批基类里没见过的新类每一类只有 1 到 5 张标注样本support set要求模型能对同一批新类的查询图片query set正确分类。这个场景最常见的形式是 5-way 1-shot、5-way 5-shot也就是 5 个类别每类 1 张或 5 张支持图。传统思路是迁移学习用基类把骨干网络训好新任务来的时候把最后的全连接分类头换掉用新类样本去微调。问题在于新类样本太少一个 N 类分类器输出维度虽然不大但每个类只有一两张样本全连接层的权重根本拟合不稳基本都是过拟合收场。所以当时的主流解法分成两派一派用距离度量代替分类器另一派用元学习学一个“好初始值”。度量派的问题在于决策边界被特征空间的分布限制住可解释性强但上限不高元学习派的代表 MAML 思路灵活但测试时需要迭代梯度推理慢训练也容易不稳定。这篇论文其实踩在两者中间保留一个可学习的线性分类器但是不靠梯度去解权重而是靠另一个网络“算”出权重。它把“生成分类器”这件事本身变成了一个可以学习的映射。1.2 核心立意把参数当输出把激活当输入假设骨干网络是 f_θ输入一张图片 x 经过骨干网络后得到特征向量 f_θ(x)。对于新任务里的某个类 k它的支持集是若干张图我们把这几张图的特征取平均得到一个类级别的激活向量 a_k这个向量的含义是“这个类在特征空间里的代表点”。传统原型网络会拿它直接做距离度量而这篇论文的做法是把 a_k 喂给一个小网络 P_φ让它输出类 k 在最终线性分类器里的权重向量 w_k。形式化一点就是w_k P_φ(a_k)。所有类别共享同一个参数预测器 P_φ。得到每个类的权重之后查询图片的特征直接和这些权重做内积得到 logits再过一个 softmax 就是分类结果。这个设计最聪明的地方在于它把“支持集信息如何转化为判别边界”这件事从手工设计变成了数据驱动。距离度量方法相当于人为规定“用原型点的邻近度做决策”这个约束太强而参数预测方法只规定“权重是特征的函数”函数具体长什么样由训练数据说了算所以它理论上能表达比余弦距离、欧氏距离更复杂的决策规则。1.3 和主流方法放在一起对比差异一目了然我用一张表把当时几类代表性方法的差异列出来方便理解这篇论文的位置。方法类型代表思路测试时是否要梯度更新分类器形式主要瓶颈常规微调预训练骨干 新头微调是线性分类器样本太少过拟合严重匹配网络特征嵌入 注意力加权否非参数最近邻决策规则受限原型网络类均值作为原型否距离度量只能表达线性/球状边界MAML学一个好的初始化是梯度更新后的分类器推理慢二阶导数训练难本文方法从激活预测参数否动态生成的线性分类器预测器本身需要精心训练从这个对比能看出本文的定位很清晰它保留了线性分类器的判别力又避免了测试时迭代推理速度接近原型网络表达能力却向微调方法看齐。对一个要部署到在线服务里的系统来说“拿到几张样本、一次前向就生成分类器”这个特性非常实用。2. 方法细节解析参数预测器的结构设计与那些容易忽略的数学细节2.1 整体数据流从支持集到分类器权重的完整链路我把整个流程拆成分步描述复现的时候照着这个顺序写代码就行。第一步采样一个任务episode。从基类里随机选 N 个类每类抽 K 张支持图、Q 张查询图。第二步把支持图和查询图都送进骨干网络取骨干网络输出的特征。注意这里一般要做全局池化得到一个向量而不是保留空间位置的 feature map因为最终分类器权重是一个向量形状的东西。第三步对每个类把该类 K 张支持图的特征逐元素取平均得到 a_k。第四步把 a_k 输入参数预测器 P_φ输出该类对应的分类器权重 w_k。第五步计算查询图片特征与所有 w_k 的内积拼接成 logits算交叉熵损失。整个链路里骨干网络和参数预测器是一起端到端训练的。也就是说特征提取的方向也会被“要方便生成分类器权重”这个目标反向塑造。这一点很重要如果你先固定骨干网络只训练预测器效果会差不少因为基类训出来的特征不一定适合被预测器映射成判别权重。2.2 零均值约束一个降低输出维度的关键数学细节最初我以为预测器就是简单地把 D 维特征映射到 D 维权重后来读细节才发现有一个很精巧的预处理对预测出来的权重矩阵做零均值约束。假设最终分类器要处理 N 个类权重矩阵是 W形状是 N×D其中第 k 行是 w_k。论文要求每一行的均值都为零也就是 1ᵀw_k 0。为什么这样做好处大一个线性分类器的输出是 w_kᵀf(x) b_k。如果 w_k 的均值不为零那么 w_k 可以拆成一个常数向量 c 加上一个零均值向量其中常数部分对每个类的贡献是相同的本质上可以被偏置项 b_k 吸收。如果不加约束预测器就要在 D 维空间里预测权重加了约束真正需要预测的自由度变成 D-1 维。少一维听起来不多但对于高维权重来说输出空间的体积直接缩小了预测器要搜索的范围变小很多训练稳定性和泛化能力都会提升。实现的时候有两种做法软约束是在损失函数里加一项权重均值的惩罚项硬约束是在预测器输出之后直接减去每行的均值。论文走的是硬约束路线我复现时也推荐硬约束简单且可控。如果你看到别人复现代码里输出层后面跟了一段“减均值”的操作就是这个原因。2.3 参数预测器的结构选择从单层线性到残差加深一个很自然的疑问是参数预测器 P_φ 应该做多复杂论文的实验结论很有启发性哪怕只有一个不带偏置的全连接层也就是 w_k W_map · a_k在这个框架下就已经能取得不错的效果。这说明“激活到权重”的映射确实存在一个近似线性的核心规律。但只用单层线性映射表达能力有限分类边界本质上还是被限制在特征空间的线性划分上。论文尝试把预测器加深发现性能会持续提升。我复现时采用的方案是先一层线性映射把激活向量投影到中间维度然后接 BatchNorm 和 ReLU再叠加几个残差块最后输出 D 维权重向量。残差结构在这里不是强行堆深度而是有实际意义——如果预测器的目标函数接近恒等映射加一个小扰动残差结构能让网络先学会“什么都不做”再慢慢学“需要调整的部分”训练稳定得多。还有一点值得注意预测器是逐类独立作用的也就是说它输入一个类的激活向量输出这个类的权重每个类的处理完全共享同一套参数。这样做的好处是测试时类的数量可以任意变化训练时用 5-way测试时如果变成 10-way 甚至 20-way直接把每个类的激活向量分别过一遍预测器就行不需要重新设计网络。这一点对实际部署太重要了。2.4 训练方式Episode 采样、损失函数与端到端更新训练流程是标准的 few-shot episodic training。每个 episode 里从基类训练集中随机抽出 N 个类作为“假装的新类”每类抽 K 张支持图、Q 张查询图。这个“假装是少样本任务”的设计是整个元学习范式的核心训练的时候模型经历的分布和测试时一致它才知道该怎么应对“类别没见过、样本非常少”的情况。损失函数就是查询集上的交叉熵。和普通分类不同这里的分类头不是固定参数而是由当前 episode 的支持集会动态生成的。因此每一轮迭代都会走一个完整路径支持集提取特征 → 平均成激活 → 预测器生成权重 → 查询集打分 → 算损失 → 反向传播。梯度会同时更新骨干网络和预测器。我在复现时有一个小发现先把骨干网络在基类上用标准分类任务预训练一个阶段再进入 episodic 训练收敛速度会明显更快最终精度也更好。如果从头直接做 episodic 训练也不是不行但要花更多时间让骨干网络从零学会一个“可被预测器利用”的特征空间。预训练相当于给了特征提取一个很好的起点这个技巧在很多 few-shot 方法里都通用。3. 实操过程与复现要点从数据集到超参数的一次完整落地3.1 数据集选择与划分策略复现这篇论文最常用的基准是 miniImageNet一共 100 个类每类 600 张图通常按 64/16/20 切分成训练、验证、测试三部分。切分的原则是训练和测试的类别完全不相交这样才能模拟“新类没见过”的真实场景。另一个常用基准是 Omniglot手写字符数据集类别数量多样本数少适合做 20-way 的比较极端设置。我个人的建议是先用 Omniglot 跑通流程再上 miniImageNet。Omniglot 数据集小、训练快几分钟就能看到结果适合验证代码有没有写对miniImageNet 才真正考验方法能力。如果追求更强的对比公平性还可以用 tieredImageNet它的类别按超类划分训练和测试的语义差异更大难度也更高但这也是后话了。3.2 骨干网络与图像预处理骨干网络我用过 ResNet-50 和 ResNet-12。ResNet-50 上限更高但对机器要求高训练慢ResNet-12 参数量小收敛快在 few-shot 社区里更常见。输入尺寸常见做法是 224×224或者干脆缩小到 84×84 来提升训练速度。预处理方面标准化用 ImageNet 的均值方差数据增强用随机裁剪、水平翻转和颜色抖动。这里有一个容易被忽视的问题支持集和查询集在测试阶段的预处理必须完全一致。我见过有人训练时给支持集做随机增强、查询集不做这会在训练和测试之间制造不一致。更合理的做法是训练时支持集和查询集都做同样强度的数据增强测试时都只用中心裁剪。增强的目的不是让模型看到更多噪声而是让它在少样本情况下对位置、光照变化更鲁棒。3.3 关键超参配置与训练策略下面这份配置是我复现时实际用起来比较稳的一套拿去做参考完全可以具体数值可以按机器性能微调。需要说明的是这不是论文原始实验的逐项复刻而是我在多次调试中验证过的一组合理参数。配置项推荐值说明骨干网络ResNet-12小模型、收敛快适合快速验证输入尺寸84×84训练加速few-shot 任务常见尺寸任务设置5-way 1-shot / 5-shot对应论文主要实验设置查询图数量 Q15 张/类保证每个 episode 损失有足够统计量优化器Adam初始学习率 0.001对预测器这种非标准结构更稳学习率调度每 30 轮衰减 0.1后期收敛更精细总训练轮数60~100miniImageNet 上一般够用预测器结构线性层 残差块 BN先用单层线性跑通再加深零均值约束输出后直接减行均值硬约束简单有效一个关键细节是 BatchNorm 的使用。base 网络里的 BN 在测试时要切到 eval 模式用滑动平均统计量预测器里的 BN 在训练时也不能依赖太小的 batch。少样本任务里一个 episode 的样本数本来就不多1-shot 时预测器一次只能看到一个类的 1 个激活向量如果 BN 的 batch 统计量不稳定训练会非常飘。我的做法是预测器里优先用 GroupNorm 或者 LayerNorm它们不受 batch 大小影响稳定性好很多。3.4 从零到一的伪代码参考我贴一段训练核心流程的伪代码用常见的深度学习框架语法写重点在于展示整体数据流。实际工程里还要加上数据加载、模型保存、日志等外围代码。def train_one_episode(base_net, predictor, opt): # 1. 随机采样一个任务N 个类每类 K 张支持图、Q 张查询图 classes sample(base_classes, N) # N 5 support, query sample_episode(classes, K, Q) # 2. 提取支持集特征并做全局池化得到向量 feat_s base_net(support) # 形状 [N*K, C, H, W] feat_s feat_s.mean(dim(-2, -1)) # 形状 [N*K, D] # 3. 每类的 K 张特征取平均得到类级别激活 act feat_s.view(N, K, -1).mean(dim1) # 形状 [N, D] # 4. 参数预测器从激活生成分类器权重 W predictor(act) # 形状 [N, D] # 5. 对权重做零均值约束 W W - W.mean(dim1, keepdimTrue) # 6. 对查询集打分 feat_q base_net(query).mean(dim(-2, -1)) # 形状 [N*Q, D] logits feat_q W.t() # 形状 [N*Q, N] # 7. 交叉熵损失反向传播 loss cross_entropy(logits, query_labels) loss.backward() opt.step() return loss这段代码看着简单但每一步都对应前面讲的某个设计决策。第 4 步的 predictor 就是核心第 5 步的零均值约束不是可选项是有理论依据的关键操作第 3 步取平均对应“类原型”的概念。整个 episode 里查询集的标签只在算损失时出现支持集只用来生成分类器两者角色完全正交。3.5 评估方式与评测陷阱评测时要在测试类的集合里随机采样大量 episode比如 600 个计算平均准确率和 95% 置信区间。测试时唯一的变化是去掉了基类预训练阶段常用的 augment 操作模型权重全部固定不再有任何梯度更新。评估协议是否严谨直接决定了结果能不能发文章采样 seed、episode 数量、支持集和查询集的划分都要固定并记录否则不同 run 之间的差异会盖过方法本身的差异。我提个容易被坑的点测试时的 n-way 可以跟训练不一致。如果训练用 5-way测试硬改成 20-way由于参数预测是逐类的网络结构上没问题但准确率会掉因为任务难度变了。对比论文结果时必须确认用的是同一套 way-shot 设置。4. 复现过程中的常见问题与排查技巧4.1 训练不收敛loss 来回震荡这个问题在参数预测类方法里尤其常见原因是预测器输出的权重范围很难控制。如果预测出来的 w_k 数值特别大查询特征和权重一内积logits 就会暴涨softmax 直接饱和梯度变成零模型怎么训都原地踏步。解决办法有两个思路第一给预测器输出做逐行归一化让每个类的权重模长被限制住第二在 logits 上乘一个可学习的缩放因子相当于学一个温度系数。我一开始没加这些训练 loss 一直下不去后来在 logits 上加了一个初始值很小的缩放问题立刻缓解。另一个稳定性的来源是初始化。预测器的最后一层参数不要用太大的标准差初始化否则第一轮生成的分类器权重分布就会离合理范围太远。复现时优先用小的随机初始化配合 BN 或 LayerNorm训练过程会平滑很多。4.2 性能退化成“原型网络”甚至还不如原型网络如果你发现深一点的预测器和单层线性映射效果一模一样那说明预测器并没有学到比“距离度量”更多的东西。检查方向有两个一是预测器是否被训练得足够充分可以把它从残差块换成更宽的结构或者加大训练 episode 数量二是零均值约束是否生效如果没有这个约束预测器很容易退化成把激活向量缩放一下再原样输出这等价于余弦相似度分类表达力有限。我在调参过程中还有过一个观察预测器太浅时最终结果大概率就是原型网络换了个马甲预测器加深后尤其在 5-shot 设置下性能才有肉眼可见的提升。原因是 5-shot 时每个类的激活向量更稳定映射网络有更多信息可以利用复杂的映射才能显露出优势。4.3 训练类别数和新任务类别数不一致如果你在实现时选择把 N 个类的激活向量拼接成一个超长向量再一起预测所有权重那么训练时的 N 就必须固定测试时换一个 way 就会维度对不上。这种“全局拼接”的做法在论文讨论里出现过但工程上太脆。我更推荐逐类独立预测每个类走同一个映射函数N 是多少完全无所谓。另一个常见问题是训练时虽然用了 5-way但每个批量里不同 episode 的类别组合差异很大导致预测器震荡。这个可以通过适当增大每个 batch 的 episode 数量来缓解统计梯度更稳。4.4 BatchNorm 带来的隐藏陷阱BatchNorm 在普通分类里是标配但在少样本任务里处处是坑。第一个坑在前面提过1-shot 时base 网络输出的特征其实是“一张图的特征”逐类取平均只是把单张特征复制了一份BN 的统计量在这么少的样本上全是噪声。我的建议是骨干网络里如果用了 BN训练早期先固定住 running stats或者干脆在 backbone 里换用 GroupNorm。第二个坑更隐蔽如果训练时 backbone 开着 BN 的 train 模式那么 query 特征的统计量也会参与更新测试时 switch 到 eval 模式特征分布会发生偏移。经验是训练后期把 backbone 的 BN 固定或者全程用 eval 模式的 BN让特征提取器保持稳定只让预测器去适应。4.5 一条经过验证的复现路径踩过这些坑之后我总结出一条适合从零复现这篇论文的路径第一步只用一个线性映射作为预测器目标是把整个流程跑通先看到 5-way 1-shot 有个不那么离谱的准确率第二步加上零均值约束确认效果有提升第三步把单层线性换成残差块结构逐步加深每一步记录准确率变化第四步调 BN 相关细节把评估协议的置信区间跑出来。这样每一步都可以验证出了问题也知道是哪个环节导致的比一把梭哈到完整结构好得多。5. 扩展思考从这篇论文还能延伸出什么5.1 参数预测的本质是 Hypernetwork 思想往大了说这篇论文是超网络hypernetwork思想在少样本领域的一次漂亮落地。超网络的概念是训练一个小网络去生成另一个大网络的参数在生成模型、压缩和动态推理里都有应用。这里预测器生成的是分类头那几层参数规模不大但这个思路一旦打开后续可以做很多事情比如不只预测分类层还可以预测骨干网络的部分权重不只从激活预测还可以结合任务描述文本、类别语义等多种信息来预测。这种“用网络生成网络”的路径在少样本场景里尤其有价值因为传统梯度更新对样本量极其敏感而生成式预测天然不依赖测试样本数量只要训练阶段见过足够多的任务分布推理时就能快速输出合理的参数。5.2 可以尝试的改进方向如果基于这篇论文做改进我觉得下面几个方向都值得一试。首先是特征层的选择论文主要用骨干网络最后的池化特征但不同层级的特征其实对应不同粒度的视觉信息可以把多层特征拼接起来再送进预测器增加鲁棒性。其次是支持集信息的利用方式简单平均会丢掉类内分布的信息使用注意力机制或者可学习的聚合方式可以缓解这个问题尤其对 5-shot 场景。第三是和偏置参数的预测结合起来我上面提过偏置可以置零但如果有足够的训练数据把偏置也预测出来通常能再提一点精度。最后是置信度与不确定性估计预测器输出的权重如果能带上不确定性对于一个新类只有 1 张样本的情况系统就可以知道自己的决策有多可靠。我在实际复现中的体会是这篇论文最值得学习的不只是那个结构而是“分类器参数是一个可学习映射的输出”这一思维方式。先想清楚函数关系是什么再设计网络去拟合这个关系而不是一上来就套一个相似度度量或者优化套路。另外也提醒一句少样本实验特别容易因为随机 seed 得出误导性的结论所有消融都要在同一组 seed 上比较结论才站得住脚。如果你也想动手复现我的建议是从最小配置跑起把每一步的预期结果都想清楚再逐步加复杂度这条路走下来会比直接抄完整网络收获多得多。