直接结论:必须用conda创建带CUDA和NCCL的专用环境并配DDP启动逻辑,因其最稳、复现性强、踩坑少;conda需显式指定pytorch-cuda版本及nvidia/conda-forge源,避免pip混装导致链接错误;DDP初始化须设MASTER_ADDR等环境变量或用torchrun启动;数据加载必须用DistributedSampler并调用set_epoch();batch_size指每卡大小;NCCL通信需检查GPU拓扑与NCCL_DEBUG日志。

直接结论:别从 pip + virtualenv 开始,用 conda 创建带 CUDA 和 NCCL 的专用环境,再配 DDP 启动逻辑——这是目前最稳、复现性最强、踩坑最少的路径。
conda 环境必须显式指定 pytorch-cuda 和 channel
很多人卡在 torch.cuda.is_available() 返回 False,根本原因不是驱动没装,而是 pip 安装的 PyTorch 没绑定对应 CUDA 版本。conda 能原子化拉取预编译二进制包,包含 cuDNN、NCCL、cudnn 等原生依赖。
- 先添加可靠源:
conda config --add channels conda-forge和conda config --add channels nvidia - 创建环境时明确指定 CUDA 版本,例如:
conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia - 避免混用 pip 和 conda 安装 PyTorch 相关包,否则可能触发
undefined symbol: ncclGetVersion这类链接错误
DDP 初始化必须靠环境变量或 init_method 显式传参
PyTorch 的 dist.init_process_group() 不会自动猜你有几台机器、哪台是 master。它依赖一组确定的启动参数,漏掉任意一个都会卡死或报 Connection refused。
- 单机多卡推荐用
init_method='env://',但必须提前设好:MASTER_ADDR(如127.0.0.1)、MASTER_PORT(如29500)、WORLD_SIZE(总 GPU 数)、RANK(当前进程序号) - 启动命令要带
torchrun或手动python -m torch.distributed.run,不要自己写mp.spawn—— 它容易漏设local_rank,导致模型加载到 CPU - 验证是否生效:
print(f"Rank {dist.get_rank()}/{dist.get_world_size()}"),所有进程输出应覆盖 0~N−1
数据加载器必须用 DistributedSampler 才能真正分片
即使 DDP 初始化成功、模型也跑在 GPU 上,如果还用普通 DataLoader,所有进程仍会读取全部数据,造成重复训练、显存溢出、结果不可复现。
立即学习“Python免费学习笔记(深入)”;
-
DistributedSampler是唯一能按RANK自动切分数据集的工具,必须传给DataLoader(sampler=...) - 每个 epoch 开始前要调用
sampler.set_epoch(epoch),否则 shuffle 行为在各进程间不一致 - batch_size 指的是**每张卡上的 batch 大小**,不是全局 batch;若想等效于单卡 bs=64 的四卡训练,应设
batch_size=16
最容易被忽略的是 NCCL 通信层的静默失败:没有报错,但 GPU 利用率长期低于 30%,ncclCommInitRank 卡住却无提示。务必在启动前加 export NCCL_DEBUG=INFO,并检查 nvidia-smi topo -m 输出中 GPU 间是否为 NV(而非 PHB 或 PIX)连接——PCIe 拓扑不合理时,DDP 加速比会断崖式下跌。


















