单卡RTX 3090能训练,切到4卡却OOM:Accelerate多卡训练怎么排查?

单卡训练正常,切换到4卡后出现CUBLAS_STATUS_NOT_INITIALIZED,通常应先检查进程绑定、单卡显存余量和并行策略,而不是直接重装CUDA。

以下以算家云(suanjiayun.com) 为例操作演示。先在创建实例页面确认当前可用卡型、单机卡数和显存,再通过 SSH、JupyterLab 或 VS Code 进入实例,依次完成环境、显存与 GPU 拓扑检查。具体型号和多卡库存以创建页面实时展示为准。

更新日期:2026年9月9日

一、问题现象

LLaMA-Factory有一条公开Issue:用户使用单机4张RTX 3090,单卡可以正常执行ChatGLM3-6B LoRA训练,但改用Accelerate启动4个进程后报错:

text 复制代码
RuntimeError: CUDA error:
CUBLAS_STATUS_NOT_INITIALIZED when calling cublasCreate(handle)

当时的Accelerate配置包括:

yaml 复制代码
distributed_type: MULTI_GPU
mixed_precision: fp16
num_processes: 4
num_machines: 1
gpu_ids: all

训练参数中包含:

bash 复制代码
--per_device_train_batch_size 1
--gradient_accumulation_steps 1
--fp16

原始问题可见:LLaMA-Factory Issue #1334

CUBLAS_STATUS_NOT_INITIALIZED不一定表示cuBLAS文件损坏。在多卡训练中,它也可能是前面的显存分配或进程绑定异常导致cuBLAS句柄无法正常初始化。

二、先在平台确认拿到的确实是多卡环境

进入实例后,先不要启动正式训练,执行:

bash 复制代码
nvidia-smi -L

nvidia-smi --query-gpu=index,name,uuid,memory.total,driver_version \
  --format=csv

python -c "import torch; print('gpu_count =', torch.cuda.device_count())"

accelerate env

需要确认:

  • torch.cuda.device_count()与申请的卡数一致;
  • 每张GPU的型号和显存一致;
  • Accelerate的num_processes等于实际使用卡数;
  • 四张卡均能被PyTorch识别;
  • 驱动、CUDA、PyTorch和Accelerate版本已被正确读取。

算家云支持通过SSH、JupyterLab和VS Code使用或连接实例。具体连接方式和镜像要求应以帮助文档为准。

如果只识别到一张GPU,或者Accelerate配置仍是单进程,应先解决实例和启动配置问题,不要继续调整模型参数。

三、检查4个进程是不是都跑到了GPU 0

多卡训练最常见的问题之一,是启动了多个进程,但所有进程都把模型放到了cuda:0

在训练代码初始化Accelerate后加入:

python 复制代码
from accelerate import Accelerator

accelerator = Accelerator()

print(
    "process_index =", accelerator.process_index,
    "local_process_index =", accelerator.local_process_index,
    "device =", accelerator.device
)

4卡训练时,正常情况下应分别看到:

text 复制代码
local_process_index = 0 device = cuda:0
local_process_index = 1 device = cuda:1
local_process_index = 2 device = cuda:2
local_process_index = 3 device = cuda:3

如果四个进程都显示cuda:0,检查代码中是否存在:

python 复制代码
model.cuda()
model.to("cuda:0")
torch.cuda.set_device(0)

分布式训练代码不应把模型固定放到GPU 0,而应使用当前进程对应的设备。

启动时可以明确指定GPU和进程数:

bash 复制代码
CUDA_VISIBLE_DEVICES=0,1,2,3 \
accelerate launch \
  --multi_gpu \
  --num_processes 4 \
  train.py

Hugging Face文档中,--num_processes表示启动的并行进程总数,--gpu_ids用于指定参与训练的GPU。Accelerate命令行文档

还可以先运行:

bash 复制代码
accelerate test

确认Accelerate能够正常启动分布式进程。

四、不要把4张3090理解成一块96GB显存显卡

RTX 3090单卡配备24GB GDDR6X显存。NVIDIA官方规格

四张卡的物理显存总量虽然是96GB,但普通DDP不会自动把这些显存合并成一块连续空间。

DistributedDataParallel通常会在每个GPU进程中保存完整的模型副本,然后同步梯度。PyTorch官方文档也说明,DDP的每个进程拥有自己的模型副本。PyTorch DDP教程

因此:

text 复制代码
单卡24GB显存 × 4张GPU
≠ 单个训练进程获得96GB显存

DDP主要用于提高数据并行吞吐,不是为了解决单卡无法装下模型的问题。

如果单卡训练时已经接近24GB上限,切换DDP后增加的CUDA上下文、梯度同步缓冲区和通信资源可能让某个进程越过显存边界。

五、检查每张卡的真实显存占用

启动训练前先执行:

bash 复制代码
nvidia-smi

重点检查:

  • 是否存在遗留Python进程;
  • GPU 0是否被Jupyter或其他服务占用;
  • 四张卡的空闲显存是否接近;
  • 是否有多个训练进程集中在同一张卡。

可以持续观察:

bash 复制代码
watch -n 1 nvidia-smi

训练代码中还可以记录峰值显存:

python 复制代码
import torch

torch.cuda.reset_peak_memory_stats()

# 执行若干训练步骤

allocated = torch.cuda.max_memory_allocated() / 1024**3
reserved = torch.cuda.max_memory_reserved() / 1024**3

print(f"peak allocated: {allocated:.2f} GB")
print(f"peak reserved: {reserved:.2f} GB")

allocated表示张量实际占用,reserved还包含PyTorch缓存分配器保留的显存。两者都应记录。

如果报错前GPU 0的显存明显高于其他卡,优先排查进程绑定;如果四张卡都接近上限,则更可能是单卡显存余量不足。

六、重新计算多卡训练的全局Batch Size

多卡训练中的全局Batch Size通常可以按下面的方式理解:

text 复制代码
global_batch_size =
per_device_train_batch_size
× GPU进程数
× gradient_accumulation_steps

例如:

text 复制代码
1 × 4 × 1 = 4

per_device_train_batch_size是每张GPU的Batch Size,不是所有GPU合计。

如果单卡原来使用:

bash 复制代码
--per_device_train_batch_size 4

切换4卡后仍然保持这个值,全局Batch Size将从4变成16。

这不一定直接增加每张GPU的显存,但会改变有效Batch Size、数据加载量和梯度累积关系。把单卡脚本迁移到多卡时,应重新计算,而不是原样复制参数。

七、单卡显存已经贴线时怎么处理?

可以依次尝试下面的方法。

1. 降低每卡Batch Size

bash 复制代码
--per_device_train_batch_size 1

2. 用梯度累积保持有效Batch Size

bash 复制代码
--gradient_accumulation_steps 4

3. 缩短序列长度

大模型训练中,序列长度会明显影响激活值和注意力显存。

例如先从:

bash 复制代码
--cutoff_len 4096

降低到:

bash 复制代码
--cutoff_len 2048

具体参数名以所使用的训练框架版本为准。

4. 开启梯度检查点

如果训练框架支持,可以启用:

bash 复制代码
--gradient_checkpointing

它通常以增加部分计算时间为代价,减少需要长期保留的激活值。

5. 暂时关闭评估

原始问题同时启用了:

bash 复制代码
--do_train
--do_eval

排错时可以先关闭评估,只运行少量训练步骤,从而判断错误发生在训练阶段,还是评估、保存或阶段切换时。

这只是定位问题的方法,不代表正式训练应该永久关闭评估。

八、模型单卡放不下时,不要继续使用普通DDP硬顶

如果模型本身无法稳定放入单张24GB显存,继续增加普通DDP进程并不能解决根本问题。

此时应评估:

  • PyTorch FSDP;
  • DeepSpeed ZeRO;
  • Tensor Parallel;
  • Pipeline Parallel;
  • CPU或NVMe Offload。

PyTorch FSDP的FULL_SHARD策略会对模型参数、梯度和优化器状态进行分片,从而降低每个进程长期保存的模型状态。PyTorch FSDP文档

Accelerate支持相关启动参数:

bash 复制代码
--use_fsdp
--use_deepspeed

但不能只在原命令后面增加一个开关。还需要生成并核对相应的FSDP或DeepSpeed配置,特别是分片策略、混合精度、CPU Offload和Checkpoint格式。

判断逻辑可以简化为:

text 复制代码
模型单卡能放下,只是训练慢
→ 使用DDP

模型单卡放不下
→ 使用FSDP、ZeRO或模型并行

多个任务互相独立
→ 每张GPU运行不同任务

九、显存问题解决后,再检查NCCL和GPU拓扑

如果训练已经不再OOM,但增加GPU后速度提升很小,再检查GPU通信。

执行:

bash 复制代码
nvidia-smi topo -m
nvidia-smi topo -p2p p
nvidia-smi topo -p2p n

这些命令用于查看:

  • GPU之间经过的PCIe路径;
  • GPU与CPU的NUMA关系;
  • PCIe P2P状态;
  • NVLink P2P状态。

NVIDIA文档指出,GPU之间能否正常使用P2P通信,会受到PCIe拓扑、驱动、虚拟化方式和系统配置影响。NCCL排错文档

因此,多卡训练的正确排查顺序是:

text 复制代码
实例是否识别全部GPU
→ 进程是否分别绑定GPU
→ 每卡显存是否足够
→ 全局Batch Size是否正确
→ 并行策略是否符合目标
→ NCCL和GPU拓扑是否正常
→ 1卡、2卡、4卡吞吐是否合理

不要一看到CUBLAS_STATUS_NOT_INITIALIZED,就直接重装CUDA。

十、什么时候应该从算家云自助实例转向裸金属?

如果只是验证代码、显存和训练参数,可以先使用算家云自助实例完成小规模测试。

算家云公开文档显示,其容器实例支持按量、按天、按周和按月等计费方式。按量实例开机开始计算实例费用,关机结束实例算力计费;数据盘、项目网盘和镜像有各自的计费与释放规则。算家云计费说明

当训练任务出现下面这些特征时,可以进一步评估算家计算裸金属:

  • 长期固定使用4卡或8卡;
  • 要求多张GPU位于同一物理节点;
  • 需要固定CUDA、PyTorch和NCCL环境;
  • 数据量较大,不希望反复迁移;
  • 需要独立网络或固定IP;
  • 希望提前验证GPU拓扑。

裸金属页面当前采用专属资源、独立网络和实时报价方式。具体GPU型号、数量和多卡拓扑需要按实际资源确认。算家计算裸金属

从自助实例迁移前,建议保存:

bash 复制代码
python -m torch.utils.collect_env
accelerate env
pip freeze > requirements-lock.txt
nvidia-smi topo -m

同时记录模型目录、数据目录和Checkpoint位置,避免更换环境后重新排查同一批问题。

总结

单卡RTX 3090正常、切换4卡后报错时,优先检查:

  1. 实例是否识别到全部GPU;
  2. 四个进程是否分别绑定到四张卡;
  3. 单卡训练是否已经用满24GB显存;
  4. 全局Batch Size是否符合预期;
  5. 当前使用的是DDP,还是能够分片模型状态的FSDP或ZeRO;
  6. NCCL和GPU拓扑是否正常。

DDP提高的是数据并行能力,不会自动把多张RTX 3090变成一块大显存显卡。

先在算家云完成单卡到多卡的环境和参数验证;当任务需要固定4卡、8卡同机并持续运行时,再根据真实训练结果评估裸金属配置。

常见问题

为什么报的是CUBLAS错误,而不是CUDA OOM?

cuBLAS初始化也需要显存和CUDA资源。如果此前的显存分配、进程绑定或CUDA状态已经异常,可能在创建cuBLAS句柄时才暴露错误。应结合前面的日志和nvidia-smi判断,不能只根据最后一行重装cuBLAS。

4张RTX 3090能解决单卡OOM吗?

不一定。DDP通常会复制模型。模型单卡放不下时,需要使用FSDP、ZeRO或其他模型分片方案。

多卡训练时,每卡Batch Size应该除以GPU数量吗?

取决于目标全局Batch Size。如果希望全局Batch Size保持不变,就需要同时调整每卡Batch Size或梯度累积步数。

怎么判断是显存问题还是通信问题?

训练启动或第一轮前向、反向就失败,优先排查进程绑定和显存;训练能运行但多卡加速效果差,再检查NCCL、P2P和GPU拓扑。

自助实例测试通过后,能迁移到裸金属吗?

可以,但应提前保存依赖版本、环境检查结果、数据目录和Checkpoint,并在新环境重新进行1卡、2卡、4卡基线测试。

相关推荐
2601_956319881 小时前
看到“2026年量化学习”时,交易想法要先拆成条件和动作
人工智能·python
武哥聊编程1 小时前
【AI实战项目】AI Agent数据分析平台,基于SpringAI+Springboot+Vue+Agent的电商数据分析平台
人工智能·spring boot·数据分析·springai
衡石科技1 小时前
衡石 Data Agent 选型指南:企业级 AI 分析智能体怎么选?
人工智能
GeWeAPI技术支持1 小时前
私域自动回复机器人怎么配:规则、时机、转人工实战
人工智能
江畔柳前堤2 小时前
云原生 × AI 全景图谱:从 Kubernetes 到 Agent 基础设施的一次认知跃迁
人工智能·分布式·gpt·云原生·kubernetes·原型模式·agi
大模型码小白2 小时前
Harness Engineering 驾驭工程:从使用到项目实战
大数据·数据库·人工智能·python·spring
学习日记5252 小时前
第 5 章:自定义 Skill——把重复操作封装成一键命令
人工智能·ai·prompt
云上工程笔记2 小时前
AstraFlow + React/Vite:如何用自然语言开发并部署一个 AI 咖啡网站原型
前端·人工智能·react.js
醍醐实验室2 小时前
长文本外推中的 RoPE Base 调优:从 10,000 到 500,000 的数学底数奥秘
人工智能