PyTorch快速入门:深度学习框架初探

Dev工程师
2025-06-28 11:32
阅读 2319

引言:为什么选择PyTorch?

引言:为什么选择PyTorch?

记得刚进公司的时候,我被分配到了一个AI组,负责开发一个图像分类项目。那时我对深度学习的框架一知半解,只知道TensorFlow和PyTorch这两个名字,但在实际使用上完全没有经验。

刚开始几天,前辈们让我先学学PyTorch,说它在研究领域用得越来越多了。当时的我心里直打鼓:“真的比TensorFlow更简单吗?”但事实证明,PyTorch的确非常适合上手快、实验性强的项目,特别是对于我们这些刚接触深度学习的新手来说。

今天我就结合自己的亲身经历,分享一下我是如何从零开始上手PyTorch,以及在这个过程中踩过的坑、学到的经验。希望能帮你在学习PyTorch的路上少走点弯路。


项目背景与挑战:图像分类的起点

项目背景与挑战:图像分类的起点

我们当时做的项目是“商品类别识别”。用户上传一张图片,系统要能自动判断这是哪一类的商品——比如衣服、电子产品、食品等。数据集来自电商网站的公开数据(当然是脱敏处理过的),总共有10个类别,每个类别的样本数量大概在2万张左右。

听起来是不是挺标准的一个图像分类任务?但真正做起来才发现问题不少:

  • 数据不平衡:有些类别数据多得吓人,有些却特别稀有。
  • 图像分辨率参差不齐:有的清晰到像素级,有的模糊得像是隔着毛玻璃。
  • 硬件资源有限:我们只能跑单卡训练,没有GPU集群。

而我们的目标是,在两周内完成一个初步模型的训练和验证。时间紧、任务重,必须选一个上手快、调试方便的框架。于是,我们选择了PyTorch


解决方案:PyTorch到底适合哪些场景?

如果你也遇到类似的情况,比如:

  • 想快速实现一个想法
  • 需要频繁调整网络结构
  • 希望代码可读性高、便于调试

那PyTorch真的很适合你!

相比静态图机制的TensorFlow,PyTorch采用的是动态计算图(Dynamic Computation Graph),也就是大家常说的“define-by-run”,这意味着每一条操作都会立即执行,而不是先定义再运行。这对于调试非常友好,尤其是当你写完一行代码就想知道输出是什么样的时候,简直不要太爽。

我们的整体思路如下:

  1. 搭建基础模型:用预训练的ResNet-18作为backbone,微调部分层。
  2. 数据增强:统一输入大小、颜色空间转换、随机翻转等。
  3. 数据采样策略:使用WeightedRandomSampler来缓解类别不平衡的问题。
  4. 模型优化器选择:Adam + 学习率衰减策略。
  5. 性能评估:准确率+混淆矩阵分析关键错误来源。

整个过程都是用PyTorch来实现的。


代码实践:从零开始构建你的第一个PyTorch项目

1. 安装与环境配置

首先当然是安装PyTorch,建议用pip或者conda安装:

pip install torch torchvision torchaudio

这里要注意版本兼容性,推荐使用torchvision一起安装,因为很多常用的数据集和transform都封装在其中。

我们用了一个简单的Dataloader结构:

from torchvision import datasets, transforms
from torch.utils.data import DataLoader, WeightedRandomSampler

transform = 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='data/train', transform=transform)
val_dataset = datasets.ImageFolder(root='data/val', transform=transform)

# 构建sampler解决数据不平衡问题
targets = train_dataset.targets
class_counts = [i for i in list(Counter(targets).values())]
weights = 1. / np.array(class_counts)
samples_weights = weights[targets]

sampler = WeightedRandomSampler(weights=samples_weights,
                                num_samples=len(samples_weights),
                                replacement=True)

train_loader = DataLoader(train_dataset, batch_size=64, sampler=sampler)
val_loader = DataLoader(val_dataset, batch_size=64)

这一段看似简单,其实非常重要。尤其是那个WeightedRandomSampler,如果不加的话,训练中模型会偏向样本多的类别,严重影响最终效果。

2. 构建模型:迁移学习实战

接着我们用预训练的ResNet-18来初始化网络,并冻结前面几层:

import torch.nn as nn
import torchvision.models as models

model = models.resnet18(pretrained=True)

# 冻结所有层
for param in model.parameters():
    param.requires_grad = False

# 只微调最后的全连接层
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 10)  # 假设总共有10个类别
model = model.to(device)  # device可以是'cuda'或'cpu'

然后定义损失函数和优化器:

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.fc.parameters(), lr=0.001)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)

注意我们只对最后一层fc启用梯度更新,其余参数冻结。这样可以在保证性能的同时节省训练时间。

3. 开始训练

训练的逻辑其实和传统Python程序差不多,只是多了backward和step:

def train_model(model, dataloader, criterion, optimizer, device):
    model.train()
    running_loss = 0.0
    corrects = 0

    for inputs, labels in dataloader:
        inputs = inputs.to(device)
        labels = labels.to(device)

        outputs = model(inputs)
        _, preds = torch.max(outputs, 1)
        loss = criterion(outputs, labels)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        running_loss += loss.item() * inputs.size(0)
        corrects += torch.sum(preds == labels.data)

    epoch_loss = running_loss / len(dataloader.dataset)
    epoch_acc = corrects.double() / len(dataloader.dataset)

    return epoch_loss, epoch_acc

这个函数会在每个epoch中调用一次,每次训练完后打印loss和accuracy。


踩坑经验:那些年我们一起踩过的坑

🐞 1. 数据预处理搞错了Normalize参数

一开始我没仔细看文档,直接用了ImageNet的标准Normalization参数 [0.5, 0.5, 0.5],结果模型训练半天也没涨点。后来发现官方说明里已经指出应该用:

transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

这玩意儿直接影响输入特征的分布,千万不能乱改。

🐞 2. GPU内存溢出 OOM

有一次我贪心地把batch size设成了128,结果报错 CUDA out of memory。后来改成64还勉强能跑。这时候我才明白,PyTorch默认不会自动释放显存,一定要控制batch size和模型大小之间的平衡。

建议新手用以下方式检查当前显存占用情况:

torch.cuda.memory_allocated() / (1024 ** 2)  # MB

🐞 3. 误用了DataLoader的shuffle参数

之前我在用了WeightedRandomSampler的情况下又加上了shuffle=True,导致两个抽样逻辑互相冲突,训练效果大打折扣。后来查了文档才知道,一旦用了自定义的sampler,就不需要再设置shuffle=True了。


效果总结:两周后的收获

经过两周的努力,我们在测试集上的top-1准确率达到了82.3%,虽然不是很高,但对于一个刚起步的团队来说已经是不错的开始。

更重要的是,我们通过这个项目熟悉了PyTorch的工作流程,掌握了从数据加载、模型构建、训练优化、到评估指标的一整套工作链,为后续更复杂的模型打下了坚实的基础。


经验分享:给新手的几点建议

如果你现在正准备入坑PyTorch,不妨参考下面几个小建议:

✅ 1. 动态图 vs 静态图:按需选择

PyTorch适合快速原型设计和科研实验,TensorFlow更适合工业部署。如果你主要在做算法研究或者需要灵活性,PyTorch绝对是首选。

✅ 2. 多写Debug代码:print一切可能的东西

PyTorch不像Jupyter那样可以实时看到变量内容,所以你可以适当加入一些打印语句,观察输入shape、loss值是否合理。

print(f"Input shape: {inputs.shape}, Labels: {labels[:5]}")

✅ 3. 利用好torchvision和pretrained库

PyTorch生态里有很多现成的数据集、模型、Transform工具,不要自己造轮子。多查官方文档,事半功倍。

✅ 4. 多画图!可视化很重要

可以用Matplotlib或seaborn来画混淆矩阵、loss曲线,帮助发现问题所在:

import matplotlib.pyplot as plt
plt.plot(loss_history)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

✅ 5. 记录好实验日志

别怕麻烦,建议你为每一次训练建立一个log文件,记录learning rate、batch size、acc、备注等等。后期你会发现这些日志简直就是救命稻草。


后记:从PyTorch开始,不止于PyTorch

如今回过头来看,PyTorch只是一个工具,但它却打开了我进入深度学习世界的大门。它教会我如何动手、如何调试、如何迭代改进。

也许你也会在某个深夜因为一个bug抓耳挠腮,但我相信,只要坚持下去,你一定能感受到那种“终于跑通”的喜悦。

希望这篇文章对你有帮助,也欢迎留言交流你们在PyTorch实践中遇到的有趣故事。毕竟,技术之路从来不是一个人的战斗。


作者:一位互联网公司的AI开发者,经历过从0到1构建深度学习项目的全过程,热爱分享技术点滴。

评论 0

最热最新
暂无评论
Dev工程师Lv.1
0
影响力
0
文章
0
粉丝