16GB Mac 跑 LoRA:训练参数怎么算
以 Qwen2.5-1.5B、rank 16 和 16GB Apple Silicon Mac 为例,拆解 LoRA 的可训练参数量。说明适配层选择、rank、冻结权重精度和优化器步数如何影响训练资源与耗时。
AI 深度解读
LoRA 只训练模型约 1% 的参数,为什么还是可能撑爆内存?作者用一台 16 GB Apple Silicon Mac 上的 Qwen2.5-1.5B-Instruct 训练,把适配器参数、冻结权重、优化器状态和更新次数逐项算清楚。
结论很实用:训练参数少,不代表底座占用小;数据跑完 3 轮,也可能只有约 39 次参数更新。对准备在本地调整模型语气、输出格式的开发者,这些账比一句「支持微调」更有参考价值。
LoRA 的 1.2% 到底训练在哪里
LoRA 冻结原来的权重矩阵 W,只训练两个较薄的矩阵:A 的形状为 r × d_in,B 为 d_out × r。层的输出变为:
W·x + (α/r)·B·A·x
其中 r 是 rank,决定新增矩阵的宽度;α 控制增量的缩放。B 初始为零,因此训练起点与底座一致。单层新增参数是 r × (d_in + d_out),随 rank 线性增加。
作者使用 osFoundry 桌面应用的本地训练器,底层是 transformers + PEFT + TRL,配置为 rank 16、alpha 32。

它给每个 transformer 块的七个线性投影添加适配器:
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"]
要看的是第二行:MLP 的三个投影也参与训练,不能把这套配置理解成只调整注意力。
Qwen2.5-1.5B 有 28 个块,隐藏维度 1536,MLP 维度 8960。分组查询注意力(GQA)只有 2 个 KV 头,每头 128 维,因此 k、v 的输出维度为 256。
rank 16 时,每块新增 659,456 个参数,合计 18,464,768,约为 18.5M;相对总计 1.54B 的参数,占 1.2%。其中 MLP 占约四分之三。若按一些旧配方只适配 q、v,新增参数约为 2.2M,只有这套配置的八分之一。
讨论 LoRA 的训练规模,必须同时看 rank 和 target_modules。
内存的大头,依然是冻结的底座
作者的训练器按设备选择权重精度:
torch_dtype = torch.float16 if device == "cuda" else torch.float32
关键在 else torch.float32:Apple GPU 使用 MPS,这条路径会加载 32 位权重。作者给出的理由是 MPS 半精度训练容易出现 NaN 损失,这是该训练器选择精度的依据。
冻结只代表不更新权重,权重仍然要装进内存。1.54B 参数乘以 4 字节,底座约占 6.2 GB。
适配器反而小得多:18.5M 个 fp32 参数约 74 MB,梯度再占 74 MB;AdamW 为每个参数保存两份动量估计,再占 148 MB。合计约 0.3 GB。
还有激活值,也就是前向计算中为反向传播保留的中间结果。它们随 batch size 和序列长度增长。训练器把序列限制在 2048 tokens,短聊天样本通常远低于上限。
按作者估算,16 GB Mac 约有 12 GB 可供 GPU 使用,1.5B 底座尚有余量;7B 底座仅 fp32 权重就约需 30 GB。QLoRA 用 4 位冻结权重缓解这部分压力,但原文所用的 bitsandbytes 4 位路径需要 NVIDIA GPU,所以这次 Mac 实验采用普通 LoRA。
200 条数据跑 3 轮,实际只更新约 39 次
这次训练的 batch size 为 4,梯度累积为 4 步,因此一次优化器更新对应 16 个样本。
200 对训练数据,每轮产生 200 ÷ 4 = 50 个前向、反向批次;再除以 4,约为 13 次优化器更新。3 轮合计约 39 次,其中前 10 次用于学习率 warmup。学习率为 2e-4,之后线性衰减。
这里的 13 次是近似计算,最后不足累积步数的批次如何处理,仍取决于训练实现。

作者据此判断:几百条回答足以改变语气,却不足以教会模型新知识。它解释了为什么训练进度走完,模型可能只是「更像示例那样回答」。
训练后只保存 196 个适配层的 A、B,文件为几十 MB。应用再将适配器转为 LoRA GGUF,由 llama.cpp 加载到底座的量化副本上;本例使用 qwen2.5-1.5b-instruct,量化格式为 Q4_K_M。多个任务或语气可以共用底座,各自增加一份适配器。

但训练时底座是 fp32,推理时是 4 位。作者认为小型风格适配器通常不容易显出差异,同时提醒:运行时加载适配器,与先合并再重新量化,行为可能略有不同。
我的判断
这篇最有用的是把「可训练参数」「内存占用」「优化器更新」分开计算。三者对应不同瓶颈,单看 1.2% 很容易误判机器能否承受训练。
需要保留意见的是效果结论。原文没有提供知识任务评测、不同数据量对照,也没有量化训练前后的风格变化。「改变语气,不能教新知识」应当理解为作者对这次小数据训练的经验判断,不能扩展成 LoRA 的能力边界。
同样,MPS 使用 fp32、bitsandbytes 路径的设备要求,都与具体软件实现有关,不能据此推断所有 Mac 微调方案。原文也没给出训练耗时,无法据这些内存数字估算等待时间。
如果需求只是调整回答口吻,我会先用提示词和示例建立对照,再判断训练的收益;如果需求是回答持续变化的项目资料,也不能仅凭这次风格实验就决定采用微调。
能拿来做什么
- 准备在本地训练前,先列出底座精度、target_modules、rank、序列长度和 batch size,分别估算冻结权重与训练状态,给激活值留出空间。
- 为日常 AI 编程助手调整回答风格时,先用固定提示词比较底座与适配器,检查代码解释、格式和语气是否按预期变化。
- 整理训练数据时,同时计算有效 batch 和优化器更新次数。面对 200 条数据、约 39 次更新的配置,先设定有限、可检验的目标。
- 若最终通过 llama.cpp 和 Q4_K_M 部署,就在这条推理路径上验收;采用合并后重新量化的方案时,再单独比较输出。