1. 从“装得上”到“跑得动”:Pytorch 实操到底卡在哪
很多人第一次装 Pytorch,都是兴冲冲打开官网,复制一行 pip 命令,然后回车。结果要么是下到一半网速归零,要么装完一跑import torch直接报错,要么明明装了 GPU 版却跑在 CPU 上。这类问题我在各种群里见了无数次,自己也全都踩过一遍。
先说结论:Pytorch 的安装本身不难,难的是装之前搞清楚三件事——你的机器是什么显卡、你的 Python 环境是干净的还是已经被 Anaconda 管理起来了、你下载的包和本机 CUDA 驱动版本是否匹配。这三件事没理顺,后续每一步都在填坑。这个实操版,就是把这些坑一个个拆开,从环境准备到模型导出 ONNX,按真实操作顺序走一遍。写这篇的目的不是让你看一遍就懂,而是让你照着敲一遍就能跑通,顺便知道每一步背后的原因。
适合谁看?刚接触深度学习、准备正式搭一套本地 Pytorch 环境的人,以及已经装过但没跑通 GPU 训练、想排查问题的同学。有基础的也可以直接跳到 3、4 章看验证脚本和常见报错对照。
2. 动手之前,先想清楚这 5 个选择
2.1 用 GPU 还是纯 CPU
这是最容易忽略的一条。如果你的电脑没有 NVIDIA 显卡,或者只有核显,那就老老实实装 CPU 版。别觉得装了 GPU 版就是“完整版”,在无 N 卡机器上装 GPU 版,轻则报CUDA driver not installed,重则整个 torch 导入失败。
有 N 卡的,也要先确认显卡型号和显存大小。GTX 1650、RTX 3060 这类 4~12G 显存,跑 CV 小模型、NLP 基础实验足够了。想训练大 Batch 的 Transformer,显存直接决定上限。装之前先看一眼显存,既能避免后面 OOM(显存溢出)时不知所措,也能帮你决定要不要用混合精度。
2.2 先查显卡驱动支持的最高 CUDA 版本
很多人把“英伟达驱动”和“CUDA 工具包”搞混。Pytorch 安装时选择的 CUDA 版本,是它运行时需要的 runtime API 版本,而你的显卡驱动决定系统最高能支持到哪个 CUDA 版本。如果 Pytorch 选的 CUDA 版本比驱动支持的最高版本还高,它根本起不来。
在命令行跑nvidia-smi,右上角CUDA Version就是当前驱动支持的最高版本。以这个数字为准,往下选 Pytorch 对应的 CUDA 版本即可。我的机器驱动支持 12.4,我就选cu121或cu124的 Pytorch,稳妥又省事。
提示:驱动是向下兼容的。驱动支持 12.4,代表 11.8、12.1 这些更低的版本也能跑。所以 Pytorch 版本选低半档往往比选顶格更稳。
2.3 Conda 还是 Pip
这是个老问题,我两种都用过。总的使用经验是:新手优先用 Conda,因为它同时管理 Python 版本和包依赖,创建一个干净的虚拟环境,把 Pytorch 及相关库装进去,互不污染。Pip 更轻量,但在 Python 版本混乱的机器上容易装乱。
另外,Anaconda 自带的conda命令下载包时可以把源替换成国内镜像,速度提升非常明显。Pip 也可以换源,但需要先把pip升级到位。下文安装教程我会把两条路径都写清楚,你先看完再选一条走就行。
2.4 Python 版本是不是越新越好
不是。Pytorch 对 Python 版本有明确的兼容范围,目前稳定支持 3.8~3.12,最新版已经支持 3.13,但很多配套库还没跟上。实操建议直接选 3.10 或 3.11,这两个版本目前兼容性最好,后续装 transformers、onnxruntime 这类依赖时不会碰到"编译失败"这种头疼问题。
2.5 装完要不要先验证 GPU 可用
这个看似多余,实则是整个安装过程的分水岭。很多人装完 Pytorch,跑个import torch不报错就以为万事大吉,实际却一直用 CPU 训练。验证 GPU 是否真的被 Pytorch 识别,只需要三行代码,但能帮你省下后续排查训练速度异常的大把时间。具体命令在 4.1 会给到。
下面这张表把当前主流的安装方案做个对比,你可以直接照着自己情况定位:
| 机器情况 | 推荐方案 | 说明 |
|---|---|---|
| Windows + NVIDIA 显卡 | Conda 创建 Python 3.11 环境,pip 安装 cu121/cu124 版本 | 最简单,极少出编译问题 |
| Windows 无独显 | pip 安装 CPU 版 | 防止误选 GPU 包导致报错 |
| Linux + NVIDIA 显卡 | pip 或 conda 安装,检查驱动支持版本 | 注意系统是否有 libcuda 限制 |
| WSL2 环境 | 在 WSL 内部安装,驱动由 Windows 侧共享 | 不要单独在 WSL 里装驱动 |
3. 完整安装流程:从零到跑通的每一步
3.1 安装 Anaconda 并创建干净环境
以 Windows 为例,去 Anaconda 官网下载安装包,一路默认安装。装完后打开Anaconda Prompt,先创建一个不附带任何预设包的干净环境:
conda create -n pytorch_2 python=3.11 -y这里pytorch_2是环境名,你可以随喜好起。创建完成后激活:
conda activate pytorch_2激活后,命令行的开头会变成(pytorch_2),说明已经进入这个环境。之后所有操作都在这个环境内完成,和系统其他 Python 环境彻底隔离。这一步是环境管理的基础,强烈建议养成习惯。
3.2 配置国内镜像源(下载速度翻倍的关键)
不换源的话,Pytorch 安装包动不动几百 MB,从 PyPI 官方源拉取可能等十分钟。需要依次给 conda 和 pip 配置镜像。
Conda 源配置,在命令行执行:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --set show_channel_urls yesPip 源配置,执行:
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/这样后续用 pip 安装任何包,都默认走阿里云镜像,速度稳定。实测下来,一个 700MB 左右的 torch 包,在普通宽带下两三分种就能下载完。
3.3 确定安装命令:GPU 和 CPU 分开操作
激活环境后,先进入 Pytorch 官网首页的Get Started页面,它会自动识别你的操作系统和包管理器,给出对应命令。
如果你用的是 NVIDIA 显卡且驱动支持 CUDA 12.x,建议走 pip 路线。比如安装 CUDA 12.1 版本:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121CPU 版本则简单得多:
pip install torch torchvision torchaudio这里torchvision和torchaudio是配套库,分别负责图像处理和音频处理,如果你只做 NLP 任务,不装这两个也能用,但装齐了后面扩展方便。
如果官网给出的命令在你的网络上经常断掉,可以加超时和重试参数,实测有效:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 --timeout 60 --retries 3注意:不要手动去下载 whl 文件再本地安装,除非你非常明确版本依赖关系。Pytorch 各组件版本之间强耦合,手动指定版本极易出现
torchvision和torch版本不匹配的报错。
3.4 安装后的依赖补齐
Pytorch 装完只是第一步。实际操作中你几乎一定会用到 numpy、matplotlib、scikit-learn、jupyter,这些在同一个环境下一次装齐:
pip install numpy matplotlib scikit-learn jupyter pandas tqdmNLP 方向再补 transformers、tokenizers 和 datasets;CV 方向看需求装 opencv-python。这一步虽然不直接属于 Pytorch 安装,但万一后续代码跑一半发现缺库,再回头补装会比较打断节奏。
3.5 Linux 与 WSL2 环境的特殊说明
Linux 环境下安装过程基本一致,但有一个关键点必须注意:如果你的发行版是 Ubuntu,且是通过 apt 安装过python3-pip,那么系统内可能存在多个 Python 环境。用which python确认当前 python 路径是否指向你创建的 conda 环境,避免 pip 装到了系统环境里却不知情。
WSL2 用户则要留意,Windows 侧需要安装好 NVIDIA 显卡驱动,WSL 内部不要再重复安装nvidia-driver,否则会冲突。WSL 里能够直接调用 Windows 侧的 GPU,只要在 WSL 内安装支持 CUDA 的 Pytorch 即可。检查 WSL 能否看到 GPU:
nvidia-smi如果能看到显卡信息,说明共享驱动正常。
4. 环境验证:三行代码确认安装成功
4.1 确认版本与 CUDA 可用性
激活环境,进入 Python 交互模式,依次输入:
import torch print(torch.__version__) print(torch.cuda.is_available())第一条输出2.x.x+cu121这样的版本号,说明 torch 是 CUDA 版本;第二条输出True,说明 GPU 可用。如果输出False,不用急,常见原因在下文排查表里逐条对照。
同时看一下 GPU 设备名称:
print(torch.cuda.get_device_name(0))正常会输出类似NVIDIA GeForce RTX 3060的信息。到这一步,你的 GPU 环境才真正跑通。这里再补充一个容易忽略的点:torch.cuda.is_available()返回True,只说明 Pytorch 找到了可用的 CUDA 设备,并不代表显存足够。训练大一点模型前最好看一眼显存占用:
import torch print(torch.cuda.memory_allocated() / 1024**2, "MB") print(torch.cuda.memory_reserved() / 1024**2, "MB")4.2 跑通第一个张量运算
光验证可用性还不够,最好做一个真实的张量运算来确认前向传播没问题。比如在 GPU 上做矩阵乘法:
a = torch.randn(1024, 1024, device="cuda") b = torch.randn(1024, 1024, device="cuda") c = torch.matmul(a, b) print(c.sum().item())这句代码能跑通,且输出一个数值,基本说明从驱动到 Pytorch 的全链路都正常。如果此时报错提示显存不足,说明显卡驱动和 Pytorch 版本匹配,但显存太小或已有进程占用,释放后再试。
4.3 小土堆式入门测试:全连接网络训练一轮
更接近真实使用场景的验证是跑一个小训练循环。只写一个非常简单的一维回归任务,代码就一段:
import torch import torch.nn as nn model = nn.Linear(1, 1) optimizer = torch.optim.SGD(model.parameters(), lr=0.01) loss_fn = nn.MSELoss() for epoch in range(50): x = torch.rand(16, 1) * 10 y = 3 * x + 1 pred = model(x) loss = loss_fn(pred, y) optimizer.zero_grad() loss.backward() optimizer.step() if epoch % 10 == 0: print(f"epoch {epoch}, loss = {loss.item():.4f}")这段就是深度学习的最小训练闭环:定义模型、定义损失、反向传播、更新参数。跑通它之后,再学任何网络结构都不会在环境层面卡壳。把它当成环境验证的“基准测试”非常合适,一旦这个流程稳定输出 loss 递减,你的环境就算真正就绪了。
5. 常用模型训练实操:从随机数据到有效训练
5.1 构建一个两层的神经网络
Pytorch 最常用的建模方式是继承nn.Module,手动定义 forward 前向传播。下面是一个用于二分类示例的两层网络:
import torch.nn as nn import torch.nn.functional as F class SimpleNet(nn.Module): def __init__(self, in_dim=2, hidden_dim=16, out_dim=2): super().__init__() self.fc1 = nn.Linear(in_dim, hidden_dim) self.fc2 = nn.Linear(hidden_dim, out_dim) def forward(self, x): x = F.relu(self.fc1(x)) x = self.fc2(x) return x这是最标准的 Pytorch 结构。注意forward只是定义函数,Pytorch 在调用model(x)时自动执行它。这也是 Pytorch 和 TensorFlow 最大的不同:Pytorch 是动态图,前向传播按代码逻辑即时执行,调试时可以直接打印中间张量,不用像静态图那样必须先构图再运行。
5.2 数据加载:用 Dataset 和 DataLoader 避免手动管 Batch
很多入门案例用循环手写 batch,真实项目中使用官方数据加载机制更高效。以随机数据为例:
from torch.utils.data import Dataset, DataLoader class MyDataset(Dataset): def __init__(self, n_samples=1000): self.x = torch.randn(n_samples, 2) self.y = (self.x[:, 0] * 2 + self.x[:, 1] > 0).long() def __len__(self): return len(self.x) def __getitem__(self, idx): return self.x[idx], self.y[idx] data = MyDataset() loader = DataLoader(data, batch_size=32, shuffle=True)__getitem__返回单个样本,DataLoader负责按 batch_size 拼装并随机打乱。这种方法在你切换到真实图片、文本数据集时完全通用,属于必须掌握的核心机制。
5.3 训练循环的标准模板
有了模型、数据、优化器和损失函数之后,训练循环基本固定为五个动作:
model = SimpleNet() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) loss_fn = nn.CrossEntropyLoss() model.train() for epoch in range(5): total_loss = 0 for x_batch, y_batch in loader: optimizer.zero_grad() pred = model(x_batch) loss = loss_fn(pred, y_batch) loss.backward() optimizer.step() total_loss += loss.item() print(f"epoch {epoch}, avg loss = {total_loss / len(loader)}")这里有两个细节值得注意。第一是optimizer.zero_grad(),它必须在每次反向传播前清空梯度,否则梯度会累加,导致训练不稳定。第二是model.train(),它让 BN、Dropout 等层进入训练模式,验证或推理时则要切换到model.eval(),两者切换必须在训练前后主动调用。
5.4 L2 正则化的实际写法
Pytorch 内置了 L2 正则化,只是入口在优化器参数里:weight_decay就是 L2 系数。很多新手以为要在 loss 里手动加lambda * sum(w^2),其实官方支持的方式更简洁:
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)weight_decay=1e-4相当于是对每个参数施加 2e-4 的衰减系数,防止权重过大,从而缓解过拟合。如果你追求更精细的控制,比如对某些层不施加 L2,可以手动实现 L2 loss 并在反向传播前加入总 loss,但那属于进阶操作,基础阶段直接用weight_decay足够。
5.5 把训练放到 GPU 上
上面这版训练默认在 CPU 上运行。真实场景里这一步必须改,核心是模型和 batch 切换设备:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = SimpleNet().to(device) for x_batch, y_batch in loader: x_batch, y_batch = x_batch.to(device), y_batch.to(device)注意:数据和模型必须同时移到 GPU,否则 Pytorch 报错提示Expected all tensors to be on the same device。这是一个高频新人报错,记得写成习惯动作。用sentence-transformers等第三方库时尤其注意,很多人导入预训练模型后忘了.to(device),结果 GPU 占用 0%,实际上模型还在内存里。
6. Pytorch 转 ONNX:把训练好的模型变成跨平台部署格式
6.1 为什么需要 ONNX
模型训练完后,无论是部署到 Web 后端、移动端还是嵌入式设备,直接通过 Python 调用 Pytorch 往往不是最优选择。ONNX(Open Neural Network Exchange)是一种开放的模型表示格式,可以把 Pytorch 模型转换为统一的图结构,再用 ONNX Runtime、TensorRT 等推理引擎加载,实现跨框架、跨设备推理。
通俗地讲,Pytorch 模型可以当成训练态的“活体”,需要 Python 环境和对应网络结构代码才能复现;而 ONNX 模型是“定型”的推理产物,只要有个简单的运行时库就能执行,部署环境可以很瘦。这也是我们把转 ONNX 作为实操板块放进来的原因。
6.2 从state_dict到完整模型输出
通常训练完保存的model.pt有两种形式:
- 只保存参数
state_dict,加载时需要先重新创建模型结构再load_state_dict - 整个模型对象直接用
torch.save(model, "model.pt")保存
转 ONNX 前,不管哪种形式都要先恢复成一个nn.Module实例且切换为eval模式。转换的标准写法如下:
import torch import onnxruntime as ort model = SimpleNet() model.load_state_dict(torch.load("model.pt", weights_only=True)) model.eval() dummy_input = torch.randn(1, 2) torch.onnx.export( model, dummy_input, "simplenet.onnx", input_names=["input"], output_names=["output"], opset_version=17, dynamic_axes={"input": {0: "batch_size"}, "output": {0: "batch_size"}} )这里dummy_input是给模型的“虚拟输入”,用于追踪计算图结构,形状可以随便设,但维度必须匹配模型真实输入。dynamic_axes允许推理时 batch 大小不固定,适合服务端并发请求场景。如果把dynamic_axes去掉,则模型固定只能接受 batch=1 或 batch=2(取决于 dummy_input),这会给后续部署带来极大不便。
6.3 转完怎么验证正确性
转完的 ONNX 模型至少要做一步一致性验证:同样的输入,Pytorch 模型和 ONNX 模型的输出应当一致。
onnx_model = ort.InferenceSession("simplenet.onnx", providers=["CPUExecutionProvider"]) sample = torch.randn(1, 2).numpy() onnx_output = onnx_model.run(None, {"input": sample})[0] torch_output = model(torch.from_numpy(sample)).detach().numpy() print(np.max(np.abs(onnx_output - torch_output)))这个差值理论上应该在 1e-5 左右,甚至更小。差值过大说明转换出了精度问题,常见原因是 opset 版本不合适、使用了自定义算子未注册,或是模型里存在转换不支持的动态控制流。
6.4 转 ONNX 的常用坑
第一,opset_version别太新也别太旧。太旧的 opset 缺少一些新算子支持,太新的则对推理引擎版本有要求。目前用 17 左右的版本在 onnxruntime 1.17+ 上最稳。
第二,模型里如果有torch.max、torch.argmax这类算子,部分 opset 下会导致 ONNX 权重尺寸推断异常,建议转换前先测试。
第三,动态输入涉及文本长度或图片尺寸变化时,dynamic_axes的配置要细化到具体维度,不能只写 batch。一个常用技巧是在torch.onnx.export中同时设置dynamic_axes为多个轴,比如时间步长度和 batch 都标为动态。
7. 常见问题与排查技巧实录
7.1 GPU 可用性检测 False
这是最普遍的报错。按下面顺序排查:
- 运行
nvidia-smi,确认驱动能识别 GPU。不行就是显卡驱动问题。 - 确认安装的 Pytorch 版本带 CUDA。
pip show torch看版本号有没有+cu后缀,没有就卸载重装。 - 确认驱动支持的 CUDA 版本大于等于 Pytorch 需要的版本。比如下载了 cu124 但驱动只支持 12.1,就会出现 False。
- 如果是在 WSL2 中,确认
nvidia-smi在 WSL 内部能正常显示。
7.2 安装了 GPU 版却只跑 CPU
检查你的代码有没有把模型和数据都.to(device)。另一个隐性原因是 Pytorch 默认 tensor 在 CPU,如果你直接构建 tensor 而不指定 device,哪怕是 GPU 环境也会落在 CPU 上。训练前先写一个辅助函数,统一为所有输入指定设备位置,养成显式.to()的习惯。
7.3 pip 下载慢导致超时
已经配置镜像源的前提下,还可以临时用 pip 参数:
pip install <包名> --timeout 600 --retries 10但更推荐的是在使用 Pytorch 官方源时,用wget到浏览器下载 whl 再用 pip 本地安装,适合网络状况差的时候。不过记住,这种方式要手动保证版本一致。
7.4 torchvision 和 torch 版本不匹配
报错示例是torchvision==0.x.x依赖torch==2.x.x但你装的是别的版本。根本解法是不要分别单独安装,而是用一条命令同时安装torch, torchvision, torchaudio,让 pip 自动解析依赖。如果已经出现了冲突,就先卸载这三件套再重装。
7.5 显存不足 OOM
训练时报CUDA out of memory表示显存真的不够。快速解决办法是减小 batch size,或者开启混合精度。混合精度示例:
from torch.cuda.amp import autocast, GradScaler scaler = GradScaler() for x_batch, y_batch in loader: optimizer.zero_grad() with autocast(): pred = model(x_batch) loss = loss_fn(pred, y_batch) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()混合精度可以在不显著降低精度的情况下节省显存。GTX 16 系显卡在部分操作上对自动混合精度支持不完整,如果遇到 loss 变成 NaN,临时关闭 autocast 再确认原因。
| 问题 | 根因定位 | 推荐处理 |
|---|---|---|
torch.cuda.is_available()为 False | 驱动 / CUDA 版本 / Pytorch 版本不匹配 | 从 nvidia-smi 查最高支持版本,重装对应 Pytorch |
| 装了 GPU 版却在 CPU 跑 | 模型或数据未.to(device) | 检查所有 tensor 设备位置 |
| 训练 loss 为 NaN | 学习率过大 / 数据含 NaN / 混合精度问题 | 调低学习率,检查数据预处理 |
| 转 ONNX 输出不一致 | opset 版本或算子支持不全 | 升高或降低 opset,逐个算子测试 |
| 下载慢或超时 | 网络原因 | 换镜像源或加长 timeout |
7.6 我的几条保命习惯
装完环境第一件事,我会建一个env_check.py脚本,把 4.1、4.2、4.3 的代码全部放进去,每次换机器后直接跑一遍,几分钟内确认环境是否 OK。这个习惯帮我省去了大量重复排查时间。
另外,训练前先在DataLoader上打个断点,用next(iter(loader))手动取一个 batch,检查数据类型、形状和值分布。数据是模型的上游,数据有问题再怎么调参都是白费。
保存模型时,我倾向于同时保存state_dict和完整的超参配置,也就是把model_ckpt.pt、config.json都放同一个目录。后面恢复模型时直接加载配置重建结构,不会再出现“别人给的代码里结构不一致”的情况。
8. 一个更省心的方案:把整个流程固化成模板
开始跑真实项目后,你会发现“环境搭建”只是第一道坎,“每次新实验复现环境”才是持续的消耗。这里说一个我自己的做法:把安装完成后的一整套依赖固化到一个 requirements 文件里,之后每个新项目直接用同一个文件创建环境。
生成当前环境的全部依赖列表:
pip freeze > requirements.txt新机器上重建环境:
conda create -n project_env python=3.11 -y conda activate project_env pip install -r requirements.txt这样能保证同一个项目在多人协作或换机时表现得完全一致。如果项目用到特定版本的 cuda 算子,还建议把torch.__version__和nvidia-smi的驱动版本号一并记录在 README 里。很多人复现不了别人的实验,十有八九是环境记录不完整。
如果再往后走,真正省心的做法是转向 Docker 或 Python venv。Docker 适合服务端部署,可以把 Pytorch 和 CUDA 相关库一起打进去,实现真正的“一次构建、到处运行”。但前期学习阶段,本地 Anaconda 环境已经够用了,不必过度工程化。
最后分享一个我个人一直坚持的小习惯:不要在 base 环境装任何项目依赖。Anaconda 的 base 当工具台用,创建独立环境去跑项目。只有这样,多个项目之间的包冲突才能彻底隔离。踩过几次“在 base 里装坏 torch”的坑之后,我真的建议所有人都把项目环境分开管理。这套流程走完后,后续无论跑分类模型、目标检测还是文本生成,Pytorch 基础环境这块你都不需要再操心了。