CT肝脏4分类数据集:从文件结构到训练落地的保姆级实操笔记
简介医学图像分类是深度学习在医疗领域的重要应用方向其效果高度依赖规范化的数据集。一个标准的数据集不仅需要图像样本还需合理的类别划分与标签映射才能支撑模型训练与评估。在实际工程中采用ImageFolder结构组织数据配合类别字典文件可降低数据加载复杂度而借助ImageNet预训练模型进行迁移学习则能有效缓解医学图像样本不足的问题。从肝脏CT影像分析到病灶识别高质量的医学影像数据集配合可视化校验工具能显著提升模型开发的效率。本文围绕CT肝脏4分类任务提供一套含训练集、验证集、测试集及可视化脚本的完整数据集并分享从数据加载、模型训练到评估的实践经验助力快速落地医学图像分类应用。 CT肝脏4分类数据集从文件结构到训练落地一份保姆级实操笔记做医学图像分类的朋友应该都有同感找数据集永远比写模型更磨人。公开数据集要么太大下载困难要么标注格式五花八门好不容易下下来还得自己整理成ImageFolder结构。更别说医疗影像这种特殊场景数据本身就稀缺类别字典、划分逻辑、可视化校验这些环节稍有疏漏后面训练时就会冒出各种莫名其妙的问题。我这次整理了一份CT肝脏4分类数据集覆盖正常肝脏、肝囊肿、肝血管瘤、肝细胞癌四种常见类型。除了原始图像还附带已经划分好的训练集、验证集、测试集以及对应的类别字典文件JSON格式和Python可视化脚本。也就是说数据拿到手之后不需要再做任何预处理工作直接从可视化脚本开始确认数据质量然后就能进入训练环节非常省心。这篇博文我会把这套数据集的整体设计思路、文件格式、可视化工具的使用方法、以及实际训练时的注意事项都过一遍也会把我在使用过程中遇到过的一些坑和解决办法一并分享出来希望能帮刚接触医学图像分类的朋友省掉一些摸索的时间。1. 数据集整体设计思路1.1 为什么选择这4个类别CT肝脏分类任务在临床上非常常见而“4分类”这个设定实际上是经过了仔细考虑的。最常见的肝脏CT诊断场景就是区分正常肝脏和几种高发局灶性病变。我选定的4个类别分别是正常肝脏Normal肝脏实质无明显异常密度灶CT值均匀。肝囊肿Cyst圆形或类圆形低密度灶边界清晰CT值接近水0~20HU增强扫描不强化。肝血管瘤Hemangioma最常见的肝脏良性肿瘤典型表现为动脉期边缘结节状强化延迟期向心性充填。肝细胞癌HCC最常见的肝脏原发恶性肿瘤典型表现为“快进快出”强化模式。选择这4类一方面是因为它们的影像特征差异比较明显适合作为分类任务的基准另一方面也对应了临床上最常遇到的鉴别诊断场景——看到肝脏占位首先需要判断良性还是恶性而囊肿、血管瘤、HCC正好覆盖了最常见的三种情况。为什么不把类别做得更细我个人的经验是对入门级医学图像分类任务来说类别太多反而会增加标注难度和模型训练成本。4分类是一个很好的平衡点既能验证算法的有效性又不会因为类别过于细分导致样本不均衡问题过于严重。如果你的应用场景还需要区分肝脓肿、局灶性结节增生等其他病变完全可以在后续环节自行扩展。1.2 目录结构与交付文件说明这份数据集的目录结构非常清爽拿到手之后不用做任何额外的整理工作直接就能用。完整结构如下实际操作中以您下载到的压缩包为准CT_Liver_Classification/ ├── train/ │ ├── Normal/ │ ├── Cyst/ │ ├── Hemangioma/ │ └── HCC/ ├── valid/ │ ├── Normal/ │ ├── Cyst/ │ ├── Hemangioma/ │ └── HCC/ ├── test/ │ ├── Normal/ │ ├── Cyst/ │ ├── Hemangioma/ │ └── HCC/ ├── classes_dict.json ├── data_distribution.png ├── sample_grid.png └── visualize_data.pytrain、valid、test三个文件夹直接就是标准ImageFolder结构子文件夹名称即类别名。这个结构意味着你不需要写任何自定义Dataset类直接用torchvision.datasets.ImageFolder就能加载数据非常省事。classes_dict.json是类别字典文件一看就懂将类别名称映射为数字标签是监督学习的“翻译官”。visualize_data.py是Python可视化脚本能帮你快速检查数据划分是否合理、各类别样本是否均衡、图像格式是否统一。我在实际使用中建议拿到数据集后先跑一遍这个脚本确认数据质量没有问题了再进行训练。提示数据目录名称采用英文Normal/Cyst/Hemangioma/HCC这是刻意为之的。因为大多数深度学习框架对英文字符串的处理最稳妥避免中文路径导致编码问题。虽然现在很多框架已经支持中文路径但医疗数据本身已经够复杂了没必要在文件名上给自己增加风险。2. 核心文件解析类别字典与数据划分逻辑2.1 类别字典文件的格式与使用方法classes_dict.json文件内容非常直观本质就是一个将类别名称映射为整数标签的普通字典。内容大致如下具体键值以实际文件为准{ Normal: 0, Cyst: 1, Hemangioma: 2, HCC: 3 }这个文件的用法很明确——在训练和推理阶段模型输出的是一个0~3的整数索引它表示的就是该索引对应的类别。如果开发相关应用或模型推理程序时需要根据该字典将索引映射回可读的类别名称。我见过不少教程把类别映射关系写死在代码里比如idx_to_class {0: Normal, 1: Cyst}这样确实能用但扩展性很差。一旦新增类别、调整顺序就要改动代码。而将类别映射放到独立的JSON文件里你想怎么改就怎么改。后续做推理部署或者写WebAPI时直接读取这个JSON文件就行不用动代码逻辑。如果你用torchvision.datasets.ImageFolder加载数据它会自动生成一个class_to_idx属性。需要注意这个属性是按字母顺序排列的即Cyst(0)、HCC(1)、Hemangioma(2)、Normal(3)与我的classes_dict.json顺序不同。所以建议在使用时统一以classes_dict.json为准或者干脆用ImageFolder自动生成的映射。千万不要一会儿用JSON字典一会儿用ImageFolder自动生成的否则标签顺序错乱会直接导致训练结果全错。2.2 训练集、验证集、测试集的划分比例数据集已经按约7:2:1的比例划分完毕。在医学图像领域这个比例实际应用时效果很稳健我在多次训练中也验证了这一点训练集70%用于模型参数的学习和更新。验证集20%用于训练过程中的模型调参比如学习率调整、早停策略选择能及时发现过拟合。测试集10%用于最终评估模型的泛化能力模拟模型在面对从未见过的真实数据时的表现。注意如果样本总量不大10%的测试集可能只有几十张图像评估结果方差会比较大。在这类情况下可以考虑采用K折交叉验证或重复划分测试集进行多次评估。但作为基准测试7:2:1划分已经足够日常使用了。还有一个关键问题这批数据在划分之前做了类别分层采样。简单来说就是保证训练集、验证集、测试集中每个类别的样本比例都大致相同。举个例子如果HCC类总共100张那么训练集大约70张、验证集20张、测试集10张。分层采样的好处是能有效避免某类样本在某个子集中出现比例失衡特别是对医学图像中常见的类别不均衡情况来说这个处理真的很重要。另外在划分数据时需要确保同一病例的多个切片不会同时出现在训练集和测试集中。虽然原始数据已经提前做了处理但这一步还是要提出来因为如果同一病例的不同切片被拆到训练集和测试集中模型可能只是在“记住”同一个病人的影像特征而不是真正学习到疾病模式的泛化规律这会导致评估结果虚高。2.3 图像预处理说明这份数据集中的图像统一为8位灰度PNG格式单通道原始大小为512×512像素。选择灰度PNG的原因很实际兼容性好几乎所有图像分类框架都能直接处理PNG。体积可控灰度图像比RGB三通道图像小得多存储和读取效率更高。CT影像本身就是灰度图保留单通道不会丢失诊断信息反而能避免不必要的计算开销。如果希望用ImageNet预训练模型如ResNet、EfficientNet进行迁移学习需要将单通道灰度图转换为三通道RGB图像并将像素值归一化到ImageNet的均值和标准差。推荐做法是直接用cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)将灰度图复制到三个通道或者更高效的方式是在Dataset的__getitem__中用img.convert(RGB)在读取时完成转换这样不占用额外的磁盘空间。3. Python数据可视化脚本实操3.1 环境准备与依赖安装要运行可视化脚本需要先准备Python环境。建议使用Python 3.8或更高版本避免低版本在部分依赖库上出现兼容性问题。需要安装的依赖库如下pip install matplotlib numpy Pillow这三个库各司其职matplotlib绘图包括条形图、饼图、样本网格图。numpy数组运算用于统计计算和数据变换。Pillow图像读取和处理确保能打开各种格式的图片。如果你的机器上没有安装以上命令可以一步到位。如果下载速度慢可以加-i指定国内镜像源pip install matplotlib numpy Pillow -i https://pypi.tuna.tsinghua.edu.cn/simple3.2 运行可视化脚本统计分布与样本预览这份数据集自带的可视化脚本会完成两个核心任务绘制类别统计分布图和生成样本预览网格。流程如下python visualize_data.py脚本会在当前目录下生成data_distribution.png和sample_grid.png两个文件。data_distribution.png是一张条形图展示训练集、验证集、测试集中每个类别的样本数方便你一眼看出数据分布是否均衡。实际临床数据中HCC病例往往比肝囊肿更少如果某类样本数量明显偏少就要考虑在训练时使用类别权重或数据增强来缓解不均衡问题。sample_grid.png是一个网格图每个类别随机抽取几张图像并排展示让你直观检查图像质量、CT窗位、图像分辨率是否一致。这一步很关键能帮你发现一些隐蔽问题。比如我见过有些数据集里个别样本被错误地裁剪过或者缩放过直接在网格图里就能肉眼识别。3.3 可视化脚本的核心代码思路虽然数据集已经附带了一个可直接运行的脚本但我也把核心逻辑拆解出来方便需要定制化修改的朋友。统计各类别样本数的核心代码大致如下import os import json from collections import Counter def count_samples(root_dir): counts {} for split in [train, valid, test]: split_path os.path.join(root_dir, split) split_counts {} for cls_name in os.listdir(split_path): cls_path os.path.join(split_path, cls_name) if os.path.isdir(cls_path): n len([f for f in os.listdir(cls_path) if f.lower().endswith((.png, .jpg, .jpeg))]) split_counts[cls_name] n counts[split] split_counts return counts这段代码遍历train/valid/test三个目录统计每个子文件夹中的图像数量。注意用.endswith((.png, .jpg, .jpeg))来过滤非图像文件避免把缩略图或隐藏文件计入。生成样本网格图的代码逻辑更直观import matplotlib.pyplot as plt from PIL import Image def visualize_samples(root_dir, samples_per_class3): fig, axes plt.subplots(4, samples_per_class, figsize(12, 12)) for row, cls_name in enumerate(sorted(os.listdir(os.path.join(root_dir, train)))): cls_dir os.path.join(root_dir, train, cls_name) img_files [f for f in os.listdir(cls_dir) if f.lower().endswith((.png, .jpg, .jpeg))] for col in range(samples_per_class): img_path os.path.join(cls_dir, img_files[col]) img Image.open(img_path) axes[row, col].imshow(img, cmapgray) axes[row, col].set_title(cls_name) axes[row, col].axis(off) plt.tight_layout() plt.savefig(sample_grid.png, dpi150)这里有一个值得注意的细节imshow时传了cmapgray。因为CT图像是灰度图如果不指定colormapmatplotlib会默认用彩色映射viridis渲染出来的图会看起来花花绿绿的容易误导观察。4. 用这份数据集训练一个分类模型4.1 使用ImageFolder加载数据因为数据集已经整理成了标准的ImageFolder结构加载数据变得非常简洁import torch from torchvision import datasets, transforms from torch.utils.data import DataLoader data_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset datasets.ImageFolder(rootCT_Liver_Classification/train, transformdata_transform) valid_dataset datasets.ImageFolder(rootCT_Liver_Classification/valid, transformdata_transform) test_dataset datasets.ImageFolder(rootCT_Liver_Classification/test, transformdata_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) valid_loader DataLoader(valid_dataset, batch_size32, shuffleFalse, num_workers4) test_loader DataLoader(test_dataset, batch_size32, shuffleFalse, num_workers4)这段代码有几点需要说明关于Resize((224, 224))这是ImageNet预训练模型的默认输入尺寸。如果你的显卡显存比较充足也可以考虑用256×256或512×512的原始分辨率但需要相应地调整模型输入层或选择支持更大输入的模型结构。关于RandomHorizontalFlip这是最基础的图像增强策略之一。对于CT影像水平翻转不会改变解剖结构的诊断意义左右对称性在大多数肝脏病变中不是判别因素所以可以放心使用。但要注意不要使用随机裁剪和垂直翻转因为肝脏在腹腔中的相对位置、上下方向在临床解读中有特定意义过度空间变换可能导致模型学到错误的特征。关于Normalize这里用的均值是ImageNet的RGB均值。如果输入是三通道RGB由灰度图转换而来这样处理没问题。但如果你直接以单通道灰度图输入就需要自己计算数据集的均值和标准差或者直接用0.5作为均值、0.5作为标准差进行简单归一化。4.2 模型选型与迁移学习实践对于CT肝脏4分类任务在数据量不算特别大的情况下通常几千张我强烈推荐使用迁移学习。实践中最稳的做法是使用在ImageNet上预训练过的ResNet系列模型。具体代码如下import torch.nn as nn import torchvision.models as models model models.resnet18(pretrainedTrue) num_features model.fc.in_features model.fc nn.Linear(num_features, 4)如果这里使用resnet18参数量较小训练速度快适合快速验证追求更高精度可以使用resnet50但训练时间会增加不少。根据个人实践在医学图像小数据集上resnet18往往不比resnet50差多少关键是做好数据增强和正则化。迁移学习有两种常用策略这里一并说清楚策略一冻结特征提取层只训练分类头for param in model.parameters(): param.requires_grad False for param in model.fc.parameters(): param.requires_grad True这种方式训练速度快、不容易过拟合适合数据量很少的情况。先把分类头训练收敛再考虑解冻部分层进行微调。策略二全模型微调for param in model.parameters(): param.requires_grad True这种方式模型能更好地适应医学图像的特殊纹理特征效果上限更高但需要更精细的学习率调节通常建议从较小的学习率开始比如1e-4或5e-5还要配合早停策略防止在验证集上过拟合。我的实践习惯是先用策略一训练5~10个epoch再切换到策略二用较小学习率微调全部参数效果往往比直接全量微调更稳。4.3 训练过程的坑损失函数与类别不均衡这4个类别的样本数量大致均衡划分时做了分层采样所以直接使用标准交叉熵损失函数nn.CrossEntropyLoss()是没问题的。但如果后续你扩展了类别或者自己的数据分布不均衡就需要考虑类别权重方案。使用类别权重调整损失的代码如下class_counts torch.tensor([counts[Cyst], counts[HCC], counts[Hemangioma], counts[Normal]]) weights 1.0 / class_counts.float() weights weights / weights.sum() * len(weights) # 归一化 criterion nn.CrossEntropyLoss(weightweights)注意这里的顺序必须与模型输出类别索引的顺序严格一致。我在实际项目中被坑过类别顺序和权重顺序错位导致模型损失一直不降排查了好久才发现是权重顺序搞错了。此外训练时建议使用torch.optim.lr_scheduler.ReduceLROnPlateau或CosineAnnealingLR来动态调整学习率。医学图像分类任务通常收敛较慢动态学习率能显著提升最终精度。5. 常见问题与排查技巧实录5.1 问题速查表我在使用这份数据集的完整过程中整理了一些典型问题以表格形式呈现问题现象可能原因解决方法训练loss不下降学习率太大或太小初始学习率设置为1e-3微调时1e-4观察前几个batch的loss变化验证集准确率远低于训练集过拟合增加数据增强旋转、对比度变化、添加Dropout、使用权重衰减加载图像时报“Cannot identify image file”有损坏图片或非图像文件混入用可视化脚本检查目录删除损坏文件或加try-except跳过预测阶段标签与真实类别不符类别字典使用不一致统一使用classes_dict.json作为唯一标签映射来源灰度图训练效果差可能归一化方式不对确认灰度图范围0-255是否已通过ToTensor归一化到0-1也检查是否需要直方图均衡化显存不足输入尺寸太大或batchsize过大减小图像分辨率、batchsize或启用梯度累积数据集划分后每类样本数差异较大原始数据本身不均衡或划分未做分层采样使用分层采样重新划分或训练时使用类别权重5.2 医学图像特有的避坑经验CT窗宽窗位问题CT影像的窗宽Window Width和窗位Window Level直接影响图像对比度。肝囊肿在宽窗下可能不明显而HCC在窄窗下特征更清晰。公开数据集的CT图像通常已经使用了统一的窗宽窗位设置但如果你自行扩展数据请务必保持一致的窗宽窗位否则模型会学到窗位差异而非疾病特征。标签泄露问题这是医学图像分类中最隐蔽也最致命的错误之一。如果一个病人的多个CT切片被同时分到训练集和测试集模型性能会被严重高估。使用这份数据集前如果你要把自定义数据加进来必须确认同病人数据已被正确划分不要手动随机拆分。数据维度统一检查所有图像尺寸是否一致、是否需要统一为正方形。大多数分类网络要求固定输入尺寸如果不统一预处理时要么resize要么填充。对于CT图像我会选择先resize到统一尺寸因为填充会引入非生理结构的黑色区域可能干扰模型。5.3 评估模型的正确姿势训练完成后千万别只盯着准确率看。对于医学图像分类混淆矩阵是必须看的。如果HCC经常被误判为Hemangioma说明模型在良恶性肿瘤的鉴别上存在明显短板这时候需要做的是增加HCC的样本量或做针对性数据增强而不是盲目调模型结构。评估脚本参考from sklearn.metrics import confusion_matrix, classification_report model.eval() all_preds [] all_labels [] with torch.no_grad(): for inputs, labels in test_loader: inputs inputs.to(device) outputs model(inputs) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_names[Cyst, HCC, Hemangioma, Normal])) print(confusion_matrix(all_labels, all_preds))这份报告能告诉你每个类别的精确率Precision、召回率Recall和F1分数。临床上特别重视召回率——漏诊一个恶性肿瘤的代价远高于把良性误判为恶性所以如果某个恶性类别的召回率不理想模型就需要继续优化。从这个角度看classification_report比单纯准确率更有实际指导意义。6. 数据集的扩展思路6.1 扩展到更多类别如果你的目标是处理更复杂的肝脏疾病分类比如6分类甚至8分类可以在这份4分类数据集的基础上补充新的类别样本如肝脓肿、局灶性结节增生、转移性肝癌然后重新生成classes_dict.json即可。注意新增类别后要重新检查各类别样本量是否均衡必要时对原有类别进行欠采样或对新类别进行过采样。6.2 从图像分类升级到目标检测或分割图像分类只是第一步。如果实际项目需要定位病灶位置或者评估病灶大小可以将这份数据集作为基础扩展到目标检测用YOLO或Faster R-CNN或语义分割用U-Net。常见做法是保留原始CT图像在专业医生指导下对病灶区域进行精细标注生成COCO格式或VOC格式的标注文件。这个扩展路径比较自然因为分类数据集的图像质量是经过筛选的作为后续标注的基础完全够用。6.3 尝试多模态融合CT图像只是诊断信息的一部分。如果有条件获取对应的临床文本数据如检验报告、病理结果可以尝试图文多模态模型将CT影像特征与临床文本特征进行融合通常能提升分类性能。这也是目前医学影像AI研究的一个热门方向。我在实际使用这份数据集时最大的感受是一份整理规范、附带完整工具链的数据集能帮研究者和开发者节省至少一周的重复劳动。特别是对刚入门医学图像分类的同学来说与其纠结“找不到数据集”不如先把这份已经划分好的CT肝脏4分类数据集跑通从可视化检验到模型训练再到评估结果完整走一遍流程对理解整个医学图像分类Pipeline会有很大帮助。最后再分享一个我自己的小习惯每次拿到一份新的医学图像数据集无论对方说是处理好的还是没处理好的我都会先跑一遍可视化脚本亲眼确认每个文件夹的样本数量、图像尺寸、灰度范围、有没有损坏文件。花不了几分钟但能避免后面训练到一半才发现数据处理问题的尴尬局面。希望这篇笔记对你有帮助也欢迎在实际使用中有新的问题随时来交流。本文还有配套的精品资源点击获取