很多朋友在接触大模型微调时,第一反应就是:我的显卡能不能跑?要多大显存?训练多久?其实在没有真正上机器之前,算力估算是最容易让人心里发虚的一环。特别是LoRA这种轻量级微调方案,网上说法从“6GB显存就能跑”到“一路玩到H100”,数据五花八门,搞得新人一头雾水,老手又懒得逐条解释。
我一开始也是靠一个个试错踩出来的。刚碰LoRA那会儿,以为它冻结了大部分参数,显存开销可以忽略不计,结果把序列长度拉高之后,显存直接爆了,训练进程被系统杀掉。后来认真把参数算明白,才发现问题出在“只看可训练参数、忽视了中间激活值”这个思维陷阱上。这篇就专门写写LoRA微调里算力估算这件事,把公式、实际案例和工程上的取舍一次性讲清楚。
先说清楚:LoRA微调的算力估算,核心不是看模型有多少参数,而是看你同时塞了多少数据、多长的序列、多大的batch size,以及你用哪种精度跑。 可训练参数只影响优化器状态那块的开销,真正吃掉显存大头和计算量的,是反传时的激活值,这一块恰恰是网上教程里讲得最少的部分。
1. 算力估算的核心公式:从零推一遍
1.1 为什么说“算力估算”不等于“显存估算”
很多人把算力估算和显存估算混为一谈,这是第一步就走偏的坑。算力是单位时间内你消耗的计算量,单位是FLOPs,它决定训练一版要跑多久;显存则是硬件上存储的占用,它决定你到底跑不跑得动。两者的关系就像搬砖:算力决定了你一小时能搬多少块,显存决定了你工地上最多能同时堆多少砖。你能搬得快,但工位太小砖叠不上去,照样完不成活。
在LoRA微调的情境里,显存不够是最大的瓶颈,所以大家最先问的总是“我显卡能不能跑”。但如果你想知道“跑一轮要多久”,那必须另算FLOPs,这属于两个维度的问题,不能混着看。设计工程方案时,这两项都得估算,不然要么显存门槛没摸清导致OOM,要么估错训练时长导致排期翻车。
1.2 显存开销公式:参数、梯度和优化器状态
LoRA微调时,显存占用其实可以分成三块:模型自身参数、梯度、优化器状态,这三块在训练时缺一不可;除此之外还有一块动态的,就是中间激活值,它和显存的总关联最密切,也最容易估算失误。
先说静态部分。假设你要微调的基础模型参数量是 (P)。全量微调时,模型参数占 (P) 个单位的显存,梯度占 (P),优化器状态(以AdamW为例)通常要占到 (12P) 字节(如果按FP32存储的话)。也就是说全量微调FP32状态下,仅模型和优化器相关静态开销就是 (16P) 字节左右。
LoRA的聪明之处在于:只训练额外注入的低秩矩阵。原来那 (P) 个参数全被冻结,不需要梯度,也不需要优化器状态,只有那 (r \times d) 大小的小矩阵消耗训练资源。以一个7B模型为例,模型参数全量训练需要 (16 \times 7)GB也就是112GB左右,而LoRA注入的参数可能只有0.1%到0.5%,静态开销一下就降到了1GB量级。
这就是LoRA能在消费级显卡上跑7B甚至13B模型的底牌。不过底牌归底牌,能不能真跑得动,还得看剩下的动态部分,也就是激活值。
1.3 激活值才是显存消耗的隐藏大户
激活值这个词,很多人第一次听可能觉得陌生。简单解释就是:前向传播时每一层输入输出都会在显存里存一份,反向传播算梯度时要拿这些中间结果去链式求导。数据流像流水线一样经过模型,而这些中间的“在制品”全得堆在车间(显存)里,不能随便丢。
它的显存开销大约可以这样估算:
[
\text{激活显存} \approx \text{每层输出维度} \times \text{序列长度} \times \text{层数} \times \text{batch size} \times \text{字节数}
]
注意,这里序列长度是按token算的,不是按“条”算的,一个句子的长度不同,激活值差异非常大。看起来只是一个维度的差异,但在传回梯度的时候,每一层都要保留完整的中间结果,模型一旦深了,这个数字就很吓人。我用7B模型、序列长度2048、batch size为1跑过一次实验,纯用LoRA,激活值部分也能吃掉12到16GB显存。很多人以为LoRA天下无敌,结果序列一拉长照样爆显存,多半就是栽在激活值上。
所以工程上一个核心思路就是:激活值能不能省?省多少? 最直接的方式是梯度检查点,它不是把中间值全存下来,而是反向传播时重新算一遍前向过程,典型的用算力换显存。这跟“省着用工地堆砖”是两个思路:要么扩仓库,要么减少同时堆的砖数,代价是多跑几趟搬运。
