ARTICLE DETAIL

资讯详情

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

PyTorch入门实战:从环境搭建到MNIST图像分类

PyTorch入门实战:从环境搭建到MNIST图像分类 提到深度学习框架PyTorch 是一个绕不开的名字。它的联合创始人 Soumith Chintala 在加入 Meta 之前曾经历过多次碰壁却在 AI 框架这条路上坚持了下来最终把 PyTorch 带成了学术界和工业界都广泛使用的开源项目。很多人刚入门深度学习时第一个接触的框架就是 PyTorch因为它的动态图机制、Python 友好的接口和调试体验确实比早期静态图框架更容易上手。本文将先从 PyTorch 的诞生背景说起再完整演示从环境搭建到训练一个手写数字识别模型的整个流程最后整理安装配置过程中的高频报错和工程实践建议。无论你是刚准备学深度学习的学生还是想在公司项目里把 PyTorch 用起来的工程师这篇文章都可以作为一份可复现的入门手册。1. PyTorch 的前世今生Soumith Chintala 与技术革命1.1 从被拒 15 次到改变 AI 界Soumith Chintala 并不是一开始就站在聚光灯下的天才。在 PyTorch 成功之前他在学术和职业道路上经历过很多次拒绝。传闻中他曾被 15 个机会拒绝这在今天看来有些不可思议因为正是这个人后来推动了 PyTorch 的发展并成为 Meta 的副总裁。当然我们不能把一个人的成功简单归结为“逆袭”。更值得关注的是他为什么能在被拒之后仍然持续投入深度学习基础设施的建设。他早期在深度学习社区里做了很多工程贡献包括数据加载、模型加速、训练脚本的改进。这些看似琐碎的工作为他后来设计 PyTorch 积累了非常扎实的经验。当 Facebook AI Research现在的 Meta AI决定构建一个新的深度学习框架时团队希望解决当时框架“静态图不灵活、调试困难”的痛点。Soumith Chintala 和团队一起提出了动态计算图与 Python 原生集成的设计思路。这个思路后来成为 PyTorch 最核心的竞争力。1.2 PyTorch 为什么能流行PyTorch 最初在 2016 年底发布2017 年正式开源随后迅速在学术论文中流行起来。它和 TensorFlow 的路线不同TensorFlow 早期采用静态图模式先定义图再运行PyTorch 则采用动态图每次执行都是一次真实的计算过程你可以随时打印中间结果也可以使用 Python 的 if、for、print 来调试模型。这种“交互式”开发体验让研究者节省了大量调试时间。论文复现和模型原型验证这类工作PyTorch 是最自然的工具。后来许多知名项目比如 Hugging Face Transformers、Stable Diffusion、LLaMA 等都基于 PyTorch 生态构建。从工程角度看PyTorch 的资源也极其丰富。torchvision提供了常见数据集和预训练模型torch.utils.data提供了标准的数据加载管道torch.nn提供了大量神经网络模块。这些设计让 PyTorch 不仅能做科研也能支撑工业级训练和部署。1.3 为什么现在依然值得学习 PyTorch虽然现在像 JAX、PaddlePaddle 等框架也有不少声量但 PyTorch 在学术界、竞赛、开源大模型生态中的占有率依然非常稳定。对于新手来说学习 PyTorch 还有一个额外好处文档完善、社区问答多、招聘市场上岗位也多。如果你已经学过机器学习基础接下来想动手跑深度学习模型PyTorch 是性价比最高的选择。本文后续部分将带你把 PyTorch 环境从零搭起来并完成一个完整的图像分类项目。2. 环境准备搭建 PyTorch 开发环境2.1 选择 Python 与 Anaconda在安装 PyTorch 之前需要先准备好 Python 环境。推荐使用 Anaconda 或者 Miniconda 来管理虚拟环境避免不同项目之间的包版本冲突。Windows、macOS、Linux 都可以安装 Anaconda。安装完成后打开终端或 Anaconda Prompt先创建一个独立的虚拟环境。我这里以 Python 3.10 为例实际版本可以根据你本机情况调整。建议不要直接使用 base 环境跑项目隔离环境会更安全。conda create -n pytorch-env python3.10 -y conda activate pytorch-env创建成功后终端提示符会变成(pytorch-env)前缀说明当前已经在虚拟环境中。后续所有安装和运行命令都在这个环境里执行。2.2 安装 PyTorch 的 CPU/GPU 版本PyTorch 的安装方式非常简单官方提供了一条命令在 pytorch.org 首页可以直接选择操作系统、包管理工具和 CUDA 版本然后复制命令执行。如果你只是学习基础操作可以先安装 CPU 版本。CPU 版本在训练小模型时也够用而且安装体积小、没有显卡驱动问题。CPU 版本安装命令示例使用 pippip install torch torchvision torchaudio如果你想在 GPU 上训练需要确认本机安装了 NVIDIA 显卡驱动并且显卡支持 CUDA。注意PyTorch 的 CUDA 版本需要和显卡驱动匹配。可以先在命令行执行nvidia-smi查看驱动支持的 CUDA 版本。----------------------------------------------------------------------------- | NVIDIA-SMI 545.23.08 Driver Version: 545.23.08 CUDA Version: 12.3 | -----------------------------------------------------------------------------比如这里显示驱动的 CUDA Version 是 12.3那么你可以选择 PyTorch 官方源中匹配 CUDA 12.x 的安装命令。不要只看nvidia-smi显示的版本就装一定要以 PyTorch 官网给出的组合为准。一个常见的选择是在 pip 命令中指定 CUDA 版本pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121这里cu121表示 CUDA 12.1。如果你的显存驱动版本支持 CUDA 12.1这个命令就能安装对应预编译的 PyTorch。如果你的网络下载速度较慢也可以使用国内镜像源加速但需要注意镜像是否同步了最新的 PyTorch 版本。以清华 PyPI 镜像为例pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple这里要提醒一下PyPI 镜像可能默认安装的是 CPU 版本因为 PyTorch 官方在 PyPI 上发布的 Linux 包通常支持 CUDA而 Windows 包则区分 CPU 和 GPU。所以如果你需要 GPU 版本最好使用官方指定--index-url的方式安装或者使用 Anaconda 渠道安装。2.3 验证安装是否成功安装完成后在 Python 环境中验证一下。python -c import torch; print(torch.__version__); print(torch.cuda.is_available())如果输出版本号并且torch.cuda.is_available()返回True说明 CUDA 可用。如果返回False说明当前安装的是 CPU 版本或者 CUDA 环境没有配好。还可以检查 GPU 名称import torch print(torch.version.cuda) print(torch.cuda.get_device_name(0))在 PyTorch 中torch.cuda.is_available()是判断 GPU 是否可用的标准函数。很多新手安装后第一件事就是看这个结果。2.4 IDE 推荐与项目结构环境搭建好之后推荐使用 VS Code 或者 PyCharm 编写代码。VS Code 安装 Python 扩展后选择对应的 conda 解释器即可。PyCharm 在设置中选择项目解释器也可以直接识别 conda 环境。为了方便管理建议按如下结构组织项目pytorch_demo/ ├── data/ # 存放数据集 ├── models/ # 存放训练好的权重 ├── train.py # 训练脚本 └── utils.py # 工具函数数据文件尽量不要放在代码同级目录尤其是大数据集单独目录更清晰。模型参数保存到固定目录方便后续读取和部署。3. 核心概念Tensor、自动求导与神经网络模块3.1 TensorPyTorch 的数据核心Tensor张量是 PyTorch 中最基础的数据结构。你可以把它理解成多维数组。0 维是标量1 维是向量2 维是矩阵3 维及以上就称为张量。Tensor 和 NumPy 的 ndarray 很像但 Tensor 可以自动记录计算图并且支持 GPU 加速。创建张量的几种常见方式import torch # 从列表创建 a torch.tensor([1, 2, 3]) print(a) # 创建全零张量 b torch.zeros(3, 4) print(b) # 创建随机张量 c torch.randn(2, 3) print(c) # 张量类型 print(a.dtype, b.dtype, c.device)输出中可以看到torch.int64、torch.float32等类型。默认创建的浮点张量是float32这在深度学习中是最常用的精度。Tensor 和 NumPy 可以直接互转import numpy as np np_array np.array([1, 2, 3]) tensor_from_np torch.from_numpy(np_array) tensor_to_np tensor_from_np.numpy() print(tensor_from_np, tensor_to_np)这里要注意绝大多数情况下 Tensor 和 NumPy 共享内存修改其中一个会影响另一个。如果你不希望共享内存可以显式使用.clone()。在 GPU 可用时可以把 Tensor 搬到 GPUif torch.cuda.is_available(): a a.cuda() print(a.device)3.2 自动求导PyTorch 的灵魂神经网络训练的核心是反向传播而反向传播需要求梯度。PyTorch 的autograd模块可以自动计算所有参与运算的张量的梯度。你只需要把需要计算梯度的张量设置requires_gradTrue然后进行运算最后调用.backward()梯度就会自动传递回来。来看一个最基础的计算图示例import torch # 定义一个叶子节点开启梯度追踪 x torch.tensor(2.0, requires_gradTrue) y x ** 2 3 * x 1 # 反向传播计算 dy/dx y.backward() # 查看 x 的梯度应该是 2*x 3 7 print(x.grad)这个例子虽然简单但解释了 autograd 的基本机制。在神经网络中x是模型的参数y是损失函数通过loss.backward()每个参数都会得到梯度然后优化器用梯度更新参数。需要特别注意的是默认情况下每次backward()之后计算图会被释放。如果希望保留计算图可以在backward()中传入retain_graphTrue但在一般训练中不需要。3.3 torch.nn快速搭建模型torch.nn模块提供了神经网络层的封装。你不需要手写矩阵乘法、激活函数、卷积等底层实现只需要调用对应模块。一个简单的线性层示例import torch.nn as nn # 定义一个线性层输入维度 4输出维度 3 fc nn.Linear(4, 3) print(fc.weight.shape) print(fc.bias.shape)nn.Linear内部会自动创建权重矩阵和偏置形状分别是[3, 4]和[3]。默认情况下这些参数会自动要求梯度方便后续优化。完整的神经网络通常继承自nn.Module需要实现forward方法class MyNet(nn.Module): def __init__(self): super().__init__() self.fc1 nn.Linear(784, 128) self.relu nn.ReLU() self.fc2 nn.Linear(128, 10) def forward(self, x): x self.fc1(x) x self.relu(x) x self.fc2(x) return x net MyNet() print(net)forward定义前向传播过程。当你调用net(x)时实际上会自动调用forward。模型中所有nn.Module子模块都会自动注册不需要手动管理参数列表。PyTorch 的生态还提供了很多预训练模型在torchvision.models中可以直接加载比如 ResNet、VGG、MobileNet 等。在自己的数据集上做迁移学习时非常方便。4. 实战用 PyTorch 训练一个手写数字分类模型下面整个项目用 PyTorch 完成 MNIST 手写数字识别。MNIST 是深度学习入门的经典数据集包含 60000 张训练图片和 10000 张测试图片每张图片是 28x28 的灰度图标签是 0-9。这个实战会覆盖数据加载、模型定义、训练循环、验证和保存模型全流程。4.1 准备数据PyTorch 的torchvision.datasets可以直接下载 MNIST 数据集。为了加快国内下载速度这里使用downloadTrue也可以提前从镜像站下载后放到data目录中。首先安装依赖pip install torchvision matplotlib编写数据加载代码保存为prepare_data.pyimport torch from torch.utils.data import DataLoader from torchvision import datasets, transforms # 数据预处理将 PIL 图片转为 Tensor并归一化到 [0, 1] transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # 加载训练集和测试集 train_dataset datasets.MNIST( root./data, trainTrue, transformtransform, downloadTrue ) test_dataset datasets.MNIST( root./data, trainFalse, transformtransform, downloadTrue ) # DataLoader自动打乱、分批、多进程读取 train_loader DataLoader( datasettrain_dataset, batch_size64, shuffleTrue, num_workers2 ) test_loader DataLoader( datasettest_dataset, batch_size256, shuffleFalse, num_workers2 ) print(f训练集大小: {len(train_dataset)}) print(f测试集大小: {len(test_dataset)}) print(f一个 batch 的形状: {next(iter(train_loader))[0].shape})transforms.ToTensor()会自动将图片像素从 0-255 转换为 0-1 的浮点数并且把维度从H x W x C调整为C x H x WPyTorch 卷积层要求的输入顺序正是如此。Normalize用均值和标准差做标准化让数据分布更稳定。4.2 定义网络模型我们使用一个简单的三层全连接网络。因为 MNIST 图片大小为 28x28展开后是 784 个像素所以第一层输入维度是 784。文件model.pyimport torch.nn as nn class MLP(nn.Module): def __init__(self): super().__init__() self.net nn.Sequential( nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, 10) ) def forward(self, x): # 输入形状: [batch_size, 1, 28, 28] x x.view(x.size(0), -1) # 展平成 [batch_size, 784] return self.net(x)这里使用nn.Sequential把多个层串联起来代码更简洁。view的作用是把四维张量展平成二维第二维度是自适应计算-1表示自动推断也就是1*28*28784。4.3 训练脚本完整训练脚本train.pyimport torch import torch.nn as nn import torch.optim as optim from model import MLP from prepare_data import train_loader, test_loader # 设置随机种子保证可复现 torch.manual_seed(42) if torch.cuda.is_available(): torch.cuda.manual_seed_all(42) device torch.device(cuda if torch.cuda.is_available() else cpu) print(f当前设备: {device}) model MLP().to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) epochs 5 for epoch in range(epochs): model.train() total_loss 0 for images, labels in train_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() avg_loss total_loss / len(train_loader) # 每个 epoch 结束后验证一次 model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() accuracy 100.0 * correct / total print(fEpoch [{epoch1}/{epochs}], Loss: {avg_loss:.4f}, Test Acc: {accuracy:.2f}%) # 保存模型权重 torch.save(model.state_dict(), ./models/mnist_mlp.pth) print(模型已保存到 ./models/mnist_mlp.pth)训练过程包含几个关键步骤optimizer.zero_grad()每次更新梯度前必须把上一轮的梯度清零否则梯度会累加。model.train()和model.eval()切换训练/评估模式。对于包含 Dropout、BatchNorm 的模型这个切换非常重要。torch.no_grad()验证阶段不需要计算梯度可以减少内存占用并加速计算。4.4 运行与验证在项目目录下依次运行python prepare_data.py python train.py如果一切正常输出类似Epoch [1/5], Loss: 0.2793, Test Acc: 92.50% Epoch [2/5], Loss: 0.1271, Test Acc: 95.87% Epoch [3/5], Loss: 0.0877, Test Acc: 96.95% Epoch [4/5], Loss: 0.0664, Test Acc: 97.50% Epoch [5/5], Loss: 0.0521, Test Acc: 97.89%这个准确率在 MNIST 上属于全连接网络的正常水平。如果想得到更高准确率可以换成卷积神经网络。4.5 结果说明与预测训练完成后加载模型对单张图片进行预测。新建predict.pyimport torch from PIL import Image from torchvision import transforms from model import MLP # 加载模型 model MLP() model.load_state_dict(torch.load(./models/mnist_mlp.pth, map_locationcpu)) model.eval() # 预处理单张图片 transform transforms.Compose([ transforms.Grayscale(num_output_channels1), transforms.Resize((28, 28)), transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # 假设要预测的图片为 digit.png image Image.open(./digit.png) image transform(image) image image.unsqueeze(0) # 增加 batch 维度 with torch.no_grad(): output model(image) pred torch.argmax(output, dim1).item() print(f预测结果: {pred})这里使用unsqueeze(0)把单张图片变为形状[1, 1, 28, 28]因为模型在训练时接收的就是四维输入。torch.argmax返回概率最大的类别索引。5. 常见问题与排查思路PyTorch 安装和运行过程中新手经常遇到几类问题。下面把高频场景整理成表格方便大家快速定位。问题现象常见原因解决思路torch.cuda.is_available()返回 False安装的是 CPU 版 PyTorch显卡驱动不支持 CUDACUDA 版本不匹配重新安装对应 CUDA 的 PyTorch更新显卡驱动检查驱动支持的最高 CUDA 版本ModuleNotFoundError: No module named torch没有激活虚拟环境或者当前环境里没有安装 PyTorch执行conda activate pytorch-env然后pip list查看是否包含 torch下载 MNIST 数据时卡住/报错网络连接问题或数据集资源连接不稳定手动下载数据集放到./data目录使用国内镜像或增加超时时间CUDA out of memorybatch_size 过大显存不足其他程序占用显存降低 batch_size释放其他进程的显存使用梯度累积训练 Loss 不下降学习率设置不合理模型结构错误数据预处理错误尝试降低或提高学习率检查数据标签是否有误确认model.train()num_workers报错Windows 下多进程数据加载冲突Windows 上设置num_workers0或者将训练代码放入if __name__ __main__:这里重点说一下num_workers。Linux 下设置num_workers4可以加速数据读取但 Windows 下经常因为多进程序列化问题报错。如果你在 Windows 上跑建议先设为0跑通后再调大。另一个常见问题是在 PyTorch 2.x 版本中torch.load加载模型权重时默认参数发生变化。从 PyTorch 2.6 开始weights_only参数默认改为True如果你的.pth文件里包含自定义类直接加载可能会报错。可以显式设置torch.load(./models/mnist_mlp.pth, weights_onlyFalse)不过如果你保存的只是纯state_dict通常weights_onlyTrue也没问题。重点是理解这个参数的作用。6. 工程最佳实践与深层建议6.1 随机种子与可复现性深度学习训练中随机性来自多个层面权重初始化、数据打乱顺序、GPU 并行计算。为了保证实验结果可以复现可以在训练脚本开头设置固定种子import random import numpy as np import torch def set_seed(seed): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) set_seed(42)不过要注意某些 GPU 算子仍然是非确定性的。要让运算完全确定还需要在 PyTorch 中配置torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark False配置benchmarkTrue可以让 cuDNN 自动选择最快算法但会引入随机性。在追求速度时打开在追求稳定复现时关闭。6.2 设备管理与扩展性不要把device cuda写死在代码里。更推荐这样管理device torch.device(cuda if torch.cuda.is_available() else cpu)所有模型和张量都通过.to(device)放到对应设备上。这样代码在笔记本和服务器之间迁移时不需要大范围改动。如果你要进行分布式训练PyTorch 提供了torch.nn.DataParallel和torch.nn.parallel.DistributedDataParallel。一般建议优先使用DistributedDataParallel它在多进程多 GPU 场景下效率更高。不过这是进阶内容入门阶段先把单卡跑通。6.3 模型保存与断点续训保存模型有两种常见方式只保存参数torch.save(model.state_dict(), model.pth)保存完整模型torch.save(model, model.pth)推荐保存state_dict因为它体积小、兼容性好并且在加载时需要重建模型结构。训练中断时建议同时保存优化器状态和 epochcheckpoint { epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), loss: loss, } torch.save(checkpoint, ./models/checkpoint.pth)恢复训练时checkpoint torch.load(./models/checkpoint.pth) model.load_state_dict(checkpoint[model_state_dict]) optimizer.load_state_dict(checkpoint[optimizer_state_dict]) start_epoch checkpoint[epoch] 1这种设计在长时间训练或者大型模型时非常重要可以避免因为意外中断而重头再来。6.4 日志与可视化建议在训练过程中使用tensorboard或wandb记录指标而不是只靠print。tensorboard是本地可视化的经典方案PyTorch 也内置了torch.utils.tensorboard。简单示例from torch.utils.tensorboard import SummaryWriter writer SummaryWriter(runs/mnist_experiment) # 在每个 epoch 结束后记录 writer.add_scalar(Loss/train, avg_loss, epoch) writer.add_scalar(Accuracy/test, accuracy, epoch) writer.close()然后命令行运行tensorboard --logdirruns浏览器打开http://localhost:6006就能看到训练曲线。日志和可视化能帮你更早发现过拟合、振荡等问题。6.5 数据预处理与加载规范对于真实项目建议把所有数据预处理逻辑集中到一个模块中并尽量使用Dataset类维护。无论是读取本地图片、CSV还是数据库数据都可以封装成自定义数据集。这样后续做数据增强、分布式采样、缓存都会很自然。也需要考虑数据增强的适用场景。对于图像分类RandomCrop、RandomHorizontalFlip是常见操作但对于 OCR、医学影像等场景需要根据领域知识决定哪些增强是合理的不能盲目套用。7. 学习路线从 PyTorch 到现代 AI 实战到这里你已经具备用 PyTorch 训练一个完整模型的能力。回顾一下本文的路径我们先了解了 PyTorch 和 Soumith Chintala 的故事明白了动态图给研究者带来的价值然后搭建了 CPU/GPU 环境理解了 Tensor、autograd、nn.Module等核心概念接着用 MNIST 完成了从数据加载、模型定义、训练循环到保存预测的闭环最后整理了常见报错和工程规范。下一步你可以沿着这几个方向继续深入学习卷积神经网络和 ResNet 等经典结构在 CIFAR-10 或 ImageNet 子集上训练图像分类模型。学习torchvision.models中的预训练模型掌握迁移学习在实际数据集上微调。学习 Hugging Face Transformers体验 PyTorch 生态在 NLP 和大模型领域的完整链路。学习torch.utils.data的高级用法包括自定义 Dataset、Sampler、Collate 函数为真实业务数据做准备。PyTorch 的生态还在持续演进但它的核心设计思想——让深度学习的代码更接近 Python 直觉——从 PyTorch 诞生至今没有改变过。这种设计也解释了为什么它能从一个研究工具成长为 AI 领域的工业标准之一。即便你之后转向其他框架PyTorch 里的张量思维、自动求导、训练循环这些概念都会一直复用。动手跑通一个小项目是入门的最好方式。如果你想进一步确认自己对代码的理解可以尝试修改模型结构、调整学习率或者找一个真实数据集替换 MNIST做一次完整的迁移学习实验。踩过几个坑之后你对 PyTorch 的理解会又上一个台阶。
返回列表