ARTICLE DETAIL

资讯详情

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

乳腺细胞癌症分割数据集实战:50张图跑通医学图像分割全流程

乳腺细胞癌症分割数据集实战:50张图跑通医学图像分割全流程 简介本资源面向医学图像分割方向的初学者与算法实践者提供一套乳腺细胞癌症分割的二分类数据集可用于训练与验证语义分割网络帮助解决医学影像中病灶区域自动提取的入门实验需求。压缩包共103个文件以png图像为主101个另含1个txt类别说明与1个py可视化脚本整体约23.91MB采用7z格式打包。数据分为训练集与测试集训练集含40张原图及40张对应mask测试集含10张原图及10张mask图像分辨率统一为512×512mask为0与255的阈值图像0代表背景、255代表癌症细胞具体类别可在classes文本中查看。配套脚本无需修改即可直接运行随机抽取一张图片将原始图像、GT图像以及GT在原图上的蒙板结果一并展示并保存至当前目录便于直观核对标注质量。目前已有207人学习下载适合作为分割模型训练、掩膜可视化与数据预处理的练手素材。1. 乳腺细胞癌症分割数据集50 张图里藏着医学图像分割的完整闭环医学图像分割入门最卡脖子的从来不是模型结构而是找不到一份干净、带标注、能直接跑通的小数据集。公开的息肉分割、皮肤病变数据集动辄几百上千张下载慢、标注格式杂光做数据清洗就能劝退一批人。这份乳腺细胞癌症分割数据集走的是另一条路50 张 512×512 的 PNG 图像训练集 40 张、测试集 10 张每张图配一张同尺寸的 mask 标签像素值只有 0 和 255 两种0 是背景、255 是癌症细胞。它小到能在笔记本上几分钟跑完一轮又完整到包含 images、masks、classes 文本和一份可视化脚本适合刚接触医学图像分割、想先把「读数据—看标签—训模型—验结果」这条链路走通的人。下面按我实际拆包的顺序讲清楚它怎么用、参数怎么设、哪里容易翻车。2. 拆开数据包目录结构、标签语义与可视化脚本怎么跑2.1 目录结构与文件命名规律拿到压缩包解压后常见的目录组织是这样breast_cancer_seg/ ├── train/ │ ├── images/ # 40 张原图png │ └── masks/ # 40 张对应 maskpng ├── test/ │ ├── images/ # 10 张原图 │ └── masks/ # 10 张对应 mask ├── classes.txt # 标签类别说明 └── visualize.py # 可视化脚本文件名是07_1.png、04_8.png、10_1.png这种「编号_序号」的形式images 和 masks 两个目录里同名文件一一对应。这一点很关键做分割训练时图像和标签必须靠文件名配对任何一张图找不到同名 mask训练时就会报FileNotFoundError或者更隐蔽地读到上一批缓存。我一般会先跑一遍配对检查确认两边文件名集合完全相等再往下走。import os img_dir train/images mask_dir train/masks imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.png)} masks {os.path.splitext(f)[0] for f in os.listdir(mask_dir) if f.endswith(.png)} print(仅图片有:, imgs - masks) print(仅标签有:, masks - imgs) print(配对成功:, len(imgs masks))这段代码用集合差集找出单边存在的文件。os.path.splitext去掉扩展名后比较避免.png大小写或多余后缀干扰。正常输出应该是两个空集合加一个 40。如果差集非空先手动补齐或删掉孤立文件别带着脏数据进训练。2.2 mask 的像素语义与 classes.txt这份数据集的 mask 是单通道阈值图像素分布只有 0 和 255。0 对应背景255 对应癌症细胞区域。classes.txt里记录了具体类别常见是两行一行背景一行前景。这里有个新手最容易踩的点很多人拿到 mask 直接除以 255 当标签结果发现模型输出全是 0。原因是 255/2551看起来没问题但如果用某些框架的CrossEntropyLoss它期望的是类别索引 0/1 的整型张量而不是 0/1 的浮点。正确做法是先确认任务类型——二分类分割用BCEWithLogitsLoss配 0/1 浮点标签多分类用CrossEntropyLoss配 0/1 整型标签。import numpy as np from PIL import Image mask np.array(Image.open(train/masks/07_1.png)) print(shape:, mask.shape) # (512, 512) 或 (512, 512, 3) print(unique:, np.unique(mask)) # 期望 [0, 255] print(前景像素占比:, (mask 255).mean())np.unique是验证标签语义最快的手段。如果输出里出现 128、200 这类中间值说明 mask 被做过抗锯齿或压缩需要重新二值化。前景占比能帮你判断样本是否极度不平衡——乳腺细胞分割里前景往往只占几个百分点后面选损失函数时要考虑这一点。2.3 可视化脚本的运行与输出解读数据集自带的visualize.py是随机抽一张图把原图、GT 掩膜、GT 叠加在原图上的蒙版三张图并排展示并保存到当前目录。脚本不需要改路径就能跑前提是你解压后直接在数据集根目录执行。cd breast_cancer_seg python visualize.py运行后当前目录会多出一张拼接图。三栏分别是原始 RGB 图像、纯黑白 mask、红色半透明叠加图。叠加图是判断标注质量的关键——如果红色区域明显溢出到细胞外的空白区或者漏掉了明显的细胞团说明这份标注本身有噪声训练时要么接受这个上限要么手动修几张。我一般会连跑几次脚本随机多看几张对整体标注风格有个印象再开训。脚本里通常用matplotlib的subplot做拼接alpha参数控制叠加透明度想调得更清楚可以把它从 0.5 改到 0.3。3. 把数据喂进模型从 Dataset 到训练循环的落地写法3.1 自定义 Dataset 的同步增强50 张图属于极小样本直接训必然过拟合。常见做法是在 Dataset 里做在线增强但分割任务有个铁律图像和 mask 必须用同一组随机参数做几何变换否则标签就错位了。用albumentations可以一次性同步处理。import cv2 import numpy as np import albumentations as A from torch.utils.data import Dataset class BreastSegDataset(Dataset): def __init__(self, img_dir, mask_dir, size512): self.img_dir img_dir self.mask_dir mask_dir self.names sorted(os.listdir(img_dir)) self.tf A.Compose([ A.Resize(size, size), A.HorizontalFlip(p0.5), A.VerticalFlip(p0.5), A.RandomRotate90(p0.5), A.ShiftScaleRotate(shift_limit0.05, scale_limit0.1, rotate_limit15, p0.5), ]) def __len__(self): return len(self.names) def __getitem__(self, idx): name self.names[idx] img cv2.imread(os.path.join(self.img_dir, name)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) mask cv2.imread(os.path.join(self.mask_dir, name), 0) mask (mask 127).astype(np.float32) # 二值化到 0/1 out self.tf(imageimg, maskmask) img out[image].astype(np.float32) / 255.0 img img.transpose(2, 0, 1) # HWC - CHW return torch.from_numpy(img), torch.from_numpy(out[mask]).unsqueeze(0)A.Compose里的几何变换会同时作用于 image 和 mask这是分割增强和分类增强最大的区别。mask 127把 255 压成 1同时容忍轻微压缩噪声。transpose(2,0,1)是 PyTorch 要求的通道前置格式忘了这步会报维度不匹配。unsqueeze(0)给 mask 补一个通道维配合BCEWithLogitsLoss使用。3.2 损失函数与评价指标的选择前景占比低的时候纯 BCE 容易被背景主导模型学会全预测 0 就能拿到很高的像素准确率但 IoU 惨不忍睹。常见做法是 BCE 加 Dice 的混合损失。import torch import torch.nn as nn class BCEDiceLoss(nn.Module): def __init__(self, bce_weight0.5): super().__init__() self.bce nn.BCEWithLogitsLoss() self.bce_weight bce_weight def forward(self, logits, targets): bce_loss self.bce(logits, targets) probs torch.sigmoid(logits) inter (probs * targets).sum(dim(2, 3)) union probs.sum(dim(2, 3)) targets.sum(dim(2, 3)) dice (2 * inter 1e-6) / (union 1e-6) dice_loss 1 - dice.mean() return self.bce_weight * bce_loss (1 - self.bce_weight) * dice_lossbce_weight控制两项的配比0.5 是常用起点。Dice 里的1e-6防止空 mask 导致除零。评价指标建议直接算 IoU 和 Dice别只看 loss因为 loss 下降不代表分割边界变好。验证时把torch.sigmoid(logits) 0.5作为预测掩膜和 GT 比。3.3 训练循环与显存控制512×512 的图batch size 设 4 在 8G 显存上比较稳设 8 可能就 OOM。训练轮数不用太多50 张图 100 到 200 轮足够看到收敛趋势。from torch.utils.data import DataLoader import torch.optim as optim train_ds BreastSegDataset(train/images, train/masks) train_loader DataLoader(train_ds, batch_size4, shuffleTrue, num_workers2) model ... # 常见选 U-Net 或轻量 DeepLabV3 model model.cuda() criterion BCEDiceLoss().cuda() optimizer optim.Adam(model.parameters(), lr1e-3) for epoch in range(150): model.train() for img, mask in train_loader: img, mask img.cuda(), mask.cuda() optimizer.zero_grad() logits model(img) loss criterion(logits, mask) loss.backward() optimizer.step()num_workers2在 Windows 上偶尔会卡死遇到就改成 0。学习率 1e-3 配 Adam 是安全起点如果 loss 震荡就降到 3e-4。每 10 轮存一次权重小数据集上过拟合来得快早停比硬训到底更划算。4. 避坑与排查这份数据集上最容易翻车的五件事4.1 现象训练 loss 正常下降但预测全是黑图原因通常是标签没二值化mask 读进来还是 0/255BCEWithLogitsLoss把 255 当成极大正样本梯度爆炸后模型输出饱和。解决在 Dataset 里强制(mask 127).astype(np.float32)并在第一个 batch 打印mask.max()确认是 1.0。4.2 现象可视化脚本报FileNotFoundError原因是没在数据集根目录执行脚本里用的是相对路径。解决cd到解压后的根目录再跑或者把脚本里的路径改成绝对路径。别在别的目录用python /path/to/visualize.py相对路径会以当前工作目录为基准。4.3 现象图像和 mask 尺寸对不上报维度错误512×512 是标准尺寸但个别图可能被工具重新保存成 513 或 511。解决在 Dataset 里统一A.Resize(512, 512)别依赖原始尺寸一致。同时检查 mask 是不是三通道三通道 mask 要取单通道再二值化。4.4 现象验证集 IoU 远低于训练集50 张图里训练 40、测试 10测试集太小单张图波动就能让指标跳十几个点。解决别把测试集当唯一标准从训练集里再切 8 张做验证或者做 5 折交叉验证。小数据集上报告单一 IoU 数字意义有限多看几张预测叠加图更实在。4.5 现象增强后 mask 出现灰色边缘原因是用了带插值的几何变换mask 被双线性插值出中间值。解决几何变换对 mask 用最近邻插值albumentations默认对 mask 就是最近邻但如果你手动用cv2.resize就要显式指定interpolationcv2.INTER_NEAREST。5. 小数据集的进阶玩法交叉验证、伪标签与边界后处理50 张图想榨出更多信息单次训练切分太浪费。我一般会做 5 折交叉验证把 50 张图分成 5 份每份 10 张轮流当验证集其余 40 张训练。这样每个样本都参与过验证指标更稳还能顺带产出 5 个模型做集成。集成时把 5 个模型的 sigmoid 输出平均再阈值化边界通常比单模型干净。import numpy as np def ensemble_predict(models, img): probs [torch.sigmoid(m(img)).cpu().numpy() for m in models] mean_prob np.mean(probs, axis0) return (mean_prob 0.5).astype(np.uint8)阈值 0.5 不是金科玉律。医学分割里如果更怕漏检把阈值降到 0.3 能提高召回代价是误检增多。可以画一条不同阈值下的 Dice 曲线挑拐点。另一个技巧是伪标签用训练好的模型在测试集 10 张图上预测把置信度高的预测当额外标签和原训练集合并再训一轮。小数据集上这招偶尔能涨一两个点但前提是初始模型别太差否则错误会被放大。我一般只在 Dice 已经过 0.7 之后才尝试。边界后处理也值得一试。预测出的 mask 常有孤立小噪点用形态学开运算去掉再保留最大连通域能明显改善视觉效果。import cv2 def postprocess(mask): kernel np.ones((3, 3), np.uint8) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) num, labels, stats, _ cv2.connectedComponentsWithStats(mask) if num 1: largest 1 np.argmax(stats[1:, cv2.CC_STAT_AREA]) mask (labels largest).astype(np.uint8) return maskMORPH_OPEN先腐蚀后膨胀去掉小噪点。connectedComponentsWithStats找出所有连通域保留面积最大的那个。注意如果一张图里确实有多个独立细胞团这步会误删所以先看几张可视化结果再决定要不要加。从那以后我每次拿到新的分割数据集都强制先跑一遍配对检查、np.unique看标签、可视化脚本随机抽三张这三步走完再写训练代码。这份乳腺细胞癌症分割数据集规模小、结构清晰正好适合把这套流程固化成习惯。希望帮到你。本文还有配套的精品资源点击获取
返回列表