# LoRA 微调：用 TRL 和 PEFT 训练适配器

> LoRA 微调会冻结基础模型，只用你的样本训练一些小的适配器矩阵，所以得到的是一个小得多的适配器文件，而不是模型的又一份拷贝。用 TRL 和 PEFT，一个 LoraConfig 加一个 SFTTrainer 就够了。模型在 GPU 显存里装不下时，改用 QLoRA。

Mindket 编辑部 · 发布于 2026-11-06 · 更新于 2026-11-06 · https://mindket.com/cn/learn/fine-tuning/lora-fine-tuning

## 要点

- 训练的是适配器，不是整个模型：一个 LoraConfig 加一个 SFTTrainer 就是全部脚本。
- LoRA 的学习率取 2e-4 左右，大约是 SFT 默认值的十倍。
- 只有模型在显存里装不下时，才加 BitsAndBytesConfig。
- 把基础模型的名字和保存的适配器放在一起，因为加载时两者都要用。

## 开始之前

你需要第 2 课的 `train.jsonl`、装好 `pip install "trl[peft]" datasets` 的 Python，以及一块 GPU。像 `Qwen/Qwen3-0.6B` 这样 0.6B 的模型是个温和的起点，TRL 自己的快速上手就加载它。要用 QLoRA，还要再运行 `pip install bitsandbytes`。

## LoRA 训练什么，不碰什么

2021 年的 [LoRA 论文](https://huggingface.co/papers/2106.09685)提出冻结预训练权重，在 Transformer 的每一层里注入可训练的低秩矩阵，只有这些矩阵在学习。相比用 Adam 对 GPT-3 175B 做全量微调，作者报告可训练参数少了 10,000 倍，GPU 显存需求少了 3 倍，而且和适配器层不同，推理时没有额外的延迟。

在 [PEFT](https://huggingface.co/docs/peft/developer_guides/lora) 里，两个矩阵一个从随机值开始，另一个从零开始，所以刚建好的适配器在训练推动它之前什么也不改变。基础模型自始至终不变，保存下来的东西因此很小。

代价可能是容量。2024 年，[LoRA Learns Less and Forgets Less](https://huggingface.co/papers/2405.09673)在编程和数学上，针对 Llama-2-7B 比较了 LoRA 和全量微调。比较包括约 10 万条提示词和回答配对的指令微调，以及数十亿个 token 的继续预训练。在它的标准低秩设置下，LoRA 明显落后，但它更好地保住了基础模型在目标领域之外的能力。对工单标题这种窄任务，保住这些能力是好事。这项研究只涉及代码和数学，所以要学一项大的新技能时出现差距，应当当作要去测试的风险，而不是已有的结论。

**2021 年 LoRA 论文报告的数字，对照 GPT-3 175B 的全量微调**

- **10,000x** 比用 Adam 全量微调少的可训练参数 (来源：[Hugging Face Papers: LoRA: Low-Rank Adaptation of Large Language Models](https://huggingface.co/papers/2106.09685), 2026-11-06 查阅)
- **3x** 比用 Adam 全量微调少的 GPU 显存 (来源：[Hugging Face Papers: LoRA: Low-Rank Adaptation of Large Language Models](https://huggingface.co/papers/2106.09685), 2026-11-06 查阅)

## 训练适配器

这个脚本是本课自己写的，用的是 TRL 的 [SFT 训练器](https://huggingface.co/docs/trl/sft_trainer)和 [PEFT 集成](https://huggingface.co/docs/trl/peft_integration)页面上的调用。模型名是个字符串，所以 TRL 会替你下载并加载模型。它只读 `train.jsonl`。别把 `test.jsonl` 放进这个脚本，第 4 课要用它，必须保持没动过。

````python
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")
````

1. 把脚本存成 `train.py`，放在 `train.jsonl` 旁边，运行 `python train.py`。
2. 看训练器打出来的训练损失。损失始终不动，说明数据或学习率该检查了。
3. 训练结束后打开 `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 论文](https://huggingface.co/papers/2305.14314)报告在单块 48 GB 的 GPU 上微调了 65B 的模型，同时保持全 16 位微调的表现。TRL 说，相比标准 LoRA，它最多能把显存需求降到四分之一。

TRL 推荐 NF4 数据类型配 bfloat16 计算，再加双重量化，后者每个参数能再省约 0.4 位。你把配置直接传给训练器，放在 `peft_config` 旁边，训练器会加载并量化模型。下面的代码块是对 `train.py` 的两处改动，都抄自 TRL。

````python
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 的[显存指南](https://huggingface.co/docs/trl/reducing_memory_usage)说，梯度检查点在它的训练器里已经默认开启。
- 激活值卸载会把激活值挪到 CPU 内存，速度会变慢。
- 打包能省掉填充，但需要 FlashAttention 或它的变体。
- 每设备批大小调小、梯度累积步数调多，是 TRL 自己的 QLoRA 命令用的办法：批大小 1，累积十六步。

## Unsloth，可选的更快加载器

[Unsloth](https://github.com/unslothai/unsloth) 是一个开源的微调库。它的 README 声称训练速度是两倍，显存少用 70%，TRL 的 [Unsloth 页面](https://huggingface.co/docs/trl/unsloth_integration)则说最多少用 80%。这些都是厂商自己的数字，没有一个来自为本课运行它的结果。它的 README 说明采用双重许可：核心包是 Apache 2.0，Studio 界面这类可选组件用 AGPL-3.0。

它接在同一个训练器上。你用 `FastLanguageModel` 加载模型，不再传模型名，用 `get_peft_model` 包一层，再把结果交给 `SFTTrainer`。下面的代码块抄自 TRL 的页面，其中的 `dataset` 指你的训练部分。`pip install unsloth` 即可安装。第 4 课会讲它的 GGUF 导出。

````python
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()
````

[Fine-Tuning Local LLMs with Unsloth & Ollama](https://www.youtube.com/watch?v=W_xh6qNSfAQ)

NeuralNine 的 YouTube 视频，按它的简介所说，讲怎样用 Unsloth 微调本地大模型，再在 Ollama 里使用。它发布于 2025 年 8 月，所以代码可能和上面的 Unsloth 页面不同。

## 常见错误

- 给适配器沿用 SFT 的学习率 2e-5。TRL 说适配器要取它的十倍左右。
- 把 `test.jsonl` 当评估集传进去，对着它调参，直到它不再是测试。
- 训练损失还在下降就接着加轮数。小数据集上，这拟合的是样本，不是任务，只有留出的测试分数能说明是哪一种。
- 没有测试就指望 LoRA 学会一项大的新技能。2024 年的那项研究在代码和数学上发现了差距。
- 忘了基础模型是哪个。加载适配器需要那个确切的模型，第 4 课会演示。

## 自己试一试

- 在 `train.jsonl` 上运行 `train.py`，记下最后的训练损失。
- 只把学习率改成 2e-5，再运行一次，比较两条损失曲线。
- 某次运行显存装不下，就加上 `bnb_config` 那几行再跑一次。
- 看一下输出文件夹里各个文件的大小。

## 接下来

[第 4 课](https://mindket.com/cn/learn/fine-tuning/run-a-fine-tuned-model)在 `test.jsonl` 上给适配器和基础模型打分，合并适配器，转成 GGUF，再放进 Ollama 里运行。

## 自测一下

1. LoRA 训练的是什么？

   A. 基础模型里的每一个权重
   B. 加到各层上的小型低秩矩阵，基础模型保持冻结
   C. 只训练分词器
   D. 4 位的第二份模型拷贝

   答案：B。 LoRA 冻结预训练权重，在每一层里注入可训练的低秩矩阵。

2. TRL 里 SFT 的默认学习率是 2e-5，TRL 建议 LoRA 用多少？

   A. 2e-6，低十倍
   B. 2e-5，不变
   C. 高十倍左右，接近 2e-4
   D. 正好 1.0

   答案：C。 TRL 把适配器训练的学习率定在全量微调的十倍左右。

3. 什么时候该改用 QLoRA？

   A. 标准 LoRA 下基础模型在 GPU 显存里装不下
   B. 想要更大的适配器文件
   C. 数据集里没有系统提示词
   D. 想跳过评估

   答案：A。 QLoRA 用 4 位精度加载冻结的基础模型来省显存，按 TRL 的说法，相比标准 LoRA 最多省到四分之一。

4. LoRA 训练结束后保存训练器，写出的是什么？

   A. 一份完整的新模型拷贝，几个 GB
   B. 只有分词器
   C. 一个可直接给 Ollama 用的合并 GGUF 文件
   D. 只有适配器权重，比模型小得多

   答案：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) (2026-11-06 查阅)
- [Hugging Face Papers: QLoRA: Efficient Finetuning of Quantized LLMs](https://huggingface.co/papers/2305.14314) (2026-11-06 查阅)
- [Hugging Face Papers: LoRA Learns Less and Forgets Less](https://huggingface.co/papers/2405.09673) (2026-11-06 查阅)
- [Hugging Face TRL: SFT Trainer](https://huggingface.co/docs/trl/sft_trainer) (2026-11-06 查阅)
- [Hugging Face TRL: PEFT Integration](https://huggingface.co/docs/trl/peft_integration) (2026-11-06 查阅)
- [Hugging Face TRL: Reducing memory usage](https://huggingface.co/docs/trl/reducing_memory_usage) (2026-11-06 查阅)
- [Hugging Face TRL: Unsloth Integration](https://huggingface.co/docs/trl/unsloth_integration) (2026-11-06 查阅)
- [Hugging Face PEFT: LoRA](https://huggingface.co/docs/peft/developer_guides/lora) (2026-11-06 查阅)
- [GitHub: unslothai/unsloth](https://github.com/unslothai/unsloth) (2026-11-06 查阅)
- [NeuralNine on YouTube: Fine-Tuning Local LLMs with Unsloth & Ollama](https://www.youtube.com/watch?v=W_xh6qNSfAQ) (2026-11-06 查阅)

[浏览技能](https://mindket.com/cn/skills)
