多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

深度学习图像分割实战:语义、实例与全景分割的完整落地路径

深度学习图像分割实战:语义、实例与全景分割的完整落地路径 简介面向深度学习与计算机视觉入门者这份压缩包聚焦图像分割三大方向——语义分割、实例分割与全景分割系统涵盖理论讲解与Python实践示例适合希望掌握像素级分类、目标实例区分的学习者也可作为相关课程或竞赛的辅助材料。包内共4个文件以md说明文档、py脚本和pro工程文件为主整体仅10KB轻量易用目前已有453人学习下载。内容源自deeplearning-image-segmentation-master项目包含CCF遥感图像分割预处理工程、语义分割脚本label_to_singlechannel.py及两份README说明完整呈现从数据准备、模型构建、损失优化到评估部署的典型流程。读者可借助这些材料理解FCN、U-Net、Mask R-CNN等模型在PASCAL VOC、COCO等数据集上的应用思路并参考IoU、mAP等指标进行效果验证。小巧的体积非常适合快速上手能为后续深入图像分割实战打下基础。1. 深度学习图像分割从像素级分类到场景理解的完整落地路径图像分割是计算机视觉里比目标检测更细粒度的一层任务它的目标不是画框而是给每个像素赋予一个语义标签。无论你是做自动驾驶的道路理解、医学影像的病灶勾画还是工业质检里的缺陷定位只要涉及“哪一块属于什么”就绕不开语义分割、实例分割和全景分割这三类方法。这份基于深度学习方法的图像分割资源包把三种分割任务的数据准备、模型选型、训练调参与评估验证串成了一条可复现的完整路径适合那些已经跑通分类或检测、想在分割方向建立实战能力的人。很多人第一次接触分割时会误以为它只是“把分类网络的全连接层换成上采样”实际做起来才发现标签怎么画、损失函数怎么处理类别不均衡、预测图怎么还原到原图尺寸、mIoU 怎么算才能对齐官方结果每一环节都有坑。这篇笔记会把这条路径拆开来讲按“任务边界 → 数据格式 → 训练代码 → 常见故障 → 验收技巧”推进每个步骤都有可直接抄走的参数和命令。2. 语义、实例与全景分割三种任务的边界、数据差异与选型依据2.1 三种任务的本质区别从“是什么”到“哪一个是谁”语义分割Semantic Segmentation解决的是“这个像素属于哪个类别”它不区分同类别的不同个体。比如一张街景图里有三辆车语义分割会把所有车的像素都标成“car”但你无法知道哪块像素属于哪一辆车。实例分割Instance Segmentation在语义分割的基础上增加了个体编号它会输出“car_1”“car_2”“car_3”这样的掩码同一类别下的不同对象被分开。全景分割Panoptic Segmentation则是两者的融合对可数物体things做实例分割对背景等不可数区域stuff做语义分割最终输出一个统一的分割图每个像素既要标类别也要标实例 ID。这三者的数据标注格式差异很大。语义分割的标签图是一张与原始图像等尺寸的单通道图每个像素的值是该位置的类别 ID。实例分割的标签需要以 COCO 格式的 JSON 存储每个对象有 polygon 或 RLE 掩码。全景分割的标注格式则更复杂典型做法是每张图同时生成一个语义标签图和一个实例 ID 图实例 ID 只在 things 类别范围内递增stuff 类别的实例 ID 固定为 0。从选型角度说如果你的业务只关心区域归属比如遥感影像的土地分类语义分割足够如果你需要统计图中目标数量并做精细轮廓提取比如细胞计数必须上实例分割如果你的场景需要同时兼顾背景结构与前景个体比如机器人导航中的场景解析全景分割是更完整的方案。资源包里三者的对比实验数据能看出同一种骨干网络下语义分割的训练成本最低全景分割的工程复杂度最高但信息量也是递进的。2.2 数据标注与格式转换VOC、COCO 与 PNG 掩码的互转逻辑分割任务的数据准备比检测麻烦得多核心原因在于标签不是框坐标而是逐像素的掩码。实践中最常见的标注工具有 LabelMe、精灵标注助手和 CVAT导出格式各不相同但最终都要整理成模型训练需要的统一格式。资源包里的脚本主要围绕 VOC 格式与 COCO 格式的互转展开因为这两个格式在开源模型里覆盖面最广。VOC 格式下语义分割标签是 PNG 文件像素值即类别 ID调色板固定实例分割标签则是 PNG 文件加一个分割对象 XML 文件。COCO 格式下所有标注集中在 JSON 里实例掩码用 polygon 或 RLE 压缩表示。从 LabelMe 导出的 JSON 转成 VOC 或者 COCO常见的做法是先解析多边形坐标再通过 OpenCV 的 fillPoly 把多边形绘制到掩码图上。这里有个容易搞错的细节VOC 的类别 ID 是从 0背景开始连续递增的而 COCO 的类别 ID 是从 1 开始并且可能不连续转换时一定要建立类别名到 ID 的映射表否则训练出来的结果会整体错位。资源包里提供了一个转换脚本核心逻辑是读取标注 JSON提取每个对象的类别名和 polygon 坐标按预设映射表编码最后用多边形的掩码合并策略生成训练标签。合并策略同样需要重视——当两个对象重叠时后标注的对象应该覆盖先标注的这要求标注文件里的对象列表顺序与标注时的图层顺序一致否则边缘会出现异常的锯齿。2.3 选型参考骨干网络与分割头怎么搭分割模型的选型不是越新越好需要结合显存、推理速度与精度要求。语义分割的经典路线是 FCN 系的端到端卷积结构U-Net 适合医学影像这类中小尺寸目标DeepLabV3 在街景数据上表现稳定。实例分割的主流选择是 Mask R-CNN它在 Faster R-CNN 的检测分支基础上并行增加了一个掩码分支训练时一个 batch 里同时计算分类损失、回归损失和掩码损失。全景分割的现成框架相对少资源包里给出的是基于 Mask R-CNN 加语义分割头的组合方案things 走实例分支stuff 走语义分支最后用一个融合模块统一输出。骨干网络的选择直接影响显存占用与训练速度。ResNet-50 在单张 1080Ti 上训练 DeepLabV3batch size 4 的情况下显存占用约 9GB换 ResNet-101 会升到 12GB 左右。如果你的数据规模不大几千张图从 ResNet-50 起步更划算训练一个 epoch 的时间大约比 ResNet-101 少 40%精度差距通常在 12 个 mIoU 点以内。MobileNetV3 之类的轻量骨干适合边缘部署但训练时要注意 BatchNorm 参数在低 batch size 下的不稳定问题。3. 数据准备与标签工程从原始图像到可训练的掩码数据集3.1 目录结构与数据集划分别把所有图扔一个文件夹分割项目的目录结构直接决定后续脚本的改动量。我一般会按下面的方式组织数据目录无论用哪个框架都能快速适配dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── masks/ │ ├── train/ │ ├── val/ │ └── test/ ├── annotations/ │ ├── train.json │ ├── val.json │ └── test.json └── class_names.txtimages 放原始图masks 放掩码图annotations 放 COCO 格式的标注文件。语义分割训练时只需要 images 和 masks 两个目录实例分割训练时需要 images 和 annotations全景分割则三者都依赖。这种结构的好处是后续做数据增强时图像和掩码可以同步处理做交叉验证时只需要改 train/val 的划分文件不用动代码里的路径逻辑。数据集划分比例上分割任务对验证集的依赖比检测更强因为掩码质量直接影响评估指标的可信度。我通常按 8:1:1 划分训练、验证和测试。随机划分是不够的必须按场景分组划分如果数据来自 10 个不同场景每个场景的图像要同时出现在训练集和验证集里比例保持一致否则验证集上的 mIoU 会虚高部署到新场景时精度断崖式下跌。3.2 数据增强的尺度约束掩码与图像必须同步变换分割任务的数据增强比分类严格因为任何几何变换都要同时作用于图像和掩码。常见的增强策略包括水平翻转、随机缩放0.52.0 倍、随机裁剪和颜色抖动。水平翻转最简单直接在 numpy 或 PyTorch 的 tensor 维度上翻转即可。随机缩放则需要特别注意图像缩放了多少倍掩码必须缩放同样的倍数并且插值方法要区别对待——图像用双线性插值掩码用最近邻插值否则掩码边缘会出现不存在的类别混合值。随机裁剪是另一个高频操作。这里有个参数配对问题裁剪尺寸决定了网络输入分辨率而输入分辨率又直接影响感受野与细节保留的平衡。对街景等大目标数据裁剪 512×512 通常够用对医学影像等小目标数据裁剪尺寸可以保持 256×256但需要叠加一个 0.5 概率的随机旋转90 度的倍数避免模型学到固定的方向偏好。资源包里给出的增强参数组合我实测下来比较稳水平翻转概率 0.5、缩放范围 0.52.0、裁剪尺寸与训练分辨率一致、颜色抖动幅度 HSV 各 ±20。你可以直接抄这组参数也可以按自己数据的尺度分布微调。如果训练数据里目标尺寸差异巨大建议把随机缩放改成“多尺度训练”即每隔多少个 iteration 从预设尺度列表里随机选一个缩放因子这种方式能显著提升模型对尺度变化的鲁棒性但要求 GPU 显存能容纳最大尺度的 batch。3.3 类别不均衡的标签处理加权损失与重采样怎么选分割数据天然存在严重的类别不均衡现象比如街景图中天空、道路占比可能超过 60%而行人、自行车只占几个百分点。如果直接用交叉熵损失模型会倾向于把所有像素预测为大类导致小类目标完全不出现。资源包里给了两种解决方案类别权重加权和困难样本重采样。类别权重加权是在损失函数里给每个类别乘一个权重权重的计算方式通常采用中位数频率平衡median frequency balancing公式是weight_c median_freq / freq_c其中freq_c是类别 c 的像素占比。这样既不会让高频类别权重太低也不会让稀有类别权重过高导致噪声被放大。另一种做法是在损失里同时加入 Dice 损失项Dice 损失对小目标的梯度响应更强和交叉熵按 1:1 加权混合后整体收敛速度和最终精度都有提升。如果数据集本身很小几千张图重采样可能比加权损失更有效。做法是按类别像素占比的反比给每个样本分配采样概率让包含稀有类别的图像更大概率被抽到训练 batch 里。注意这里的“样本”是一整张图不是图内的某个区域所以即使某张图只有一个像素属于稀有类别它的采样权重也会明显提高。实现时用 PyTorch 的 WeightedRandomSampler 传入每张图的权重列表即可简单高效。4. 基于 Python 的深度学习分割实战从配置到训练一个语义分割模型4.1 环境配置与依赖清单torch 版本和 CUDA 对齐是第一步分割项目对环境的敏感度比普通分类项目高最常见的问题不是代码逻辑而是 CUDA、PyTorch 和 OpenCV 三者版本不对齐导致的掩码读写错误或显存报错。资源包要求的环境组合是 Python 3.8、PyTorch 1.10 以上、CUDA 11.1 以上、OpenCV 4.5 以上。其中 OpenCV 负责掩码的读写与多边形绘制版本过低时读入 PNG 的通道顺序可能异常导致掩码和图像错位。安装命令我按下面的顺序执行减少依赖冲突conda create -n segenv python3.8 -y conda activate segenv conda install pytorch torchvision torchaudio cudatoolkit11.3 -c pytorch pip install opencv-python4.5.5.64 albumentations1.1.0 tqdm tensorboardpytorch 通过 conda 安装会自动匹配 CUDA 工具链opencv 的版本号里尾部的 64 代表的是编译选项不影响正常使用。albumentations 版本固定在 1.1.0 是因为它的 API 在 1.2 以后有过一次调整旧代码可能跑不通。安装完成后先执行一行验证命令确认 CUDA 可用import torch assert torch.cuda.is_available() print(torch.cuda.get_device_name(0))如果第二步就报错优先检查 conda 环境的 CUDA 版本与驱动是否匹配用nvidia-smi看驱动支持的 CUDA 版本再决定是否回退 cudatoolkit 版本。这一步看似基础但我在多个机器上见过“安装成功但训练时 tensor 在 CPU 上”的隐蔽问题根源就是 PyTorch 编译时用的 CUDA 版本与驱动不匹配。4.2 构建 Dataset 子类语义分割的读图与预处理核心逻辑PyTorch 训练分割模型的 Dataset 子类核心工作只有两件读入图像与掩码、做同步增强。这里贴一个精简但完整的语义分割 Dataset 实现可以直接抄到自己的项目里改造import cv2 import torch from torch.utils.data import Dataset import albumentations as A class SegDataset(Dataset): def __init__(self, image_dir, mask_dir, size(512, 512)): self.image_paths sorted(image_dir.glob(*.jpg)) self.mask_paths sorted(mask_dir.glob(*.png)) self.size size self.transform A.Compose([ A.RandomCrop(size[0], size[1], p0.8), A.HorizontalFlip(p0.5), A.ColorJitter(brightness0.2, contrast0.2, saturation0.2, p0.3), ]) def __len__(self): return len(self.image_paths) def __getitem__(self, idx): image cv2.imread(str(self.image_paths[idx])) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) mask cv2.imread(str(self.mask_paths[idx]), cv2.IMREAD_UNCHANGED) if mask.ndim 3: mask mask[:, :, 0] transformed self.transform(imageimage, maskmask) image transformed[image].astype(float32) / 255.0 mask transformed[mask].astype(int64) image torch.from_numpy(image).permute(2, 0, 1) mask torch.from_numpy(mask) return image, mask这段代码里有三个关键点。一是掩码读取要使用IMREAD_UNCHANGED保证读入的 PNG 不会因为通道转换而丢失类别 ID如果掩码是三通道的 RGB 调色板格式只取第一个通道即可因为 VOC 格式的调色板 PNG 三个通道的值并不相同。二是 albumentations 的同步增强处理它保证了图像和掩码应用同一套随机变换这点比手动写多个随机数的分支代码可靠得多。三是图像归一化放在__getitem__里而不是提前存成 numpy节省磁盘空间代价是每个 epoch 都会重复一遍归一化计算但对中小数据集来说完全可以忽略。4.3 训练脚本的关键配置损失函数、优化器与学习率策略分割模型的训练脚本主体和分类网络类似但有几个参数值得单独说明。损失函数我选交叉熵加 Dice 的组合权重比是 1:1。优化器用 SGD 而不是 Adam虽然 Adam 收敛更快但分割任务的最终精度通常 SGD 配动量更好一个常见原因是 Adam 对梯度的一阶矩估计在小 batch size 下波动较大导致掩码边缘不够干净。SGD 的动量设为 0.9权重衰减设为 0.0001这两个参数在分割任务里几乎不用调。学习率策略用的是多项式衰减poly decay初始学习率 0.01训练结束时衰减到 0。这种策略对分割任务非常有效原因在于分割网络的最后几层上采样结构对小学习率变化很敏感线性或多项式衰减比阶梯式下降更平滑。PyTorch 里实现多项式衰减不需要额外库直接自定义一个 LambdaLRdef poly_lr(epoch, max_epochs, power0.9): return (1 - epoch / max_epochs) ** power scheduler torch.optim.lr_scheduler.LambdaLR( optimizer, lr_lambdalambda e: poly_lr(e, 50) )power0.9时衰减曲线前期较缓后期较快实测比线性衰减效果好。如果你的显存不够大batch size 只能在 4 到 8 之间可考虑把初始学习率降到 0.001同时开启梯度累积两步等效于 batch size 翻倍。4.4 训练状态保存与恢复断点续训的 checkpoint 设计分割任务的训练周期通常比分类长一个完整训练可能要跑十几个小时甚至几天中途断电或显存溢出是常态。checkpoint 设计直接影响工作效率。我的习惯是每 5 个 epoch 保存一次完整的训练状态包括模型参数、优化器状态、scheduler 状态、epoch 号和最佳 mIoU 值。只保存模型参数的话恢复训练时学习率会从初始值重新走整个训练流程就被破坏了。保存 checkpoint 的代码用 PyTorch 的torch.save写成一个字典即可checkpoint { epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), scheduler_state_dict: scheduler.state_dict(), best_miou: best_miou, } torch.save(checkpoint, fcheckpoints/seg_epoch{epoch}.pth)恢复训练时先加载字典再依次load_state_dict到 model、optimizer、scheduler然后把epoch赋值给循环的起点。这里有个容易踩的细节如果训练中途改了模型结构或类别数旧 checkpoint 加载会报尺寸不匹配需要先把新结构下随机初始化的模型参数保存一份再手动把匹配的层拷贝过来。5. 避坑与常见问题排查分割训练中翻过车的五个典型场景5.1 掩码读入后全是黑色或全白通道顺序与像素值编码问题现象训练时损失值一直在低位徘徊验证时 mIoU 等于 0可视化预测结果全是背景类。检查训练数据里的掩码发现读入的数组全是 0或者全是 255。原因这种情况绝大多数是掩码的读取方式不对。PNG 掩码如果是以调色板模式存储的OpenCV 用默认的IMREAD_COLOR读入时会把调色板索引映射成 RGB 三个通道导致原本的类别 ID 变成三个通道完全不同的值而如果掩码是用IMREAD_GRAYSCALE读入调色板索引会被拉伸到 0~255原本类别 ID 为 1 的区域可能变成 253 或其他值模型完全没法学习。解决统一用cv2.IMREAD_UNCHANGED读取所有掩码文件然后在代码里显式判断维度。如果是三通道就取第一通道。另外在预处理里加入一个断言打印训练集里掩码的类别数与实际num_classes比对。我见过最隐蔽的情况是某个标注工具导出的 PNG 里背景不是 0 而是 255训练时背景类的交叉熵损失把所有前景区域一并吞掉特征图被反向传播污染。遇到这种问题写一段小脚本统计掩码像素值的分布输出np.unique(mask)一眼就能看出异常类别 ID 的存在。5.2 训练 loss 不下降学习率与损失函数的适配问题现象训练前 10 个 epoch交叉熵损失从 1.2 降到 0.9 后就停滞不动Dice 损失始终在 0.8 以上预测结果里前景目标的轮廓模糊成一片。原因常见原因有两个。一是学习率设置过高或过低。过高的学习率会让损失在前几个 epoch 快速下降后进入震荡区过低的则让网络一直在同一个局部区域打转。二是损失函数搭配不当。加权交叉熵虽然能解决类别不均衡但如果权重设置过极端比如稀有类别的权重超过几十模型会进入一种“对稀有类别过度敏感但对常见类别完全无视”的失衡状态。解决先做 5 个 epoch 的预热训练观察损失曲线。如果损失从 1.5 降到 0.6说明学习率合适如果降得很慢把学习率乘 2如果震荡除以 2。同时检查损失函数里的权重配置中位数频率平衡计算出的权重通常都在 0.5~2 之间如果出现某个权重超过 5考虑截断到 5或者改用 Dice 损失配合交叉熵的做法来抑制极端权重的影响。5.3 显存溢出batch size 减到 1 还是溢出怎么办现象训练脚本启动后几秒钟内报CUDA out of memory把 batch size 调到 1 仍然报错。但同一张显卡运行分类任务完全没有问题。原因分割网络的上采样层和跳层连接会占用大量中间特征图缓存。DeepLabV3 的 ASPP 模块会并行执行多个空洞卷积分支各自的输出都要保留到拼接阶段U-Net 的跳层连接则是把每一层的特征图都保存下来供解码阶段逐层拼接。这两类结构对显存的消耗远超同等参数量的分类网络。解决优先把图片分辨率调到 256×256大多数分割任务在这个尺度下仍有可用的精度。如果仍然溢出把 backbone 换成轻量网络比如 ResNet-18。另一个被很多人忽略的选项是开启 PyTorch 的torch.cuda.amp混合精度训练显存占用直接降到原来的一半左右而且精度损失通常小于 0.5 个 mIoU。scaler torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): outputs model(images) loss criterion(outputs, masks) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()混合精度训练的核心是自动将部分算子转为 FP16 计算梯度缩放器防止小梯度被 FP16 精度吞掉。开启后训练速度也会提升值得作为分割训练的默认配置。5.4 验证 mIoU 与训练 mIoU 相差巨大数据泄漏与评估方式不对现象训练过程中的 mIoU 稳定在 0.75 左右验证集的 mIoU 只有 0.4且每次验证结果波动很大。查看训练集和验证集的图像发现很多背景相似、目标位置相似的重复图。原因分割数据集经常出现“同场景不同帧”的连续采样问题比如从一段视频里按帧抽图时相邻帧的目标位置相差很少。如果只做随机划分同一场景的帧会被同时分到训练集和验证集模型相当于“背下了”场景特征验证集上表现虚高。另一种情况更隐蔽数据增强里如果用了基于像素统计的归一化方式比如计算整个训练集的均值方差验证集直接复用这个均值方差会有微小的数据泄漏。解决数据集划分必须以场景为单位进行分组。先按场景 ID 分组再把场景整体拆到训练集/验证集/测试集。具体实现时给数据目录里的文件夹命名带上场景 ID划分脚本按文件夹遍历即可。评估时确保验证集不使用训练增强只做 resize 和归一化。5.5 预测结果与原图尺寸不一致上采样后分辨率对齐问题现象推理阶段模型输出的预测图尺寸是 512×512但原图是 1920×1080直接 resize 回去后目标边缘出现明显的锯齿和偏移。原因分割模型的输出分辨率通常与输入分辨率相同但训练时如果只做随机裁剪而没有在 val 阶段把整图缩放成固定尺寸输出尺寸就和原图不一致。更隐蔽的原因是有些上采样层使用了反卷积反卷积的输出尺寸不是线性的需要手动算对齐参数。解决推理时先在原图上做滑动窗口推理sliding window每张窗口图单独预测再将所有窗口的预测结果按原位置拼回去。窗口之间加 overlap比如 64 像素重叠区域取置信度均值。另外一种更省事的做法是把原图 resize 到训练尺寸做一次预测再把预测结果 resize 回原尺寸但这种方式对大目标位置有轻微偏移。对精度要求高的场景滑动窗口是标准做法代码也不复杂def sliding_window_predict(model, image, window_size512, stride448): h, w image.shape[:2] pred np.zeros((h, w), dtypenp.float32) count np.zeros((h, w), dtypenp.float32) for y in range(0, h - window_size 1, stride): for x in range(0, w - window_size 1, stride): crop image[y:ywindow_size, x:xwindow_size] logits model(crop) # (1, num_classes, H, W) pred_cls logits.argmax(dim1).squeeze(0).cpu().numpy() pred[y:ywindow_size, x:xwindow_size] pred_cls count[y:ywindow_size, x:xwindow_size] 1 pred pred / np.maximum(count, 1) return pred.astype(np.uint8)stride448意味着相邻窗口有 64 像素重叠重叠区域的类别由多次预测投票决定分割边缘会更稳定。边界处count为 0 的像素需要单独处理通常复制最近窗口的预测结果。6. 评估指标与置信度卡点mIoU 之外实战里更值得看的两个指标分割模型的标准评估指标是 mIoU平均交并比公式是每个类别计算预测区域与真实区域的交叠率再求平均。mIoU 能反映整体精度但它有两个众所周知的问题一是对大目标的分数变化不敏感二是对类别不均衡数据的评价不够全面。实战里我额外会看两个指标。第一个是边界 F1 分数Boundary F1 Score它衡量的是预测掩码与真实掩码在目标轮廓处的重合质量。很多场景下 mIoU 达标了但轮廓会有一圈 2~3 像素的偏移对医学影像或工业测量这类对边界敏感的任务这是不可接受的。计算方式是对预测和真值分别提取边界像素统计两者的 F1。在资源包的评估脚本里边界提取用的方式是形态学梯度——先膨胀再减去原图能稳定地抽出一圈轮廓。第二个指标是按类别统计的 Dice 系数尤其关注稀有类别的 Dice。mIoU 会掩盖稀有类别的失败比如背景占了 95% 像素的医学数据集mIoU 高但病灶区域的 Dice 可能只有 0.2。按类别每个类都算一个 Dice能直接定位到是哪一类拖了后腿。这两个指标在模型调参阶段比 mIoU 更敏感我遇到过训练到第 20 个 epoch 时 mIoU 还在缓慢上升但边界 F1 已经开始下降的情况这种情况说明模型在牺牲边界精度换取区域填充率需要及时停掉早停策略里的 patience 计数。推理阶段的置信度阈值也值得单独卡。大多数分割模型输出的是每个像素的 softmax 概率取 argmax 得到类别 ID。但在低置信度区域直接取 argmax 的类别经常是错的尤其是在类别之间颜色纹理高度相似的地方。做法是给每个像素设定一个置信度阈值低于阈值的像素标记为“不确定”再配合条件随机场CRF或简单的中值滤波做后处理。我通常先看验证集上每个类别的平均置信度再取中位数作为初始阈值然后按 0.05 的步长扫描调优目标不是提升 mIoU而是降低误检率。资源包提供了一份完整的验证与可视化脚本输入是训练好的模型权重和验证集目录输出内容包括逐类别的 IoU 表、边界 F1 分数、混淆矩阵热力图以及多张预测可视化图。我拿到任何分割资源第一件事都是跑通这个脚本把验证指标和官方报告对比一次如果差异小于 1 个点说明整个数据流水线是可信的如果差异超过 3 个点优先排查数据格式和预处理逻辑而不是怀疑模型本身。后来我看每个分割项目都强迫自己先跑一遍完整流程再做模型实验。这套习惯帮我省下的时间远超想象。希望这份速查笔记能帮你绕过那几个坑把精力花在真正值得调的地方。本文还有配套的精品资源点击获取
返回列表