返回专辑
·Johan·4 分钟阅读

cudnn deterministic 全链路

固定 seed 只能控制部分随机性——DataLoader shuffle、cudnn benchmark、多线程和硬件仍会带来 run-to-run 差异,要 log 环境并报告 variance。

cudnn deterministic 全链路

1. seed=42 两次 run 差 0.3 mAP

做 ablation 对比两种 augment,torch.manual_seed(42) 只设一次——两次 full run 的 val mAP 差 0.3。不是 augment 无效,是 随机性没封全:DataLoader 多 worker 各自 seed 不同、cudnn benchmark 选不同 kernel、augment 用 global random 未控。只报单次 best 数值,在噪声里做决策;论文式「提升 0.2」可能落在方差内。团队应约定:ablation PR 至少双 seed,主结论 seed 报 mean±std。

2. seed 要覆盖的链路

python
import os, random, numpy as np, torch

def seed_all(seed=42):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False

DataLoader shuffle:generator=torch.Generator().manual_seed(seed)。多 worker 必须 worker_init_fn

python
def worker_init_fn(worker_id):
    s = torch.initial_seed() % 2**32
    np.random.seed(s)
    random.seed(s)

augment 里 random.* / np.random.* 都要纳入 worker seed;Albumentations 等库也要在 worker 内设 seed。主进程里设的 seed 不会自动进 worker,这是最常见的复现洞。

3. deterministic 的代价与边界

python
torch.use_deterministic_algorithms(True)
os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8"

慢 10–30%,部分 op 无 deterministic 实现会抛错。cudnn.benchmark=True 更快但 run-to-run 不同——ablation 期关 benchmark,上线 profiling 再开。AMP fp16 可能有 bitwise 差异;验收看 metric parity(mAP ±0.01),不苛求逐元素相同。生产训练不必全程 deterministic,但 对比实验 必须同一随机性契约。

4. 实验报告契约

同 config 至少跑 2–3 seed(42/43/44),报 mean ± std。log:PyTorch/CUDA/cuDNN 版本、GPU 型号、deterministic/benchmark 标志、DataLoader num_workers。改一项固定其余做单变量 ablation。PR 描述贴 seed 列表与方差,比只贴 best 曲线可审。leaderboard 条目应含硬件与软件栈,否则跨机器数字不可比。

5. DDP 与多 GPU

各 rank 用相同 base seed + rank offset,保证 augment 不同但 run 可复现。各 rank batch shape 一致;drop_last=True 避免末 batch BN 不稳。DDP 下只 rank0 写 log,但所有 rank loss 应同步检查——某 rank NaN 常来自 shard 数据或 seed 不一致。DistributedSampler 的 epoch seed 也要在每次 epoch 开始时 set_epoch(seed)

6. TF32 与硬件差

PyTorch 2.x 在 Ampere+ 默认 TF32 on matmul,与 strict fp32 有数值差。strict repro 可 torch.backends.cuda.matmul.allow_tf32 = False(更慢)。换 GPU 型号复现失败,先对齐软件栈再谈算法。实验 README pinned 版本,而不是「差不多就行」。

7. 失败模式

现象原因
两次 loss 分叉worker seed / augment 未控
deterministic 报错某 op 无 deterministic 实现
DDP 不一致各 rank batch 或 seed 不同

8. 验收

strict 模式下同 config 两次 loss 曲线应 overlap(或 metric 在报告 std 内)。环境 dump 进 experiment log。val best → test 一次。改动可单变量回滚。

9. 案例:只 seed 主进程

某检测 ablation 主进程 seed 固定,Albumentations 在 worker 里用默认 random——双 run mAP 0.312 vs 0.341,结论反转。加上 worker_init_fn 后双 run 0.328±0.002,augment 真实无效。教训:复现脚本模板 应内置 seed_all + worker_init_fn + generator,copy 训练脚本时不应省略这三行。

10. 与 CI / 回归测试

主分支 nightly 用固定 seed 跑 smoke train 10 step,loss 曲线与 golden 比 diff——超阈 block。不是 full repro,但能抓 worker seed 回归。发版 notes 贴 PyTorch/CUDA 版本;客户环境复现失败时先 diff 环境再 diff 代码。

11. 何时不必 deterministic

生产训练追求吞吐,可开 cudnn.benchmark、多 seed 报 mean。但 发 paper / 定架构 ablation 必须 deterministic 或至少 multi-seed。把两种模式写进文档,避免工程师在 prod 集群上跑 ablation 或在 lab 上用 benchmark 报单次 best。数据增广论文对比时,应固定 seed 列表并报告 std;只比 best seed 与「复现」口号矛盾。容器镜像 pin PyTorch 小版本,升级前跑 repro regression suite。第三方 augment 库升级可能改 random 行为,lock 版本并在 changelog 里标 augment 依赖。论文复现请求应附带 environment.yml 与三 seed 日志,而不是只给 checkpoint。Notebook 里 %env 改 CUBLAS 要在 import torch 前执行,否则 deterministic 设置半生效——把 env 写进启动脚本而不是 cell 顺序靠记忆。Multi-GPU 实验记录 world size 与 per-GPU batch,复现时不一致会改 effective BN 与 LR scaling。

12. 案例:benchmark 打开 ablation 作废

同一 augment ablation,A 配置 benchmark=True 报 +0.4 mAP,B 配置 deterministic 报 +0.05 mAP——结论反转。规定 ablation 分支强制 deterministic+multi-seed;prod 训练才允许 benchmark。CI 检查 train 脚本里 benchmark flag 与实验类型匹配。Fix seed 后仍差 0.05 mAP 内可接受时,报告 std 而非 claim 显著。Numpy 1.x vs 2.x 某些 rng 行为变,升级时重跑 repro suite。

CI 检查 train 脚本里 benchmark flag 与实验类型匹配。Fix seed 后仍差 0.05 mAP 内可接受时,报告 std 而非 claim 显著。Numpy 1.x vs 2.x 某些 rng 行为变,升级时重跑 repro suite。W&B 等工具 log seed 与 git sha,复现请求应能 one-click 找到三元组 seed/env/commit。Split 应用 group(scene/user)而非 random row,否则 leak 与 seed 纠缠难查;group split 与 seed 分开写进 paper 方法节。Prod 训练 log 含 torch.__version__cuda 版本,半年后可复现 dispute 时对齐环境。Paper supplementary 附 seed 列表与 std,不单贴 best seed 截图。

复现是让 ablation 结论可辩护的最低工程标准——seed 只设一次不够,要封整条随机性链路。

← 全部文章

johan's blog