实用技巧
学习目标
完成本节后,你将能够:
- 正确处理 CPU / GPU 设备切换
- 使用随机种子提升实验可复现性
- 理解混合精度训练和梯度裁剪的作用
- 会保存和恢复模型 checkpoint
- 建立一份 PyTorch 调试检查清单
一、先解决最常见的工程问题
1.1 设备切换:先别假设你一定有 GPU
很多初学者会直接把代码写死成 cuda(),结果在没有 GPU 的机器上直接报错。
更稳妥的写法是:
import torch
device = "cuda" if torch.cuda.is_available() else "cpu"
print("当前设备:", device)
x = torch.tensor([[1.0, 2.0], [3.0, 4.0]]).to(device)
print(x)
print("张量所在设备:", x.device)
你可以把 device 理解成“训练发生在哪张工作台上”:
- CPU:普通桌面
- GPU:并行运算的大工作台
1.2 固定随机种子:让实验尽量可复现
训练不稳定时,第一件事往往不是改模型,而是先固定随机性。
import random
import numpy as np
import torch
def set_seed(seed=42):
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)
print(torch.randn(3))
set_seed(42)
print(torch.randn(3))
如果两次打印结果一样,说明这部分随机性被固定住了。
为什么“尽量”而不是“绝对”?
有些 GPU 算子和并行细节仍然可能引入微小差异,所以可复现通常是“更接近”,不是“绝对一模一样”。
二、让训练过程更稳
2.1 train()、eval() 和 no_grad() 要形成肌肉记忆
训练与验证最容易写乱的地方,不是模型结构,而是模式切换。
标准习惯:
model.train() # 训练前
...
model.eval() # 验证 / 推理前
with torch.no_grad():
...
你可以把它理解成:
train():模型进入“练习模式”eval():模型进入“考试模式”no_grad():考试时不做反向传播草稿,节省内存
2.2 梯度裁剪:防止梯度突然爆掉
在 RNN、Transformer 或较深网络里,梯度有时会变得很大,导致训练不稳定。
梯度裁剪就是“给梯度设一个上限”。
import torch
from torch import nn
torch.manual_seed(42)
model = nn.Sequential(
nn.Linear(10, 20),
nn.ReLU(),
nn.Linear(20, 1)
)
x = torch.randn(32, 10)
y = torch.randn(32, 1) * 50
loss_fn = nn.MSELoss()
pred = model(x)
loss = loss_fn(pred, y)
loss.backward()
def grad_norm(model):
total = 0.0
for p in model.parameters():
if p.grad is not None:
total += p.grad.norm(2).item() ** 2
return total ** 0.5
before = grad_norm(model)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
after = grad_norm(model)
print("裁剪前梯度范数:", round(before, 4))
print("裁剪后梯度范数:", round(after, 4))
这就像给下坡的自行车加个限速器,避免冲得太猛。