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

资讯详情

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

PyTorch入门:从张量计算到模型部署的完整链路

PyTorch入门:从张量计算到模型部署的完整链路 如果你已经装了 PyTorch却发现自己盯着终端里蹦出来的Using CPU发愣如果你跟着教程跑通了 MNIST但换一个真实数据集就不知道代码该怎么改如果只是想在 Windows 上装个 GPU 版 PyTorch结果被 CUDA、cuDNN、conda、pip 的组合拳劝退了一下午——那这篇内容就是给你写的。PyTorch 入门这件事最大的误区不是“数学太难”也不是“代码看不懂”而是很多人把它当成一门需要“背 API”的课来学。你背了torch.nn.Linear、torch.optim.Adam、model.train()却不知道它们为什么出现在那个位置不知道数据从哪来、梯度怎么算、loss 为什么不降更不知道一次跑通之后怎么让它稳定反复地跑。我对 PyTorch 入门的核心判断是它真正值得学的不是某一个函数或某一种网络而是一条“数据流水线 → 张量计算 → 自动求导 → 模型训练 → 部署验证”的完整链路。这条链路你能从头到尾自己搭一遍才算真正入了深度学习的门否则你只是在“运行别人的代码”。下面我会按照这条链路来拆解。不堆公式不炫技重点放在“为什么这样做”和“真实落地时你会踩什么坑”。1. 先想清楚PyTorch 到底在帮你解决什么很多入门教程第一节课就扔出“PyTorch 是一个开源的深度学习框架”听完之后你依然不知道它有什么用。换一个问法如果没有 PyTorch你想训练一个神经网络需要自己写哪些东西至少三样一是高效的多维数组运算二是自动求梯度三是把计算搬到 GPU 上加速。这三件事如果全都从零写哪怕只是实现一个最简单的全连接网络也足够消耗掉你几周时间。1.1 它不是“深度学习全家桶”而是三块积木PyTorch 的核心可以拆成三层来理解张量Tensor这是 PyTorch 的数据基本单位你可以先把它理解成“能自动记录版本、能搬到 GPU 上的多维数组”。它和 NumPy 的ndarray很像但多了一个关键能力支持自动求导。自动求导autograd这是 PyTorch 最核心的引擎。你定义好前向计算过程后它帮你自动记录计算图反向传播时自动算出每个参数的梯度。没有它深度学习训练的门槛会高一个数量级。神经网络模块torch.nn它把层、激活函数、损失函数、优化器都封装成了积木。你不需要自己写卷积的前向计算也不需要自己写 SGD 更新公式。这三块加起来才是 PyTorch 的完整价值。很多人只看到了第三块觉得它是一个“模型仓库”这就本末倒置了。1.2 入门路径别用背单词的方式学框架我见过太多人学 PyTorch 的方式是今天学torch.Tensor明天学torch.nn.Conv2d后天学torch.utils.data.Dataset一个 API 一个 API 地刷刷到后面全忘了。更好的路径应该是以项目为主轴每遇到一个问题再去查对应 API先用一个 20 行代码的线性回归理解 Tensor、梯度、优化器之间的关系。再用手写数字识别理解 Dataset、DataLoader、模型定义、训练循环的完整流程。然后换一个真实的小型图像数据集体会“换数据”会带来哪些新问题。最后再考虑 GPU、混合精度、模型部署这些工程化话题。这个路径的核心是每一层都建立在一个“你能跑通的最小系统”之上而不是在空中学函数。2. 环境搭建不要从“最全配置”开始从“能跑”开始环境配置是 PyTorch 入门劝退率最高的环节。Windows、Linux、Mac 各有各的问题显卡驱动、CUDA、cuDNN、PyTorch 版本之间还有兼容关系。很多人卡在这里不是因为笨而是因为一上来就想配一个“最完整、最先进、什么都能跑”的环境。2.1 一张最小清单如果你是第一次接触我建议你先装 CPU 版。理由很简单CPU 版一定能跑通能让你先专注于学 PyTorch 本身而不是和驱动搏斗。等第一个 demo 跑通了再回头装 GPU 版效率反而更高。最小环境清单组件建议说明操作系统Windows / Ubuntu / macOS 均可后续深度学习实践Ubuntu 的坑相对少Python3.9 ~ 3.12 之间不要装最新的 Python 3.13很多依赖可能还没跟上包管理pip 或 conda 均可新手用 Anaconda 里自带的 conda 更省心PyTorch先装 CPU 版一行命令几分钟搞定集成开发环境VS Code / PyCharm 均可关键是能看变量和报错信息常见的 CPU 版安装命令以官方源为例pip install torch torchvision torchaudio如果你用 condaconda install pytorch torchvision torchaudio cpuonly -c pytorch注意不要直接复制网上老教程里的安装命令PyTorch 的安装命令会根据操作系统、CUDA 版本、包管理器不同而变化。最靠谱的方式是打开 PyTorch 官网选择你的系统配置复制它生成的那条命令。2.2 GPU 版本先弄清楚 CUDA、cuDNN、PyTorch 三者关系很多人装 GPU 版失败是因为没搞清楚三者的关系GPU 驱动是操作系统层面的显卡插上就能用但驱动版本不能太老。CUDA Toolkit是英伟达提供的并行计算平台。PyTorch 的 GPU 版会自带一部分 CUDA 运行时不一定需要你再单独装完整版。cuDNN是深度学习加速库PyTorch 安装包里也会带上兼容版本。所以一个常见的误解是“我必须先去英伟达官网装 CUDA Toolkit才能装 PyTorch GPU 版。”其实 PyTorch 官方预编译包通常已经包含了运行所需的 CUDA 库。你真正需要确认的是显卡驱动是否足够新PyTorch 版本对应的 CUDA 版本是否和驱动兼容。安装命令示例以 CUDA 12.x 为例pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121安装后用这一行代码验证 GPU 是否真的被识别import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果torch.cuda.is_available()输出False优先检查三件事显卡驱动是否装了NVIDIA 驱动面板能不能打开。PyTorch 是不是装成了 CPU 版torch.__version__里如果带cpu后缀说明装错了。系统里是不是有多个 Python 环境包装到了另一个环境里。注意在 Windows 下最容易出的问题不是“版本太新”而是“Anaconda 和 pip 混装导致包环境不一致”。建议在创建虚拟环境时固定用同一个包管理器不要一会儿 pip 一会儿 conda。2.3 几个特殊场景的处理思路热词里出现了几个典型问题“AMD 安装 PyTorch CUDA”“安装 PyTorch 时手机开了热点下载很慢”“Ubuntu 24.04 配置深度学习环境”。AMD 显卡PyTorch 官方现在也支持 ROCm但支持范围和稳定性不如 CUDA。如果你用的是 AMD 显卡且刚入门建议先装 CPU 版或者直接用云平台跑 GPU 实例避免在驱动兼容性上消耗太多时间。如果你想在 AMD GPU 上跑 PyTorch先确认你的显卡型号在 ROCm 支持列表里安装过程要严格对照 PyTorch 官方文档不要依赖第三方整合包。下载慢这是国内常见问题。核心解法是换国内镜像源或换网络环境。pip 官方源下载慢可以直接用清华、阿里等镜像pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple另外 PyTorch 的.whl包很大下载中断后要避免反复重传pip 会做缓存重试时通常能续传。Ubuntu 24.04这类新系统版本刚发布时部分底层库和驱动的兼容性需要适配。配置深度学习环境时优先看 PyTorch 官方文档说明不要盲目安装系统软件源里的旧版本驱动。环境这块我给的建议就一句话能跑通比跑得“高级”重要得多。先用 CPU 版把流程跑通再考虑 GPU 加速这是效率最高的路径。3. Tensor所有操作最后都落到“数据在哪里、类型是什么、形状对不对”很多初学者对 Tensor 的理解停留在“它是一个多维数组”然后用起来却总报错。报错的核心原因几乎可以归结成三类数据不在同一个设备上、数据类型不匹配、形状对不上。3.1 从创建到搬运把 Tensor 理解成“有类型的矩阵 设备标记”一个 Tensor 至少包含三部分信息值具体的数据dtypefloat32、float64、int64、booldevice数据在 CPU 还是 GPU 上这三者缺一不可。你看报错信息时只要先确认这三件事通常就找到问题根源了。常见的创建方式import torch # 从 Python 列表创建 a torch.tensor([1, 2, 3]) # 创建全零/全一张量 b torch.zeros(3, 4) c torch.ones(2, 2) # 创建随机张量 d torch.randn(3, 3) # 和 NumPy 互转 import numpy as np arr np.array([1, 2, 3]) tensor_from_np torch.from_numpy(arr)3.2 形状、dtype、device 三个视角的排查如果你遇到一个 Tensor 相关报错不要直接问“为什么报错”先按以下顺序自查形状shape两个矩阵要做矩阵乘法维度对不对要做拼接拼接维度的其他维度对不对数据类型dtype常见报错是Expected object of scalar type Float but got scalar type Double说明你把float64传给了一个期望float32的模型。解决办法是给数据加.float()。设备device常见报错是Expected all tensors to be on the same device意思是模型在 GPU 上、数据还在 CPU 上或者反过来。解决办法是统一调用.to(device)。我见过很多人在这一步栽跟头其实不是不懂原理而是没有养成“先看报错前两行再判断是哪类问题”的习惯。PyTorch 的报错信息通常已经很明确关键是你能不能把它归到这三类里。3.3 先掌握这几个基本操作初学者不需要背所有 Tensor API但有几个操作要非常熟练tensor.shape查看形状tensor.reshape()/tensor.view()改变形状tensor.transpose()交换维度tensor.cat()拼接tensor.mean()/tensor.sum()聚合tensor.to(device)搬运设备tensor.detach()从计算图中分离这些操作足够覆盖你 90% 的入门场景。剩下的 API遇到具体问题时再查也不迟。4. autogradPyTorch 入门真正的分水岭如果说 Tensor 是 PyTorch 的“数据仓库”那 autograd 就是它的“动力引擎”。不理解 autograd你只是“会用 PyTorch 跑代码”理解了 autograd你才能“自己写得出来”。4.1 为什么说“能训练”和“会用框架”是两回事很多人跑通了第一个训练循环但问他一个问题就卡住了loss.backward() 到底执行了什么从表面看它只是反传了梯度。但真正重要的是它会沿着计算图从 loss 一步步回溯到每个参数计算出 loss 对每个参数的偏导数。这些导数随后被优化器用来更新参数。你不用手推链式法则但你要知道PyTorch 之所以能自动求导是因为它在每次前向计算时偷偷记录了一张“计算图”。import torch x torch.tensor(2.0, requires_gradTrue) y x ** 2 3 * x y.backward() print(x.grad) # 结果是 2*x 3 7这个例子里requires_gradTrue告诉 PyTorch你要追踪所有和x相关的操作并为后续的反向传播做准备。4.2 计算图与 requires_grad 的直观理解可以把计算图理解成一张“流水线台账”。你每做一次运算PyTorch 就在后台记录“这一步是谁和谁算出来的”。反向传播时它按图索骥把梯度从输出端一路传回输入端。这里有两个实际意义如果你不需要对某个张量求梯度就用torch.no_grad()包起来或者设置requires_gradFalse。推理阶段、计算准确率时一定要加no_grad()否则会白白占用内存。如果你想把某个张量从计算图里“拆”出来用.detach()。这在一些特殊训练技巧里有大用。4.3 梯度清零、累积和 no_grad 的工程意义PyTorch 的梯度默认是累积的。也就是说如果你不手动清零每跑一次backward()梯度都会加在之前的梯度上。所以在训练循环里你一定会看到这行代码optimizer.zero_grad()它必须在backward()之前调用才能保证每个 batch 的梯度都是干净的。如果你忘记清零loss 的下降曲线会非常奇怪甚至直接发散。推理阶段验证集、测试集的正确姿势是with torch.no_grad(): outputs model(inputs) # 计算准确率等指标不做梯度计算4.4 源码阅读该从哪里下手热词里有“源码解读”很多人一听源码就头大。其实 PyTorch 的源码阅读不需要从底层 C 开始入门阶段建议按这个顺序先读torch.nn.Module的基本用法弄懂__init__里定义层、forward里定义计算过程。再看一个小型模型的forward比如 LeNet理解数据形状在每一层之间是怎么流动的。然后看torch.optim下的一个优化器参数更新逻辑比如 SGD。最后再挑战一个自定义Dataset和DataLoader的实现理解数据批处理流程。你不用一行行读而是要带着问题去读“如果我想改这个模块应该改哪里”源码阅读的意义不是看它写了什么而是搞清楚哪里是边界、哪里可以扩展、哪里是写死的默认行为。5. Dataset 与 DataLoader真正决定项目能不能跑的往往是数据流水线模型结构再花哨如果数据供给不上来或者供给错了训练照样完蛋。Dataset 和 DataLoader 是 PyTorch 数据流水线的两个核心组件。5.1 批量处理图像时最容易出错的边界问题当我们使用 torchvision 里的数据集时代码看起来很简单from torchvision import datasets, transforms transform transforms.Compose([ transforms.Resize((32, 32)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) trainset datasets.CIFAR10(root./data, trainTrue, downloadTrue, transformtransform)但换到自定义数据集时最容易出错的就是“图片大小不一致”或者“标签和图片路径没对齐”。建议从一开始就写一个统一的规则所有图片先经过Resize统一尺寸。使用ImageFolder时目录结构必须是根目录/类别名/图片文件.jpg。每训练完一个 epoch抽几张训练样本的input和label打印出来看一眼确认数据没喂错。5.2 batch_size、shuffle、num_workers 的中庸配置DataLoader 里最常用的参数有四个dataloader DataLoader( dataset, batch_size32, # 每次取多少条样本 shuffleTrue, # 每个 epoch 是否打乱顺序 num_workers2, # 用几个子进程加载数据 pin_memoryTrue # 是否锁页内存可以加速 CPU 到 GPU 的传输 )batch_size太小则梯度更新不稳定、训练慢太大会显存溢出。常见起点是 32、64、128。显存不够时优先减半。shuffle训练集建议True测试集建议False。num_workers如果数据读取太慢可以增大这个值但不是越大越好Windows 下偶尔会引发多进程报错可以先设为 0 跑通再逐步调大。pin_memory如果数据在 GPU 上训练通常建议设置True。注意先不要一上来就把num_workers拉满它会显著增加内存占用和进程管理开销。先用小样本把训练流程跑通再加并行加载。5.3 数据不均衡和样本量不足时的工程应对热词里有“深度学习样本数量少的缺点”这是一个非常现实的工程问题。样本量少时最直接的问题是模型容易过拟合训练集 loss 很低验证集准确率上不去。应对手段通常有这几类数据增强对图像做随机翻转、裁剪、旋转、颜色扰动等。torchvision 的transforms提供了现成组件。模型简化减少层数、减小隐藏层维度、加 Dropout。正则化在 loss 里加权重衰减weight decay。迁移学习先加载一个在大数据集上预训练好的模型只训练最后几层这样可以用很小的数据量取得相对可用的结果。这一类问题没有银弹。关键是你要通过实验观察验证集的表现而不是只盯着训练集 loss 是否下降。6. 一个真正可以跑通的项目从 LeNet 到 CIFAR-10到了这一步你终于可以从“知道”变成“做到”。我们用一个非常经典的组合来走完整条流水线LeNet 网络 CIFAR-10 数据集。6.1 最小可运行代码下面这段代码示例覆盖了数据加载、模型定义、训练循环、验证循环。建议不要直接复制跑完就完事而是手动敲一遍一行一行理解它的作用。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms # 1. 数据预处理 transform transforms.Compose([ transforms.Resize((64, 64)), # LeNet 经典输入是 32x32这里放到 64 更稳 transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) batch_size 64 trainset datasets.CIFAR10(root./data, trainTrue, downloadTrue, transformtransform) testset datasets.CIFAR10(root./data, trainFalse, downloadTrue, transformtransform) trainloader DataLoader(trainset, batch_sizebatch_size, shuffleTrue, num_workers2) testloader DataLoader(testset, batch_sizebatch_size, shuffleFalse, num_workers2) # 2. 定义 LeNet 模型 class LeNet(nn.Module): def __init__(self, num_classes10): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 6, kernel_size5, stride1, padding2), nn.ReLU(), nn.MaxPool2d(kernel_size2, stride2), nn.Conv2d(6, 16, kernel_size5, stride1, padding0), nn.ReLU(), nn.MaxPool2d(kernel_size2, stride2), ) self.classifier nn.Sequential( nn.Linear(16 * 14 * 14, 120), nn.ReLU(), nn.Linear(120, 84), nn.ReLU(), nn.Linear(84, num_classes) ) def forward(self, x): x self.features(x) x x.view(x.size(0), -1) x self.classifier(x) return x # 3. 初始化模型、损失函数、优化器 device torch.device(cuda if torch.cuda.is_available() else cpu) model LeNet().to(device) criterion nn.CrossEntropyLoss() optimizer optim.SGD(model.parameters(), lr0.01, momentum0.9) # 4. 训练循环 num_epochs 5 for epoch in range(num_epochs): model.train() running_loss 0.0 for i, (images, labels) in enumerate(trainloader): 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() if i % 100 99: print(fEpoch [{epoch1}/{num_epochs}], Step [{i1}], Loss: {running_loss/100:.4f}) running_loss 0.0 # 5. 验证 model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in testloader: 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()必须在每次 backward 之前防止梯度累积。torch.max(outputs, 1)返回每一行最大值和对应下标下标就是预测类别。with torch.no_grad()在验证阶段禁用梯度计算既省内存又避免意外修改计算图。6.2 每一步都要能回答“为什么”很多人跑通之后我会建议他做一件事改一个参数观察会怎样。比如把batch_size从 64 改成 16训练时间、准确率会有什么变化把 SGD 换成 Adamloss 下降速度有什么不同去掉transforms.Normalize那行模型的 loss 还降得下去吗把 LeNet 的padding2改成padding0会报什么错这些实验比刷十节课都管用。因为你只有亲手制造过错误、亲手解决过错误才能真正理解框架为什么要这样设计。6.3 训练日志怎么看loss 下降缓慢可能不是模型问题训练过程中最常见的问题就是 loss 不降或者降得极慢。很多人第一反应是调模型结构但排查顺序应该更理性先确认数据没问题打印一两张图片和对应标签看是不是图片和标签错位了。再确认 loss 在正常范围如果是分类任务刚初始化时 loss 大约在log(类别数)附近如果偏差很大说明初始化或数据处理有问题。然后看 LR 是否合适学习率太大容易发散太小则下降很慢。常见起点是0.01或0.001。最后才是改模型结构很多“loss 不降”的案例最后查出来是数据归一化做错了或者标签从 1 开始而不是从 0 开始。7. 从项目到工程训练稳定之外还要考虑部署和精度选型一旦模型在验证集上表现满意下面就要考虑“怎么把模型用起来”。这一步也是深度学习和普通软件工程分道扬镳的地方。7.1 模型保存格式pt / pth / onnx 的区别PyTorch 里常见的模型保存格式有几种.pt/.pthPyTorch 原生的模型文件。通常有两种保存策略只保存模型参数或保存整个模型。.onnx开放神经网络交换格式可以跨框架导出到 TensorRT、ONNX Runtime、OpenVINO 等推理引擎部署时更通用。最简单的保存和加载方式# 保存参数推荐 torch.save(model.state_dict(), model.pth) # 加载参数 model LeNet() model.load_state_dict(torch.load(model.pth)) model.eval()注意state_dict只保存参数不保存模型结构加载时需要先实例化一个同结构的模型。如果你只依赖整模型保存在框架版本升级后可能遇到兼容性问题。7.2 部署必懂的浮点数FP32、FP16、BF16、TF32热词里有一个专业词组“FP32、FP16、BF16、TF32 浮点数格式详解与实战选型”。这个内容在模型部署和加速训练时绕不开。简洁版解释如下FP32单精度浮点数默认的训练精度。稳定但占用显存大、计算相对慢。FP16半精度浮点数能用一半显存计算更快但精度范围较小容易出现溢出或精度损失。深度学习加速卡通常对 FP16 有专门优化。BF16Brain Floating Point指数位比 FP16 多、尾数位少表示的范围比 FP16 大很多更适合训练场景。它在较新的 GPU 上支持良好。TF32TensorFloat-32是某些 GPU 上的一种特殊格式介于 FP32 和 FP16 之间主要用于矩阵乘法加速。它不是一个完全独立的存储格式更像是一种在 Tensor Core 上加速 FP32 矩阵乘法的截断策略。选型思路大致是训练阶段默认用 FP32。显存不够再考虑混合精度AMPPyTorch 自带torch.cuda.amp可以方便地把部分计算自动切到 FP16/BF16。推理阶段如果希望减小模型体积和提升速度再把模型转成 FP16 或 INT8 量化。但每一轮精度压缩后都要在真实数据上验证准确率变化不能只看文件名。注意混合精度不是把整个模型强制转换成 FP16而是由框架自动选择哪些计算可以用更低的精度哪些还必须用 FP32。强制转换常常导致梯度不稳定这也是很多人在迁移到低精度训练时踩坑的原因。7.3 视觉项目里的常见三方库边界热词里提到“计算机视觉三方库”。实际工程中PyTorch 之外还有几个常见视觉库它们常常混用但边界要搞清楚库定位和 PyTorch 的关系OpenCV图像读取、预处理、传统视觉算法通常在进入模型前处理图像torchvision数据集、预训练模型、图像变换工具PyTorch 官方视觉库Pillow基础图像读写和处理更轻量常用于数据预处理albumentations数据增强库可以搭配 PyTorch 使用增强策略更丰富如果你只是做分类、检测、分割的标准流程torchvision 足够如果涉及复杂的数据增强或传统视觉预处理可以考虑引入库的方式。但不要一上来就把所有库都装一遍项目用到什么再加什么。7.4 谈到“深度学习检测加速”先分清训练加速还是推理加速热词里有“Halcon 深度学习检测加速”“深度学习模型部署”这类词。我想强调一个容易混淆的地方训练加速和推理加速是两回事。训练加速目标是尽量缩短模型收敛时间。手段包括 GPU 并行、混合精度、分布式训练、更高效的 DataLoader。推理加速目标是让模型在部署环境里尽快给出结果。手段包括模型量化、剪枝、蒸馏、使用专门的推理引擎。很多人误以为“模型部署就是把这个 pth 文件加载进来 then 跑一下”结果到了生产环境发现延迟不达标。这是两种思维模式越早区分越好。8. 遇到问题别慌按这条链路排查PyTorch 报错信息千奇百怪但大多数问题都可以用同一条链路来排查。我把这套链路总结成一个“从现象到根因”的顺序现象报错 / 卡住 / 结果异常 → 输入数据格式、路径、标签 → 环境Python 版本、PyTorch 版本、CUDA 是否可用 → 数据管道Dataset、DataLoader、形状、dtype → 模型forward、参数初始化、层配置 → 损失函数loss 值范围、是否收敛 → 设备CPU/GPU 是否一致、显存是否溢出 → 日志从哪个 epoch/step 开始异常8.1 一个实际的报错案例走查假设你遇到这个报错RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!按上面的链路来排查现象已经告诉你tensor 不在同一个设备上。输入检查你传入模型的 images 和 labels 有没有都执行了.to(device)。环境确认torch.cuda.is_available()是否返回True。数据管道检查 DataLoader 返回的 tensor 有没有混用 CPU 和 GPU。模型查看模型初始化时是否某些 buffer如 BatchNorm 的 running_mean留在了 CPU 上。loss 和设备确认 criterion 计算时输入是否都在 GPU 上。大多数情况下这个报错就是“某个地方忘了.to(device)”或者你用了多个模型、多个 tensor有一部分没搬。8.2 排查时最容易忽略的三个点模型里的 buffer 和参数可能不在同一设备。特别是使用了 BatchNorm 或某些带缓存状态的层时model.to(device)会把参数和 buffer 一起搬但如果你手动创建了额外的 tensor就要自己负责搬运。DataLoader 的 num_workers 在 Windows 下有特殊坑。Windows 下多进程加载数据时必须把主逻辑放在if __name__ __main__:里否则可能无限递归启动子进程。loss 变成 NaN 不一定是学习率太大。先检查数据里有没有 NaN 值、标签是不是越界了、损失函数有没有用到log(0)之类的操作。8.3 防患于未然养成三个小习惯每个新项目开始前先跑一个 batch 的数据通过模型确认形状和 loss 值正常再开始完整训练。每一个 epoch 结束时打印验证指标不要只靠训练 loss 判断模型好坏。保存模型时把epoch、optimizer、best_acc一起保存方便恢复训练和对比。结语先把最小闭环跑通再谈优化回看整篇文章我没有给你一份完整的 API 手册也没有写一个复杂的大项目。我只想帮你建立一条主线深度学习入门不是学会 PyTorch 的每个函数而是建立一条从数据到模型到训练再到部署的完整路径。在这条路径上你每一步都会遇到问题。遇到问题的第一反应不应该是我打开搜索引擎搜“报错怎么解决”而是先想清楚我现在卡在哪一层是数据没对是模型结构有问题还是只是设备没对齐如果你能看到这里我建议你的下一步只有一件事把上面那个 LeNet CIFAR-10 的代码自己敲一遍然后做三个修改实验改 batch size、改优化器、去掉 normalize。做完这三个实验你对 PyTorch 的理解会比刷十遍教程更深。
返回列表