超声乳腺良性图像分割数据集:U-Net训练与避坑实践指南

发布时间:2026/9/28 1:44:17
超声乳腺良性图像分割数据集:U-Net训练与避坑实践指南 简介这是一份面向医学图像分析与深度学习研究者的超声乳腺良性图像分割数据集按背景与良性乳腺两类进行像素级标注classes文件详细说明类别信息。训练集约300对图像与mask测试集约130对图像与mask目录以images与masks分开存放便于直接接入U-Net、DeepLabV3等常见分割模型训练与验证。包内另附Python可视化脚本可随机抽取样本将原始超声图、GT掩膜图及GT在原图上的叠加效果保存到当前目录方便快速核对数据质量与标注效果。压缩包为7z格式共877个文件主体为875张png图像另含类别说明txt与可视化脚本py大小88.01MB。这份数据集已有958人学习对开展乳腺超声病灶分割实验、构建基准数据集或入门医学图像分割的读者来说可直接用这套数据划分与配套工具完成模型训练与结果可视化。1. 医学图像分割数据集上手这份超声乳腺良性图像资源能直接用来训练影像组学方向的从业者应该都有同感医学图像分割最耗时间的往往不是调模型而是找一份干净、带标签、划分好训练测试集的数据。看到这份“超声乳腺良性图像分割数据集”时我的第一反应是终于不用自己从 PACS 里翻病例、找医生标注、再手工划分数据了。它把训练集、测试集和对应的 mask 标签都打包好还附带一个可视化脚本拿到手先跑一遍脚本就能确认图像和标签是否对齐再进入训练流程。适合正在做医学图像分割、乳腺超声相关课题的学生和工程师也适合想快速验证 U-Net 等分割网络效果的从业者。下面从数据本身开始拆。2. 先摸清家底images/masks 目录结构、2 类别标签与训练测试集划分2.1 目录结构与文件命名这份数据集的目录划分很直接训练集和测试集各自拥有 images 与 masks 两个子目录。images 放原始超声图像masks 放对应的分割标签。从项目正文给出的文件名来看图像以“benign (编号).png”的方式命名括号里是序号说明数据在采集时做了匿名化处理不携带患者信息这对后续公开发表实验结果是友好的。我用常见的目录树展示一下内部结构dataset/ ├── train/ │ ├── images/ │ │ ├── benign (1).png │ │ ├── benign (2).png │ │ └── ... │ └── masks/ │ ├── benign (1).png │ ├── benign (2).png │ └── ... └── test/ ├── images/ │ ├── benign (300).png │ └── ... └── masks/ ├── benign (300).png └── ...这里的命名规律有一个隐藏信息同一张图的 image 和 mask 共享文件名只是放在不同目录下。这意味着训练时可以用文件名做关联而不是依赖目录顺序后续做数据划分或剔除坏样本时也不容易错位。2.2 “2 类别分割”到底怎么理解摘要里写的是“2 类别的分割背景、良性乳腺具体的查看 classes 文件”。很多初学者看到“2 类”会以为是“良性和恶性”二分类实际上这里的分割任务是像素级的每个像素要么属于背景要么属于良性乳腺区域。mask 图像中灰度值为 0 的区域代表背景灰度值为 255或 1的白色区域代表乳腺区域。这一点在开始训练前一定要确认。常见做法是写一段脚本统计 mask 的像素取值分布避免出现标签值和你预期不一致的情况。我一般会先打印出 mask 中出现的灰度值集合from PIL import Image import numpy as np mask np.array(Image.open(train/masks/benign (1).png).convert(L)) print(mask shape:, mask.shape) print(unique pixel values:, np.unique(mask))逻辑说明把 mask 读成单通道灰度图用 np.unique 列出所有出现的像素值。正常情况应该只输出 0 和 255 两类如果出现其他数值说明标签图里有噪点或标记遗留。参数说明convert(L) 强制转灰度可以避免 PNG 带 alpha 通道时读到四通道数据的干扰也可以用 np.bincount 统计各类别占比进一步确认类别是否均衡。2.3 训练集和测试集划分的合理性摘要说明训练集约 300 张、测试集约 130 张比例接近 7:3。在医学图像分割项目里这个划分比例算是可接受的。需要提醒的是这份数据集的划分是作者预先做好的不代表所有超声乳腺数据都适合直接套用这个比例。如果你的场景里数据量更少建议用 K 折交叉验证而不是固定划分。我第一次跑这份数据集时走了个弯路直接用训练集训练测试集评估然后发现指标不错后来换到另一批超声图像上效果明显下降。回头排查才发现超声图像的探头型号、增益设置、图像尺寸都会影响模型泛化。所以拿到任何数据集第一步都应该检查训练集和测试集是否存在明显的分布差异比如平均亮度、对比度、图像尺寸是否一致。import cv2 import glob train_images glob.glob(train/images/*.png) test_images glob.glob(test/images/*.png) train_mean [cv2.imread(p, 0).mean() for p in train_images[:50]] test_mean [cv2.imread(p, 0).mean() for p in test_images[:50]] print(train mean intensity:, sum(train_mean) / len(train_mean)) print(test mean intensity:, sum(test_mean) / len(test_mean))逻辑说明用灰度模式读取图像计算平均像素强度。如果训练集和测试集均值差异超过 1520建议考虑归一化方式或加入亮度扰动增强。参数说明这里只取了前 50 张做快速抽查如果时间充裕可以全量统计灰度读取用 0 参数cv2.imread 的第二个参数传 0 表示 IMREAD_GRAYSCALE。3. 从数据到模型U-Net 训练这批乳腺超声图像的整体流程3.1 为什么首选 U-Net 而不是先做目标检测或分类乳腺超声图像中的良性区域形状不规则、边界模糊和背景的灰度对比度有时很低。如果用目标检测框来做框很难贴合不规则病灶边缘如果直接做分类又丢掉了病灶位置信息。U-Net 的编码器-解码器结构天然适合这种像素级预测任务跳连skip connection能把浅层的边缘细节传递到深层特征在小样本医学图像上表现稳定。我一般不会上来就换 Attention U-Net 或 TransUNet而是先用标准 U-Net 跑通流程。原因很简单标准 U-Net 训练稳定、显存占用适中、代码资料多出了任何问题都容易排查。等你确认数据和标签没问题再往上加注意力模块才有意义。3.2 数据加载与预处理把 PNG 图像和 mask 对齐喂给网络医学图像分割的数据加载有几个关键点图像可能被存储为三通道 RGB 但内容实际是灰度mask 必须是单通道且像素值为 0 和 255或 0 和 1图像尺寸需要统一。以下是我常用的 DataLoader 写法import torch from torch.utils.data import Dataset, DataLoader from PIL import Image import numpy as np import glob class BreastUltrasoundDataset(Dataset): def __init__(self, image_dir, mask_dir, img_size(256, 256)): self.image_paths sorted(glob.glob(image_dir /*.png)) self.mask_paths sorted(glob.glob(mask_dir /*.png)) self.img_size img_size def __len__(self): return len(self.image_paths) def __getitem__(self, idx): img Image.open(self.image_paths[idx]).convert(RGB) mask Image.open(self.mask_paths[idx]).convert(L) img img.resize(self.img_size, Image.BILINEAR) mask mask.resize(self.img_size, Image.NEAREST) img_np np.array(img) / 255.0 mask_np np.array(mask) / 255.0 mask_np (mask_np 0.5).astype(np.float32) img_tensor torch.from_numpy(img_np).permute(2, 0, 1).float() mask_tensor torch.from_numpy(mask_np).unsqueeze(0).float() return img_tensor, mask_tensor dataset BreastUltrasoundDataset(train/images, train/masks, (256, 256)) dataloader DataLoader(dataset, batch_size8, shuffleTrue, num_workers4)逻辑说明图像统一转 RGB 并归一化到 01mask 转灰度后同样归一化再通过阈值判断强制转为 0 和 1 二值标签。mask 的 resize 必须用 NEAREST 插值因为线性插值会在边界产生灰色过渡值破坏分割标签的硬边界。参数说明img_size 选 256×256 是速度和安全性的折中如果你的显存足够可以改成 512×512 保留更多超声纹理细节batch_size 在显存 8GB 左右时设 8 比较稳妥。3.3 损失函数选择BCE 和 Dice 的配合逻辑训练分割模型时最常踩的坑是类别不平衡。超声图像里背景区域往往远大于乳腺区域如果只用二值交叉熵BCE模型会倾向于把所有像素预测为背景因为这样 loss 已经很低。解决方式是引入 Dice Loss按像素预测的类别区域重叠度计算损失对小目标更敏感。常见做法是把 BCE 和 Dice Loss 按权重相加比如loss 0.5 * bce_loss dice_loss。Dice Loss 的计算方式如下def dice_loss(pred, target, smooth1.0): pred torch.sigmoid(pred) intersection (pred * target).sum() dice (2.0 * intersection smooth) / (pred.sum() target.sum() smooth) return 1.0 - dice逻辑说明smooth 参数防止分子分母都为 0 时出现除零错误。可以看出 Dice Loss 不关心像素绝对数量只关心预测区域和目标区域的重叠程度这对小目标分割非常关键。参数说明smooth 通常取 1.0可以理解为拉普拉斯平滑如果你发现训练后期 loss 震荡可以尝试把 smooth 降到 0.1 让梯度更稳定。3.4 训练参数建议与完整训练循环对于约 300 张训练图的规模我建议用 AdamW 优化器初始学习率 1e-4配合余弦退火学习率调度。batch size 根据显存调节训练 100 epoch 左右。训练循环里记录每个 epoch 的 loss 和在验证集上的 Dice方便后续回溯。import torch.optim as optim model UNet(in_channels3, out_channels1) optimizer optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-5) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max100) for epoch in range(100): model.train() total_loss 0.0 for images, masks in dataloader: optimizer.zero_grad() outputs model(images) loss 0.5 * torch.nn.functional.binary_cross_entropy_with_logits(outputs, masks) loss loss dice_loss(outputs, masks) loss.backward() optimizer.step() total_loss loss.item() scheduler.step() print(fEpoch {epoch1}, Loss: {total_loss / len(dataloader):.4f})逻辑说明BCEWithLogits 内部已经包含 sigmoid所以 Dice Loss 里再手动做一次 sigmoid 不会冲突。两个损失相加后梯度会同时回传到模型训练过程中如果发现 loss 在某个值附近不再下降优先检查学习率是否过大或者 mask 标签是否出现错位。参数说明weight_decay 设为 1e-5 做轻微正则防止过拟合T_max 和 epoch 数保持一致。4. 效果怎么看自带可视化脚本、Dice/IoU 指标与 bad case 复盘4.1 数据集自带可视化脚本的用法这份数据集附带了一个图像分割可视化脚本功能是随机抽取一张图把原始图像、GT mask、GT 叠加在原图上的效果拼在一起展示并保存。这个脚本虽然简单但对验证数据质量很有价值。我第一次拿到数据集时先跑这个脚本发现有一张图的 mask 明显比原图中的病灶区域大一圈这种情况如果直接拿去训练模型会学到错误的边界。脚本的核心逻辑可以用下面的代码概括import matplotlib.pyplot as plt import random from PIL import Image import numpy as np idx random.randint(0, len(image_paths) - 1) img np.array(Image.open(image_paths[idx]).convert(RGB)) mask np.array(Image.open(mask_paths[idx]).convert(L)) overlay img.copy() overlay[:, :, 0] np.where(mask 0, 255, overlay[:, :, 0]) fig, axes plt.subplots(1, 3, figsize(15, 5)) axes[0].imshow(img); axes[0].set_title(Original) axes[1].imshow(mask, cmapgray); axes[1].set_title(GT Mask) axes[2].imshow(overlay); axes[2].set_title(Overlay) plt.savefig(visual_check.png, dpi150, bbox_inchestight)逻辑说明overlay 把 mask 区域在原图上标红便于肉眼观察标注边界是否贴合真实病灶。这段逻辑和项目自带脚本的思路一致你可以直接运行原脚本也可以按自己的需求改输出尺寸和颜色。参数说明dpi150保证保存的图片足够清晰方便放大看边缘细节随机抽取的方式决定了每次运行结果不同如果想复现某次结果需要固定随机种子。4.2 用 Dice 和 IoU 量化分割质量肉眼查看可视化结果只能定性判断要说服自己或别人模型有效还得用数值指标。分割任务最常用的两个指标是 Dice 系数和 IoU。Dice 衡量预测区域与真实区域的重叠程度IoU 更严格对过分割或欠分割更敏感。计算方式如下def calculate_metrics(pred_mask, gt_mask): intersection (pred_mask gt_mask).sum() union (pred_mask | gt_mask).sum() dice (2.0 * intersection) / (pred_mask.sum() gt_mask.sum()) iou intersection / union return dice, iou逻辑说明pred_mask 和 gt_mask 都是布尔数组。我一般会在测试集上逐张计算再取平均同时记录每张图的分项指标方便定位到是哪一类图像拉低了整体分数。参数说明注意 pred_mask 和 gt_mask 的数据类型必须一致否则位运算会出问题。4.3 边缘质量与失败样例过分割和欠分割怎么区分指标高不代表模型一定好尤其是医学图像分割边界质量比整体重叠度更重要。我复盘 bad case 时通常会看两类错误过分割预测区域比真实病灶大边缘向外膨胀和欠分割预测区域比真实病灶小边缘收缩。过分割常见的原因是模型无法区分病灶边界附近的低回声区域把正常组织也划进了预测区域欠分割则可能是因为病灶中心和背景灰度差异太小模型只分割出了高对比度部分。解决办法各不相同过分割可以尝试给损失函数加边界惩罚项或者在数据增强里加入更强的对比度变换欠分割可以尝试加深网络、增大输入分辨率或使用多尺度特征融合。我习惯在测试集上把所有预测结果导出成一张 montage按 Dice 从低到高排列优先看分数最低的十几张import torchvision grid torchvision.utils.make_grid(pred_images, nrow5, padding10) grid_np grid.numpy().transpose((1, 2, 0)) plt.imsave(bad_case_montage.png, grid_np)逻辑说明把一批预测结果拼成网格图方便快速浏览。结合每张图对应的 Dice 分数可以迅速发现哪些样例是模型系统性失败的而不是随机噪声。参数说明nrow5 表示每行放 5 张padding10 控制图像间距避免拼在一起难以分辨边界。5. 超声乳腺分割避坑指南五条踩过的坑与解法5.1 文件名带空格和括号glob 排序错位这份数据集的图像文件名是“benign (117).png”这种格式空格和括号都在文件名里。直接使用命令行按文件名拼接字符串或者写 shell 脚本时很容易因为空格没转义导致路径拼接错误。更隐蔽的问题是排序错位如果按字符串排序“benign (9)”会排在“benign (11)”后面因为字符顺序是逐位比较的。在 PyTorch 的 DataLoader 里用 sorted(glob.glob(...)) 就会得到错误的顺序导致 image 和 mask 对不上。解决方式是用自然排序或者用带零填充的数字序号重新命名。我通常的做法是在数据预处理阶段把所有文件统一重命名为固定数字格式for file in train/images/*.png; do num$(echo $file | grep -oP \(\d\) | tr -d ()) mv $file train/images/img_$(printf %03d $num).png done逻辑说明用正则提取括号里的数字序号printf 补零到三位数确保排序和视图都对齐。这个脚本同时处理 image 和 mask 两份目录跑完后目录会清爽很多。注意建议保留原始文件备份以防重命名后发现问题需要回溯。5.2 超声图像三通道但内容灰度通道数搞错让刚接触医学图像的同学最容易翻车的一点很多超声 PNG 是作为 RGB 三通道存储的但三个通道的内容几乎一样。如果不做处理直接喂给网络模型要额外学习“三个通道其实是同一个灰度图”这件事浪费参数量且容易过拟合。更隐蔽的是某些 PNG 带 alpha 通道直接读出来是四通道。解决方式是读图时统一转成灰度再复制三通道或者直接在输入层用 1 通道模型。我建议显存充足时还是保留三通道输入因为后续如果想换用 ImageNet 预训练模型三通道输入是必须的。核心逻辑就是保证喂给模型的数据通道含义完全一致。5.3 mask 是一种像素标签不是三种颜色的“涂色图”有同事把 mask 当成 RGB 图像读取直接plt.imshow(mask)发现显示出来是绿色的因为 matplotlib 对单通道图像默认用 colormap 渲染。这只是显示问题真正的坑在于把它当真值算损失时数值范围变成了 0255 而不是 01。Dice Loss 在这种数值错位下会计算出毫无意义的梯度。统一的做法是在 dataset 读取时强制convert(L)然后除以 255 并做阈值处理。千万不要省这一步。如果发现训练时 loss 一直在 0.7 附近不动先检查 mask 的数值范围是不是 0255。5.4 背景和病灶类别严重不平衡BCE 单独撑不住超声图像里背景占比经常超过 90%良性区域可能只占图像的 5% 到 10%。BCE Loss 是逐像素独立计算的模型只要把所有像素都预测为背景loss 就已经很低了。最终的结果就是模型输出全黑的掩膜Dice 直接为 0。我一般用 BCE Dice 组合损失必要时还可以给前景像素加权。如果你发现模型在验证集上全输出 0先检查是不是 loss 里缺少 Dice 项。这个数据类型下单独用 BCE 训练基本等于自杀。5.5 训练集和测试集图像尺寸不一致resize 后掩膜错位超声设备不同导出的图像尺寸可能不同。这份数据集看起来都是正方形的超声图像但训练和测试集的平均尺寸可能会有微小差异。直接 resize 到同一尺寸后病灶区域的比例关系理论上不会变但如果使用 padding 而不是 resize掩膜和原图的坐标就完全对不上了。我踩过这个坑原图是 800×600我 pad 成 800×800 再 resize 到 256×256结果 mask 也被 pad 了白色区域的位置整体偏移训练出来的模型边界全部错位。后来改成统一 resize不用 padding问题才解决。原因很简单resize 会按比例缩放内容而 padding 会改变内容在原图中的绝对位置。6. 多模型怎么选同一个测试集上做 Dice 对比的小设置当你用这份数据集把手头的 U-Net 跑通之后自然会想试试改进版本比如 Attention U-Net、DeepLabV3或者加入预训练编码器。这个时候最容易犯的错误是每换一个模型就改一批超参数最后指标变了也不知道是模型改进带来的还是参数调整带来的。我常用的做法是固定一组“公共实验设置”让所有模型在这组设置下比赛。具体来说固定训练轮数 100、初始学习率 1e-4、损失函数 0.5 * BCE Dice、batch size 8、输入尺寸 256×256其他一切保持不变。每个模型只在结构上做变化这样才能公平对比。完整流程我一般会这么组织先准备好一个 baseline 模型标准 U-Net跑完一轮记录最测试集 Dice再跑目标模型比如加入了注意力模块的 U-Net用完全相同的随机种子和数据增强策略。数据增强我只做水平翻转、垂直翻转和随机旋转旋转角度限定在 20 度以内这样不会改变乳腺病灶的基本形态。此外我还会把每轮的预测蒙版都保存下来最后挑一个中间 epoch 和一个最终 epoch 的预测结果做对比。中间 epoch 的预测如果比最终 epoch 还好说明模型开始过拟合训练轮数需要缩减或者加正则化。这一轮对比做完你基本能确定这个数据集的“标准答案”什么模型、什么参数、什么增强策略下能得到最好的分割效果。从做这个实验之后我每次拿到新的医学分割数据集都会强制走一遍这个流程先跑自带可视化脚本检查数据固定一组公共超参数跑通 baseline再逐个替换结构做对比。虽然麻烦但你会发现很多“换了模型涨了 3 个点”的结论其实换个随机种子就消失了。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询