4、模型显存占用分析:参数显存、梯度显存、优化器状态显存、激活值显存

显存到底被谁吃了?这是我在做模型训练时最常被问到的问题。

很多人一看到OOM(显存溢出)就慌了,要么盲目调小batch size,要么直接换卡。其实,搞清楚显存里都装了啥,你就能精准定位瓶颈。我个人习惯把模型训练时的显存占用分成四大块:参数显存、梯度显存、优化器状态显存、激活值显存。

咱们一个一个拆开看。

4.1 参数显存:模型本身的“体重”

参数显存,说白了就是模型权重占的地方。你加载一个模型,这部分就固定占上了。

计算公式很简单:

参数显存 = 参数量 × 每个参数占用的字节数

常用的精度和字节数对应关系:

精度类型 字节数 常见场景
FP32(单精度) 4字节 默认训练精度
FP16(半精度) 2字节 混合精度训练
BF16 2字节 部分新卡支持
INT8 1字节 推理量化

举个例子,一个7B参数的模型,用FP32加载:

7 × 10^9 × 4字节 = 28GB

嗯,这里要注意。如果你用FP16加载,那就是14GB。但很多框架会默认保留一份FP32的副本做精度累积,实际占用会翻倍。我在项目中遇到过这种情况:明明模型只有7B,用FP16算下来应该14GB,结果一跑占了28GB。查了半天,原来是框架偷偷保留了FP32的主权重。

避坑指南: 我曾经在加载大模型时忽略了框架的默认精度策略。建议你训练前先打印一下模型参数的数据类型,确认实际占用。

4.2 梯度显存:反向传播的“临时账本”

梯度显存,存的是每个参数对应的梯度值。它的大小和参数显存一模一样。

为什么?因为每个参数都有一个梯度。你用FP32训练,梯度就是FP32,占4字节。你用混合精度,梯度通常也是FP32(为了数值稳定)。

所以:

梯度显存 ≈ 参数显存

你想想看,一个7B模型,参数显存28GB,梯度又占28GB。光这两项就56GB了。很多人的显存就是这么被吃掉的。

我个人习惯在显存紧张时,检查一下梯度是否真的需要全精度。有些框架支持梯度压缩,但会牺牲一点收敛速度。要不要用,得看你的场景。

4.3 优化器状态显存:Adam的“小算盘”

这一块最容易被忽略。优化器状态,就是优化器自己维护的额外变量。

以最常用的Adam优化器为例:

  • 它需要保存每个参数的一阶动量(m)
  • 还需要保存二阶动量(v)
  • 这两个变量的大小,都和参数一样大

所以Adam的优化器状态显存 = 2 × 参数显存。

算一笔账:

7B模型,FP32训练:
参数显存:28GB
梯度显存:28GB
优化器状态:56GB(Adam)
合计:112GB

看到没?优化器状态才是真正的显存大户。我刚开始做分布式训练时,一直想不通为什么单卡跑不动7B模型。后来一算,112GB,A100 80G也扛不住啊。

小技巧: 如果你用SGD优化器,它只需要保存动量(如果有的话),显存占用会小很多。但SGD收敛慢,你得权衡。

4.4 激活值显存:前向传播的“中间结果”

激活值显存,存的是每一层前向计算时的中间输出。这部分最灵活,也最坑。

它的计算公式:

激活值显存 = 每层输出大小 × batch size × 层数 × 字节数

举个例子,一个Transformer模型,每层输出维度是4096,序列长度2048,batch size为4,共32层,FP16:

4096 × 2048 × 4 × 32 × 2字节 ≈ 2GB

看起来不大?但实际训练中,激活值显存会随着batch size线性增长。你把batch size从4调到8,激活值显存直接翻倍。

为什么会这样?因为反向传播时需要用到前向的中间结果来计算梯度。如果你不保存这些结果,就得重新算一遍,那就成“重计算”了。

核心要点: 激活值显存是唯一一个可以通过“重计算”来节省的。用时间换空间,训练速度会慢一些,但显存占用能降30%-50%。

4.5 一张图看懂显存分布

下面这张SVG图,是我根据实际项目经验画的。它展示了训练一个典型大模型时,四类显存的占比关系。

大模型训练显存占用分布(7B模型,FP32,Adam) 参数显存 28GB (25%) 梯度显存 28GB (25%) 优化器状态 56GB (50%) 激活值显存 动态变化 关键结论 1. 优化器状态是最大头(50%) 2. 参数+梯度占50%,两者大小相等 3. 激活值随batch size变化 4. 混合精度可大幅降低参数显存 优化方向: • 使用混合精度训练 • 开启激活值重计算 • 考虑ZeRO优化器分片 • 适当减小batch size 注:激活值显存未计入总占比

4.6 实战:如何快速估算显存

我在做模型选型时,会用一个简单的公式快速估算:

总显存 ≈ 参数显存 × (1 + 1 + 优化器系数) + 激活值显存

其中优化器系数:

  • Adam:2(一阶+二阶动量)
  • SGD + Momentum:1
  • SGD无动量:0

举个例子,你要训练一个13B模型,用FP16 + Adam,batch size为8:

参数显存:13B × 2字节 = 26GB
梯度显存:26GB
优化器状态:26GB × 2 = 52GB
激活值显存:估算约8GB(取决于模型结构)
合计:26 + 26 + 52 + 8 = 112GB

嗯,A100 80G单卡肯定跑不了。这时候你就得考虑分布式训练或者模型并行。

我的经验: 实际训练时,显存占用会比估算值多10%-20%,因为还有框架开销、临时变量等。建议留出20%的余量。

4.7 避坑总结

最后,分享几个我踩过的坑:

  1. 别只看参数显存——优化器状态才是大头,很多人只算参数就以为能跑,结果一跑就OOM。
  2. 激活值显存会暴涨——特别是长序列任务。我试过把序列长度从512调到2048,激活值显存翻了4倍。
  3. 混合精度不是万能的——它只降参数显存,梯度还是FP32,优化器状态也还是FP32。
  4. 重计算要谨慎开启——虽然省显存,但训练时间可能增加30%-50%。

搞清楚这四块显存,你就能像医生一样,精准诊断显存瓶颈在哪里。是优化器状态吃太多?还是batch size太大导致激活值爆炸?对症下药,才能高效训练。

可可精品资料库