想让服务器上的程序跑在指定GPU上,核心答案就是通过环境变量CUDA_VISIBLE_DEVICES和框架自带的设备参数(如PyTorch的device)来精确控制,设置后程序只会看到你指定的那一块卡。
怎么让程序在指定gpu上跑?先看懂CUDA_VISIBLE_DEVICES
很多人在单卡环境下写代码,一上多卡服务器就懵了,程序默认占用第0块卡,但第0块卡可能已经被同事的训练任务占满了,这时候直接用nvidia-smi看到的是物理编号,但CUDA程序内部使用的是另一套逻辑编号。
CUDA_VISIBLE_DEVICES这个环境变量,就是连接物理GPU和程序可见GPU的桥梁,它的规则很简单:程序启动时读取这个变量,只暴露你指定的物理卡给CUDA运行时,比如你设置CUDA_VISIBLE_DEVICES=2,那么程序内部看到的是cuda:0,但实际跑在物理编号为2的卡上。
临时设置和永久设置的区别
临时设置只对当前终端窗口生效,适合快速测试,永久设置写在~/.bashrc或/etc/profile里,每次登录自动生效,适合固定使用某张卡的同学。
# 临时设置:程序跑在物理卡2上 export CUDA_VISIBLE_DEVICES=2 python train.py # 或者单条命令方式,不污染当前终端环境 CUDA_VISIBLE_DEVICES=2 python train.py # 跑多张卡,用逗号分隔 CUDA_VISIBLE_DEVICES=1,3 python train.py
在Python代码内部指定GPU
有些场景下,你不想改启动脚本,希望代码自动选择空闲的卡,可以在Python文件开头设置环境变量,但必须在导入PyTorch或TensorFlow之前。
import os os.environ['CUDA_VISIBLE_DEVICES'] = '2' # 必须放在import torch之前 import torch
行业共识认为,这种代码内设置方式适合写深度学习推理服务,因为服务框架启动时不太方便手动加环境变量,但要注意,如果多个进程同时执行这段代码且指定了同一块卡,显存会直接溢出。
多卡服务器如何指定gpu运行:框架内的几种常见写法
实际训练时,很多人习惯在Python代码里用torch.device('cuda:0')或model.to('cuda:1')来指定设备,但这里有个常见的坑:代码里的cuda:0对应的是CUDA可见设备列表中的第0个,而不是物理卡0。
假设服务器有4块卡,物理编号0到3,你设置了CUDA_VISIBLE_DEVICES=2,3,那么代码里的cuda:0对应物理卡2,cuda:1对应物理卡3,如果你在代码里写cuda:0,实际跑的是物理卡2。
PyTorch中的指定方式
PyTorch提供了两种思路,一种是用device参数逐模型指定,另一种是用torch.cuda.set_device()全局指定。

# 方式一:显式创建device对象
device = torch.device('cuda:1') # 注意这是逻辑编号
model = model.to(device)
# 方式二:全局指定当前设备
torch.cuda.set_device(1)
model = model.cuda()
# 方式三:配合环境变量使用(推荐)
# 启动时设置 CUDA_VISIBLE_DEVICES=2,3
# 代码里直接写 cuda:0 或 cuda:1,不关心物理编号
业内专家指出,绝大多数深度学习训练框架都有一个通病:默认占用所有可见GPU,即使你只写了model.cuda(),有些框架也会把所有卡都初始化一遍,解决方法是设置环境变量CUDA_VISIBLE_DEVICES只暴露一张卡,或者用os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'配合物理编号排序。
TensorFlow中的指定方式
TensorFlow 2.x推荐用tf.config.list_physical_devices('GPU')配合tf.config.set_visible_devices来控制。
import tensorflow as tf
gpus = tf.config.list_physical_devices('GPU')
if gpus:
# 只使用物理卡1
tf.config.set_visible_devices(gpus[1], 'GPU')
如果你习惯用环境变量,CUDA_VISIBLE_DEVICES=1同样适用于TensorFlow,而且比代码内设置更早生效,能避免框架初始化时扫描所有GPU。
docker容器内指定gpu运行:一张卡还是多张卡
容器化部署越来越普遍,docker指定gpu运行和裸机设置不太一样,容器内部只能看到你通过--gpus参数分配的设备,容器里的nvidia-smi显示的也是宿主机GPU的物理编号。
用–gpus参数控制
Docker 19.03以上版本支持--gpus参数,这是最直接的方式。
# 分配所有GPU docker run --gpus all -it my_image # 只分配物理卡0和2 docker run --gpus '"device=0,2"' -it my_image # 分配指定数量的GPU(从0开始自动挑) docker run --gpus 2 -it my_image
容器内同样支持CUDA_VISIBLE_DEVICES环境变量,但在docker场景下,这个变量作用于宿主机物理编号还是容器内可见编号,取决于NVIDIA Container Toolkit的版本,较新版本中,容器内环境变量作用于容器可见设备,建议用--gpus参数直接控制,避免混淆。
多容器共享GPU的分配策略
实际运维中,经常遇到多个容器需要共享一张GPU的情况,这时候需要限制显存和计算单元占用。
# 限制显存占用为4GB docker run --gpus '"device=0,"' -e NVIDIA_DRIVER_CAPABILITIES=compute,utility -it my_image # 配合nvidia-smi监控宿主机显存 watch -n 1 nvidia-smi
关于服务器指定gpu运行后显存不够的问题,多数情况下是多个进程同时分配了同一张卡,建议在启动脚本里加一个简单的显存检测逻辑,找到空闲卡再分配。

怎么确认程序真的跑在了指定gpu上
指定完GPU,最重要的就是验证,很多人设置了环境变量,但程序还是跑在其他卡上,问题出在设置时机太晚或被其他配置覆盖。
用nvidia-smi实时监控
最直接的方法是在程序运行期间,另开一个终端执行nvidia-smi,看进程列表里的GPU编号和显存占用。
# 实时刷新GPU状态 watch -n 1 nvidia-smi # 只看进程和GPU对应关系 nvidia-smi --query-compute-apps=gpu_uuid,pid,used_memory --format=csv
如果程序确实跑在指定卡上,nvidia-smi的进程列表会显示对应的GPU编号和Python进程PID。
在代码里打印当前设备
更精确的验证方式是在代码里打印实际使用的设备信息。
import torch
# 打印当前PyTorch使用的设备编号
print(f'当前设备: {torch.cuda.current_device()}')
print(f'设备名称: {torch.cuda.get_device_name()}')
# 查看CUDA可见设备数量
print(f'可见设备数量: {torch.cuda.device_count()}')
这套方法能直接验证CUDA_VISIBLE_DEVICES是否生效,如果设置CUDA_VISIBLE_DEVICES=2,torch.cuda.device_count()应该返回1,current_device()返回0,但get_device_name()显示的物理卡2的名称。
排查GPU编号漂移问题
多卡服务器容易出现GPU编号漂移,比如重启后物理卡编号变化,解决方案是设置CUDA_DEVICE_ORDER=PCI_BUS_ID,让CUDA按照PCI总线顺序识别GPU,而不是默认的枚举顺序。
export CUDA_DEVICE_ORDER=PCI_BUS_ID export CUDA_VISIBLE_DEVICES=2
这样设置后,物理编号和CUDA编号的对应关系更稳定,尤其适合服务器上有不同型号GPU混插的场景。
服务器GPU指定常见问题与避坑
指定了GPU但程序报错CUDA out of memory
这是最常见的问题。程序报显存不够,不一定是你指定的卡显存真的不够,很可能是程序初始化时默认加载了所有可见GPU,或者有残留进程占用了显存。
排查步骤:
- 用
nvidia-smi查看指定卡的实际显存占用和进程 - 用
fuser -v /dev/nvidia查看哪些进程占用了GPU设备文件 - 用
kill -9 PID杀掉残留进程后重新运行
代码里写了cuda:0但实际跑在别的卡上
这个问题几乎都是CUDA_VISIBLE_DEVICES设置时机不对,Python代码里用os.environ设置环境变量,但import torch时CUDA已经初始化了,环境变量不生效。
正确做法:
import os os.environ['CUDA_VISIBLE_DEVICES'] = '2' # 必须在导入torch之前 import torch

或者干脆在启动脚本里设置,不要在Python代码里设置,有些框架(比如HuggingFace的transformers)会在导入时自动调用torch.cuda.is_available(),提前锁定设备,代码内设置更不可靠。
多进程训练时GPU分配不均
在使用DataLoader的num_workers>0时,每个worker子进程会继承父进程的CUDA上下文,如果父进程已经设置了CUDA_VISIBLE_DEVICES,子进程也会沿用,不会出现分配不均,真正的麻烦在于多个独立训练任务同时启动,没有协调机制。
推荐做法是写一个简单的GPU分配脚本:
#!/bin/bash
# 检测当前显存占用,自动挑选最空闲的GPU
for i in 0 1 2 3; do
mem=$(nvidia-smi -i $i --query-gpu=memory.used --format=csv,noheader,nounits | tr -d ' ')
if [ $mem -lt 1000 ]; then
export CUDA_VISIBLE_DEVICES=$i
break
fi
done
python train.py
这套脚本解决的是gpu利用率不均衡怎么办的问题,尤其适合多人共用一台服务器的场景。
Q&A
服务器指定gpu运行后,程序仍然把模型加载到所有卡上怎么办?
在PyTorch中,如果你使用了nn.DataParallel,它会默认使用所有可见GPU,设置CUDA_VISIBLE_DEVICES只暴露一张卡后,DataParallel会退化为单卡模式,如果不想改环境变量,可以显式指定DataParallel使用的设备列表,例如nn.DataParallel(model, device_ids=[0]),TensorFlow中设置tf.config.set_visible_devices后,其他设备不会被初始化。
多卡服务器如何指定gpu运行,才能避免和别人的任务抢显存?
先跑nvidia-smi查看当前各卡显存占用,选一张占用率最低的卡,然后用CUDA_VISIBLE_DEVICES=卡号启动程序,如果服务器有多个用户同时使用,建议在启动脚本里加一个自动检测空闲卡的逻辑,或者用nvidia-smi --query-compute-apps=pid --format=csv查看是否有其他用户的计算进程在运行,多数情况下,显存占用超过80%的卡不建议再分配新任务。
docker容器内怎么确认程序用的是哪块gpu?
在容器内执行nvidia-smi,显示的是宿主机GPU的物理编号,如果容器是用--gpus '"device=2"'启动的,容器内nvidia-smi只会显示物理卡2的信息,如果容器内nvidia-smi显示所有GPU,说明容器启动时用的是--gpus all,这时需要在容器内设置CUDA_VISIBLE_DEVICES来限制,容器内程序实际使用的设备编号,依然可以通过PyTorch的torch.cuda.current_device()或TensorFlow的tf.config.list_logical_devices('GPU')来确认。
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/671943.html


评论列表(3条)
这篇文章写得非常好,内容丰富,观点清晰,让我受益匪浅。特别是关于之前的部分,分析得很到位,给了我很多新的启发和思考。感谢作者的精心创作和分享,期待看到更多这样高质量的内容!
这篇文章的内容非常有价值,我从中学习到了很多新的知识和观点。作者的写作风格简洁明了,却又不失深度,让人读起来很舒服。特别是之前部分,给了我很多新的思路。感谢分享这么好的内容!
读了这篇文章,我深有感触。作者对之前的理解非常深刻,论述也很有逻辑性。内容既有理论深度,又有实践指导意义,确实是一篇值得细细品味的好文章。希望作者能继续创作更多优秀的作品!