768×768 的权重矩阵,全量微调 589824 个参数; LoRA 秩取 8 只要 12288 个,占 2.08%,省 48 倍。 放大到 128 个这样的矩阵:75.5M 降到 1.6M,存成文件 144 MB 降到 3 MB。
前面六节讲的是架构。从这一节起是本章的实战部分:用 LoRA 训练一个特定画风的模型。 五节对应五个步骤。
一、LoRA 就一句话
原权重 W 冻住,一个数都不改。 旁边加一条旁路:先用一个瘦矩阵 A 把输入降到低维,再用一个胖矩阵 B 升回原来的维度。 前向时走两路:原来那一路照旧,旁路算出来的结果乘一个系数加回去。要训练的只有 A 和 B。 数学上就是 W 新值 = W + (alpha / r) × B × A。其中 r 是低维的维度(叫秩),alpha 是缩放系数。 为什么这算「低秩」?因为 A 和 B 相乘得到的那个矩阵,秩最多是 r。 也就是说我们只允许在原权重上加一个「结构很简单」的修正。
二、省多少:一个矩阵的账
768×768 的矩阵有 589824 个参数。 LoRA 一条旁路是 d×r 加 r×d,也就是 2 × 768 × r。 r 取 8 时是 12288,占原来的 2.08%,省 48 倍。 r 取 1 只占 0.26%,取 64 占 16.67%。这个比例和 r 成正比——r 翻一倍,参数量也翻一倍。 所以 r 是 LoRA 最核心的那个旋钮:r 小,学得浅、文件小、不容易过拟合; r 大,学得多、文件大、容易把训练集背下来。
三、放大到整个模型
一个模型不是一个矩阵。以 SD 1.5 为例,注意力层里 768×768 这种矩阵有上百个。我按 128 个算: 全量微调 75.5M 个参数,LoRA 只要 1.6M。按 fp16 存到磁盘,是 144 MB 对 3 MB。 这就是为什么一张 8 GB 的消费级显卡也能训 LoRA。 要更新的参数少五十倍,优化器状态(Adam 里的 m 和 v,每个参数两份)也跟着少五十倍。 显存大头就是这么省下来的。 注意 128 这个数是我按常见结构估的,不是精确统计。 真实模型里还有 320 维、1280 维的层,数量也不一样。但量级是对的。
四、代价是什么
ΔW 被强制压成秩不超过 r 的矩阵。这意味着它只能表达「方向很集中的那种变化」。 实践上它擅长学风格、质感、构图习惯这类整体偏移——这些东西确实可以由少数几个方向描述。 它学不了凭空多认识一种从没见过的结构。 比如你想让模型学会画一种全新的透视,秩太低撑不起来,只能加大 r 或者干脆全量微调。 这也解释了一条实践经验:r 调到 64 以上收益就开始不明显。 再往上加,不如直接全量微调——反正省下来的已经不多,不如把表达能力拿满。
五、动手 Demo:LoRA 的账:秩取 8 只占 2.08%,模型文件从 144 MB 降到 3 MB
按 768×768 的权重矩阵,算秩从 1 到 64 时 LoRA 的参数量和占比; 再假设模型里有 128 个这样的矩阵,算全量微调和 LoRA 的总参数量、以及按 fp16 存盘的体积差。
for r in (1, 2, 4, 8, 16, 32, 64):
n = d * r + r * d # A 是 r×d,B 是 d×r
print(r, n, 100.0 * n / (d * d))
full = 128 * d * d # 全量微调
lora = 128 * 2 * d * 8 # LoRA r=8
实跑输出:
--------------------------------------------------------------------
LoRA 到底省多少:低秩分解的参数量账
--------------------------------------------------------------------
原始权重矩阵 768 × 768
全量微调要训的参数 589824
LoRA 的做法 W 冻住不动,旁边加一条 B × A 的低秩旁路,只训 A 和 B
秩 r LoRA 参数量 省下的参数量 占比 省几倍
----------------------------------------------------
1 1536 588288 0.26% 384.0 倍
2 3072 586752 0.52% 192.0 倍
4 6144 583680 1.04% 96.0 倍
8 12288 577536 2.08% 48.0 倍
16 24576 565248 4.17% 24.0 倍
32 49152 540672 8.33% 12.0 倍
64 98304 491520 16.67% 6.0 倍
一条旁路的参数量是 2 × d × r。r 取 8 时是 12288,占原矩阵的 2.08%。
但真实模型不是一个矩阵。以常见的 SD 1.5 UNet 为例,注意力层里这样的 768 × 768 矩阵有上百个:
假设这样的矩阵有 128 个(只算注意力里的投影层)
全量微调 75.5 M 个参数
LoRA r=8 1.6 M 个参数
存成文件差多少 144 MB 对 3 MB(fp16)
这张表解释了 LoRA 为什么能在一个消费级显卡上跑:要更新的参数少了两个数量级,优化器状态(Adam 里的 m 和 v)也跟着少两个数量级,显存大头就这么省下来了。
代价是表达能力受限:ΔW 被强制压成秩不超过 r 的矩阵,所以它擅长学「风格、质感、构图习惯」这类整体偏移,学不了「凭空多认识一种从没见过的结构」。
这也解释了实践中的经验:r 调到 64 以上收益就开始不明显,再大不如直接全量微调。
表里那一列「占比」是关键:r=8 是 2.08%,r=64 是 16.67%。 所以「LoRA 一定很省」这个说法要加个前提——秩不能太大。 128 个矩阵那一档:75.5M 对 1.6M。少了五十倍,而不是「少了两个数量级」。 注意这个措辞差别,五十倍是两个数量级的一半。 存盘体积 144 MB 对 3 MB。 这也解释了为什么 Civitai 上一张 LoRA 通常几 MB 到一百多 MB,而底座模型动辄 2 GB 起。 128 这个数是我估的。真实的 SD 1.5 UNet 大约 8.6 亿参数,注意力层的投影矩阵远不止 128 个。 所以 144 MB 这个数字偏小,只能当量级参考,不能当实际文件大小看。
30 秒实验
- 把秩改成 128,看占比变成多少——这时还叫「省」吗?
- 算一下 r=8 的 LoRA 在 8 GB 显存上训练,光权重和优化器状态占多少。
- 想一想:为什么 LoRA 只加在注意力层,不加在全部层上?
常见误区
- 「LoRA 参数量很少所以一定快」——前向时旁路还是要算,只是参数量少。
- 「秩越大效果越好」——超过 64 收益递减,还更容易过拟合训练集。
- 「LoRA 能学会任何东西」——低秩限制了表达能力,学不了结构性新知识。
自测三问(点开对答案)
1. LoRA 的核心做法是什么? —— 冻住原权重,旁路加一对低秩矩阵 A 和 B,只训练这两个。 2. 一条旁路的参数量是多少? —— 2 乘 d 乘 r,d 是原矩阵维度,r 是秩。 3. 秩 r 的作用是什么? —— 控制旁路的表达能力:r 小省参数但学得浅,r 大表达强但容易过拟合。原理清楚了。下一步是动手之前的事:装训练器、整理数据集。
- ☐ (本节完,下一节 8.3.2 准备工作:安装训练器与整理数据集)