12 种微调技术不是 12 个选项,是三张账单的折扣券

图上写 BitFit「约占全部参数的 0.08%」。我按真模型的张量表数了一遍:129,024 个 bias,占 0.001694%,差 47 倍——而且它已经不是最省的那个。顺着这条线把 12 种技术钉进显存、标注、数据位置三张账单。

信息图上 BitFit 那一格写着:「只更新 bias,约占全部参数的 0.08%,是参数效率最高的 PEFT 方法。」

我把这句话拿本机那个 Qwen2.5-7B-Instruct 的 GGUF 张量表数了一遍:全部 bias 张量合计 129,024 个参数,占 7,615,616,512 的 0.001694%——差了 47 倍。 而且在这台模型上它连「最省」都不是,纯 soft prompt 比它还小 3.6 倍。

图没有抄错,是模型换了。而这个 47 倍的缝隙正好通向一件更要紧的事:这 12 种技术不是 12 个选项,是三张互不相干的账单——显存、标注、数据位置——每种技术只打折其中一张。 把它们摊在一张平铺的 12 宫格里,最容易得出的错结论就是「挑一个最好的用」。

名词速查

术语一句话解释
PEFTparameter-efficient fine-tuning,只训练一小部分参数、把底座冻住的那一类方法
冻结(freeze)参数照常参与前向计算,但不为它算梯度、不给它分配优化器状态
优化器状态Adam 为每个可训练参数额外存的两个量(动量 m 和二阶动量 v),通常用 fp32
激活(activation)前向过程中留下来、反向时要用到的中间结果。它跟「参数多少」无关,只跟「网络多深、序列多长」有关
bias / RMSNorm线性层的偏置项;RMSNorm 是 LLaMA 系列用的归一化层,只有缩放权重、没有 bias,这是本文 47 倍差距的来源
soft prompt / 虚拟 token一串可训练的向量,拼在真实 token 前面一起送进模型,本身不对应任何词
内在维度「把一个任务学会」实际需要的自由度。它远小于参数总数,是整个 PEFT 家族的地基
reward model把「人更喜欢哪个回答」拟合成一个打分函数的模型,RLHF 里的常驻第三份模型
verifier一段代码(而不是模型),对答案返回 0/1。RLVR 用它替掉 reward model

读这篇需要先有的三件事,站内都有专文,不必现在补:LoRA 里 rank 到底是什么、为什么常取 8/16/32,见LoRA rank 的通俗数学;指令微调为什么几乎教不会新知识、它的真实身份是解锁器,见SFT 是解锁器不是填充器;GRPO 组内优势为什么要求一道题「不全对也不全错」,见什么是大模型的环境工程。这三条本文都不重讲,只在它们身上接新的东西。

一句根本约束:三张账单,各自独立

先把本质写成一句可反驳的话:

这 12 种技术之所以不能互相替代,是因为微调有三张互不相干的账单——显存、标注、数据位置——而每种技术只打折其中一张。

怎么反驳它?找出一种同时打折三张的技术。 找不到,这句话就立着。逐个试:QLoRA 把显存压到 1/29,但它一条标注样本都没帮你省;RLVR 把数学题的标注成本直接归零,却一个字节的显存都没省;Federated Tuning 让数据不出设备,但训练该花的显存和该有的标注一分不少。三张账单正交,所以这 12 种技术是可以相乘的,不是互相排斥的——2026 年真正在跑的配方(QLoRA + GRPO + RLVR)就是三列各取一个。

账单稀缺的是什么图上属于这一列的技术
一·显存账训练一步要同时住下多少张量LoRA、QLoRA、Prefix Tuning、Adapter Tuning、P-Tuning、BitFit
二·标注账训练信号从哪来、每条样本要人付多少钱Instruction Tuning、RLHF / RLAIF、DPO、GRPO、RLVR
三·数据位置账数据允许被搬到哪里Federated Tuning

12 宫格里那条隐形的分界线就在这儿。下面按账单走,每张账单只回答三件事:账怎么算、最朴素的做法先在哪崩、图上这几格各自打折了哪一项。

第一张账单:显存

先把账算清楚

全量微调一个 7B 模型,用 Adam 的标准配置(bf16 权重 + bf16 梯度 + fp32 的 master 副本和 m、v),每个参数要 16 字节。拿实测的 7,615,616,512 代进去:

方案底座常驻梯度 + 优化器合计
全量微调(bf16)14.19 GiB99.30 GiB127.67 GiB
LoRA r=8(bf16 底座)14.19 GiB0.066 GiB14.26 GiB
QLoRA r=8(4bit 底座)4.36 GiB0.066 GiB4.44 GiB

底座那一栏的 4.36 GiB 不是我估的,是本机那个 Q4_K_M 文件的实际大小。梯度+优化器那一栏的比值是 1509 倍,而它恰好等于参数量的比值 7,615,616,512 ÷ 5,046,272——因为这一项是严格线性的,每个可训练参数收 14 字节,一分不多。

这三行才是 LoRA 和 QLoRA 分工的真相。 LoRA 砍的是右边那一栏(99.30 → 0.066),左边那一栏它一个字节都没动。QLoRA 砍的是左边那一栏(14.19 → 4.36)。所以 QLoRA 的贡献从来不是「LoRA 的改进版」——LoRA 早就有了——它是去收拾 LoRA 没碰的那半张账单

我这台机器 24 GB 统一内存,llama.cpp 启动日志里报的 GPU 可用预算是 MTL0: Apple M5 Pro (18186 MiB, 18185 MiB free),也就是 17.76 GiB。对着上面三行看:全量微调差 7.2 倍,门都进不去;LoRA 的 14.26 GiB 塞得进去,但只剩 3.5 GiB 余量;QLoRA 的 4.44 GiB 才有 13 GiB 的空档。这个「塞得进去但只剩 3.5 GiB」是最危险的状态,因为上面那张表漏了一整项。

崩点:LoRA 一个字节都没砍激活账单

那张表里只有权重、梯度、优化器,没有激活。而激活跟可训练参数量完全无关——你在第 1 层挂了 LoRA,梯度就得从最后一层一路传回第 1 层,整个前向过程的中间结果全都得留着。冻结不等于不参与反向传播。

这件事有多重?挑一个能手算的最大头:输出 logits。Qwen2.5 的词表是 152,064(实测 GGUF 元数据),logits 通常按 fp32 算,所以一次前向要 S×152064×4S \times 152064 \times 4 字节:

下面这一列的分母是 LoRA r=8 的全部显存开销 0.0752 GiB(adapter 权重 2 字节 + 梯度 2 + fp32 的 master/m/v 12,合 16 字节/参数;上一张表里的 0.066 GiB 是其中不含权重本身的那 14 字节部分):

序列长度 Slogits 张量(fp32)相当于 LoRA r=8 全部开销(0.0752 GiB)的
5120.290 GiB3.9 倍
20481.160 GiB15.4 倍
40962.320 GiB30.9 倍

一个张量,就是 LoRA 辛辛苦苦省下来的那部分的 15 倍。 而这只是 logits,还没算 28 层的 attention 和 MLP 中间结果。所以「PEFT 省显存」这句话在工程上要改成:PEFT 省的是优化器账,省不了激活账;激活账要靠别的手段(梯度检查点、序列长度、chunked loss)单独交。 我第一次在这台机器上撞墙就是这么撞的——参数量算得漂漂亮亮,一上真实序列长度就 OOM,回头才发现自己算的那张表根本不包含会爆的那一项。

参数量排行榜:BitFit 已经不是最省的了

现在回到开头那个 47 倍。BitFit 的 bias 数量可以逐层手算,用 GGUF 里的真实形状:

每层: attn_q.bias 3584 + attn_k.bias 512 + attn_v.bias 512 = 4608
×28 层 = 129,024        ← 与张量表实测值完全一致

注意那几个「缺席者」:attn_output 没有 bias,三个 FFN 张量(ffn_gate / ffn_up / ffn_down都没有 bias,attn_norm / ffn_norm 是 RMSNorm,只有权重没有 bias。而 BitFit 论文(arXiv 2106.10199)做实验的对象是 BERT——BERT 的每个线性层都带 bias,LayerNorm 还有 γ\gammaβ\beta 两套。从 BERT 到 LLaMA 式架构这一路,bias 被一层层删掉了,BitFit 的可训练预算就跟着蒸发了 47 倍。

把图上这一列全部按真实形状算一遍,排行榜是这样(都用 Qwen2.5-7B 的实测形状,p 是虚拟 token 数,m 是 adapter 瓶颈维):

方法可训练参数占比相对 BitFit
Prompt Tuning(p=10,仅输入层)35,8400.00047%0.28×
BitFit(全部 bias)129,0240.00169%1.00×
Prefix Tuning(p=10,逐层 KV)286,7200.00376%2.22×
LoRA r=8(q,k,v,o)5,046,2720.06626%39.1×
Adapter(m=16)6,422,5280.08433%49.8×
Adapter(m=64)25,690,1120.33733%199×
LoRA r=16(全线性层)40,370,1760.53010%313×
全量微调7,615,616,512100%59,024×

两个可以核的地方。第一,Prompt Tuning 比 BitFit 小 3.6 倍,所以「BitFit 是参数效率最高的 PEFT 方法」在这台模型上不成立——它在 2021 年的 BERT 上成立。第二个有点巧:Adapter 在 m=16 时算出来是 0.0843%,几乎正好是图上写给 BitFit 的那个 0.08%。 那个数量级是 Adapter / LoRA 的量级,不是现代模型上 bias 的量级。

几个数也能自己验:LoRA 的每层每 rank 开销是 r(din+dout)r(d_{in}+d_{out}) 逐矩阵累加 = (3584+3584)+(3584+512)×2+(3584+3584)=22528(3584{+}3584) + (3584{+}512) \times 2 + (3584{+}3584) = 22528,乘 rank 乘 28 层;Prefix Tuning 每层要一份 K 和一份 V,所以是 2×p×28×5122 \times p \times 28 \times 512

这一列的隐藏子账:推理时要不要一直付钱

参数量表还藏着一件图上完全看不出来的事。看图上 LoRA 那格的小字:「合并后不增加任何推理开销」。这句话只有 LoRA 说得出来

方法训练时省显存推理时的永久成本
LoRA / QLoRA——BABA 加回 WW,结构完全不变
Adapter Tuning每层多两个串行瓶颈模块,合不进去,延迟永久增加
Prefix / P-Tuning虚拟 token 占住每层的 KV 和有效上下文,拿不掉
BitFit零(bias 本来就在那儿)

LoRA 赢过 Adapter 的地方不在参数量(Adapter m=16 才 6.4M,跟 LoRA r=8 的 5.0M 同级),而在「可合并」这一条。 一个能在部署时消失的训练结构,和一个永久留在推理路径上的训练结构,工程价值完全不同。这也解释了 Adapter 论文(arXiv 1902.00751)LoRA(arXiv 2106.09685)早两年多,却是后者赢了这条路线。

这一列的地基:内在维度

上面所有方法都押在同一个假设上:「把一个任务学会」需要的自由度远小于参数总数。 这不是猜测,是有直接实验的——Intrinsic Dimensionality(arXiv 2012.13255)报告:只优化 200 个可训练参数(随机投影回全参数空间),RoBERTa 在 MRPC 上能拿到全参数微调 90% 的水平;而且模型越大,内在维度越低(论文原文结论,我未复现)。

这条约束也划出了 PEFT 的失效边界。它说的是「适应一个任务」维度低,不是「装进新知识」维度低。LoRA Learns Less and Forgets Less(arXiv 2405.09673)在编程和数学两个域上量到了这个分界:标准低 rank 设置下 LoRA 明显打不过全量微调,但它对域外能力的保持明显更好(论文报告,我未复现)。想改行为,PEFT 够用;想灌知识,账单还是要付。 这跟站内SFT 是解锁器那篇的主线是同一件事的两个侧面,而 BitFit 论文摘要自己的话也正好撞上:微调「主要是把语言模型预训练已经诱导出的知识暴露出来,而不是学到新的任务专属语言知识」。

第二张账单:标注

第二列换的不是参数,是训练信号的来源。它跟显存账完全解耦——你可以用任何一种 PEFT 去跑任何一种信号。

图上五格的演进,其实是一条可以数的路线。这条路线上真正在减少的东西是「训练时必须同时常驻几份模型」:

方法训练信号从哪来常驻模型副本7B bf16 下的常驻显存
Instruction Tuning人写的输入-输出对1(policy)14.19 GiB
RLHF / PPO人排序 → 训一个 reward model4(policy + ref + reward + value)56.74 GiB
RLAIF同上,排序的人换成模型456.74 GiB
DPO偏好对,直接优化策略2(policy + ref)28.37 GiB
GRPO组内采样自己当基线,砍掉 value 网络3(policy + ref + reward)42.56 GiB
GRPO + RLVRverifier 是一段代码,不是模型228.37 GiB

这张表是这一列的骨架。 RLHF 之所以贵,一半不是算法贵,是四份模型同时住在显存里贵。之后每一步都在拆掉其中一份:DPO 用「KL 约束下最优策略有闭式解」这个推导把 reward model 消掉(arXiv 2305.18290);GRPO 用组内均值当基线把 value 网络消掉(DeepSeekMath,arXiv 2402.03300);RLVR 干脆把 reward model 换成一段执行代码(Tulu 3,arXiv 2411.15124)。从 4 到 2,这就是过去三年 RL 后训练最实在的进展。

为什么这一列的起点是「排序」而不是「打分」?因为人给不出可靠的绝对分。问一个标注员「这个回答值几分」,不同人、不同天的标尺都不一样;问「A 和 B 哪个好」,一致性高得多。RLHF 的整个 reward model 就是为了把「只能比较」翻译成「可以标度」而存在的——这也是它必须多养一份模型的根因。

一个能自己核的锚点:DPO 的初始损失必须是 0.6931

DPO 的损失里有个隐式奖励差 β[logπ(yw)πref(yw)logπ(yl)πref(yl)]\beta\left[\log\frac{\pi(y_w)}{\pi_{ref}(y_w)} - \log\frac{\pi(y_l)}{\pi_{ref}(y_l)}\right]。训练第 0 步,π=πref\pi = \pi_{ref},所以这两个 log 比值都严格等于 0,整个差值是 0,于是

L=logσ(0)=log0.5=ln2=0.693147L = -\log \sigma(0) = -\log 0.5 = \ln 2 = 0.693147

这是一个和数据、和 β\beta、和模型全都无关的常数。 工程上它是个很好用的探针:如果你的 DPO 训练曲线第一步不是 0.693,那不是超参没调好,是 reference model 接错了(最常见的是忘了冻结,或者加载成了别的 checkpoint)。

GRPO / RLVR:本地实测的命中率,以及一个把我坑到的地方

GRPO 组内优势要求一道题「不全对也不全错」,这条结论站内已有专文推导过(环境工程那篇),我不重讲。我只补一个那篇没有的东西:在真模型上,这种塌方的命中率有多高。

我拿本地 Qwen2.5-7B 对四道题各采样 8 次(temperature=1.0),用 RLVR 式的二值 verifier(抽 \boxed{} 里的数字做精确匹配)打分:

题目8 次 rewardsmeanstd组内优势
17 + 25[1,1,1,1,1,1,1,1]1.0000.000全为 0
23 × 7 − 18[1,1,1,1,1,1,1,1]1.0000.000全为 0
三步应用题(找零)[1,1,1,1,1,1,1,1]1.0000.000全为 0
3719 × 482[1,0,1,1,0,1,1,0]0.6250.484+0.775 ×5,−1.291 ×3

四道题里三道贡献了严格为零的梯度。 24 次采样白花,只有第四道的 8 次在产生信号。那一行的优势可以手算核:mean=5/8=0.625\text{mean}=5/8=0.625,总体标准差 0.625×0.375=0.48412\sqrt{0.625 \times 0.375}=0.48412,于是答对的是 0.375/0.48412=+0.77460.375/0.48412=+0.7746、答错的是 0.625/0.48412=1.2910-0.625/0.48412=-1.2910,八个加起来正好为 0(我用的是组内总体标准差;换成样本标准差 0.51755 会得到 +0.7246/1.2076+0.7246 / -1.2076,塌成零的结论不变)。

把我坑到的是第四道题那三个 0。 我一开始当成三次算错,去看原始输出才发现:只有一个是真算错了(给出 1795358,正确是 1792558),另外两个根本没输出 \boxed{}——320 token 的预算被中间步骤吃光,答案还没写出来就截断了。verifier 抽不到,记 0。

二值 verifier 分不清「算错了」和「没按格式说完」,两者都是 0 分。 这意味着 RLVR 的奖励信号里混着一份纯粹的格式噪声,而且它跟难度正相关——题越难、思考越长、越容易撞上 token 上限、越容易被记成「算错」。这跟我上一篇里 G-Eval 的概率质量跑到空格 token 上是同一类病:评估的静默失败从来不报错,它只是安静地给你一个数。工程上的最低防线是把「没抽到答案」和「抽到但不对」记成两个不同的计数器,别让它们一起进 reward。

第三张账单:数据位置(Federated Tuning)

第三列只有一格,但它是唯一一个不在前两张账单上做任何优化的技术——它换的是别的东西:数据不离开设备。

机制很朴素:把训练分发到客户端,各自在本地私有数据上微调,只把权重更新回传,服务端做聚合。这就是 2016 年 FedAvg(arXiv 1602.05629)的做法,比这张图上其他 11 格里的大多数都老。它在 LLM 时代重新有用,是因为跟第一列产生了一个非常好的化学反应:如果客户端用 LoRA,回传的就只是那几 MB 的 adapter,而不是 14 GiB 的全量权重。按上面表里的数字,LoRA r=8 是 5,046,272 个参数,bf16 下约 9.6 MiB——通信量降到全量的约 1/1500。没有 PEFT 的联邦 LLM 微调在通信上是不可行的,这两列是互相成就的关系。

它的「坏掉会怎样」也和前两列完全不同类型:

  • 只传更新不等于不泄漏数据。 梯度/权重更新本身携带训练样本的信息,梯度反演类攻击就是冲着这个来的。所以实用系统要在聚合上再加一层(安全聚合、差分隐私噪声),而噪声会直接换走模型质量——这是一笔真实的交换,不是免费的。
  • 通信成为新瓶颈。 前两列的瓶颈是显存和标注,这一列是带宽和轮次。
  • 客户端数据非独立同分布。 每台设备的数据分布不一样,朴素平均会互相抵消。

这一格是本站此前完全没写过的方向,所以多说了两句;它的深入展开我留着,还没动手跑过任何联邦实验,这里的判断全部来自论文和机制推理,不是实测。

共同祖先:三列各有各的老根

这一列它的共同祖先老想法是什么
显存账(PEFT)低维重参数化高维问题存在一个低维等效表示——从主成分分析到 LoRA 是同一个动作
标注账(RL 那一列)目标函数即命运你度量什么就得到什么。reward model 是代理目标,verifier 是把代理目标换成真目标的一次尝试
数据位置账(联邦)分布式共识与隐私计算去找数据,而不是数据去找计算——和当年 MapReduce「移动计算而非移动数据」是同一句话

第二列尤其值得挂上去:RLHF → DPO → GRPO → RLVR 这条线上每一步都在缩短代理目标到真目标的距离。reward model 是「拟合人的偏好」,隔了一层;verifier 是「直接判对错」,不隔。这也说明了 RLVR 为什么只在数学、代码这些「对错可执行判定」的领域好用——它的前提是真目标本身可以写成代码。写不成的领域(文风、有用性、安全),你还是得回去养那份 reward model。

总表:12 格 × 打折哪张账单 × 坏掉会怎样

技术打折哪张账单具体省掉什么坏掉会怎样
LoRA显存优化器状态降 1509 倍;可合并,推理零开销低 rank 装不进新知识,只改得动行为
QLoRA显存冻结底座 14.19 → 4.36 GiB4bit 底座本身有量化误差,且不省激活
Prefix Tuning显存只训 286,720 个参数(p=10)虚拟 token 永久占每层 KV 和有效上下文
Adapter Tuning显存只训瓶颈模块合不进底座,推理延迟永久增加
P-Tuning显存只训 soft prompt + 一个可丢弃的编码器容量极小,任务一复杂就不够
BitFit显存只训 129,024 个 bias(0.0017%)现代架构删了大部分 bias,预算蒸发 47 倍
Instruction Tuning标注不需要 RL 那一套需要现成的标准答案;几乎教不会新知识
RLHF / RLAIF标注只要排序,不要标准答案四份模型常驻(56.74 GiB);reward model 会被钻空子
DPO标注砍掉 reward model + value 网络,4 份降到 2 份需要成对偏好数据;序列 logprob 带长度偏好
GRPO标注组内均值当基线,砍掉 value 网络一组全对或全错时优势严格为 0(我这里 3/4 命中)
RLVR标注reward model 换成一段代码,标注成本归零只适用于可执行判定的领域;分不清算错和格式违规
Federated Tuning数据位置数据不出设备更新本身会泄漏;通信成新瓶颈;非独立同分布

三张账单,各自一句结论

显存账:根本约束是「适应一个任务的自由度远低于参数总数」(200 个参数拿到 90% 的水平)。崩点是最朴素的读法——「参数少 1509 倍所以显存少 1509 倍」——先在激活上崩:单个 logits 张量在 2048 序列长度下就是 LoRA 全部开销的 15.4 倍。可带走的动作:把显存账拆成优化器账和激活账两栏分别算,PEFT 只管前一栏。

标注账:根本约束是「人只能比较,不能标度」。崩点是把它当算法问题看——它一半是「四份模型同时常驻」的显存问题,从 4 份降到 2 份才是这条线上最实在的进展。可带走的动作:选 RL 方案时先数模型副本数,再看算法名字。

数据位置账:根本约束是「有些数据在法律或物理上不能移动」。崩点是以为「只传更新就等于隐私」——更新本身携带样本信息。可带走的动作:联邦和 PEFT 必须一起用,否则通信量本身就不可行。

换成一句不带术语、能直接讲给同事听的话:微调像装修一间租来的房子。第一张账单是「你能改动多少结构」(拆一面墙还是只换把门锁),第二张是「谁来告诉你什么叫装得好」(自己按图纸、请人评分、还是有验收标准可以直接量),第三张是「家具能不能搬出门」。 这三件事互不替代——门锁换得再省,也不会因此就知道装得好不好看。所以看到这 12 宫格时,先问「我卡在哪张账单上」,再去那一列里挑,而不是在 12 个里挑一个「最好的」。

五分钟实验:在你自己的模型上核一遍 bias 还剩多少

这是本篇最便宜的一手实验,也是我核出那 47 倍的全部方法。它值得你在自己要用的模型上跑一次,因为这个数完全取决于架构,别人的结论不能直接搬

uv run --with gguf python -c "
from gguf import GGUFReader
r = GGUFReader('your-model.gguf')
tot = bias = 0
for t in r.tensors:
    n = 1
    for d in t.shape: n *= int(d)
    tot += n
    if t.name.endswith('.bias'): bias += n
print(f'total={tot:,}  bias={bias:,}  {bias/tot*100:.6f}%')
"

要记录的数字就一个:bias 占比。我在 Qwen2.5-7B-Instruct 上是 0.001694%,而 BitFit 论文的 BERT 时代是 0.08% 量级。判据很直接:如果你的模型也是 LLaMA 式(RMSNorm、线性层无 bias),这个数会落在千分之几的万分位上,BitFit 基本可以从选项里划掉。 顺手把 .bias 换成 'norm' in t.name 再跑一次,就能看到归一化层那一列(我这台是 204,288 个、0.0027%),这两个数加起来就是「只训归一化和偏置」这一类极省方法的全部预算。成本:不到一分钟,唯一前提是手上有一个 GGUF 文件。

跑完你会顺带得到一张按类型汇总的张量表——我就是在那张表上第一次直观看到,7B 模型里 74.9% 的参数都在三个 FFN 矩阵上ffn_down / ffn_gate / ffn_up 各 24.96%),注意力四个矩阵加起来才 10.8%。这个分布本身就解释了为什么「LoRA 只挂注意力」和「LoRA 挂全部线性层」是两个差 4 倍的决定。

参考来源

工程实践

  • 本文所有参数与显存数字:由本机 Qwen2.5-7B-Instruct-Q4_K_M.gguf 的 GGUF 张量表实测得出(总参数 7,615,616,512,28 层,d_model 3584,KV 维 512,FFN 18944,词表 152064),配套的手算脚本在正文里给全了
  • GRPO / RLVR 实验:llama.cpp llama-server + 同一模型,每题 k=8、temperature=1.0,verifier 为 \boxed{} 精确匹配
  • 机器:Apple M5 Pro / 24 GB 统一内存,llama.cpp 启动日志报 GPU 预算 18186 MiB
  • 信息图原作:DailyDoseofDS《12 LLM Fine-Tuning Techniques》

arXiv 论文