ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

猫狗图像分割数据集:U-Net/DeepLabv3+开箱即用训练基线

猫狗图像分割数据集:U-Net/DeepLabv3+开箱即用训练基线 简介本资源是一份专为深度学习图像分割任务设计的猫狗二分类分割数据集面向计算机视觉初学者与算法工程师可用于U-Net、Mask R-CNN等模型的训练与评估。数据集已按标准规范划分训练集5912对图像mask和测试集1478对图像mask所有图像均为JPEG格式共1999张另含1个Python可视化脚本支持一键加载样本、叠加真值蒙版并保存对比图显著降低数据验证门槛。压缩包共2000个文件总大小230.81MB解压后即用无需额外标注或格式转换。目前已有237人学习下载数据质量高前景区域丰富、边缘清晰、mask标注精准配合结构化目录images/masks双轨并行便于快速接入PyTorch或TensorFlow数据管道是入门语义分割实践的理想基准数据集。1. 猫狗图像分割数据集不是“带标签的图库”而是能直接喂进 U-Net/DeepLabv3 的开箱即用训练闭环你手头那套标注模糊、mask 边缘锯齿、train/test 划分混乱的自制猫狗图大概率正在拖慢你的分割模型收敛——而这个 247MB 的数据集是我在三个项目里反复验证过的「最小可行分割基线」5912 张训练图 1478 张测试图每张图配一个像素级二值 mask猫/狗为前景背景为 0且所有 mask 均经人工校验边缘连续性与前景完整性。它不解决多类别语义分割但专治「二分类前景提取」场景下的过拟合、边界模糊、泛化崩塌三大玄学问题。适合刚跑通 U-Net 骨干、想快速验证 loss 曲线是否健康的新手也适合需要 baseline 对比的熟手——比如你正调参 HRSC2016 或 CCPD 数据集却卡在数据预处理环节这个猫狗集就是你的「隔离舱」排除标注噪声干扰专注网络结构与损失函数设计。它不是玩具数据集而是我部署地毯图像分割系统前必跑的 sanity check。2. 数据结构解析与加载实操从文件夹路径到 PyTorch Dataset 类的零跳转封装2.1 文件组织逻辑为什么 train/images 与 train/masks 必须严格同名对齐该数据集采用最简明的双目录结构train/images/下存放全部 5912 张原始 JPEG 图像如Egyptian_Mau_165.jpegtrain/masks/下存放同名 PNG 格式 maskEgyptian_Mau_165.png像素值仅含 0背景和 255前景test/images/与test/masks/同理共 1478 对。提示mask 必须为单通道 PNG非 RGB若误用 JPEG 存储 mask压缩会引入灰度值如 254、253导致mask 0判定失效。我曾因某次批量转换脚本漏设-colors 2参数导致 37% 的 mask 出现中间灰度值模型在 epoch 3 就开始输出“毛边猫”。2.2 PyTorch Dataset 类三步封装屏蔽路径硬编码import os from torch.utils.data import Dataset from PIL import Image import numpy as np import torch class CatDogSegmentationDataset(Dataset): def __init__(self, img_dir, mask_dir, transformNone): self.img_dir img_dir self.mask_dir mask_dir self.transform transform # 关键只取 images 目录下文件名不含扩展名确保与 masks 同名 self.img_names [f.split(.)[0] for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .jpeg, .png))] def __len__(self): return len(self.img_names) def __getitem__(self, idx): img_name self.img_names[idx] # 严格按同名规则加载 img_path os.path.join(self.img_dir, f{img_name}.jpeg) mask_path os.path.join(self.mask_dir, f{img_name}.png) image Image.open(img_path).convert(RGB) # 统一转 RGB mask Image.open(mask_path).convert(L) # 强制单通道 # 归一化 mask0→0, 255→1 mask np.array(mask) // 255 if self.transform: image self.transform(image) # mask 不做几何变换时的插值避免出现 0.5 值 mask torch.from_numpy(mask).long() return image, mask参数说明img_dir和mask_dir必须指向train/images与train/masks这类具体子目录而非顶层train/transform应使用torchvision.transforms.Compose其中Resize((256,256))后接ToTensor()即可切勿对 mask 使用RandomHorizontalFlip等需插值的增强——这是新手翻车高发区np.array(mask) // 255是安全归一化PNG 中 255 像素值整除后得 10 得 0规避浮点误差。2.3 DataLoader 构建batch_size 与 num_workers 的血泪平衡点from torchvision import transforms from torch.utils.data import DataLoader # 定义训练集 transformmask 不参与几何增强 train_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset CatDogSegmentationDataset( img_dirdata/train/images, mask_dirdata/train/masks, transformtrain_transform ) # 关键参数组合batch_size8 是 24GB 显存的甜点 train_loader DataLoader( train_dataset, batch_size8, shuffleTrue, num_workers4, # Linux 系统可设为 CPU 核心数-1Windows 建议 ≤2 pin_memoryTrue, # 加速 GPU 数据传输 drop_lastTrue # 避免最后 batch size 不足引发 shape mismatch )为什么 batch_size8256×256 输入下U-Net encoder 层 feature map 内存占用约 1.2GB/batch若设batch_size16显存峰值易超 24GB实测达 25.7GB触发 CUDA OOMnum_workers4在 8 核 CPU 上实测数据加载吞吐达 180 img/s再高则 I/O 瓶颈worker 空转率超 40%。3. 可视化脚本深度拆解不只是看图而是验证数据质量的诊断工具3.1 脚本核心逻辑三图同屏对比的底层实现数据集附带的visualize_sample.py并非简单plt.imshow()其关键价值在于暴露标注缺陷。我们重写为可调试版本import matplotlib.pyplot as plt import numpy as np from PIL import Image import random import os def visualize_sample(img_dir, mask_dir, save_pathsample_visualization.png): # 随机选一张图避开首尾防缓存干扰 img_files [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .jpeg))] sample_img random.choice(img_files[:-10]) # 排除最近 10 个文件 img_name sample_img.split(.)[0] # 加载原图与 mask img np.array(Image.open(os.path.join(img_dir, sample_img)).convert(RGB)) mask np.array(Image.open(os.path.join(mask_dir, f{img_name}.png)).convert(L)) # 生成 overlaymask 区域用红色半透明覆盖 overlay img.copy() overlay[mask 255] overlay[mask 255] * 0.5 np.array([255, 0, 0]) * 0.5 # 三图布局 fig, axes plt.subplots(1, 3, figsize(15, 5)) axes[0].imshow(img), axes[0].set_title(Original Image), axes[0].axis(off) axes[1].imshow(mask, cmapgray), axes[1].set_title(Ground Truth Mask), axes[1].axis(off) axes[2].imshow(overlay), axes[2].set_title(Overlay (Red: Foreground)), axes[2].axis(off) plt.tight_layout() plt.savefig(save_path, dpi150, bbox_inchestight) print(fVisualization saved to {save_path}) plt.show() # 调用示例 visualize_sample(data/train/images, data/train/masks)逻辑说明overlay[mask 255]直接索引前景区域避免cv2.addWeighted的插值模糊* 0.5 [255,0,0] * 0.5实现精确 50% 透明度红色通道叠加不溢出uint8 范围内bbox_inchestight防止标题被裁切——这在检查 mask 是否完整覆盖猫耳/狗鼻尖时至关重要。3.2 诊断 checklist三图对比中必须盯住的 4 个致命细节检查项正常表现异常现象与后果修复动作mask 连续性前景区域无断裂像素猫胡须处出现 1~2 像素断点用cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)闭运算补洞kernel3×3边缘贴合度mask 边界与毛发轮廓严丝合缝mask 切掉部分耳朵或尾巴手动用 GIMP 的“智能选择”工具重绘禁止用模糊滤镜背景纯净度mask 中背景区域全为 0地板纹理渗入 mask值0mask cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)[1]二值化尺寸一致性img.shape mask.shapemask 比 img 少 1 行/列用cv2.copyMakeBorder补零不可 resize破坏像素级对齐注意该脚本每次运行都生成新图建议在训练前执行 5 次随机抽查不同品种如 Sphynx 无毛猫 vs British_Shorthair 长毛猫验证标注鲁棒性。我曾发现Abyssinian_29.jpeg的 mask 漏标尾巴尖端正是靠第 3 次可视化暴露的。4. 训练 pipeline 实战从 DICE Loss 到 Early Stopping 的完整链路4.1 损失函数选型为什么不用 BCEWithLogitsLoss虽然 mask 是二值但直接使用BCEWithLogitsLoss会导致前景召回率暴跌——原因在于猫狗图像中前景占比常低于 15%尤其坐姿猫loss 被背景主导。实测对比Loss 函数val Dice Scoreepoch 50前景 recallIoU0.5训练稳定性BCEWithLogitsLoss0.7210.632振荡剧烈±0.08Dice Loss BCE 混合α0.50.8470.791平稳收敛±0.01import torch import torch.nn as nn class DiceLoss(nn.Module): def __init__(self, smooth1.): super().__init__() self.smooth smooth def forward(self, pred, target): pred torch.sigmoid(pred) # 输出层未加 sigmoid 时必需 intersection (pred * target).sum() dice (2. * intersection self.smooth) / \ (pred.sum() target.sum() self.smooth) return 1 - dice # 混合损失Dice 主导BCE 辅助边缘校准 class DiceBCELoss(nn.Module): def __init__(self, dice_weight0.5): super().__init__() self.dice_loss DiceLoss() self.bce_loss nn.BCEWithLogitsLoss() self.dice_weight dice_weight def forward(self, pred, target): dice self.dice_loss(pred, target.float()) bce self.bce_loss(pred, target.float()) return self.dice_weight * dice (1 - self.dice_weight) * bce criterion DiceBCELoss(dice_weight0.7) # 前景稀缺时加大 Dice 权重参数说明dice_weight0.7适用于前景占比 20% 的猫狗图若用于地毯图像分割前景占比40%可降至 0.4pred是网络 raw outputlogitstarget是 0/1 tensortorch.sigmoid()在 loss 内部执行避免nn.Sigmoid层影响梯度流。4.2 Early Stopping 实现监控 val Dice 而非 lossclass EarlyStopping: def __init__(self, patience7, delta0.001): self.patience patience self.delta delta self.counter 0 self.best_score None self.early_stop False def __call__(self, val_dice): if self.best_score is None: self.best_score val_dice elif val_dice self.best_score self.delta: self.counter 1 if self.counter self.patience: self.early_stop True else: self.best_score val_dice self.counter 0 # 训练循环中调用 early_stopping EarlyStopping(patience10, delta0.0005) for epoch in range(100): # ... train_step ... val_dice validate(model, val_loader) # 自定义验证函数 early_stopping(val_dice) if early_stopping.early_stop: print(fEarly stopping triggered at epoch {epoch}) break为什么监控 val_dice分割任务中 val loss 下降但 Dice 不升是过拟合典型信号delta0.0005防止因浮点抖动误触发实测 val_dice 在 0.847±0.0003 波动patience10允许模型在 plateau 区间探索更优解比patience3多获 0.008 Dice 提升。5. 避坑指南5 个让模型在第 3 个 epoch 就崩溃的真实陷阱5.1 现象训练 loss 爆炸1e5val Dice 为 nan原因mask 加载时未做// 255归一化导致target值为 0/255而BCEWithLogitsLoss期望 0/1。当pred输出 -10~10 logitssigmoid(-10)4.5e-5log(4.5e-5)≈-10乘以 target255 后 loss 瞬间超限。解决强制在__getitem__中添加mask np.array(mask) // 255并在visualize_sample中打印mask.max()验证。5.2 现象验证时所有像素预测为背景output 全负原因U-Net decoder 最后一层未加sigmoid且 loss 使用了BCEWithLogitsLoss内部有 sigmoid但validate()函数中torch.sigmoid(output)被误删。解决统一约定——若 loss 用BCEWithLogitsLoss则validate()必须手动sigmoid若 loss 用BCELoss则网络末层必须加nn.Sigmoid()。5.3 现象mask 边缘出现“虚化光晕”IoU 评估虚高原因训练时对 mask 应用了transforms.GaussianBlur增强常见于分类任务迁移导致 mask 像素值变为 0~255 连续值mask 0.5判定产生亚像素偏移。解决transforms中 mask 专用 pipeline 仅保留Resize和ToTensor()删除所有涉及插值的 transform。5.4 现象DataLoader 返回的 mask shape 为 [B, 3, H, W]原因Image.open(mask_path).convert(L)失败PNG 保存为 RGB 模式返回三通道图。解决加载后立即检查mask.mode强制mask mask.convert(L)并assert len(mask.getbands()) 1。5.5 现象训练速度骤降 5 倍GPU 利用率 10%原因num_workers0时Windows 系统默认spawn方式初始化 worker重复加载大型模型权重。解决Windows 用户必须将DataLoader调用包裹在if __name__ __main__:下并设置torch.multiprocessing.set_start_method(spawn)。6. 进阶技巧用 Grad-CAM 定位模型“注意力盲区”反向优化数据标注6.1 为什么 Grad-CAM 比 IoU 更早暴露标注缺陷IoU 是全局指标当模型在Sphynx_249.jpeg上 IoU0.82 时你无法知道它是否把猫眼区域判为背景——而 Grad-CAM 可视化能直接显示模型认为“重要区域”集中在躯干却忽略头部。这往往意味着标注时漏标猫耳/鼻尖真实缺陷或模型被长毛品种如British_Shorthair_275.jpeg的毛发纹理误导模型缺陷。我们用captum库实现轻量级 Grad-CAMfrom captum.attr import LayerGradCam from captum.attr import visualization as viz def gradcam_visualize(model, img_tensor, target_layer, save_path): model.eval() img_tensor img_tensor.unsqueeze(0).requires_grad_(True) # [1,C,H,W] # 初始化 Grad-CAM cam LayerGradCam(model, target_layer) cam_attr cam.attribute(img_tensor, target1) # target1 表示前景类 # 可视化叠加热力图到原图 img_np img_tensor.squeeze(0).permute(1,2,0).numpy() cam_np cam_attr.squeeze(0).sum(0).numpy() # sum over channels fig, ax plt.subplots(1, 2, figsize(12, 6)) ax[0].imshow(img_np), ax[0].set_title(Original) ax[1].imshow(img_np), ax[1].imshow(cam_np, cmapjet, alpha0.5) ax[1].set_title(Grad-CAM Overlay) plt.savefig(save_path, dpi150) plt.close() # 示例对 U-Net 的 bottleneck 层encoder 最后一层做 CAM # target_layer model.encoder[-1][-1] # ResNet-style encoder # gradcam_visualize(model, test_image, target_layer, gradcam_sphynx.png)6.2 基于 Grad-CAM 的标注修正工作流步骤操作工具验证方式1. 抽样从 test set 随机选 50 张图生成 Grad-CAM 热力图captummatplotlib热力图应覆盖猫狗全身非局部聚集2. 标记异常标记“热力图缺失区域”如耳朵、尾巴尖对应的原图LabelImg矩形框或 GIMP自由选区导出 CSVfilename, missing_region_xmin, ymin, xmax, ymax3. 重标注用cv2.fillPoly在对应 mask 上补全缺失区域OpenCV Python补全后visualize_sample确认边缘连续4. 重训练用修正后的 mask 重新训练对比 val Dice 提升PyTorch提升 ≥0.015 为有效修正实测效果在我处理Egyptian_Mau子集时通过 Grad-CAM 发现 12 张图漏标胡须重标注后 val Dice 从 0.847 → 0.862提升 0.015 等效于增加 800 张高质量标注。这比盲目扩增数据集高效得多。从那以后我每次启动新分割项目都会先跑 3 轮 Grad-CAM —— 不是为了调参而是为了确认我的数据真的教会模型“看哪里”了吗希望帮到你。本文还有配套的精品资源点击获取
返回列表