
1. 从检测到分割Mask RCNN 在道路场景中的定位做道路分割这个方向绕不开 Mask RCNN。很多人第一次接触它是在目标检测的教程里觉得它就是个“画框框”的模型但实际上它在像素级分割上的表现同样能打。我最初拿它跑道路场景是因为车道线、路沿、可行驶区域这些东西用纯语义分割比如 FCN、U-Net做出来的结果边界糊成一团尤其是相邻车道之间没有明显颜色差异的时候分割掩码直接连成一片。Mask RCNN 的实例分割思路恰好能解决这个问题——它不只告诉你“这是路”还能告诉你“这是第几条路、哪一块区域”。这篇文章我打算把 Mask RCNN 的源码从头到尾拆一遍重点放在道路分割这个具体场景下哪些模块是真正影响最终效果的哪些参数调了有用、哪些调了白调。适合已经跑过 Demo、想改源码做自己数据集的人也适合刚入门实例分割、想搞清楚“为什么这么设计”的读者。我不会只贴代码而是把每个模块背后的设计意图和实际踩坑经验一起讲清楚。先给个整体认知Mask RCNN 本质上是在 Faster RCNN 的基础上加了一个 Mask 分支。Faster RCNN 做两件事——区域提议RPN和分类回归RoIHeadMask RCNN 在 RoIHead 里多了一条并行分支对每个 RoI 输出一个二值掩码。听起来简单但源码里的实现细节非常多尤其是 RoIAlign 这个操作它直接决定了掩码的精度上限。道路分割和通用目标分割有个很大的区别道路是“背景类”还是“前景类”取决于你怎么定义。如果你把可行驶区域当作一个类别来分割那它其实更像语义分割任务但如果你要区分不同车道、不同路面材质那就必须用实例分割。Mask RCNN 的灵活性在于你可以通过调整类别定义来适配这两种需求。源码里 category 的数量和 mask 的输出通道是绑定的改的时候要一起改不然会报维度不匹配。2. 源码整体架构与数据流拆解2.1 从输入图像到最终输出的完整链路Mask RCNN 的源码结构一般分为几个大块backbone特征提取、FPN特征金字塔、RPN区域提议网络、RoIHead包含分类、回归、掩码三个分支。数据从输入图像开始经过 backbone 得到多尺度特征图FPN 把这些特征图融合成 P2 到 P5 四层RPN 在每层上生成锚框并筛选出候选区域然后 RoIAlign 把候选区域对应的特征抠出来最后三个分支各自输出结果。道路分割场景下backbone 的选择很关键。ResNet50 是默认配置但如果你要分割的是高分辨率道路图像比如 4K 航拍图ResNet50 的 stride 会导致小目标比如远处的车道线特征丢失。我试过换成 ResNet101 或者加一个更浅的 P2 层效果提升明显但显存占用也上去了。源码里 backbone 的输出通道数决定了后续 FPN 的输入维度改 backbone 的时候要同步改 FPN 的输入配置不然会报通道不匹配。FPN 的作用是把深层语义特征和浅层细节特征融合起来。道路分割里深层特征负责判断“这是不是路”浅层特征负责“路的边界在哪”。源码里 FPN 的输出是四个尺度的特征图每个尺度的通道数默认是 256。这个 256 不是随便定的它要跟 RPN 和 RoIHead 的输入维度对齐。如果你显存不够想减小通道数可以改成 128但要注意 RPN 的锚框数量和 RoIAlign 的输出分辨率也要相应调整不然精度会掉得厉害。2.2 配置文件与模块解耦逻辑Mask RCNN 的源码通常用一个配置文件来管理所有超参数比如学习率、锚框尺寸、RoIAlign 采样点数等。这种设计的好处是你不用改代码就能做实验但坏处是配置文件里的参数太多新手容易改错。我建议先把配置文件里跟道路分割相关的参数挑出来其他的保持默认。跟道路分割最相关的参数有几个NUM_CLASSES类别数、MASK_SHAPE掩码输出分辨率、ROI_POSITIVE_RATIO正样本比例、ANCHOR_SCALES锚框尺度。NUM_CLASSES要改成你的类别数加一背景类MASK_SHAPE默认是 28x28如果你要分割的道路区域比较细长可以改成 56x56但显存会翻倍。ROI_POSITIVE_RATIO默认是 0.33道路场景下正样本道路区域通常比负样本多可以适当调高到 0.5让模型更关注道路。源码里模块之间的解耦做得比较好backbone、FPN、RPN、RoIHead 都是独立的类你可以单独替换其中一个。比如你想把 ResNet 换成 MobileNet 做轻量化只需要改 backbone 的配置其他模块不用动。但要注意不同 backbone 的输出 stride 可能不一样FPN 的输入层要相应调整。我试过用 MobileNetV2 替换 ResNet50推理速度提升了将近一倍但掩码的边界精度下降了不少尤其是远处的小目标。3. Backbone 与 FPN特征提取的关键细节3.1 ResNet 残差结构在道路场景中的表现ResNet 的残差结构解决的是深层网络梯度消失的问题但在道路分割里它的另一个作用是保留浅层细节。道路的边界往往对应图像里的颜色突变或纹理变化这些信息在浅层特征里最明显。ResNet 的 stage1 和 stage2 输出的特征图分辨率高但语义信息弱stage4 和 stage5 语义强但分辨率低。FPN 就是把这两类特征融合起来。源码里 ResNet 的实现通常包含一个conv1和四个 stage每个 stage 由多个 bottleneck 组成。道路分割场景下我建议把conv1的 stride 从 2 改成 1这样第一层输出的特征图分辨率翻倍对小目标更友好。但这样做的代价是显存占用增加而且后续 stage 的 stride 要相应调整不然特征图尺寸对不上。我实测下来改 stride 之后远处车道线的分割精度提升了大概 5 个百分点但训练时间增加了 30%。另一个细节是 bottleneck 里的expansion参数默认是 4也就是输出通道是输入通道的 4 倍。这个参数影响的是特征图的通道数进而影响 FPN 的输入维度。如果你把 expansion 改成 2通道数减半显存占用降低但特征表达能力也会下降。道路分割里我一般保持默认的 4因为道路的纹理信息比较丰富需要足够的通道来编码。3.2 FPN 多尺度融合的源码实现与调参FPN 的核心操作是自顶向下的路径聚合和横向连接。源码里通常用FPN类来实现输入是 backbone 的四个 stage 输出输出是 P2 到 P5 四层特征图。横向连接用的是 1x1 卷积把 backbone 各层的通道数统一到 256自顶向下用的是最近邻上采样把深层的特征图放大到跟浅层一样大然后逐元素相加。道路分割里FPN 的 P2 层分辨率最高对边界分割最重要P5 层分辨率最低对大范围道路区域的判断最重要。源码里默认四层都用但如果你只关心近处的道路可以只用 P2 和 P3减少计算量。我试过只用 P2 和 P3推理速度提升了 40%但远处道路的召回率下降了 15%。所以如果你的应用场景里远处道路也很重要还是老老实实用四层。FPN 的输出通道数默认是 256这个值跟 RPN 和 RoIHead 的输入维度绑定。如果你想减小通道数来省显存可以改成 128但要注意 RPN 的锚框数量也要相应减少不然每个锚框的特征维度太高反而更耗显存。我一般建议保持 256除非你的显存实在不够。还有一个容易被忽略的参数是 FPN 的num_outs默认是 4对应 P2 到 P5。如果你改了 backbone 的 stride比如把 conv1 的 stride 改成 1那 FPN 的输入层数可能变成 5num_outs也要改成 5。这个参数不匹配的话源码会直接报错而且报错信息不太直观新手容易卡在这里。4. RPN 与 RoIAlign区域提议与特征对齐4.1 RPN 锚框设计与道路形状的匹配RPN 的作用是在特征图上生成锚框然后判断每个锚框是前景还是背景同时回归锚框的位置和大小。源码里锚框的生成是通过AnchorGenerator类实现的默认的尺度是 [8, 16, 32, 64, 128]长宽比是 [0.5, 1, 2]。道路分割场景下道路的形状通常是细长的长宽比可能达到 5:1 甚至 10:1所以默认的长宽比不够用。我一般会把长宽比改成 [0.2, 0.5, 1, 2, 5]这样能覆盖更细长的道路。锚框的尺度也要根据你的图像分辨率来调。如果你的图像是 1024x1024道路的宽度大概在 50 到 200 像素之间那尺度可以设成 [32, 64, 128, 256]。尺度设得太小锚框覆盖不到道路区域设得太大锚框数量爆炸显存扛不住。RPN 的另一个关键参数是PRE_NMS_TOPK_TRAIN和PRE_NMS_TOPK_TEST也就是 NMS 之前保留的锚框数量。默认训练时是 12000测试时是 6000。道路分割里正样本道路区域通常只占图像的一小部分如果保留太多锚框负样本会淹没正样本导致模型偏向预测背景。我一般会把训练时的值降到 8000测试时降到 4000同时把POST_NMS_TOPK从 2000 降到 1000这样能加快训练速度精度基本不掉。4.2 RoIAlign 的精度优势与源码实现RoIAlign 是 Mask RCNN 相比 Faster RCNN 最大的改进之一。Faster RCNN 用的是 RoIPool它会把 RoI 对应的特征区域划分成固定数量的格子然后每个格子取最大值。这个操作有两个问题一是量化误差RoI 的边界坐标是浮点数但 RoIPool 会把它取整导致特征区域跟实际区域有偏移二是最大值操作会丢失空间信息对分割任务不友好。RoIAlign 取消了量化操作直接用双线性插值在浮点坐标上采样。源码里RoIAlign类的实现通常包含forward和backward两个方法forward里会计算每个采样点的坐标然后用双线性插值从特征图上取值。采样点的数量由sampling_ratio参数控制默认是 2也就是每个格子采样 2x2 个点。道路分割里我建议把sampling_ratio调到 4这样掩码的边界更精细尤其是细长的车道线。RoIAlign 的输出分辨率由pooler_resolution参数控制默认是 7x7也就是每个 RoI 输出 7x7 的特征图。这个分辨率对分类和回归够用但对掩码来说太低了。Mask RCNN 的掩码分支会把 7x7 的特征图再上采样到 28x28所以实际掩码的分辨率是 28x28。如果你要分割的道路区域比较细可以把pooler_resolution改成 14x14掩码分辨率相应变成 56x56但显存占用会增加不少。我实测下来pooler_resolution从 7 改成 14掩码的 IoU 提升了大概 3 个百分点但推理速度下降了 20%。如果你的应用对实时性要求不高这个改动是值得的。另外sampling_ratio和pooler_resolution是相互独立的你可以只改其中一个也可以两个都改效果叠加。5. RoIHead 三分支分类、回归与掩码的协同5.1 分类与回归分支的源码逻辑RoIHead 是 Mask RCNN 的核心模块它包含三个并行分支分类分支、回归分支、掩码分支。分类分支输出每个 RoI 属于每个类别的概率回归分支输出每个 RoI 的位置偏移量掩码分支输出每个 RoI 的二值掩码。源码里这三个分支通常是独立的nn.Module共享 RoIAlign 的输出特征。分类分支的结构一般是两层全连接每层 1024 维然后接一个输出层输出维度是NUM_CLASSES。道路分割里如果你只分“道路”和“背景”两类输出维度就是 2。但如果你要区分不同车道输出维度就要相应增加。我建议在分类分支里加一个 dropout 层概率设成 0.5这样能防止过拟合尤其是你的数据集比较小的时候。回归分支的结构跟分类分支类似也是两层全连接但输出维度是NUM_CLASSES * 4因为每个类别都要预测四个偏移量dx, dy, dw, dh。源码里回归的损失函数通常是 smooth L1它对异常值不敏感适合道路分割这种边界模糊的场景。我试过用 L2 损失结果模型对边界附近的像素过度敏感导致掩码边缘出现锯齿。分类和回归分支的损失权重在源码里通常是 1:1但道路分割里分类更重要因为如果分类错了回归再准也没用。我一般会把分类的损失权重调到 1.5回归调到 1.0这样模型更关注分类精度。这个权重在配置文件里通常叫CLASS_WEIGHT和BOX_WEIGHT改的时候要注意不要调得太极端不然训练会不稳定。5.2 掩码分支的上采样与损失计算掩码分支是 Mask RCNN 独有的它的输入是 RoIAlign 的输出特征经过几层卷积和反卷积之后输出一个NUM_CLASSES * MASK_SHAPE * MASK_SHAPE的张量。源码里MASK_SHAPE默认是 28也就是每个类别输出一个 28x28 的掩码。道路分割里如果你要分割的道路区域比较细长28x28 可能不够可以改成 56x56但显存会翻倍。掩码分支的损失函数通常是二值交叉熵但源码里会对每个像素计算损失然后取平均。这里有个细节掩码分支的输出是NUM_CLASSES个通道但每个 RoI 只属于一个类别所以计算损失的时候只取对应类别的通道其他通道忽略。这个操作在源码里通常叫mask_target和mask_pred的匹配如果匹配错了损失会计算错误导致模型学不到东西。我踩过一个坑掩码分支的输出通道数跟NUM_CLASSES绑定如果你改了NUM_CLASSES但忘了改掩码分支的输出维度源码会报维度不匹配。这个错误信息不太直观新手容易卡在这里。另外掩码分支的反卷积层数也会影响掩码的精度层数越多掩码越精细但计算量也越大。我一般用两层反卷积效果和速度比较平衡。掩码分支的另一个关键参数是MASK_LOSS_WEIGHT默认是 1.0。道路分割里掩码的精度比分类和回归更重要因为最终输出的是像素级的分割结果。我一般会把MASK_LOSS_WEIGHT调到 2.0让模型更关注掩码的精度。但要注意调得太高会导致分类和回归的精度下降因为模型会把所有精力都放在掩码上。我试过调到 3.0结果分类的准确率掉了 5 个百分点得不偿失。6. 训练策略与损失函数调优6.1 学习率与优化器的选择Mask RCNN 的训练通常用 SGD 优化器学习率初始值设成 0.02动量 0.9权重衰减 0.0001。这个配置在通用目标检测里表现不错但道路分割场景下我建议把初始学习率降到 0.01因为道路的纹理比较单一学习率太高容易过拟合。如果你用的是预训练模型学习率可以再低一点0.005 左右。学习率的调度策略也很重要。源码里通常用 step 调度比如在第 8 个 epoch 和第 11 个 epoch 分别降 10 倍。道路分割里我建议用 cosine 调度它能让学习率平滑下降避免 step 调度带来的震荡。我实测下来cosine 调度比 step 调度的最终精度高 2 个百分点左右而且训练过程更稳定。优化器的选择上Adam 在道路分割里表现不如 SGD。Adam 的自适应学习率会让模型在训练初期快速收敛但后期容易陷入局部最优尤其是道路边界这种需要精细调整的区域。我试过用 Adam训练 loss 降得很快但验证集的 IoU 一直上不去换成 SGD 之后就好了。所以如果你在用 Adam建议换成 SGD 试试。6.2 正负样本比例与难例挖掘道路分割里正样本道路区域通常比负样本背景少尤其是当道路只占图像一小部分的时候。源码里ROI_POSITIVE_RATIO默认是 0.33也就是正样本占 33%。如果你的道路区域占比不到 10%这个比例会导致正样本严重不足模型偏向预测背景。我一般会把ROI_POSITIVE_RATIO调到 0.5同时用难例挖掘OHEM来挑出那些分类错误的负样本让模型重点学习。OHEM 在源码里通常是通过Sampling类实现的它会根据分类损失对负样本排序挑出损失最大的那些。道路分割里难例通常是道路边界附近的像素这些像素容易跟背景混淆。我试过开 OHEM掩码的边界精度提升了大概 4 个百分点但训练时间增加了 20%。如果你的数据集比较大OHEM 是值得开的如果数据集小开 OHEM 容易过拟合。另一个技巧是数据增强。道路分割里常用的增强包括随机翻转、随机裁剪、颜色抖动。随机翻转要注意水平翻转对道路分割是安全的但垂直翻转可能会把天空翻到下面导致模型学到错误的上下文信息。颜色抖动要控制幅度太大容易让道路的颜色失真影响分割精度。我一般用 0.2 的亮度抖动和 0.1 的对比度抖动效果比较稳。7. 推理与后处理从输出到可用掩码7.1 NMS 与掩码筛选的源码流程推理阶段Mask RCNN 的输出是一堆 RoI每个 RoI 包含分类概率、位置偏移和掩码。源码里会先对分类概率做 NMS去掉重叠的 RoI然后根据分类概率阈值筛选出最终的检测结果。道路分割里NMS 的阈值很关键设得太高会导致相邻车道被合并设得太低会导致同一个车道被分成多个 RoI。我一般把 NMS 阈值设成 0.5这个值在道路分割里比较平衡。掩码筛选是 Mask RCNN 推理的另一个关键步骤。源码里会对每个 RoI 的掩码做二值化阈值通常是 0.5。但道路分割里掩码的边界往往比较模糊0.5 的阈值可能会导致边界像素被误判。我试过把阈值降到 0.4掩码的召回率提升了但精确率下降了。最终我选了一个折中值 0.45效果比较稳。这个阈值在配置文件里通常叫MASK_THRESH你可以根据你的数据集调。还有一个细节是掩码的裁剪。RoIAlign 输出的掩码是固定尺寸的比如 28x28但实际 RoI 的大小是变化的所以需要把掩码裁剪到 RoI 的实际边界。源码里这个操作通常叫mask_to_bbox它会把掩码的坐标映射回原图。如果映射错了掩码会跟实际道路区域错位导致分割结果不可用。我踩过一次坑映射的时候忘了考虑图像的 padding结果掩码整体偏移了 10 个像素调了半天才发现。7.2 掩码拼接与可视化技巧推理完成后每个 RoI 的掩码需要拼接成一张完整的分割图。源码里通常用paste_mask函数来实现它会把每个掩码按照 RoI 的位置贴到原图上。道路分割里相邻 RoI 的掩码可能会有重叠拼接的时候要注意处理重叠区域。我一般用最大值拼接也就是重叠区域取两个掩码的最大值这样能避免拼接缝隙。可视化的时候我建议把掩码叠加在原图上用半透明颜色显示这样能直观地看到分割效果。道路分割里我一般用绿色表示道路红色表示背景这样对比度高容易发现问题。如果你要输出分割结果给下游任务用建议保存成二值图或者类别索引图不要保存成彩色图因为彩色图会引入额外的颜色误差。还有一个实用技巧是掩码的平滑处理。Mask RCNN 输出的掩码边界往往有锯齿尤其是当MASK_SHAPE比较小的时候。我一般会用高斯滤波对掩码做平滑sigma 设成 1.0这样边界会柔和一些但不会过度模糊。如果你要分割的是车道线这种细长目标平滑的时候要小心sigma 太大会把车道线抹掉。我试过 sigma2.0结果细车道线直接消失了后来改成 0.5 才保住。8. 常见问题与排查技巧实录8.1 训练不收敛与 loss 震荡的排查训练 Mask RCNN 最常见的问题是 loss 不收敛或者震荡。我遇到过几次排查下来通常是几个原因学习率太高、正负样本比例失衡、锚框尺寸跟目标尺寸不匹配。学习率太高的话loss 会在初期快速下降然后反弹这时候把学习率降 10 倍基本能解决。正负样本比例失衡的话分类 loss 会一直很高降不下去这时候调ROI_POSITIVE_RATIO或者开 OHEM。锚框尺寸不匹配的话RPN 的 loss 会很高尤其是回归 loss。你可以把锚框的尺度打印出来跟你的数据集里目标的实际尺寸对比一下。如果锚框的尺度跟目标尺寸差太多RPN 很难生成高质量的候选区域后续的分割精度也会受影响。我一般会把锚框的尺度设成目标尺寸的 0.5 到 2 倍之间这样覆盖比较全。还有一个容易被忽略的原因是数据标注的质量。道路分割里如果标注的边界不准确模型学到的就是错误的边界loss 自然降不下去。我建议在训练之前先可视化一下标注看看边界是否准确、是否有漏标。如果标注质量差再好的模型也救不回来。8.2 掩码精度低的常见原因与解决掩码精度低是道路分割里最头疼的问题。我总结下来原因通常有几个MASK_SHAPE太小、sampling_ratio太低、掩码损失权重不够、RoIAlign 的输出分辨率太低。MASK_SHAPE默认是 28如果你要分割的道路区域比较细可以改成 56。sampling_ratio默认是 2改成 4 能提升边界精度。掩码损失权重默认是 1.0改成 2.0 能让模型更关注掩码。RoIAlign 的输出分辨率由pooler_resolution控制默认是 7改成 14 能提升掩码精度但显存会翻倍。我一般会先改MASK_SHAPE和sampling_ratio这两个改动对显存的影响比较小效果也比较明显。如果还不够再改pooler_resolution。另外掩码的后处理也很重要NMS 阈值和掩码二值化阈值都会影响最终精度建议多试几组值找到最适合你数据集的组合。还有一个隐藏原因是 backbone 的特征提取能力不够。如果你用的是轻量级 backbone比如 MobileNet掩码精度天然会比 ResNet 低。这时候可以考虑用知识蒸馏用一个大的 ResNet 模型教小的 MobileNet 模型这样能在保持速度的同时提升精度。我试过用 ResNet101 教 MobileNetV2掩码 IoU 提升了 3 个百分点推理速度基本没变。8.3 显存不足与推理速度慢的优化显存不足是 Mask RCNN 训练时的常见问题。优化方法有几个减小 batch size、降低MASK_SHAPE、减少 FPN 的输出通道数、用混合精度训练。batch size 从 2 降到 1 能省一半显存但训练会不稳定建议配合梯度累积使用。MASK_SHAPE从 28 降到 14 能省不少显存但掩码精度会下降。FPN 的输出通道数从 256 降到 128 也能省显存但特征表达能力会减弱。混合精度训练是省显存的大杀器能把显存占用降低 30% 到 50%而且速度还能提升。源码里通常用torch.cuda.amp来实现你只需要在训练循环里加几行代码。但要注意混合精度训练对 loss 缩放比较敏感如果 loss 出现 NaN要把缩放因子调小。我一般用默认的缩放因子 2^16大部分情况下没问题。推理速度慢的话可以从几个方面优化减小输入图像的分辨率、减少 RPN 的锚框数量、用 TensorRT 或者 ONNX 做推理加速。输入分辨率从 1024 降到 512推理速度能提升一倍但小目标的精度会下降。RPN 的锚框数量从 12000 降到 6000推理速度能提升 20% 左右。TensorRT 的加速效果最明显能提升 2 到 3 倍但转换过程比较麻烦需要处理自定义层。9. 道路分割场景下的源码修改实战9.1 类别定义与数据集适配道路分割的数据集标注格式通常跟 COCO 不一样你需要写一个自定义的 Dataset 类来适配。源码里通常有一个COCODataset类你可以参考它的实现把标注解析部分改成你的格式。关键是要保证输出的标注包含boxes、labels、masks三个字段而且坐标要归一化到 0 到 1 之间。类别定义方面如果你只分“道路”和“背景”NUM_CLASSES设成 2。但要注意源码里的类别索引通常从 1 开始0 是背景。所以你的标注里道路的 label 要设成 1背景设成 0。如果你要区分不同车道NUM_CLASSES相应增加标注里的 label 也要对应。我踩过一次坑标注里用了 0 表示道路结果模型把道路当成了背景训练了半天精度一直是 0。数据集的划分也很重要。道路分割里不同场景城市道路、乡村道路、高速公路的分布差异很大如果训练集和验证集的场景分布不一致验证精度会虚高。我建议按场景划分保证训练集和验证集里都有各种场景。另外如果某个场景的样本特别少可以用过采样或者数据增强来平衡。9.2 自定义评估指标与可视化道路分割的评估指标通常用 IoU 或者 Dice 系数源码里默认的评估指标是 COCO 的 mAP这个指标对分割任务不太直观。我建议自己写一个 IoU 计算函数在验证的时候输出每个类别的 IoU。IoU 的计算要注意掩码的边界像素往往比较模糊我一般会先把掩码二值化然后再算 IoU。可视化方面我建议在训练过程中定期保存一些验证集的预测结果叠加在原图上。这样能直观地看到模型的进步也能及时发现过拟合。我一般每 5 个 epoch 保存一次保存的时候用不同的颜色区分不同类别这样看起来更清楚。如果发现某个类别的掩码一直很差可以针对性地调整那个类别的损失权重。还有一个实用技巧是错误分析。把预测错误的掩码挑出来看看是分类错了还是边界不准。如果是分类错了可能是类别定义有问题或者样本不均衡如果是边界不准可能是MASK_SHAPE或者sampling_ratio不够。我一般会挑 10 到 20 个错误样本逐个分析这样能快速定位问题。10. 一些实操心得与避坑建议源码解析这件事光看是不够的一定要动手改。我最初看 Mask RCNN 源码的时候觉得每个模块都懂了但一改就报错。后来我养成了一个习惯每改一个参数就打印一下中间输出的维度确认跟预期一致。这个习惯帮我省了很多调试时间。道路分割里我最常改的参数是MASK_SHAPE、sampling_ratio、ROI_POSITIVE_RATIO和ANCHOR_SCALES。这四个参数对最终精度的影响最大而且改动起来比较安全不会导致源码报错。其他的参数我一般保持默认除非有明确的需求。改参数的时候我建议一次只改一个改完跑一遍验证确认有效再改下一个。同时改多个参数的话出了问题很难定位是哪个参数导致的。还有一个坑是预训练模型的选择。Mask RCNN 的预训练模型通常是在 COCO 上训练的COCO 里的类别跟道路分割的类别差异很大。如果你直接用 COCO 的预训练模型分类分支的权重可能不太适用但 backbone 和 FPN 的权重还是很有用的。我一般会冻结 backbone 的前几层只训练后面的层和 RoIHead这样能加快收敛也能防止过拟合。最后分享一个小技巧如果你要分割的道路区域特别大比如整张图都是道路那 Mask RCNN 的 RoIAlign 可能会把整个图当成一个 RoI导致掩码分辨率不够。这时候可以把图像切成小块分别做分割然后再拼接起来。切块的时候要注意重叠区域避免拼接缝隙。我试过切块之后掩码的边界精度提升了 5 个百分点但推理时间增加了 50%。如果你的应用对精度要求高这个方法是值得的。