FEATURED · 精选文章

医学深度学习毕设实战:ResNet+UNet+DeepLabV3+YOLOv5多任务PyTorch复现

发布时间 / 2026/9/7 2:56:18
来源 / 创域科博编辑部
栏目 / 资讯中心
医学深度学习毕设实战:ResNet+UNet+DeepLabV3+YOLOv5多任务PyTorch复现 医学深度学习毕设没思路ResNetUNetDeepLabV3YOLOv5多任务实战PyTorch完整复现每年毕业季都会有一批做医学图像方向的本科生、研究生卡在同一个问题上开题时兴致勃勃真正动手却发现——数据不会处理模型跑不起来复现论文像拆盲盒好不容易训练完还不知道怎么评估。尤其医学影像数据集普遍偏小、标注成本高、类别不平衡严重更让初学者手足无措。如果你现在正处于“毕设没思路”的状态我的第一个建议是不要一味追最新模型先把经典模型用扎实。ResNet、UNet、DeepLabV3、YOLOv5 这四个模型分别覆盖了医学图像分析中最核心的分类、分割、检测三大任务。把这条主线打通你不仅能跑通实验还能在论文里讲出一个逻辑自洽的故事。这篇文章会用 PyTorch 把这四个模型串起来讲清楚每个模型解决什么问题、代码怎么写、训练时有哪些坑以及如何把它们组合成一个完整的医学图像多任务分析流程。读完你至少能完成两件事一是理解医学图像深度学习的常见建模套路二是照着一套可复现的代码路径完成自己的毕设实验框架。1. 这篇文章真正要解决的问题很多同学拿到医学影像课题后第一反应是去 GitHub 找现成代码。但医学图像方向和自然图像方向有个很大的差异数据质量、数据分布和标注方式对结果的影响往往大于模型结构本身。你可能遇到的情况是导师给了一个 CT / MRI / 病理切片数据集说明是“分类任务”但数据量只有几百张既要做病灶分割又要做异常检测不知道这些任务怎么组织成一个系统代码能跑但训练 loss 不下降或者分割结果完全没有边界不会设计实验对比论文里只有一组结果答辨时无从解释。这些问题不是单独靠某个模型能解决的。你需要先建立一个整体判断医学图像深度学习不是“选一个模型跑起来”而是“围绕任务形式组合模型完成分析链路”。基于这个判断本文规划了一条主线用ResNet做医学图像分类例如病灶良恶性分类用UNet和DeepLabV3做医学图像分割例如病灶区域提取用YOLOv5做医学图像目标检测例如细胞、器官、病灶定位把三者串联成一个多任务流程并设计可对比的实验方案。这条主线的好处在于每个模型都是经过大量论文验证的经典结构网上资料多、PyTorch 实现成熟、复现难度适中没有脱离实际更重要的是它们恰好能对应医学图像分析里最常用的三种问题定义方式。无论你的具体题目是肺结节、皮肤镜图像、眼底图像还是病理切片都可以迁移这套框架。2. 四个经典模型分别解决什么问题在写代码之前先建立一个模型选型的基本认知。很多初学者把模型当成黑盒只看精度指标这是不够的。每个模型的结构设计对应了它擅长的任务类型。2.1 ResNet图像分类的骨干网络ResNet残差网络解决的核心问题是深层网络难以训练。网络层数加深后梯度消失和退化问题会导致准确率不升反降。ResNet 通过跳跃连接skip connection让梯度可以跨层传播使得几十层甚至上百层的网络也能稳定训练。用公式表示残差块的学习目标从拟合原始映射 H(x) 变为拟合残差 F(x)H(x)-x。这个设计看似简单但极大降低了深层网络的优化难度。在医学图像领域ResNet 通常不直接单独使用而是作为特征提取骨干网络加载 ImageNet 预训练权重后替换最后的全连接层来适配自己的分类任务。对于医学毕设ResNet 最常用的两种方式是直接用 ResNet 做二分类或多分类任务作为 UNet 的编码器、DeepLabV3 的骨干网络。2.2 UNet小样本医学分割的默认选择UNet 是医学图像分割领域最有影响力的模型之一。它的结构是典型的编码器-解码器对称结构左侧编码器逐层提取特征并缩小空间分辨率右侧解码器逐步恢复分辨率中间通过跳跃连接把同尺度的低级特征和高级语义特征拼接在一起。这种设计对医学图像极其友好原因有两个。第一医学图像数据集通常很小UNet 参数量适中配合数据增强就能训练第二跳跃连接保留了边缘、纹理等细节信息对病灶边界的分割更精确。所以当你面对 CT、MRI 或病理图像的分割任务时UNet 是首选的 baseline 模型。2.3 DeepLabV3基于空洞卷积的语义分割DeepLabV3 与 UNet 的出发点不同。它使用空洞卷积dilated convolution在保持特征图分辨率的同时扩大感受野并通过 ASPPAtrous Spatial Pyramid Pooling模块用不同空洞率的卷积并行提取多尺度上下文信息最后用编码器-解码器结构恢复细节。和 UNet 对比DeepLabV3 的优势在于多尺度上下文建模能力更强。如果你的分割目标大小差异很大或者在复杂背景下需要更强的语义信息DeepLabV3 通常会优于基础版 UNet。它的劣势也一样明显结构更复杂对显存要求更高在小数据集上不一定比 UNet 更容易训练。在毕设中更合适的做法是把 UNet 和 DeepLabV3 作为两条对比路线证明你针对自己的数据特点做了模型选择的思考而不是只跑一个模型就草草收场。2.4 YOLOv5单阶段目标检测的工程化标杆YOLOv5 是目前工程落地最广泛的目标检测模型之一。它把目标检测看成回归问题直接在图像上预测边界框和类别不需要两阶段检测器那样生成候选区域速度优势非常明显。YOLOv5 在医学场景中解决的任务通常是在一张完整切片或内窥镜图像中定位多个目标区域例如检测细胞、息肉、器官区域。它和分割的区别在于检测只给边界框不给像素级掩膜但检测速度更快也更适合视频或实时场景。从毕设角度来说YOLOv5 最大的价值是工程链成熟。官方仓库提供了完整的数据准备、训练、验证、推理和导出流程你可以把注意力集中到数据处理和实验设计上不需要从零实现检测器。2.5 四个模型协同工作的总体框架可以把这四个模型的关系理解为不同分析粒度任务类型代表模型输出形式医学典型应用图像分类ResNet整图类别良恶性分类、疾病分级语义分割UNet / DeepLabV3每个像素的类别病灶区域提取、器官分割目标检测YOLOv5边界框 类别细胞检测、多病灶定位特征提取骨干ResNet特征图为分割/检测模型提供预训练权重3. PyTorch 环境搭建与前置准备不管后续代码怎么写第一步一定是把 PyTorch 环境准备到位。这一步看似基础但很多人在这里浪费了大量时间。3.1 安装 Anaconda 并创建虚拟环境强烈建议使用 Anaconda 管理 Python 环境避免多个项目依赖互相污染。conda create -n medical_dl python3.9 -y conda activate medical_dlPython 版本不要盲目追求最新。PyTorch、CUDA 和 torchvision 等版本之间有对应关系建议先确认你要安装的 PyTorch 版本所支持的 Python 版本范围再选择 Python 3.8 或 3.9。这里用 3.9 是相对稳妥的选择。3.2 安装 PyTorchPyTorch 的安装方式建议从 PyTorch 官网的获取页面选择因为 CUDA 版本和安装命令会随版本更新而变化。如果你有 NVIDIA GPU先执行nvidia-smi查看驱动支持的 CUDA 版本再选择对应的 PyTorch wheel。没有 GPU 的读者也不要放弃下面的代码在 CPU 上也能运行只是训练时间会长一些。对于小规模医学数据集CPU 训练一个简化版 UNet 依然是可行的。# GPU 环境示例具体命令以官网为准 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118安装完成后用下面的代码验证环境import torch import torchvision print(PyTorch 版本:, torch.__version__) print(torchvision 版本:, torchvision.__version__) print(CUDA 是否可用:, torch.cuda.is_available()) if torch.cuda.is_available(): print(GPU 名称:, torch.cuda.get_device_name(0))如果torch.cuda.is_available()返回 False先检查驱动是否正常、PyTorch 的 CUDA 版本和驱动是否匹配。这是最常见的环境问题之一。3.3 安装辅助库除 PyTorch 外还需要一些常用库pip install matplotlib numpy opencv-python pillow tqdm tensorboard如果做分割评估建议安装医学图像领域常用的评估库例如 monai 或 segmentation-models-pytorch。本文示例中分割模型会直接基于 PyTorch 和 segmentation-models-pytorch 来简化实现。到这里环境准备完成。接下来进入数据部分。4. 医学图像数据组织与预处理医学图像深度学习的真实瓶颈往往在数据。下面先建立一套通用的数据组织方式后续所有模型都可以复用。4.1 分类任务的数据目录以二分类为例如“正常”和“病变”推荐目录结构如下data/classify/ train/ normal/ 001.jpg 002.jpg lesion/ 001.jpg val/ normal/ lesion/4.2 使用 torchvision.datasets.ImageFolderPyTorch 的ImageFolder可以直接读取这种目录结构from torchvision import datasets, transforms train_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset datasets.ImageFolder(data/classify/train, transformtrain_transform)这里的Normalize参数使用了 ImageNet 的均值和标准差。如果使用预训练模型做迁移学习这组参数是标准配置。4.3 医学图像数据增强注意事项医学图像增强要谨慎。翻转和轻微旋转通常安全但不要随意使用强烈的颜色抖动因为医学图像的色彩或灰度信息可能具有诊断意义。例如染色病理图像的颜色分布发生变化可能改变组织结构的视觉特征。如果数据集严重不平衡可以考虑对少数类做过采样复制使用加权随机采样器WeightedRandomSampler在损失函数中设置类别权重。5. ResNet 医学图像分类实战分类是最基础的医学图像任务。以 ResNet50 为例展示从加载预训练模型到训练微调的全过程。5.1 加载预训练模型并修改分类头import torch import torch.nn as nn from torchvision import models def create_resnet_model(num_classes, use_pretrainedTrue): model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V1 if use_pretrained else None) # 替换最后一层全连接层 in_features model.fc.in_features model.fc nn.Linear(in_features, num_classes) return model model create_resnet_model(num_classes2)model.fc是 ResNet 最后的全连接层。替换时需要保持前面的特征提取部分不变。这样可以利用 ImageNet 上学习到的通用特征显著减少医学小数据集上的训练难度。5.2 冻结与解冻策略对于小数据集有一个常用技巧先冻结骨干网络只训练新替换的全连接层训练几个 epoch 后再解冻骨干网络用更低的学习率微调。def freeze_backbone(model, freezeTrue): for param in model.parameters(): param.requires_grad not freeze # 最后一层始终可训练 for param in model.fc.parameters(): param.requires_grad True这种做法的道理是预训练模型已经学到了通用的边缘、纹理、形状特征小数据集不足以也不应该大改这些特征只负责把特征映射到新分类任务上。毕设实验中可以把“全量微调”“只训全连接层”“分阶段微调”做对比本身就是一个有价值的小实验点。5.3 训练循环import torch.optim as optim from torch.utils.data import DataLoader device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-4) train_loader DataLoader(train_dataset, batch_size16, shuffleTrue, num_workers4) def train_one_epoch(model, loader, criterion, optimizer, device): model.train() total_loss 0.0 correct 0 total 0 for images, labels in loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * images.size(0) _, preds torch.max(outputs, 1) correct (preds labels).sum().item() total labels.size(0) return total_loss / total, correct / total for epoch in range(10): train_loss, train_acc train_one_epoch(model, train_loader, criterion, optimizer, device) print(fEpoch {epoch1}/10, Loss: {train_loss:.4f}, Acc: {train_acc:.4f})训练完成后验证集上评估 accuracy、precision、recall、F1。医学场景样本不平衡时accuracy 会骗人F1 和 AUC 更有参考价值。6. UNet 与 DeepLabV3 医学图像分割实战分割是医学图像中最有辨识度的任务。下面给出 UNet 的最小实现以及 DeepLabV3 的迁移学习用法。6.1 UNet 核心结构实现UNet 的基本组成是双卷积块、下采样、上采样和跳跃连接。下面是基于 PyTorch 的简化实现适合理解结构实际实验中可以直接使用 segmentation-models-pytorch 等成熟库。import torch import torch.nn as nn class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue) ) def forward(self, x): return self.conv(x) class UNet(nn.Module): def __init__(self, in_channels3, num_classes1, features[64, 128, 256, 512]): super().__init__() self.downs nn.ModuleList() self.ups nn.ModuleList() self.pool nn.MaxPool2d(2) # 编码器 for f in features: self.downs.append(DoubleConv(in_channels, f)) in_channels f self.bottleneck DoubleConv(features[-1], features[-1] * 2) # 解码器 for f in reversed(features): self.ups.append(nn.ConvTranspose2d(f * 2, f, kernel_size2, stride2)) self.ups.append(DoubleConv(f * 2, f)) self.final_conv nn.Conv2d(features[0], num_classes, kernel_size1) def forward(self, x): skip_connections [] for down in self.downs: x down(x) skip_connections.append(x) x self.pool(x) x self.bottleneck(x) skip_connections skip_connections[::-1] for idx in range(0, len(self.ups), 2): x self.ups[idx](x) skip skip_connections[idx // 2] if x.shape ! skip.shape: x nn.functional.interpolate(x, sizeskip.shape[2:]) x torch.cat((skip, x), dim1) x self.ups[idx 1](x) return self.final_conv(x)这个实现的核心逻辑是编码器逐步下采样获取多尺度特征跳跃连接把每层编码器的输出传给对应解码器在通道维度拼接后再上采样。如果输入和跳过连接特征图尺寸不匹配使用interpolate调整尺寸这是实际训练时常遇到的细节问题。6.2 分割损失函数选择医学图像分割中二分类任务常用 BCEWithLogitsLoss 或 DiceLoss。Dice Loss 对小目标和类别不平衡更友好class DiceLoss(nn.Module): def __init__(self, smooth1.0): super().__init__() self.smooth smooth def forward(self, pred, target): pred torch.sigmoid(pred) pred pred.contiguous().view(-1) target target.contiguous().view(-1) intersection (pred * target).sum() dice (2.0 * intersection self.smooth) / (pred.sum() target.sum() self.smooth) return 1.0 - dice多分类场景则使用 CrossEntropyLoss但考虑加权重缓解样本不平衡。6.3 DeepLabV3 迁移学习实现不需要从零实现 DeepLabV3 时可以使用 segmentation-models-pytorch 库几行代码即可创建pip install segmentation-models-pytorchimport segmentation_models_pytorch as smp model smp.DeepLabV3Plus( encoder_nameresnet50, encoder_weightsimagenet, in_channels3, classes1, )这里encoder_name指定骨干网络encoder_weights加载 ImageNet 预训练权重classes是分割类别数。这种方式把 DeepLabV3 的工程复杂度降到了很低你可以把精力放在数据处理和训练调参上。6.4 分割训练的数据加载分割任务需要图像和掩膜一一对应。建议自定义 Datasetfrom torch.utils.data import Dataset from PIL import Image import numpy as np import os class SegDataset(Dataset): def __init__(self, image_dir, mask_dir, image_size(256, 256)): self.image_paths sorted([os.path.join(image_dir, f) for f in os.listdir(image_dir)]) self.mask_paths sorted([os.path.join(mask_dir, f) for f in os.listdir(mask_dir)]) self.image_size image_size def __len__(self): return len(self.image_paths) def __getitem__(self, idx): image Image.open(self.image_paths[idx]).convert(RGB).resize(self.image_size) mask Image.open(self.mask_paths[idx]).convert(L).resize(self.image_size) image np.array(image) / 255.0 mask np.array(mask) / 255.0 mask (mask 0.5).astype(np.float32) image torch.tensor(image, dtypetorch.float32).permute(2, 0, 1) mask torch.tensor(mask, dtypetorch.float32).unsqueeze(0) return image, mask注意需要确保 image 和 mask 的文件名在同一语义顺序上对应这是分割任务最容易出错的地方。训练前最好打印几个样本可视化核对 mask 是否与图像对齐。6.5 分割评估指标医学分割论文最常用的指标是 Dice 系数和 IoU。计算方式建议从验证集所有样本的累计混淆矩阵计算而不是逐图平均这样更接近实际效果。7. YOLOv5 医学图像检测实战检测任务在医学图像中同样常见。使用 YOLOv5 官方仓库是最快捷的方式。7.1 克隆仓库并安装依赖git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt这里需要注意YOLOv5 官方仓库更新迭代较快建议在做毕设时固定一个 release 版本方便复现。7.2 数据集格式准备YOLOv5 使用 LabelImg 或 Roboflow 等工具标注后会生成 txt 格式的标签文件。每一行表示一个目标class_id x_center y_center width height其中坐标都是相对图像宽高的归一化值。数据集目录结构data/yolo_dataset/ images/ train/ img001.jpg val/ img002.jpg labels/ train/ img001.txt val/ img002.txt7.3 编写数据集配置 yaml# data/medical.yaml train: data/yolo_dataset/images/train val: data/yolo_dataset/images/val nc: 2 names: [normal_region, lesion_region]nc是类别数量names是类别名称顺序不能乱。7.4 训练模型python train.py --data data/medical.yaml --weights yolov5s.pt --img 640 --batch-size 16 --epochs 100 --device 0常用参数说明参数含义--weights预训练权重yolov5s 是轻量版本--img训练输入尺寸一般取 640--batch-size批大小根据显存调整--epochs训练轮数--deviceGPU 编号CPU 可设为 cpu如果医学图像分辨率很高例如病理切片不建议盲目降低到 640 以下的尺寸否则小目标容易丢失。但也要考虑显存和训练时间可以用 640 起步做 baseline再尝试更大尺寸。7.5 推理与结果可视化python detect.py --weights runs/train/exp/weights/best.pt --source data/yolo_dataset/images/val --save-txt --save-conf--save-txt保存检测框坐标--save-conf保存置信度。可视化结果会保存在runs/detect/exp目录下。8. 多任务串联构建一个完整的医学图像分析流程把分类、分割、检测三个模型串联起来是毕设中体现系统性的关键。一个常见的串联思路是输入一张完整医学图像用YOLOv5定位图像中的候选目标区域对每个目标区域裁剪后用UNet / DeepLabV3进行像素级分割提取精确病灶边界对分割区域提取特征后用ResNet判断病灶性质。这种流程的设计逻辑是检测负责“在哪里”分割负责“边界有多准”分类负责“这是什么”。三个模型各司其职形成分析链路。虽然你也可以把检测和分类合并为检测模型的分类分支但在毕设论文中拆分开来更能体现不同方法的互补性。多任务设计的另一个价值是可以做消融实验。论文中你可以这样设计对比方案任务组合预期效果A仅分类分类准确率基线B检测 分类端到端目标定位 分类C检测 分割 分类完整分析链路每个模型的实验结果都要记录核心指标不仅是最终分数还包括训练曲线、样本可视化结果和失败案例分析。9. 常见问题与排查方法以下表格总结了医学图像 PyTorch 实战中最常见的几类问题问题现象可能原因排查方式解决方案CUDA 不可用驱动版本过低或 PyTorch CUDA 版本不匹配运行 nvidia-smi检查驱动查看 torch.version.cuda根据驱动版本重装匹配的 PyTorch训练 loss 一直不下降学习率过高或数据预处理错误先训练少量样本过拟合观察 loss检查标签是否有误降低学习率检查数据归一化与标签对齐分割结果全黑或全白损失函数不适合、mask 归一化错误可视化少量预测输出检查 sigmoid 或 softmax 是否缺失统一损失函数与输出激活方式加载预训练权重报错分类头维度与新任务不一致打印 model 结构检查 fc 层输入输出精确匹配 state_dict 并允许缺失或多余键显存不足batch size 过大或输入尺寸过大观察报错时使用的 batch 和图像尺寸降低 batch size或使用梯度累积数据集太小导致过拟合数据量不足、增强不够对比训练集和验证集准确率差距增加数据增强使用迁移学习引入 Dropout样本严重不平衡少数类样本太少统计类别分布使用加权损失函数或过采样10. 毕设实验设计与最佳实践10.1 实验记录从第一天开始就建立实验记录表。每跑完一组实验记录日期、数据版本、模型、超参数、指标、备注。毕业设计后期你一定会庆幸自己有这个习惯。推荐一个轻量级记录格式实验编号exp012 时间2025-xx-xx 模型UNet 数据肺结节数据集 v2裁剪到 256x256 预处理只做了归一化未做增强 超参数lr1e-4, batch16, epoch100, optimizerAdam 结果Dice0.87, IoU0.78 备注对边缘小目标分割效果差尝试加强旋转增强10.2 代码工程化建议训练、验证、预测的三个脚本要分开例如train.py、val.py、infer.py统一使用argparse或 yaml 管理超参数固定随机种子确保实验可复现每个 epoch 保存 checkpoint包含模型权重、优化器状态、epoch 数使用 TensorBoard 记录训练曲线不用只靠 print。10.3 数据集与合规提醒医学图像数据涉及患者隐私毕设中使用的数据必须确保来源合法去除可识别患者身份的信息。使用公开数据集时要确认数据的使用许可协议并在论文中正确引用来源。千万不能使用未授权爬取的数据。10.4“能跑通”和“能答辩”之间的差距如果你只想让代码跑起来跟着上面示例几个小时就能做到。但毕业设计真正考察的是你能不能解释为什么选择这些模型、数据是怎么处理的、实验是怎么对照的、最终结果有什么不足以及后续怎么改进。所以建议你至少完成以下三步用最小数据集跑通分类、分割、检测三个基础 baseline对你的真实数据做预处理和统计分析数据分布和难点设计至少两组对比实验记录训练曲线和评估指标而不是只跑出一组结果就结束。10.5 超参数调整的一些通用经验医学图像小数据集上有几个经验可以参考学习率从 1e-4 起步相对稳定比常用的 1e-3 更适合医学小数据输入分辨率比模型深度更重要在显存允许的情况下优先保证分辨率迁移学习基本是必选项没有预训练权重时小数据集训练效果很难保证分割任务中Dice Loss 往往比纯 BCE Loss 更容易收敛早停early stopping是防止过拟合的有效手段不要盲目把 epoch 拉到很大。写在最后回头再看本文开头的问题医学深度学习毕设没思路怎么办答案是不要把思路寄托在某一个“更好的模型”上而是把经典模型组合成一条可解释、可验证的技术链路。ResNet、UNet、DeepLabV3、YOLOv5 这四个模型分别对应分类、分割、检测三大医学影像基本任务用 PyTorch 复现并不需要多高深的技术真正体现工作量的是数据组织、实验设计和结果分析。下一步建议很简单照着本文第 5、6、7 章的代码先在自己的机器上把三个 baseline 分别跑通然后拿出你的真实数据完成预处理和人工标注检查最后设计好对比实验开始正式训练。遇到报错时回到第 9 节的排查表逐项对照大多数问题都可以在半小时内解决。祝你的毕设实验顺利答辩时能自信地讲出每个环节的设计逻辑和工程细节。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻