开始之前
你需要第 2 课的 train.jsonl、装好 pip install "trl[peft]" datasets 的 Python,以及一块 GPU。像 Qwen/Qwen3-0.6B 这样 0.6B 的模型是个温和的起点,TRL 自己的快速上手就加载它。要用 QLoRA,还要再运行 pip install bitsandbytes。
LoRA 训练什么,不碰什么
2021 年的 LoRA 论文提出冻结预训练权重,在 Transformer 的每一层里注入可训练的低秩矩阵,只有这些矩阵在学习。相比用 Adam 对 GPT-3 175B 做全量微调,作者报告可训练参数少了 10,000 倍,GPU 显存需求少了 3 倍,而且和适配器层不同,推理时没有额外的延迟。
在 PEFT 里,两个矩阵一个从随机值开始,另一个从零开始,所以刚建好的适配器在训练推动它之前什么也不改变。基础模型自始至终不变,保存下来的东西因此很小。
代价可能是容量。2024 年,LoRA Learns Less and Forgets Less在编程和数学上,针对 Llama-2-7B 比较了 LoRA 和全量微调。比较包括约 10 万条提示词和回答配对的指令微调,以及数十亿个 token 的继续预训练。在它的标准低秩设置下,LoRA 明显落后,但它更好地保住了基础模型在目标领域之外的能力。对工单标题这种窄任务,保住这些能力是好事。这项研究只涉及代码和数学,所以要学一项大的新技能时出现差距,应当当作要去测试的风险,而不是已有的结论。
10,000x
比用 Adam 全量微调少的可训练参数
来源:Hugging Face Papers: LoRA: Low-Rank Adaptation of Large Language Models (2026年11月6日 查阅)
3x
比用 Adam 全量微调少的 GPU 显存
来源:Hugging Face Papers: LoRA: Low-Rank Adaptation of Large Language Models (2026年11月6日 查阅)
训练适配器
这个脚本是本课自己写的,用的是 TRL 的 SFT 训练器和 PEFT 集成页面上的调用。模型名是个字符串,所以 TRL 会替你下载并加载模型。它只读 train.jsonl。别把 test.jsonl 放进这个脚本,第 4 课要用它,必须保持没动过。
from datasets import load_dataset
from peft import LoraConfig
from trl import SFTConfig, SFTTrainer
data = load_dataset("json", data_files={"train": "train.jsonl"})
peft_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
target_modules="all-linear",
)
args = SFTConfig(
output_dir="ticket-title-lora",
learning_rate=2e-4,
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
)
trainer = SFTTrainer(
model="Qwen/Qwen3-0.6B",
args=args,
train_dataset=data["train"],
peft_config=peft_config,
)
trainer.train()
trainer.save_model("ticket-title-lora")- 把脚本存成
train.py,放在train.jsonl旁边,运行python train.py。 - 看训练器打出来的训练损失。损失始终不动,说明数据或学习率该检查了。
- 训练结束后打开
ticket-title-lora文件夹。里面是适配器文件,比模型小得多,不是模型的拷贝。
各项设置的来源
把这些值当作第一次运行的取值,不是最优解。一次只改一项,并在第 4 课里把每次运行和基础模型比较。训练文件小,步数就少,所以只跑一次说明不了多少。
| 设置 | 这里的值 | 来源 |
|---|---|---|
r | 16 | TRL 把 8、16、32 和 64 列为常见的秩。秩越高,可训练参数越多。 |
lora_alpha | 32 | TRL 说通常取秩的两倍。它缩放更新的幅度。 |
lora_dropout | 0.05 | TRL 给出的常见范围是 0.05 到 0.1。 |
target_modules | "all-linear" | PEFT 的指南用这种写法把 LoRA 加到所有线性层,QLoRA 也这样做。较轻的常见选择是注意力的 query 和 value。 |
learning_rate | 2e-4 | TRL 把适配器训练的学习率定在 SFT 默认值 2e-5 的十倍左右。 |
num_train_epochs | 3 | SFTConfig 的默认值,保持不变。 |
| 批大小 | 4 x 4 步 | 本课自己的选择:每设备批大小 4,梯度累积 4 步。 |
模型装不下时:QLoRA
QLoRA 把冻结的基础模型用 4 位精度加载,再在它上面用更高的精度训练 LoRA 适配器。2023 年的 QLoRA 论文报告在单块 48 GB 的 GPU 上微调了 65B 的模型,同时保持全 16 位微调的表现。TRL 说,相比标准 LoRA,它最多能把显存需求降到四分之一。
TRL 推荐 NF4 数据类型配 bfloat16 计算,再加双重量化,后者每个参数能再省约 0.4 位。你把配置直接传给训练器,放在 peft_config 旁边,训练器会加载并量化模型。下面的代码块是对 train.py 的两处改动,都抄自 TRL。
import torch
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
trainer = SFTTrainer(
model="Qwen/Qwen3-0.6B",
args=args,
train_dataset=data["train"],
quantization_config=bnb_config,
peft_config=peft_config,
)- TRL 的显存指南说,梯度检查点在它的训练器里已经默认开启。
- 激活值卸载会把激活值挪到 CPU 内存,速度会变慢。
- 打包能省掉填充,但需要 FlashAttention 或它的变体。
- 每设备批大小调小、梯度累积步数调多,是 TRL 自己的 QLoRA 命令用的办法:批大小 1,累积十六步。
Unsloth,可选的更快加载器
Unsloth 是一个开源的微调库。它的 README 声称训练速度是两倍,显存少用 70%,TRL 的 Unsloth 页面则说最多少用 80%。这些都是厂商自己的数字,没有一个来自为本课运行它的结果。它的 README 说明采用双重许可:核心包是 Apache 2.0,Studio 界面这类可选组件用 AGPL-3.0。
它接在同一个训练器上。你用 FastLanguageModel 加载模型,不再传模型名,用 get_peft_model 包一层,再把结果交给 SFTTrainer。下面的代码块抄自 TRL 的页面,其中的 dataset 指你的训练部分。pip install unsloth 即可安装。第 4 课会讲它的 GGUF 导出。
from trl import SFTConfig, SFTTrainer
from unsloth import FastLanguageModel
max_length = 2048
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/mistral-7b",
max_seq_length=max_length,
dtype="auto",
load_in_4bit=True,
)
model = FastLanguageModel.get_peft_model(
model,
r=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_alpha=16,
lora_dropout=0,
bias="none",
use_gradient_checkpointing=True,
random_state=3407,
)
training_args = SFTConfig(output_dir="./output", max_length=max_length)
trainer = SFTTrainer(model=model, args=training_args, train_dataset=dataset)
trainer.train()
常见错误
- 给适配器沿用 SFT 的学习率 2e-5。TRL 说适配器要取它的十倍左右。
- 把
test.jsonl当评估集传进去,对着它调参,直到它不再是测试。 - 训练损失还在下降就接着加轮数。小数据集上,这拟合的是样本,不是任务,只有留出的测试分数能说明是哪一种。
- 没有测试就指望 LoRA 学会一项大的新技能。2024 年的那项研究在代码和数学上发现了差距。
- 忘了基础模型是哪个。加载适配器需要那个确切的模型,第 4 课会演示。
自己试一试
- 在
train.jsonl上运行train.py,记下最后的训练损失。 - 只把学习率改成 2e-5,再运行一次,比较两条损失曲线。
- 某次运行显存装不下,就加上
bnb_config那几行再跑一次。 - 看一下输出文件夹里各个文件的大小。
接下来
第 4 课在 test.jsonl 上给适配器和基础模型打分,合并适配器,转成 GGUF,再放进 Ollama 里运行。
自测一下
LoRA 训练的是什么?
查看答案
B. 加到各层上的小型低秩矩阵,基础模型保持冻结 LoRA 冻结预训练权重,在每一层里注入可训练的低秩矩阵。
TRL 里 SFT 的默认学习率是 2e-5,TRL 建议 LoRA 用多少?
查看答案
C. 高十倍左右,接近 2e-4 TRL 把适配器训练的学习率定在全量微调的十倍左右。
什么时候该改用 QLoRA?
查看答案
A. 标准 LoRA 下基础模型在 GPU 显存里装不下 QLoRA 用 4 位精度加载冻结的基础模型来省显存,按 TRL 的说法,相比标准 LoRA 最多省到四分之一。
LoRA 训练结束后保存训练器,写出的是什么?
查看答案
D. 只有适配器权重,比模型小得多 TRL 说这个调用只保存适配器权重,而不是完整模型。
常见问题
LoRA 微调需要多少 GPU 显存?
这取决于模型、序列长度和批大小,资料里没有给出一个固定数字。给出的是几个杠杆:TRL 称 PEFT 是最有效的省显存办法之一,4 位加载还能再省,QLoRA 论文在一块 48 GB 的 GPU 上装下了 65B 的模型。
该用 LoRA 还是 QLoRA?
模型装得下就先用 LoRA。QLoRA 是同一个适配器放在 4 位的基础模型上,它的用处就是省显存。
LoRA 能和全量微调打平吗?
要看任务。LoRA 的原论文(2021 年)报告,在四类模型上质量持平或更好。2024 年那项针对 Llama-2-7B 在代码和数学上的研究发现,在标准低秩设置下 LoRA 明显落后,不过它保住了更多基础模型的其他能力。
训练损失在下降,就说明适配器有用吗?
不能。训练损失只说明模型在拟合它的样本。留出的 test.jsonl 上的分数,对照基础模型来看,才能说明适配器有没有帮助。第 4 课会做这个比较。
我需要 Unsloth 吗?
不需要。TRL 加 PEFT 本身就能训练适配器。Unsloth 只是可选的加载器,声称训练更快、显存更少,训练仍由 SFTTrainer 完成。
资料来源
- Hugging Face Papers: LoRA: Low-Rank Adaptation of Large Language Models https://huggingface.co/papers/2106.09685
- Hugging Face Papers: QLoRA: Efficient Finetuning of Quantized LLMs https://huggingface.co/papers/2305.14314
- Hugging Face Papers: LoRA Learns Less and Forgets Less https://huggingface.co/papers/2405.09673
- Hugging Face TRL: SFT Trainer https://huggingface.co/docs/trl/sft_trainer
- Hugging Face TRL: PEFT Integration https://huggingface.co/docs/trl/peft_integration
- Hugging Face TRL: Reducing memory usage https://huggingface.co/docs/trl/reducing_memory_usage
- Hugging Face TRL: Unsloth Integration https://huggingface.co/docs/trl/unsloth_integration
- Hugging Face PEFT: LoRA https://huggingface.co/docs/peft/developer_guides/lora
- GitHub: unslothai/unsloth https://github.com/unslothai/unsloth
- NeuralNine on YouTube: Fine-Tuning Local LLMs with Unsloth & Ollama https://www.youtube.com/watch?v=W_xh6qNSfAQ
