单卡训练正常,切换到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卡后报错时,优先检查:
- 实例是否识别到全部GPU;
- 四个进程是否分别绑定到四张卡;
- 单卡训练是否已经用满24GB显存;
- 全局Batch Size是否符合预期;
- 当前使用的是DDP,还是能够分片模型状态的FSDP或ZeRO;
- 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卡基线测试。