
MMDetection 长尾实例分割实战Seesaw Loss 原理、LVIS 配置与复现指南【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址: https://gitcode.com/gh_mirrors/mm/mmdetectionSeesaw LossCVPR 2021《Seesaw Loss for Long-Tailed Instance Segmentation》是 OpenMMLab MMDetection 中内置的一类专为长尾分布设计的分类损失函数旨在解决 LVIS 等真实场景数据集中头部类别样本压倒尾部类别、导致尾部目标被误判为背景或头部类别的问题。本文以 MMDetection 仓库中 configs/seesaw_loss 的完整配置集为核心深入讲解 Seesaw Loss 的 mitigation factor 与 compensation factor 两大机制、源码级实现细节、LVIS 数据集与 RFS 采样策略的搭配方式并给出 12 个可复现实验的全部参数与结果对照。读完本文你将掌握在 MMDetection 3.x 中配置并训练长尾实例分割模型Mask R-CNN / Cascade Mask R-CNN的完整技术路线。一、背景长尾分布下实例分割分类器为何失效在类均衡的公开基准如 COCO上实例分割模型已经取得了显著进展。但真实世界的物体类别分布天然带有长尾long tail特性少数头部类别head classes如人、车、狗占据了数据集的绝大多数样本而大量尾部类别tail categories只出现极少的实例。在 LVISLarge Vocabulary Instance Segmentation数据集上这种长尾现象尤为突出——类别数高达 1203 个且样本数量跨越多个数量级。长尾数据给分类器训练带来一个核心问题头部类别的实例在训练中既是尾部类别的正样本来源缺失又是其最主要的负样本来源。尾部类别在每一轮迭代中接收到的大量负样本梯度来自被错分类为头部类别或背景的样本是压倒性的导致分类器对尾部类别的学习产生严重偏差。最终结果是尾部类别的物体更容易被误分类为背景或头部类别模型的长尾识别能力大打折扣。为解决这一问题原论文提出Seesaw Loss为每个类别动态地重新平衡正负样本的梯度贡献使用两个互补因子——缓解因子mitigation factor与补偿因子compensation factor并且无需修改网络结构或引入额外的分支即可端到端地替换标准交叉熵损失。二、Seesaw Loss 核心原理缓解因子与补偿因子Seesaw Loss 的核心思想是跷跷板式地动态调节损失权重当类别间样本数量悬殊时抑制头部类别作为尾部类别负样本时产生的惩罚缓解因子同时对被错误预测为头部类别或背景的样本加大惩罚补偿因子避免尾部类别被过度抑制而产生大量误报。2.1 缓解因子Mitigation Factor缓解因子根据两个类别之间累积训练样本数量的比值来降低对尾部类别的惩罚强度。在 seesaw_ce_loss 的源码实现中# mitigation factor if p 0: sample_ratio_matrix cum_samples[None, :].clamp( min1) / cum_samples[:, None].clamp(min1) index (sample_ratio_matrix 1.0).float() sample_weights sample_ratio_matrix.pow(p) * index (1 - index) mitigation_factor sample_weights[labels.long(), :] seesaw_weights seesaw_weights * mitigation_factor假设类别 j 的累积样本数远大于类别 i那么当样本的真实类别为 i尾部类而预测类别为 j头部类时比值cum_samples[j] / cum_samples[i]远大于 1缓解因子(ratio)^p会显著放大……准确地说源码中sample_ratio_matrix[cum_i][cum_j] 1时即 i 比 j 更头部权重为ratio^p小于 1从而降低头部类别对尾部类别负样本的惩罚梯度。参数p默认 0.8控制缓解的强度p越大缓解越强。2.2 补偿因子Compensation Factor补偿因子防止缓解因子带来矫枉过正如果一味降低惩罚模型可能把尾部类别的所有负样本都忽略导致尾部类别产生大量误报false positive。因此当某个头部类别样本被错误地预测为尾部类别时补偿因子会依据预测分数的比值加大惩罚# compensation factor if q 0: scores F.softmax(cls_score.detach(), dim1) self_scores scores[torch.arange(0, len(scores)).to(scores.device).long(), labels.long()] score_matrix scores / self_scores[:, None].clamp(mineps) index (score_matrix 1.0).float() compensation_factor score_matrix.pow(q) * index (1 - index) seesaw_weights seesaw_weights * compensation_factor当某类别的预测分数超过真实类别分数时score_matrix 1以score^q放大该样本的损失贡献参数q默认 2.0控制补偿强度eps默认 1e-2用于平滑除法、保持数值稳定。最终将两个因子合成的seesaw_weights以对数形式加到分类 logits 的负标签位置上再走标准交叉熵计算cls_score cls_score (seesaw_weights.log() * (1 - onehot_labels)) loss F.cross_entropy(cls_score, labels, weightNone, reductionnone)2.3 类别累积样本的在线统计源码在SeesawLoss.forward中通过一个注册缓冲区buffer持续累积每个类别的样本数供缓解因子实时使用缓冲区定义见 seesaw_loss.pyself.register_buffer(cum_samples, torch.zeros(self.num_classes 1, dtypetorch.float))每次前向传播时按标签累加见 seesaw_loss.pyunique_labels labels.unique() for u_l in unique_labels: inds_ labels u_l.item() self.cum_samples[u_l] inds_.sum()由于cum_samples是随训练过程动态累积的全局状态Seesaw Loss 的训练无法简单地在推理阶段即插即用它必须与训练循环绑定这与纯函数式的 Focal Loss 等静态加权损失有本质区别。2.4 自定义分类头接口与 LVIS 双通道预测的适配LVIS 的分类任务包含背景 1203 个类别 前景/背景判定的复合结构SeesawLoss通过三个自定义接口适配检测头对应实现见 seesaw_loss.pycustom_cls_channels True与get_cls_channels分类输出通道数为num_classes 21203 个类别分数 2 个 objectness 分数custom_activation True与get_activation分别对类别分数做 softmax、对 objectness 分数做 softmax再组合得到最终置信度custom_accuracy True与get_accuracy同时统计 objectness 精度与类别精度。forward返回的损失是一个 dict包含loss_cls_objectness前景/背景判定损失走标准交叉熵与loss_cls_classesSeesaw CE 损失仅在存在正样本时计算见 seesaw_loss.py两者共同构成最终的分类损失。三、配套归一化预测器NormedLinear 与 NormedConv2d长尾场景下分类器权重范数与类别样本量高度相关头部类别的权重范数通常偏大。为了缓解这一偏差Seesaw Loss 配置集同时引入了权重归一化的预测层实现在 normed_predictor.pyNormedLinear定义在nn.Linear基础上对输入与权重分别做 L2 归一化后乘以温度系数tempearture配置中默认为 20并对权重做均值为 0、标准差 0.01 的正态初始化。其前向为F.linear(x / ||x|| * temperature, W / ||W||, b)NormedConv2d定义对 Mask Head 的卷积预测层做同样的归一化支持norm_over_kernel选项控制是否按卷积核维度归一化。在配置文件中二者通过cls_predictor_cfg/predictor_cfg字段注入bbox_headdict( cls_predictor_cfgdict(typeNormedLinear, tempearture20), ...) mask_headdict( predictor_cfgdict(typeNormedConv2d, tempearture20), ...)其中Norm Mask Y的实验即表示 Mask Head 使用了NormedConv2d归一化预测器。四、LVIS 数据集准备与两种数据采样策略4.1 数据集准备本文所有实验均基于LVIS v1数据集1203 类其配置骨架位于 configs/base/datasets/lvis_v1_instance.py继承自 lvis_v0.5_instance.py数据集类型LVISV1Datasetdata_root data/lvis_v1/训练标注annotations/lvis_v1_train.json验证标注annotations/lvis_v1_val.json评估器LVISMetric指标metric[bbox, segm]同时输出 box AP 与 mask AP。详细的 LVIS 下载与目录组织方式请参考 configs/lvis/README.md。请将数据解压到data/lvis_v1/目录下与配置文件中的data_root保持一致。4.2 两种采样策略random 与 RFS原文档明确指出RFS indicates to use oversample strategy here with oversample threshold1e-3.在 MMDetection 3.x 中该策略对应 MMEngine 的ClassBalancedDataset数据集包装器具体用法见 docs/en/advanced_guides/customize_dataset.md 中 Customize datasets by dataset wrappers 一节。其配置形态直接体现在 lvis_v0.5_instance.pytrain_dataloader dict( batch_size2, num_workers2, persistent_workersTrue, samplerdict(typeDefaultSampler, shuffleTrue), batch_samplerdict(typeAspectRatioBatchSampler), datasetdict( typeClassBalancedDataset, oversample_thr1e-3, # RFS 采样阈值 datasetdict( typedataset_type, data_rootdata_root, ann_fileannotations/lvis_v0.5_train.json, data_prefixdict(imgtrain2017/), filter_cfgdict(filter_empty_gtTrue, min_size32), pipelinetrain_pipeline, backend_argsbackend_args)))oversample_thr1e-3的含义是样本占比低于该阈值的类别会被过采样重复抽取以平衡类别分布这正是原文档所称的 RFSRepeat Factor Sampling策略。本文 12 个实验中Data Sampler random直接使用原始 LVIS 训练集不做过采样Data Sampler RFS外层套上ClassBalancedDatasetoversample_thr1e-3对应的随机配置文件名含sample1e-3。4.3 多尺度训练管线所有 Seesaw Loss 配置均使用多尺度ms训练管线短边在 640~800 之间随机选择train_pipeline [ dict(typeLoadImageFromFile, backend_args{{_base_.backend_args}}), dict(typeLoadAnnotations, with_bboxTrue, with_maskTrue), dict( typeRandomChoiceResize, scales[(1333, 640), (1333, 672), (1333, 704), (1333, 736), (1333, 768), (1333, 800)], keep_ratioTrue), dict(typeRandomFlip, prob0.5), dict(typePackDetInputs) ]五、配置文件逐项解析5.1 基线配置Mask R-CNN R-50-FPN random 采样以 mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py 为例完整配置如下_base_ [ ../_base_/models/mask-rcnn_r50_fpn.py, ../_base_/datasets/coco_instance.py, ../_base_/schedules/schedule_2x.py, ../_base_/default_runtime.py ] model dict( roi_headdict( bbox_headdict( num_classes1203, cls_predictor_cfgdict(typeNormedLinear, tempearture20), loss_clsdict( typeSeesawLoss, p0.8, q2.0, num_classes1203, loss_weight1.0)), mask_headdict(num_classes1203)), test_cfgdict( rcnndict( score_thr0.0001, # LVIS allows up to 300 max_per_img300)))各关键参数说明如下参数取值含义与作用num_classes1203LVIS v1 的类别总数需同时写入bbox_head、mask_head与SeesawLosscls_predictor_cfg.typeNormedLinear将 RoI 分类头替换为权重归一化线性层tempearture20归一化预测器的温度系数缩放归一化后的 logits 幅度注意该参数在源码中的拼写即为tempeartureloss_cls.typeSeesawLoss使用 Seesaw Loss 替换标准 CrossEntropyLossp0.8缓解因子指数控制对尾部类别负样本惩罚的降低程度q2.0补偿因子指数控制对误分类样本的惩罚放大程度loss_weight1.0分类损失在总损失中的权重score_thr0.0001推理时的分数阈值LVIS 类别多、分数普遍偏低需要放低阈值max_per_img300每张图最多保留 300 个检测结果LVIS 官方评估允许的上限训练周期配置train_cfg dict(val_interval24)2x schedule 共 24 个 epoch且每 24 个 epoch 做一次验证即训练结束时验证。训练资源与优化器沿用基础配置8 块 V100 GPU、SGD Momentum Weight Decay见 metafile.yml 中的 Training Techniques 记录。5.2 RFS 变体替换数据集骨架mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py 与基线唯一的差异是_base_中的数据集骨架从coco_instance.py换成lvis_v1_instance.py并覆写训练管线_base_ [ ../_base_/models/mask-rcnn_r50_fpn.py, ../_base_/datasets/lvis_v1_instance.py, ../_base_/schedules/schedule_2x.py, ../_base_/default_runtime.py ] ... train_dataloader dict(datasetdict(datasetdict(pipelinetrain_pipeline)))由于lvis_v1_instance.py继承自lvis_v0.5_instance.py而后者已经用ClassBalancedDataset(oversample_thr1e-3)包装了训练集因此这一行即完成了 RFS 采样策略的启用。这也是random与RFS两组实验在配置上最直观的区别。5.3 Normed Mask 变体以 mask-rcnn_r50_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py 为例Normed Mask 变体极其精简只需覆写 mask head 的预测器_base_ ./mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.py model dict( roi_headdict( mask_headdict( predictor_cfgdict(typeNormedConv2d, tempearture20))))即通过局部继承_base_指向同目录下的基础配置将 Mask Head 的预测卷积替换为归一化版本。Cascade Mask R-CNN 的 Normed Mask 变体如 cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.py采用完全相同的写法_base_指向同目录下对应的 Cascade 基础配置。六、实验配置与结果全览LVIS v1以下 12 组实验复现了 CVPR 2021 原论文在 LVIS v1 上的实例分割性能全部采用 2x schedule24 epoch与多尺度训练。训练细节如 8×V100 GPU记录在 metafile.yml 中各模型权重文件的下载地址同样可在该 metafile 的Weights字段中获取。MethodBackboneStyleLr schdData SamplerNorm Maskbox APmask APConfigMask R-CNNR-50-FPNpytorch2xrandomN25.625.0mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.pyMask R-CNNR-50-FPNpytorch2xrandomY25.625.4mask-rcnn_r50_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.pyMask R-CNNR-101-FPNpytorch2xrandomN27.426.7mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.pyMask R-CNNR-101-FPNpytorch2xrandomY27.227.3mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.pyMask R-CNNR-50-FPNpytorch2xRFSN27.626.4mask-rcnn_r50_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.pyMask R-CNNR-50-FPNpytorch2xRFSY27.626.8mask-rcnn_r50_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.pyMask R-CNNR-101-FPNpytorch2xRFSN28.927.6mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.pyMask R-CNNR-101-FPNpytorch2xRFSY28.928.2mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.pyCascade Mask R-CNNR-101-FPNpytorch2xrandomN33.129.2cascade-mask-rcnn_r101_fpn_seesaw-loss_random-ms-2x_lvis-v1.pyCascade Mask R-CNNR-101-FPNpytorch2xrandomY33.030.0cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_random-ms-2x_lvis-v1.pyCascade Mask R-CNNR-101-FPNpytorch2xRFSN30.029.3cascade-mask-rcnn_r101_fpn_seesaw-loss_sample1e-3-ms-2x_lvis-v1.pyCascade Mask R-CNNR-101-FPNpytorch2xRFSY32.830.1cascade-mask-rcnn_r101_fpn_seesaw-loss-normed-mask_sample1e-3-ms-2x_lvis-v1.py从结果可以观察到的规律均基于仓库内记录的数据RFS 采样对 Mask R-CNN 有明显增益R-50 的 mask AP 从 25.0 提升到 26.4Norm Mask Y 时为 26.8R-101 从 26.7 提升到 27.6Normed Mask 主要提升 mask AP多个组合下 mask AP 均有 0.4~0.8 的提升如 Cascade R-101 random 组合从 29.2 升至 30.0Cascade Mask R-CNN 在随机采样下表现最佳box AP 33.1 / mask AP 29.2其中 Normed Mask 版本取得了最高的 mask AP30.0/30.1。需要说明的是以上数值均为仓库官方记录的单次训练结果不同随机种子与硬件环境下复现时可能出现合理浮动。七、训练与评估命令准备工作就绪后可直接使用仓库的 tools/train.py 与 tools/test.py 进行训练与评估。单卡训练以随机采样基线为例python tools/train.py configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py8 卡分布式训练与论文设置一致bash tools/dist_train.sh configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py 8评估需传入训练得到的 checkpoint 路径或使用 metafile.yml 中对应模型权重python tools/test.py configs/seesaw_loss/mask-rcnn_r50_fpn_seesaw-loss_random-ms-2x_lvis-v1.py /path/to/checkpoint.pth评估输出同时包含 box APLVIS 的 bbox 指标与 mask APsegm 指标与配置文件中的val_evaluator dict(typeLVISMetric, metric[bbox, segm])对应。八、使用注意与适用边界仅支持 softmax 输出SeesawLoss的构造器中assert not use_sigmoid即该损失只适配 softmax 分类头不能直接用于 sigmoid 多标签头见 seesaw_loss.py必须在线累积样本统计cum_samples随训练动态更新且forward中会修改缓冲区状态因此该损失不适用于纯推理时替换损失也不支持在无监督/蒸馏场景下直接套用类别数必须对齐SeesawLoss(num_classes1203)、bbox_head.num_classes、mask_head.num_classes三者必须一致且get_cls_channels会在类别数不匹配时直接断言失败分数阈值需要放低LVIS 类别数多、长尾类别分数天然偏低因此推理配置使用score_thr0.0001与max_per_img300直接沿用 COCO 默认的阈值如 0.05会显著低估长尾类别召回。九、引用本文对应的算法论文为Seesaw Loss for Long-Tailed Instance SegmentationCVPR 2021仓库内完整 BibTeX 记录如下inproceedings{wang2021seesaw, title{Seesaw Loss for Long-Tailed Instance Segmentation}, author{Jiaqi Wang and Wenwei Zhang and Yuhang Zang and Yuhang Cao and Jiangmiao Pang and Tao Gong and Kai Chen and Ziwei Liu and Chen Change Loy and Dahua Lin}, booktitle{Proceedings of the {IEEE} Conference on Computer Vision and Pattern Recognition}, year{2021} }十、关键文件速查配置与结果总览configs/seesaw_loss/README.md、configs/seesaw_loss/metafile.yml12 个实验配置configs/seesaw_loss 目录mask-rcnn_*与cascade-mask-rcnn_*系列损失实现mmdet/models/losses/seesaw_loss.py归一化预测器mmdet/models/layers/normed_predictor.py数据集骨架configs/base/datasets/lvis_v1_instance.py、configs/base/datasets/lvis_v0.5_instance.py模型骨架与训练周期configs/base/models/mask-rcnn_r50_fpn.py、configs/base/schedules/schedule_2x.pyLVIS 数据准备指南configs/lvis/README.md【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址: https://gitcode.com/gh_mirrors/mm/mmdetection创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考