DDP需显式初始化进程组、用DistributedSampler切分数据、包装模型并正确启动;推荐torch.distributed.run启动,单机多卡须设device_ids=[rank],保存权重用model.module.state_dict()。

PyTorch 的 DDP(DistributedDataParallel)不是“开箱即用”的多卡加速,它要求你显式初始化进程组、划分数据、包装模型,并且所有 GPU 必须在同一个节点上——跨节点需额外处理 NCCL 后端和网络配置。
如何正确启动 DDP 进程(torch.distributed.run vs mp.spawn)
直接调用 python train.py 不会自动分发进程;必须用 PyTorch 提供的启动器。推荐优先使用 torch.distributed.run(PyTorch 1.9+),它自动处理 rank、world_size 和主节点发现,比手写 mp.spawn 更少出错。
-
torch.distributed.run默认使用nccl后端,适合单机多卡;启动命令形如:python -m torch.distributed.run --nproc_per_node=4 train.py - 若需自定义进程启动逻辑(例如固定 CUDA_VISIBLE_DEVICES),才考虑
mp.spawn,但要手动传入rank和world_size,且容易漏掉torch.cuda.set_device(rank) - 错误现象:未设置
CUDA_VISIBLE_DEVICES或未调用set_device→ 所有进程挤在 GPU 0 上,报RuntimeError: CUDA out of memory
为什么必须用 DistributedSampler 而不是普通 DataLoader
DDP 不会自动切分数据;每个进程加载全量数据会导致重复训练、收敛异常甚至 loss 不下降。必须用 DistributedSampler 让每个 GPU 只看到数据子集,且 epoch 间 shuffle 是全局一致的。
- 初始化时传入
num_replicas=world_size和rank=rank,否则采样逻辑失效 -
DataLoader的shuffle=True应设为False,由DistributedSampler控制;否则两次 shuffle 冲突 - 验证/测试阶段也建议用
DistributedSampler(shuffle=False),避免不同卡上评估样本不一致 - 注意:如果数据集长度不能被
world_size整除,DistributedSampler会补零(drop_last=False默认行为),导致少量样本被重复采样
model = DDP(model) 的关键参数与常见陷阱
DDP 包装不是简单套壳;参数选择直接影响梯度同步时机、内存占用和兼容性。
立即学习“Python免费学习笔记(深入)”;
-
find_unused_parameters=True:仅当模型含条件分支(如部分层在某些 batch 不参与前向)时才启用,否则严重拖慢训练速度(每步都遍历全部参数检查) -
device_ids=[rank]:单机多卡必填,否则 DDP 可能尝试在 CPU 上同步 GPU 张量,报Expected all tensors to be on the same device - 模型必须先
.to(device)再传给DDP;顺序反了会触发隐式设备转移,引发不可预测的 CUDA 错误 - 不要对
DDP模型调用.module以外的属性(如.named_parameters()),应统一通过model.module访问原始模型
如何安全地保存和加载 DDP 模型权重
DDP 模型的 state_dict 带有 module. 前缀;直接保存会污染权重键名,导致单卡加载时报 Missing key。
- 保存时用
model.module.state_dict(),而非model.state_dict() - 加载时,如果权重是 DDP 格式(含
module.),单卡模型需先加前缀或用strict=False;更稳妥的是始终保存原始模型权重 - checkpoint 中应同时保存
optimizer.state_dict()和epoch,但optimizer本身无需 DDP 包装——DDP 只负责梯度同步,优化器仍由各进程独立执行 - 注意:
torch.save用torch.save(..., _use_new_zipfile_serialization=True)(默认)可避免旧版本读取失败
最易被忽略的一点:DDP 的梯度同步发生在 loss.backward() 之后、optimizer.step() 之前,这个时机由 NCCL 后端控制,无法手动干预;任何在 backward 和 step 之间修改模型参数或梯度的操作(比如梯度裁剪后又清零)都必须确保所有进程同步执行,否则破坏一致性。


















