LoRA / QLoRA / PEFT 微调原理与取舍
LoRA / QLoRA / PEFT 微调原理与取舍
微调(Fine-Tuning)大概是这门课最容易让人心态爆炸的一段:QLoRA、PEFT、量化、SFT、DPO……名词像开会点名一样往外蹦。
先给你吃颗定心丸,也是我踩过坑才敢说的一句话:大多数你以为"要微调"的场景,其实用 Prompt 或 RAG 就搞定了,而且更便宜、更快、更好维护。 这篇先帮你判断到底该不该微调,再把 LoRA/QLoRA 讲成人话(带一点点原理和一份能跑的配置),最后讲微调完怎么评估、怎么防翻车——这一步 90% 的教程都不讲,却最容易出事。
先别急着调:三条路先选对
有个"知识问答不准"的需求找上你,新手第一反应是"微调一个"。慢着,先过这张表:
| 方法 | 解决什么问题 | 成本 | 一句话选型建议 |
|---|---|---|---|
| Prompt / 上下文工程 | 调整行为、格式、语气 | 极低 | 能用 prompt 解决就别碰后面两个 |
| RAG | 让模型用上它没见过的新知识/私有数据 | 中 | "答得不对是因为不知道"→ 用 RAG |
| 微调 (Fine-Tuning) | 固化风格/领域语感/输出结构,或省 token | 高 | "知识对但腔调/格式老不对、或量大要压成本"→ 才微调 |
记死这条:微调擅长教模型**"怎么说"(风格、格式、专业语感),不擅长塞"新知识"**(那是 RAG 的活)。想靠微调给模型灌一本最新产品手册,大概率灌不进去还灌出幻觉。
说实话,我见过太多团队一上来就要微调,烧了几天 GPU,最后发现一个写好的 system prompt + 几条 few-shot 就达标了。微调是最后一招,不是第一招。
动手前拿这四个问题自查一遍,全答"是"再往下走:
- Prompt 里塞 3-5 条 few-shot 示例,效果还是不达标?
- 问题出在"腔调/格式/语感不对",而不是"知识不知道"?
- 你能凑出至少几百条高质量的"输入 → 期望输出"数据?
- 你有办法在微调后量化对比效果(有测试集,不是靠感觉)?
有一条答"否",先回去补那一条,别开训。
两个真实案例:一个不该调,一个该调
案例一(不该调):有个做跨境电商的团队找我,说要微调一个"懂他们产品的客服模型",预算都批了。我问了一句:客服答错的问题,是"不知道产品参数"还是"说话方式不对"?他们查了一周的客服日志,答案是前者——90% 的差评是"答非所问",因为产品目录每周都在更新。产品目录是新知识,微调灌不进去,灌进去下周也过期了。 最后方案是 RAG 挂产品库 + 一个 500 字的 system prompt 定语气,一分钱 GPU 没烧,两周上线。
案例二(该调):另一个做法律文书的团队,需求是把口语描述改写成固定格式的合同条款。知识不缺(模板都在 prompt 里),但强模型写出来的条款每次格式都有微妙的不同——标点、编号、措辞时松时紧,法务每份都要手改。few-shot 塞到 8 条还是压不稳,prompt 也被撑到 3000 token,量大成本难看。这种"知识对、格式老不对、调用量大"的场景,就是微调的主场:2000 条人工校对过的改写对,QLoRA 训完格式一致性直接拉满,system prompt 从 3000 token 砍到 200。
两个案例放一起看,判断标准就一句话:错在"不知道"→ RAG;错在"说不对"→ 微调。
全量微调为什么"调不起"
术语:全量微调 (Full Fine-Tuning)
- 一句话解释:把模型几十亿个参数全部重新训练一遍。
- 形象比喻:为了改一句口头禅,把一个人从小到大重新教育一遍——大炮打蚊子。
- 工作中怎么用:效果上限高,但个人/小团队基本玩不起。
- 最常踩的坑:显存。训一个 7B 模型的全量微调,除了模型本身,优化器状态(Adam 要为每个参数存两份额外数据)+ 梯度加起来要几十上百 GB 显存,一张消费级 24G 的 4090 想都别想。
粗算一下 7B 全量微调的显存账,你就懂为什么劝退:
模型权重(fp16) : 7B × 2 字节 ≈ 14 GB
梯度(fp16) : 7B × 2 字节 ≈ 14 GB
Adam 优化器状态 : 7B × 8 字节 ≈ 56 GB ← 大头在这
─────────────────────────────────────────
合计 ≈ 84 GB+ → 得 A100/H100 级别
于是就有了 PEFT。
术语:PEFT (Parameter-Efficient Fine-Tuning,参数高效微调)
- 一句话解释:冻住原模型不动,只额外训练一小撮新参数。
- 形象比喻:不重装整个操作系统,只装一个小插件。主体没变,行为变了。
- 工作中怎么用:LoRA 是 PEFT 里最流行的一种;个人显卡微调基本都走这条。
- 最常踩的坑:以为 PEFT 效果一定比全量差。多数任务上 LoRA 能逼近全量微调,差距小到不值那份成本。
LoRA:只训练一个"小补丁"(原理版)
术语:LoRA (Low-Rank Adaptation,低秩适配)
- 一句话解释:冻结原权重,只在旁边挂一对小矩阵学"改动量",原模型一个参数都不动。
- 形象比喻:给厚厚一本教科书夹便利贴批注。书本身没改,但你按批注去读,结论就变了。
- 工作中怎么用:训练出来的"补丁"(adapter)才几 MB~几百 MB,一个底座能挂多个不同任务的补丁,随时切换。
- 最常踩的坑:
rank (r)和alpha两个旋钮乱设。r 太小学不动,太大又浪费还可能过拟合。
原理其实一句话能说清。微调本质是给原权重矩阵 W 学一个"改动量" ΔW,让 W_new = W + ΔW。全量微调的 ΔW 和 W 一样大(几十亿数)。LoRA 赌一件事:这个 ΔW 其实很"扁",能用两个瘦长的小矩阵相乘近似出来:
ΔW ≈ B × A
│ └─ A: 形状 [r, 原维度] (瘦)
└───── B: 形状 [原维度, r] (长)
原来要训练一整块 d×d(比如 4096×4096 ≈ 1678 万个数)
现在只训 B 和 A 两条(r=16 时 ≈ 4096×16×2 ≈ 13 万个数)
→ 参数量降到约 0.8%
这里的 r(秩) 就是那个"瘦"的程度:r 越小,补丁越小越省,但表达能力越弱。原模型冻着不动,只有 B、A 在学,所以显存和存储都暴降。
(这里的 W 就是 Transformer 注意力层里那些 Q/K/V 投影矩阵——如果你还没搞清 Q/K/V 是什么,建议先补那一篇,回来看 target_modules 会豁然开朗。)
两个你一定会调到的参数:
| 参数 | 管什么 | 经验起手 | 调大/调小 |
|---|---|---|---|
| r (rank) | 补丁容量 | 8 / 16 / 32 | 简单任务 8,复杂点 16–32;再大收益递减还易过拟合 |
| alpha | 补丁"音量"(缩放) | 2 × r(如 r=16→alpha=32) | 实际生效强度≈ alpha/r,一般跟着 r 走 |
QLoRA:让 7B 挤进一张 24G 显卡
LoRA 已经省了训练参数,但底座模型本身加载进显存还是很占地方(7B 的 fp16 就 14GB)。QLoRA 再补一刀。
术语:量化 (Quantization)
- 一句话解释:把模型参数从高精度(16 位)压成低精度(4 位)存,省显存。
- 形象比喻:高清电影压成手机能存的清晰度。画质掉一点点,体积小一大截。
- 工作中怎么用:4-bit 量化能把模型显存占用砍到约 1/4(14GB → 约 3.5–4GB)。
- 最常踩的坑:量化会损失一点精度。对话/写作类基本无感,但数值精算类任务要实测别硬上。
术语:QLoRA
- 一句话解释:先把底座模型 4-bit 量化塞进显存,再在上面挂 LoRA 补丁训练。
- 形象比喻:先把大件家具拆小搬进小房间(量化),再在里面贴便利贴改造(LoRA)。
- 工作中怎么用:让"消费级单卡微调 7B~13B"从幻想变成现实,是目前个人微调的主流方案。
- 最常踩的坑:4-bit 用的
NF4数据类型 +double quantization这些配置,照抄成熟脚本(Unsloth / PEFT 官方示例)就行,别自己瞎调。
三个词的关系钉死:
PEFT = 大类思想:只训练一小撮参数
└─ LoRA = PEFT 的具体做法:挂低秩"补丁"(B×A)
└─ QLoRA = LoRA + 4-bit 量化底座:再省一大截显存
显存实操档位(7B,随序列长度/batch 浮动):
| 方案 | 大致显存 | 消费级卡够不够 | 一句话记忆 |
|---|---|---|---|
| 全量微调 | 80G+ | ❌ 得 A100/H100 | 小团队别想 |
| LoRA (16-bit 底座) | ~16–24G | 勉强(24G 卡紧) | 够用但紧 |
| QLoRA (4-bit 底座) | ~6–12G | ✅ 舒服 | 个人微调首选 |
环境先排雷:锁版本,别裸装
写代码之前先说个丑事。我第一次跑 QLoRA,照着一篇教程抄,ImportError 报了一晚上。教程本身没错——错在它是十个月前写的,而 peft 和 trl 这两个库的 API 变得比天气还快。微调生态的版本坑,比微调本身难缠。
安装时把版本范围锁死(下面这组是互相兼容的搭配,别一把 pip install -U 拉最新):
pip install "transformers>=4.51,<5" \
"peft>=0.15,<0.18" \
"bitsandbytes>=0.45.0" \
"trl>=0.17,<0.20" \
"accelerate>=1.3" \
"datasets>=3.2"
跑通之后立刻 pip freeze > requirements.txt 留底。三个月后你换机器重跑,会感谢今天的自己。
网上旧教程代码跑不通时,先对号入座这张表,八成是版本坑:
| 报错/症状 | 根因 | 怎么修 |
|---|---|---|
cannot import name 'prepare_model_for_int8_training' | 这个函数在新版 peft 里已删除 | 换 prepare_model_for_kbit_training(新 API) |
SFTTrainer 报 unexpected keyword argument 'max_seq_length' | TRL 0.12 起把 max_seq_length / packing 这类参数挪进了 SFTConfig | 参数写进 SFTConfig(...) 再传给 trainer |
Trainer 警告 tokenizer 参数已弃用 | transformers 4.46 起改名 | 传 processing_class=tokenizer |
bitsandbytes was compiled without GPU support | bitsandbytes 只支持 Linux + NVIDIA CUDA | Mac / 无 N 卡的机器装了也白装,直接用 Colab/Kaggle 的免费 GPU |
| 装完 import 直接 segfault / CUDA error | bitsandbytes 版本和 CUDA 驱动不匹配 | 按官方兼容表装对应版本,别硬升 |
最后一条单独敲黑板:bitsandbytes 认死 Linux + NVIDIA。 Apple Silicon 的 Mac 本地跑不了 QLoRA(这也是为什么练习里让你用 Colab)。别在自己的 MacBook 上跟安装报错搏斗两小时——那是打不赢的仗。
上手代码:一份能跑的 QLoRA 配置
不用手撸,认识关键几行就行。下面是 PEFT + bitsandbytes 的标准骨架:
# 依赖安装见上一节的锁版本命令,别裸装最新版
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
import torch
# ① 4-bit 量化配置(这就是 QLoRA 的 "Q")
bnb = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NF4:为正态分布权重优化的 4-bit 类型
bnb_4bit_use_double_quant=True, # 二次量化,再省一点
bnb_4bit_compute_dtype=torch.bfloat16, # 计算时临时升回 bf16
)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-7B", quantization_config=bnb, device_map="auto")
# ② LoRA 补丁配置(这就是 "LoRA")
lora = LoraConfig(
r=16, lora_alpha=32, lora_dropout=0.05,
target_modules=["q_proj","k_proj","v_proj","o_proj"], # 给注意力的 Q/K/V/O 挂补丁
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora)
model.print_trainable_parameters()
# → trainable params: ~0.8% ← 只有这么点在训,其余全冻结
图解说明:
- 这段在干嘛:先把 Qwen2.5-7B 用 4-bit 量化装进小显存(①),再挂上只占 0.8% 参数的 LoRA 补丁(②)。
- 先看哪里:先看
load_in_4bit=True(省显存的开关),再看r=16, lora_alpha=32(前面讲的两个旋钮),最后看print_trainable_parameters打印的比例。 - 关键判断:
target_modules决定给哪些层挂补丁——挂注意力的 q/k/v/o 是最常见的稳妥选择;想更强可加上 FFN 层,但显存和过拟合风险都上升。
训练超参一般这样起手(用 TRL 的 SFTTrainer):learning_rate=2e-4、num_epochs=1~3、batch_size 看显存(配 gradient accumulation 凑等效大 batch)。epoch 别贪多,2–3 轮往往就够,多了就开始背题(过拟合)。
把训练跑起来:SFTTrainer 完整骨架
上面只挂好了补丁,真正开训还差一段。注意这是 TRL 0.12 之后的新写法——超参进 SFTConfig,不再直接传给 trainer(旧教程的写法会报 unexpected keyword argument,前面版本表里提过):
from trl import SFTTrainer, SFTConfig
from datasets import load_dataset
dataset = load_dataset("json", data_files="train.jsonl", split="train")
split = dataset.train_test_split(test_size=0.1) # 留 10% 当验证集,别省这步
config = SFTConfig(
output_dir="./qlora-out",
num_train_epochs=2, # 2-3 轮起手,别贪
per_device_train_batch_size=2,
gradient_accumulation_steps=8, # 等效 batch = 2×8 = 16
learning_rate=2e-4,
max_length=1024, # 序列长度,OOM 时第一个砍它
logging_steps=10, # 每 10 步打一次 loss,盯曲线用
eval_strategy="epoch", # 每轮结束跑一次验证集
save_strategy="epoch",
bf16=True,
)
trainer = SFTTrainer(
model=model, # 上一段挂好 LoRA 补丁的模型
args=config,
train_dataset=split["train"],
eval_dataset=split["test"],
processing_class=tokenizer, # 4.46+ 的名字,不叫 tokenizer 了
)
trainer.train()
trainer.save_model("./qlora-out/adapter") # 存的只是补丁,几百 MB
图解说明:
- 这段在干嘛:把数据切成训练/验证两份,配好超参,交给
SFTTrainer跑完整训练循环,最后只保存 LoRA 补丁。 - 先看哪里:先看
train_test_split(验证集是防背题的保险丝),再看gradient_accumulation_steps(显存不够时凑等效大 batch 的关键),最后看save_model存出来的目录——里面是 adapter 不是完整模型。 - 关键判断:
eval_strategy="epoch"让每轮结束都有一个验证 loss。训练 loss 一直降、验证 loss 开始回升 = 过拟合信号,停在验证 loss 最低的那一轮。
训练时盯什么:loss 曲线的三种翻车信号
开训之后不是挂机等结果,loss 曲线会提前把事故预告给你:
- loss 纹丝不动(一直飘在 2 点几、3 点几):九成不是学习率的锅,是数据的锅。先跑
print_trainable_parameters()确认可训参数不是 0(target_modules写错模块名,模型就整个冻着,一个参数都没在学);再检查数据有没有套对 chat template(见下一节)。这两个都排除了,再试着把学习率从 2e-4 往上调一档。 - loss 断崖式掉到接近 0:不是天才降临,是背题了。数据太少、重复太多,或 epoch 太狠。这时候模型对训练集倒背如流,见到新输入就露馅。
- loss 变成 nan:学习率太大,或 fp16 数值溢出。降学习率、compute dtype 换
bfloat16,一般能救回来。
一句话记忆:loss 不降先怀疑数据,loss 太低先怀疑背题,loss 是 nan 先怀疑精度。
数据比参数重要:格式与数量
微调效果 70% 取决于数据质量,不是参数。最常见的是指令微调 (SFT, Supervised Fine-Tuning) 格式——一堆"输入→期望输出"的例子:
{"instruction": "把这句话改写得更专业", "input": "这个东西还行吧", "output": "该方案整体表现符合预期。"}
{"instruction": "把这句话改写得更专业", "input": "赶紧弄一下", "output": "请尽快安排处理。"}
几条铁律(踩过才知道有多疼):
- 少而精 > 多而杂。几百条高质量、风格一致的数据,好过几万条乱七八糟的。数据里的坏习惯,模型学得比好习惯还快。
- 格式要统一。你希望它输出什么样,训练数据就得字字是那个样,包括标点和结构。
- 留验证集。留一小撮模型没见过的数据评估(为什么?见 ML/DL 基础 的过拟合)。
还有一个新手必踩、报错还不报在脸上的坑——对话模板:
术语:Chat Template(对话模板)
- 一句话解释:把 instruction/input/output 拼成模型预训练时认识的那种对话格式的"套壳规则"。
- 形象比喻:信写得再好,装错信封对方也拆不出来——每家模型认自己家的信封。
- 工作中怎么用:用
tokenizer.apply_chat_template()套模板;TRL 的SFTTrainer遇到标准 messages 格式会自动处理。 - 最常踩的坑:Qwen、Llama 各家的模板互不相同。拿 A 家格式的数据训 B 家模型,loss 照样在降(它总能学到点什么),推理时输出却前言不搭后语——这种"训练像成功了"的假象最坑人。
大概要多少条?按任务类型给个手感:
| 任务 | 大致数据量 | 一句话记忆 |
|---|---|---|
| 固定风格/语气改写 | 几百 ~ 1k | 风格活,少量就见效 |
| 特定输出结构/格式(如固定 JSON) | 1k ~ 5k | 要稳,得多喂几遍模式 |
| 领域语感(法律/医疗腔) | 5k ~ 数万 | 越"专业"越吃量 |
真实数据不够怎么办?先别急着降标准凑数——用强模型生成再人工筛,往往比硬凑的真实数据质量还稳,具体套路见合成数据与数据增强。
⚠️ 微调完别急着上线:评估 + 灾难性遗忘
这是最容易被跳过、又最容易翻车的一步。
术语:灾难性遗忘 (Catastrophic Forgetting)
- 一句话解释:为了学会新任务,模型把原来会的通用能力忘了。
- 形象比喻:为了苦练一个高难舞步,结果把走路都练岔了。
- 工作中怎么用:微调后一定要同时测新任务和旧的通用能力,别只看新任务分数上去就庆祝。
- 最常踩的坑:只在自己那点微调数据上验,发现"完美!",一上线才发现模型变得只会那一件事、别的全退化了。LoRA 因为只动一小撮参数,遗忘比全量微调轻,但不是不会。
微调后的评估至少三条腿:
- 任务集:你目标任务的独立测试集(模型没见过的),看有没有真学会。
- 回归集:一批通用问题(常识、其它格式),对比微调前后,看有没有退化(防遗忘)。
- 人工抽检:自动分再高,也要人眼扫一批真实输出——很多"腔调不对/胡说"自动指标测不出来。
怎么把这三条腿搭成可复跑的 eval 流程(而不是每次手工问几句),评估与质量监控有完整做法,微调玩家迟早要用上。
顺带认个进阶名词,直播会提:SFT → DPO / RLHF 是一条链。SFT 教它"会说"(照着示例学);DPO/RLHF 再用"人类更偏好 A 还是 B"的偏好数据,教它"说得更讨人喜欢"。个人项目多数到 SFT(+LoRA/QLoRA)就够了,DPO 是想进一步对齐口味时才上。
训练完的补丁怎么用:挂载还是合并
训练结束你手里多了个几十~几百 MB 的 adapter 目录。上线有两条路,选错会给自己挖坑:
路线 A:底座 + 补丁分开挂载。 推理时先加载原底座,再把 adapter 挂上去。好处是一个底座能同时伺候多个任务——客服腔一个补丁、法务腔一个补丁,按请求切换;vLLM 这类推理引擎原生支持 multi-LoRA serving,多任务省显存省到笑。代价是推理时多一次矩阵加法(通常无感),以及部署链路多一个要管理的文件。
路线 B:merge_and_unload() 合并成单模型。 把补丁"焊死"进底座权重,输出一个普通模型,之后当成任意开源模型部署、量化、分发都行。发布最省心,但合并是单向的——合完再想改补丁,只能回头重训。
我的私房建议:任务多、还在迭代 → 挂载;就一个任务、追求部署最简 → 合并。不管走哪条,合并前先把 adapter 目录备份一份,那才几百 MB,丢了哭都来不及。
合并出来的模型怎么本地跑、怎么选推理引擎,接着看开源模型、本地部署与模型路由;上生产后的算力账,看部署与成本优化。
顺手算笔账:微调什么时候真省钱
开头选型表里写微调能"省 token",这句话值得展开算一次,因为它是很多团队微调的真实动机:
不微调:3000 token 的 system prompt(规则 + 8 条 few-shot)
× 每天 10 万次调用
= 每天 3 亿输入 token 花在"重复讲规则"上
微调后:规则和格式焊进了权重,prompt 砍到 200 token
每天省下 2.8 亿 token 的输入费
代价 = 一次性训练成本(QLoRA 租卡几十美元)+ 自托管推理
但注意两个前提,少一个这笔账就不成立:
- 调用量要够大。一天几百次调用,省的 token 钱可能几年都回不了训练 + 自托管的本。
- 自托管本身有账。开源模型 + 补丁得自己伺候 GPU,这部分成本怎么估,部署与成本优化里有完整算法。
一句话记忆:微调省钱的前提是"量大 + prompt 长",两者缺一,先别拿省钱当理由。
⚠️ 常见陷阱
| 陷阱 | 后果 | 怎么办 | 一句话记忆 |
|---|---|---|---|
| 拿微调塞新知识 | 灌不进 + 幻觉变多 | 新知识用 RAG,微调只管风格/格式 | 知识找 RAG,腔调找微调 |
| 数据几十条就开训 | 学不到东西或直接过拟合 | 起码几百条高质量、风格统一 | 几百条起步,少而精 |
| r / alpha 拍脑袋 | 学不动或过拟合 | 起手 r=16、alpha=32,再按效果调 | alpha 跟着 2×r 走 |
| epoch 贪多 | 背题、遗忘加重 | 2–3 轮起,盯验证 loss 早停 | 会了就停,别复读 |
| 只测新任务不测旧能力 | 灾难性遗忘没发现就上线 | 加"回归集"对比微调前后 | 新旧一起考 |
| 数值精算任务硬上 4-bit | 精度掉出问题 | 这类任务先实测,必要时用更高精度 | 精算任务先实测 |
| CUDA OOM 直接放弃 | 白白错过能跑的配置 | 依次砍:max_seq_length 减半 → batch_size=1 + gradient accumulation → 开 gradient checkpointing | 先砍序列,再砍 batch |
| loss 不降就狂调学习率 | 方向错了越调越乱 | 先查 trainable params 是不是 0、chat template 对没对 | loss 不降先查数据 |
| 裸装最新版依赖 | 教程代码全线报错 | 按兼容组合锁版本,跑通就 pip freeze | 锁版本,freeze 留底 |
| 合并后删掉 adapter | 想改补丁只能重训 | merge_and_unload() 前备份 adapter 目录 | 合并前先备份 |
🔧 动手练习
练习 1:选型判断 + 手写 SFT 数据(约 20 分钟,不需要 GPU)
- 给自己一个真实需求(如"把口语客服话术改写成正式书面语"),对着开头那张三选一的表判断:该用 Prompt、RAG 还是微调?写一句理由。
- 假设结论是微调,照上面的 JSON 格式手写 10 条这个任务的 SFT 数据,要求风格、标点、结构完全统一。
- 写完检查:把 10 条里随机抽 2 条当"验证集"划出来不许用——这就是你的第一个评估习惯。
验收标准:写到第 6、7 条你开始头疼、开始想"要不让 AI 帮我生成几条"——恭喜,你已经体会到"凑高质量数据才是微调最累的部分"这个行业共识了。
练习 2:Unsloth Colab 真跑一次 QLoRA(约 40–60 分钟,用免费 GPU)
- 打开 Unsloth 官方 GitHub,找到免费的 Colab notebook(选 Qwen 或 Llama 的 4-bit 示例),用 Colab 免费 T4 就能跑——别在 Mac 本地折腾 bitsandbytes,前面说过那是打不赢的仗。
- 逐行对照本文认参数:
load_in_4bit、r、lora_alpha、learning_rate、num_train_epochs各自管什么,改之前先说出它的作用。 - 跑通默认示例后,把训练数据换成练习 1 手写的那 10 条(注意套对 chat template),观察 loss 曲线——数据这么少,大概率会看到"loss 掉太快"的背题信号,正好把前面讲的翻车信号亲眼看一遍。
- 训完做双测:先问它你的目标任务,再问"1+1 等于几"这种通用问题。两边都答好才算成——这就是最小版的"任务集 + 回归集"评估。
📚 小结
- 微调是最后一招:能用 Prompt / RAG 就别碰。微调管"怎么说"(风格/格式),不管"新知识"(那是 RAG)。
- 全量微调显存吃不消(7B 要 80G+);LoRA 用
ΔW ≈ B×A的低秩补丁把可训参数降到约 0.8%,补丁才几 MB。 - QLoRA = 4-bit 量化底座 + LoRA,显存砍到 6–12G,消费级单卡能训 7B;
r=16 / alpha=32、lr=2e-4、2–3 epoch 起手;依赖要锁版本(peft/trl/transformers 的 API 变得飞快,bitsandbytes 只认 Linux + CUDA)。 - 数据 > 参数:少而精、格式统一、套对 chat template、留验证集;数据量按任务从几百到数万不等。
- 微调完必须评估:任务集(学会没)+ 回归集(防灾难性遗忘)+ 人工抽检,别只看新任务分就上线;adapter 合并前先备份;想进一步对齐口味再上 DPO。
相关阅读
- 开源模型、本地部署与模型路由 — 微调完的模型(尤其合并后)怎么本地跑、怎么选推理引擎
- Transformer 与注意力机制基础 —
target_modules里的 q/k/v/o 到底是什么 - ML / DL 基础速通 — 过拟合、验证集这些微调绕不开的底层概念
- 合成数据与数据增强 — SFT 数据不够时怎么造
- RAG 基础 — "塞新知识"该走的那条路
- 评估与质量监控 — 把"任务集 + 回归集"搭成可复跑的 eval 流程
下一步:部署与成本优化 →
📚 相关资源
❓ 常见问题
关于本章主题最常被搜索的问题,点击展开答案
什么时候该微调,什么时候该用 RAG?
一句话判断:错在"不知道"用 RAG,错在"说不对"才微调。微调擅长固化风格、格式、领域语感,塞不进新知识——硬灌产品手册只会灌出幻觉。动手前先自查:few-shot 试过没、能不能凑几百条高质量数据、有没有测试集量化对比。微调是最后一招,多数需求 prompt + RAG 就达标。
LoRA 和 QLoRA 分别省了什么?
LoRA 省训练参数:冻结底座,只训 ΔW≈B×A 两个低秩小矩阵,可训参数降到约 0.8%,补丁才几 MB 到几百 MB。QLoRA 再省底座显存:先把模型 4-bit 量化(14GB 砍到约 4GB)再挂 LoRA。7B 全量微调要 80G+ 显存,QLoRA 只要 6–12G——这就是消费级单卡能微调的原因。
消费级单卡能微调多大的模型?参数怎么起手?
QLoRA 方案下一张 24G 的 4090 能舒服地调 7B~13B(占 6–12G 显存,随序列长度浮动);16-bit LoRA 调 7B 就很紧了。参数起手:r=16、alpha=2×r=32、lr=2e-4、2–3 个 epoch、target_modules 挂注意力 q/k/v/o。OOM 时依次砍:序列长度减半 → batch=1 + gradient accumulation → 开 gradient checkpointing。注意 bitsandbytes 只支持 Linux+CUDA,Mac 本地跑不了,用 Colab 免费 GPU。
SFT 数据要准备多少条?质量上要注意什么?
按任务定量:固定风格改写几百到 1k 条,固定输出结构 1k–5k 条,领域语感 5k 到数万条。质量三铁律:少而精大于多而杂(坏习惯模型学得更快)、格式字字统一(含标点结构)、必须留验证集。另外要套对 chat template——Qwen 和 Llama 模板不通用,套错了 loss 照降但推理输出乱码。数据不够用强模型合成再人工筛。
微调完效果变差或行为异常,怎么排查?
按信号定位:通用能力退化是灾难性遗忘,用回归集对比微调前后,减 epoch 或减小 r;只会训练集那一件事是背题,数据太少或 epoch 贪多;输出乱码先查 chat template 是否套对;loss 一直不降先查 trainable params 是不是 0(target_modules 写错)。上线前必须三条腿评估:任务集 + 回归集 + 人工抽检,别只看新任务分数。