我最早用 LoRA 调一个 7B 模型的时候,踩过一个很实在的坑:把 rank 从 4 调到 8,效果明显变好;调到 16,又好了一点;调到 32,几乎没变化,但训练时间肉眼可见地变长。那时我冒出一个念头——既然 rank 越大效果越好,那为什么不干脆把 rank 设成 4096?或者干脆全参微调?

这个问题困扰了我很久。直到我真正把 LoRA 的数学形式掰开揉碎看了一遍,才明白 rank 不是一个可以无限调大的旋钮,它背后藏着一个关于「表达空间」和「优化路径」的权衡。
LoRA 到底做了什么?一句话:把权重更新限制在一个低秩子空间里
全参微调时,模型要学习的增量是 ΔW,一个 d×d 的矩阵,每个元素独立变化。LoRA 的想法是:ΔW 不一定需要那么多自由度,它可以被分解成两个小矩阵的乘积:
ΔW = B × A
其中 B 是 d×r,A 是 r×d,r 就是 rank。训练时只更新 B 和 A,原来的权重 W 冻住不动。
用画面感的话说:全参微调是允许城市里每一条街道都重新规划,LoRA 只允许你在几条主干道上做调整。r 就是主干道的条数。条数越少,你能改动的地方越少,但每条改动都会影响一大片区域。
这意味着两件事。第一,可训练参数量从 d² 降到 2dr。在 7B 模型上,d 通常是 4096,r=16 时 LoRA 只训练约 1600 万个参数,不到全参的 0.3%。第二,ΔW 的秩最大只能是 r——你无论怎么组合 B 和 A,乘积矩阵的秩都不会超过 r。这就是低秩约束的本质。
rank 小了效果差,是因为表达空间真的不够
假设任务需要的真实权重变化 ΔW* 的秩是 32,你只给 r=4,那无论怎么训练,你都无法精确表示 ΔW*。就像你想画一幅 32 种颜色的画,手里只有 4 支笔——你可以混色,但混出来的永远不是原色。
所以 rank 小效果差,往往是欠拟合。模型学不到足够复杂的任务相关表示。这个道理很直白,但真正有意思的是反过来的现象:在某些任务上,rank=1 的效果竟然接近 rank=64。
LoRA 原始论文里就做过这个实验。在 GPT-3 175B 上,他们把 rank 从 1 调到 64,发现几个下游任务的性能几乎不随 rank 变化,rank=1 和 rank=64 的差距小到可以忽略。作者因此推测:预训练模型本身已经学到了足够好的特征,微调时需要的权重更新是高度低秩的,所以很小的 r 就够用。
这解释了我之前的一个困惑:为什么 rank=4 在某些任务上比 rank=32 还好?因为低秩本身是一种正则化。rank 越小,模型的假设空间越窄,越不容易在少量数据上过拟合。当你的训练数据只有几千条时,大 rank 的 LoRA 会拼命去记住训练集的噪声,而小 rank 的 LoRA 反而学到更泛化的模式。
关键判断:rank 小是欠拟合,rank 大是过拟合,中间有一个甜点。但甜点的位置取决于任务复杂度和数据量,没有统一答案。
rank 大了跟全参微调有什么区别?区别比你想的大得多
一个常见的直觉是:rank 越大,LoRA 越接近全参微调。这个直觉只对了一半。即使你把 r 设成 d,LoRA 也不会等价于全参微调。原因有三个。
第一,参数化方式不同,优化轨迹完全不同。全参微调直接更新 ΔW 的每一个元素,梯度是稠密的。LoRA 把 ΔW 分解成 B×A,梯度要分别对 B 和 A 求,而 B 和 A 的梯度会互相影响。这导致即便两个方法最终能到达同一个最优解,它们走的路径也不一样,最终收敛点大概率不同。一个直观的类比:你要从北京到上海,全参微调是直线开车,LoRA 是必须经过某个中转站——就算终点一样,路线不同,油耗也不同。
第二,初始化方式不同。LoRA 的 B 初始化为零矩阵,A 初始化为随机高斯。所以训练开始时 ΔW=0,模型完全保持预训练行为。全参微调则是从预训练权重出发,一开始就有完整信息。LoRA 这种「从零开始学增量」的方式,天然限制了它对预训练知识的破坏程度。2024 年那篇《LoRA Learns Less and Forgets Less》的论文就发现:低 rank 的 LoRA 在目标任务上学得少,但对预训练知识的遗忘也更少;而高 rank 的 LoRA 学得多,遗忘也更多。全参微调在两者之间的平衡点和你选 r 的位置并不完全一致。
第三,参数空间的拓扑结构不同。全参微调的解是 d×d 空间里的任意一点。LoRA 的解被限制在「所有秩不超过 r 的矩阵」组成的流形上。这个流形虽然包含很多点,但它不是整个空间。r 越大,流形越接近全空间,但永远不会等于——除非 r=d,而 r=d 时 LoRA 的参数量是 2d²,比全参微调的 d² 还多一倍,没有任何实用意义。
说人话就是:rank 大了,LoRA 只是在低秩流形里找到一个「尽量接近全参最优解」的点,而不是全参最优解本身。
| 对比维度 | rank 小(如 4-8) | rank 中(如 16-32) | rank 大(如 64+) | 全参微调 |
|---|---|---|---|---|
| 表达能力 | 弱,仅适合简单任务 | 中等,覆盖多数场景 | 强,接近全参 | 最强 |
| 过拟合风险 | 低 | 中 | 高 | 很高(尤其数据少时) |
| 可训练参数量 | 极小(百万级) | 小(千万级) | 中(千万到亿级) | 全部(数十亿级) |
| 对预训练知识的遗忘 | 少 | 中 | 多 | 视训练策略而定 |
| 推理时开销 | LoRA 可合并进原权重,推理零额外开销 | 使用完整新权重,无额外开销 | ||
那 rank 到底怎么选?我的建议是:从 8 或 16 开始,根据曲线调
没有银弹,但有一套可操作的方法。以下是我踩坑后总结的流程:
- 先用 rank=8 或 16 跑一个短实验,看验证集 loss 下降情况。如果 loss 在训练后期仍然明显下降,说明模型还没学够,可以加大 rank。
- 如果训练集 loss 降得很快,但验证集 loss 回升,说明过拟合了,减小 rank 或加正则。
- 如果 rank 从 16 加到 32,性能提升不超过 1%,那就用 16。不要为那 1% 付出双倍训练时间。
- 如果你在调一个超大规模模型(70B+),尝试 rank=4 或 8,往往有惊喜。LoRA 论文在 GPT-3 175B 上的实验已经证明,大模型的任务适应性往往只需要极低秩。
另外,别忘了 rank 和 α(缩放系数)的关系。LoRA 的缩放是 α/r,所以当你调大 rank 时,通常也要相应调大 α。很多框架默认 α 等于 rank,这不是巧合——它在尝试保持缩放比例一致。
进阶:为什么 α/r 的缩放这么重要?
LoRA 的更新是 α/r × BA。如果不缩放,当 r 增大时,BA 的初始量级会随 r 的累计和增大,导致训练不稳定。除以 r 可以保持初始输出的量级一致。所以你在调 rank 的时候,最好同步调整 α,否则之前找到的学习率可能不再适用。
几个常见问题
rank=64 比 rank=16 一定更好吗?
不一定。如果你的任务本身是低秩的,64 只是浪费显存和算力。但如果任务很复杂(比如让模型学会新的代码格式),64 可能确实需要。看验证集曲线,不要凭感觉。
LoRA 能完全替代全参微调吗?
在多数场景下可以,但不是因为性能等价,而是因为性价比。全参微调需要 7 份显存,LoRA 可能只需要 1.5 份。而且 LoRA 可以同时训练多个任务,推理时动态切换。如果你的任务对性能要求极致,且数据量充足,全参微调仍然是上限更高的方案。
如果我的任务需要学习全新的知识领域,LoRA 还够用吗?
这正是 LoRA 的短板。低秩假设成立的前提是:预训练模型已经掌握了相关知识,微调只是「唤醒」和「重定向」。如果你让一个中文模型去学一门全新的编程语言,预训练知识中完全没有对应表示,LoRA 的秩再高也可能不够。这种情况下,考虑继续预训练或全参微调。
回到我最初的问题——rank 大了跟全参微调有什么区别?现在我可以明确回答:区别在于「你在哪个空间里寻找答案」。LoRA 把搜索空间限制在低秩流形上,这个限制既带来了计算效率,也带来了正则化收益,但代价是永远无法到达全参微调的解。理解这一点,你就不再会纠结「为什么我的 rank 设了 128 还是不如全参微调」——因为问题的关键不是秩的大小,而是模型对任务的先验假设是否成立。
所以我的最终建议是:别把时间花在纠结 rank 的绝对值上。先跑起来,看曲线,感受你的任务到底需要多少自由度。你调过的每一个 r,都在帮你估计任务的内在维度——这比任何理论推荐值都可靠。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/240.html