资讯动态

语义分割实战指南:从像素级分类到模型部署的完整解析

发布时间:2026/10/14 7:49:06 来源:尧图企业网站定制
1. 从像素到语义为什么我们需要语义分割第一次接触图像语义分割这个概念很多人脑子里冒出来的第一个问题就是这玩意儿跟目标检测到底有啥区别我直接框出来不就行了吗为什么非得一个像素一个像素地去分类这个问题我当年也问过自己。后来在实际项目里踩了坑才明白目标检测给你的是一个矩形框告诉你“这里有一只猫”但框里面其实混着背景、猫的耳朵边缘、可能还有半截沙发腿。如果你要做的是自动驾驶里的可行驶区域判断或者医学影像里的肿瘤边界勾画矩形框那点精度根本不够用。语义分割要解决的就是这个“最后一公里”的问题——它要把图像里每一个像素都贴上标签告诉你这个像素属于猫、那个像素属于马路、另一个像素属于天空。说白了语义分割是像素级别的分类任务。输入一张RGB图像输出一张同样大小的标签图每个像素位置上的值代表它所属的类别。跟图像分类整张图一个标签和目标检测一张图若干个框加类别相比语义分割的信息密度是最高的因为它保留了完整的空间结构。这个任务难在哪难在“语义”两个字。同一个物体在不同光照、不同角度、不同遮挡条件下像素值可能天差地别但它们的语义标签是一样的。猫的毛色有黑有白有花但都是猫。马路有柏油的有水泥的有被水淹了一半的但都是可行驶区域。模型要学会的是忽略表观差异抓住语义本质。这对网络的特征提取能力提出了很高的要求。那语义分割到底能干什么我列几个我实际接触过的场景你就明白了。自动驾驶里需要把画面分割成道路、车辆、行人、交通标志、天空、建筑物等类别下游的规划模块才能做决策。医学影像里需要把CT或MRI切片中的器官、病灶、血管分割出来辅助医生做诊断和手术规划。遥感图像里需要把地表覆盖分成建筑、农田、水体、林地等类型用于城市规划和环境监测。工业质检里需要把产品表面的缺陷区域精确分割出来判断缺陷面积和形状是否超标。适合谁来学如果你已经了解卷积神经网络的基本原理知道卷积、池化、上采样是怎么回事那就可以直接上手语义分割了。如果你连CNN都还没搞明白建议先去补一下基础不然看分割网络的结构会一头雾水。另外语义分割对工程实现能力要求比较高数据标注、数据增强、损失函数设计、评价指标计算每一个环节都有不少细节需要打磨。2. 语义分割的核心概念拆解2.1 语义分割、实例分割与全景分割的区别这三个概念经常被混在一起说但它们的任务定义和输出形式有本质区别。我用一个生活化的类比来解释假设你面前有一盘水果里面有苹果、香蕉、橘子其中苹果有两个、香蕉有一根、橘子有三个。语义分割的任务是把每个像素标记为“苹果”“香蕉”“橘子”或“背景”。它不区分是哪一个苹果所有苹果像素的标签都是“苹果”。输出是一张类别标签图。实例分割的任务是不仅要知道像素属于哪一类还要知道它属于哪一个具体个体。两个苹果要分别标记为“苹果1”和“苹果2”。输出是每个实例的掩码加类别。全景分割则是把语义分割和实例分割统一起来对于“可数”的物体人、车、苹果做实例分割对于“不可数”的背景区域天空、马路、草地做语义分割。输出是一张统一的标签图每个像素既有类别ID又有实例ID。我刚开始学的时候总觉得实例分割更高级后来做项目才发现很多场景下语义分割就够用了。比如做路面分割你不需要区分是第几条车道线只需要知道哪些像素是路面就行。选哪种任务完全取决于你的下游需求。2.2 从FCN到DeepLab语义分割网络演进的关键节点语义分割真正进入深度学习时代标志性事件是2015年FCNFully Convolutional Network的提出。在FCN之前大家做分割基本是靠手工特征加图割算法效果惨不忍睹。FCN的核心贡献在于把分类网络比如VGG最后的全连接层换成卷积层使得网络可以接受任意尺寸的输入并输出同样空间尺寸的预测图。但FCN有个致命问题经过多次下采样之后特征图的分辨率降得很低直接上采样回来的结果非常粗糙边缘像狗啃的一样。为了解决这个问题后续的网络各显神通。编码器-解码器结构是最直观的思路。编码器负责逐步下采样、提取语义特征解码器负责逐步上采样、恢复空间分辨率。U-Net是这方面的经典代表它通过跳跃连接把编码器的高分辨率特征直接拼接到解码器的对应层让网络在恢复分辨率的时候还能看到原始的细节信息。这个结构在医学图像分割里几乎是标配。空洞卷积是另一条路。DeepLab系列网络的核心思想是既然下采样会丢分辨率那我干脆不下采样那么多次用空洞卷积来扩大感受野。空洞卷积就是在卷积核的元素之间插入空洞使得同样大小的卷积核能覆盖更大的区域。DeepLabv3结合了空洞卷积和编码器-解码器结构在精度和速度之间取得了很好的平衡。注意力机制是近几年的热点。分割任务里不同位置的特征重要性差异很大边缘区域的像素需要更精细的处理。通过引入通道注意力或空间注意力网络可以自适应地调整不同特征的权重。Transformer结构进入分割领域之后基于自注意力的分割网络如SegFormer也展现出了很强的性能。2.3 评价指标IoU到底怎么算语义分割最常用的评价指标是IoUIntersection over Union也叫Jaccard指数。对于每一个类别IoU等于预测为该类且真实也为该类的像素数除以预测为该类或真实为该类的像素并集数。公式很简单IoU TP / (TP FP FN)。其中TP是真正例FP是假正例FN是假负例。但实际计算的时候有几个坑要注意。第一背景类要不要算进去通常背景类的像素占比很大如果把背景算进去整体IoU会被拉高掩盖前景类的问题。所以一般会分别报告每个前景类的IoU再取平均得到mIoUmean IoU。第二如果某个类别在真实标签里不存在但模型预测出来了这个类别的IoU怎么算通常的做法是跳过这个类别不纳入平均。第三对于多类别分割计算IoU的时候要逐类计算不能把所有类别混在一起算一个全局IoU。除了IoU还有像素准确率Pixel Accuracy和Dice系数。像素准确率就是预测正确的像素占总像素的比例这个指标在类别极不平衡的时候会严重虚高。比如一张图里95%是背景模型把所有像素都预测成背景准确率也有95%但实际毫无用处。Dice系数和IoU是单调等价的Dice 2*IoU / (1IoU)在医学图像分割里用得更普遍。3. 语义分割的数据准备与标注实操3.1 数据标注工具选型与标注规范制定语义分割的数据标注是整個流程里最耗时耗力的环节。我做过一个统计一张1024x1024的图像如果里面有十几个物体需要精细标注熟练的标注员大概需要15到30分钟。如果项目有几千张图这个成本是相当可观的。标注工具方面我实际用过的主要有LabelMe、CVAT和Label Studio。LabelMe是Python写的安装简单适合小规模标注和快速验证。CVAT功能更强大支持多人协作、视频标注、自动标注辅助适合团队使用。Label Studio的优点是支持多种标注类型分类、检测、分割、文本可以统一管理不同任务的数据。选工具的时候要考虑几个因素是否支持多边形标注语义分割通常用多边形而不是矩形框、是否支持导出为常见的分割格式如COCO JSON、Pascal VOC PNG、是否支持多人协作和审核流程、是否支持预标注用已有模型先跑一遍人工修正能大幅提升效率。标注规范是比工具更重要的事情。我踩过的坑是一开始没有制定详细的标注规范几个标注员对“边缘怎么算”“遮挡部分怎么标”“模糊区域算不算”的理解不一致导致标注数据质量参差不齐模型训练出来效果很差。后来我们制定了详细的规范文档包括每个类别的定义和边界案例、遮挡情况下的标注策略比如被遮挡超过50%的物体是否标注、最小标注尺寸小于多少像素的物体忽略、边缘像素的归属规则等。规范制定之后标注一致性大幅提升。3.2 数据增强策略让少量数据发挥最大价值语义分割的数据标注成本高所以数据增强是必不可少的。但分割的数据增强和分类不一样分类只增强图像就行分割必须同时对图像和标签图做完全相同的空间变换。常用的增强手段包括随机裁剪、随机翻转水平、垂直、随机旋转、随机缩放、颜色抖动亮度、对比度、饱和度、色调、高斯噪声、模糊等。其中几何变换必须同步作用于图像和标签颜色变换只作用于图像。我特别推荐的是随机裁剪加大尺寸训练。比如原图是1024x1024训练时随机裁剪512x512的patch。这样做的好处是第一变相增加了数据量第二小patch可以增大batch size训练更稳定第三模型见到更多局部场景泛化能力更强。但要注意裁剪的时候要保证每个类别在patch里都有足够的像素否则会出现某些patch全是背景的情况。我的做法是做一个类别平衡的采样器优先裁剪包含稀有类别的区域。另一个实用技巧是CutMix和Copy-Paste。CutMix是把一张图的某个区域抠出来贴到另一张图上标签也相应合并。Copy-Paste是把稀有类别的实例抠出来随机粘贴到其他图像上。这两种方法对小类别分割效果的提升非常明显。但要注意粘贴时的上下文合理性比如把一只猫粘贴到天空区域就很不合理会引入噪声。3.3 数据格式转换与预处理流水线语义分割的数据格式主要有几种PNG掩码图每个像素值代表类别ID、COCO JSON多边形坐标、Pascal VOC格式XML加PNG。不同框架对格式的要求不一样所以经常需要做格式转换。PNG掩码图是最通用的格式但要注意几个细节。第一PNG的位深要选对如果类别数少于256用8位就够了如果类别数超过256需要用16位。第二调色板PNG和灰度PNG要区分清楚有些工具生成的调色板PNG读出来是RGB值需要做映射才能得到类别ID。第三标签图的像素值必须是整数不能有插值产生的中间值。预处理流水线一般包括读取图像和标签、尺寸调整保持长宽比或直接resize、归一化减均值除标准差、转为Tensor、数据增强。我习惯用Albumentations库来做分割的数据增强它支持图像和掩码的同步变换API也很简洁。import albumentations as A import cv2 import numpy as np # 定义增强流水线 transform A.Compose([ A.RandomCrop(width512, height512), A.HorizontalFlip(p0.5), A.VerticalFlip(p0.3), A.RandomRotate90(p0.5), A.ColorJitter(brightness0.2, contrast0.2, saturation0.2, hue0.1, p0.5), A.GaussNoise(var_limit(10.0, 50.0), p0.3), A.Normalize(mean(0.485, 0.456, 0.406), std(0.229, 0.224, 0.225)), ]) # 同时变换图像和掩码 image cv2.imread(image.jpg) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) mask cv2.imread(mask.png, cv2.IMREAD_GRAYSCALE) augmented transform(imageimage, maskmask) aug_image augmented[image] aug_mask augmented[mask]注意使用Albumentations做增强时要确保掩码的插值方式为最近邻插值否则会产生不存在的类别ID。Normalize操作只对图像做不要对掩码做。4. 语义分割模型训练的关键环节4.1 损失函数选择交叉熵、Dice与Focal Loss的适用场景语义分割的损失函数选择直接影响到模型能不能收敛、小类别能不能学好。最基础的是逐像素交叉熵损失它对每个像素的预测概率和真实标签计算交叉熵然后对所有像素取平均。交叉熵的优点是梯度稳定、收敛快缺点是当类别极度不平衡时数量多的类别会主导损失小类别学不好。Dice Loss直接优化Dice系数它关注的是预测区域和真实区域的重叠程度对类别不平衡不敏感。在医学图像分割里病灶区域往往只占图像的很小一部分Dice Loss的效果通常比交叉熵好。但Dice Loss的梯度在预测和真实完全重合时会变得不稳定而且训练初期容易震荡。Focal Loss是交叉熵的改进版通过引入调制因子降低易分类样本的权重让模型聚焦于难分类的样本。在分割任务里Focal Loss对边缘像素和稀有类别的效果比较好。我的经验是组合使用效果最好。常见的组合是交叉熵加Dice Loss权重各占一半。交叉熵保证训练稳定Dice Loss提升小类别的分割质量。如果某个类别特别稀有可以再加一个Focal Loss专门针对这个类别。import torch import torch.nn as nn import torch.nn.functional as F class CombinedLoss(nn.Module): def __init__(self, ce_weight0.5, dice_weight0.5): super().__init__() self.ce_weight ce_weight self.dice_weight dice_weight self.ce nn.CrossEntropyLoss() def dice_loss(self, pred, target, smooth1e-6): # pred: [B, C, H, W] logits # target: [B, H, W] long tensor pred F.softmax(pred, dim1) target_onehot F.one_hot(target, num_classespred.shape[1]) target_onehot target_onehot.permute(0, 3, 1, 2).float() intersection (pred * target_onehot).sum(dim(2, 3)) union pred.sum(dim(2, 3)) target_onehot.sum(dim(2, 3)) dice (2. * intersection smooth) / (union smooth) return 1 - dice.mean() def forward(self, pred, target): ce_loss self.ce(pred, target) d_loss self.dice_loss(pred, target) return self.ce_weight * ce_loss self.dice_weight * d_loss4.2 学习率调度与优化器配置分割网络的训练对学习率比较敏感。学习率太大损失容易震荡不收敛学习率太小训练速度慢而且容易陷入局部最优。我一般用多项式衰减Poly Learning Rate或者余弦退火Cosine Annealing。多项式衰减的公式是lr base_lr * (1 - iter / max_iter)^power其中power通常取0.9。这个策略在分割任务里很常用因为它在训练后期学习率下降得比较平缓有利于模型精细调整。余弦退火则是让学习率按照余弦曲线从最大值降到最小值然后再周期性回升。这种策略的好处是可以帮助模型跳出局部最优但需要配合热重启Warm Restarts使用。优化器方面SGD加动量是经典选择泛化性能好但收敛慢。Adam收敛快但有时候泛化不如SGD。AdamW修正了Adam的权重衰减方式在分割任务里表现不错。我的建议是如果训练数据量大、训练轮次多用SGD加动量如果数据量小、需要快速验证用AdamW。还有一个细节是权重衰减。分割网络通常需要一定的正则化来防止过拟合权重衰减系数一般设在1e-4到1e-5之间。如果用了预训练权重权重衰减可以设小一点因为预训练权重本身已经包含了一定的正则化效果。4.3 预训练权重加载与微调策略语义分割的数据集通常不大从头训练很容易过拟合。所以加载预训练权重几乎是标配操作。预训练权重一般来自ImageNet分类任务因为分类任务的数据量大学到的特征泛化性好。加载预训练权重的时候要注意编码器部分比如ResNet、EfficientNet可以直接加载解码器部分通常是随机初始化。如果分割任务的类别数和预训练分类任务的类别数不一样最后的分类层需要重新初始化。微调策略上我一般分两个阶段。第一阶段冻结编码器只训练解码器学习率设大一点比如1e-3。这个阶段让解码器先学会如何利用编码器提取的特征。第二阶段解冻编码器整个网络一起训练学习率调小比如1e-4。这个阶段让编码器也适应分割任务的特点。如果数据量特别小比如只有几百张图可以考虑只微调编码器的最后几层前面的层保持冻结。这样可以进一步降低过拟合风险。实操心得加载预训练权重时经常遇到键名不匹配的问题。比如预训练模型用的是backbone.conv1.weight而你的模型用的是encoder.conv1.weight。这时候需要写一个映射函数把预训练权重的键名映射到你的模型上。我一般会打印两边模型的state_dict键名然后手动建立映射关系。5. 语义分割的推理部署与性能优化5.1 模型推理流程与后处理技巧训练好的模型要部署到实际场景里推理流程和后处理同样重要。推理的基本流程是读取图像、预处理归一化、resize、前向传播、取argmax得到类别图、后处理上采样回原尺寸、形态学操作、连通域过滤。后处理里最容易被忽视的是形态学操作。模型输出的分割图往往有一些孤立的噪点或者空洞用开运算先腐蚀后膨胀可以去除小噪点用闭运算先膨胀后腐蚀可以填充小空洞。但要注意形态学操作的核大小要适中太大了会把有用的细节也抹掉。连通域过滤也很实用。对于某些类别比如车辆、行人可以假设它们是以连通域的形式存在的。如果某个连通域的面积小于阈值就把它过滤掉。这个操作对去除误检特别有效。还有一个技巧是多尺度推理。把图像缩放到不同尺度分别推理然后把结果融合。这样做可以提升模型对不同大小物体的检测能力但计算量会成倍增加。如果对精度要求高、对速度要求不高可以试试。import torch import torch.nn.functional as F import numpy as np import cv2 def inference(model, image, device, num_classes): model.eval() # 预处理 h, w image.shape[:2] input_tensor preprocess(image).unsqueeze(0).to(device) with torch.no_grad(): output model(input_tensor) # 上采样回原尺寸 output F.interpolate(output, size(h, w), modebilinear, align_cornersFalse) pred output.argmax(dim1).squeeze().cpu().numpy() # 后处理形态学操作 kernel np.ones((3, 3), np.uint8) pred cv2.morphologyEx(pred.astype(np.uint8), cv2.MORPH_OPEN, kernel) pred cv2.morphologyEx(pred, cv2.MORPH_CLOSE, kernel) # 连通域过滤 for cls_id in range(1, num_classes): mask (pred cls_id).astype(np.uint8) num_labels, labels, stats, _ cv2.connectedComponentsWithStats( mask, connectivity8) for i in range(1, num_labels): if stats[i, cv2.CC_STAT_AREA] 100: pred[labels i] 0 return pred5.2 模型压缩与加速从理论到落地实际部署的时候模型大小和推理速度往往是硬约束。一个ResNet-101 backbone的DeepLabv3模型参数量大概在60M左右在服务器GPU上跑一张1024x2048的图大概需要200ms但在边缘设备上可能要好幾秒完全没法用。模型压缩主要有几条路。剪枝是去掉网络中不重要的通道或层减少参数量和计算量。结构化剪枝按通道剪对硬件友好非结构化剪枝按权重剪压缩率高但需要特殊硬件支持。量化是把FP32的权重和激活值用FP16或INT8表示可以减少一半到四分之三的模型大小推理速度也能提升。知识蒸馏是用一个大模型教师指导一个小模型学生训练让小模型学到和大模型相近的性能。我实际用得最多的是量化因为实现简单、效果稳定。PyTorch提供了动态量化和静态量化两种方式。动态量化只量化权重激活值在推理时动态量化适合LSTM和全连接层多的模型。静态量化需要校准数据同时量化权重和激活值适合卷积层多的模型。分割网络基本都是卷积层所以用静态量化。import torch.quantization as quant # 静态量化示例 model.eval() model.qconfig quant.get_default_qconfig(fbgemm) model_prepared quant.prepare(model) # 用校准数据跑一遍 with torch.no_grad(): for data in calib_loader: model_prepared(data) model_quantized quant.convert(model_prepared) torch.save(model_quantized.state_dict(), model_quantized.pth)注意量化后的模型精度会有一定下降通常mIoU会掉1到3个百分点。如果对精度要求极高可以考虑量化感知训练QAT在训练过程中模拟量化误差让模型适应量化后的数值分布。5.3 部署框架选型与性能对比部署框架的选择取决于目标硬件和性能要求。TensorRT是NVIDIA GPU上的首选它会对模型做图层融合、精度校准、内核自动调优推理速度通常比原生PyTorch快2到5倍。ONNX Runtime跨平台支持好CPU和GPU都能跑适合需要灵活部署的场景。OpenVINO是Intel平台上的优化框架在CPU和集成显卡上表现很好。TFLite和NCNN则适合移动端和嵌入式设备。我做过一个对比测试同一个DeepLabv3模型输入512x512在相同硬件上PyTorch原生推理大概45msONNX Runtime大概30msTensorRTFP16大概12msTensorRTINT8大概8ms。当然这个数据只是参考具体取决于模型结构和硬件配置。选框架的时候还要考虑算子支持度。有些分割网络用了自定义算子或者特殊的上采样方式导出ONNX的时候可能会失败。这时候要么改模型结构要么用支持自定义算子的框架。我一般会先用ONNX导出测试一下如果导出顺利就用ONNX Runtime如果导出有问题再考虑TensorRT。6. 语义分割常见问题与排查实录6.1 训练不收敛、损失震荡的排查思路训练不收敛是新手最常遇到的问题。我总结了一个排查清单按优先级排序第一检查数据标签是否正确。这是最容易被忽视但也最常见的问题。标签图的像素值是不是从0开始的连续整数有没有因为JPEG压缩导致标签值发生偏移有没有把调色板PNG当成灰度PNG读我建议写一个可视化脚本把图像和标签叠加显示随机抽几十张看看。第二检查学习率是否过大。如果损失在前几个epoch就飙到NaN大概率是学习率太大了。可以先把学习率降到1e-4甚至1e-5试试。如果损失下降很慢再逐步调大。第三检查损失函数实现是否正确。交叉熵损失要求输入的logits没有经过softmax如果已经softmax了再算交叉熵结果会不对。Dice Loss要注意平滑项的设置太小了梯度不稳定太大了损失下降慢。第四检查BatchNorm的配置。如果batch size太小比如小于8BatchNorm的统计量会很不准确导致训练不稳定。这时候可以考虑用GroupNorm或者SyncBatchNorm。第五检查权重初始化。解码器部分如果是随机初始化的初始化方式对训练影响很大。我一般用Kaiming初始化或者从预训练模型加载。6.2 小类别分割效果差的改善方法小类别分割是语义分割里的老大难问题。比如自动驾驶场景里交通标志、红绿灯的像素占比可能不到0.1%模型很容易把它们忽略掉。改善方法我试过几种效果从好到差排序重采样是最直接的在构造batch的时候优先采样包含小类别的图像。损失加权是给不同类别的损失加权重小类别的权重设大一点。权重的设置可以用中位数频率或者逆频率。OHEMOnline Hard Example Mining是只对损失大的像素回传梯度让小类别和难样本得到更多关注。Copy-Paste增强是把小类别的实例抠出来粘贴到其他图上增加小类别的样本量。这几种方法可以组合使用。我的经验是重采样加损失加权是基础配置Copy-Paste增强对小类别提升最明显OHEM在训练后期效果比较好。6.3 边缘分割粗糙的优化技巧分割边缘粗糙是另一个常见问题。模型输出的分割图在物体边界处往往不够精细有锯齿或者模糊。原因主要是下采样丢失了细节信息上采样又没法完全恢复。优化方法有几个。使用高分辨率特征在解码器里融合编码器浅层的高分辨率特征比如U-Net的跳跃连接。CRF后处理用全连接条件随机场对分割结果做后处理利用像素间的颜色和位置关系优化边缘。但CRF计算量大实时场景用不了。边缘感知损失在损失函数里加一项边缘损失专门惩罚边缘区域的预测错误。多尺度融合把不同尺度的预测结果融合粗尺度的结果提供语义信息细尺度的结果提供边缘信息。我实际用下来跳跃连接加多尺度融合是性价比最高的方案不需要额外的后处理训练时就能学到精细的边缘。CRF虽然效果好但推理速度太慢只适合离线处理。6.4 常见问题速查表问题现象可能原因排查方法解决方案损失为NaN学习率过大、数据有异常值检查学习率、检查输入数据范围降低学习率、裁剪输入值损失不下降标签错误、模型结构问题可视化标签、检查前向传播修正标签、调整模型小类别IoU为0类别不平衡、损失函数不合适统计类别分布、检查损失值重采样、损失加权、Copy-Paste边缘粗糙下采样丢失细节可视化预测边缘跳跃连接、多尺度融合过拟合数据量小、模型太大对比训练和验证损失数据增强、正则化、减小模型推理速度慢模型太大、输入分辨率高测各层耗时剪枝、量化、降低分辨率导出ONNX失败自定义算子不支持查看报错信息替换算子、使用其他框架避坑技巧训练分割模型的时候一定要在验证集上定期可视化预测结果。光看损失和IoU数值是不够的很多问题比如某个类别完全没学到、边缘特别差只有看图才能发现。我一般每5个epoch保存一次预测可视化训练完了回头翻能发现很多数值指标反映不出来的问题。7. 语义分割的典型应用场景拆解7.1 自动驾驶中的可行驶区域分割自动驾驶是语义分割最大的应用场景之一。可行驶区域分割的目标是把画面中的道路、停车场、加油站等车辆可以行驶的区域分割出来为下游的路径规划提供依据。这个场景的特点是对实时性要求极高通常要求30FPS以上对安全性要求极高漏检比误检严重得多场景变化大白天黑夜、晴天雨天、城市高速。模型选型上通常用轻量级backbone如MobileNet、EfficientNet-lite加高效的解码器输入分辨率一般降到512x1024甚至更低。数据标注方面可行驶区域的边界定义是个难点。路沿算不算可行驶区域被水淹了一半的路面算不算施工区域的锥桶围起来的区域算不算这些都需要在标注规范里明确定义。我参与过一个项目就是因为标注规范没定清楚导致模型在路沿附近的预测很不稳定。7.2 医学影像中的器官与病灶分割医学影像分割是另一个大场景包括CT、MRI、超声、病理切片等多种模态。这个场景的特点是数据量小通常几百例、标注成本极高需要专业医生、类别不平衡严重病灶可能只占图像的1%、对精度要求极高直接影响诊断。U-Net及其变体是医学图像分割的主流架构。因为数据量小通常需要强数据增强和正则化。损失函数方面Dice Loss加交叉熵的组合是标配。评价指标除了IoU和Dice还经常用HDHausdorff Distance来衡量边界误差。这个场景我踩过最大的坑是数据分布偏移。训练数据来自一台CT机器测试数据来自另一台图像的灰度分布、噪声水平、分辨率都不一样模型性能大幅下降。解决方案包括直方图匹配、域适应、在测试数据上做微调。7.3 遥感图像中的地表覆盖分类遥感图像分割的目标是把卫星或航拍图像分割成建筑、道路、水体、植被、农田等类别。这个场景的特点是图像尺寸极大可能上万像素、类别定义有层级关系比如植被下面分森林、草地、农田、季节和光照变化影响大。因为图像太大通常需要切patch训练和推理。切patch的时候要注意重叠区域推理完再拼接回去。重叠区域的大小一般取patch尺寸的1/4到1/2太小了拼接缝明显太大了计算浪费。遥感图像还有一个特殊问题是标签噪声。因为标注员对遥感图像的判读能力参差不齐同一个地物不同人标出来的结果可能不一样。我一般会做多人标注加投票或者用置信度学习的方法让模型自己学习标签噪声。8. 从语义分割到实例分割的进阶路径如果你已经掌握了语义分割想往实例分割进阶我建议的路径是先理解Mask R-CNN的原理然后在COCO数据集上跑通一个实例分割模型最后在自己的数据上做迁移。Mask R-CNN的核心是在Faster R-CNN的基础上加了一个分割分支对每个候选框预测一个二值掩码。它的损失函数是分类损失加框回归损失加掩码损失。掩码分支对每个类别输出一个掩码但只计算真实类别的掩码损失。实例分割的难点在于第一需要同时处理检测和分割两个任务网络结构更复杂第二实例之间的遮挡和重叠处理起来很麻烦第三评价指标更严格既要框准又要掩码准。从语义分割转实例分割最大的思维转变是从“像素分类”转到“实例检测加像素分类”。语义分割的输出是一张类别图实例分割的输出是一组实例每个实例有类别、框、掩码。下游任务对这两种输出的消费方式完全不同。我个人在实际操作中的体会是语义分割是理解图像分割的基础把语义分割做扎实了再学实例分割和全景分割会顺很多。很多实例分割里的技巧比如ROI Align、掩码预测都能在语义分割里找到影子。所以如果你刚开始学分割不要急着上Mask R-CNN先把FCN、U-Net、DeepLab这些经典语义分割网络吃透后面的路会好走很多。最后再分享一个小技巧训练分割模型的时候可以先用小分辨率比如256x256快速验证模型结构和损失函数是否合理确认能收敛之后再上大分辨率。这样能省很多调试时间。我见过不少人一上来就用1024x1024训练跑了一天发现损失不下降回头查发现是标签读错了白白浪费一天。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑