必须同时设置torch、CUDA、NumPy和Python random四类种子,并禁用cuDNN benchmark、启用deterministic模式,且为DataLoader的worker显式初始化随机状态,否则结果仍不可复现。

PyTorch中必须设置的三个随机种子
只设 torch.manual_seed() 不够,实验仍可能不可复现。PyTorch 依赖多个独立的随机源:CPU 张量运算、CUDA 运算、Python 自带的 random 和 numpy。漏掉任意一个,都可能导致初始化、数据打乱、Dropout 或 DataLoader 的 shuffle 行为出现差异。
实操建议如下:
- 调用
torch.manual_seed(seed)(对 CPU 和 CUDA 都生效) - 显式设置
random.seed(seed)和numpy.random.seed(seed) - 若使用 CUDA,必须加
torch.cuda.manual_seed_all(seed)(注意是all,不是seed) - 在模型定义、数据加载、训练循环之前一次性设置,且只设一次
DataLoader 的 shuffle 和 worker_seed 容易被忽略
即使全局种子设了,DataLoader 中的 shuffle=True 仍可能引入不可复现性——因为其内部使用 torch.Generator,且多进程 worker 会继承父进程状态但不自动同步 seed。
正确做法:
立即学习“Python免费学习笔记(深入)”;
- 禁用多进程干扰:设
num_workers=0(最简单可靠) - 或保留多 worker 时,必须传入带 seed 的
generator:generator = torch.Generator().manual_seed(seed),再传给DataLoader(generator=generator, shuffle=True) - 若用
num_workers > 0,还需设置worker_init_fn来为每个子进程重设种子:worker_init_fn=lambda worker_id: np.random.seed(seed + worker_id)
其他影响可复现性的隐藏因素
有些操作看似无关,实则破坏确定性:
- CUDA 算法选择:某些算子(如卷积、RNN)默认启用非确定性优化。需强制开启确定性模式:
torch.backends.cudnn.enabled = False(关闭 cuDNN)
或更精细地:torch.backends.cudnn.deterministic = True且torch.backends.cudnn.benchmark = False - 浮点运算顺序:混合精度(AMP)或不同 GPU 架构下,累加顺序可能变化。若需极致复现,避免
torch.cuda.amp或固定torch.set_float32_matmul_precision("highest") - 第三方库:Hugging Face Datasets、albumentations 等可能自带随机逻辑,需查文档确认是否支持 seed 透传
验证是否真正可复现的最小检查项
不要只比对最终指标,那太晚。应在关键节点插入断言:
- 模型参数初始化后,立刻用
torch.allclose(model.state_dict()[key], expected_tensor)检查前几层权重 - 第一个 batch 的输入和标签,在两次运行中是否完全一致(打印
hash(tuple(data.flatten().tolist()))) - 训练前调用
torch.use_deterministic_algorithms(True)(PyTorch ≥ 1.8),它会在非确定性算子触发时报错,而非静默出错
GPU 型号、驱动版本、PyTorch 编译方式(conda vs pip vs source)也会影响底层行为,跨环境复现时这些细节往往比种子本身更难控制。


















