深色模式
微调方式总览:SFT / PEFT / LoRA
摘要:本文面向需要在生产环境对大语言模型(LLM)做定制化的 SRE / 平台工程师。我们厘清「全量微调 / SFT / PEFT / LoRA / QLoRA / RLHF / DPO」之间的关系与边界,给出显存、成本、迭代速度的对比,并落到可复制的 PEFT 代码片段与选型决策树。适用版本:Hugging Face
transformers≥ 4.40、peft≥ 0.11、bitsandbytes≥ 0.43、PyTorch ≥ 2.2(具体版本以各库官方文档为准,[版本相关])。
适用版本与前提
- 框架:
transformers4.40+、peft0.11+、accelerate0.30+、bitsandbytes0.43+([版本相关:PEFT 0.12+ 对LoraConfig参数如use_rslora、init_lora_weights取值有扩展,使用时请对齐文档]) - 硬件:单卡 24GB(RTX 4090 / A10G)可跑 7B QLoRA;8×A100 80GB 可跑 70B QLoRA 或 7B~13B 全量微调
- 身份与权限:训练任务通常以非 root 容器用户运行;模型权重来自 Hugging Face Hub 时需配置
HF_TOKEN,注意令牌的最小权限(只读即可,避免赋予 write 权限)
核心概念:把术语摆正
很多团队在需求评审阶段就把「微调」当成一个动作,事实上微调是一个分层谱系,从便宜到贵、从稳定到易崩依次为:
| 方法 | 训练对象 | 典型显存(7B) | 是否改基座权重 | 主要用途 |
|---|---|---|---|---|
| 全量微调 Full FT | 全部参数 | ~60–112 GB(fp16) | 是 | 行为/分布大幅改变 |
| SFT(监督微调) | 全部参数(通常) | 同全量 | 是 | 教模型「怎么做」(指令遵循) |
| PEFT(参数高效微调) | 少量新增参数 | 远小于全量 | 否(冻结基座) | 低成本适配 |
| LoRA | 低秩矩阵 A/B | ~16–28 GB | 否 | PEFT 的一种主流实现 |
| QLoRA | 4-bit 基座 + LoRA | ~6–12 GB | 否 | 单卡训大模型 |
| RLHF / DPO | 偏好对齐 | 视阶段 | 是 / 否 | 教模型「什么更好」 |
关键区分
- SFT 与 PEFT 不是同一维度:SFT 是「用监督数据训练」的目标/阶段,PEFT 是「只训少量参数」的策略。两者可组合——你可以用 LoRA 做 SFT(LoRA-SFT)。
- LoRA 是 PEFT 的子集,QLoRA = LoRA + 4-bit 量化基座。
- RLHF / DPO 是偏好对齐阶段,通常在 SFT 之后;DPO 不需要单独训练奖励模型,比 RLHF/PPO 简单稳定。
架构与原理:低秩适配如何省显存
LoRA 的核心思想:冻结预训练权重 W,对每层的投影矩阵注入可训练的低秩分解 W + ΔW = W + B·A,其中 B ∈ ℝ^{d×r}、A ∈ ℝ^{r×k},秩 r ≪ d。训练时只更新 A/B,显存与存储大幅下降。
QLoRA 进一步把基座量化为 4-bit NF4(NormalFloat4)并启用双重量化(double quantization),在 4-bit 上跑 LoRA。其内存收益来自:基座权重以 4-bit 存储 + 分页优化器(paged optimizer)。
生产实践:什么时候选哪条路
选型经验法则
- 单卡 24GB 想动 7B/13B:QLoRA,先验证可行性再考虑全量。
- 多卡集群、要最佳质量且不差钱:全量 SFT(结合 DeepSpeed ZeRO-2/3,见
infra.md)。 - 多任务、要热插拔能力:保留 LoRA 适配器,一个基座挂多个 adapter,按需加载。
- 只想让模型「更懂某个领域词汇/事实」:先做 CPT(继续预训练)(见
cpt.md),再 SFT。 - 想让模型「更符合人类偏好/更安全」:SFT 后接 DPO(见
alignment.md)。
不要盲目上全量微调
全量微调的 checkpoint 是完整模型副本(7B fp16 ≈ 14GB,70B ≈ 140GB),存储、分发、回滚成本都高;且更易过拟合与灾难性遗忘。除非有明确证据表明 LoRA/QLoRA 质量不达标,否则优先 PEFT。
操作步骤:用 PEFT 跑一次 LoRA-SFT
以下以 Llama-3.1-8B / Mistral-7B 量级、单卡 24GB+ 为例。QLoRA 在加载阶段额外加 BitsAndBytesConfig(见 lora.md)。
bash
# 环境(版本以官方文档为准,[版本相关])
pip install -U "transformers>=4.40" "peft>=0.11" "accelerate>=0.30" "bitsandbytes>=0.43" "datasets>=2.20"
python -c "import torch, peft, transformers; print(torch.__version__, peft.__version__, transformers.__version__)"
# 期望输出形如 2.2.x / 0.11.x / 4.4x.x —— 以你实际安装为准,[未实测]1
2
3
4
2
3
4
python
# lora_sft.py —— 用 PEFT 做 LoRA 监督微调
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model, TaskType
import torch
model_id = "meta-llama/Llama-3.1-8B" # 8B 参数;需接受许可证并登录 HF
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(
model_id, torch_dtype=torch.bfloat16, device_map="auto",
)
model.config.use_cache = False # 与 gradient_checkpointing 冲突,训练时关闭
lora_config = LoraConfig(
r=16, # 低秩维度,常用 8/16/32/64
lora_alpha=32, # 缩放系数,常取 2*r
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"], # Llama/Mistral/Qwen 系
lora_dropout=0.05,
bias="none",
task_type=TaskType.CAUSAL_LM,
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters() # 打印可训练比例,7B 通常 <1%
args = TrainingArguments(
output_dir="./lora-out",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4, # LoRA 学习率通常高于全量
num_train_epochs=3,
bf16=True,
gradient_checkpointing=True,
logging_steps=10,
save_strategy="steps",
save_steps=200,
)
# Trainer 需要 dataset(见 sft-data.md);此处略
# trainer = Trainer(model=model, args=args, train_dataset=ds, ...)
# trainer.train()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
验证
bash
# 1) 确认可训练参数占比(应远小于 100%)
# 上文 print_trainable_parameters() 输出示例:trainable params: 20M || all params: 8B || trainable%: 0.24% [示例数值,实际以运行结果为准]
# 2) 训练 loss 应平稳下降;若震荡请降 lr 或加 warmup
# 3) 用 lm-evaluation-harness 对比基座与微调后(见 post-eval.md)1
2
3
4
5
2
3
4
5
回滚与清理
生产变更先评估
- LoRA 适配器是独立小文件(7B 通常 50–150 MB),回滚只需切回基座或旧 adapter,无需重训。
- 全量微调的回滚要保留旧 checkpoint 目录并切换模型服务路由;删除大 checkpoint 前确认无在途推理与下游依赖。
- 用
git/ 对象存储版本化保存adapter_config.json与训练超参,保证可复现。
故障排查
- OOM:降
per_device_train_batch_size、升gradient_accumulation_steps、开gradient_checkpointing、换 QLoRA。 target_modules不匹配:PEFT 对未知模块名可能静默跳过。先用model.named_modules()打印线性层名再配置([版本相关:不同模型架构模块名不同])。- 精度异常/数值不稳定:确认
bf16在 Ampere+ 支持;老卡用fp16并谨慎。 - HF 下载 401:模型需许可证(如 Llama 系列),先
huggingface-cli login并在 Hub 接受协议。
安全与合规
数据泄露与越权
- 训练数据可能含 PII / 机密:SFT 数据进入梯度会「记忆」内容。上线前做脱敏与去重,并参考
sft-data.md的清洗清单。 - 模型权重与数据集权限:
HF_TOKEN用只读 fine-grained token,禁止把写权限令牌放进镜像或日志。 - 越权风险:训练平台若为多租户,需隔离 GPU / 存储 / 网络命名空间,避免一个团队的 checkpoint 被另一团队读取(见 k8s 安全相关文档)。
- 合规:继续预训练 / 全量微调可能带来版权与许可证问题(如商用模型权重的使用条款),上线前核对基座模型许可证。
成本与性能(估算,[未实测],请以云厂商实时报价为准)
| 方案 | 硬件 | 7B 训练时长(示例) | 单价假设 | 单轮估算 |
|---|---|---|---|---|
| QLoRA 7B | 1× RTX 4090 24GB | 50k 样本 × 3 epoch ≈ 2–4 h | ~$0.5/h | $1–2 |
| LoRA 7B | 1× A100 80GB | 1–2 h | ~$2/h | $2–4 |
| 全量 SFT 7B | 8× A100 80GB | 数小时 | ~$16/h(整机) | $数十 |
| QLoRA 70B | 1× A100 80GB | 数十小时 | ~$2/h | $数十–百 |
成本备注
上表时长为粗略区间,受 batch size、序列长度(如 2048 vs 4096)、是否开 FlashAttention 影响显著;利用率(GPU-util)建议通过 nvidia-smi / DCGM 监控,低于 60% 多半是数据加载或通信瓶颈。QLoRA 因 4-bit 反量化有少量额外计算开销,但显存收益远大于此。[成本/时长为估算,非实测报价]