尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

PyTorch入门教程:从张量、自动求导到MNIST实战

PyTorch入门教程:从张量、自动求导到MNIST实战 各位学习深度学习和 PyTorch 的朋友大家好。这段时间经常看到有同学在群里问“我想入门深度学习一定要学 PyTorch 吗怎么装环境代码跑不起来怎么办”说实话现在网上关于 PyTorch 的资料非常多但不少教程要么版本太旧要么直接跳到复杂模型对新手并不友好。如果你也正处在“看了很多理论但写不出代码”的阶段这篇文章正好适合你。本文不会堆砌长篇理论而是围绕 PyTorch 学习中最核心的三点展开张量Tensor、自动求导autograd和数据加载Dataset 与 DataLoader。同时会带大家从零搭建环境完成一个可运行的图像分类实战项目并提供完整源码。不管你是准备做毕业设计、转行 AI还是工作中需要落地深度学习模型这套保姆级流程都能帮你节省大量踩坑时间。1. 为什么深度学习入门首选 PyTorch1.1 深度学习框架解决了什么问题先回到一个基础问题深度学习到底是什么。简单来说深度学习是通过多层神经网络让计算机从大量数据中自动学习特征和规律。以图像识别为例传统的图像处理需要人工设计边缘检测、纹理提取等特征算子而深度学习只需要把原始像素丢给网络模型自己会去学习“什么是猫”“什么是狗”。但是如果从零手写神经网络你会遇到两个现实困难反向传播计算量大且容易算错。对于一个 10 层的网络手动推导每一层的梯度几乎是不可能的。GPU 并行计算代码编写门槛高。直接用 CUDA 编程做矩阵运算开发效率极低。深度学习框架就是为了解决这些问题而诞生的。它帮我们封装好了张量运算类似 NumPy 但支持 GPU 加速自动求导机制反向传播只需要调用一个backward()大量预构建的神经网络层如卷积层、循环层、全连接层模型训练、保存、加载的完整工具链。1.2 为什么选 PyTorch 而不是其他框架目前主流的深度学习框架有 PyTorch、TensorFlow、PaddlePaddle 等。PyTorch 能成为学术界和工业界的主流选择主要有几个原因第一动态计算图。PyTorch 的计算图是运行时动态构建的可以随时修改网络结构调试时还能用print直接输出中间结果这对新手非常友好。而静态图框架需要先定义好完整的计算图再执行调试体验相对繁琐。第二Python 风格足够自然。PyTorch 的 API 设计非常贴近 NumPy 和 Python 原生写法学过 Python 基础的人几乎可以无缝过渡。第三生态完善。Hugging Face Transformers、mmdetection、Detectron2 等大量知名开源项目都基于 PyTorch学习 PyTorch 意味着你能直接阅读和修改这些工业级代码。当然TensorFlow 在生产部署和移动端支持上也有自己的优势但如果你是初学者从 PyTorch 入门是当前效率最高、资料最全的选择。1.3 本文的学习目标学完这篇文章你将会掌握在本机正确安装 PyTorch CPU 版或 GPU 版理解张量的创建、运算、变形以及与 NumPy 的互转理解自动求导机制明白requires_grad、backward()的作用掌握自定义 Dataset 和 DataLoader 的标准写法独立完成一个 MNIST 手写数字识别项目看到训练损失下降和测试准确率提升。2. 环境准备与 PyTorch 安装在看代码之前先把环境搞定。本部分会分别介绍 CPU 版本和 GPU 版本的安装方式并给出验证代码。2.1 安装 Python 和 AnacondaPyTorch 是 Python 库因此你需要一个可用的 Python 环境。这里推荐使用 Anaconda 来管理 Python 环境它可以很方便地创建独立环境避免项目之间依赖冲突。到 Anaconda 官网下载对应系统的安装包安装完成后打开终端Windows 下是 Anaconda Prompt执行conda --version如果能看到版本号说明安装成功。接下来创建并激活一个用于 PyTorch 学习的虚拟环境conda create -n pytorch python3.9 conda activate pytorch注意python3.9 是一个比较稳定的选择如果你需要最新特性也可以使用 3.10 或 3.11但要注意 PyTorch 版本对 Python 版本的支持范围。实际版本需要根据你的项目实际情况调整本文重点演示配置思路。2.2 安装 PyTorch CPU 版如果没有 NVIDIA 独立显卡或者暂时只想在 CPU 上先跑通代码可以直接使用 pip 安装 CPU 版pip install torch torchvision torchaudioCPU 版安装包体积较小也不会涉及 CUDA 环境配置适合初学者先跑通流程。2.3 安装 PyTorch GPU 版含 CUDA 环境配置如果有 NVIDIA 显卡建议安装 GPU 版因为模型训练速度会快很多。GPU 版安装分三步第一步确认显卡驱动和 CUDA 版本在终端或命令行执行nvidia-smi输出中会显示驱动版本和最高支持的 CUDA 版本。第二步选择合适的 CUDA 版本并安装以 CUDA 11.8 为例可以使用 PyTorch 官方源安装pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118如果你的显卡支持更高版本的 CUDA可以到 PyTorch 官网获取对应的安装命令。注意安装时 CUDA 版本选择以 PyTorch 预编译包为准只要nvidia-smi显示的驱动版本不低于 PyTorch 所需 CUDA 版本即可。第三步验证 GPU 是否可用import torch print(torch.__version__) print(torch.cuda.is_available())如果输出True说明 GPU 版安装成功。2.4 常见安装问题提前避坑问题现象常见原因解决思路pip 安装速度慢默认源在国外使用清华源或阿里源pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simpletorch.cuda.is_available() 返回 False安装了 CPU 版或 CUDA 版本不匹配卸载后安装 GPU 版并检查nvidia-smi驱动版本安装后 import torch 报错 DLL load failed缺少 Visual C 运行库安装 Visual C Redistributable或更换 Python 版本conda create 后 pip 无法使用环境未激活执行conda activate pytorch后再安装3. 三大核心知识点详解3.1 张量TensorPyTorch 的数据核心学习 PyTorch 的第一站一定是张量。张量可以理解为多维数组它在概念上与 NumPy 的 ndarray 非常相似但额外支持 GPU 加速和自动求导。标量是 0 维张量向量是 1 维张量矩阵是 2 维张量更高维的数组就是高维张量。先看创建张量最常用的几种方式import torch # 1. 从 Python 列表创建 a torch.tensor([1, 2, 3]) print(a) # 2. 全零张量 b torch.zeros(2, 3) print(b) # 3. 全一张量 c torch.ones(2, 3) # 4. 随机张量取值在 [0, 1) 均匀分布 d torch.rand(2, 3) # 5. 指定数据类型的张量 e torch.tensor([1.5, 2.5], dtypetorch.float32) # 6. 形状相同的全一张量 f torch.ones_like(a)张量的形状操作在模型编写中非常常用import torch x torch.randn(2, 3, 4) # 形状为 (2, 3, 4) 的标准正态分布张量 print(x.shape) # 输出 torch.Size([2, 3, 4]) # 改变形状不改变数据和元素总数 y x.view(2, 12) print(y.shape) # 输出 torch.Size([2, 12]) # 增加维度 z x.unsqueeze(0) print(z.shape) # 输出 torch.Size([1, 2, 3, 4]) # 压缩维度 w z.squeeze(0) print(w.shape) # 输出 torch.Size([2, 3, 4])张量和 NumPy 之间的互转也是非常常见的操作import torch import numpy as np # NumPy 转 Tensor arr np.array([1.0, 2.0, 3.0]) tensor torch.from_numpy(arr) # Tensor 转 NumPy arr2 tensor.numpy()还需要特别注意张量默认存储在 CPU 上如果想放到 GPU 上运算需要调用.cuda()或.to(device)。后面实战部分会统一用.to(device)来做设备管理。3.2 自动求导autograd训练模型的引擎神经网络训练的核心是反向传播。PyTorch 的自动求导机制让我们只需要定义前向计算过程框架会自动计算梯度。为了理解自动求导我们先看一个最简单的例子计算函数 (y x^2) 在 (x 3) 处的导数。import torch # 创建一个需要求导的张量 x torch.tensor(3.0, requires_gradTrue) # 前向计算 y x ** 2 # 查看 y 的梯度函数这里会自动生成一个 grad_fn 属性 print(y.grad_fn) # 反向传播 y.backward() # 查看 x 的梯度 print(x.grad) # 输出 tensor(6.)requires_gradTrue表示我们要追踪这个张量的所有运算。在调用backward()后x.grad会保存计算得到的梯度值。在实际项目中我们一般不会手动设置每个张量的requires_grad而是通过nn.Parameter来定义模型参数模型参数默认就是需要求导的。这里有一个需要留意的地方import torch x torch.tensor(3.0, requires_gradTrue) y x ** 2 # 第一次反向传播 y.backward() print(x.grad) # tensor(6.) # 再次反向传播 # y.backward() # 报错梯度只能计算一次除非 retain_graphTrue如果需要在同一个计算图上多次反向传播需要传入retain_graphTrue。不过在标准训练流程中每个 batch 都会重新构建计算图所以不需要这个参数。3.3 数据加载Dataset 与 DataLoader深度学习模型训练离不开数据的批量加载。PyTorch 提供了非常优雅的数据加载机制Dataset负责定义数据从哪里读、怎么处理DataLoader负责批量、打乱、并行加载。来看一个自定义 Dataset 的标准写法import torch from torch.utils.data import Dataset, DataLoader class MyDataset(Dataset): def __init__(self, data, labels): self.data data self.labels labels def __len__(self): # 返回样本总数 return len(self.data) def __getitem__(self, idx): # 根据索引返回一个样本和对应的标签 return self.data[idx], self.labels[idx] # 模拟一批数据 train_data torch.randn(100, 3, 32, 32) # 100 张 32x32 的 3 通道图像 train_labels torch.randint(0, 10, (100,)) dataset MyDataset(train_data, train_labels) # 通过 DataLoader 批量加载 dataloader DataLoader(dataset, batch_size16, shuffleTrue) for batch_data, batch_labels in dataloader: print(batch_data.shape) # 输出 torch.Size([16, 3, 32, 32]) print(batch_labels.shape) # 输出 torch.Size([16]) breakshuffleTrue表示每个 epoch 训练前打乱数据顺序这对提升模型泛化能力很重要。batch_size则决定了每轮迭代送入模型的样本数量是训练中需要调节的超参数之一。在开始复杂模型之前建议先亲手操作以上三部分代码确保张量创建、自动求导和数据加载都了然于胸。这三个知识点是所有后续 PyTorch 代码的基石。4. 完整实战MNIST 手写数字识别铺垫完基础概念接下来我们完成一个端到端的项目用卷积神经网络CNN识别 MNIST 手写数字。这个项目麻雀虽小但五脏俱全涵盖了数据加载、模型定义、训练、评估和保存的全部流程。4.1 项目结构先规划项目目录pytorch-mnist/ ├── main.py # 主脚本包含训练和测试逻辑 ├── model.py # 模型定义 └── README.md # 项目说明可选实际代码按照可读性可以拆分成多个文件但为了便于初学者复制运行这里先提供一个完整的单文件版本。4.2 数据准备使用 torchvision 加载 MNISTMNIST 数据集是深度学习的“Hello World”包含 0-9 共 10 类手写数字每张图片是 28x28 的灰度图。torchvision 中已经内置了 MNIST 数据集我们可以直接下载使用。import torch from torch.utils.data import DataLoader from torchvision import datasets, transforms # 定义数据预处理转为 Tensor 并标准化 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(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size64, shuffleFalse)这里Normalize((0.1307,), (0.3081,))使用的是 MNIST 数据集的均值和标准差。标准化能让数据分布在 0 附近有助于模型更稳定地训练。4.3 模型定义从全连接到卷积网络先定义一个简单的全连接网络方便理解网络结构import torch.nn as nn class SimpleNN(nn.Module): def __init__(self): super().__init__() self.fc1 nn.Linear(28 * 28, 128) self.fc2 nn.Linear(128, 64) self.fc3 nn.Linear(64, 10) self.relu nn.ReLU() def forward(self, x): # 展平 28x28 图像为 784 维向量 x x.view(x.size(0), -1) x self.relu(self.fc1(x)) x self.relu(self.fc2(x)) x self.fc3(x) return x全连接网络的问题在于它忽略了图像的空间结构。对于图像任务卷积神经网络CNN效果更好。下面是一个简单的 CNNimport torch.nn as nn class SimpleCNN(nn.Module): def __init__(self): super().__init__() # 卷积层 1输入通道 1输出通道 32卷积核 3x3 self.conv1 nn.Conv2d(1, 32, kernel_size3, padding1) # 卷积层 2输入通道 32输出通道 64卷积核 3x3 self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) # 池化层2x2 最大池化 self.pool nn.MaxPool2d(2, 2) # 全连接层经过两次池化后特征图尺寸变为 7x7 self.fc1 nn.Linear(64 * 7 * 7, 128) self.fc2 nn.Linear(128, 10) self.relu nn.ReLU() def forward(self, x): x self.pool(self.relu(self.conv1(x))) x self.pool(self.relu(self.conv2(x))) x x.view(x.size(0), -1) # 展平 x self.relu(self.fc1(x)) x self.fc2(x) return x4.4 训练循环与测试循环定义好模型之后还需要优化器、损失函数和训练循环。import torch import torch.nn as nn import torch.optim as optim # 设备设置优先使用 GPU device torch.device(cuda if torch.cuda.is_available() else cpu) print(Using device:, device) model SimpleCNN().to(device) criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.001) # 训练循环 num_epochs 5 for epoch in range(num_epochs): model.train() running_loss 0.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() running_loss loss.item() * images.size(0) epoch_loss running_loss / len(train_loader.dataset) print(fEpoch [{epoch 1}/{num_epochs}], Loss: {epoch_loss:.4f}) # 测试循环 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, 1) total labels.size(0) correct (predicted labels).sum().item() print(fTest Accuracy: {100 * correct / total:.2f}%)这里有几个值得解释的细节model.train()和model.eval()切换训练和评估模式。虽然当前模型没有 Dropout 和 BatchNorm但这个习惯要养成因为复杂模型中这两种模式行为不同。optimizer.zero_grad()每次迭代前清零梯度防止梯度累积。with torch.no_grad()评估时不需要计算梯度既省内存又加速。torch.max(outputs, 1)取出每个样本预测概率最大的类别下标。4.5 运行结果与预期训练完成后你会在控制台看到类似输出Using device: cuda Epoch [1/5], Loss: 0.1212 Epoch [2/5], Loss: 0.0398 Epoch [3/5], Loss: 0.0275 Epoch [4/5], Loss: 0.0216 Epoch [5/5], Loss: 0.0171 Test Accuracy: 99.03%如果使用 CPU 训练每个 epoch 可能需要一分钟左右使用 GPU 会快很多。准确率在 98% 以上属于正常水平。4.6 模型保存与加载训练结束后可以保存模型参数# 保存模型参数推荐方式 torch.save(model.state_dict(), mnist_cnn.pth)加载模型进行推理时# 创建新模型实例 model SimpleCNN() # 加载参数 model.load_state_dict(torch.load(mnist_cnn.pth)) model.to(device) model.eval()注意torch.save(model.state_dict(), ...)只保存参数不保存模型结构。加载时需要先自己定义好同样的网络结构再读入参数。5. 常见问题与排查思路在实际操作过程中大家经常会在不同环节遇到报错。下面整理一些高频问题和对应的排查策略。5.1 数据维度过不匹配错误现象运行模型时报错类似RuntimeError: size mismatch。常见原因输入数据的形状和模型期望的形状不一致。解决思路检查数据预处理确认图像是否被转换成正确的通道数和尺寸。比如 MNIST 是单通道灰度图如果错误地当作三通道处理就会报错。可以在输入模型前用print(images.shape)调试。5.2 训练损失不下降错误现象epoch 迭代多次但损失一直保持不变。常见原因学习率设置不合适、数据未标准化、模型没有正确进入训练模式。解决思路确认是否调用了model.train()尝试降低或调高学习率常用范围是 1e-4 到 1e-2检查数据是否做了标准化处理。5.3 GPU 显存不足错误现象报错CUDA out of memory。常见原因batch_size过大或输入图像尺寸过大。解决思路降低batch_size或者减少图像尺寸。如果仍然不够可以考虑使用混合精度训练或梯度累积。5.4 预测阶段结果离谱错误现象模型在训练集上准确率很高但预测自己的图片全部错误。常见原因测试时的图像预处理和训练时不一致。解决思路推理时同样需要执行ToTensor()和Normalize()且 Normalize 的均值和标准差要与训练一致。5.5 训练和测试行为不一致错误现象训练准确率高测试准确率极低且波动明显。常见原因忘记切换model.eval()导致 Dropout 和 BatchNorm 在测试时仍处于训练模式。解决思路测试阶段显式调用model.eval()。6. 最佳实践与工程化建议当你能跑通 MNIST 之后距离真实项目还有一段路。下面这些实践建议可以帮助你从一开始就养成良好的工程习惯。6.1 固定随机种子深度学习训练涉及大量随机性。为了实验可复现建议在代码开头固定随机种子import torch import torch.nn as nn import numpy as np import random 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 运算仍然存在不确定性但固定种子能最大程度保证结果稳定。6.2 统一设备管理不要在代码里混用.cuda()和.cpu()建议在开头定义device然后统一使用.to(device)device torch.device(cuda if torch.cuda.is_available() else cpu)这样代码在 CPU 和 GPU 机器上都能运行不需要大量改动。6.3 代码组织规范当项目变大后建议按模块拆分config.py存放超参数和路径配置dataset.py封装数据加载逻辑models/存放网络结构定义train.py训练脚本utils.py通用工具函数。这样每个文件职责单一维护起来更轻松。6.4 训练可视化建议尽早使用 TensorBoard 或 wandb 记录训练曲线而不是只盯着终端输出。PyTorch 可以直接配合 TensorBoard 使用from torch.utils.tensorboard import SummaryWriter writer SummaryWriter(runs/exp1) # 每个 epoch 记录损失 writer.add_scalar(Loss/train, epoch_loss, epoch) writer.add_scalar(Accuracy/test, accuracy, epoch) writer.close()通过可视化你能更快发现过拟合、欠拟合和学习率问题。6.5 模型部署前需要关注的精度问题当模型训练完成并准备部署时很多同学会直接把 PyTorch 模型接入服务。这里需要注意训练时的浮点数精度和部署时可能不同。在主流的模型训练与部署流程中我们通常会看到 FP32、FP16、BF16、TF32 等浮点数格式。简单来说FP32单精度是训练时的默认精度精度高但占内存大FP16半精度在 GPU 上运算速度快但数值范围小容易出现精度溢出BF16Brain Floating Point拥有与 FP32 相同的指数位更适合大模型训练TF32 是 NVIDIA Ampere 架构推出的一种截断精度模式常用于矩阵加速。在实际部署场景中选用哪种精度要看硬件支持和任务精度要求。对新手来说暂时不需要深入实现混合精度但需要知道模型训练和部署的数值精度差异可能会引起线上结果和线下评测不一致。在正式上线前一定要在目标硬件上做充分的精度验证。6.6 安全与权限提醒如果在公司服务器上训练模型要注意以下几点不要把训练好的模型直接暴露在公网尤其是模型文件可能包含训练数据信息使用 Docker 或虚拟环境隔离项目依赖涉及 GPU 资源调度时遵循团队或管理员的分时使用规则模型上线前做公平性和安全性评估避免模型被恶意样本攻击。7. 总结与下一步学习路线到这里我们已经完成了一次从环境搭建到模型训练再到结果评估的完整闭环。回顾本文你需要掌握的核心内容可以浓缩为以下几点张量TensorPyTorch 的基本数据结构支持 GPU 加速和自动求导掌握创建、运算、形状调整以及与 NumPy 的互转。自动求导autogradrequires_gradbackward()是训练模型的基础机制理解它才明白梯度从哪来、到哪去。数据加载Dataset 与 DataLoader真实项目中数据处理会占大量时间掌握自定义 Dataset 的标准写法是必备技能。训练流程model.train()→zero_grad()→forward()→loss()→backward()→step()是 PyTorch 训练的固定套路建议背下来。评估流程model.eval()torch.no_grad()是测试阶段的标准配置防止影响结果和浪费显存。如果按照一周学习计划来安排建议如下第 1 天搭建环境完成张量相关基础练习第 2 天理解自动求导手动实现一个简单线性回归第 3 天掌握 Dataset 和 DataLoader跑通 MNIST 数据加载第 4 天理解全连接网络和卷积网络的区别运行本文的 CNN 代码第 5 天尝试修改网络结构、batch size、学习率观察结果变化第 6 天用 CIFAR-10 替换 MNIST挑战彩色图片分类第 7 天整理笔记复盘踩坑尝试阅读一个开源项目的源码。后续你可以继续深入学习经典网络结构ResNet、VGG、Transformer 的原理与实现训练技巧学习率调度、正则化、数据增强、早停模型部署ONNX 导出、TorchScript、TensorRT 加速实际业务问题分类、检测、分割、时间序列预测等。学习 PyTorch 最忌讳只看不写。如果你能亲手把本文代码完整跑一遍再根据自己的想法修改网络结构和超参数你对深度学习模型训练流程的理解会上一个台阶。不要怕报错每一个 Bug 都是你排查能力提升的机会。希望这篇教程能帮你在深度学习的路上少踩一些坑。如果你在安装环境或运行代码时遇到问题欢迎在评论区留言一起讨论解决思路。
返回列表