回忆 GPT-3 课你学的「改动幅度轴」:从零训 → 全量微调 → in-context learning。全量微调虽然只是「在预训练权重上小幅再拧」,但「拧」的是全部参数:
① 更新所有参数——175B 个权重每个都要算梯度、存优化器状态,显存需求是参数本身的好几倍; ② 每个任务存一整份模型——你有 100 个客户/任务,就要存 100 份几十 GB 的副本; ③ 切换/部署笨重——没法在一个底座上灵活地服务多任务。 问题来了:适配一个新任务,真的需要动全部参数吗?还是其实只需要动「一点点」?
关键不在 W 本身,而在微调带来的「更新量」ΔW。全量微调学的是一个和 W 同样大的 ΔW(d×k)。LoRA 说:别学整块 ΔW,把它分解成两片瘦矩阵的乘积:
A 是 r×k(矮胖→其实是「瘦高压扁」),B 是 d×r。两者相乘还原成 d×k 的更新量,但真正要训的只有 A、B 里的参数。
秩 r(rank)是唯一的核心旋钮,取 1、2、4、8 这种极小值就够用。
参数量对比(以一个 4096×4096 的权重矩阵为例):全量 = 4096×4096 ≈ 1678 万;LoRA(r=8) = 2×4096×8 ≈ 6.5 万——不到 0.4%。整个模型层层叠加,总量同比例缩小,典型砍掉约 10000 倍。
两个洞察撑起整篇论文——一个解释「为什么省得掉」,一个解释「为什么省了还不亏」。
| 洞察 | 说明 |
|---|---|
| ① 任务适配的「内在秩」很低 | 把一个预训练好的模型适配到新任务,需要改变的方向其实很少——更新量 ΔW 活在一个低维子空间里。所以用低秩的 B·A 去近似它,几乎不丢东西。这就是 LoRA 敢把 r 取到个位数的底气。 |
| ② 推理时可合并,零额外延迟 | 用完后把 W + B·A 算出来合并成一个新的 W,模型结构和原来一模一样——推理不多花一分钟。这是 LoRA 胜过早期 adapter(往网络里插额外层、推理变慢)的关键。 |
第 ① 点接得上你学过的直觉:预训练已经把通用能力压进 W,适配新任务只是在通用底子上做一个小角度的偏转——不需要重塑整个大脑,只需要一个低秩的「微调补丁」。
以一个 4096×4096 的权重矩阵为例。点不同的秩 r,看可训练参数、checkpoint 大小、质量怎么变——你会看到秩极小时省得惊人、质量却几乎不掉。
看出门道了吗:从 r=8 到全量,质量几乎没差,但参数量差了 200 多倍。这就是「内在秩很低」的实证——花极小代价,买到接近全量微调的效果。
| 维度 | 全量微调 | LoRA |
|---|---|---|
| 动多少参数 | 全部(如 175B) | 约万分之一(只训 A、B) |
| 显存 | 极高(要存所有梯度+优化器状态) | 大降(~3 倍以上节省) |
| 每任务 checkpoint | 一整份模型(GB 级) | 一个小补丁(MB 级) |
| 推理延迟 | 正常 | 零额外延迟(合并回 W) |
| 多任务服务 | 每任务一份大模型,笨重 | 一底座 + 热插拔小补丁 |
| 质量 | 基准 | 逼近、常持平 |
把 LoRA 放回你 GPT-3 课学的那条「动权重多少」的轴上,它正好补上中间那个「便宜的微调」档:
| 方式 | 动权重吗 | 成本 / 特点 |
|---|---|---|
| 全量微调 | 改全部,永久 | 贵、每任务一整份 |
| LoRA | 只改低秩补丁,永久 | 便宜、补丁 MB 级、可热插拔 |
| in-context learning | 不改,临时 | 零训练,但每次推理带例子、占 token |
🔮 LoRA 之上长出一整片生态:QLoRA(把底座量化压缩 + LoRA,让你在一张消费级显卡上微调几十 B 的模型)、PEFT(参数高效微调)成为默认范式。产品形态上,它直接催生了「一个冻结底座 + 给每个客户/角色/任务挂一个小 LoRA 补丁」——这正是你做「一套模型服务多场景」时最该想到的架构。
| 你工作里的东西 | 其实就是这篇论文的什么 |
|---|---|
| 你想给某个垂直场景/客户「定制」一个模型,又怕成本爆炸 | LoRA:冻底座、只训低秩补丁——定制成本砍上万倍,补丁 MB 级,想要多少个就存多少个 |
| 「一套模型怎么同时服务很多不同任务/客户」 | 就是 一底座 + 热插拔 LoRA 的架构:底座只存一份,每个场景一个小补丁,按需切换 |
| 你纠结「这个需求该 few-shot 还是去微调」 | 现在轴上多了一档:LoRA = 便宜的永久微调。高频稳定且 few-shot 压不住 → 上 LoRA,比全量微调省太多 |
| 你听到「我们在消费级 GPU 上微调了一个 13B 模型」 | 大概率是 QLoRA——量化 + LoRA 把门槛打到一张显卡,这就是「人人都能微调」的技术底 |
| 你想做「音色定制 / 角色定制」这类低成本批量定制 | 同一思路:一个底座 + 每个声音/角色一个小补丁,而不是为每个都全量训一份 |
把这一课接到你真实工作上的几个关键问。点开看答。
不是越大越好,而是「够用就行」。r 是「补丁的容量」:太小可能装不下复杂任务的适配,太大则参数变多、向全量微调退化、还可能过拟合。论文里 r=1~8 在很多任务上就已逼近全量——因为内在秩本就低。
实操经验:从 r=8 起步,任务简单可降到 4/2,效果不够再往上调到 16/32。它是你要调的主旋钮,但调的空间通常很小——这本身就是「内在秩低」的体现。
两种用法,按需选:① 合并模式——把某个 LoRA 永久并进 W,得到一个专用模型,推理零延迟,但就固定成这一个任务了;② 不合并模式——保持 W 冻结、补丁单独挂着,推理时临时加上 B·A。这样同一个底座可以同时挂不同补丁、按请求切换,代价是这点旁路计算(很小)。
产品上常这么用:底座只存一份,每个客户/角色一个 MB 级补丁,请求来了挂对应的那个。要极致单任务性能就合并,要多租户灵活就不合并——两头都给你了。
它们解决不同的事,常常组合用:
| 改什么 | 适合 | |
|---|---|---|
| few-shot | 不改权重,临时给例子 | 任务多变、量小、要快试 |
| LoRA | 改权重(低秩补丁),永久 | 固定风格/技能/格式,高频稳定,要省推理 token |
| RAG | 不改权重,外挂知识 | 需要最新/私有事实,知识常变 |
一句话区分:要它「换个说话方式/学个技能」→ LoRA;要它「知道某些具体事实」→ RAG;要它「这次照这几个例子办」→ few-shot。RAG 是后面 P19 的正课,到时再深挖。
因为 W 不是低秩的,ΔW 才是。W(预训练权重)高秩、信息致密,装着海量通用知识——直接 W≈B·A 会把知识压扁、大量丢失,模型废掉。而适配新任务所需的「改变量」ΔW 内在秩很低,用低秩拟合它几乎不丢东西。
比方:W 是厚百科全书(不能压),ΔW 是薄勘误便利贴(就这任务微调几处)。LoRA 只用低秩去拟合那张便利贴。附带还白赚:W 冻结 → 不灾难性遗忘;W 原样保留 → 才能合并/卸载、一底座挂多补丁。
标准做法:A = 小随机高斯,B = 全 0,于是开训那刻 ΔW = B·A = 0。必须从 0 起步,是因为要从「一模一样的预训练模型」开始——补丁若一上来随机非零,等于往调好的模型注入噪声,前几步全在撤销自己的破坏。从 0 起步则第 0 步=纯预训练行为,补丁只朝有用方向慢慢长出来。
为什么不两片都设 0?会梯度死锁:∂/∂A 依赖 B、∂/∂B 依赖 A,全 0 则梯度全 0、学不动。所以一片随机(打破对称、留梯度通路)、一片归零(保证乘积为 0)。另有 α/r 缩放因子控更新幅度,次要。
能挂很多,看怎么用,风险递增:
| 用法 | 怎么做 | 会打架吗 |
|---|---|---|
| 切换(热插拔) | 每请求用「底座+一个补丁」 | 不会,干净常规做法 |
| 合并 | 把一个补丁永久并进 W | 不会,但固定成单任务 |
| 同时叠加 | W+ΔW₁+ΔW₂… 一起上 | 可能打架 |
叠加会冲突,是因为低秩更新线性相加、而各补丁没学过共存,可能把权重往互相矛盾的方向拽。缓解:加权组合、约束正交、或路由(Mixture-of-LoRAs)。实操标准姿势是「一请求一补丁」(零冲突);同时叠多个是要小心的高级玩法(类比文生图社区混多个风格 LoRA、堆多了就糊)。服务侧有 S-LoRA 等方案:底座只放一份,成千上万补丁高效换进换出。
读原典:Hu et al. — LoRA: Low-Rank Adaptation of Large Language Models(2021)。
想看它怎么把门槛打到一张显卡:搜 QLoRA(Dettmers et al., 2023,量化 + LoRA)。
阶段三进入「效率」段:LoRA 省微调成本。下一篇 P13 FlashAttention:省的是推理/训练时 attention 的显存与速度——直接对应实时语音等低延迟场景里的长上下文与延迟问题。
参考:
Hu et al., LoRA: Low-Rank Adaptation of Large Language Models, 2021.