上一章 《思维链与测试时计算》 算的是「多想一会儿」的训练账;这一章问:只改千分之一的参数够吗?
全量
| 项目 | 每个参数占多少 | 说明 |
|---|---|---|
| 权重(bf16) | 2 字节 | 模型本身 |
| 梯度(bf16) | 2 字节 | 反向传播算出来的 |
| Adam 动量 m | 4 字节 | fp32 存,否则精度不够 |
| Adam 方差 v | 4 字节 | 同上 |
| 合计 | 12 字节 | 还没算 |
互动 · 训练比推理,多吃的是哪几个字节
你想给一本 1000 章的教科书加几条批注。
全量微调 = 把整本书重写一遍,还要保留所有草稿(梯度 + 优化器状态)。
LoRA = 在书页边上贴便签。原书一个字不动,只写那几条便签。
因为微调不需要重新学会一门语言。基座模型已经会了。微调只是让它
"更倾向于某种回答方式"或"适应某个领域"——这是一个小改动,不是一次重写。
而"小改动"在数学上有个精确的说法:低秩。第 3 节会看到这个假设怎么被演示。
假设一个权重矩阵 W 是 4096 × 4096——d = 4096 就是矩阵的行宽,也就是一层的宽度。 全量微调会改动它的每个元素;LoRA 赌的是:真正需要的改动,可以用少数几个「方向」组合出来。 这里的「方向」就是后面窄门里过门的人;能同时过去的人数 r 叫秩,ΔW 最多由 r 个独立方向拼出来。
互动 · 秩 r 决定参数量
互动 · 三维看低秩:改动都挤在一张纸上
公式里还有一个 α/r:实际更新用的是 ΔW = (α / r) · B·A。
为什么要补这一下?因为调大 r 时 B·A 的数值也会跟着变大(加了更多项);
没有它,改了 r 之后
「改动只需要几个方向」在数学上是一个形状问题: 原来的改动是一张 d×d 的大表(d2 个自由度),LoRA 换成两块窄条—— A 是 r×d、B 是 d×r,中间只留一个 r 宽的通道。 x 是这一层收到的输入(这里取 8 个数演示;真实模型里 d = 4096)。 把鼠标移到下面公式卡的符号上,对应的滑杆会亮。
动画 · 数据穿过窄门:d → r → d
想象一排 d 个人要过一道门。全量微调把门开成整面城墙那么宽——
每个人都能单独决定自己怎么改,所以要 d2 个旋钮。
LoRA 把门收成只让 r 个人过去:剩下的人怎么变,只能靠这 r 个人组合出来。
旋钮从 d2 掉到 2dr,代价是「组合不出来的改动,永远做不到」——
这就是第 7 节那条限制的来源。
上面那一行是活的:拖本节那两根滑杆,它会跟着变。
把「窄门宽度 r」从 1 一路拖到 8:
· r = 1 时中间只有一个数能过门——所有改动只能沿同一个方向;
· r = 8(和输入一样宽)时门等于没关,这时候 2dr = 2×8×8 = 128,比 d² = 64 还大,LoRA 反而不省了。
所以真正的参数账要去第 2 节看:那里 d = 4096、r = 8,这一层的可训参数省了 256 倍。
LoRA 的秩 r 是它唯一的重要超参。下面这个互动真做一次 SVD(奇异值分解): 把一张表拆成若干层,每一层的重要性就是一个奇异值;这些奇异值排成的那列数叫谱。 矩阵是我们构造的,一共只有 10 个方向(秩 = 10),所以 r = 10 时误差正好为 0。
互动 · 秩 r 与近似误差(真算的 SVD)
左图:奇异值谱明显是「陡降」的。前几个奇异值占了绝大部分"能量", 后面的迅速衰减到接近 0。这就是低秩假设成立时的样子—— 如果谱是平的,说明每个方向都同样重要,那就不存在"低秩"这回事,LoRA 根本不该有效。
右图:误差曲线在某个点之后基本变平。那个拐点就是"够用的秩"。 再往上加秩,参数量在涨,但误差几乎不降——纯浪费。
这里算的误差是给定秩下最好的可能近似(数学上能保证截断 SVD 就是最优的)。
实际 LoRA 训练从随机初始化开始做梯度下降,达不到这个上界。
所以真实的 LoRA 需要比拐点稍大一点的秩:实践里 r = 8~64 是常见区间,而理论上的拐点常在 r = 2~4。
起步可以先试 r = 8~16。
LoRA 把可训练参数压到了千分之一上下,但基座模型本身还是要占显存——65B 模型 bf16 就占 130GB。
QLoRA 的答案是:把冻住的基座也
互动 · 三种方案的显存对比
| 技巧 | 做什么 | 省下什么 |
|---|---|---|
| NF4 量化 | 把权重量化成 4 位的"正态分布友好"格式 | 基座显存直接砍到 1/4。NF4 是专门为神经网络权重设计的, 比普通 int4 精度更高 |
| 双重量化 | 连量化用的"缩放系数"本身也量化一遍 | 每 64 个参数存一个 fp32 缩放系数,累积起来不小。再量化一次能省约 0.4 bit/参数 |
| 分页优化器 | 显存不够时把优化器状态临时换到内存 | 避免长序列训练时的显存尖峰直接 OOM(显存不够,程序当场崩) |
互动 · NF4 和「普通的 4bit」到底差在哪
2023 年,QLoRA 让 65B 模型的微调第一次能在单张 48GB 的工作站显卡上跑完。 在这之前,微调 65B 需要一整个机柜;这一下把"自己微调一个大模型"从只有大公司能做的事,变成了单张专业卡就能做的事。 消费级 24GB 卡(配 QLoRA)能微调的通常是 7B~13B。 今天大部分消费级显卡上的微调,走的就是 QLoRA 这条路。
LoRA 可以插在 Transformer 的任何线性层上。
| 插在哪 | 参数量 | 效果 |
|---|---|---|
| 只有 q, v LoRA 原论文(Hu et al. 2021)的选择 |
最小 | 在原论文的设定下够用,是流传最广的默认值。但这是在 GPT-3 时代的模型上得出的结论 |
| q, k, v, o | 2 倍 | 略好。注意力部分全覆盖 |
| 所有线性层 含 FFN |
4~6 倍 | 明显更好。FFN 占了 2/3 的参数,也大致占了 2/3 的"知识"(粗略说法)—— 想改知识必须动它 |
| embedding + lm_head 把词表映射成向量、再把向量映射回词表的两张表 |
视词表而定 | 学新词汇、新语言时必须加,否则新 token 学不到东西 |
互动 · 插在哪几个模块,差多少参数(LLaMA-7B 的真账)
很多人听说"r 越大越好",就一直往上加 r。但 r 不是越大越好:
秩给多了,模型会开始拟合数据里的噪声和格式癖好,
先确保数据是对的,再考虑要不要加秩。
数据量看任务:LIMA 用 1,000 条,Alpaca 用 5.2 万条。
| 方法 | 在哪加参数 | 特点 |
|---|---|---|
| Adapter 2019 |
在每层后面插一个小 MLP | 最早的方案。但增加了推理深度,推理时会变慢 |
| Prefix / Prompt Tuning | 在输入前面拼一段可训练的"虚拟 token" | 不改模型结构。但占用上下文长度,效果不如 LoRA 稳定 |
| LoRA 2021 |
旁挂在权重矩阵上,ΔW = BA | 推理时可以合并回原权重,零额外延迟——这是它胜出的关键 |
| QLoRA 2023 |
LoRA + 4bit 量化的基座 | 消费级显卡也能微调大模型。历史意义最大的一次 |
| LoRA+ | 给 B 用比 A 更大的学习率 | 收敛更快。实现极简,性价比高 |
| DoRA 2024 |
把权重拆成「幅度」和「方向」分开调(每个数的长度和朝向) | 低秩时明显优于 LoRA,接近全量微调。代价是训练慢一些 |
| PiSSA | 用 SVD 的主成分初始化 A 和 B,而不是随机 | 收敛更快,效果更好。思想就是拿这一章第 3 节的谱分析结果当初始化 |
| AdaLoRA | 不同层分配不同的秩,重要层给大秩 | 自动化了"该给多少秩"这个问题,但实现复杂 |
| VeRA / LoHa | 共享一对随机矩阵、只学缩放向量 / 用 Hadamard 积(两个矩阵对应位置的数直接相乘) | 参数更少,适合极端的显存限制 |
| 全量微调 | — | 上限最高,原因是没有容量约束——不是它"学到了更多"。 数据够多、算力够时它仍然是最好选择 |
互动 · 这一家成员,各要多少参数(同一个 7B 模型上的真账)
下面是另一个例子:一个行为型的改动,主要成分只有 3 个方向。 它和第 3 节那张谱不是同一个矩阵——但两张图回答同一个问题:一个改动里有多少能被 r 个方向装下。
| 限制 | 具体表现 |
|---|---|
| ① 只能"激活已有的",学不会全新的 | 这是最重要的一条。LoRA 是在原权重的边上加扰动,
它能改变模型"倾向于做什么",但很难塞进基座完全不具备的知识或技能。 换句话说:调行为,LoRA 够;学新知识,得靠继续 |
| ② 多 LoRA 会互相干扰 | 虽然可以给不同任务训不同的 LoRA 再动态切换,但 同时加载多个、或者远距离任务之间,会互相打架。 这也是为什么"统一的通用 LoRA"很难训出来 |
| ③ 合并之后无法反悔 | 把 ΔW 合并回 W 之后推理零延迟,但你就失去了这个 LoRA 的独立身份,不能再做切换或撤销。 不合并、让多个 LoRA 动态切换也行:每个插了 LoRA 的模块多算两次小矩阵乘,额外开销很小 |
| ④ 对数据的质量更敏感 | 因为容量小,它没法"稀释"坏数据的影响。 同样一批脏数据,全量微调可能还能扛住,LoRA 会更快地被带偏 |
互动 · 同一张改动,LoRA 够得着哪一部分
示意图 · 能改到的区域,一眼看完
前面那两张误差图说的都是这件事:窄带内的地方随便去,窄带外的地方一步也迈不出去。
「我要改的是知识,还是行为?」
· 行为(输出格式、说话风格、遵循指令的方式、领域术语的用法)→ LoRA 足够
· 知识(这个学科的专业事实、一门新语言、一个陌生的代码库)→
LoRA 基本无效,要么 RAG,要么继续
「领域术语的用法」拿不准时:用法偏行为,术语背后的新事实偏知识;还是拿不准就先上 RAG。
这一条判断错了,后面调什么参数都是白费。
回到第 3 节那张「秩 r 与近似误差(真算的 SVD)」。把 r 从 1 一格一格拖到 11, 盯着右图那条误差曲线:前几格陡降,过了某个拐点就几乎躺平、不再往下走。
那个躺平的瞬间你看到的就是「学习即压缩」:误差不再降,说明你要的那点改动里信息量就这么大, 秩再往上加,加进去的只是参数和噪声。
| 直觉上 | 实际发生的事 |
|---|---|
| 微调 = 教模型新东西,所以得动很多参数 | 微调更像是让一个已经会说话的东西换一种说法。 基座早就把「世界」压进权重里了,微调只是在已有表征上选边站—— 选边是一项低信息量的动作 |
| 秩 r 小 = 模型被削笨了 | 秩 r 小 = 可训练的自由度少。它不是「不会」,只是能改的地方太少, 所以坏数据一来就直接把它带偏(见第 7 节 ④) |
| 参数越多,能力越强 | 本章就是反例:参数量降到千分之一,效果常常看不出差别。 「能力」和「参数量」不是同一件事——能力的大部分存在你根本没动的那些参数里 |
| 暗线 | 这一章的回答 |
|---|---|
| 信息流动 | 输入输出的形状一个都没变,变的只有前向沿途经过的那些权重矩阵:
d×d 的 W 被换成 W + BA,其中 A 是 r × d、B 是 d × r。
信息穿过 ΔW 时被迫挤过一道只有 r 宽的窄门——这就是「低秩」在形状上的全部含义 |
| 什么被牺牲了 | 牺牲容量,换来显存和延迟两块。而且这笔交换是不对称的: 省下的显存是形状必然带来的,损失的能力却只在任务恰好需要它时才出现, 所以它在「改行为」的任务上看起来像白赚的。这笔交易的账单就是第 7 节那张表 |
| 参数账本 | 全量微调 70B:12 字节/参数 ≈ 840 GB
(不含 FP32 主副本;含主副本 16 字节/参数 ≈ 1.1 TB)。 LoRA 的账:一个 4096×4096 的权重有 16,777,216 个参数; r=8 时 ΔW = BA 只有 2 × 4096 × 8 = 65,536 个,是这一层的 0.39%(整模型的可训占比见第 5 节)。 QLoRA 的账:65B 基座 bf16 要 130 GB, NF4 之后约 33 GB(双重量化再省约 0.4 bit/参数)—— 这就是「单张 48GB 卡能微调 65B」这个数字的来源 |
| 它假设了什么 | 假设「不同下游任务之间的差异是低维的」——这是一句关于世界的断言,不是数学定理。 第 3 节那张奇异值谱画的就是它成立时的样子:谱陡降,假设成立;谱要是平的,LoRA 根本不该有效。 LoRA 赌的是「人交给模型的那些微调任务,彼此只差几个方向」——低秩近似好不好用,是这句话的推论 |
| 违背了哪个直觉 | 「模型越大越难微调」是错的。模型越大,ΔW 相对基座的占比越小,LoRA 越划算——
它把「能不能微调」从算力问题换成了装不装得下的问题。 第二个反直觉:参数量不等于能力。降到千分之一而效果不变, 说明能力大部分躺在你根本没动的那些参数里 |
它接住了上一章的什么:《思维链与测试时计算》留下的账是 「训一个会想的模型,贵在训练显存」;LoRA 问:这笔账能不能只付千分之一?
它给后面哪一章留了什么:如果连微调都能压到千分之一, 那「让模型记住一批事实」是不是可以完全不碰权重?这就是更后面的 《RAG 检索增强》的出发点。
LoRA 赌的是"微调带来的改动是低秩的"——用 ΔW = BA 两个小矩阵代替整个矩阵的更新,
整模型的可训参数降到千分之一,而且推理时可以合并回原权重,零额外延迟。
α/r 缩放是让人能安心调 r 的关键;r 调到某个值之后误差就不再降,
再加秩纯属浪费。QLoRA(4bit 基座 + LoRA)把大模型微调拉进了消费级显卡。
最后记住那条分界线:LoRA 改「行为」绰绰有余,改「知识」基本没用——
先想清楚你要改的是哪一个。
上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。