1. 为什么增量小样本目标检测值得单独写一篇笔记
目标检测这个方向,常规套路已经相当成熟了。你拿一个 backbone,接上检测头,准备一批标注数据,训上几十上百个 epoch,基本都能拿到一个能用的模型。但现实场景很少这么理想化。我接触过的几个实际需求里,几乎都绕不开两个问题:第一,新类别的数据永远只有那么几张,标注成本高得离谱;第二,模型不能每次来了新类别就从头训一遍,旧类别的数据可能因为隐私、存储或者授权问题根本拿不到。
这两个问题叠在一起,就是增量小样本目标检测(Incremental Few-Shot Object Detection,简称 iFSOD)要解决的核心矛盾。它要求模型在只见过少量新类别样本、且无法访问旧类别训练数据的前提下,持续地把新类别学进来,同时不能把旧类别忘掉。这个设定比单纯的小样本检测更难,因为小样本检测通常还允许你保留一个大的基类数据集做联合训练,而增量设定直接把这条路堵死了。
我读的这篇《Incremental Few-Shot Object Detection》就是冲着这个设定去的。它要回答的问题很具体:在基类训练完成之后,如何用极少量样本把新类"挂"到已有检测器上,而不破坏原有的特征空间和分类边界。适合读这篇笔记的人,我大致分三类:一是正在做检测落地、被新类别迭代折磨的工程同学;二是刚入门小样本学习、想找一个具体任务切入的研究新手;三是需要快速判断这篇方法能不能迁移到自己业务里的技术负责人。下面我按自己的理解把整篇论文拆开讲,重点放在设计动机、关键模块和实操时容易踩的坑上。
2. 问题设定与整体设计思路拆解
2.1 增量小样本检测到底难在哪
先把问题定义说清楚。整个训练分两个阶段。第一阶段叫基类训练,你有一个数据量充足的基类数据集,类别集合记为 C_base,正常训一个检测器出来。第二阶段叫增量小样本阶段,给你一批新类别 C_novel,每个类别只有 K 个标注样本(K 通常是 1、5、10 这种量级),而且基类的训练数据这时候拿不到了。目标是让模型在 C_base ∪ C_novel 上都能检测。
难点的本质是灾难性遗忘和数据稀缺的叠加。如果第二阶段直接拿新类样本去微调整个网络,参数量那么大,几个样本根本约束不住,网络会迅速过拟合到新类,旧类的检测性能断崖式下跌。反过来,如果你冻结大部分参数只调分类头,新类的特征又没被充分学习,检测框回归也会受影响。更麻烦的是,检测任务不像分类,它同时要输出类别和位置,新类的定位能力也需要从少量样本里学出来。
论文里我印象最深的一个观察是:基类训练出来的特征空间其实已经相当通用,新类样本在这个空间里并不是完全不可分,问题主要出在分类器的决策边界和特征分布的偏移上。这个观察直接决定了后面方法的设计方向——不是去重新学特征,而是想办法在已有特征空间里做校准和适配。
2.2 整体框架的两阶段设计
论文的整体框架可以概括为"基类预训练 + 增量适配"两段式。基类阶段用标准检测流程训一个完整的检测器,包括 backbone、neck、RPN、ROI head 这些常规组件。增量阶段则冻结绝大部分参数,只对少数几个关键模块做调整。
为什么这么设计?我的理解是,检测器的 backbone 学到的是通用的视觉特征,边缘、纹理、部件这些底层信息对新类同样有效,没必要动。真正需要适配的是高层语义相关的部分,也就是分类分支和与类别强相关的特征变换。冻结底层、只调高层,既降低了参数量、缓解了小样本过拟合,又保留了基类的通用能力,一举两得。
具体到模块划分,论文把增量阶段的可学习部分集中在两个地方:一个是特征重加权或校准模块,用来缓解新旧类特征分布不一致;另一个是分类器的增量扩展,用来给新类腾出决策空间。这个划分不是拍脑袋定的,而是基于前面那个"特征空间已足够通用"的观察。我在复现类似思路时也验证过,如果连 backbone 一起微调,5-shot 设定下旧类 mAP 掉个十几二十个点是常事,冻结之后就稳很多。
2.3 与联合训练、微调两种朴素方案的对比
为了说明这个设计的必要性,论文对比了几种朴素方案。我把它们的核心差异整理成表,方便对照理解。
| 方案 | 旧类数据 | 新类数据 | 主要问题 |
|---|---|---|---|
| 联合训练 | 需要 | 需要 | 旧类数据往往拿不到,不现实 |
| 全量微调 | 不需要 | 需要 | 严重遗忘旧类,新类过拟合 |
| 仅调分类头 | 不需要 | 需要 | 新类特征学习不足,定位差 |
| 论文方法 | 不需要 | 需要 | 需要精心设计适配模块 |
从表里能看出来,论文方法在数据可用性上是最贴近真实场景的,代价是设计复杂度上去了。这个取舍我认为是合理的,因为工程上"拿不到旧数据"是硬约束,不是可以商量的选项。你不可能要求业务方为了模型迭代把历史数据一直留着,尤其是涉及第三方数据的时候。
3. 核心模块的细节解析与实操要点
3.1 特征校准:让新类样本落到该落的位置
增量阶段最直接的问题是新类样本的特征分布和基类不一致。基类训练时,网络见到的都是基类样本,特征空间的某些区域被基类占满了。新类样本进来,可能落在一些边缘或者空白区域,直接拿去做分类,决策边界会非常混乱。
论文用的思路是做特征校准,通俗讲就是给新类特征做一个变换,把它们"拉"到和基类特征可比的分布上。具体实现上,常见做法是学习一个轻量的变换模块,输入是 ROI 特征,输出是校准后的特征。这个模块参数量很小,几个样本就能训起来,不会过拟合。
这里有个实操要点:校准模块的输入特征从哪一层取很关键。取太浅,特征语义性不够,校准了也没用;取太深,特征已经高度类别相关,校准空间太小。我的经验是取 ROI head 里分类分支之前的那个特征比较合适,既有足够语义,又还没被分类器"污染"。论文里也是类似的位置,这一点在复现时要特别注意对齐。
注意:校准模块一定要加正则或者约束,否则几个样本训下来,它可能学出一个把所有特征都映射到同一个点的退化解,表面上 loss 很低,实际完全失效。常见做法是加一个恒等映射的约束,让校准后的特征不要偏离原始特征太远。
3.2 分类器的增量扩展与权重生成
分类器这块是 iFSOD 的另一个核心。基类分类器的权重是固定的,新类需要新的权重。最朴素的做法是给新类随机初始化几个权重向量,然后拿新类样本去训。但几个样本训一个随机初始化的向量,效果通常很差。
论文采用的是基于基类权重的生成式思路。核心假设是:新类的分类权重不应该凭空产生,而应该和基类权重有某种关联。比如新类"斑马"和基类"马"在特征空间里接近,那斑马的分类权重就可以从马的权重出发做调整得到。实现上,可以学一个映射函数,输入是基类权重或者基类特征的统计量,输出是新类权重。
这个思路的好处是,新类权重从一开始就落在一个合理的区域,不需要从零学起,几个样本就能微调到位。我在类似任务上试过,用基类权重做初始化比随机初始化,5-shot 下新类 AP 能高出好几个点,而且训练更稳定。
具体到权重生成的形式,论文里给的是一种带注意力的聚合方式,让新类权重自适应地从多个基类权重里"借"信息。这个设计比简单的平均或者最近邻要灵活,因为它不要求新类和某个基类一一对应,而是可以组合多个基类的信息。
3.3 损失函数的设计与平衡
增量阶段的损失函数需要同时照顾新类学习和旧类保持。论文的损失大致分三块:新类的分类损失、新类的回归损失,以及一个用来约束旧类性能不下降的正则项。
新类分类损失和常规检测一样,用交叉熵或者 focal loss。回归损失用 smooth L1 或者 IoU 系列。关键是那个正则项,它的作用是防止模型在学新类的时候把旧类的决策边界推得太远。常见做法是约束新旧类权重之间的某种距离,或者约束特征校准模块的输出不要偏离恒等映射太多。
这里有个参数平衡的问题。正则太强,新类学不进去;正则太弱,旧类掉点。论文里给了一个权重系数,但实际用的时候这个系数和你的基类数量、新类数量、shot 数都有关,不能照搬。我的建议是先在验证集上扫一遍,找到旧类掉点可接受、新类又能涨上去的平衡点。通常这个系数在 0.1 到 1 之间,具体看数据。
3.4 训练策略:冻结哪些、训练哪些
训练策略直接决定成败。论文的设定是冻结 backbone 和 RPN,只训练 ROI head 里的校准模块和分类权重生成模块。这个划分我基本认同,但有几个细节值得说。
第一,BN 层要不要冻结。如果基类训练时 BN 的统计量已经稳定,增量阶段最好冻结,否则几个样本会把统计量带偏。如果框架支持,用 eval 模式跑 BN 更稳。第二,ROI head 里的共享卷积要不要动。我的经验是前几层共享卷积冻结,只动最后和分类、回归直接相连的部分。第三,学习率要设得比基类训练小一个量级左右,因为现在是在一个已经很好的解附近做微调,步子大了容易崩。
提示:增量阶段建议用较小的 batch size,因为新类样本本来就少。但 batch 太小梯度噪声大,可以配合梯度累积。另外,每个 batch 里最好保证新旧类样本都有,哪怕旧类样本是从基类里留出来的少量样本,也比纯新类 batch 稳。
4. 完整实操流程与关键环节实现
4.1 基类训练阶段的准备
基类训练是整个流程的地基,这一步没做好,后面增量怎么调都白搭。基类数据集要尽量覆盖多样的类别和场景,类别数越多、类间差异越大,学到的特征空间越通用,后面新类挂上去越容易。
训练配置上,用标准的检测训练流程就行,backbone 用预训练权重初始化,训到收敛。这里有个容易被忽略的点:基类训练时最好留出一部分类别不参与训练,专门用来模拟增量阶段的新类。这样你才能在没有真实新类数据的时候,先验证整套增量流程是否 work。论文里的实验也是这么做的,把数据集按类别划分成基类和新类两组。
基类训练完成后,建议把 backbone 和 RPN 的参数冻住存一份,增量阶段直接用。同时把基类分类器的权重也存下来,后面生成新类权重要用。这些中间产物管理好,复现的时候能省很多事。
4.2 增量阶段的数据组织
增量阶段的数据组织有几个讲究。每个新类 K 个样本,这 K 个样本怎么选很关键。如果随机选,可能选到一些质量差的样本,影响很大。实际操作中,如果条件允许,尽量选那些目标清晰、遮挡少、尺度适中的样本。如果只能随机,那就多跑几次取平均,减少方差。
数据增强在增量阶段要慎用。常规检测里常用的 mosaic、mixup 这些强增强,在小样本设定下可能引入噪声,因为样本本来就少,增强后的分布可能偏离真实分布。我的建议是只用一些温和的增强,比如随机翻转、小幅度缩放和颜色抖动。论文里也是偏保守的增强策略。
另外,新旧类的样本比例要控制。如果新类样本太少,一个 batch 里全是新类,旧类的正则就形同虚设。可以在每个 batch 里混入少量基类样本(如果允许保留的话),或者用基类特征的统计量做正则,间接约束旧类性能。
4.3 关键参数的计算与选择
增量阶段有几个参数需要仔细调。第一个是学习率。基类训练可能用 0.01 或者 0.02,增量阶段我一般从 0.001 开始试,最多到 0.005。因为现在只训少量参数,而且是在好解附近微调,学习率大了容易震荡。
第二个是训练轮数。小样本设定下,训太多轮必然过拟合。5-shot 的话,我通常训 50 到 100 个 iteration 就停,看验证集上新类 AP 不再涨、旧类 AP 开始掉的时候就该停了。早停在这个任务里特别重要。
第三个是前面提到的正则权重。这个没有固定值,得扫。我一般设几个候选值,比如 0.1、0.3、0.5、1.0,跑一遍看新旧类的 trade-off 曲线,选一个旧类掉点不超过 2 个点、新类尽量高的点。
第四个是特征校准模块的维度。这个模块通常是个小的 MLP 或者线性层,隐层维度不用太大,256 或者 512 就够。太大了参数多,小样本训不动;太小了表达能力不够,校准效果差。
4.4 评估方式与指标解读
评估的时候要同时看新类和旧类的性能。常用的指标是新类 mAP、旧类 mAP,以及两者的调和平均。调和平均这个指标很关键,因为它惩罚偏科。如果你只顾新类,旧类掉得厉害,调和平均会很难看。
论文里还报告了不同 shot 下的结果,1-shot、5-shot、10-shot 都有。看结果的时候要注意,shot 数增加,新类性能提升是正常的,但旧类性能可能反而下降,因为新类样本多了,模型更容易偏向新类。这个 trade-off 在解读结果时要心里有数。
还有一个容易被忽略的点:评估时用的旧类测试集和新类测试集要分开,不能混在一起算一个总 mAP,否则看不出新旧类的分别表现。论文里是分开报的,复现时也要对齐。
5. 常见问题与排查技巧实录
5.1 新类 AP 上不去怎么办
这是最常见的问题。排查思路按顺序来:先看新类样本的特征有没有被正确校准,可以把校准前后的特征可视化一下,如果校准后新类样本还是散得很开,说明校准模块没学好,可能是学习率太大或者正则太强。再看分类权重生成是否合理,把生成的新类权重和基类权重做个相似度分析,如果新类权重和所有基类权重都正交,那说明生成模块没起作用,退化成随机初始化了。
如果这两个都正常,那可能是 shot 数太少,信息量不够。可以试试用更强的数据增强,或者引入一些无标注的新类数据做半监督。论文里没展开半监督,但实际场景里这是个可行的补充方向。
5.2 旧类性能掉得厉害怎么办
旧类掉点说明正则不够或者学习率太大。先把正则权重调大试试,如果调大后新类也学不进去,那就是平衡点没找对,需要更细地扫。另一个可能是校准模块把旧类特征也带偏了,这时候可以给校准模块加一个只对新类生效的 mask,让旧类特征走恒等映射。
还有一个隐蔽的原因:BN 统计量被新类样本带偏了。如果增量阶段 BN 没冻结,几个新类样本就能把统计量拉到一个奇怪的位置,旧类特征经过 BN 后分布全变了。解决办法就是冻结 BN,用基类训练时的统计量。
5.3 训练不稳定的排查
小样本训练不稳定是常态,梯度噪声大。除了前面说的梯度累积,还可以用 EMA(指数移动平均)来平滑参数更新。EMA 在增量阶段特别有用,因为它相当于对多个 iteration 的参数做平均,能显著降低方差。
另外,初始化也很关键。新类权重生成模块的初始化如果不好,前几个 iteration 可能就崩了。建议用较小的初始化方差,或者干脆从恒等映射开始,让模型先学会"不改变",再慢慢学"改变"。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 新类 AP 低 | 校准失效/权重生成退化 | 可视化特征、分析权重相似度 |
| 旧类 AP 掉 | 正则不足/BN 偏移 | 调正则、冻结 BN |
| 训练震荡 | 学习率大/初始化差 | 降学习率、EMA、调初始化 |
| 过拟合新类 | 训练轮数多/增强弱 | 早停、加温和增强 |
| 新旧类都不行 | 基类训练不充分 | 回退检查基类模型 |
6. 这套方法能迁移到什么场景
增量小样本检测的落地场景其实比想象中多。电商商品检测,新品类不断上架,每个品类初期只有几张图;工业质检,新缺陷类型出现时样本极少;遥感目标检测,新目标类型标注成本极高。这些场景的共同点都是"类别动态增长 + 标注稀缺",正好是 iFSOD 的靶心。
迁移的时候要注意,论文的设定是类别增量,不是域增量。如果你的新数据不仅类别新,域也变了(比如从白天变到夜晚),那单纯的特征校准可能不够,还需要考虑域适配。这是两个不同的问题,别混在一起。
另外,实际部署时还要考虑推理效率。增量阶段加的模块如果太重,推理时会拖慢速度。论文里的校准模块和权重生成模块都比较轻量,这是它适合落地的一个原因。如果你自己设计,也要把参数量和计算量控制住,别为了涨点把模型搞得跑不动。
我个人在复现这类方法时的体会是,基类训练的质量决定了上限,增量阶段的设计决定了下限。基类训得好,增量随便调调都不会太差;基类训得烂,增量阶段再怎么设计也救不回来。所以如果你要上手这个方向,先把基类训练这一环做扎实,别急着上增量模块。