第 12 篇 · 第三部分 · 大语言模型是怎么长出来的
LoRA:大模型的低成本微调
全量微调成本很高——一个 7B 模型若要重新训练一遍,仅优化器状态就足以占满一张 80GB 显卡的显存。但实验中有一个耐人寻味的现象:微调真正改动的那些部分,维度低得出奇。既然改动是"低秩"的,就没有必要连主干一起训练。LoRA 正是顺着这条线索做出来的:主干冻结,只在旁边附加两个小矩阵参与训练。
一、先算一笔全量微调的账(拿 7B 模型举例)
训练一个 7B 参数的模型,显存主要被四项内容占用:
- 模型权重:7B 参数用 fp16 存,约 14 GB。
- 梯度:和权重一一对应,又是约 14 GB。
- 优化器状态:Adam 要为每个参数存一阶、二阶动量,用 fp32 就是 2 × 4 字节 × 70 亿,约 56 GB。
- 激活值:前向过程要留中间结果给反向用,批次越大、序列越长,这块越膨胀。
前三项加起来已经过了 80 GB。也就是说,一张 A100 80GB 连权重加优化器都容纳不下,需要依靠多卡切分(ZeRO、张量并行等方法)才能开始训练。再算上实验要反复进行,全量微调的门槛直接把中小团队挡在门外。
再往细里说,激活值这一项尤其容易被低估,它随批次大小和序列长度增长得很快。批次设成 8、序列长度 2048,激活值可能就十几 GB;序列长度拉到 8192,会按比例成倍增长。很多团队第一次训练 7B 时受挫,原因不是权重容纳不下,而是激活值先占满显存。
二、一个关键观察:微调带来的变化是低秩的
关键在于,微调并不需要把每个参数都改一遍。Aghajanyan 等人 2020 年测过一个很有启发性的量:把权重更新 ΔW 投影到一个低维子空间里再训练,只要子空间维度有几千,效果就能追平甚至超过全量微调。他们管这个叫"内在维度"。
换成更直接的说法:模型早已掌握整套语言能力,微调只是在原有能力上做一点点方向上的偏移。这张偏移表看起来很宽(比如 4096 × 4096),实则被少数几个方向主导着,用一个很小的矩阵就能近似出来。这是 LoRA 能够成立的前提。
更值得琢磨的是,这个"内在维度"跟任务有关。任务越接近模型预训练时见过的东西,需要的维度越低——改个回答风格、加些领域术语,几千维就已足够;让它学一门全新技能,需要的维度会明显变大。LoRA 的秩 r,本质就是在给这个内在维度留余量。
三、LoRA 的做法:主干冻结,旁挂两个小矩阵
LoRA(Low-Rank Adaptation,低秩适配)的动作很直接。原来一层里有个大权重矩阵 W,尺寸 d × k。它不去动 W,而是保持 W 原样冻结,在旁边并联两个小矩阵 A 和 B:
- A 负责把输入从 k 维压到 r 维(r 就是秩,通常取 8 或 16);
- B 再把 r 维升回 d 维;
- 前向时,原来的 Wx 加上旁路的 BAx,一起输出。
初始化有个讲究:A 用高斯随机小值,B 全零。这样一开始 BA 乘积是零,旁路不干扰主干,模型从"和原模型一模一样"的状态起步,训练不会因为突然出现的噪声而崩溃。
参数上,因为 B 从零起步,一开始乘积 BA 是零矩阵;训练开始之后 B 逐渐被填入数值,旁路才开始起作用。这个"从零长出来"的过程,让 LoRA 的训练曲线通常比从头训练一个新模块平稳得多。
数学上,这件事可以看成给 W 加了一个低秩的修正项,训练完把 BA 加回 W,模型就是一份普通权重,推理时不留任何额外开销。
四、秩 r、alpha 是什么,秩分解的直观含义
秩 r 决定这两个小矩阵有多"宽"。它的含义可以这样理解:用几道菜来概括一整桌满汉全席。r 越大,概括得越细,能表达的变化越复杂,但参数也越多;r 小则参数更省,但可能覆盖不了需要调整的方向。
r 的常用取值是 8 或 16。耐人寻味的是,不少任务里把 r 从 8 提到 64,效果提升并不明显——这又反过来印证了"改动是低秩的"这个前提。r 设到几百,可训练参数会接近全量微调的量级,省显存的意义也就基本消失。
alpha 是缩放系数。真正生效的更新是 (alpha / r) × BA。它的作用是让 A、B 里数值的尺度不随 r 变来变去:r 调大了,除以 r 就把幅度抵消掉,学习率不用随之重调。实践里有一条常用的经验——把 alpha 设成 r 的两倍,比如 r = 16 配 alpha = 32,多数情况的起点都不会太差。
反过来,如果任务本身离基座很近,r 和 alpha 都能往小里设;任务跨度大,再考虑把 r 抬高一些。很少有人能一次定准,稳妥的办法是拿一个小验证集,把 r 在 8、16、32 这几档上各训练一遍,看哪个收敛得又快又稳。
五、数学就一行:W + BA
把上面几条综合起来,LoRA 的全部公式就一行:
h = W x + (alpha / r) · B A x 其中 W 冻结梯度只流给 A 和 B。原始权重 W 从头到尾不更新,也就没有对应它的梯度和优化器状态——显存省下来的正是这一大块。用一个不那么精确但好记的比方:W 是一本已经印好的大部头,LoRA 只是贴上去的几页便签,翻的时候便签的内容会叠加上去,但原书一个字没改。
参数量上更直观。一层 4096 × 4096 的大矩阵有约 1677 万个参数;换成 r = 8 的旁路,A 是 8 × 4096、B 是 4096 × 8,加起来约 6.5 万个,只是原来的 0.4%。这还只是一层。放到整个 7B 模型上,可训练参数通常也就几百万到几千万,占总数的百分之一以下。
六、只训旁路的好处:显存、切换、部署
省显存只是最表面的好处,实际用起来还有几个隐性红利:
- 能同时挂多个任务:一个基座配十几个 LoRA 适配器,每个只占几十 MB。想切换任务,换适配器即可,不用把整个模型重新加载一遍。
- 部署成本低:训练完把 BA 合并进 W,导出成普通权重,推理路径和原生模型一模一样,没有额外延迟。
- 不容易影响主干:因为改动被限制在低秩旁路上,灾难性遗忘的风险比全量微调小不少。
- 分发方便:别人拿几 MB 的适配器文件就能复现你的微调效果,模型权重本身不用重发。
有一点容易被误解:LoRA 省的是训练时的显存。推理时如果选择合并权重,省不了推理显存;保留旁路不合并,反而会多一些计算开销。它真正的收益集中在"训练"和"分发"这两端,而不是推理速度。
所以现在很多开源模型社区的分发方式是"一个大基座 + 一组小适配器",LoRA 这套做法功不可没。
七、QLoRA:4bit 基座配 LoRA
LoRA 把优化器和梯度省了,可基座权重本身还是 fp16,7B 也要 14 GB 显存。QLoRA(Dettmers 等,2023)的思路是再往前走一步:把冻结的基座量化到 4bit 存储,用的时候临时反量化去算,LoRA 旁路则保持在 16bit 精度上训练。
它配了几个技巧:一个是 NF4 这种对正态分布友好的 4bit 量化格式;一个叫双重量化,把量化系数本身再压缩一层;还有分页优化器,一旦显存出现尖峰就把优化器状态转移到 CPU 内存,避免 OOM。这些方法叠加之后,7B 模型的微调显存需求能从 80 GB 级降到 24 GB 以下,一张消费级显卡即可运行。
数值上略微的精度损失,换来的是训练门槛从不可及变为可及。对绝大多数个人和中小团队来说,QLoRA 是能否开展微调的分界线。
八、实操要点清单(target_modules、学习率、合并权重)
实际开始训练时,有几个参数最需要确定:
- target_modules:决定给哪些层挂旁路。只加注意力的 q、k、v、o 投影是最常见的最小配置;再加上 MLP 里的 gate、up、down,效果通常更好,代价是参数多一些。
- learning_rate:LoRA 的学习率一般比全量微调高一个数量级,常用 1e-4 到 3e-4。太低则收敛过慢,太高则直接发散。
- lora_dropout:小数据集上开 0.05 到 0.1,防过拟合。
- 合并权重:训练完用 merge_and_unload 之类的方法把 BA 加回 W,得到一份可以直接部署的普通权重;想保留切换能力,就单独存适配器。
- 批次与序列长度:这两个直接决定激活值显存,是训练显存不足时最先该调整的。
顺序上也有讲究。一般先定 target_modules 和秩,再调学习率,批次与序列长度这些留到前两项确定之后再调整。一次只改一个变量,否则训练失败时无法判断是哪一项导致的——LoRA 可调的参数比全量微调还多,这一点尤其要小心。
工程上,Hugging Face 的 PEFT 库把这些流程封装得比较完善,微调脚本改几行配置就能从全量微调切换为 LoRA;微软官方的 LoRA 仓库则适合对照原理阅读。
九、它不适合做什么
LoRA 是低成本的方案,但不是万能的,有几种情况需要另想办法:
- 需要大量注入新知识:比如让模型掌握一门全新的、它完全没接触过的方言或领域。低秩的旁路容纳不了这么大的信息量,可能需要继续预训练或全量微调。
- 基座本身不够强:LoRA 是在已有能力上做偏移。基座不会的东西,靠一个小旁路也无法习得,换更强的基座往往比强行微调更划算。
- 需要彻底改变模型的根本行为:当改动幅度远超"低秩"能表达的范围时,全量微调的必要性就凸显出来。
还有一种情况容易被忽略:基座模型本身已经被对齐得很稳,再叠加一层 LoRA,两边的改动方向可能相互冲突,训出来的模型顾此失彼。这时候先做一次评估,衡量一下基座在你的任务上到底差多少,比一开始就直接添加适配器更稳妥。
总结来说:LoRA 押注的是"微调只改动很少的方向"这个经验事实。它成本低、易于切换、便于分发,把微调从大机构专属降低到个人电脑可用的程度——但它改变的始终是模型的"口音",不是它的"母语"。
参考资料
- Edward J. Hu 等, LoRA: Low-Rank Adaptation of Large Language Models, ICLR 2022. arXiv:2106.09685(LoRA 的原始论文)
- Tim Dettmers 等, QLoRA: Efficient Finetuning of Quantized LLMs, NeurIPS 2023. arXiv:2305.14314(4bit 量化加 LoRA)
- Armen Aghajanyan, Luke Zettlemoyer, Sonal Gupta, Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning, ACL 2021. arXiv:2012.13255(低秩观察的来源)
- Microsoft, LoRA 官方代码仓库. github.com/microsoft/LoRA(原始实现)
- Hugging Face, PEFT: Parameter-Efficient Fine-Tuning. github.com/huggingface/peft(工程封装,上手最快)