多卡环境下 Ray Tune 的 GPU 冲突,多数不是 Ray 本身崩溃,而是 trial 进程实际拿到的 GPU 资源超过了被分配的配额,或两个 trial 被调度到同一张卡上同时申请显存。判断方向是:先确认每个 trial 真实需要多少 GPU 算力与显存,再把请求值压到隔离级别以下。
1 排查冲突来源
先看自己的 Ray 集群是否只调用了单节点多卡。可在 head 节点执行 ray status,确认总资源数中有多少 GPU,每个 trial 会消耗多少。如果是通过 ray.init(num_gpus=...) 启动,该参数只指定 Ray 进程可用的 GPU 数,不限制每个 trial 的资源。
冲突的常见信号:trial 日志里有 'EAGAIN: CUDA' 或 out of memory,Ray dashboard 里 trial 卡在等待资源,或者 nvidia-smi 显示同一张卡被多个 python 进程占用。需要结合这三个来源确认是调度冲突还是显存超限。
2 设置每试验 GPU 配额
在 tune.run 中通过 resources_per_trial 给出每个试验的逻辑资源请求。例如每个 trial 只使用 1 张卡,写 resources_per_trial={'gpu': 1};如果希望多个试验共享一张卡,可写 {'gpu': 0.5},但需要模型代码本身能限制物理显存占用。
from ray import tune
def train_fn(config):
# 若有多个 GPU,让当前 trial 绑定到分配给它的设备
import os, torch
local_rank = int(os.environ.get('LOCAL_RANK', '0'))
torch.cuda.set_device(local_rank)
# 训练逻辑...
tune.run(
train_fn,
resources_per_trial={'gpu': 1}, # 或 0.5 表示共享一张卡
num_samples=10,
)
更细粒度的控制可以使用 tune.PlacementGroupFactory,它与绑定 CPU、GPU、显存绑在一起,能减少不同 trial 被塞到同一个加速器中的概率。
3 验证与观察
启动一个小规模搜索(例如 2-3 个 trial),同时观察 dashboard 的资源位图和 nvidia-smi 的进程。确认每个 trial 都只命中自己申请的那张 GPU 设备编号,且显存占用未超过单卡容量。验证动作是:记录每个 trial 的 PID 与 CUDA 设备编号,再对照其日志中实际使用的显存。
也可以快速打印当前 Ray 分配给 trial 的资源:
from ray import get_gpu_ids
print('gpu_ids:', get_gpu_ids())
若能打印出预期的设备 id 列表,说明调度层正常;如果返回空,但任务仍然跑到 GPU 上,就要检查 ray 初始化参数。
4 冲突时回退调整
当冲突仍存在,优先把 resources_per_trial 中的 GPU 请求值降低,例如从 1 降到 0.5,并配合在训练函数里根据 get_gpu_ids() 设置 CUDA_VISIBLE_DEVICES,保证只有一个进程能看到目标卡。显存不够时,先调小 batch size,不要先提升并行度。
如果问题出在 Ray 集群层面,例如单机多卡但 Ray 只识别到 1 张卡,需要重启 ray,并检查 ray status 中的资源列表。如果多机环境,要确认节点间没有由于显存配额不一致导致调度不均衡。
作为应急止血,可以用 max_concurrent_trials 限制同时运行的 trial 数,但这个只能是权宜,不能替代正确的资源配额。