从PyTorch入门到Unet图像分割:环境搭建与模型复现全攻略
2026/9/8 11:46:13 网站建设 项目流程

简介:面向深度学习入门与进阶群体的PyTorch学习代码与模型复现资源,适合正在学习神经网络实现、想通过完整代码理解Unet、Vision Transformer等经典架构的读者。资源整理自龙良曲老师的PyTorch教程,并在其基础上增加了多类模型的复现实验,覆盖图像分割、视觉Transformer、无监督自编码器以及扩散模型(DDPM)等方向,可帮助学习者将理论快速落实到可运行代码中。压缩包共259个文件,约18.88MB,以145个Python源码文件为核心,辅以25张结构图/效果图、15个配置文件、7个Markdown说明文档及若干Jupyter Notebook示例,层次清楚,便于对照源码查看训练与可视化结果。此外,Notebook示例与GIF动图直观展示了扩散模型、MAE可视化等任务的训练效果与生成过程,所附说明文档和目录结构也能帮助快速定位代码模块。目前已有186人学习下载,适合需要边读边练、快速梳理PyTorch建模流程的开发者。 做深度学习的朋友应该都刷到过龙良曲老师的《PyTorch深度学习实践》系列,这套教程最大的特点是代码干净、逻辑清晰,每一节讲完理论马上给一段能跑的代码,非常适合入门。我自己在带团队带新人的时候,也经常把这份代码仓库推给他们当第一份学习材料。不过很多人卡在一个地方:视频刷完了、代码也跑通了,但真正要拿一个模型出来做自己的任务时,又不知道从哪下手。这篇文章就是基于龙良曲的PyTorch学习代码,把从环境搭建到Unet图像分割模型复现的完整链路讲清楚,包括每一步为什么这么做、中间会踩哪些坑,希望能给正在学PyTorch或者准备复现Unet的朋友省点时间。

1. 内容整体设计与思路拆解

1.1 为什么选择这套代码作为复现基础

我第一次看龙良曲的代码仓库时,最大的感受是“够用、不绕”。很多教程代码为了展示某个技巧,会把简单的网络写得特别复杂,动不动就上自定义Layer、钩子函数、分布式封装,对新手来说信息量过载。龙良曲的代码不一样,线性回归就是线性回归,卷积网络就是卷积网络,每一段代码只解决一个核心问题,变量命名也直白,读起来像在跟一个同事对需求。

但光有代码还不够,学习路径也很关键。我的建议是按“基础模块 → 小型实战 → 语义分割”三步走。基础模块包括张量操作、自动求导、nn.Module的搭建流程,这些在龙良曲代码里都有对应章节;小型实战可以选择他代码里的MINIST分类或者简单的回归任务,把训练循环、loss计算、参数更新的流程跑通;最后再上Unet,这时候你对PyTorch的API已经有手感了,再把Unet拆开看,会发现它其实就是“卷积块 + 池化 + 上采样 + 跳跃连接”的组合,每一块都能在之前的代码里找到原型。

1.2 复现Unet时的核心决策点

Unet的复现并不是照抄结构就能出结果的,有几个决策点会影响最终效果。第一个是输入尺寸的选择,Unet内部做下采样和上采样时,特征图尺寸必须对齐,所以输入尺寸最好是16的倍数,比如256×256或512×512,省去对齐的麻烦;第二个是损失函数的选择,二分类分割常用BCEWithLogitsLoss,多分类常用CrossEntropyLoss,如果样本类别极度不平衡,建议加上Dice Loss做组合损失;第三个是数据增强策略,随机翻转、随机旋转、亮度抖动这几个操作在医学图像分割里几乎必用,能有效提升泛化能力。

我当时复现Unet时,把龙良曲代码里的训练循环模板直接拿过来改了改,发现它的通用性确实好:数据加载、模型实例化、优化器配置、epoch循环,这套结构换任何模型都适用。所以这篇文章写的是Unet,但你掌握的方法论可以迁移到DeepLab、PSPNet甚至Transformer类的分割模型上。

2. 环境搭建是第一个坎,先把这个搞定

2.1 用Anaconda创建隔离环境

不管你是Windows、Ubuntu还是CentOS,我都建议用Anaconda管理Python环境。很多人在系统Python里直接pip install torch,装完发现跟TensorFlow或者其他包冲突,最后整个环境乱七八糟,代码跑不了还找不到原因。用conda建一个独立环境,只需要三条命令:

conda create -n pytorch python=3.10 -y conda activate pytorch pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

这里Python版本选3.10是我实测比较稳的,3.8、3.9也能用,但新版本PyTorch对Python 3.10+的优化更好,而且很多第三方库(比如albumentations)对3.10的支持很完善。CUDA版本的选择要看你本机显卡驱动能支持的最高版本,用nvidia-smi查看驱动对应的CUDA版本,然后选择不高于它的PyTorch版本。NVIDIA官方文档里写明,CUDA版本向下兼容,比如驱动支持CUDA 12.2,那装cu121、cu118都没问题。

2.2 CPU版和GPU版的区别

如果只是练手跑跑小数据集,CPU版也够用,但Unet训练跑起来就慢了,一个epoch可能要好几分钟甚至更久。我的建议是只要显卡支持,一律上GPU版。判断显卡是否支持CUDA,先看是不是NVIDIA显卡,然后看驱动是否安装正确。集成显卡和AMD显卡就别想CUDA了,直接用CPU版走通流程更重要。

离线环境的话,比如内网服务器,可以先在有网的机器上下载好torch的whl包和依赖包,再拷贝进去用pip install --no-index --find-links=本地路径安装。很多人问为什么安装pytorch一直报错,大多不是版本选错,而是网络问题导致下载不完整,离线安装能绕开这个坑。

2.3 验证环境是否装好

装完之后一定要做一件事:验证CUDA是否真的可用。很多人只看了torch.__version__就以为装好了,结果跑训练时发现一直在用CPU,慢得离谱还不知道问题在哪。验证代码很简单:

import torch print(torch.__version__) # 例如 2.8.0 print(torch.cuda.is_available()) # True 才是真的可用 print(torch.cuda.get_device_name(0)) # 显示显卡型号

如果torch.cuda.is_available()返回False,通常有三种原因:一是PyTorch版本和CUDA版本不匹配,重新按对应版本的命令安装即可;二是显卡驱动版本太低,需要去NVIDIA官网更新驱动;三是Windows下没有正确安装NVIDIA驱动核心组件,可以重装驱动并勾选“执行清洁安装”。

3. 从龙良曲代码里拎出最核心的几个知识点

3.1 张量与自动求导:PyTorch的“记账本”

PyTorch最核心的设计就是用动态计算图记录每一步运算,训练时调用backward(),梯度就能自动回传到每一个参数上。龙良曲的代码里会专门讲张量和自动求导,这块一定要吃透,不然后面写自定义网络会非常痛苦。

张量操作可以类比成Excel里的单元格引用:你在A1写了个公式= B1 + C1,之后B1变了,A1跟着变,而且它能记住自己依赖了谁。PyTorch里设置requires_grad=True,相当于告诉框架“这个张量需要被跟踪”,之后所有用到它的运算都会被记进计算图。反向传播时,框架沿着计算图从最后一层往前推,把梯度送到每个参数手里。理解了这个机制,你就能明白为什么训练代码里每次都要调用optimizer.zero_grad()——不清零的话,上一次batch的梯度会累加到这一次,参数更新方向就乱了。

3.2 nn.Module的搭建范式和forward的关系

龙良曲代码里大量使用nn.Module和nn.Sequential,前者是模型基类,后者是便捷的容器。很多刚接触PyTorch的人会好奇:为什么定义了forward方法,调用时直接model(x)就行?这是因为nn.Module实现了__call__方法,它内部会先执行一些钩子和校验,再调用你写的forward。理解这一点很重要,如果你在模型内部直接调用self.conv.forward(x),就跳过了nn.Module默认的一些处理,比如hook、参数更新检查,虽然大多数时候不出错,但这是个坏习惯,保持使用model(x)的调用风格最稳妥。

3.3 训练循环的统一模板

龙良曲的代码刷完,你会发现所有训练代码都长一个样:前向传播、算loss、反向传播、优化器更新。这个模板是通用的,复现Unet时直接套用即可。

for epoch in range(num_epochs): model.train() for images, masks in dataloader: images, masks = images.to(device), masks.to(device) outputs = model(images) loss = criterion(outputs, masks) optimizer.zero_grad() loss.backward() optimizer.step()

这段代码值得反复抄写加理解。model.train()和model.eval()的切换也别忘了,它影响的是Dropout和BatchNorm的行为,训练模式下这两个层有随机性,评估模式下要冻结下来保证结果稳定。

4. Unet模型复现全流程

4.1 Unet结构拆解:编码器、解码器和跳跃连接

Unet最初是为了医学图像分割提出的,但现在已经泛化到各种像素级预测任务。它的结构像一个U形:左边是编码器,通过卷积和池化逐渐缩小特征图尺寸、增加通道数,提取语义信息;右边是解码器,通过上采样逐级恢复空间分辨率;中间用跳跃连接把编码器的特征图拼接到解码器对应层,把小尺度的高频细节传过去,弥补下采样丢失的位置信息。没有跳跃连接的Unet退化成一条直筒,分割边缘会很糊,效果大打折扣。

复现时,最小组成单元是DoubleConv:两次卷积加BatchNorm加ReLU激活。可以用循环生成四个编码器块,然后倒序生成对应的解码器块,最后用一个1×1卷积把通道数映射到类别数量上。代码如下:

class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv = nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True), nn.Conv2d(out_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True) ) def forward(self, x): return self.conv(x)

4.2 数据加载是容易忽略的重头戏

Unet的输入是图像和对应的掩码标注。PyTorch的Dataset需要实现三个方法:initlen__和__getitem。__getitem__返回一对样本和标签。实际写的时候,经常碰到图像尺寸不一致的问题,这时统一resize到256×256或者512×512即可。数据增强建议在__getitem__里面做,这样每个epoch看到的都是“新”的数据,相当于免费扩充了训练集。常用的增强包括随机水平翻转、随机旋转、随机缩放、色彩抖动等。

图像分割的数据集格式五花八门:有的是单通道PNG,有的是三通道彩色标注,还有的是JSON多边形标注要转换成掩码。我的习惯是先把所有数据统一处理成numpy数组,再用Dataset封装,避免在训练循环里处理各种格式转换,省心很多。

4.3 损失函数和评估指标的选择逻辑

选了BCEWithLogitsLoss还是CrossEntropyLoss,取决于你的任务类别数。二分类分割(比如分割前景和背景)用BCEWithLogitsLoss就行,它内部已经包含了sigmoid操作,所以模型输出层别额外加sigmoid;多分类分割用CrossEntropyLoss,内部包含softmax,输出层也别额外处理。有个常见的坑:如果训练时loss是降下来了,但预测出来的结果全黑或全白,大概率是推理时多做了sigmoid,或者样本里前景背景比例严重失衡。

评估指标方面,很多初学者只盯着loss看,但loss降不代表分割效果好,因为像BCE这种逐像素损失在类别不平衡时会偏向预测多数类。建议加上mIoU(均交并比)和Dice系数这两个指标。mIoU计算预测和真实标签的交集与并集之比,Dice系数在一定程度上等价于F1分数,两者在分割任务里是最常用的评估尺度。实测下来,我自己的经验是Dice Loss配合CrossEntropyLoss组合使用,对小目标的召回率有明显改善。

4.4 训练和推理的完整闭环

训练时需要把Dataset、DataLoader、模型、优化器、学习率调度器全部串起来。有一个细节容易被忽略:DataLoader的num_workers要根据机器情况设置,Windows下最好设为0,不然容易报多进程相关的错误;Linux下可以设成4或8,能明显加快数据加载速度。

推理阶段和训练有区别。训练时模型处于train模式,推理时要切到eval模式,且用torch.no_grad()包裹,否则每个中间张量都会被记录,显存很快爆掉。加上sigmoid处理输出后,阈值取0.5得到分割结果,再用颜色映射叠加到原图上可视化。整体流程跑通一遍后,你会觉得Unet其实没那么高深。

5. 常见问题与排查技巧实录

5.1 安装和环境类问题速查表

以下是几个我遇到最多、也最典型的安装问题,整理成表格方便对照。

问题现象可能原因解决办法
torch.cuda.is_available()返回False驱动版本低,或PyTorch的CUDA版本与驱动不匹配更新显卡驱动,用nvidia-smi确认支持的CUDA版本,重装对应版本的PyTorch
安装时提示找不到匹配版本没有指定正确的index-url,或Python版本过旧使用官网生成的命令,确保Python 3.8以上
内网环境无法在线安装无外网权限在能联网的机器上下载whl包及全部依赖,离线安装
启动器提示PyTorch与驱动版本不符驱动太旧,或PyTorch版本过新升级驱动,或换低版本PyTorch

5.2 训练过程中高频踩坑纪实

训练Unet时,最常碰到的就是显存不足(OOM)。解决办法不是一上来就换更大的显卡,先做三件事:减小batch size、降低输入分辨率、开启混合精度训练。PyTorch原生支持torch.cuda.amp,代码改动极小,显存占用能降低30%~50%,速度还能提升。

第二个高频坑是loss变成nan。这个问题90%出在学习率上,把学习率从0.001改成0.0005甚至0.0001经常会好很多。剩下的10%可能是数据里有NaN值,检查一下输入图像是否有异常像素。

第三个坑是模型输出尺寸跟标签对不上。Unet里卷积默认padding=0时,每经过一次卷积尺寸就会变小,解码器恢复时尺寸对不齐,最终输出尺寸和原图不一样。解法是卷积层统一加padding=1,或者输入尺寸设置为16的倍数。

5.3 分割效果差的原因排查

分割效果不好,问题往往不在模型结构,而在数据或训练配置上。首先是检查标签是否正确——有没有背景和前景像素标反,或者标签和图像没有对齐。其次看损失函数的权重,如果前景目标占比太小,BCE loss会倾向把所有像素预测为背景,此时给正样本加权重,或者换Dice Loss,效果立竿见影。最后看数据增强是否过于激进,旋转角度太大、亮度调整范围过大,可能导致模型学不到稳定的特征,反而掉点。

有一次我复现Unet在自建数据集上跑,前两个epoch还不错,后面突然loss骤降但验证集指标也在降,一看是标签处理写错了位置,训练和验证用了不同版本的预处理逻辑。这个问题后来成了团队代码Review的必查项:数据前后处理逻辑必须只写一份。

写在最后的几点操作体会

从龙良曲的入门代码到Unet的成功复现,我个人的体会是:学习PyTorch不需要一开始就啃源码,先把小的细节用熟,再把它们拼装成实际的项目框架。复现Unet最有价值的地方在于,它把卷积、池化、上采样、跳跃连接、数据增强、损失函数、训练评估这些零散知识点串成了一条完整链路。

如果你已经能跑通基础代码,下一步可以试着在Unet基础上做改进:把编码器换成ResNet或MobileNet做迁移学习,或者把跳跃连接改成带注意力的Attention Gate,再进一步试试Unet++、Unet 3+。这些改进方向在GitHub上都有开源实现,挑一个数据集从零开始复现一遍,收获会非常大。

最后分享一个我自己常用的调试技巧:训练过程中定期保存验证集里预测效果最差的几张图,肉眼观察模型在哪里输错了。这个习惯帮我快速发现过不少问题,比只看指标分数直观得多。希望这篇文章能让你在PyTorch学习和Unet复现的路上少走点弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询