昨天深夜调一个分割模型,输入尺寸改到512x512后,mIoU直接从0.78掉到0.62。盯着输出张量看了半小时才发现,原来是卷积层padding没跟着调整,特征图尺寸对不上,最后上采样时边缘信息全乱了。这种细节问题在分割任务里太常见了,今天就来聊聊语义分割和实例分割那些实战中的门道。

语义分割的核心:像素级分类

语义分割的本质是给每个像素打标签。FCN(全卷积网络)把全连接层换成卷积层,让网络能接受任意尺寸输入,这是现代分割模型的起点。但FCN的输出比较粗糙,边缘细节丢失严重。

后来U-Net加了跳跃连接,把浅层特征和深层特征拼接起来。注意这里有个坑:拼接前要确保通道数对齐。我见过有人直接concat,结果训练时loss震荡得厉害。

# 典型的跳跃连接实现
def forward(self, x):
    # 编码器部分
    enc1 = self.enc1(x)    # 浅层特征,细节多
    enc2 = self.enc2(enc1) # 中间层
    enc3 = self.enc3(enc2) # 深层特征,语义强
    
    # 解码器部分
    dec3 = self.dec3(enc3)
    # 关键在这里:先调整enc2的通道数再拼接
    enc2_adjusted = self.conv_adjust(enc2)  # 不加这个会出问题
    dec2_input = torch.cat([dec3, enc2_adjusted], dim=1)
    dec2 = self.dec2(dec2_input)
    
    # 继续上采样...
    return final_output

DeepLab系列用了空洞卷积扩大感受野,但计算量要注意。V3+版本那个ASPP模块(空洞空间金字塔池化)效果确实好,不过部署到边缘设备时得考虑内存占用。

实例分割:既要分割也要区分

Mask R-CNN在Faster R-CNN基础上加了个mask分支,这个设计很巧妙。但训练时有个细节:ROI Align比ROI Pooling效果好,因为避免了两次量化误差。实际部署时如果硬件不支持双线性插值,得自己实现个轻量版本。

YOLACT这类单阶段模型速度快,但小目标分割质量一般。它的原型生成和mask系数预测是并行的,训练时要注意两个分支的loss平衡。我试过调整权重系数,发现mask_loss_weight设到1.5时效果比较均衡。

实战调试经验

输入归一化要用训练集的均值和标准差,这个很多人会忽略。有一次我用ImageNet的统计量归一化医学图像,结果模型根本不收敛。

数据增强方面,随机裁剪和颜色抖动对分割任务很有效,但要注意裁剪不能把目标物体切没了。我一般会先统计标注框尺寸,设定合适的裁剪范围。

损失函数的选择:交叉熵最常用,但对于类别不平衡的数据,Dice Loss或Focal Loss更好用。不过Dice Loss训练初期可能不稳定,可以先用交叉熵训几轮再切换。

# 混合损失函数的写法
class HybridLoss(nn.Module):
    def __init__(self, alpha=0.5):
        super().__init__()
        self.alpha = alpha
        self.ce = nn.CrossEntropyLoss(ignore_index=255)
        self.dice = DiceLoss()
        
    def forward(self, pred, target):
        # 前期主要靠CE,后期Dice贡献增大
        ce_loss = self.ce(pred, target)
        dice_loss = self.dice(pred, target)
        # 这里可以加个动态权重,我一般epoch>10后让alpha从0.5慢慢升到0.7
        total_loss = (1 - self.alpha) * ce_loss + self.alpha * dice_loss
        return total_loss

部署时的注意事项

ONNX导出时要注意opset版本,有些自定义操作(如可变形卷积)需要特定版本支持。TensorRT加速时,如果模型有动态尺寸输入,记得显式指定优化profile。

内存优化方面,尝试过把float32换成float16,推理速度能提升30%左右,但某些边缘设备上精度损失明显。建议先做量化感知训练,再导出INT8模型。

个人建议

别盲目追求新模型。很多场景下,好好调参的U-Net比没调好的最新SOTA更管用。工业项目里,稳定性和推理速度往往比那1-2个点的精度提升更重要。

多看看中间特征图。用TensorBoard或简单的可视化脚本,观察不同层的输出,能帮你快速定位问题在哪一层。特别是跳跃连接前后,特征对齐没有一目了然。

保持数据一致性。训练、验证、测试集的数据预处理必须完全一致,我吃过亏:验证时用了不同的resize方法,指标虚高,上线后效果差一截。

最后,分割任务很吃数据。没有足够标注数据时,试试半监督方法,或者用预训练模型做迁移学习。但要注意领域差异,用Cityscapes预训练的模型直接去分割卫星图像,效果肯定打折扣。

分割模型调试是个细致活,从数据管道到损失函数,每个环节都可能藏坑。多写测试用例验证数据流,养成可视化中间结果的习惯,这些时间不会白花。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐