很多人遇到 CUDA out of memory 时,第一反应是:换一张显存更大的 GPU。
这个办法有时有效,但它并不是排查的第一步。显存不足可能来自 batch size、上下文长度、优化器状态、数据类型、显存碎片,甚至是某个没有释放的临时张量。没有先确认原因,直接换卡,成本可能增加了,问题却还在。
本文给出一套适合个人开发者的排查顺序:先确认是哪一种内存不足,再定位峰值显存,最后按任务类型选择优化方案。
一、先确认:到底是哪一种“内存不够”?
1. GPU 显存不足
典型报错包括:
CUDA out of memory Tried to allocate ... GPU ... has a total capacity of ...先执行:
watch-n1nvidia-smi观察任务开始前、模型加载后和第一个 batch 运行时的显存变化。
2. CPU 内存不足
如果系统出现 OOM killer、进程被直接终止,或者日志里出现 killed,不一定是 GPU 显存问题。数据集缓存、DataLoader worker、解压和预处理都可能消耗大量 CPU 内存。
3. 磁盘空间不足
模型下载、缓存、checkpoint 和日志会占用磁盘。磁盘满时,常见现象是模型下载失败、保存 checkpoint 失败,或者任务运行一段时间后突然报错。
df-hdu-sh~/.cache2>/dev/null这三类问题的解决方向完全不同。先确认类型,比盲目更换 GPU 更重要。
二、用 PyTorch 找到显存峰值
只看任务结束时的显存占用不够,因为峰值可能发生在模型加载、第一次 forward、反向传播或保存临时张量时。
可以在一个最小可复现任务里加入:
importtorch device="cuda"iftorch.cuda.is_available()else"cpu"print("device:",device)ifdevice=="cuda":torch.cuda.reset_peak_memory_stats()print(torch.cuda.memory_summary(device=0,abbreviated=True))# 在这里执行一次目标模型的加载和推理/训练peak_allocated=torch.cuda.max_memory_allocated()/1024**3peak_reserved=torch.cuda.max_memory_reserved()/1024**3print(f"peak allocated:{peak_allocated:.2f}GB")print(f"peak reserved:{peak_reserved:.2f}GB")allocated 更接近当前张量实际使用的显存,reserved 是 PyTorch 缓存分配器保留的显存。两者差距较大时,可能存在缓存或碎片问题,但不能仅凭这个现象下结论。
另外,torch.cuda.empty_cache() 只能释放缓存分配器中暂时没有被使用的块,不能释放仍被 Python 变量引用的张量。把它当成“清空所有显存”是不准确的。
三、训练任务为什么特别容易 OOM?
训练时,显存通常不只存放模型权重,还需要保存:
- 激活值;
- 梯度;
- 优化器状态;
- 临时张量;
- 当前 batch 和数据搬运产生的缓冲。
因此“模型能加载”不代表“训练能开始”。
优先尝试的顺序
1. 降低 batch size
这是最直接的办法。可以先把 batch size 调到 1,确认流程能否跑通,再逐步增加。
2. 使用梯度累积
如果希望保持较大的有效 batch,可以把多个小 batch 的梯度累积后再更新参数:
loss=model(**batch).loss/accumulation_steps loss.backward()if(step+1)%accumulation_steps==0:optimizer.step()optimizer.zero_grad()梯度累积降低了单次显存峰值,但会增加完成一个有效 batch 所需的时间,吞吐不一定更高。
3. 使用合适的精度
在硬件、框架和模型支持的前提下,可以评估 FP16 或 BF16。不要只根据“显存会下降”做判断,还要观察精度稳定性和实际速度。
4. 开启梯度检查点
梯度检查点通过减少保存的激活值来降低显存,但会增加部分计算。它更适合显存紧张、计算资源相对充足的训练任务。
5. 量化或参数高效微调
LoRA、QLoRA 等方案可以减少需要训练的参数量;量化可以降低权重占用。但要确认目标模型、算子和训练框架的兼容性,不能把量化当成所有 OOM 的通用修复。
四、推理任务的显存,常常卡在 KV Cache
推理时,模型权重只是显存的一部分。上下文越长、并发越高,KV Cache 通常越大。
如果模型可以加载,但一提高并发就 OOM,优先检查:
- 上下文长度是否过大;
- 并发请求数是否超过设计范围;
- KV Cache 是否使用了合适的数据类型;
- 是否同时加载了多个模型;
- 推理框架是否预留了过多显存。
可以先固定模型版本、精度和上下文长度,只改变并发数,观察显存曲线。这样比直接更换 GPU 更容易定位问题。
五、显存还有余量,但 GPU 跑不满怎么办?
显存不足和 GPU 利用率低是两类不同问题。
GPU 利用率低,常见原因包括:
- CPU 预处理太慢;
- DataLoader worker 数量不合适;
- 磁盘或网络读取成为瓶颈;
- batch 太小;
- 输入数据没有及时搬到 GPU;
- 代码中存在频繁同步、打印或保存;
- 多卡任务的通信开销过大。
排查时同时观察:
nvidia-smi dmon iostat-xz1free-h如果 GPU 使用率低、CPU 占用高,应该先查数据处理;如果磁盘 I/O 很高,应该先查数据位置和缓存;如果单卡很忙但多卡效率很低,应该查通信和 batch 切分。
六、什么时候才应该换更大显存的 GPU?
满足下面任意情况时,升级显存通常更合理:
- 在 batch size 已经降到可接受范围后,任务仍然无法运行;
- 已经使用合适的精度、检查点或量化,峰值显存仍超过上限;
- 长上下文或高并发是业务硬需求,无法继续降低;
- 多模型同时驻留是部署要求;
- 当前配置虽然能运行,但完成一次任务的时间和重试成本过高。
比较 GPU 时,不要只记录“能不能启动”,还要记录:
- 峰值显存;
- 单步耗时或 tokens/s;
- 完成一次任务的总时间;
- 失败重试次数;
- 单次任务的完整成本。
七、给个人开发者的最小排查清单
遇到 OOM 时,可以按这个顺序走:
- 用 nvidia-smi 确认 GPU 型号、显存和当前占用;
- 区分 GPU 显存、CPU 内存和磁盘空间问题;
- 记录模型加载和第一个 batch 的显存峰值;
- 先把 batch size 降到 1,确认任务是否能跑通;
- 再评估梯度累积、混合精度、检查点或量化;
- 推理任务单独检查上下文长度和 KV Cache;
- 仍不满足需求时,再比较更大显存的 GPU;
- 任务完成后保存结果并释放实例,避免无效计费。
结语
显存不足不是一句“换更大 GPU”就结束了。对个人开发者来说,更可靠的做法是先把问题拆开:是模型权重太大、训练状态太多、上下文太长、数据搬运太慢,还是环境里有张量没有释放。
只有知道显存花在哪里,才能判断应该优化代码、调整参数,还是更换 GPU。这样做不仅能省成本,也能让后续的配置选择更有依据。
如果你准备验证不同 GPU 配置,可以先访问起源算力官网查看资源与使用入口:
- 官网:https://origpu.com
- 控制台:https://origpu.com/app