LoRA 与 QLoRA
LoRA 解决“要训练多少参数”,QLoRA 进一步解决“冻结的基座权重以什么精度驻留”。二者经常一起使用,但不是同一个概念。
LoRA 的低秩更新
线性层原权重为 。LoRA 冻结 ,不学习完整的 ,而是学习两个小矩阵:
其中 、,。可训练参数从 降为约 。
LoRA 带来的直接收益是:优化器状态和梯度显著减少;每个任务只需保存很小的 Adapter;同一个基座可以挂载多套 Adapter。它不意味着基座不占显存,也不保证推理天然更快。
关键参数
r=8/16/32、alpha=2r 只是常见起点,不是定律。更合理的做法是固定数据和有效 batch,比较多个 rank 的验证集效果、训练时间和 Adapter 大小。
PEFT 还支持 rank-stabilized LoRA:use_rslora=True 时缩放改为 ,较大 rank 下可能更稳定;是否采用仍应由本地实验决定。
QLoRA 多做了什么
QLoRA 的训练路径是:
- 将冻结的基座权重以 4-bit 量化形式加载;
- 前向/反向时按计算 dtype 反量化参与计算;
- LoRA 参数本身仍以 BF16/FP16 等可训练精度保存;
- 优化器只维护 Adapter 的状态。
QLoRA 论文的重要组件包括:
- NF4:针对近似正态分布权重设计的 4-bit 数据类型;
- Double Quantization:连量化常数也进一步量化;
- Paged Optimizer:缓解长序列或梯度检查点造成的显存尖峰。
“4-bit 训练”并不是直接更新 4-bit 权重。bitsandbytes 只支持在量化模型之上训练额外参数;基座仍被冻结。
原论文曾在特定硬件、模型和训练设置下将 65B 模型放入单张 48GB GPU;不能把这个结果外推成任意 65B、任意序列长度都只需 48GB。
显存花在哪里
训练显存不只等于模型文件大小:
- LoRA 主要减少梯度和优化器状态;
- QLoRA 同时压缩冻结基座权重;
- 梯度检查点减少激活值,但增加重计算;
- 减小序列长度往往比减小 batch 更能快速止住 attention 激活的增长;
- 多卡下
device_map="auto"不是分布式训练方案,应该使用 DDP/FSDP/DeepSpeed。
可运行的 PEFT + TRL QLoRA
下面使用 DeepSeek-R1-Distill-Qwen-1.5B。它是 Qwen2 架构的蒸馏小模型,适合教学;完整 DeepSeek-R1/V3 是 671B MoE,不能套用同一份单卡脚本。
环境
推荐 NVIDIA GPU + Linux/WSL2。先按 PyTorch 官网安装与驱动匹配的 PyTorch,再安装训练包:
Windows PowerShell 激活命令是 .venv\Scripts\Activate.ps1。bitsandbytes 支持的 CUDA/ROCm/Intel 后端会变化,首次训练前应核对官方兼容表并运行一个小样本冒烟测试。
保存为 train_qlora.py:
运行:
当前 API 的几个要点:
- TRL 的参数是
SFTConfig(max_length=...),不是旧教程中的max_seq_length; SFTTrainer当前用processing_class=tokenizer,不是旧版tokenizer=;- Transformers v5 文档推荐模型加载使用
dtype=,旧环境可能仍使用torch_dtype=; - conversational dataset 可直接提供
messages; assistant_only_loss=True只有在 chat template 能生成 assistant mask 时才能打开,不能盲用。
改成普通 LoRA
普通 LoRA 只需取消 BitsAndBytesConfig 和 prepare_model_for_kbit_training():
其余 Trainer 代码不变。训练前务必打印可训练参数;如果比例异常,优先检查 target module,而不是直接开跑。
target_modules 怎么选
经典 LoRA 常只适配 attention 的 q_proj、v_proj;QLoRA 常把 attention 与 MLP 的所有线性层都纳入。PEFT 可用:
但并非所有模型都采用同样的层名和线性层类型。先检查:
如果目标是分类头、卷积或自定义架构,需要按模型模块树显式配置,不能把 Llama/Qwen 的层名复制过去。
加载 Adapter 推理
Adapter 目录包含 PEFT 配置和增量权重,不包含完整基座。加载时会根据配置找到基座:
合并 Adapter
某些推理引擎可以直接加载 LoRA;不支持时再合并:
合并后体积接近完整基座,失去动态切换 Adapter 的优势;发布时还要同时满足基座与数据的许可证。
选参和排错顺序
建议从小规模网格开始,而不是凭感觉一次训到底:
遇到 OOM,按这个顺序排查:
- 缩短
max_length,检查是否存在异常长样本; - 减小 micro-batch,增加 gradient accumulation 保持有效 batch;
- 开梯度检查点和 BF16/FP16;
- 从 LoRA 切到 QLoRA;
- 再考虑 DeepSpeed ZeRO、FSDP 或 offload。
不要先把 r 从 16 降到 4:LoRA 可训练参数通常不是激活显存的主要部分。
常见误区
- 把 QLoRA 当推理量化:它描述训练方法;最终部署可另选 BF16、AWQ、GPTQ 或 GGUF。
- 把
device_map="auto"当多卡训练:它更偏向大模型加载/推理;训练应交给 Accelerate、FSDP 或 DeepSpeed。 - 重复注入 Adapter:显式
get_peft_model()后,不要再向 Trainer 传同一个peft_config。 - 忘记保存 tokenizer:推理模板或特殊 token 缺失会让正确权重也生成异常。
- 只看 Adapter 大小:峰值显存常被长序列激活、临时缓冲和 eval generate 主导。