设置 os.environ["CUDA_VISIBLE_DEVICES"] 是最可靠的方式,必须在 import torch 前设置,其值决定PyTorch可见的逻辑GPU序号,影响 torch.device("cuda:N") 的有效性及多卡训练行为。

设置 os.environ["CUDA_VISIBLE_DEVICES"] 是最可靠的方式
PyTorch 本身不直接解析 GPU 编号字符串,而是依赖 CUDA 运行时环境变量来“屏蔽”或“重映射”可见设备。设置 CUDA_VISIBLE_DEVICES="2" 后,PyTorch 看到的 cuda:0 实际对应物理 GPU 2。这比在代码里用 torch.device("cuda:2") 更底层、更稳妥——后者在环境未限制时可能因驱动/NCCL 初始化失败而静默降级到 CPU。
关键点:
- 必须在
import torch之前 设置,否则无效(PyTorch 在首次 import 时读取该变量) - 值为逗号分隔的整数,如
"1,3"表示只暴露 GPU 1 和 3,此时torch.cuda.device_count()返回 2 - 设为
"-1"可强制禁用 CUDA(等价于无 GPU 环境)
torch.device("cuda:N") 的行为与陷阱
这个写法看似直观,但实际效果高度依赖 CUDA_VISIBLE_DEVICES 是否已设置。如果环境变量未设,"cuda:2" 会尝试使用物理编号 2;如果已设为 "1,3",那 "cuda:2" 会报 Invalid device ordinal 错误——因为此时只有 0 和 1 两个逻辑设备。
常见错误现象:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
-
RuntimeError: CUDA error: invalid device ordinal:通常是因为CUDA_VISIBLE_DEVICES缩小了可见范围,但代码仍硬写大编号 -
torch.cuda.is_available()返回True,但torch.tensor(...).cuda(2)报错:说明 CUDA 可用,但指定设备不可达 - 多进程训练中子进程未重新设置环境变量,导致所有进程争抢同一 GPU
验证 GPU 是否真正生效的三步检查
光看 device 对象不保险,得查运行时绑定和内存分配。
执行以下检查:
- 打印
torch.cuda.current_device()—— 它返回当前默认设备的逻辑索引(不是物理编号) - 调用
torch.cuda.get_device_name(0)(注意传的是逻辑序号)确认型号,避免“看到 cuda:0 却其实是 GPU 0 而非你想要的 GPU 2” - 运行
nvidia-smi观察对应物理 GPU 的显存占用是否上升,这是最终判据
示例片段:
import os os.environ["CUDA_VISIBLE_DEVICES"] = "2" # 必须在 import torch 前 import torch print(torch.cuda.device_count()) # 输出 1 print(torch.cuda.current_device()) # 输出 0 print(torch.cuda.get_device_name(0)) # 输出类似 'NVIDIA A100-SXM4-40GB'
多卡训练时 torch.nn.DataParallel 和 torch.distributed 的区别
两者对 GPU 编号的处理逻辑完全不同:DataParallel 自动使用 range(torch.cuda.device_count()),即只用你通过 CUDA_VISIBLE_DEVICES 暴露出来的设备;而 DistributedDataParallel(DDP)要求每个进程绑定一个**唯一** GPU,需显式传入 torch.device(f"cuda:{local_rank}"),且 local_rank 必须与 CUDA_VISIBLE_DEVICES 的顺序一致(比如设为 "3,4",则 rank 0 应绑 cuda:0 → 物理 3,rank 1 绑 cuda:1 → 物理 4)。
容易被忽略的细节:
- DDP 场景下,
torch.cuda.set_device(local_rank)必须在模型 .to(device) 前调用,否则 NCCL 初始化可能失败 -
CUDA_VISIBLE_DEVICES若设为"2,2"(重复编号)会导致device_count()返回 2,但两个逻辑设备指向同一物理卡,极易 OOM - 容器环境中(如 Docker),需加
--gpus '"device=2,3"'参数,否则宿主机环境变量对容器内无效

















