坐褐 发表于 2026-1-24 21:35:02

微调显存总爆炸?问题往往不在你以为的地方

<h2 id="显存不够几乎是每个微调项目的入场仪式">显存不够,几乎是每个微调项目的“入场仪式”</h2>
<p>如果你做过大模型微调,那“显存不够”这四个字,你几乎不可能陌生。</p>
<p>第一次跑,直接 OOM。<br>
换个 batch size,再 OOM。<br>
开 bf16,还是不够。<br>
关掉一些东西,终于能跑了,但速度慢得离谱。</p>
<p>很多人会在这个阶段得出一个结论:<br>
“是我显卡不行。”</p>
<p>但当你真的开始拆解显存使用之后,你会发现一个非常反直觉的事实:</p>
<p>大多数显存,并不是被模型参数吃掉的。</p>
<p>而你之所以总感觉显存不够,往往是因为你根本不知道显存是怎么被花掉的。</p>
<h2 id="一个必须先说清楚的事实显存不是模型大小--2">一个必须先说清楚的事实:显存不是“模型大小 × 2”</h2>
<p>这是新手最常见、也最危险的一个误解。</p>
<p>很多人心里都有一笔非常粗糙的账:<br>
模型参数多少 GB,我有多少显存,差不多就能跑。</p>
<p>但在真实训练中,这个估算几乎一定是错的。</p>
<p>因为模型参数,只是显存账单里最小的一项。</p>
<p><br>
显存构成的“账单拆解图”</p>
<h2 id="显存第一大户激活activation而且它非常隐蔽">显存第一大户:激活(Activation),而且它非常“隐蔽”</h2>
<p>很多人第一次被问到“显存主要花在哪”,会下意识回答:<br>
模型参数。</p>
<p>但在训练阶段,真正吃显存的,往往是 activation。</p>
<p>activation 是什么?<br>
简单说,就是模型前向计算过程中,每一层产生的中间结果,用来在反向传播时算梯度。</p>
<p>关键在于两点:</p>
<p>第一,activation 和 batch size 强相关<br>
batch size 一大,activation 几乎线性增长。</p>
<p>第二,activation 和模型深度强相关<br>
层数越多,存的中间结果就越多。</p>
<p>所以你会看到一个非常典型的现象:<br>
模型参数看起来不大,但一开训练就 OOM。</p>
<p>不是模型“太大”,而是 activation 在默默吃显存。</p>
<p><br>
batch size 增加导致 activation 激增示意图</p>
<h2 id="第二大头优化器状态尤其是-adam">第二大头:优化器状态,尤其是 Adam</h2>
<p>如果你用的是 Adam 或 AdamW,那你几乎一定低估了它的显存消耗。</p>
<p>Adam 至少要为每一个可训练参数,维护两份额外状态:</p>
<ul>
<li>一份一阶动量</li>
<li>一份二阶动量</li>
</ul>
<p>也就是说:<br>
参数 × 3,才是 Adam 的真实显存账单。</p>
<p>在全参数微调里,这个成本是灾难性的;<br>
在 LoRA 微调里,它看起来“还好”,但依然不可忽视。</p>
<h2 id="第三个被忽略的消耗梯度本身">第三个被忽略的消耗:梯度本身</h2>
<p>很多人以为梯度“算完就没了”,但实际上,在反向传播过程中,梯度也要完整存储。</p>
<p>尤其是在没有梯度累积、没有清理缓存的情况下,梯度会和 activation 一起,占据一大块显存。</p>
<p>这也是为什么你会看到:<br>
前向还好,<br>
一到 backward 就直接炸显存。</p>
<h2 id="显存杀手中的隐形-bosspytorch-缓存与碎片化">显存杀手中的“隐形 Boss”:PyTorch 缓存与碎片化</h2>
<p>这是很多人查了一天 nvidia-smi 都想不明白的问题。</p>
<p>你明明看到:<br>
显存用了 20GB,卡有 24GB,<br>
但就是分配不了一个 1GB 的 tensor。</p>
<p>原因很简单:<br>
显存碎片化。</p>
<p>PyTorch 会缓存显存以加速后续分配,但这也意味着,显存并不是一整块连续空间。</p>
<p>你“看得到”的空闲,不等于“用得上”。</p>
<h2 id="为什么你已经开了-bf16显存还是不够">为什么你“已经开了 bf16”,显存还是不够</h2>
<p>很多人会觉得:<br>
“我已经用 bf16 / fp16 了,应该很省显存了。”</p>
<p>但半精度,只解决了一件事:<br>
参数和部分激活的存储大小。</p>
<p>它并没有解决:</p>
<ul>
<li>activation 数量本身</li>
<li>优化器状态数量</li>
<li>缓存和碎片化</li>
</ul>
<p>所以 bf16 是“必要条件”,但绝不是“充分条件”。</p>
<h2 id="gradient-checkpointing显存的以时间换空间">gradient checkpointing:显存的“以时间换空间”</h2>
<p>这是最常见、也最有效的一种显存优化方式。</p>
<p>gradient checkpointing 的核心思想非常朴素:<br>
我不保存所有中间激活,需要时再算一遍。</p>
<p>这会明显降低 activation 的显存占用,但代价是:<br>
前向计算要重复做,训练时间会变长。</p>
<p>下面是一段非常典型的开启方式(示意):</p>
model.gradient_checkpointing_enable()
<p>这一行代码,往往能直接救活一个“差一点就 OOM”的训练。</p>
<p><br>
checkpointing 前后显存 vs 时间对比图</p>
<h2 id="梯度累积你以为在调-batch其实在拆账单">梯度累积:你以为在调 batch,其实在拆账单</h2>
<p>当 batch size 太大显存扛不住时,梯度累积是最常见的替代方案。</p>
<p>它的本质是:<br>
把一个大 batch,拆成多个小 batch,梯度累加后再更新。</p>
loss = loss / grad_accum_steps
loss.backward()

if step % grad_accum_steps == 0:
    optimizer.step()
    optimizer.zero_grad()
<p>这样做的好处是:<br>
activation 显存按“小 batch”算,<br>
但优化效果近似“大 batch”。</p>
<p>坏处是:</p>
<ul>
<li>训练逻辑更复杂</li>
<li>调试更容易出错</li>
</ul>
<p><br>
真实 batch vs 梯度累积 batch 示意图</p>
<h2 id="offload显存省了但系统开始喘气">Offload:显存省了,但系统开始“喘气”</h2>
<p>当你开始把 optimizer state 或部分参数 offload 到 CPU,你确实能省下一大截显存。</p>
<p>但你也必须意识到:<br>
你是在用 PCIe 带宽换显存。</p>
<p>一旦 offload 过多,训练速度可能直接腰斩,甚至不稳定。</p>
<p>这类优化,非常不适合新手“无脑打开”。</p>
<h2 id="一个容易被忽略的问题你可能根本不需要这么大">一个容易被忽略的问题:你可能根本不需要“这么大”</h2>
<p>这是一个很多人不愿意面对的问题。</p>
<p>你显存不够,真的是因为模型必须这么大吗?<br>
还是因为你默认选了一个“看起来更强”的模型?</p>
<p>在微调阶段,模型大小的边际收益往往非常低。</p>
<p>有时候,换一个小一点的基座模型,反而比死磕显存优化更理性。</p>
<h2 id="一个现实建议别一开始就把显存榨干">一个现实建议:别一开始就把显存榨干</h2>
<p>这是我见过最多人踩的坑。</p>
<p>刚好能跑 ≠ 稳定能跑<br>
刚好不 OOM ≠ 可以反复试错</p>
<p>你永远需要给显存留余地,用来:</p>
<ul>
<li>调试</li>
<li>评估</li>
<li>临时开 profiler</li>
<li>打印中间结果</li>
</ul>
<h2 id="显存问题往往是系统设计问题不是参数问题">显存问题,往往是“系统设计问题”,不是参数问题</h2>
<p>当你已经打开 bf16、checkpointing、梯度累积,还是跑不动时,通常意味着一件事:</p>
<p>你该停下来重新审视整体方案了。</p>
<p>继续抠显存,只会让系统越来越脆。</p>
<h2 id="一个健康的显存优化顺序经验总结">一个健康的显存优化顺序(经验总结)</h2>
<p>不是“能开什么开什么”,而是:</p>
<ul>
<li>bf16 / fp16</li>
<li>减 batch size</li>
<li>梯度累积</li>
<li>gradient checkpointing</li>
<li>评估是否需要 offload</li>
<li>重新审视模型规模</li>
</ul>
<h2 id="在显存受限阶段更重要的是验证方向">在显存受限阶段,更重要的是“验证方向”</h2>
<p>这点和前面几篇其实是一脉相承的。</p>
<p>当你显存很紧张时,你真正该做的,不是把训练堆到极限,而是尽快验证:</p>
<p>这个方向值不值得继续投入。</p>
<p>在显存和算力都受限的阶段,先用 LLaMA-Factory online 快速跑通微调流程、验证数据和目标是否有效,再决定是否投入重资源,会比一开始就死磕本地显存更理性。</p>
<h2 id="总结显存不够往往是你算错账而不是你资源太少">总结:显存不够,往往是你“算错账”,而不是你“资源太少”</h2>
<p>写到最后,其实可以把这篇文章压缩成一句话:</p>
<p>显存问题,本质上是一个系统认知问题。</p>
<p>当你真正搞清楚显存是怎么被吃掉的,你会发现:<br>
很多 OOM,并不是不可避免的;很多显存优化,也不是必须的。<br>
真正成熟的工程师,不是“把显存榨到 0”,而是知道哪些钱该省,哪些钱不该省。</p><br>来源:程序园用户自行投稿发布,如果侵权,请联系站长删除<br>免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

粒浊 发表于 2026-1-25 17:48:36

懂技术并乐意极积无私分享的人越来越少。珍惜

姊囝 发表于 2026-1-26 06:37:15

感谢发布原创作品,程序园因你更精彩

洪思思 发表于 2026-1-26 08:32:07

谢谢分享,试用一下

懵径 发表于 2026-1-27 05:39:06

收藏一下   不知道什么时候能用到

南宫玉英 发表于 2026-1-28 12:41:18

过来提前占个楼

闻人莹华 发表于 2026-1-30 05:08:33

用心讨论,共获提升!

怀陶宁 发表于 2026-2-1 22:23:30

不错,里面软件多更新就更好了

电棘缣 发表于 2026-2-2 04:37:29

收藏一下   不知道什么时候能用到

焦尔蕾 发表于 2026-2-3 04:25:53

鼓励转贴优秀软件安全工具和文档!

韦逸思 发表于 2026-2-4 09:00:42

过来提前占个楼

拼匍弦 发表于 2026-2-5 23:40:51

鼓励转贴优秀软件安全工具和文档!

勺缓曜 发表于 2026-2-7 21:04:13

用心讨论,共获提升!

吮槌圯 发表于 2026-2-8 04:15:09

这个好,看起来很实用

甄婉丽 发表于 2026-2-8 08:05:40

这个有用。

劳暄美 发表于 2026-2-8 08:54:59

喜欢鼓捣这些软件,现在用得少,谢谢分享!

林鱼 发表于 2026-2-8 09:53:51

过来提前占个楼

懵崭 发表于 2026-2-8 11:09:39

鼓励转贴优秀软件安全工具和文档!

谅潭好 发表于 2026-2-8 20:07:50

yyds。多谢分享

哈妙思 发表于 2026-2-9 05:03:46

懂技术并乐意极积无私分享的人越来越少。珍惜
页: [1] 2
查看完整版本: 微调显存总爆炸?问题往往不在你以为的地方