4、常见显存溢出场景分析:大模型训练、批量大小过大、序列过长、多任务并行等场景的溢出原因

显存溢出(CUDA Out of Memory,OOM)并不是一个孤立故障,它往往是模型规模、批量大小、序列长度、并行策略与运行时缓存共同作用的结果。定位时如果只盯着“显存不够”这一句话,很容易陷入盲目调小 batch 的循环。更有效的做法,是把显存占用拆成几个可解释的部分,再判断当前场景究竟压垮了哪一块。

显存占用 ≈ 模型参数 + 梯度 + 优化器状态 + 激活值 + 临时缓冲区 + 通信缓冲 + 碎片预留

下面这张图展示了训练过程中显存的主要构成,以及不同溢出场景最容易冲击的环节。可以看到,参数、梯度、优化器状态属于“静态占用”,而激活值、临时缓冲和碎片则随 batch、序列长度和并行方式动态变化。

训练显存构成与常见溢出冲击点 静态占用(与 batch / 序列长度基本无关) 模型参数 梯度 优化器状态(Adam 约 2×参数) 通信缓冲 动态占用(随 batch、序列长度、并行策略变化) 激活值(与 batch×seq 近似线性) 注意力矩阵(seq² 增长) 临时缓冲区 / 算子 workspace 显存碎片与预留(allocator 已缓存但无法复用) 大模型训练 参数+梯度+优化器 静态占用先爆 批量过大 激活值线性放大 峰值随 batch 上升 序列过长 注意力 seq² 增长 长文本最易 OOM 多任务并行 多进程/多卡争抢 碎片与预留叠加 典型报错信号 CUDA out of memory. Tried to allocate 2.00 GiB (GPU 0; 24.00 GiB total capacity; 21.30 GiB already allocated; 1.20 GiB free; 22.50 GiB reserved in total by PyTorch)

一、大模型训练:静态占用先触顶

当模型参数量达到数十亿甚至更大时,参数、梯度和优化器状态会形成巨大的静态占用。以 Adam 为例,每个参数通常需要保存一阶矩和二阶矩,混合精度下还要保留 FP32 主权重,因此优化器状态可能达到参数量的 2 倍以上。此时即使 batch 很小,也可能在第一次反向传播前就 OOM。

这类场景的典型特征是:报错发生在模型加载或优化器初始化阶段,或者第一次 forward 后立刻失败。解决方向不是继续调小 batch,而是引入 ZeRO 分片、FSDP 或张量并行,把静态状态分散到多卡上。

# 典型现象:模型加载后尚未开始训练就 OOM
torch.cuda.OutOfMemoryError: CUDA out of memory.
Tried to allocate 1.50 GiB (GPU 0; 24.00 GiB total capacity;
20.80 GiB already allocated; 0.90 GiB free; 21.50 GiB reserved)

二、批量大小过大:激活值线性放大

批量大小直接决定前向激活值的规模。对于 Transformer 类模型,激活值大致与 batch × seq × hidden × layers 成正比。batch 翻倍,激活值也近似翻倍,而反向传播还需要保存中间结果,峰值显存会进一步上升。

判断方法很简单:固定序列长度,逐步增大 batch,观察显存峰值变化。如果显存随 batch 近似线性增长,说明瓶颈在激活值。此时可以使用梯度累积模拟大 batch,或启用激活重计算(gradient checkpointing)用计算换显存。

梯度累积可以在不增加显存的前提下获得更大的有效 batch,但要注意 BatchNorm 等对 batch 敏感的操作,以及学习率是否需要同步缩放。

三、序列过长:注意力矩阵的平方级压力

序列长度是比 batch 更隐蔽的显存杀手。标准自注意力的注意力矩阵大小为 seq × seq,显存占用随序列长度呈平方增长。当序列从 512 增加到 4096 时,注意力矩阵规模扩大 64 倍,极易触发 OOM。

长序列场景常见于文档理解、代码生成、长对话和多模态输入。除了注意力矩阵,位置编码、KV Cache 和中间激活也会同步膨胀。解决手段包括 FlashAttention、滑动窗口注意力、序列并行和 KV Cache 量化。

场景 主要显存压力来源 增长关系 优先优化手段
大模型训练 参数、梯度、优化器状态 与参数量近似线性 ZeRO / FSDP / 张量并行
批量过大 前向激活值、中间结果 与 batch 近似线性 梯度累积、激活重计算
序列过长 注意力矩阵、KV Cache 与 seq² 相关 FlashAttention、序列并行
多任务并行 多进程显存争抢、碎片 与并发数叠加 限制并发、统一分配策略

四、多任务并行:争抢与碎片叠加

在同一张卡上同时运行多个训练任务、推理服务或数据加载进程时,显存会被多个进程瓜分。更麻烦的是,PyTorch 的缓存分配器会为每个进程保留已申请但未释放的显存,导致“空闲但不可用”的碎片。此时 nvidia-smi 显示显存接近占满,但实际可用块却很少。

典型表现是:单任务运行正常,多任务同时启动后随机 OOM;或者训练过程中偶发 OOM,重试后又恢复正常。这类问题需要从进程隔离、显存上限设置和分配器配置入手。

# 限制单进程显存增长,避免一次性占满
export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128

# 查看当前显存占用与碎片情况
nvidia-smi --query-gpu=memory.total,memory.used,memory.free --format=csv
不要把所有 OOM 都归因于 batch 太大。若报错发生在模型初始化、优化器构建或第一次反向传播之前,优先检查静态占用和并行策略,而不是盲目调小 batch。

五、复盘:建立场景化排查顺序

面对 OOM,建议按以下顺序排查:第一,确认报错发生的时间点,是加载、前向、反向还是更新阶段;第二,估算静态占用,判断参数、梯度、优化器状态是否已经接近上限;第三,检查 batch 和序列长度,判断激活值与注意力矩阵是否超预期;第四,观察是否存在多进程争抢和碎片;第五,再选择 ZeRO、激活重计算、FlashAttention 或并发限制等对应手段。

只有把“溢出场景”与“显存构成”对应起来,才能避免无效调参,让每一次优化都落在真正的瓶颈上。