不够。只设 torch.manual_seed 无法覆盖 random、numpy、CUDA、DataLoader 多进程及 cuDNN 等多个独立随机源,必须同时固定这四类种子、禁用 cudnn.benchmark、启用 cudnn.deterministic,并为 DataLoader 显式配置 generator 和 worker_init_fn。

PyTorch训练结果不一致,是不是只设 torch.manual_seed 就够了?
不够。只调用 torch.manual_seed 只能控制 PyTorch CPU 张量的初始化和部分算子(如 torch.rand),但训练中还有多个独立的随机源未被覆盖,导致即使种子相同,不同运行间 loss 或权重更新仍可能漂移。
必须同时固定以下四个来源:
-
python自带的random模块(影响数据采样、增强逻辑等) -
numpy(很多自定义 transform、label shuffle 依赖它) -
torchCPU 和 GPU(torch.cuda.manual_seed_all对多卡尤其关键) - 如果用了
Dataloader的shuffle=True,还需设generator参数(否则shuffle内部用的是未受控的默认 RNG)
如何正确设置全部随机种子?
推荐封装成一个函数,在训练脚本最开头(import 后、模型/数据加载前)调用:
def set_seed(seed: int):
import random
import numpy as np
import torch
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)
但注意:这还不够——DataLoader 的 shuffle 默认不认这个 seed。必须显式传入 generator:
train_loader = DataLoader(
dataset,
batch_size=32,
shuffle=True,
generator=torch.Generator().manual_seed(42) # 必须单独设!
)
别用 torch.Generator(device="cuda"),除非你确定所有 worker 都在 GPU 上跑(实际几乎不会),否则会报错或失效。
为什么开了 num_workers > 0 还是不复现?
因为每个 worker 子进程会继承主进程的 seed,但 Python 的 random 和 numpy 状态**不会自动同步到子进程**,导致每个 worker 内部的随机行为不可控。
解决方法是在 DataLoader 中加 worker_init_fn:
def worker_init_fn(worker_id):
import random
import numpy as np
import torch
worker_seed = torch.initial_seed() % 2**32
random.seed(worker_seed)
np.random.seed(worker_seed)
<p>train_loader = DataLoader(
dataset,
num_workers=4,
worker_init_fn=worker_init_fn,
generator=torch.Generator().manual_seed(42)
)</p>
这里的关键是用 torch.initial_seed() —— 它返回当前 worker 初始化时的真实 seed,比硬编码更可靠;而 % 2**32 是为了适配 numpy.random.seed() 要求的 uint32 范围。
还有哪些容易被忽略的随机性来源?
这些点不常被文档强调,但实测会影响复现性:
-
torch.backends.cudnn.enabled = True(默认开启)时,cuDNN 会启用非确定性算法加速卷积。必须关掉:torch.backends.cudnn.enabled = False - 更进一步,设
torch.backends.cudnn.deterministic = True(强制使用确定性算法),同时torch.backends.cudnn.benchmark = False(禁用 benchmark,否则首次运行会选快但不确定的 kernel) - 如果你用了
torch.compile或torch._dynamo,目前(v2.3)仍存在非确定性行为,建议暂时关闭 - 某些第三方库(如 albumentations)内部用
cv2或自己的 RNG,需查其文档看是否支持 seed 注入
GPU 显存分配顺序、TensorRT 插件、甚至某些 NCCL 版本下的分布式 all-reduce 都可能引入微小差异——真正追求 100% 复现,得在完全相同的软硬件环境里跑,且接受训练速度明显下降。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











