笔记日期: 2026-08-21 作者: Zhongzhu Zhou 论文标题: CARE-LoRA: Compressed Activation REconstruction for Memory-Efficient LoRA 论文作者: Gengyu Zhang, Haiyin Ran, Zhengbao He, Yuhang Liu, Hanling Tian, Zhehao Huang, Xiaolin Huang(上海交通大学) arXiv: 2607.11940 发表情况: 预印本(cs.LG),2026 年 7 月
1. 这篇论文到底在解决什么问题
LoRA(Low-Rank Adaptation)已经把微调大模型的”参数内存”问题解决得很好了:冻结预训练权重 ,只训练一对很小的低秩矩阵 、,其中 通常只有 8、16 这种量级,比隐藏维度 小几十上百倍。这意味着梯度和优化器状态(Adam 需要为每个可训练参数额外存两份浮点数:一阶矩和二阶矩)这两块内存开销都随之大幅下降。
但这里藏着一个容易被忽略的”剩余”瓶颈:激活内存。每一个被 LoRA 包裹的线性层,在反向传播真正走到它之前,都得把输入激活 ( 是 batch size × 序列长度, 是隐藏宽度)原样存着——因为 LoRA 下投影矩阵 的精确梯度是
这里 是上游梯度。只要这个公式里还需要完整的 ,激活内存就依然是 ——跟全参数微调是一个数量级,跟你把 LoRA 的秩 调得多小完全无关。这一块,LoRA 从来没真正解决过。
已有的解法叫 LoRA-FA,做法很直接:把 冻结在初始化的位置,只训练 。 一旦冻结,就再也不需要 ,也就不需要完整的 ——只需要存下压缩后的激活 ,比原来小 倍。这是个实打实的内存优化,但代价也是实打实的:冻结 就等于冻结了整个低秩更新 所能存在的子空间,而且是冻结整个训练过程。 的每一列都是 各列的线性组合, 一旦固定, 就永远成立,不管 怎么训练都无法逃出这个子空间。如果那个随机初始化出来的子空间恰好没覆盖到任务真正需要的方向,LoRA-FA 就是结构性地学不到——论文自己的实验数字也证实了这一点:LoRA-FA 相对标准 LoRA 在 GLUE 上掉了 4.25 分,SuperGLUE 上掉了 7.37 分,CoLA 上更是暴跌了将近 14 分,CB 上暴跌了约 12 分。
CARE-LoRA 的核心主张是:这个”内存换精度”的交易根本没必要做。你完全可以让 、 都继续训练,同时依然扔掉完整的 ,内存账单和 LoRA-FA 差不多——办法是从你反正都要算的压缩激活 出发,重建出一份 的近似值。这个机制本身很朴素(每次前向传播算一次最小二乘投影),但论文给出了一个真正意义上的理论论证,说明这样重建出来的梯度依然能让 的子空间”动起来”——这恰恰是 LoRA-FA 缺失的性质。下面我会把推导、内存/算力账目和实验都过一遍,最后给出我自己的独立评估:哪些地方站得住脚,哪些地方还留着真问题没回答。
1.1 本文阅读建议
如果你已经熟悉 LoRA 的前向/反向公式、最小二乘拟合的基本形式,可以直接跳到第 3 节看方法本身;如果你想先把相关背景知识补齐,第 2 节会从头把微调内存的四大块头、LoRA 的精确前向/反向公式、以及已有激活内存技巧的版图都讲一遍。第 4 节是内存/算力开销的完整推导,比较数学化,赶时间的读者可以先跳到第 5 节的实验部分。第 6–9 节是局限性、批判性分析和实践落地建议,是本篇笔记比论文原文额外提供的部分。
2. 前置知识
2.1 微调到底在为什么东西付内存
一个 transformer 线性层的训练内存可以拆成四块:
- 模型参数——权重矩阵 本身。
- 梯度——每个可训练参数对应一份梯度。
- 优化器状态——Adam 类优化器要为每个可训练参数额外存两份浮点数(一阶矩、二阶矩估计),这一块往往是全参数微调里最大的单项开销。
- 激活——每个参与链式法则的层的输入张量,都得一直留着(或者反向时重新算一遍),直到对应的反向传播用到它为止。
全参数微调这四块都要按全部参数的规模付费。像 LoRA 这样的参数高效微调(PEFT)方法直接砍掉了第 1–3 块:冻结 ,引入一对很小的低秩矩阵 (),梯度和优化器状态只需要为 保留——参数量能压缩好几个数量级。
第 4 块却纹丝不动。激活内存不随”可训练参数量”缩放,它随 (batch size × 序列长度)和层的宽度(也就是线性层的 、)缩放——不管这个层里有多少参数可训练都一样。一旦 PEFT 把参数/梯度/优化器状态这三块大幅削减,激活内存自然就变成剩下最重的那块,尤其是在现代微调常见的长序列、大 batch 场景下更明显。这个”PEFT 处理完参数侧之后,激活内存才是真正瓶颈”的重新框定,正是这篇论文的出发点,也是理解后面方法设计的正确视角。
2.2 精确写出 LoRA 的前向/反向传播
对于一个冻结预训练权重为 的线性层,令 是输入激活。LoRA 引入 、,计算
(论文省略了通常的 LoRA 缩放因子 ,因为它可以被吸收进 里而不影响推导——这是标准简化,不是真的省略了什么。)
记上游梯度为 ,对式 (1) 反向传播能得到三个梯度:
从这三个式子里直接能读出两个结构性事实,后面所有内容都建立在这两个事实之上。
事实一—— 只需要 ,不需要 。 式 (2) 可以用 重写为 。既然 ,如果你只想更新 ,那么一旦算出 就可以立刻扔掉 ,完全不会有任何损失。这正是 LoRA-FA 利用的机制。
事实二—— 是真的需要完整的 。 式 (3) 里的 (宽度是完整的 )直接乘在 上。式 (3) 没有任何只用 就能精确改写的办法—— 是一次有损压缩,压缩之后一般无法完美逆推回去,所以没法从 单独精确恢复出 。这才是真正的障碍:如果你想继续训练 ,标准反向传播就要求你把整个 留着。
LoRA-FA 的做法是让事实二根本不成立——干脆不训练 。CARE-LoRA 的做法是承认式 (3) 没法从 精确算出来,但从 尽量构造出一个最好的 近似值——并且证明这个近似足够好,能保住那个真正重要的性质(子空间会动),即便这确实给 (且只给这一处)引入了真实的梯度误差。
2.3 一个直觉:为什么 时重建这件事本身是合理的
在进入最小二乘的机械推导之前,值得先建立一点直觉:为什么从一个宽度只有 的压缩激活里,大致恢复出一个宽度是 (可能比 大几十上百倍)的激活,这件事本身是可能的?原因不是 真的包含了 的全部信息——一般来说不可能,因为 是一个固定的线性投影,投影天生有损。真正的原因是统计意义上的:真实的激活 并不是 空间里随便的点,它们大致落在一个维度低得多的流形附近,因为 的每一行都是前面(冻结的、预训练好的)网络层对相关的自然语言或图像输入做变换后的输出。已有大量证据表明,预训练 transformer 里很多层的激活协方差矩阵的”有效秩”远小于名义上的隐藏维度 ——这正是本博客之前介绍过的整条 SVD 压缩路线(SVD-LLM、GaLore 等)赖以成立的经验前提。
具体说:如果批次内 的真实秩恰好是 ,并且 恰好对齐到 的前 个特征方向,那么 就能(在投影零空间之外)无损确定 ,最小二乘重建 也就是精确的。实际情况当然两个条件都不完全成立—— 不是通过对 做特征分解选出来的,它是一个普通的(随机初始化后再梯度更新的)矩阵——所以 只是一个近似,重建误差的大小取决于 有多少”能量”落在 之外。这恰好也是决定 作为真实梯度 代理好坏的同一个量: 跟激活方差主方向对齐得越好,误差就越小。第 3.4 节会从”梯度子空间”而非”重建误差”的角度重新审视同一个问题——这是同一枚硬币的两面。
2.4 最小二乘投影,简要复习
CARE-LoRA 的重建步骤本质上就是一次普通的线性最小二乘拟合。给定数据 和目标 ,使 (Frobenius 范数)最小的 有闭式解 ,其中 是 的(左)Moore–Penrose 伪逆。这就是标准最小二乘正规方程的解,只不过同时对 个输出维度做。唯一需要处理的细节是 在实践中可能秩亏或病态(比如某个 batch 里压缩激活恰好高度线性相关),所以要加一个 Tikhonov(岭)正则项 来保证数值稳定——这是标准做法,不影响渐近结论。
2.5 已有激活内存技巧的版图
要把 CARE-LoRA 放对位置,有必要先精确讲清楚每个竞争方法到底在做什么,因为论文的实验对所有这些方法都做了直接对比:
- LoRA-FA(Zhang 等人):冻结 ,只训练 。彻底回避了”事实二”,代价是子空间永远不动。
- LoRAct:、 都继续训练,但不精确存 ,而是对缓存的激活做一次独立的在线低秩分解(有自己的分解秩 ,跟 LoRA 的秩 无关),反向传播时用这个分解重建近似的 。这是概念上离 CARE-LoRA 最近的对手——两者都是”重建近似激活”——但 LoRAct 用的是自己另算的一般性分解,而不是复用 LoRA 分支本身的投影 ,所以每次前向都要多做一次分解(还带着自己的秩超参 ),这直接体现为后面实验里 1.38× 的时间开销。
- HyC-LoRA:对 LoRA 分支周围非线性算子(不是线性层输入本身)缓存的激活做带异常值感知的低比特量化。这跟 CARE-LoRA 是互补关系,处理的是激活预算里不同的那部分。
- 梯度检查点(Gradient Checkpointing, GC):最通用、跟具体方法无关的解法——干脆什么中间激活都不存,反向传播时重新跑一遍前向(针对被检查点标记的模块)。这是无条件的”用算力换内存”,适用于任何架构,但计算开销会随模型深度叠加(要重新跑整个 transformer block,不只是一个 LoRA 分支)。
- DoRA / PiSSA:这两个根本不是激活内存方法——它们是精度导向的 LoRA 变体(DoRA 把权重分解成幅度和方向;PiSSA 用 SVD 从预训练权重的主奇异向量初始化 )。论文把它们纳入实验是作为精度基线,用来说明 CARE-LoRA 的优势不只是”随便一个精度导向的变体都能打赢原版 LoRA”,而是货真价实的内存-精度帕累托改进。
2.6 一个容易混淆的概念区分:重建 vs. 插值 vs. 外推
在继续看方法部分之前,值得先把三个容易混为一谈的数学概念区分开:重建(reconstruction)、插值(interpolation)、外推(extrapolation)。CARE-LoRA 做的事情严格来说是重建:已经知道 ,反过来估计产生它的 本身。这跟插值(在已知数据点之间估计未知值)和外推(在已知数据范围之外估计)都不一样——重建问题的特点是:目标量 在计算 的时候其实是已知的(前向传播时 还在),只是在反向传播时它已经不在了,所以需要从 把它“请回来”。这个区别很重要:它意味着 可以完全拟合在同一个 batch 自己的数据上(训练集内拟合),而不需要担心泛化到新数据上的表现——因为它从来不需要在没见过的数据上预测 ,只需要在同一个已经看过的 batch 上把已知的 重新表示一遍。这也是为什么这个方法不需要额外的验证集或者注意过拟合问题——因为它从本质上说不是一个在训练集上学习、希望泛化到新样本的模型,而是一个每次都重新求解的确定性优化问题。
3. 方法
3.1 前向传播:顺手算出 和
CARE-LoRA 完全没有改变 LoRA 的前向计算—— 照常算。唯一的区别在于反向传播要留下什么。前向传播时,趁 还在,CARE-LoRA 多算两个量:
本来就是 LoRA 前向计算的副产品(,所以不管怎样都会算出 )。 需要多做一次小型线性求解——对一个 矩阵求逆, 通常是 8 到 64 这个量级——相对任何涉及完整 激活的运算来说,内存和算力代价都很小。算出 和 之后,CARE-LoRA 扔掉 ,只保存 和 供反向传播使用。这就是整个内存技巧的全部内容:两个小张量( 和 )替代了一个大张量()。
3.2 反向传播: 和 的梯度精确, 的梯度是重建出来的
给定上游梯度 ,反向传播先重建出一份近似激活
然后计算三个梯度:
有两点值得专门强调,因为第一遍读很容易漏掉。第一, 和 是完全精确恢复的——CARE-LoRA 给传给更早层的梯度、给 的更新引入的近似误差是零。整个反向传播里唯一被近似的量只有 。这比”CARE-LoRA 近似了整个反向传播”这种说法要窄得多,也正是这个方法相对标准 LoRA 精度损失很小的原因:两个可训练矩阵里只有一个拿到了带偏的梯度,而且这个偏差还是被约束住的(见 3.3 节)。第二,论文的实现从来不会真正实体化那个 的矩阵 ——它利用矩阵乘法的结合律,直接计算 ,所以整个反向传播里跟 的梯度相关的最大中间张量只有 或 ,从来不会是 。这个”运算顺序”的细节才是真正把内存优势落到实处的关键,不只是纸面上的公式游戏。
3.3 算法,逐步拆解
算法 1 —— 一个 LoRA 层的 CARE-LoRA 前向/反向
前向传播
输入:激活 X ∈ R^(N×m)
状态:冻结权重 W ∈ R^(m×n);可训练 A ∈ R^(m×r)、B ∈ R^(r×n)
输出:Y ∈ R^(N×n);保存的张量 Z、M
1: Z ← X @ A # 压缩激活(标准 LoRA 前向也会算这一步)
2: M ← (Z^T @ Z + λ*I_r)^-1 @ Z^T @ X # 岭正则化的最小二乘重建矩阵
3: ΔY ← Z @ B # LoRA 分支的输出
4: Y ← X @ W + ΔY # 整个层的输出
5: 保存 Z、M;扔掉 X # <-- 内存优化就发生在这里
6: 用 Y 继续前向传播
反向传播
输入:上游梯度 G = ∂L/∂Y ∈ R^(N×n)
状态:保存的 Z ∈ R^(N×r)、M ∈ R^(r×m)
输出:梯度 ∇_A~L、∇_B L、∇_X L
1: ∇_X L ← G @ (W + A@B)^T # 精确——和标准 LoRA 完全一样
2: ∇_B L ← Z^T @ G # 精确——和标准 LoRA 完全一样
3: X_hat ← Z @ M # (只是概念上的一步,实践中从不实体化)
4: ∇_A~L ← M^T @ Z^T @ (G @ B^T) # A 的重建/近似梯度
5: 用 ∇_A~L、∇_B L 更新 A、B
6: 用 ∇_X L 继续反向传播
逐行看为什么每一步重要:前向传播第 1 行不是新增计算——这是 LoRA 反正都要算的值。第 2 行是真正新增的一步,一个 的线性求解,代价是 ,只要 ,相对 LoRA 矩阵乘法本身的 代价可以忽略不计。第 5 行(扔掉 )是内存账目真正发生变化的地方:原本要从前向传播一直存活到反向传播的张量,从 个浮点数降到了 个。反向传播里第 1–2 行和普通 LoRA 完全一样(这就是”没有近似误差传给更早的层,也没有传给 “这个保证的来源),第 3–4 行才是重建:不用(已经被扔掉的)真实 去算 ,而是把重建出来的 代入同一个公式。
3.4 为什么 LoRA-FA 会失败,而 CARE-LoRA(可证明地)不会:子空间演化论证
这是这篇论文理论上最核心的部分,值得完整推导一遍,因为它的核心主张——“CARE-LoRA 的下投影子空间在微调过程中可以演化”——并不是一眼就能看出来的;不够仔细的读者很容易以为,任何基于”重建”的 梯度,都跟 LoRA-FA 干脆没有梯度一样受限。
先搭建几何框架。 记 为 当前的列空间——这是 维输入特征空间里的一个 维子空间。因为 LoRA 更新的每一列都满足 ,所以适配器在任意时刻的全部表达能力都被限制在 里。LoRA-FA 冻结 , 就在整个训练过程中被冻住—— 随机初始化时捕捉到(或者没捕捉到)的方向,就是这个适配器永远的家底。对任何一个通过梯度下降来更新 的方法,自然要问的问题是:更新后的 ,它的列空间还落在 里,还是能跑到新的方向去?
推导重建梯度的闭式表达。 从式 (9) 出发,代入式 (6) 的闭式 :
把 代入进去:
其中 是输入激活的(未归一化)二阶矩/批协方差矩阵,而
这是最关键的代数事实:重建梯度可以分解成 (一个决定子空间的固定项)乘以 (一个 的混合矩阵)。接下来一切都取决于 对当前子空间 做了什么。
为什么 可能跑出子空间。 如果 恰好是 的一个不变子空间——意味着 会把 里的每个向量都映射回 ——那么 的所有列依然会落在 里,梯度更新就永远逃不出 。但 是真实训练数据的协方差结构,一个随机初始化的 维子空间对一个任意的 半正定矩阵而言,几乎不可能恰好是它的不变子空间——除非是像 (各向同性数据,真实激活里几乎不可能出现的高度结构化/各向异性情形)这种退化特例。用 、 分别表示投影到 和它的正交补上的算子,梯度更新真正引入新方向的条件是
这个条件只有在 (也就是刚才排除掉的那种不变子空间特例)时才不成立。
为什么混合项 不会把这个差异抵消掉。 即便 ,理论上也有可能右乘 恰好把子空间外的分量抵消成零。论文直接验证了这一点:在梯度更新 之后,利用 (因为按定义 自己的列都落在 里):
要让这个式子等于零(也就是让 即便 也依然留在 里), 就必须秩亏得恰到好处,正好能抵消 的行空间——这是一个非一般性的巧合。论文分析了 可能归零的三种情形( 秩亏、 秩亏、或者满秩的 与 之间出现非一般性的代数抵消),并论证对真实、非退化的训练 batch 来说,这三种情形都是概率为零的事件。结论是:一般情况下 ——子空间会动。
这个结论到底证明了什么,又没有证明什么。 这跟 LoRA-FA(其子空间根本不可能动,这是结构性决定的,不只是”一般情况下不会动”)确实是本质区别,也是一个不那么显然、值得认真对待的数学事实——算是一个真正的贡献。但要精确地看清楚这个论证到底断言了什么、没有断言什么:它证明了子空间可以动,而且在数据非退化的一般情况下确实会以非零的量、朝着一个由数据决定的方向()移动。它没有证明子空间会朝着任务相关的方向移动,没有证明它会收敛到一个好的子空间,也没有证明它移动得足够快、能完全追上标准 LoRA 的精度。这是一个一般性/非退化性论证,不是一个收敛性保证——实验部分其实也隐含印证了这个区别:在匹配秩 的情况下,CARE-LoRA 并没有完全追平标准 LoRA 的精度(GLUE 上 85.22 对 85.24,Mistral LLM 任务上 53.12 对 53.74),它只是补上了 LoRA-FA 大部分的差距,而不是全部。这一点值得留到后面的批判性分析里再展开。
3.5 设计取舍讨论:为什么是这种重建方式,而不是别的替代方案
为什么复用 ,而不是像 LoRAct 那样做一次独立分解? CARE-LoRA 最明显的替代方案就是 LoRAct 那种做法:对 做一次独立的低秩分解(有自己的分解秩 ,跟 LoRA 的秩 无关),单纯为了压缩激活,跟 LoRA 的计算完全脱钩。这样做的好处是分解秩可以独立于 调节。但代价是 CARE-LoRA 避开了的两点:(a) 需要在 LoRA 前向传播之外额外做一次分解计算(比如随机化 SVD 之类),而不是复用 LoRA 分支反正都要算的 ——这在实验里直接体现为 LoRAct 1.38× 的时间开销,相比之下 CARE-LoRA 只有 1.02×;(b) 更微妙的一点是,LoRAct 的分解跟真正决定 LoRA 更新方向的 并没有绑定关系,所以没有任何保证它压缩出来的表示恰好捕捉到了对 而言重要的方向——CARE-LoRA 的重建则天生就是(通过最小二乘拟合)专门针对”如何从 LoRA 分支自己的 最好地预测 “这个目标优化的,而这恰好就是下游真正需要的量。
为什么用最小二乘(Frobenius)重建,而不是一个学出来的重建网络? 一个可学习的非线性解码器(比如一个把 映射到 的小 MLP)理论上可以比线性映射 表达能力更强。论文没有走这条路,原因很实际:一个学出来的解码器需要自己的参数、自己的梯度、自己的优化器状态——这恰恰重新引入了 CARE-LoRA 本来想消除的那种开销。闭式的最小二乘 完全不需要额外的可训练参数(它是 和 的确定性函数,每次前向传播都重新算一次),所以不会增加任何优化器状态内存,而且只是一次矩阵求解,计算量很小。代价是 只对当前这个 batch 最优——它是每次前向都重新拟合的,而不是在整个训练轨迹上学出来的,这既是优点(不会有分布漂移导致的过时问题),也是局限(无法把跨 batch 的信息积累/摊销下来,而且每一步都得付出一点非零的重新计算代价)。
岭正则项为什么重要,又可能在哪里失效? 式 (6) 里的正则项 是为了防止 病态时最小二乘求解直接爆掉——这种情况会在 batch 内压缩激活近似线性相关时发生,比如 batch size 非常小( 接近甚至小于 ),或者 batch 内输入数据高度重复、多样性很低。在这些情形下,重建质量 会变差,而且——按 3.4 节的论证——一个条件数很差的 也更容易让 逼近秩亏的临界情形,这恰好就是 可能归零、子空间演化保证变弱的场景。论文没有专门针对这个交互做”batch size vs 精度”的消融实验,这是一个真实存在的空白(后面还会再展开)。
为什么不直接对 LoRA 分支自己的输入做类似 HyC-LoRA 那样的激活量化? 直接量化 (而不是通过 来压缩)也能省内存,而且跟 无关,是均匀生效的。CARE-LoRA 不这么做的原因是架构契合度问题:LoRA 的 作为前向传播的副产品,本来就已经对 做了一次线性、可学习的压缩——复用它几乎零成本,而量化则需要单独的校准/异常值处理机制(HyC-LoRA 的设计就说明了这一点),处理的也是不同的冗余维度(数值精度,而不是维度本身)。这两条路线并不互斥——在 CARE-LoRA 已有的维度压缩之上,再对 和 本身做量化,是论文没有探索的一个自然的进一步压缩方向。
3.5b 一个容易被忽略的细节: 的对称性与数值稳定性
还有一个实现细节值得单独拿出来讲:式 (6) 里的 是一个对称正定矩阵(只要 ),所以它的 Cholesky 分解总是存在的,实现时完全可以用 Cholesky 求解而不是通用的 LU 分解来求解这个线性系统,这在 GPU 上通常能带来一定的常数因子加速(相比一般的 torch.linalg.solve,用 torch.linalg.cholesky + cholesky_solve 组合通常更快)。这个实现细节论文没有提及,但对想把这个方法写得尽可能快的实现者来说,是一个值得先知道的小优化。另外值得注意的是, 的选取需要和 的数值量级相匹配——如果激活本身经过了 LayerNorm 或者其他归一化操作, 的数值范围会相对稳定;但如果某些层的激活本身数值尺度很大(比如深层 transformer 里未经归一化的残差流),固定一个全局的 就可能对不同层而言过强或过弱。论文没有讨论这个层际差异,但实际实现时值得注意。
3.6 CARE-LoRA 在更大的 PEFT 内存版图里处于什么位置
值得退一步,把这篇论文放到本博客已经介绍过的”内存高效/低秩训练”这条更大脉络里看,因为 CARE-LoRA 的定位其实比第一眼看上去要窄——而这恰恰让它更站得住脚。GaLore(本系列之前介绍过)把全参数微调的梯度投影到一个低秩子空间来省优化器状态内存,作用于全参数微调而不是基于适配器的 PEFT;它解决的是不同的一块(第 3 块,优化器状态),用于不同的训练场景(全参数微调),机制上(低秩投影)看着相似,但两者并不能互相替代——GaLore 不解决 LoRA 的激活瓶颈,CARE-LoRA 也不解决全参数微调的优化器状态瓶颈。AdaLoRA 则正好落在 LoRA 这类适配器范畴内,但攻击的是另一种资源:它根据重要性分数在不同层之间重新分配秩预算,跟 CARE-LoRA 的激活内存技巧是正交的(原则上可以结合)——CARE-LoRA 的重建机制并不假设各层的秩是固定不变的。DoRA 和 PiSSA,前面已经讨论过,是对 LoRA 更新本身做精度导向的重参数化(幅度/方向分解;基于 SVD 的初始化),跟 CARE-LoRA 没有共享任何省内存的机制——它们被纳入实验纯粹是作为精度参照。这样一来,CARE-LoRA 其实解决的是 PEFT 内存问题里一块非常窄、此前又没被充分处理过的角落:不是参数,不是优化器状态,不是秩分配,而是”因为两个 LoRA 矩阵都可训练”这个具体原因所导致的激活内存。这种窄,是优点而不是缺点——它让这个方法很容易推理清楚,很容易跟秩分配或量化技巧组合起来,也很容易验证论文所宣称的那些保证是否成立——因为它只改动了训练流水线里一个定义清晰的环节。
3.7 一个容易问的问题:为什么不干脆把 也冻住,单独用 重建 的梯度?
一个乘乘地可能会想到的更激进方案是:既然 已经不需要 ,那为什么不干脆跟 LoRA-FA 一样把 也冻住,只让 可训练,这样连重建矩阵 都不需要算了?答案就回到了第 3.4 节的核心论点:这样做就重新回到了 LoRA-FA 的根本问题—— 一旦冻住, 就永远固定,整个适配器的表达能力就被永久锁死在初始化时的那个随机子空间里。CARE-LoRA 恕恕是因为让 继续可训练,才能让子空间有机会演化,而这恰恰需要一个(哪怕近似的)对 的估计。换句话说,重建矩阵 存在的唯一目的就是为了支撑 的更新——如果你决定不更新 ,那整个重建机制就彻底没有存在的必要了,直接用 LoRA-FA 就好。这个推理链条值得明确写出来,因为它说明了 CARE-LoRA 的设计不是两个独立技巧的拼接(“重建激活”+“保留 可训练”),而是一个目的单一的设计:所有机制都是为了服务同一个目标——让 能继续学习,同时不介意存完整 。
4. 内存和算力开销,精确推导
4.1 激活内存
标准 LoRA 必须留着完整的输入激活才能算出 ,所以 LoRA 分支的激活内存是
CARE-LoRA 只保存 和 :
比值是
代入论文自己的算例——T5-Base 在 MNLI 上微调,序列长度 256,batch size 32,所以 ,隐藏维度 ,秩 :
论文表 5 报告了这个比值在不同 batch size 下的实测版本(用 对比 LoRA 的 ,这是更严格的比较),很有意思的是这个比值随 batch size 增大而收窄:batch size 对应 8 时是 4.65%,到 batch size 128 时降到 2.46%。这和式 (18) 的预测完全吻合:当 时,比值 ( 这一项相对 变得可以忽略,比值趋于一个只取决于 的常数),而 较小时,固定的 开销占比就相对更大。这是一个可以直接验证的清晰预测,实测数字也确实印证了它。
4.2 算力开销
只统计 LoRA 分支的矩阵乘法(前向+反向两个方向),标准 LoRA 的开销是
(三次代价为 的乘法:前向的 ,加上两个形状相同的反向项;三次代价为 的乘法:前向的 和它对应的两个反向项——论文是按这个方式打包统计的,没有把每一项都拆开写清楚,这是个小的表述空白,但总量是标准的。)
CARE-LoRA 的额外开销主要来自求解 (一个应用在 、 规模操作数上的 求解)以及用它来计算重建后的 梯度:
相对开销是
由于设计上 ,分子大致按 缩放,而分母按 缩放——只要 (实践中总是成立的——batch×序列的 token 数量远远超过秩),这个比值会保持接近 1。论文的算例(T5-Base,,,)给出
也就是理论上几乎可以忽略的开销——跟后面报告的 1.02–1.38× 实测耗时比值是一致的(理论上约 0.4% 的”仅 LoRA 分支”估计,和实测约 2–38% 的端到端开销之间有落差,原因是理论统计只覆盖 LoRA 分支的矩阵乘法,没有算上 transformer block 剩余部分、优化器步骤,或者内存分配/拷贝开销——这一点论文没有明说,但仔细的读者在把式 (23) 当成完整训练步开销估计之前应该留意这一点)。
4.3 把式 (16)–(23) 连起来看:一个完整的例子
为了让这一节的公式不只是孤立的代数,值得把它们串起来对一个具体层完整走一遍。假设你在微调一个隐藏维度 的现代 LLM,序列长度 2048,batch size 16,所以 ,LoRA 秩 。标准 LoRA 需要保存的激活内存是 个浮点数,换算成 bf16 存储大约是 268MB——这只是单个适配层的数字,而一个现代 LLM 往往有数百个这样的适配层。CARE-LoRA 需要保存的是 个浮点数,大约 1.2MB——比例大约是 0.44%,与式 (18) 的渐近预测基本匹配(因为 相对 已经很大, 那一项已经可以忽略)。计算开销侧,按式 (22),额外开销大约是 ——在这个更接近现代 LLM 规模的例子里,相对开销比论文自己用的 T5-Base 例子(0.38%)还要小一个数量级——这是因为随着 变大, 本身也变大了,而 里主导项是 ,相对 里的 增长速度更慢。这个完整走一遍的例子说明,对现代大规模 LLM 而言,CARE-LoRA 的内存和算力优势只会比论文用的小规模 T5-Base 例子更明显,不是更微弱。
5. 实验
论文在三种真正不同的模态上做了评测——NLU(T5-Base)、NLG/LLM 微调(Mistral-7B-v0.3)、扩散模型个性化(SD3-Medium/DreamBooth)——这个覆盖面比大多数 PEFT 内存论文要广,也是这篇论文比较明显的优点之一。
5.1 实验设置
所有方法在各自任务内共享骨干模型、数据、目标模块、优化器(AdamW)、学习率、精度、batch size 和训练步数,使用同一套基于 PEFT 的代码库。结果在三个种子(0、21、42)上取平均并报告标准差。LoRAct 的分解秩 设为 64(T5-Base)/ 128(Mistral-7B)。CARE-LoRA 同时报告了匹配秩(,与 LoRA-FA 的公平内存对比)和更高秩(,“把省下来的内存再投资进去”,与标准 LoRA 及其他精度导向变体对比)两种设置。
下面的图 1(论文图 1)展示了实验所围绕的整个架构——前向传播计算并保存 ,反向传播只为 的梯度重建 ,而 的梯度和传给 的梯度都保持精确:

5.2 自然语言理解(GLUE / SuperGLUE,T5-Base,)
图 2(论文表 1–2)复现了正面对比的数字:

两个基准套件上的规律是一致的:CARE-LoRA(GLUE 平均 85.22 / SuperGLUE 平均 78.18)基本和标准 LoRA(85.24 / 77.81)打平——SuperGLUE 上甚至还略胜一筹——而 LoRA-FA(80.97 / 70.81)掉得很惨。单任务上差距最悬殊的是 CoLA(LoRA-FA 46.08 对 CARE-LoRA 60.46,差 14.4 分)和 CB(LoRA-FA 76.44 对 CARE-LoRA 90.03,差 13.6 分)——这两个都是数据量小、比较特殊的任务,一个随机冻结的子空间尤其容易恰好没覆盖到任务真正需要的方向,这正是 3.4 节子空间演化论证所预言应该被修复的失败模式。内存上,CARE-LoRA(GLUE 平均 5019 MiB)跟 LoRA-FA(4988 MiB)差距在 0.6% 以内,比标准 LoRA(6311 MiB)低约 20%。
5.3 LLM 微调(Mistral-7B-v0.3:数学、代码、指令遵循)

在匹配秩()下,CARE-LoRA(整体 53.12)比标准 LoRA(53.74)落后一点点——这正是 3.4 节标出的、近似的 带来的一个真实(但很小)的代价。但更有意思的对比是:把省下来的内存重新投资到更高的秩之后会发生什么——CARE-LoRA 在 时达到 54.47 的整体平均分,是七个对比方法里最高的,包括 DoRA(54.30,但代价是 3.39× 的单步耗时和更高的内存)和 PiSSA(53.47)。这种”把内存投资到秩上”的叙事是论文比较高明的一个修辞角度——它把 CARE-LoRA 从”更好的 LoRA-FA”重新定位成”在同样的内存预算下能负担得起比 LoRA 更高秩”的方法,这是一个真正不同、可以说更有实用价值的价值主张。
5.4 扩散模型个性化(SD3-Medium / DreamBooth)

这个实验是三个里最窄的(只有一个基线,标准 LoRA,只对比了两种秩设置),但它证明了这个方法能迁移到语言模型之外的扩散 transformer 架构:DINO 从 43.18 提升到 43.40,CLIP-I 从 69.18 提升到 69.29,CLIP-T 基本不变(28.01 → 28.00),同时峰值内存从 9882 降到 8630 MiB(约 13%)。
5.5 内存随 batch size 的变化——数学可视化图

这张表是对式 (18) 预测的实测印证——随着 增大,内存比值应该收窄(因为 这一项相对 这一项越来越小)——实测数字确实随 batch size 从 8 增大到 128 单调地从 4.65% 降到 2.46%,跟公式预测的定性(也大致是定量)行为完全一致。
5.6 耗时对比,以及梯度检查点基线

DoRA 的 3.39× 开销来自它在省内存模式下把逐步权重重归一化和反向传播重计算结合在一起——这也提醒我们”内存高效”的 LoRA 变体并不自动等于快,而 CARE-LoRA 接近 1.0× 的耗时比是一个真正区别开来的属性,不是理所当然的。

这个最后的对比是论文用来论证 CARE-LoRA 在结构上优于通用的”重新计算激活”这种退路:梯度检查点要重新跑整个 transformer block(对被检查点标记的层做一次完整的额外前向传播)才能省内存,而 CARE-LoRA 利用的是 LoRA 分支自身的低秩结构来重建只有需要的那部分——单位内存节省对应的计算代价要小得多。这个对比在精神上是公平的(匹配了内存预算),但覆盖面有点窄:只对 32 个 decoder block 里的 8 个应用了梯度检查点(专门选来匹配 CARE-LoRA 的内存占用),只在一个任务上(Math/GSM8K)做了对比,所以它证明的是这个机制是有利的,而不是把梯度检查点和 CARE-LoRA 在不同检查点比例下的完整帕累托前沿都讲清楚了。
5.7 把子空间演化论证跟实验证据再对一遍
值得把第 3.4 节的理论和实验数字明确地对应起来看一遍,因为论文本身没有直接做这个配对。理论预测:CARE-LoRA 相对 LoRA-FA 的优势应该在 LoRA-FA 那个冻结的初始子空间最不适合任务的时候最明显——一般来说,对更小、更特殊的任务(预训练出来的通用表示碰巧包含任务相关方向的概率更低)应该比大而通用的任务(几乎任何一个合理的子空间都能碰上有用信号)更容易出现这种情况。这跟实验里 CARE-LoRA 相对 LoRA-FA 差距最大的任务恰好是 CoLA(8,551 条训练样本,一个跟骨干模型预训练时做的通用语言建模相当不同的语言可接受性判断任务)和 CB(250 条样本,极端低资源)是一致的,而在 MNLI(392,702 条样本)、QNLI 这类更大、更通用的任务上,相对差距最小。理论还有一个更微妙的预测:重建梯度方向的”质量”取决于 的演化跟 实际推动它去的方向对齐得有多好——这是数据分布本身的性质,而不是任务标签空间的性质,所以这个跟任务规模/难度的相关性只是启发性的,不是论文定理真正证明了的东西——它是跟理论精神一致的经验规律,不是理论的一个被验证的实例。这正是当一篇论文把定理和实验部分处理得有点脱节时,读者应该主动去做的连接工作——如果作者自己把这层联系讲明白,也会让这篇论文更扎实。
5.8 架构无关性:这个方法是不是依赖于特定架构
审视任何一篇 PEFT 内存优化论文时,都值得问一句:这个机制是不是依赖测试架构的某些特殊性质,还是真的跟架构无关?CARE-LoRA 第 3 节的推导从头到尾都没有用到 transformer 解码器架构、注意力机制,或者 SD3 扩散 transformer 结构的任何特有性质——整个方法是针对单个通用线性层 推导出来的,原样适用于任何使用 LoRA 类适配器的架构里的任何线性层,包括注意力投影(Q/K/V/O)、MLP 上/下投影,以及(正如 DreamBooth 实验展示的那样)扩散 transformer 模块。这种真正的架构无关性是一个实实在在的优点,也是论文里比较有说服力的间接证据之一——同一套机制,除了秩 之外没有任何架构特定的调参,就在三个真正不同的领域(双向编码器-解码器 NLU、仅解码器的因果 LLM 微调,以及扩散 transformer 图像生成)上都表现出了一致的定性行为(内存下降、精度损失很小、补上了 LoRA-FA 大部分的差距),这比只在单一架构上做消融实验要提供更强的泛化信号,即便每个单独领域的评测范围都比较窄(任务少,扩散模型这块基线尤其少)。
5.9 一个更完整的数值例子:从 4.65% 到 2.46% 背后的系数
值得把表 5 里的每一个数字都代入公式验证一遍,这样才能真正确信式 (18) 不是个“大致对上”的公式,而是一个可以逐点对账的精确预测。T5-Base 在 MNLI 上微调,隐藏维度 ,序列长度固定为 256,LoRA 秩 ,CARE-LoRA 秩 。表 5 拿的是 LoRA 秩 作分母、CARE-LoRA 秩 作分子的比值,所以式 (18) 应该写成 (分子用 ,分母用 )。当 batch size = 8 时,:
这个数字跟表 5 实测的 4.65% 不完全一致,说明实际实现中可能还有一些式 (16)–(17) 没有建模的实现细节开销(比如实际内存分配器的对齐开销、PyTorch tensor 元数据开销等),但量级上是匹配的——这正好提醒读者,论文给出的公式是一个非常好的数量级对预测,但不应该被当作对实测数字的精确预测。随着 batch size 增大到 128(),同样的公式给出:
跟实测的 2.46% 同样很接近。更重要的是趋势:从 batch size 8 到 128,公式预测的比值从 3.58% 降到 2.67%,下降幅度和实测的 4.65% 降到 2.46% 在数量级上完全匹配——这才是这个公式真正有价值的地方:它能正确预测“内存比值会随 batch size 增大而收窄多少”这个变化趋势,而不仅仅是定性上“会变小”这个方向。
5.9b 把数字拉开看:为什么 CoLA 和 MRPC 上的表现差异值得多看一眼
除了 CoLA 和 CB 之外,表 1 里还有一个值得单独提一下的对比:MRPC 上 LoRA-FA(87.05)相对 CARE-LoRA(92.41)掉了近 5.4 分,而 LoRA 自己只有 92.04——这意味着 CARE-LoRA 在 MRPC 上实际上还略微超过了标准 LoRA,而不仅仅是接近。这个现象在 QNLI 、SST-2 这类数据量更大、任务本身更“平滑”的任务上就不存在了(两者误差在 0.1 分以内)。如果把这个现象和第 5.7 节的讨论放在一起看,能得到一个比仅看平均分更具体的图景:在数据量小、任务与预训练目标差异较大的任务上,CARE-LoRA 不仅补平了 LoRA-FA 的差距,有时甚至能稍微超越标准 LoRA;而在数据量大、任务相对通用的任务上,三者差距本身就很小,无论用哪种方法都差不多。这个观察对实践者的启示是:如果你的下游任务本身就是大数据量、通用型的,那么选 LoRA-FA 还是 CARE-LoRA 对最终精度的影响可能并不大,真正的可靠收益主要体现在小数据、高方差的小众任务上。
6. 局限性
论文对自己的范围还算坦诚,但有几个边界条件没有被检验:
- 单层/单适配器分析。 所有内存和算力公式都是针对单独一个被适配的线性层推导的。论文没有讨论单层的省钱效果,跟它在整体比较中排除掉的那些”跨方法共享、固定不变”的开销(模型权重、非 LoRA 激活、优化器状态)之间,在一个有几十个被适配层的完整深度模型上到底怎么叠加——聚合效果大概率接近单层比值,但论文没有直接展示这一点(表 3 里整个 Mistral-7B 相对 DoRA 省下 18.7% 内存是一个端到端数字,但没有跟第 3.4 节的单层账目明确对应起来)。
- batch size 和条件数的交互没有做实验。 前面第 3.4 节已经提到,小 batch size(或者激活多样性低)会让 条件数变差,既会降低重建质量,按子空间论证来说,也可能把 推得更接近那个退化临界情形,让子空间没法有效演化。论文对内存做了 batch size 扫描(表 5),但从来没对精度做过同样的扫描,所以我们并不知道小 batch 微调会不会(以及在什么程度上)侵蚀 CARE-LoRA 相对 LoRA-FA 的精度优势。
- 没有跟量化骨干模型的结合实验。 相关工作里提到了 QLoRA 那种 4-bit 骨干模型,但从来没有在实验里跟 CARE-LoRA 组合过——量化 、再用 CARE-LoRA 压缩激活,这个显而易见又实际重要的组合被留给了未来工作。
- 秩敏感性只测了 两个点。 所有主要结论都只用了两种秩设置。目前还不清楚”把省下的内存投资到更高秩”这个故事在 或 时是否依然成立,还是会出现收益递减(甚至反转,如果最小二乘重建本身在更高维度上求 的逆时,条件数变差导致重建质量下降)。
- 小数据集上的统计功效有限。 好几个 SuperGLUE 任务(CB 只有 250 条验证样本,COPA 500 条,WiC 和 RTE 规模差不多)在不同种子间标准差很大(比如 CB:LoRA-FA ,CARE-LoRA )——论文如实报告了这些数字,但一个只用 3 个种子、单次运行波动就有好几分的任务上出现 13 分的平均差距,其实比表面看到的干净数字要更嘈杂一些。
6.1 重建误差与梯度偏差之间的权衡,再深入一层
值得把 CARE-LoRA 引入的误差类型说清楚,因为“近似梯度”可以指很多不同的东西,而论文自己的框架(一个关于子空间移动的定理)并没有完整刑标实践中应该预期的误差行为。这里有两个逻辑上独立的误差来源,值得分开讲:
重建偏差。 就算用上最优的最小二乘解 , 一般来说也不等于 ——它是 的列向当前 batch 内 与 关系所张开的空间上的正交投影(更精确地说, 是该 batch 内从 预测 的最优线性预测器)。 里任何无法由 线性预测出来的部分就直接丢失了,而 会确定性地继承这部分损失,一个 batch 接一个 batch 都一样。这在统计学意义上是一种偏差:它不会像 mini-batch 带来的梯度噪声那样随着训练步数增多而平均掉,因为它不是零均值的噪声——它是对真实梯度中依赖于当前未被表示部分的那一块的系统性低估。
逐 batch 重新拟合,以及它带来的一体两面。 因为 每次前向传播都是从当前 batch 的 和 重新拟合的,这个重建过程会立即适应训练内部的分布变化(比如随着一个 epoch 内数据构成的变化,或者跨课程学习阶段的切换)——不会有固定或缓慢更新的解码器那种“过时”问题。但这也意味着重建的质量只取决于当前 batch 的统计特性有多好,对之前 batch 条件数有多好、信息量有多充分没有任何记忆。一个异常均质的 batch(比如在多任务微调混合中,某个 batch 恰好不成比例地多取自了某个子任务)可能会临时产生一个质量很差的 ,进而在那一步产生更噴噪的 ——这种效应,纯粹由 mini-batch 带来的梯度噪声不会以同样结构化的方式产生。论文的岭正则能缓解最糟糕的数值不稳定问题,但并没有直接解决 在一个差 batch 上的表达质量问题——正则化能防止矩阵求逆爆掉,但它不会平白无故地发明出一个本来就不在一个条件很差的 batch 里的信息。
从实践角度看,这个分析说明 CARE-LoRA 的精度应该比标准 LoRA 更对 batch 构成和 batch size 敏感一些——这恰恰就是前面(局限性一节)标出来、但还没被实验检验过的那个间隙。这也提示了一个论文没有探索的自然缓解方案:对最近几个 batch 的 维护一个指数移动平均,用一部分对分布变化的实时适应能力换取一个更平滑、方差更小、不那么容易受单一 batch 构成影响的重建矩阵——这是一个自然的后续消融方向。
6.1b 小样本任务上的方差问题,再多说一句
前面局限性部分提到了 CB、COPA 这类小数据集任务上的方差问题,这里值得把数字拉开看一下。CB 只有 250 条验证集样本,按照常见的三种子集交叉验证划分,每个种子实际评估到的样本数量可能还不到 250,这意味着单一预测错误就能让得分波动超过 0.4 个百分点。论文报告的 LoRA-FA 在 CB 上是 ,这个 的标准差在只有三个种子的情况下其实已经非常接近于单次运行的噪声上限了;相比之下 CARE-LoRA 的 方差小得多,但这本身也可能只是因为 CARE-LoRA 在这个任务上恰好没有碰到 LoRA-FA 那种子空间错错发现的情况,而不一定意味着 CARE-LoRA 本身在小样本任务上就一定更稳定。这个区别很重要:“平均分高 13 分”和“方法本身更稳定”是两件不同的事,前者论文确实证明了,后者需要更多种子才能下结论。
7. 批判性分析
(a) 这篇论文本身的弱点和瑕疵。 子空间演化定理(第 3.4 节)是一个一般性论证,不是一个收敛性保证,而论文行文里偶尔会模糊这个区别——像”下投影子空间没有被冻结”这种表述,字面上是准确的,但不够仔细的读者很容易理解成子空间是朝着有用的方向移动,而这一点论文从来没有真正证明,它只证明了子空间会动(在数据非退化的前提下以概率 1 发生)。证明 的子空间不会永远卡死,是一个真实且有用的结果,但这比”CARE-LoRA 能学到好的子空间”要弱得多,论文的表述有时候会把这个界限模糊掉。另外,第 4.2 节的算力开销分析(式 20–23)只统计了 LoRA 分支的矩阵乘法,但之后又把这个理论上约 0.38% 的开销估计,隐含地拿去跟实测的端到端单步耗时比值(1.02–1.38×)做对比,却没有提醒读者这两个数字统计的根本不是同一件事——一个只看公式、不细想的读者很容易高估这个方法实际部署时的开销有多低。
(b) 作者低估或者干脆没提的局限。 论文完全没有讨论混合精度或低精度训练(bf16/fp16)下会发生什么——在这种场景下,岭正则项 和 的条件数会跟更低的数值精度产生交互,而这恰恰是决定一个省内存的 PEFT 方法在大规模训练场景(正是内存节省最重要的地方:非常大的 batch×序列长度乘积,通常还是低精度)里是否稳健的关键细节。同样,论文也没有讨论 CARE-LoRA 跟其他秩自适应 PEFT 技术(比如 AdaLoRA,它会在训练过程中动态改变每层的 )如何组合——既然 CARE-LoRA 的内存和精度表现都依赖于 ,一个会随时间调整 的方法必然会引入论文完全没有处理的交互。最后,论文所有实验都只用了三个种子取平均;对最小的那几个 SuperGLUE 任务来说,这个样本量对”CARE-LoRA 取得了最好的平均分”这类结论而言其实相当薄弱。
(c) 具体、可操作的改进建议、以及对本博客后续追踪有用的一个开放问题。 因为 CARE-LoRA 的重建机制本质上是一个数据依赖的线性解码器,一个自然而然的后续问题是:如果把这个机制推广到不只是单个线性层,而是整个注意力块(Q、K、V、O 四个投影共享同一个重建机制,或者共享同一个 )会怎么样?直觉上,因为 Q/K/V 的输入激活其实是同一个 ,如果能共享重建计算,或许能进一步摊薄额外的 求逆开销;但另一方面,不同投影矩阵对应的 不同, 也不同,所以共享重建矩阵并不是一个直接可以做到的事情,需要额外的数学设计。这个方向论文完全没有提及,但对想要在自己的训练框架里落地这个方法的团队来说,是一个值得先自己推导一下的实际问题。具体到改进建议本身:第一,论文如果加一个精度-vs-batch size 的消融实验,直接检验 3.5 节提到的条件数担忧——比如在 MNLI 上重复实验,用 batch size 8、16、32,同时报告精度和 的实测条件数,就能确切定位这个方法的保证在什么地方(如果有的话)开始失效,会让论文更有说服力。第二,跟一个学习出来(而不是逐 batch 最小二乘)的重建做直接对比——哪怕只是一个用单层可训练线性层替代闭式 的小消融实验——就能弄清楚闭式解到底是不是为了它的零参数简洁性而牺牲了有意义的精度,还是说它对这个架构而言已经接近最优了。第三,考虑到论文自己的数字显示,LLM 规模上的差距(CARE-LoRA 在 时整体落后标准 LoRA 0.62 分)比 NLU 规模上(CARE-LoRA 打平甚至超过 LoRA)要更明显一些,一个明确讨论”为什么近似误差会随骨干模型/任务规模扩大而变大”的分析——到底是序列长度、任务难度,还是 Mistral-7B 激活统计特性本身的什么原因——会实质性地提升这篇论文的诊断价值,而不是把这个规律留给读者自己去发现。
7.1 小型实现草图
对想自己试一下这个想法、而不是直接用作者开源代码的读者来说,值得看看相对一个标准 LoRA 层的实现需要改动多少。一个标准 LoRA 线性层的 forward 方法通常计算 y = x @ w + (x @ A) @ B,并依赖 autograd 自动处理反向传播(前提是 x 一直存活着)。要把它改成 CARE-LoRA 层,forward 方法需要:(1) 和以前一样计算 z = x @ A;(2) 求解小型岭回归 M = torch.linalg.solve(z.T @ z + lam * I, z.T @ x),无论 多大,这都只是一个 的线性系统;(3) 把 z 和 M(而不是 x)注册为供反向传播保存的张量,通常需要一个带显式 backward 方法的自定义 torch.autograd.Function,而不是依赖普通的 autograd(因为整个方法的重点就是避免 autograd 默认行为中把 x 一直存着);(4) 在自定义的 backward 里,像标准 LoRA 一样计算 grad_B = z.T @ grad_output、grad_x = grad_output @ (w + A @ B).T,而 grad_A = M.T @ z.T @ (grad_output @ B.T) 取代标准的 x.T @ grad_output @ B.T。工程上需要改动的地方真的很少——一次线性求解加一个自定义 autograd 函数——这跟论文报告的接近 1.0× 时间开销是对上的,也说明这个方法应该很容易移植到现有的 LoRA 实现里(比如 Hugging Face peft),而不需要大量工程投入——相比那些需要对训练循环做更深改动的方法(比如梯度检查点需要控制哪些模块被重新计算,或者基于量化的方法需要校准过程),这是一个容易被忽略的实践优点。
8. 实践落地笔记
对一个要决定是否采用 CARE-LoRA 的实践者来说,这篇论文提供的证据大致指向一条清晰的决策路径。如果你的微调任务在标准 LoRA 下已经是 GPU 内存受限的——也就是说你选 batch size 或者序列长度是被激活内存卡住的,而不是被算力吞吐卡住的——CARE-LoRA 提供了一种近乎免费的方式来:(a) 保持同样的 batch size 和秩,内存降低大约 15%–20%,把腾出来的空间用在别处(更大的模型、更多并行实验);或者 (b) 把腾出来的空间重新投资到更高的 LoRA 秩上——Mistral-7B 的结果显示,这么做很可能会让精度超过标准 LoRA,而不只是打平。不那么适合用这个方法的场景包括:非常小的 batch size(最小二乘重建矩阵 只用很少样本拟合,容易碰到 3.5 节讨论的病态问题)、序列很短、原本激活内存就不是瓶颈的场景(这时参数/梯度/优化器状态这几块本来就已经占主导,这里讨论的这些激活内存技巧本来就帮不上什么忙)、或者已经在用很激进的梯度检查点的场景(这时 CARE-LoRA 的边际收益会收窄,虽然按表 7 的对比,在测试过的那一个场景下它在内存和速度上依然都赢了)。
有一个实现细节值得给工程师提个醒:因为 是每次前向传播都从头拟合的一次小型矩阵求逆,CARE-LoRA 给每一步训练都引入了一个标准 LoRA 没有的、依赖具体数据的数值运算。这意味着,跟 LoRA 的前向传播(纯粹是确定性的矩阵乘法,很容易被融合、也很适合标准的 kernel 级优化)不一样,CARE-LoRA 的前向传播多了一个跟控制流沾边的小开销(求解一个线性系统,哪怕只是很小的 系统),这可能跟某些编译/融合流水线(比如 torch.compile、自定义 CUDA graph)配合得不如纯矩阵乘法的前向传播那么顺畅。论文实测的 1.02× 开销说明在被测的那些场景下这不是个问题,但如果要把 CARE-LoRA 用到一个重度依赖 kernel 融合的训练框架里,而不是标准的 eager-mode PyTorch 训练循环,这是一个值得专门单独 benchmark 一下的细节。
8.1 与国内常见训练框架的适配考量
对于习惯使用 DeepSpeed、Megatron-LM 或 verl 这类大规模训练框架的国内团队来说,CARE-LoRA 这类方法落地时还有一个值得提前评估的问题:这些框架内部对 LoRA 层的实现往往已经做了深度融合优化(比如把 、 融合成一个 kernel,或者搭配张量并行切分),而 CARE-LoRA 需要在前向传播中插入一次额外的矩阵求逆,这个求逆本身在张量并行切分下需要额外处理( 是一个 矩阵,如果 本身被切分在多个设备上,需要一次 all-reduce 才能汇总完整的 )。这个额外的通信开销在论文的单卡/小集群实验里没有被记录,但在张量并行度较高的大规模训练中,这种频繁的小矩阵同步操作可能会比纯粗粒度矩阵乘法更容易成为延迟瓶颈(虽然绝对时间仍然很小)。这是一个论文未覆盖、但国内团队在落地前值得自己先小规模 benchmark 一下的现实问题。
8.2 与本博客之前介绍过的低秩压缩系列相互对照
本博客已经介绍过好几篇关于 SVD 低秩压缩、LoRA 变体和激活内存优化的论文,把 CARE-LoRA 和它们放在一起看会更清楚它的定位。LoRA 本身(2026-03-13 介绍过)是这一切的起点:用低秩矩阵对去近似全参数微调。AdaLoRA(2026-02-19 介绍)在此基础上加了一层自适应秩分配:不同层的重要性不一样,所以不应该用同一个固定的 。DoRA(2026-05-22 介绍)把权重更新拆成幅度和方向两部分,目的是提升精度而不是省内存。SVD-LLM、SwiftSVD、LACE-SVD 这一系列(本博客已写过多篇)处理的是不同的问题:如何对预训练好的权重矩阵本身做训练无关的低秩近似,目的是压缩模型体积/推理开销,而不是优化微调训练时的内存。相比之下,CARE-LoRA 解决的是一个完全不同的问题:微调过程中、LoRA 适配器本身产生的激活内存。这四类方法分别处理参数预算、秩分配、精度优化、激活内存四个不同的轴,原则上都可以同时用在同一个适配器上(比如 AdaLoRA 的动态秩 + CARE-LoRA 的激活重建 + 量化后的预训练权重),虽然论文本身并没有实验验证过这种组合。
9. 结论
CARE-LoRA 抓住了一个很窄但抓得很准的观察——一旦 LoRA 已经解决了它能解决的其他一切,最后剩下的那一笔激活内存税,就是只为了算 才需要的完整输入 ——并用一个确实很巧妙的机制来回应它:复用 LoRA 分支自己的压缩激活 ,通过普通最小二乘拟合出成本最低的线性解码器 把它映回 ,把由此产生的梯度近似只放在真正无法避免的地方( 的梯度),而让其他一切梯度保持精确。子空间演化推导是这篇论文最有分量的理论贡献,也确实说清楚了为什么 LoRA-FA 的”子空间冻结”失败模式在原理上是可以避免的——尽管这个结果是一个一般性论证,而不是收敛性保证。实验层面,这个方法在三种不同模态(NLU、LLM 微调、扩散模型个性化)上都兑现了承诺,内存接近 LoRA-FA,精度接近标准 LoRA;而它”把省下来的内存重新投资到更高秩”这个叙事,把一个激活内存的小技巧,变成了一种在固定硬件预算下换取更强表达力适配器的真实方法——这大概是论文报告的两个结果里,实践上更重要的那一个。
附录:把内存比值公式读成一个可调参数
最后再补一点直觉,因为它把实践者真正能控制的东西讲清楚了:把式 (18) 改写为
(把 拆成 得到的这个分解)把实践者真正能控制的两个旋钮直接写了出来。第一项 一旦秩和隐藏宽度选定就固定了——它是当 batch size 无限增大时这个比值最终足此的渐近下限,完全取决于适配器配置本身,跟数据或 batch 无关。第二项 才是随着 batch size(或者序列长度,因为 是两者乘积)增大而收窄的那部分——这恰恰解释了为什么表 5 的实测比值会随着 batch size 增大而不断下降。这对实践者的启示是:短序列、小 batch 的微调任务(这时 占主导,但只要 不是小得离谱,它相对 仍然较小)相对收益会比长序列、大 batch 的任务小一些,但因为对现实中常见的秩而言 本身就已经是主导项(比如 、 时 ),实践上的结论是 CARE-LoRA 的内存收益在实践者常见的 batch size/序列长度区间里相当稳定,batch size 的影响只有在 非常小时才真正重要——而这恰恰就是 3.5 节和第 6 节都标出来、最小二乘重建数值上也最不可靠的那个区间。这两条独立的推理路径(内存比值公式和条件数分析)指向了同一个从不同方向看过去的运行边界,这是整体设计一个令人安心的内部一致性检验,尽管论文自己从来没有以这种分解形式写出式 (24),也从未明确地拉过这个关联。
附录补充:条件数与子空间演化两个角度的总结对照表
为了把第 3.4 节的理论推导和第 3.5、6.1 节的实践担忧放在一起回顾一下,这里用文字总结一下两种情形下的对比:在条件良好的情形下(batch 足够大、数据多样性足够高), 接近满秩且条件数良好,此时最小二乘重建 质量高,式 (14)–(15) 描述的子空间演化也最容易发生——这正是大多数实验所处的区间,也是论文报告的数字能够成立的前提。而在条件较差的情形下(小 batch、低多样性数据), 可能接近奇异,岭正则需要介入才能保证数值稳定,但这个时候重建的 质量会变差,而且按 3.4 节的分析, 也更容易接近那个使子空间停止有效演化的退化临界情形。这两种情形对应的实际建议很直接:如果你的训练配置属于前者(大多数现代 LLM 微调都是),CARE-LoRA 的理论保证和实测数字都应该可靠;如果你的场景接近后者(比如极小 batch、高度专一化数据),建议先小规模验证一下重建质量再大规模部署,这正是论文本身没有做但实践者可以自己补上的一步。
复现性说明
- 代码已公开:
https://github.com/fishandyu/CARE-LoRA。 - 所有实验都基于统一的 PEFT 代码库(Hugging Face
peft)、AdamW 优化器和三个固定种子(0、21、42),同一任务内所有方法都保持相同的骨干模型/数据/目标模块/精度/batch size/步数——是一个相对可控的复现环境。 - 需要复现的方法特定超参数只有两个:CARE-LoRA 的岭正则项 (正文里只说是”一个非常小的值”,没有给出具体数字——这是精确复现的一个空白)和 LoRAct 的分解秩 (T5-Base 为 64,Mistral-7B-v0.3 为 128)。
- 用到的基准:GLUE(MNLI、QNLI、SST-2、CoLA、MRPC)、SuperGLUE(BoolQ、CB、COPA、RTE、WiC)、MetaMathQA→GSM8K、OpenCodeInstruct→HumanEval、SmolTalk→IFEval,以及 DreamBooth(10 个固定主体,DINO/CLIP-I/CLIP-T 指标)——都是标准、公开可用的基准,有利于复现。
- 对想自己在国内常见环境(单机多卡或小集群,使用 Hugging Face
transformers+peft标准组合)中复现的团队来说,最容易踩坑的坐标是岭正则项 的具体数值——因为论文没有给出,建议先从常用的数值量级(比如 到 )扫一遍小范围,并监控重建误差 是否在合理范围内(数个百分点以内),再回头定标准确的 。