只设 torch.manual_seed 不够,因为 PyTorch 随机性还来自 CUDA、cuDNN、DataLoader 多进程、numpy 和 Python random;需四步:固定各 RNG、禁用 cudnn.benchmark、设 cudnn.deterministic、为 worker_init_fn 传递带 ID 的种子。

为什么只设 torch.manual_seed 不够
很多用户调用 torch.manual_seed(42) 后仍得不到可复现结果,是因为 PyTorch 的随机性来源不止 CPU 张量操作。GPU 运算、CUDA 库(如 cuBLAS、cuDNN)、数据加载器的多进程采样,甚至 Python 自身的 random 和 numpy 都会干扰复现性。
- CUDA 操作默认启用非确定性算法(例如
cudnn.benchmark=True会自动选最快 kernel,但结果可能不同) -
DataLoader开启num_workers > 0时,子进程不继承主进程种子,且worker_init_fn若未显式设置,各 worker 使用不同随机状态 -
numpy.random和random是独立 RNG,必须单独固定
完整固定种子的四步写法
以下代码应放在训练脚本最开头(早于任何模型、数据、CUDA 初始化):
import torch import numpy as np import random <p>def set_seed(seed: int = 42) -> None: torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) # 多卡也生效 np.random.seed(seed) random.seed(seed) torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False # 关键:禁用自动优化 </p>
-
torch.cuda.manual_seed_all必须显式调用,torch.manual_seed不影响 CUDA RNG -
torch.backends.cudnn.deterministic = True强制 cuDNN 使用确定性算法,但会略微降低速度 -
torch.backends.cudnn.benchmark = False是关键——即使设了 deterministic,benchmark 为 True 时仍可能触发非确定行为
DataLoader 多进程下的种子陷阱
当 num_workers > 0,每个子进程有独立 RNG 状态,默认不从主进程继承种子,必须通过 worker_init_fn 手动传递。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 不要依赖
torch.initial_seed(),它返回的是该 worker 自己初始化的 seed,不是你设的那个 - 正确做法是把主进程种子 + worker id 做哈希,避免 worker 间种子重复:
def worker_init_fn(worker_id): seed = torch.initial_seed() % 2**32 np.random.seed(seed + worker_id) random.seed(seed + worker_id) - 然后在
DataLoader中传入:worker_init_fn=worker_init_fn
验证是否真的固定住了
最直接的验证方式:两次运行同一段生成代码,检查输出张量是否完全一致(包括 GPU 上的 tensor)。
- 用
torch.equal(a, b)判断两个 tensor 是否逐元素相等(注意a == b返回 bool tensor,不是标量) - 对 GPU tensor,先
.cpu()再比较,避免 device 不一致报错 - 如果用了
torch.compile或第三方库(如 FlashAttention),它们可能引入额外随机性,需单独查文档确认 determinism 支持情况
真正麻烦的不是设种子,而是所有随机源都得被你抓到——漏掉一个 numpy.random.choice 或某个库内部没暴露 RNG 控制的函数,复现就破功。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










