1. 为什么实例分割值得死磕:从项目背景说起
实例分割这个方向,在计算机视觉里一直是个"硬骨头"。目标检测给你一个框,语义分割给你一片区域,而实例分割要的是——每个物体不仅要知道它是什么类别,还要精确到像素级地把它的轮廓抠出来,而且不同个体之间要分得清清楚楚。这就好比你不光要在一堆水果里认出哪些是苹果,还得把每个苹果的边界精确画出来,哪怕它们挨在一起甚至部分遮挡。
在YOLACT出现之前,主流方案基本是两条路。一条是"先检测后分割"的两阶段路线,比如Mask R-CNN,先跑一个检测器找到候选框,再对每个框做RoI Align然后跑一个mask分支。这条路精度确实好,但速度上不去,因为每个候选框都要单独过一遍mask网络,实例一多就线性增长,实时性基本没戏。另一条是"先分割后聚类"的路线,比如先做语义分割,再用嵌入向量或者聚类把不同实例分开,这类方法速度稍好但精度和稳定性都差一截,尤其是物体重叠的时候容易糊成一片。
YOLACT的核心贡献就是打破了"mask必须依赖特征重采样"这个思维定式。它的思路是:我不对每个实例单独跑mask分支,而是一次性生成一组"原型mask"(prototype masks),然后每个实例只需要预测一组"mask系数"(mask coefficients),两者做线性组合就得到该实例的mask。这个设计把mask生成的计算量从"跟实例数成正比"变成了"跟实例数几乎无关",因为原型mask只算一次,系数预测只是每个实例多输出k个浮点数而已。
YOLACT++是在YOLACT基础上的改进版,主要做了三件事:一是引入可变形卷积(Deformable Convolution)增强骨干网络的特征采样能力;二是优化了原型mask的分辨率和anchor设计;三是加了快速mask重打分(Fast Mask Re-scoring)分支来提升mask质量。这些改进让YOLACT++在保持实时速度的同时,精度又往上提了一截,在COCO数据集上达到了34.1的mask AP,速度超过33 FPS。
这个项目适合谁看?如果你正在做实时分割相关的工程落地,比如视频编辑、自动驾驶感知、工业质检、AR/VR场景理解,或者你只是想搞明白"实时实例分割到底是怎么做到的",那这篇笔记应该能给你不少参考。我会尽量把论文里的关键设计讲透,同时补上一些论文里没细说但实操中很关键的点。
2. 核心架构拆解:YOLACT++到底是怎么跑起来的
2.1 整体流程:一张图看懂数据流向
YOLACT++的整体架构可以分成四个主要模块:骨干网络(Backbone)、特征金字塔(FPN)、原型生成分支(Protonet)、以及预测头(Prediction Head)。输入一张图片,先过骨干网络提取多尺度特征,然后FPN把这些特征融合成P3到P7五个层级的特征图。接着分两路走:一路是Protonet,从P3特征生成一组原型mask;另一路是Prediction Head,在每个特征层级上预测类别、框回归、mask系数,以及YOLACT++新增的mask重打分。
最终输出的时候,每个实例的mask就是它的mask系数与原型mask做矩阵乘法再经过sigmoid得到。然后根据预测的框把mask裁剪到框内,再二值化就完事了。整个过程没有RoI Align,没有对每个实例单独跑卷积,所以速度非常稳定。
这里有个关键设计需要展开说:为什么原型mask能从P3一层就生成?论文里的解释是,原型mask不需要太高的语义层次,它更像是一种"基础形状字典",负责提供各种可能的mask基元,而具体某个实例该用哪些基元、怎么组合,交给mask系数去决定。P3的分辨率是输入图像的1/8,这个尺度既能保留足够的空间细节,又不至于计算量爆炸。如果从P2生成,分辨率翻倍,计算量增加不少但精度提升有限;从P4生成则细节丢失太多,mask边缘会很粗糙。
2.2 原型mask生成:Protonet的设计细节
Protonet的结构其实很简单,就是几层卷积堆叠。从P3特征(通道数256)开始,先做一个3x3卷积把通道数降到256,然后上采样到1/4输入分辨率,再经过几层3x3卷积,最后输出k个通道的原型mask。YOLACT++里k默认取32,也就是说生成32张原型mask,每张都是1/4输入分辨率。
为什么是32?这个数字是实验调出来的。太少的话,原型mask的表达能力不够,复杂形状的物体分割不好;太多的话,mask系数预测的负担加重,而且容易过拟合。论文里做了消融实验,k从16到64都试过,32是精度和速度的平衡点。
这里有个实操中容易踩的坑:Protonet最后一层不要加BN和ReLU。因为原型mask是要做线性组合的,如果加了非线性激活,组合空间就被限制了。论文里明确说了最后一层是纯卷积,输出直接拿去做组合。这一点在复现的时候很容易忽略,我见过不少人照着结构图搭完发现mask质量差一截,最后查出来就是多加了激活函数。
另外,原型mask在训练时是不做归一化的,但在推理时会对每个原型做L2归一化,mask系数也做L2归一化,这样点积的结果就等价于余弦相似度,数值更稳定。这个细节论文里一笔带过,但实际写代码的时候如果忘了,推理结果会飘。
2.3 可变形卷积的引入:YOLACT++的第一个改进
YOLACT++相比YOLACT的第一个大改动是在骨干网络里引入了可变形卷积(DCN)。具体来说,是在ResNet的C3、C4、C5阶段的部分3x3卷积里,把普通卷积替换成可变形卷积。可变形卷积的核心思想是,卷积核的采样位置不是固定的网格,而是通过学习一个偏移量,让采样点能够自适应地聚拢到物体形状附近。
这个改进对实例分割特别有意义。因为实例分割要求精确的边界,而普通卷积的固定采样网格在面对非刚性物体(比如人、动物)或者极端长宽比的物体(比如栏杆、电线杆)时,感受野和物体形状匹配度不高。可变形卷积让网络能够"主动"去采样那些更有信息量的位置,边界处的特征质量会明显提升。
但这里有个工程上的权衡:可变形卷积的计算量比普通卷积大,因为要额外预测偏移量,而且采样是双线性的,不是规整的内存访问,GPU利用率会下降。YOLACT++的做法是只在骨干网络的部分层替换,而不是全部替换,这样精度提升明显但速度损失可控。实测下来,替换C3-C5的3x3卷积后,mask AP大概能涨1.5到2个点,速度下降约10%到15%。
2.4 快速mask重打分:YOLACT++的第二个改进
YOLACT++的第二个改进是加了Fast Mask Re-scoring分支。这个分支的作用是:在得到每个实例的mask之后,用一个轻量级的网络对mask质量进行重新评估,然后把这个分数和分类分数相乘,作为最终的置信度。
为什么要做这个?因为YOLACT原本的mask系数是从分类分支共享特征预测的,分类分数高不代表mask质量好。有时候一个实例分类很准,但mask可能只覆盖了物体的一部分,或者边界很粗糙。Fast Mask Re-scoring就是专门来修正这个问题的。
具体实现上,它把每个实例的mask裁剪到对应的检测框内,缩放到固定大小(比如7x7),然后过一个几层的小卷积网络,输出一个分数。这个分数和分类分数相乘后,再用于NMS和最终输出。这个分支的计算量很小,因为每个实例只需要处理一个7x7的小图,而且可以和mask组合并行做。
实测效果上,Fast Mask Re-scoring能带来大约1个点的mask AP提升,而且几乎不增加推理时间。这个改进的性价比很高,建议在复现的时候一定要加上。
3. 关键参数与训练策略:论文里没细说但你必须知道的
3.1 损失函数的设计与权重分配
YOLACT++的损失函数由三部分组成:分类损失、框回归损失、mask损失。分类损失用的是标准的交叉熵,框回归用的是smooth L1,mask损失用的是二值交叉熵。但关键在于权重分配。
论文里给的权重是:分类损失权重1,框回归权重1.5,mask损失权重6。mask损失权重这么高,是因为mask的像素级预测本身难度大,如果权重太低,网络会倾向于先把分类和框做好,mask就摆烂了。但权重太高也不行,会导致训练不稳定,梯度爆炸。6这个值是实验调出来的,我在复现的时候试过3和10,3的时候mask明显偏软,10的时候训练loss震荡厉害。
另外,mask损失只对正样本计算,而且只对与GT框IoU大于0.5的anchor计算。负样本不参与mask损失,因为负样本根本没有对应的mask。这个细节在写dataloader和loss的时候要特别注意,不然容易把负样本也算进去,导致mask分支学偏。
还有一个容易忽略的点:mask损失是在原型mask组合后的完整mask上算的,不是在裁剪后的mask上算的。也就是说,训练时每个正样本的mask是完整分辨率(1/4输入)的,而不是裁剪到框内的。这样做的好处是网络能学到全局的mask形状,而不是只关注框内区域。推理时才做裁剪,因为最终输出只需要框内的mask。
3.2 数据增强与训练技巧
YOLACT++的训练用了比较重的数据增强,包括随机翻转、随机缩放、随机裁剪、颜色抖动等。其中最重要的是随机缩放和随机裁剪的组合,因为实例分割对物体的尺度和位置很敏感。
具体来说,训练时每张图会随机缩放到[0.5, 1.5]倍,然后随机裁剪到固定大小(比如544x544)。如果裁剪后某个GT框的可见部分小于原来的20%,这个GT就被丢弃;如果可见部分在20%到80%之间,mask会被截断但保留;如果大于80%,正常保留。这个策略是为了让网络适应物体被部分遮挡或截断的情况,提升鲁棒性。
还有一个训练技巧是"warmup"阶段。前500个iteration用很小的学习率(比如1e-4),然后线性增加到初始学习率(比如1e-3),之后再按余弦退火衰减。这个策略在YOLACT++里很重要,因为可变形卷积的偏移量学习一开始很不稳定,如果直接用大学习率,偏移量会乱飞,导致训练崩溃。
另外,YOLACT++用了多尺度训练,输入图像的短边在[400, 600]之间随机选,长边不超过800。这个范围是COCO数据集上调出来的,如果你的数据集物体普遍偏小,可以适当调低;如果物体偏大,可以调高。但要注意,输入分辨率直接影响推理速度,训练时的分辨率范围和推理时最好保持一致,不然会有尺度不匹配的问题。
3.3 推理时的后处理细节
推理阶段有几个关键的后处理步骤,直接影响最终输出质量。
第一步是mask系数与原型mask的组合。每个实例有32个mask系数,32张原型mask,组合就是矩阵乘法:mask = sigmoid(coefficients @ prototypes)。这里coefficients是1x32,prototypes是32xHxW,结果就是1xHxW的mask。注意要先做L2归一化再点积,前面提过了。
第二步是mask裁剪。根据预测的框,把mask裁剪到框内,框外的像素置零。这一步是为了去掉mask在框外的噪声响应。但裁剪的时候要注意,框的坐标要clip到图像范围内,不然会越界。
第三步是二值化。通常用0.5作为阈值,大于0.5的像素置1,否则置0。但实际用的时候,可以根据场景调整阈值。比如工业质检里如果漏检代价高,可以把阈值调低到0.3,让mask更"胖"一些;如果误检代价高,可以调到0.7,让mask更"瘦"。
第四步是NMS。YOLACT++用的是标准的NMS,IoU阈值0.5。但注意,NMS是在框级别做的,mask不参与NMS。也就是说,两个框如果IoU大于0.5,分数低的那个直接被丢掉,它的mask也一起丢。这个策略在大多数场景下没问题,但如果两个物体严重重叠,可能会误删。有些改进方案会用mask IoU来做NMS,但YOLACT++原版没这么做,因为速度会慢。
最后一步是Fast Mask Re-scoring的分数融合。把重打分分支输出的分数和分类分数相乘,得到最终置信度。这个分数用于最终的排序和输出。注意,重打分分支的输入是裁剪后的mask,所以它是在NMS之前做的,NMS用的是融合后的分数。
4. 实操复现与性能调优:从零跑通YOLACT++
4.1 环境搭建与依赖安装
复现YOLACT++的第一步是搭环境。官方代码是基于PyTorch的,但原版用的是比较老的版本(PyTorch 1.0左右),现在直接跑可能会有兼容性问题。我建议用PyTorch 1.6到1.8之间的版本,太新了有些自定义CUDA算子编译不过,太旧了又不支持新的GPU架构。
可变形卷积需要单独编译CUDA算子。官方代码里有一个DCNv2的文件夹,需要进去跑python setup.py build_ext --inplace。这里常见的坑是CUDA版本和PyTorch版本不匹配,导致编译报错。解决办法是先确认torch.version.cuda和nvcc --version一致,如果不一致,要么重装PyTorch,要么改环境变量。
另外,YOLACT++还用了一个自定义的NMS算子,也是CUDA写的。如果编译不过,可以先用CPU版本的NMS顶着,但速度会慢不少。CPU版NMS在实例数多的时候(比如超过100个)会明显拖慢推理,所以建议还是把CUDA算子编译好。
依赖方面,除了PyTorch和torchvision,还需要opencv-python、pycocotools、matplotlib等。pycocotools在Windows上安装比较麻烦,建议用Linux环境或者WSL。如果非要在Windows上跑,可以用pip install pycocotools-windows这个替代包。
4.2 配置文件的关键参数解读
YOLACT++的配置文件里有一堆参数,但真正影响性能和精度的就那么几个。我挑最重要的几个说一下。
max_iter:训练总迭代次数。COCO上默认是80000,但如果你用自己的数据集,要根据数据集大小调整。一般来说,迭代次数至少是数据集图片数的100倍,不然欠拟合。
lr:初始学习率。默认1e-3,但如果你的batch size比较小(比如小于8),要相应调小,不然梯度噪声太大。经验公式是lr = 1e-3 * (batch_size / 8)。
batch_size:默认8。如果显存不够,可以降到4或2,但要注意同步调整学习率和warmup迭代次数。
num_classes:类别数。注意要加上背景类,比如你的数据集有5个类别,那num_classes要设成6。
mask_dim:原型mask的数量,默认32。前面说过了,一般不用改。
use_dcn:是否使用可变形卷积。YOLACT++默认是True,如果你只想跑YOLACT baseline,可以设成False。
use_mask_scoring:是否使用Fast Mask Re-scoring。YOLACT++默认True,建议保持。
train_dataset和val_dataset:数据集路径和标注文件路径。注意标注格式要转成COCO格式,YOLACT++的dataloader只认COCO格式的json。
还有一个容易忽略的参数是num_workers。这个参数控制dataloader的并行进程数,设得太小会导致GPU等数据,设得太大又可能内存爆掉。一般来说,设成CPU核心数的1/4到1/2比较合适。比如8核CPU,设2到4就行。
4.3 训练过程中的监控与调参
训练YOLACT++的时候,有几个指标要重点盯。
首先是总loss的下降曲线。正常情况应该是前几千个iteration快速下降,然后进入平台期缓慢下降。如果loss震荡厉害,可能是学习率太大或者batch size太小;如果loss下降太慢,可能是学习率太小或者数据增强太强。
其次是mask loss的单独曲线。mask loss通常比分类和框回归loss高一个数量级,这是正常的。但如果mask loss一直不降,可能是Protonet最后一层加了激活函数,或者mask损失权重设得太低。
还有一个重要的监控指标是正样本数量。YOLACT++用的是RetinaNet式的anchor匹配策略,每个GT框会匹配多个anchor。如果正样本数量太少(比如每张图不到10个),说明anchor尺度设置和你的数据集不匹配,需要调整anchor的尺度和长宽比。
调参的时候,我一般按这个顺序来:先调学习率和batch size,让loss稳定下降;然后调数据增强强度,让验证集精度最高;最后调anchor参数,让正样本数量合理。不要一上来就调anchor,因为anchor的影响会被学习率的问题掩盖。
另外,YOLACT++对学习率衰减策略比较敏感。默认用的是余弦退火,从1e-3降到1e-6。如果换成step decay,精度会掉0.5个点左右。所以建议保持余弦退火,不要随便换。
4.4 推理速度优化实战
YOLACT++标称33 FPS,但那是用V100测的。在实际部署环境里,比如Jetson或者普通显卡,速度会差不少。我分享几个实测有效的优化手段。
第一个是降低输入分辨率。YOLACT++默认输入是550x550,如果降到400x400,速度能提升近一倍,但mask AP会掉3到4个点。如果你的场景对精度要求没那么高,这个 trade-off 是值得的。
第二个是减少原型mask数量。从32降到16,速度提升约15%,精度掉1个点左右。如果场景里物体形状比较简单(比如都是矩形或者圆形),16个原型mask够用了。
第三个是关闭Fast Mask Re-scoring。虽然它只增加很少的计算量,但在极端追求速度的场景下,关掉它能省一点时间。不过我不太推荐,因为精度损失和速度收益不成正比。
第四个是用TensorRT加速。YOLACT++的骨干网络和FPN部分可以转成TensorRT,但可变形卷积和自定义NMS算子转起来比较麻烦。如果能把DCN替换成普通卷积,整个模型都能转TensorRT,速度能提升2到3倍。但精度会掉1.5个点左右,需要权衡。
第五个是半精度推理(FP16)。YOLACT++在FP16下基本不掉精度,速度能提升30%到50%。但要注意,可变形卷积的偏移量在FP16下可能会溢出,需要做数值裁剪。实测下来,把偏移量限制在[-1, 1]范围内比较安全。
最后说一个工程上的技巧:把Protonet和Prediction Head放在不同的CUDA stream里并行跑。因为这两路是独立的,理论上可以并行。但实际实现起来比较麻烦,而且收益有限(大概10%左右),除非你真的很缺那点速度,否则不建议折腾。
5. 常见问题与排查技巧实录
5.1 训练不收敛或loss爆炸
这是复现YOLACT++时最常见的问题。表现是训练开始没多久loss就变成NaN,或者loss剧烈震荡完全不下降。
排查思路按这个顺序来:先检查学习率是不是太大,尤其是用了可变形卷积之后,偏移量的学习对学习率很敏感。如果学习率大于1e-3,先降到1e-4试试。然后检查数据增强是不是太激进,比如随机缩放范围太大或者颜色抖动太强,导致输入分布和预训练模型差距太大。接着检查Protonet最后一层是不是误加了激活函数,这个前面强调过了。最后检查mask损失权重是不是设得太高,如果大于10,先降到6。
还有一个隐蔽的原因是梯度裁剪没开。YOLACT++默认开了梯度裁剪,阈值是10。如果你复现的时候忘了开,可变形卷积的偏移量梯度可能会爆炸。这个在官方代码里是默认开的,但如果你自己改过训练循环,要确认一下。
5.2 mask质量差、边界模糊
如果训练loss正常下降,但推理出来的mask边界很糊,或者mask只覆盖了物体的一部分,可以从这几个方面排查。
先看原型mask的可视化。把32张原型mask单独拿出来看,如果它们看起来都差不多,说明原型多样性不够,可能是Protonet的容量太小或者训练不充分。如果原型mask看起来像随机噪声,说明Protonet没学好,可能是学习率太大或者mask损失权重太低。
然后看mask系数的分布。正常情况每个实例的mask系数应该有正有负,而且不同实例的系数分布差异明显。如果所有实例的系数都差不多,说明网络没学会区分不同实例,可能是分类分支和mask分支的特征共享太强,需要加一些解耦。
还有一个常见原因是输入分辨率太低。YOLACT++的原型mask是1/4输入分辨率,如果输入只有300x300,原型mask就只有75x75,边界细节肯定丢失。建议输入至少400x400,最好550x550。
5.3 推理速度远低于预期
标称33 FPS但实际只有10 FPS,这种落差很常见。排查的时候先确认你的GPU型号,V100和1080Ti的性能差距本身就很大。然后检查是不是用了CPU版的NMS,这个在实例多的时候特别慢。接着看输入分辨率是不是设得太高,550x550和800x800的速度差距接近一倍。
还有一个容易被忽略的点是数据预处理。如果预处理是在CPU上做的,而且用了很复杂的增强(比如Mosaic),CPU可能成为瓶颈。解决办法是把预处理也放到GPU上,或者用DALI这样的加速库。
另外,PyTorch的默认推理模式没有做图优化,可以用torch.jit.trace把模型转成TorchScript,速度能提升10%到20%。如果进一步用TensorRT,提升更大,但转换过程比较折腾。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 训练loss变NaN | 学习率太大 | 检查lr是否大于1e-3 | 降到1e-4重新训练 |
| loss震荡不下降 | batch size太小 | 检查batch size是否小于4 | 增大batch size或调小lr |
| mask边界模糊 | 输入分辨率太低 | 检查输入尺寸 | 提高到550x550 |
| mask只覆盖部分物体 | mask损失权重太低 | 检查mask loss权重 | 提高到6 |
| 推理速度慢 | 用了CPU NMS | 检查NMS实现 | 编译CUDA版NMS |
| 正样本太少 | anchor尺度不匹配 | 统计每图正样本数 | 调整anchor尺度和长宽比 |
| 原型mask多样性差 | Protonet容量不足 | 可视化原型mask | 增加Protonet层数或通道数 |
| 可变形卷积编译失败 | CUDA版本不匹配 | 检查torch.version.cuda | 重装匹配版本的PyTorch |
6. 实际落地中的经验与扩展思路
6.1 在自定义数据集上的迁移经验
用自己的数据集训练YOLACT++,最关键的是数据标注质量。实例分割的标注比检测框麻烦得多,边界要精确到像素。如果标注边界很粗糙,网络学出来的mask也会很粗糙。我建议标注的时候至少放大到200%来画边界,尤其是物体边缘有细小结构的时候。
另一个经验是类别不平衡的处理。如果你的数据集里某些类别特别多,某些特别少,YOLACT++的默认损失函数可能会偏向多数类。解决办法是在分类损失里加类别权重,权重和类别频率成反比。但注意,mask损失不要加类别权重,因为mask是像素级的,类别不平衡的影响没那么大。
还有一个迁移技巧是冻结骨干网络的前几层。如果你的数据集和COCO差异比较大(比如医学图像、遥感图像),冻结C1和C2层可以防止底层特征被破坏,同时加快训练速度。实测下来,冻结前两层能让收敛快20%左右,精度基本不掉。
6.2 视频实例分割的扩展
YOLACT++本身是单帧推理的,但用在视频上也很常见。如果直接逐帧跑,会有两个问题:一是帧间mask抖动,二是速度可能跟不上高帧率视频。
解决抖动的一个简单方法是对mask系数做时序平滑。比如用指数移动平均,当前帧的系数 = 0.7 * 当前帧预测 + 0.3 * 上一帧系数。这样mask会稳定很多,但快速运动的物体会有一点拖影。如果拖影明显,可以调低平滑系数,比如0.9和0.1。
另一个方法是加一个轻量级的跟踪分支,把上一帧的mask作为先验,引导当前帧的mask预测。这个思路在视频实例分割里很常见,但需要额外训练,工作量比较大。
速度方面,如果视频帧率是30 FPS,YOLACT++在高端GPU上勉强能实时。如果帧率更高或者GPU更弱,可以考虑跳帧推理,中间帧用光流或者简单的线性插值来生成mask。这个方案在监控场景里够用,但在自动驾驶里可能不够安全。
6.3 模型压缩与边缘部署
如果要在边缘设备上跑YOLACT++,模型压缩是绕不开的。我试过几种方案,分享下效果。
剪枝方面,YOLACT++的骨干网络(ResNet-50)有很多冗余通道,剪掉30%的通道后,mask AP只掉0.8个点,但速度提升40%。剪枝的时候要注意,Protonet和Prediction Head的通道不要剪太多,因为它们本身就不大,剪了精度掉得厉害。
量化方面,INT8量化能让速度翻倍,但精度掉得比较多,大概2到3个点。如果对精度要求高,可以用混合量化,骨干网络用INT8,Protonet和Prediction Head用FP16。这样速度提升50%左右,精度只掉0.5个点。
知识蒸馏也是一个思路,用一个大模型(比如Mask R-CNN)来教YOLACT++,让小模型的mask质量接近大模型。但蒸馏的训练成本比较高,而且YOLACT++本身已经比较精简了,蒸馏的收益有限。
6.4 一些踩过的坑和实用建议
最后分享几个我在复现和落地过程中踩过的坑。
第一个坑是数据格式转换。YOLACT++的dataloader只认COCO格式的json,如果你用的是VOC格式或者YOLO格式,需要先转。转换的时候要注意,COCO的segmentation字段是polygon或者RLE,polygon的坐标是归一化到[0,1]的,不是像素坐标。这个细节很容易搞错,导致mask全错位。
第二个坑是预训练模型加载。YOLACT++的官方预训练模型是在COCO上训的,如果你直接拿来在自己的数据集上微调,要注意类别数不匹配的问题。分类层的权重需要重新初始化,其他层可以加载。但可变形卷积的偏移量层最好也重新初始化,因为不同数据集的物体形状分布不一样。
第三个坑是推理时的输入归一化。YOLACT++用的是ImageNet的均值和方差,即mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]。如果你忘了做归一化,或者用了别的均值和方差,推理结果会差很多。这个在训练和推理时要保持一致,不然会有分布偏移。
第四个坑是NMS的IoU阈值。默认是0.5,但如果你的场景里物体密集,0.5可能会误删。可以调到0.6或者0.7,但要注意,调太高会导致重复检测。我一般会在验证集上画PR曲线,找一个F1最高的阈值。
第五个坑是显存管理。YOLACT++训练时显存占用比较大,尤其是输入分辨率高的时候。如果显存不够,可以试试梯度累积,用小的batch size累积几次梯度再更新。但要注意,梯度累积会改变BN的统计量,如果用了BN,最好同步调整BN的momentum。
这个项目后续还可以往几个方向扩展:一是结合Transformer结构,用注意力机制来增强原型mask的表达能力;二是做多任务学习,把深度估计或者光流预测和实例分割一起训,共享特征;三是做半监督或者自监督,减少对标注数据的依赖。这些方向目前都有一些论文在探索,但离工程落地还有距离。