
被一组只有五张图的新类别困住的时候大多数模型的反应是迷茫。少样本图像识别few-shot image recognition要解决的就是在这样苛刻的条件下让模型尽量快地进入状态。我今天想聊的这篇论文标题起得很直白——《Few-Shot Image Recognition by Predicting Parameters from Activations》。它给出的解决方案是既然每个新任务只有少量样本那就让网络从这些样本的特征激活值里直接把分类器参数算出来。这篇论文的方法比较精巧——它不搞额外迭代优化也不靠距离度量走捷径而是训练一个参数预测器把支持集图片经过骨干网络得到的激活值转成新类别的线性分类权重。放在当时的背景下看这个思路有点“反直觉”但也正因如此后来不少工作都在不同程度上沿用了“从数据直接预测任务参数”这条主线。如果你是做小样本分类的研究生或者工程师正在 MAML、ProtoNet、Matching Network 之间挑主线或者想理解任务级参数是怎么从网络内部被“诱导”出来的这篇值得细读。下面我按“为什么这样做→怎么做→实验值得注意的地方→复现会遇到什么坑”的顺序来拆。1. 先弄清问题本质少样本识别的瓶颈到底卡在哪1.1 N-way K-shot 到底是什么问题少样本图像识别标准设定是 N-way K-shot训练阶段可以接触大量基础类别的数据但到评估阶段模型面对的是全新类别每个类只有 K 张带标签图片K 通常取 1 或 5。模型要在这样的条件下对同一批新类别中的查询样本做出正确分类。这个设定看起来只是“数据少了点”但难点远不止数据量。传统监督学习依赖大样本去估计类别分布少样本条件下任何统计估计都会严重偏差。常规迁移学习虽然可以让骨干网络学到通用特征但骨干网络顶层的分类器是按训练时的固定类别设计的一旦新类别出现分类器必须整个重建。换句话说前面的特征提取可以“迁移”最后一层分类头的权重却没法直接从旧任务里拿过来用。所以在少样本论文里反复出现一句话新任务最需要的是快速得到一个可靠的分类器。这个分类器要么通过几步梯度更新算出来要么通过某种方式直接“推出来”。这篇论文选择的是后者。1.2 两条主流路线一个共同的“最后一公里”在我读这篇论文之前少样本学习大致分两派。一派是优化型元学习代表是 MAML。它训练一个初始参数使得新任务到来时只需要少量样本上的几步梯度下降就能收敛。这个想法很美但实际用起来有两个痛点一是测试阶段要真的跑几步内循环优化算力开销和超参敏感度都不低二是当任务类别分布变化较大时初始参数未必能几步就落在好的区域。另一派是度量型元学习代表是 ProtoNet 和 Matching Network。这类方法不做分类器直接用特征距离或相似度做最近邻判断。度量学习的好处是快、稳而且天然适合任意类别数问题在于它本质上绕开了“分类”这个动作——当 K 稍大一点、特征内部有明显模态差异时一个简单的类原型或距离函数往往不如一个有判别边界的分类器好用。不管是求一个分类器还是绕开分类器都说明同一个事实少样本识别真正的“最后一公里”是如何在几乎没有监督信号的条件下构建对新类别有效的决策边界。这篇论文的出发点就是把这个边界当做一个可以“生成”的对象来处理。1.3 本文的切入角度把分类器参数当作“生成对象”论文的核心视角可以归纳成一句话分类器权重不再是训练出来的也不是优化出来的而是从一个条件信息里生成出来的。这个条件信息就是支持集图片经过骨干网络后产生的激活值。打个比方。你请一个经验老到的HR看一眼新团队的三个候选人他不需要逐个单独考察一眼扫过去就能把岗位要求、人员画像“脑补”出来。参数预测器在这里就扮演这个HR它观察支持集样本的激活值然后直接输出一组分类权重和偏置告诉模型“这三类应该这样分”。这个思路跟我之前看过的 fast weights 方向一脉相承但本文把它落到 CNN 场景中并且用一套干净的 end-to-end 训练方案验证了可行性。它不承诺在所有少样本设定下都碾压别人但至少证明分类器参数完全可以是“被预测出来”的而不必是“被优化出来”的。2. 参数预测器怎么做一次前向就得到新分类器2.1 为什么条件信息用激活值而不是原始图片从支持集图片到分类权重中间要经过一个条件信息的提取。论文选的是“激活值”而不是原始像素这背后有几层考量。第一原始像素的维度高、冗余大直接拿来做条件会导致预测器需要非常大的容量还容易被光照、背景等噪声干扰。激活值经过骨干网络抽象已经把语义信息压缩到相对紧凑的形式。第二骨干网络本身就是为分类任务训练的它的中间层激活天然携带判别性信息。用一个在基类上训练过的网络去提取激活相当于先用大量数据学了一个好的表示空间再在这个表示空间中做新类别的判别——这比从像素直接跳到权重要合理得多。第三激活值保留了空间结构不像全连接特征那样丢掉了位置信息。这对于后续的聚合操作是友好的我们可以用空间池化、卷积等操作继续处理而不用强行向量化。所以论文的流程可以概括为支持集图片 → 骨干网络 → 逐类聚合激活值 → 参数预测器 → 分类权重。2.2 激活聚合与逐类预测权重的设计这里有一个我个人很欣赏的设计细节参数预测器并不是一次性预测所有类别权重而是对每个类别单独预测。具体做法是对每个类别内部的支持集激活值做一个聚合操作得到一个“类表征”再把这个类表征送入一个共享的参数预测器输出该类对应的分类权重和偏置。这样做的好处非常直接类别数 N 可以自由变化。训练时用 5-way 还是 20-way测试时遇到 3-way 还是 10-way模型结构都无需改动因为权重是逐类生成的不依赖类别总数。聚合方式上论文讨论了求和池化、平均池化、最大池化等选择。默认推荐求和池化因为在 K-shot 场景下求和能保留每个样本的贡献不会因为样本数不同而让类表征幅度产生剧烈变化——当然这只是一种经验倾向实际问题里需要做消融验证。下面是我按照论文思路整理的一个极简伪代码def predict_classifier_from_support(support_imgs, support_labels, backbone, predictor): # support_imgs: [K * num_ways, C, H, W] feats backbone(support_imgs) # [K * num_ways, D, h, w] per_class_rep [] for c in range(num_ways): mask support_labels c pooled feats[mask].sum(dim0) # 对该类所有支持样本激活求和 per_class_rep.append(pooled) class_reps torch.stack(per_class_rep) # [num_ways, D, h, w] W, b predictor(class_reps) # 共享预测器逐类输出权重 # 对查询图片分类 query_feats backbone(query_imgs) # [num_queries, D, h, w] logits torch.einsum(qdhw,wdhw-qw, query_feats, W) b return logits从代码里能看到预测器是一个共享模块输入是聚合后的类激活输出是该类的线性层权重。在实际实现中预测器往往是一个小型卷积网络接全连接层最后输出和特征维度一致的权重向量。2.3 episode式训练用基类模拟少样本场景参数预测器不是凭空学会“预测权重”的它需要大量模拟任务来训练。论文采用标准的 episode 式训练每次迭代从训练集中随机抽取 N 个类别每类抽 K 张作为支持集再抽一部分作为查询集。接下来就是元学习的标准闭环支持集图片过骨干网络得到激活聚合后送入参数预测器得到当前模拟任务的分类器再用这个分类器对查询集样本计算交叉熵损失。反向传播时骨干网络和参数预测器会同时更新。这个训练过程和推理过程高度对称模型在训练时看到的任务形式和测试时完全一致这种一致性是元学习方法的关键。否则模型学会了在基类上直接分类却没有学会“如何适应一个新任务”那评估时就会露馅。需要说明的是论文实际训练中通常还会配合基类预训练。先用基类数据把骨干网络训练到一个比较好的初始化状态再做 episode 微调。我在实现同类方法时也发现跳过预训练直接跑 episode 训练收敛会慢很多而且最终精度往往差一截。2.4 推理阶段为什么能做到“一次前向”推理阶段没有任何梯度更新。支持集图片进入网络后经过聚合和预测器直接生成新任务的分类头然后对查询集做一次标准的前向分类。这意味着推理开销非常小支持集只需过一次骨干网络再做一次不算重的预测器前向。整个过程不需要像 MAML 那样反复计算梯度也不用为每个任务保留一套可微计算图。做工程的人会很喜欢这个特性因为少样本模型的部署场景往往对延迟敏感比如在线识别、边端分类。如果每个新任务到来都要跑几十步梯度更新产品上的代价是不可接受的。这篇论文用一次前向换掉了这个代价这是它实用价值的重要来源。2.5 它与 fast weights、LEO 这条线的血缘关系读这篇论文时最好把它放进一个更大的谱系里看。早期有一类想法叫 fast weights核心是用一个小型上下文编码器根据当前输入或任务信息直接产生网络层权重从而让网络快速适应新上下文。本文的做法在哲学上和它一致都是“权重由条件信息生成”。后来出现的 LEO 方法可以看作是这条思路的延续它先从支持集数据嵌入一个潜在编码再从潜在编码解码出分类器的初始参数之后再做少量梯度微调。换句话说LEO 把本文的“预测参数”作为起点再叠加优化过程来精修。理解这条血缘关系对复现或做后续改进都有帮助。如果你想把本文方法改得更强一个自然的尝试就是在预测出的权重之后再加几步适配优化如果你想让它更轻那纯预测路线就是极致。论文本身走的是纯净版但也给后人留了扩展空间。3. 论文报告的几个关键结果与我的解读3.1 实验基准与主干设置论文的主要实验在 miniImageNet 和 Omniglot 上进行。miniImageNet 是从 ImageNet 中抽出的子集包含 100 个类别每类 600 张图按 64/16/20 划分为训练、验证、测试类。少样本领域最常见的 5-way 1-shot 和 5-way 5-shot 设定就是在这个数据集上评估的。主干网络用的是当时少样本方法标配的 4 层卷积网络没有引入特别复杂的结构。这一点很重要因为它保证了对比的公平性——提升不是靠更大的骨干网络而是靠方法本身的设计。从论文报告的数字来看这套“预测参数”框架在当时的 benchmark 上带来了明显提升尤其是一阶少样本场景下的收益最为显著。3.2 miniImageNet 上的数值表现如果你看过当时这批工作应该会对下面的数字有印象。下面是基于论文报告数据整理的粗略对照方法5-way 1-shot5-way 5-shotMAML 类方法当时典型结果约 48.7约 63.1ProtoNet当时典型结果约 49.4约 68.2本文方法约 59.4约 73.1需要说明这些数字是论文作者在自有的复现环境下报告的不同代码版本之间会有细微浮动具体要用论文原表为准。但趋势是明确的1-shot 场景下从激活值预测参数这条路线比优化型和度量型方法都拉开了一个身位。我的解读是提升的核心并不是参数预测器本身有多复杂而是它让整个系统能够“端到端”地为一个目标服务生成好的任务分类器。相比之下MAML 的内循环优化限制了梯度的有效回传ProtoNet 则没有显式学习决策边界。本文用一个显式的分类器预测目标反而让特征提取器的训练信号更聚焦。3.3 Omniglot 上的结果与饱和现象Omniglot 是一个手写字符数据集类别多、类内简单识别难度远低于自然图像。在这个数据集上所有主流方法的表现都已经接近饱和论文报告的结果也达到了 98% 以上的水平20-way 设置下同样非常可观。饱和带来的问题就是“区分度不够”。当所有方法都撞到天花板时很难从数字上判断哪个设计更好。不过这也从侧面说明本文方法的有效性不是靠特定数据集上的偶然而是在简单和复杂两类基准上都站得住。3.4 消融研究里容易被忽略的信号论文对聚合方式、预测器结构、K-shot 扩展性都做了消融对比。几个值得关注的点聚合方式对比。不同聚合策略影响类表征的质量逐类求和带来相对稳定的结果但具体哪种最好其实和数据集、骨干网络深度有关。预测器的容量。预测器太小可能学不出有判别力的权重太大又会在少样本条件下过拟合。论文的结果显示一个中等容量的小网络已经够用再增大收益有限。K 值扩展。从 1-shot 到 5-shot预测器能够利用更多支持样本的聚合信息效果继续提升这说明它并不是只会“偷懒地”处理单样本而是能随着样本增多而获益。这些消融结论在我实际做实验时也有体感预测器网络不宜过深聚合方式需要单独验证K 值变化时要留意激活值的幅度是否出现漂移。4. 复现这类“预测参数”方法时我建议你留意的四个细节4.1 求和池化的尺度漂移复现时最容易翻车的是对求和池化的处理。1-shot 时每类只有一个特征图直接求和就是特征本身5-shot 时要把 5 张特征图叠加起来激活值的数值范围会比 1-shot 大出一截。如果参数预测器在前向时没有对输入幅度做归一化它学到的统计特征就会和 K 值强相关测试时 K 一旦变化预测出的权重就可能偏离正常范围。论文的实现里通常会对聚合结果做归一化或缩放处理自己写代码时需要格外注意这一点。有一个经验性的做法是聚合后对类表征做一个 L2 归一化或者除以 sqrt(K) 来控制幅值增长然后接一个可学习的缩放参数。这样既能保留聚合带来的信息增益又不会让网络被数值范围带偏。4.2 预训练与 episode 微调的顺序不能乱直接从一个随机初始化的骨干网络开始做 episode 训练理论上不是不行但实际收敛很慢而且容易停留在较差的局部最优。标准做法是先在基类数据上预训练骨干网络让特征提取器具备基本的语义判别能力再用 episode 方式微调参数预测器和骨干网络的尾部。还有一个小细节预训练时用的分类头在进入 episode 训练后通常会丢弃或替换不要让它残留到新的参数预测流程里。否则特征提取器会倾向于保留对旧类别友好的表示而不是对“任务自适应”友好的表示。4.3 预测器的容量够用就好别贪深我在做类似结构时一开始总觉得预测器网络越强输出权重越准结果加了两层后精度反而下降。原因是参数预测器本质上是在“从少量样本中归纳分类规则”网络过深后类表征的有限信息会被过度参数化的中间层消耗掉出现过拟合。论文中的预测器结构并不复杂通常就是几层卷积加自适应池化和全连接参数量远小于骨干网络。如果你复现时发现效果不理想可以先从预测器的宽度和深度做消融而不是急着换更大的骨干网络。4.4 预测权重之后还能不能继续优化论文本身不做测试时优化但这个方法有一个可贵的性质它预测出的权重是一个很好的“热启动”。也就是说你完全可以在预测出参数后再对支持集做几步梯度微调让分类器贴合当前任务更紧。这不算原论文的贡献但后续不少工作都证明这个组合是有效的。纯预测保证了快速微调保证了精细两者互补。我在自己的实验里也试过“预测 1 步 微调 3 步”的设定确实能在部分任务上继续涨点代价是推理延迟增加。要不要加微调取决于你的应用场景能不能接受这点延迟。5. 论文读完我记住的三件事与一点遗憾5.1 真正值得抄的三个设计如果要给这篇论文提炼方法论层面的经验我首先会记下“逐类预测参数”这个设计。它让网络摆脱了类别数的束缚无论是训练还是推理模型都天然支持任意 N-way这是很多方法做不到的。其次是“用激活值做条件信息”的选择。激活值是骨干网络对图像的语义抽象用它作为任务条件既保留了视觉细节又比原始像素更紧凑。这个经验可以迁移到其他任务级条件生成场景比如条件卷积、动态滤波器等。最后是“测试阶段无需优化”的取舍。在少样本模型要上线的工程环境里这个特性的价值不亚于精度提升。一次前向换任务自适应这种计算模式非常适合现代推理架构。5.2 它的局限也很明显首先是对分布偏移的敏感。参数预测器是从激活值到权重的映射这套映射依赖训练时的激活分布。如果测试数据来自一个和基类差异很大的域激活值分布漂移后预测出的权重可能完全不靠谱。其次是 1-shot 下的聚合信号太弱。单张图片的激活值只是一次观测里面可能混入背景、姿态等无关信息求和池化也无法消除这些噪声。这个时候度量学习里更精细的样本间关系建模反而有优势。第三个遗憾是参数预测器没有显式建模支持集内部的类别间关系。它逐类独立预测权重快速但粗暴。如果某个类别的支持样本恰好非常困难预测器不会知道去参考其他类别来调整策略。5.3 如果要在这个方向上继续做可以往哪走我自己的体会是这篇论文最值得抄的不是某个模块而是“分类器参数本身可以是条件生成的结果”这个思路。顺着这个思路可以做的扩展很多把逐类独立的预测器换成带注意力机制的结构让类别间能看到彼此的激活或者把预测和微调结合成一个可插拔的组件再或者把激活值做域自适应后再进预测器缓解分布漂移。如果你卡在一个少样本识别项目上不妨先想想能不能用一个小网络直接从支持集生成一个新分类器而不是去调一堆优化超参。这个角度看似简单却往往能带来比堆模块更干净的收益。提示论文中的数值与结论均基于原实验条件不同复现环境下会有浮动。实现时建议以官方代码为基础自己跑通一遍再做改动这样对“预测参数”这类方法才算真正有体感。