PyTorch快速入门:我在实际项目中的一次“从零上手”经历
引言:为什么会写这篇文章?

大家好,我是老李。作为一名在一线技术团队混了多年的老码农,我深知每一个刚接触深度学习的同学内心有多忐忑——尤其是面对像PyTorch这样灵活但又有点复杂的框架时。
最近我们团队接了个AI图像分类的活儿,目标是用深度学习模型来识别工厂生产线上的不良品。说实话,在开始之前,我对PyTorch只是略懂皮毛,连nn.Module和nn.functional的区别都说不太清楚。但正因为我们项目的工期紧、数据多样,我不得不硬着头皮去啃这个框架,并在这个过程中踩了不少坑,也积累了不少经验。
今天这篇文章,我会结合我们在实际项目中遇到的真实问题、走过的弯路和最终落地的效果,带大家一起快速上手PyTorch。文章不会干巴巴地讲API,而是会围绕一个完整的实战场景展开——毕竟,真正的好技术,一定是写出来的,不是背出来的。
一、项目背景与挑战:为什么选PyTorch?

我们的项目是一个基于图像的瑕疵检测任务。客户提供了几千张来自工业摄像头拍摄的产品图片,其中一部分标注了是否存在缺陷,另一部分则是未标注的数据。目标是在有限的时间内构建一个可用的深度学习模型,部署到边缘设备中做实时检测。
选择PyTorch的原因主要有两点:
- 灵活性高:我们不确定哪类网络结构效果最好,需要不断尝试不同的Backbone(比如ResNet、MobileNet等),而PyTorch对自定义网络的支持非常友好。
- 调试方便:PyTorch的动态计算图模式(Dynamic Computation Graph)让调试变得更加直观,不像TensorFlow那种静态图,一旦跑错了就只能看报错信息猜原因。
不过,刚拿到这个任务的时候,其实心里还是挺没底的——虽然知道PyTorch很火,但自己完全没有实战经验。而且,时间只有不到三周,还要兼顾模型训练、调优、部署这三块内容。
二、我的PyTorch第一课:从Hello World开始

既然是“快速入门”,咱们就不绕弯子。第一步,当然是把环境搭建起来。
我们使用的开发环境如下:
- Python版本:3.9
- PyTorch版本:2.0+
- CUDA版本:11.8
- 硬件:NVIDIA RTX 3060 × 1
安装方式很简单,直接使用conda或者pip即可:
# pip 安装命令
pip install torch torchvision torchaudio
为了验证一下是否安装成功,我们先来写个简单的“Hello, Tensor!”程序:
import torch
x = torch.rand(5, 3)
print("Random tensor:\n", x)
如果你能看到类似下面这样的输出,恭喜,你的PyTorch环境已经准备好了!
Random tensor:
tensor([[0.3357, 0.7841, 0.6442],
[0.1320, 0.3163, 0.4616],
[0.4471, 0.4536, 0.6001],
[0.8200, 0.5436, 0.3244],
[0.2417, 0.1058, 0.6407]])
但别急着高兴得太早,接下来才是真正的考验。
三、实战上手:图像分类模型初探
3.1 数据处理:怎么喂数据给模型?
我们这次任务的数据格式比较简单,就是一个标准的图像文件夹结构:
dataset/
├── train/
│ ├── class1/
│ └── class2/
└── val/
├── class1/
└── class2/
PyTorch 提供了 torchvision.datasets.ImageFolder 来加载这种目录结构的图像数据,非常方便。我们只需要配合 transforms 对图像进行预处理。
这是我们在项目中用于数据增强和预处理的部分代码:
from torchvision import transforms, datasets
transform_train = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
train_dataset = datasets.ImageFolder(root='dataset/train', transform=transform_train)
val_dataset = datasets.ImageFolder(root='dataset/val', transform=transform_train)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=32, shuffle=False)
需要注意的地方是:
- Resize操作是为了适配我们选用的ResNet-18模型(输入尺寸要求为224×224)
- Normalize是对RGB图像的标准归一化操作(ImageNet预训练的参数)
这段代码写完后,我发现第一次运行时GPU显存爆满了,后来查了一下才发现是 batch_size 设置得太大。改成16之后,一切恢复正常。
3.2 模型构建:PyTorch到底怎么搭模型?
我们一开始就打算使用PyTorch官方提供的ResNet系列作为基线模型。代码大概是这样写的:
import torchvision.models as models
model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)
num_ftrs = model.fc.in_features
model.fc = torch.nn.Linear(num_ftrs, 2) # 我们有两个类别
刚开始我还以为只要把最后的全连接层改了就行,结果发现模型根本跑不动……后来才意识到,原来有些模型默认是冻结所有层的,你必须手动指定哪些参数要优化。
正确的做法是:
for param in model.parameters():
param.requires_grad = False # 冻结前面的所有层
# 只训练最后的分类头
model.fc = torch.nn.Linear(num_ftrs, 2)
model.fc.requires_grad = True
这一招叫做“迁移学习(Transfer Learning)”,在样本量不多的情况下尤其有效。我们的训练集只有一千多张图片,靠这点数据重新训练一个完整的ResNet肯定是不行的,所以只能借用预训练权重 + 微调最后一层的方式。
3.3 训练流程:损失函数和优化器该怎么选?
我们这里的目标是一个二分类问题,所以损失函数自然选的是交叉熵(CrossEntropyLoss)。至于优化器,我们一开始用的是SGD,后来试了Adam,发现在训练初期收敛更快。
训练的核心逻辑大致如下:
import torch.optim as optim
import torch.nn as nn
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
for epoch in range(10):
model.train()
running_loss = 0.0
for inputs, labels in train_loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item() * inputs.size(0)
print(f'Epoch {epoch+1} Loss: {running_loss/len(train_dataset)}')
在这段训练代码里,有几个容易踩的坑:
model.train()和model.eval()的切换非常重要,尤其是在有 dropout 或 batchnorm 层的时候。loss.backward()要在每次迭代前清空梯度,否则梯度会累加。- 不要忘记将数据和模型都放到同一个设备上(GPU or CPU)。
四、踩过的坑:那些年我和PyTorch斗智斗勇的日子

坑一:模型不收敛?原来是学习率太高了!
刚开始我们用了0.01的学习率,结果训练几轮下来,loss根本不下降。后来换成0.001,再配合学习率衰减策略,才慢慢开始收敛。
我们最后加入了一个学习率调度器:
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
每过7个epoch就把学习率打一折,这对后期微调特别有用。
坑二:DataLoader读取慢得离谱?
这个问题我们一开始没注意到,直到训练一个epoch都要花十几分钟才发现不对劲。排查发现是因为我们没有设置num_workers,导致默认是单线程读取数据。
解决办法很简单:
train_loader = torch.utils.data.DataLoader(
train_dataset,
batch_size=16,
shuffle=True,
num_workers=4,
pin_memory=True
)
增加多个线程同时读取,加上pin_memory能显著提升数据载入速度,特别是在GPU训练时。
坑三:保存模型的时候只保存了state_dict却忘了参数配置?
有一次我们在保存模型的时候只保存了状态字典,结果恢复模型时出错了——因为模型结构变了。后来我们统一改为保存整个模型对象:
torch.save(model, 'best_model.pth') # 更安全的方式
当然更规范的做法是分开保存 state_dict 和配置,但这对于快速验证阶段来说太复杂了。小项目的话,直接保存整个模型反而省事。
五、效果总结:我们得到了什么?
经过三周的努力,我们最终训练出了一个准确率达到93%的图像分类器,在验证集上的F1分数也达到了不错的水平。虽然不是SOTA,但在客户现场部署后反馈良好,基本满足了他们的需求。
更重要的是,通过这个项目,我们不仅掌握了PyTorch的基本使用方法,还积累了几个关键的经验:
- 使用预训练模型 + 迁移学习可以大大加快开发进度;
- 数据增强和预处理对小数据集尤为关键;
- 学习率控制和优化器选择直接影响训练稳定性;
- GPU资源管理和内存优化不可忽视。
六、几点建议:写给刚入门的你

如果你也是刚刚开始学PyTorch,或者正在自学深度学习,我真心给你提几点建议:
✅ 从小项目起步,不要太贪心
PyTorch本身功能强大,但刚入门时不需要追求大模型或复杂架构。先把基础搞扎实,比什么都重要。
✅ 多写代码,少看文档
PyTorch的文档确实权威,但我发现最好的学习方式是边查边写。比如你想看看nn.Conv2d怎么用,不如直接建个项目试试。
✅ 关注社区,别死磕官方教程
PyTorch的官方教程不错,但更适合有一定基础的人。很多实用技巧其实是GitHub开源项目里学到的,建议多关注一些优秀的实战项目。
✅ 别怕报错,学会debug
深度学习训练过程总会遇到各种各样的报错,这个时候千万别慌,要学会从错误信息中找线索,比如设备不一致、形状不匹配、数据归一化错误等等。
七、尾声:工具是死的,人是活的
写到这里,我想说一句掏心窝子的话:PyTorch只是一个工具,真正决定成败的,是你对问题的理解和工程实现的能力。
在这次项目中,我看到很多人为了炫技去追求最前沿的模型,结果训练半天跑不出结果;也有人坚持用简单的方法,反而快速解决了问题。这就是经验和直觉的力量。
如果你问我为什么要分享这些内容,我想说:因为我曾经也是一个站在门口不敢进去的小白。希望这篇文章,能帮你少走点弯路,早点入门PyTorch的大门。
文末彩蛋:完整Demo GitHub地址
如果你想亲手体验上面提到的内容,我已经把完整代码打包上传到了GitHub仓库:
👉 https://github.com/xxx/pytorch-image-classifier
欢迎star & fork,有问题也可以随时私信我交流。
Happy coding! 🚀

评论 0