Mindket

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

黑色横幅,标题是“微调”,下面是“用 LoRA 在自己的数据上训练开源模型”。

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

本节内容

  • 训练的是适配器,不是整个模型:一个 LoraConfig 加一个 SFTTrainer 就是全部脚本。
  • LoRA 的学习率取 2e-4 左右,大约是 SFT 默认值的十倍。
  • 只有模型在显存里装不下时,才加 BitsAndBytesConfig。
  • 把基础模型的名字和保存的适配器放在一起,因为加载时两者都要用。
全部课程
本页目录
  1. 开始之前
  2. LoRA 训练什么,不碰什么
  3. 训练适配器
  4. 各项设置的来源
  5. 模型装不下时:QLoRA
  6. Unsloth,可选的更快加载器
  7. 常见错误
  8. 自己试一试
  9. 接下来
  10. 自测一下
  11. 常见问题
  12. 资料来源

开始之前

你需要第 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 明显落后,但它更好地保住了基础模型在目标领域之外的能力。对工单标题这种窄任务,保住这些能力是好事。这项研究只涉及代码和数学,所以要学一项大的新技能时出现差距,应当当作要去测试的风险,而不是已有的结论。

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

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")
  1. 把脚本存成 train.py,放在 train.jsonl 旁边,运行 python train.py。
  2. 看训练器打出来的训练损失。损失始终不动,说明数据或学习率该检查了。
  3. 训练结束后打开 ticket-title-lora 文件夹。里面是适配器文件,比模型小得多,不是模型的拷贝。

各项设置的来源

把这些值当作第一次运行的取值,不是最优解。一次只改一项,并在第 4 课里把每次运行和基础模型比较。训练文件小,步数就少,所以只跑一次说明不了多少。

出自 TRL 的 PEFT 集成和 SFT 训练器页面,以及 PEFT 的 LoRA 指南,阅读于 2026-11-06。
设置这里的值来源
r16TRL 把 8、16、32 和 64 列为常见的秩。秩越高,可训练参数越多。
lora_alpha32TRL 说通常取秩的两倍。它缩放更新的幅度。
lora_dropout0.05TRL 给出的常见范围是 0.05 到 0.1。
target_modules"all-linear"PEFT 的指南用这种写法把 LoRA 加到所有线性层,QLoRA 也这样做。较轻的常见选择是注意力的 query 和 value。
learning_rate2e-4TRL 把适配器训练的学习率定在 SFT 默认值 2e-5 的十倍左右。
num_train_epochs3SFTConfig 的默认值,保持不变。
批大小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()
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 课在 test.jsonl 上给适配器和基础模型打分,合并适配器,转成 GGUF,再放进 Ollama 里运行。

自测一下

  1. LoRA 训练的是什么?

    查看答案

    B. 加到各层上的小型低秩矩阵,基础模型保持冻结 LoRA 冻结预训练权重,在每一层里注入可训练的低秩矩阵。

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

    查看答案

    C. 高十倍左右,接近 2e-4 TRL 把适配器训练的学习率定在全量微调的十倍左右。

  3. 什么时候该改用 QLoRA?

    查看答案

    A. 标准 LoRA 下基础模型在 GPU 显存里装不下 QLoRA 用 4 位精度加载冻结的基础模型来省显存,按 TRL 的说法,相比标准 LoRA 最多省到四分之一。

  4. 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 完成。

资料来源

  1. Hugging Face Papers: LoRA: Low-Rank Adaptation of Large Language Models (2026年11月6日 查阅)https://huggingface.co/papers/2106.09685
  2. Hugging Face Papers: QLoRA: Efficient Finetuning of Quantized LLMs (2026年11月6日 查阅)https://huggingface.co/papers/2305.14314
  3. Hugging Face Papers: LoRA Learns Less and Forgets Less (2026年11月6日 查阅)https://huggingface.co/papers/2405.09673
  4. Hugging Face TRL: SFT Trainer (2026年11月6日 查阅)https://huggingface.co/docs/trl/sft_trainer
  5. Hugging Face TRL: PEFT Integration (2026年11月6日 查阅)https://huggingface.co/docs/trl/peft_integration
  6. Hugging Face TRL: Reducing memory usage (2026年11月6日 查阅)https://huggingface.co/docs/trl/reducing_memory_usage
  7. Hugging Face TRL: Unsloth Integration (2026年11月6日 查阅)https://huggingface.co/docs/trl/unsloth_integration
  8. Hugging Face PEFT: LoRA (2026年11月6日 查阅)https://huggingface.co/docs/peft/developer_guides/lora
  9. GitHub: unslothai/unsloth (2026年11月6日 查阅)https://github.com/unslothai/unsloth
  10. NeuralNine on YouTube: Fine-Tuning Local LLMs with Unsloth & Ollama (2026年11月6日 查阅)https://www.youtube.com/watch?v=W_xh6qNSfAQ

分享本节课

训练之前先看看有没有现成的技能

Mindket 列出了带安装说明的智能体技能。能修正你输出格式的技能,也许已经做了适配器要做的事。

浏览技能

本节课的 Markdown 版本 · 微调:用自己的数据训练开源模型 · 全部课程