
小样本学习Few-shot Learning是我这两年投入时间最多的方向之一。坦白讲刚开始看到这个名字时我以为只是“数据少的时候训练一个分类器”等到真正去读综述、复现论文才发现里面有完全不同的任务设定、眼花缭乱的方法以及大量被论文引言轻描淡写带过的评测陷阱。这篇文章是我自己阅读多篇小样本学习综述论文后的系统性总结也是一份从“看懂概念”到“落地复现”的个人路线图。适合谁看刚入门小样本学习的研究生、正在做技术方案调研的工程师以及所有不想被论文堆吓跑的朋友。读完你至少能搞清楚这个领域到底在解决什么问题方法之间有什么本质差别为什么同一张表里不同论文的精度不能直接比以及复现时最容易在哪个环节翻车。1. 先搞懂小样本学习在解决什么再看综述不迷路1.1 N-way K-shot 到底是什么小样本学习最常见的任务定义用“N-way K-shot”描述。举个例子5-way 5-shot 表示每个任务里有 5 个类别每个类别只给 5 张标注样本作为参考模型要根据这 25 张图去判断查询集里一堆新图片分别属于哪一类。对人类来说这并不难孩子见过一次“猫”就能建立起概念但深度学习模型天生依赖大量标注数据少样本场景下很容易只记住训练时的类别换一批新类就失灵。我一开始以为这个问题就是把普通分类任务的数据量调小后来才发现完全不是一回事。在普通分类里训练集和测试集的类别是同一个集合模型只要学会区分见过的类即可。小样本学习测试时遇到的是训练阶段从未见过的类别所以它真正要学的是“能够快速适应新类别”的通用能力。这也是为什么所有综述都会反复强调不要把它当作小数据分类问题而要当作一个泛化问题来看。1.2 为什么绕不开元学习读综述时你会发现小样本学习和元学习几乎是绑定的。元学习的核心思想是“学会学习”不是只学一个固定的分类器而是学一套适应规则让模型在见到新任务时能快速自我调整。具体做法是 episodic training也叫任务式训练。每次迭代时从训练集的类别池里随机抽出 N 个类别每个类别取 K 个样本作为支持集support set再取若干样本作为查询集query set组成一个虚拟任务。模型在这个任务上计算损失、更新参数。跑很多个这样的任务之后模型就“见过”大量“只有几个样本的情况”新任务到来时不至于手足无措。我当初总觉得元学习是在绕远路但后来亲手做了对比实验才明白如果不用任务式训练只是在小数据上微调一个预训练模型模型很快会把已有类别的偏好固化住遇到不认识的类时基本是在瞎猜。任务式训练相当于逼着模型在每次迭代都面临一次“新环境”这种压力才是小样本泛化能力的来源。2. 综述里的方法地图四条主流路线与一条暗线2.1 基于度量的方法让“像不像”变成学习目标基于度量的方法是我个人最喜欢的一类思路足够直观把样本都映射到一个特征空间里然后用距离来衡量相似度。查询样本离哪个支持样本或类原型更近就判成哪个类。整个问题从“分类”变成了“比较像不像”。经典路线包括孪生网络Siamese Networks双塔结构两个输入共享网络权重输出一个相似性分数训练目标是让同类别样本对得分高、不同类别样本对得分低。思路朴素但奠定了“比较式”思维的基础。匹配网络Matching Networks提出用注意力机制计算查询样本与每个支持样本的匹配程度再按类别汇总得到预测分布。这篇论文也是 episodic training 范式的重要推手。原型网络Prototypical Networks每个类别不再保留单一样本而是把支持集里该类所有样本的特征取平均得到一个“类原型”。查询样本与各类原型的欧氏距离经过 softmax 后变成类别概率。公式简洁、实现容易、效果不差所以我通常建议新手从这里入手复现。关系网络Relation Networks不固定距离函数而是把查询特征和候选特征拼在一起让一个小网络去判定“相关程度有多高”。表达能力更强但训练量和过拟合风险也更大。选型体会度量方法在特征空间清晰、训练过程比较平顺推理时只需一次前向计算工程上很友好。缺点是它对特征质量要求很高如果 backbone 学出的嵌入本身分不开不同类别再好的距离函数也没用。2.2 基于优化的方法学习一个“更容易适应”的初始点第二类方法不改变模型结构而是改变模型参数的更新方式。它的目标是找到一个“好的初始参数”让模型面对新任务时只需几步梯度下降就能收敛到不错的结果。代表性工作就是 MAMLModel-Agnostic Meta-Learning。它的训练分两层内层对每个任务做一次或几次普通梯度更新得到临时参数外层基于所有任务的临时表现去计算初始参数应该怎么调。这个外层更新涉及经过梯度更新后的损失对初始参数的求导也就是二阶导数所以实现起来又复杂又吃显存。Reptile 通过直接比较初始参数与任务更新后的参数来近似这个更新过程省去了二阶求导速度上友好很多。Meta-SGD 则更进一步不光学初始化参数还学习每一步更新的方向和步长。这类方法的优势是通用性强不只能做图像分类还能迁移到回归、强化学习等场景。代价是训练不稳定超参数很敏感尤其是内层学习率和外层学习率的比例。我在复现 MAML 时最深的感受是论文看着漂亮但工程落地需要反复调参没有度量方法那么随手。2.3 基于模型结构与数据增强的路线还有一类方法从结构和数据两个角度做文章。基于模型的方法比如记忆增强网络Memory-Augmented Neural Networks给网络增加一个可读写的外部记忆模块遇到新类别时把支持集样本写进记忆查询时再从记忆里检索相关信息。这种设计在处理“类别持续增加”的场景时很自然但在标准小样本分类中实现成本偏高我在复盘时认为性价比一般。数据增强路线则更贴近直觉既然样本少那就想办法造样本。简单做法包括随机裁剪、旋转、颜色扰动、Mixup 混合等进阶做法是用生成模型或图像翻译模型合成新的支持样本。增强确实能缓解支持集稀缺的问题尤其是对姿态、光照变化很大的数据集。但它的风险也很明显若是增强方式改变了类别语义比如把一张“轮胎裂纹”图片旋转后变成“背景纹理”模型就会学到错误模式。用生成模型造样本时还要警惕生成分布偏差生成的图像与真实分布差得太远反而拉低测试精度。2.4 被很多综述单独拎出来的暗线预训练基线过去几年有一条容易被新手忽略的暗线预训练基线的强势回归。以论文A Closer Look at Few-shot Classification为代表的工作发现一个普通的特征提取器在大规模数据集上预训练然后冻结特征、只训练一个线性分类头或者做简单的微调其精度经常超过复杂的元学习模型。这个结论当时让不少人震惊因为它动摇了“元学习必须复杂”的假设。我自己复现时也验证过这一点先把 ResNet 在基类数据上预训练好再只学一个分类头miniImageNet 上的 5-way 5-shot 精度就已经能摸到许多度量学习方法的水平。这给我的启示是对比方法前必须把 baseline 做得足够扎实否则你兴冲冲提的新方案本质上只是在弥补 baseline 的缺陷。近期预训练大模型、CLIP 这样的视觉语言模型兴起后少样本适配更多转向了 prompt、adapter 这类轻量微调方案几乎都是在“预训练特征 少量参数适配”这条暗线上往前走。路线核心思想代表工作优点适合场景基于度量在特征空间中比较相似度原型网络、匹配网络简单直观、推理快类别语义清晰、特征可分性好基于优化学习更好的初始参数MAML、Reptile通用性强、可跨任务任务间差异大、需要快速适应基于模型/记忆用外部记忆存储与检索样本记忆增强网络支持持续学习类别动态增加的场景数据增强合成或变换出更多样本Mixup、生成模型落地成本低样本过少且增强不破坏语义预训练基线大规模预训练少量适配线性探针、微调简单稳定、效果好有充足基类数据或预训练模型3. 从综述到复现评测协议和参数选择才是关键3.1 数据集划分与信息泄漏读综述时如果只看方法不看评测协议很容易被数字误导。小样本学习的常用数据集里miniImageNet 是 100 类、每类 600 张图按照 64/16/20 划分训练、验证、测试类别tieredImageNet 则按超类划分保证三个集合在语义上更不相干难度更高CUB-200 这种鸟类细粒度数据集则常用于测试跨领域泛化能力。我强调“划分”的原因是信息泄漏问题。如果基类与新类在语义上高度相似或者图像本身有重复模型在基类上看到的模式可以直接套用到新类上测试精度就会虚高。有些论文为了刷分会刻意选择容易的划分或使用验证集类别做反复调优这在综述里通常不会写明白。因此我养成了一个习惯先看数据集构建和划分方式再看实验结果最后才看方法细节。3.2 一个标准 episodic 训练循环许多综述会配一张任务构建示意图但实际复现时还是容易在数据加载上出错。这里给出一段我常用的原型网络训练伪代码基于的是该领域最常见实践大家在自己代码里可以对照调整。for epoch in range(max_epochs): # 从训练集中随机采样 N 个类别每类 K 个支持样本 Q 个查询样本 support_x, support_y, query_x, query_y sample_episode( train_set, N5, K5, Q15 ) # 共享权重的特征提取器分别对支持和查询样本编码 support_emb model(support_x) # shape: [N*K, d] query_emb model(query_x) # shape: [N*Q, d] # 原型网络计算每个类别的原型类内特征均值 prototype [] for cls in range(N): mask support_y cls proto support_emb[mask].mean(dim0) prototype.append(proto) prototype torch.stack(prototype) # shape: [N, d] # 用欧氏距离得到查询样本属于每个类的概率 dist torch.cdist(query_emb, prototype) logits -dist loss F.cross_entropy(logits, query_y) optimizer.zero_grad() loss.backward() optimizer.step()这段逻辑里最容易出错的是标签映射。因为每个 episode 的类别是临时抽出来的类别编号需要重新从 0 到 N-1 编码不能用原始数据集的全局标签。以前我就吃过这个亏模型训练时 loss 明明在降测试准确率却一直停在 1/N 附近查了半天发现是标签错位。测试阶段同样重要。在 novel 类上构建固定数量的 episode比如 600 个或 1000 个每个 episode 也包含支持集和查询集最终报告平均准确率和 95% 置信区间。注意测试时不能再更新模型参数除非你用的是 transductive 设置可以借助查询集本身的信息做特征调整但那样就和 inductive 设置的结果不能直接对比了。3.3 常见超参与 Backbone 选择关于 backboneminiImageNet 上早期论文常用 4 层卷积网络参数量小、训练快适合验证想法。稍微复杂一点的数据集上ResNet12 或 ResNet18 是更常见的选择。我的经验是不要一味堆深度小样本场景下大模型容易过拟合如果预训练数据不够多ResNet18 和 ResNet50 的差距没有想象中那么大。优化器方面SGD 加动量在元学习里表现往往比 Adam 更稳但 Adam 的调参成本低。学习率可以从 0.001 到 0.01 之间尝试配合余弦退火效果更好。训练时每个 epoch 内采多少个 episode 也值得关注我一般让每个 epoch 采样几百个 task训练 500 到 1000 个 epoch具体还要根据训练集大小和损失下降曲线调整。查询集大小 Q 常用 15 或 16太小会让梯度噪声偏大太大则增加显存压力。4. 复现小样本学习项目时我最常踩的坑4.1 训练不收敛或效果很差的排查小样本学习项目调试起来比普通分类麻烦因为“任务”是动态采样的问题不一定出在模型上。我列几个最常见的现象和排查思路。support loss 很低query loss 很高说明模型过度记住了支持集泛化到查询集时失效。试着降低模型容量、增加数据增强、调低训练任务内迭代次数或者增加任务数量。准确率一直维持在 1/N大概率是代码逻辑问题优先检查 episode 的标签映射、支持集和查询集是否重叠、损失函数是否把维度传错。训练 loss 震荡很厉害先降低学习率再看 episode 采样是否引入了类别不均衡。如果采样函数有 bug某些类别的出现频率过高模型就会产生偏好。显存溢出MAML 这类二阶优化方法最明显。可以调小 batch 大小、减少内层更新步数或者换用一阶近似实现。4.2 复现结果与论文对不上的原因复现精度差一两个点是很常见的但如果差出五六个点通常不是模型问题而是评测设置不一致。我整理了一个排查表供大家对照。常见原因现象解决办法图像预处理不一致比论文低很多核对输入尺寸、归一化均值标准差、裁剪方式随机种子差异每次结果波动大固定种子跑多个种子取平均报告置信区间查询集数量不同精度趋势不同统一 Q 的大小至少对齐到论文设置Backbone 初始化不同certain方法相差大使用相同的预训练权重或同样的随机初始化方式测试 episode 数量过少结果不可靠至少 600 个 episode有条件就上 2000 个transductive 与 inductive 混淆数字虚高明确是否使用了查询集信息在论文和表格中标注清楚我在评估时踩过最深的一个坑是为了省时间测试时只用 100 个 episode结果两次实验之间相差 5 个百分点根本分不清是方法改进还是随机波动。后来改成 1000 个 episode对比才变得有意义。4.3 业务数据上的血泪经验标准 benchmark 上的方法搬到真实业务数据时往往会打不少折扣。我之前做过一个工业缺陷检测项目缺陷类别的样本只有三十来张与正常样本数量完全不成比例。第一版方案直接在原图上跑原型网络结果 F1 值惨不忍睹。分析之后发现问题不在算法本身而在域差异公开数据集里的图像是中心目标、背景干净而产线图片背景杂乱、目标尺度不固定、拍摄角度千奇百怪。模型在预训练和基类上学到的特征根本无法直接迁移到这种图像分布上。后来我做了三件事一是把图像先做目标检测裁剪和对齐去掉无关背景二是用一个更大的通用预训练模型提取嵌入特征冻结 backbone 只在顶层做原型计算三是引入了一些无标注产线图像做无监督域适配让特征空间更贴近真实分布。这三步做完F1 从不到 0.3 提到了 0.6 以上。结论很简单小样本方法不是“开箱即用”的魔法特征分布对齐才是业务落地真正的胜负手。5. 小样本学习选型建议从论文到项目落地5.1 不同场景怎么选方法结合综述里的方法分类和我自己的项目经验我整理了一套比较实用的选型思路很建议大家先按这套方法做初步筛选再针对自己的数据做小规模消融。类别语义清晰、特征空间好分直接从原型网络开始它是效果与复杂度之间最平衡的选项。任务之间的差异非常大比如训练任务与测试任务来自完全不同的领域优先试 MAML 这类基于优化的方法。每类样本只有几张且数据增强不会破坏语义先做增强加预训练特征简单便宜效果提升很直接。业务系统里已经有现成的大模型考虑线性探针、prompt 或 adapter 这类轻量适配不要从零训练一个小模型。新类别会不断出现、系统需要持续上新记忆增强或带动态类别扩展的度量方法更合适因为每来一类只需更新原型不需要重训整个网络。5.2 工程落地时还需要考虑的约束技术指标之外工程约束常常决定方法选型。推理速度方面基于度量的方法只需要一次前向计算最容易满足线上实时性要求MAML 系列在新任务上仍需要几步梯度更新更适合离线批处理或低频更新场景。显存和训练成本方面二阶优化的代价很高如果团队只有单卡 GPU我建议慎选。可解释性是另一个容易被忽略的点。生产环境里算法输出需要能被复核原型网络天然可以把每个类别的原型和具体样本导出业务方能够直观看到“这张查询图为什么被分到这个类”而 MAML 学习到的初始化参数则很难向非技术人员解释。我在项目复盘时发现算法能否上线有时候并不取决于精度高的那一个点而取决于出了问题之后能否定位和解释。6. 读完这些综述后我留在手上的几个判断先说方法之外的第一条心得读任何一篇小样本学习综述先用半小时把评测协议彻底吃透再去看方法。评测协议决定了你看到的实验结果是否可信也决定了两个方法之间能不能放在一起比。我的第二个判断是小样本学习的本质不是某个精巧的模块而是跨任务先验的习得方式。只要抓住“如何从有限样本中提炼可泛化规律”这个命题再去理解度量、优化、记忆、增强这些流派思路就会特别清楚。最后分享一个我一直在用的工作习惯把任何一篇综述都按“问题定义、评测协议、方法分类、复现验证、应用限制”五步来读。这个方法让我从读论文容易陷入细节的毛病里解脱出来也建议你们试试。以我经验来说小样本学习这个领域真正有价值的工作往往不是把精度刷高一个点而是把评测或者问题定义往前推一步这也是我在读综述和做项目时最想抓住的东西。