ARTICLE DETAIL

资讯详情

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

3D CNN医学图像分类作业实战:从数据读取到模型训练全流程解析

3D CNN医学图像分类作业实战:从数据读取到模型训练全流程解析 简介这份资源面向机器学习、深度学习方向的课程学习者提供一套基于3D卷积神经网络完成医学图像分类的完整课程大作业方案适合期末大作业、课程设计或新手入门实践。压缩包共48个文件约11.48MB以18个Python源码文件为核心辅以pickle、h5模型与数据文件、csv结果记录、txt与md说明文档等覆盖数据加载、模型定义、训练与测试全流程。代码注释较为完整部署门槛低下载后按说明即可运行。项目围绕3D医学影像数据展开包含dataloader、models等模块划分并给出训练脚本、测试脚本与结果文件便于读者理解三维卷积在医学图像分类中的具体实现方式也能据此复现实验、调整网络结构或迁移到相似任务。目前已有440人学习下载可作为课程作业参考与深度学习实战练手素材。1. 从一份 3D CNN 医学图像分类作业说起它到底能跑出什么结果如果你正在为机器学习课程大作业发愁尤其是选题卡在「医学图像分类」这个方向上这份基于 3D 卷积神经网络的源代码包大概率能让你少熬两个通宵。它不是那种只丢一个.ipynb让你自己猜的仓库而是把训练、验证、测试、结果输出整条链路都铺好了train.py、test.py、dataloader、models、utils各司其职根目录还带着README.md和result/final_result.csv这样的产出物。换句话说你拿到手就能先跑一遍看结果再回头改模型、换数据、调参数。这份资源适合三类人一是期末大作业需要交完整代码和文档的同学二是想入门 3D CNN 但不知道从哪下手的初学者三是需要快速验证医学图像分类 pipeline 的从业者。它用 Python 写依赖 PyTorch 生态数据以.pickle和.h5两种格式出现说明作者在数据加载上做过兼容处理。接下来我会按「先跑通、再改懂、后避坑」的顺序把这份源码拆开讲清楚。2. 环境搭建与数据格式把 p3.pickle 和 p4.h5 喂进模型之前2.1 依赖安装与目录结构确认拿到压缩包解压后第一件事不是急着python train.py而是先看清楚目录里有什么。根目录下mylib是核心库里面又分了dataloader、models、utils三个子模块data文件夹放训练和验证数据result放输出结果。常见做法是先用tree或ls -R把结构打印出来确认__init__.py都在位否则 Python 导入会直接报ModuleNotFoundError。# 查看项目结构确认关键文件存在 find . -maxdepth 3 -type f -name *.py | sort # 预期看到 train.py test.py test_pickle.py test_h5.py 以及 mylib 下的模块这段命令的作用是快速定位所有 Python 文件避免因为解压不完整导致后续步骤白做。参数-maxdepth 3控制递归深度防止在数据目录里翻太久。如果你看到mylib/models/下没有__init__.py需要手动补一个空文件这是 Python 包导入的硬性要求。依赖方面核心是 PyTorch、numpy、h5py、pandas。我一般会先建一个干净的环境避免和系统里的老版本冲突python -m venv venv_3dcnn source venv_3dcnn/bin/activate # Windows 用 venv_3dcnn\Scripts\activate pip install torch torchvision numpy h5py pandas scikit-learn这里不写死版本号是因为 3D CNN 对 PyTorch 版本不算挑剔但如果你用的是比较新的 CUDA 显卡建议装对应 CUDA 版本的 torch。装完后用python -c import torch; print(torch.cuda.is_available())验证一下返回True说明 GPU 可用训练会快很多返回False就只能用 CPU3D 卷积在 CPU 上跑起来会非常慢这是第一个需要心理准备的地方。2.2 pickle 与 h5 两种数据格式的读取差异项目里同时出现了p3.pickle、p4.pickle、p5.pickle和p4.h5这不是作者随意为之而是医学图像数据常见的两种存储方式。.pickle是 Python 对象序列化读取快、写法简单但跨语言兼容性差.h5是 HDF5 格式适合存大规模数组支持分块读取和压缩。test_pickle.py和test_h5.py就是分别用来验证这两种格式读取是否正常的脚本。import pickle import h5py import numpy as np # 读取 pickle 文件通常是一个字典或数组 with open(p3.pickle, rb) as f: data_pickle pickle.load(f) print(pickle 类型:, type(data_pickle)) if isinstance(data_pickle, dict): for k, v in data_pickle.items(): print(k, np.array(v).shape) # 读取 h5 文件注意 h5 内部是类似字典的层级结构 with h5py.File(p4.h5, r) as f: def print_h5(name, obj): if isinstance(obj, h5py.Dataset): print(name, obj.shape, obj.dtype) f.visititems(print_h5)这段代码先加载 pickle 并打印类型和形状再用visititems递归遍历 h5 里的所有数据集。关键参数是rb和r分别表示二进制只读和只读模式千万别用w去打开否则会把原始数据覆盖掉这种翻车现场我见过不止一次。打印形状是为了确认数据维度是否符合 3D CNN 的输入要求通常是(样本数, 深度, 高度, 宽度)或(样本数, 通道数, 深度, 高度, 宽度)。如果形状里出现None或维度顺序明显不对就需要在dataloader里做转置或 reshape。提示先跑test_pickle.py和test_h5.py确认两种格式都能正常读取再动train.py。这两个脚本相当于数据层的「体检」跳过它们直接训练报错时你连是数据问题还是模型问题都分不清。3. 模型定义与训练流程3D 卷积核到底在卷什么3.1 3D 卷积层与 2D 卷积的本质区别医学图像分类和普通图像分类最大的不同在于「深度」这个维度。CT、MRI 这类数据是三维体数据如果硬压成 2D 切片就会丢失层与层之间的空间关系。3D 卷积核的尺寸是(kernel_d, kernel_h, kernel_w)它在三个方向上同时滑动所以能捕捉到病灶在纵深方向上的连续变化。这也是这份作业选择 3D CNN 而不是 2D CNN 的核心原因。在mylib/models/下模型定义通常会包含几个nn.Conv3d层后面接BatchNorm3d、ReLU和MaxPool3d。我一般会先看模型文件里的forward函数确认输入张量的维度顺序是(N, C, D, H, W)其中 N 是 batch sizeC 是通道数D 是深度。如果数据加载出来是(N, D, H, W)就需要在进入第一层卷积前用unsqueeze(1)补一个通道维度。import torch import torch.nn as nn class Simple3DCNN(nn.Module): def __init__(self, num_classes2): super().__init__() # 输入通道 1输出通道 16卷积核 3x3x3 self.conv1 nn.Conv3d(1, 16, kernel_size3, padding1) self.bn1 nn.BatchNorm3d(16) self.pool1 nn.MaxPool3d(2) self.conv2 nn.Conv3d(16, 32, kernel_size3, padding1) self.bn2 nn.BatchNorm3d(32) self.pool2 nn.MaxPool3d(2) self.global_pool nn.AdaptiveAvgPool3d(1) self.fc nn.Linear(32, num_classes) def forward(self, x): # x: (N, 1, D, H, W) x self.pool1(torch.relu(self.bn1(self.conv1(x)))) x self.pool2(torch.relu(self.bn2(self.conv2(x)))) x self.global_pool(x) # (N, 32, 1, 1, 1) x x.view(x.size(0), -1) # (N, 32) return self.fc(x)这段代码展示了一个最小可用的 3D CNN 结构。padding1保证卷积后空间尺寸不变MaxPool3d(2)把每个维度减半AdaptiveAvgPool3d(1)把任意大小的特征图压成 1x1x1这样全连接层的输入维度就固定了。参数num_classes根据你的分类任务改二分类就设 2多分类就设对应类别数。实际项目里的模型会比这个深但结构逻辑是一样的。3.2 训练脚本的关键参数与日志观察train.py是整条链路的入口。跑之前先打开看一眼重点确认四个东西数据路径、batch size、学习率、epoch 数。数据路径通常写在文件开头的常量里或者通过argparse传入。batch size 受显存限制3D 数据很吃显存8 或 16 是常见起点如果报CUDA out of memory就往下调到 4 甚至 2。# 典型训练启动命令具体参数名以 train.py 里的 argparse 为准 python train.py --data_dir ./data --batch_size 8 --lr 0.001 --epochs 50 --save_dir ./result学习率0.001是 Adam 优化器的常用值如果 loss 震荡厉害就降到0.0001。epoch 数看数据量小数据集 50 轮足够观察趋势。训练过程中重点看两个信号训练 loss 是否稳定下降验证集准确率是否跟着涨。如果训练 loss 降但验证准确率不涨说明过拟合了常见做法是加 dropout 或数据增强。result/final_result.csv和5_avg.csv这类文件通常是训练结束后生成的预测结果或交叉验证平均结果可以用来核对模型输出格式是否符合提交要求。注意3D 卷积的训练时间通常是 2D 的几倍甚至十几倍。如果只有 CPU建议先把 epoch 设成 2 跑通流程确认没有报错后再考虑上 GPU 或减少数据量。4. 推理与结果输出test.py 和 sampleSubmission.csv 怎么对上4.1 测试脚本的加载与预测逻辑test.py负责加载训练好的权重并对测试集做推理。它和train.py共享mylib里的模型定义和 dataloader所以只要训练能跑通测试一般不会出大问题。关键点是权重文件的路径要对项目里出现的p4.h5有可能是模型权重也有可能是数据文件需要根据README.md或代码里的load_state_dict调用来确认。import torch from mylib.models import Simple3DCNN # 以实际模型类名为准 device torch.device(cuda if torch.cuda.is_available() else cpu) model Simple3DCNN(num_classes2).to(device) # 加载权重注意 map_location 防止 GPU 权重在 CPU 上加载失败 state_dict torch.load(p4.h5, map_locationdevice) model.load_state_dict(state_dict) model.eval() # 推理时关闭梯度节省显存 with torch.no_grad(): for batch in test_loader: inputs batch[image].to(device) outputs model(inputs) preds torch.argmax(outputs, dim1)map_locationdevice这个参数很关键它解决了「在 GPU 上训练、在 CPU 上推理」的兼容问题。model.eval()会把 BatchNorm 和 Dropout 切到推理模式忘了写这句会导致预测结果不稳定。torch.no_grad()关闭梯度计算能明显降低显存占用。最后argmax取概率最大的类别作为预测标签。4.2 提交文件格式与结果校验sampleSubmission.csv是提交格式的模板final_result.csv是实际产出的结果。常见做法是先读模板看列名和行数再把自己生成的预测结果按同样格式写进去。如果列名是id,label那你的 DataFrame 就必须有这两列且行数要和测试集样本数一致。import pandas as pd submission pd.read_csv(sampleSubmission.csv) print(submission.columns.tolist(), submission.shape) # 假设 preds 是预测结果列表顺序与测试集一致 submission[label] preds submission.to_csv(result/final_result.csv, indexFalse)这段代码先检查模板的列名和形状再把预测结果填进去。indexFalse避免多出一列索引很多提交系统对格式很敏感多一列就直接判错。写完后建议再读回来head()看一眼确认没有错位。5_avg.csv如果是五折交叉验证的平均结果可以用它和final_result.csv做对比看单次训练和平均结果的差异有多大差异过大说明模型不稳定。5. 避坑与常见问题那些让作业从满分变重交的细节5.1 数据维度不匹配导致卷积层报错现象运行train.py时报RuntimeError: Expected 5D input (got 4D input)或Given groups1, weight of size [16, 1, 3, 3, 3], expected input to have 1 channel。原因通常是 dataloader 返回的张量少了通道维度或者数据本身是 2D 切片被误当成 3D 体数据。解决方法是先打印一个 batch 的 shape确认是(N, C, D, H, W)还是(N, D, H, W)如果是后者就在模型 forward 里加x x.unsqueeze(1)或者在 dataset 的__getitem__里补维度。5.2 显存不足与 batch size 的取舍现象训练刚开始就报CUDA out of memory或者跑几个 batch 后突然崩掉。原因是 3D 卷积的中间特征图占用显存远大于 2Dbatch size 稍大就撑爆。解决方法是把 batch size 降到 4 或 2同时可以用torch.cuda.empty_cache()清理缓存。如果还是不够就减小输入体积比如把深度或宽高从 128 降到 64但要注意这会损失部分空间信息准确率可能下降。5.3 pickle 与 h5 读取时的路径和权限问题现象FileNotFoundError或PermissionError。原因是代码里写的是相对路径而你在别的目录下执行脚本或者文件被其他进程占用。解决方法是用os.path.dirname(__file__)拼绝对路径确保无论从哪执行都能找到数据。Windows 下还要注意反斜杠转义建议统一用os.path.join或pathlib.Path。5.4 训练 loss 不下降或变成 NaN现象loss 一直是nan或者前几轮降一点后面就卡住。原因可能是学习率太大、数据没有归一化、或者标签编码有问题。解决方法是先把学习率降到1e-4检查输入数据是否做了ToTensor和Normalize再确认标签是不是从 0 开始的连续整数。如果用了CrossEntropyLoss标签必须是long类型不能是float。5.5 测试结果与提交格式对不上现象生成的 CSV 行数不对或者列名和模板不一致。原因是测试集顺序被打乱或者预测结果没有按原始 id 排序。解决方法是保留测试集的原始索引在生成提交文件前按 id 排序再和sampleSubmission.csv做一次merge校验确保每个 id 都有对应的预测值。6. 进阶技巧用 5_avg.csv 反推模型稳定性与改进方向跑通基础流程后真正拉开差距的是对结果的分析。项目里出现的5_avg.csv很可能是一个五折交叉验证的平均结果文件它的价值不在于提交而在于告诉你模型在不同数据划分下的表现波动有多大。我一般会把它和final_result.csv放在一起对比如果某几个类别的预测在两个文件里差异明显说明模型对这些类别的特征学习不够稳定可以考虑加数据增强或者调整类别权重。import pandas as pd final_df pd.read_csv(result/final_result.csv) avg_df pd.read_csv(result/5_avg.csv) # 假设两个文件都有 id 和 label 列 merged final_df.merge(avg_df, onid, suffixes(_final, _avg)) diff merged[merged[label_final] ! merged[label_avg]] print(预测不一致的样本数:, len(diff)) print(diff.head(10))这段代码把两次结果按 id 对齐找出预测不一致的样本。suffixes参数给同名列加后缀避免冲突。如果不一致的样本集中在某个类别就去检查那个类别的数据量是不是太少或者图像质量是不是有问题。常见做法是对少数类做重采样或者在 loss 里加weight参数。另一个进阶方向是可视化 3D 卷积核学到的特征。虽然这份源码不一定带可视化脚本但你可以自己写一个 hook 把中间层的激活值导出来用 matplotlib 画几个切片看看模型到底关注了哪个区域。如果激活值集中在图像边缘而不是病灶区域说明模型可能学到了背景噪声这时候就需要检查数据预处理是不是把关键区域裁掉了。提示改模型之前先备份原始代码每次只改一个变量跑完记录结果。3D CNN 的训练成本高盲目调参很容易浪费一整天。从那以后我每次拿到这类课程作业源码都强制先跑一遍test_pickle.py和test_h5.py确认数据层没问题再动模型。这个习惯帮我省下了大量排查「到底是数据错还是代码错」的时间。希望这份拆解能帮你顺利跑通这份 3D CNN 医学图像分类作业少走几个弯路。本文还有配套的精品资源点击获取
返回列表