算清 LoRA 微调的参数与内存开销
文章以 16 GB Apple Silicon 设备上的 Qwen2.5-1.5B-Instruct、Rank 16 配置为例,拆解适配层选择、权重精度和训练步数。通过低秩矩阵公式说明可训练参数如何随 Rank 与层宽变化,补充“只训练约 1% 参数”遗漏的资源约束。
AI 深度解读
「LoRA 只训练模型约 1% 的参数」,并不能回答你的电脑能不能跑、训练多久、最后能改出什么效果。作者用 Qwen2.5-1.5B-Instruct、Rank 16 和一台 16 GB Apple Silicon Mac,把这些问题拆成了可计算的账。
结论很实用:训练参数少,不代表基础模型占用小;数据跑了几轮,也不代表更新了很多次。想尝试本地微调,先看适配器挂在哪里、基础权重用什么精度,以及实际有多少次参数更新。
那 1.2% 的参数,究竟加在哪里
LoRA 冻结原来的权重矩阵 W,另外训练两个较薄的矩阵 A 和 B。对于形状为 d_out × d_in 的 W,A 的形状是 r × d_in,B 是 d_out × r,层的输出变成:
W·x + (α/r)·B·A·x
可以把它理解为:原模型照常计算,旁边增加一条可训练的修正路径。B 初始化为零,因此训练开始时,输出与基础模型一致。
每层新增参数量是 r × (d_in + d_out),随 Rank 线性增长。作者采用 Rank 16、Alpha 32,并给每个 Transformer 块的七个线性投影添加适配器:
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"]
要看的是整个列表:这里既覆盖注意力投影,也覆盖 MLP,不能只凭 Rank 判断训练规模。
Qwen2.5-1.5B 有 28 个块,Hidden Size 为 1536,MLP 大小为 8960。它采用分组查询注意力(GQA),2 个 KV-Heads 各有 128 维,所以 k、v 投影的输出维度只有 256。
按 Rank 16 计算,每块新增 659,456 个参数,合计 18,464,768,约 18.5M,占总计 1.54B 参数的 1.2%。其中约四分之三来自 MLP。若只适配 q、v,参数约为 2.2M,只有前者的八分之一。
所以,不同配置都叫 LoRA,训练容量也可能差得很远。

能不能装进内存,先看被冻结的模型
作者使用 osFoundry 的本地训练器,底层是 transformers、PEFT 和 TRL。其精度选择如下:
torch_dtype = torch.float16 if device == "cuda" else torch.float32
关键在 else torch.float32:这个实现会在 Apple 的 MPS 上使用 32 位权重。作者给出的原因是,半精度训练容易遇到 NaN Loss。
于是,冻结的基础模型仍需约 1.54B × 4 Bytes ≈ 6.2 GB。被冻结只是不用更新,并没有从内存里消失。
相比之下,适配器的账很小:18.5M 个 fp32 参数约 74 MB,梯度再占 74 MB,AdamW 的两份矩估计占 148 MB,合计约 0.3 GB。除此之外,还要为激活值留空间,其大小受批大小和序列长度影响。训练器把序列上限设为 2048 Token。
可训练参数的比例,不能直接当成训练内存的比例。
作者按 16 GB Mac 的 GPU 可使用约 12 GB 来估算,1.5B 模型尚有空间;7B 模型仅 fp32 基础权重就约需 30 GB。QLoRA 用 4 Bit 存储冻结权重,正是为了缩减这部分开销。但原文讨论的 bitsandbytes 4 Bit 路径需要 NVIDIA GPU,不能直接搬到这套 Mac 配置上。
训练三轮,可能只更新了约 39 次
作者的批大小是 4,梯度累积步数也是 4,因此一次优化器更新累计处理 16 个样本。
对于 200 对训练数据,每轮有 200 ÷ 4 = 50 个前向、反向批次,再除以 4,约有 13 次优化器更新。训练 3 轮,总计约 39 次,其中前 10 次用于学习率预热。学习率为 2e-4,随后线性衰减。
这解释了为什么「已经训练三轮」听起来很多,实际更新机会却有限。约 39 次更新是原文的近似计算,具体边界处理还要看训练器实现。
作者认为,这种规模足以调整语气,却不足以教授新知识,并称与其实际观察一致。不过原文没有提供知识测试、风格指标或对照实验。「几百条回答适合调整表达」可以作为经验参考,「无法学习新知识」则不宜当成普遍定律。

最后保存的是适配器,推理还要依赖基础模型
训练结束后,保存的是 196 个被适配投影层的 A、B 矩阵,而不是完整基础模型。这里的 196 来自 28 × 7,不是 196 个 Transformer 块。
应用随后把适配器转换成 GGUF-LoRA 文件,由 llama.cpp 加载到量化后的基础模型上。文中的推理配置是 qwen2.5-1.5b-instruct,量化格式为 Q4_K_M。基础模型可以共用,不同语气或任务各保存一份适配器,每份只需几十 MB。
这里有个容易忽略的差别:训练时基础权重是 fp32,推理时却是 4 Bit。作者称,小型风格适配器通常看不出明显影响,但没有给出误差测量。先合并再重新量化,与推理时动态加载适配器,也可能出现行为差异。

我的判断
这篇最有价值的地方,是把「轻量微调」拆成了参数、内存和更新次数三笔账。它适合帮助程序员检查配置,也能纠正「只训练 1%,电脑负担就很轻」的直觉。
局限在于,它展示的是一套具体实现。MPS 使用 fp32、约 12 GB 可用内存,以及 bitsandbytes 的硬件限制,都应放回作者的环境理解,不能扩大成所有 Mac 训练方案的结论。
如果只是少量语气或输出格式要求,我会先用提示词建立基线,再判断是否需要 LoRA;如果目标是查询经常变化的资料,则应另行评估检索增强方案。原文没有比较这些方案的效果、成本与延迟,不能据此认定微调更划算。
能拿来做什么
- 开始本地训练前,记录基础模型精度、
target_modules、Rank 和序列上限,分别估算基础权重与训练状态的占用,给激活值留出余量。 - 准备几百条代码解释或问答风格样本时,先计算实际优化器更新次数,再评估训练轮数、梯度累积和 Warmup 是否匹配。
- 在本地 AI 工具中加载 GGUF-LoRA 后,用固定提示词对比基础模型与适配器,并检查最终量化配置下的输出,避免只凭训练 Loss 判断效果。