16GB 显卡微调实测:内存回退拖慢训练
在 RTX 5070 Ti、LoRA rank 16、序列长度 512、批量 1 的测试中,7B 模型峰值显存约 11.9GB;14B 则触发 Windows 系统内存回退,单步耗时约增加至 51 倍。梯度检查点能降低较小模型的显存占用,但未能让该测试中的 14B 模型适配物理显存。
AI 深度解读
16 GB 显卡微调大模型,最容易踩的坑可能不是显存不足报错,而是训练成功跑完,却已经慢到没法用。作者在 RTX 5070 Ti 上测试 Qwen2.5-Instruct 的 LoRA 微调,发现 14B 可以悄悄借用系统内存,单步耗时达到 7B 的约 51 倍。
这组实验的价值,是把「能运行」和「装得下」分开了:在作者固定的配置下,7B 是实际可用的上限,梯度检查点能省显存,却没能把上限推到 14B。
为什么训练没报错,仍然不能算装得下
作者先登记了两个实验判定条件:14B 模型能否完成一次真实训练步骤且不抛出 CUDA 显存不足异常,以及梯度检查点能否让可训练规模至少提升一个测试档位。
硬件是 RTX 5070 Ti,物理显存为 16,303 MiB。测试使用 Qwen2.5-Instruct,基础模型以 4-bit 加载,LoRA rank 为 16,序列长度为 512 tokens,batch size 为 1。四个模型规模各测试开关梯度检查点两种情况,共八组配置。
LoRA 可以理解为冻结基础模型的大部分参数,只训练额外添加的低秩适配参数。但可训练参数少,不代表整个训练过程只需要存这些参数,基础权重和中间激活仍然占内存。
结果最反直觉的地方出现在 14B:两次运行都没有抛异常,按作者最初写下的条件,它似乎已经过关。
但关闭梯度检查点时,PyTorch 报告的峰值分配量达到 22,189.8 MiB,相当于物理显存的 136%,超出约 5,887 MiB。一次训练步骤耗时 21.7 秒,小模型则都在半秒以内。
作者将其归因于 Windows 驱动层的系统内存回退:显存不够时,继续通过普通 RAM 承接分配,而不是直接报错。控制台没有明确提醒,训练也确实能继续。
没有 OOM,只能证明分配没有失败,不能证明训练全程待在显存里。
这里的 torch.cuda.max_memory_allocated() 没有报错数。它记录的是 CUDA 分配器获准分配的张量内存峰值,但不负责说明这些内存最终驻留在显卡上,还是被驱动透明地换到了系统内存。必须结合物理显存和耗时一起判断。
梯度检查点能省多少,为什么救不了 14B
作者给出的峰值分配量如下,单位均为 MiB:
| 模型规模 | 关闭梯度检查点 | 开启梯度检查点 |
|---|---|---|
| 1.5B | 4,000.7 | 2,855.5 |
| 3B | 6,749.0 | 4,490.8 |
| 7B | 11,897.9 | 9,709.9 |
| 14B | 22,189.8 | 17,470.9 |
7B 在关闭梯度检查点时使用约 73% 的物理显存,还有一定余量。开启后,峰值降到 9,709.9 MiB,两种情况下单步都是 0.425 秒。
3B 则用增加 25% 的时间,换来了约三分之一的内存下降。因此,「几乎免费」在这组数据里更适合描述 7B,不能理解成所有模型都没有时间成本。1.5B 因为在脚本中最先运行,可能包含一次性的 CUDA 初始化开销,作者没有把它的耗时纳入比较。
梯度检查点(gradient checkpointing)的做法,是在前向计算时少保存一些中间激活,到反向传播时重新计算。它用额外计算换内存。
到了 14B,虽然峰值降到了 17,470.9 MiB,仍然达到物理显存的 107%,超出约 1,168 MiB。系统内存回退没有消失,单步反而从 21.7 秒增加到 34.5 秒,慢了 59%。
作者的解释是:内存节省不足以消除回退带来的代价,重新计算又增加了负担。这个解释与结果相符,但原文没有提供进一步的性能剖析,不能据此精确拆分每一部分耗时。
与 7B 的 0.425 秒相比,14B 未开启检查点时约慢 51 倍;原文开头用 0.4 秒和约 50 倍概括这一差距。
所以,预先登记的「检查点能把上限提高一个档位」假设被否定了。它在上限以内有用,却没有帮助这套配置跨过 14B 的门槛。
我的判断
我认可这篇最重要的方法:先把通过条件写清,再允许结果推翻它,甚至修正条件本身。作者发现「不报错」测错了目标,没有继续把 14B 包装成成功案例,这比单独展示一个可运行截图更有参考价值。
但「16 GB 的上限是 7B」必须带上配置限定。
这次只测了 Qwen2.5-Instruct 的四个规模,固定 4-bit、rank 16、512 tokens 和 batch size 1。它证明的是这组测试点里 7B 可用、14B 超限,没有证明所有 16 GB 显卡、所有模型和所有 LoRA 设置都只能到 7B。
作者提到,降低 rank、缩短序列、减少 LoRA 作用的目标模块,或者采用更激进的量化方案,都可能让 14B 进入显存范围。但这些没有实测,不能当成已经验证的解决方案。推理的内存需求也不同,这次实验没有测推理。
相比只看异常或峰值内存,同时记录单步耗时更能暴露实际问题。不过,这里测的是一次前向、反向和优化器更新,并没有给出长时间训练的稳定吞吐、训练质量或收敛结果。
原文称所有数字直接来自 results.json,实验计划、代码和原始输出都放在公开仓库。提供的材料没有包含仓库地址,因此这里能解读其数据,无法进一步核验实现细节。
把 7B 当作这套配置的起点合理,把它当作所有 16 GB 微调任务的硬上限就过头了。
能拿来做什么
- 如果准备在本地微调代码或指令模型,可以先按作者的 7B、4-bit、rank 16、512 tokens、batch size 1 建立基线,同时记录峰值分配量与单步耗时,确认自己的机器是否得到相近结果。
- 在 Windows 上运行训练时,把
torch.cuda.max_memory_allocated()与实际物理显存对照。遇到分配量越界、耗时突然跳高,即使没有 OOM,也应检查是否发生系统内存回退。 - 显存接近上限时,可以先对比开启和关闭梯度检查点的结果。7B 的数据支持尝试,但 3B 已出现 25% 的时间增加,需要按自己的训练预算取舍。
- 如果目标必须是 14B,就逐项测试更短序列、更低 rank 或更少目标模块,并重新记录内存和速度。原文没有验证这些路径,也没有证明训练质量能保持不变。