☰
从激活值预测分类器参数:少样本图像识别的生成式解法
2026/10/14 15:06:41 网站建设 项目流程

被一组只有五张图的新类别困住的时候,大多数模型的反应是迷茫。少样本图像识别(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(dim=0) # 对该类所有支持样本激活求和 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-shot
MAML 类方法(当时典型结果)约 48.7约 63.1
ProtoNet(当时典型结果)约 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 如果要在这个方向上继续做,可以往哪走

我自己的体会是,这篇论文最值得抄的不是某个模块,而是“分类器参数本身可以是条件生成的结果”这个思路。顺着这个思路,可以做的扩展很多:把逐类独立的预测器换成带注意力机制的结构,让类别间能看到彼此的激活;或者把预测和微调结合成一个可插拔的组件;再或者把激活值做域自适应后再进预测器,缓解分布漂移。

如果你卡在一个少样本识别项目上,不妨先想想:能不能用一个小网络,直接从支持集生成一个新分类器,而不是去调一堆优化超参。这个角度看似简单,却往往能带来比堆模块更干净的收益。

提示:论文中的数值与结论均基于原实验条件,不同复现环境下会有浮动。实现时建议以官方代码为基础,自己跑通一遍再做改动,这样对“预测参数”这类方法才算真正有体感。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询