
前阵子把手上一个 7B 底座拉出来做客服语气的适配,顺手把 LoRA 这条链路从头到尾跑了一遍。这篇不铺概念,按我实际踩坑的顺序讲:先算显存,再调超参,最后落到部署。
全参微调不是我这种单卡能碰的。原因不在权重本身:fp16 的 7B 权重才 14GB 左右,真正吃显存的是它连带的东西——对应的梯度,加上 Adam 为每个参数保存的两份 fp32 状态。几项叠起来,总量就顶到 80GB 量级,一张 A100 80G 都很紧张,更别说消费卡。
LoRA 的思路是冻结原始权重 W₀,只在旁边挂两个低秩矩阵 A(r×k)和 B(d×r),训练时只更新 A、B。前向变成:
h = W₀x + (α / r) · B A x可训练参数从 d·k 掉到 r·(d+k),7B 上占比通常不到 1%。
这里有个初始化细节值得单独拎出来:B 是零初始化的。所以训练刚开始时 ΔW = 0,模型行为跟原来一模一样,是被训练一点点带偏的,不是接上去就崩。我理解这是 LoRA 相对"随便加一层再微调"最大的稳定性来源。
我一开始的配置用的是 peft:
from peft import LoraConfig, get_peft_model
cfg = LoraConfig(
r=16,
lora_alpha=32, # 惯例:alpha = 2 * r
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(base_model, cfg)
model.print_trainable_parameters()
# 示意输出(实际数值随底座规模变化):
# trainable params: ~16.8M || all params: 6.7B || trainable%: ~0.25%target_modules 是第一个坑:不同底座的层命名不一样,q_proj 是 LLaMA 系的名字,换架构前先 print(dir(model)) 或遍历模块名确认一遍,填错会直接报找不到对应模块。
第二个坑在 r 和 alpha 的耦合:
r 的经验值我记的是这样:只改格式 / 语气,8 够;一般领域适应 16 起步;样本多、多任务再上 32 / 64。
显存我这边记的量级(框架和序列长度影响很大,仅供参考):
两种都能用,看场景:
引擎侧我用 vLLM 起服务,它支持多 LoRA 动态加载和卸载,切换在几十毫秒级,显存里同时塞十几个小适配器没压力。
对比每层插一个小模块(Adapter 类方案),LoRA 的杀手锏就是"可合并"——后者推理时模块一直在算,纯白增延迟。
这块我要重点说,因为"LoRA 参数少所以不会过拟合"是我见过最多的错误说法,它不成立。参数少只是把过拟合的门槛抬高了一点,小数据上照样过拟合,还会灾难性遗忘——我拿几百条数据反复训,模型原本会的通用问答掉了一截,后来往训练集里掺了通用对话样本才缓过来。
验收必须两头看:
只测前者,上线才发现问题就晚了。另外还有两点最容易被漏:验证集一定要和训练集分开;训练用什么拼接格式,推理就得用同一个格式。
关键先判断:模型是"不知道",还是"不会说"。
最省力的顺序永远是:先把提示词写扎实,管不住了再上 LoRA。至于用云上 API 的场景,权重不在你手里,这条路直接排除。
LoRA 省下的是训练成本(一张卡、几十分钟),但没省钱的地方是数据——那几百条干净样本的标注和审核,才是真正的瓶颈。如果这套数据你天天都在产生,微调很划算;一年才做几次、每次口径都不一样,那基本训不出来。
改口吻,别改知识;先提示词,顶不住再训。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。