(五)造自己的轮子:LLaMA-Factory微调一条龙
单卡微调7B模型——从环境配置到本地部署
前面几篇文章你一直在用别人的模型。GPT-4 什么都知道,但它不知道你们实验室的 SOP。Claude 能写漂亮的文案,但不懂你们学院的论文格式。
你需要的是让模型学会你给的东西。这就是微调(Fine-tuning)。
微调最妙的一点是:2024年以后,它不再是"大公司才做得了的事"。LoRA 和 QLoRA 的出现让一张 RTX 3060 就能微调 7B 模型。这个门槛的降低是过去两年 LLM 领域最重要的变化之一。
这篇不讲原理推导——我们走完一条完整的微调流水线:从装 LLaMA-Factory 开始,到微调、评测、量化、部署,最后在你的笔记本上跑起来。
微调的本质:三句话讲清楚
动手之前,先花几分钟搞懂微调到底在干什么。
第一,模型的本质是一个函数。 输入文本,输出下一个词的概率。这个函数长什么样,完全由几十亿个"参数"决定——参数就是模型的大脑,语法、常识、推理能力全在这些数字里。
第二,好参数是一步步试出来的。 学自行车不需要物理公式——你只需要每次摔倒后微调身体姿势。大模型训练也一样:随机初始化参数 → 喂数据 → 算误差 → 往减少误差的方向调参数 → 重复几百万次。这个"试探→纠错→再试探"的循环就叫梯度下降,没有捷径。
第三,微调是站在巨人肩膀上。 从头训练等于从零造车——发动机、底盘、电路全部自己做,需要海量数据和算力。微调等于买辆现成的车,只改装导航和内饰,让它适配你的通勤路线。基座模型已经在海量文本上学好了语法和常识,你只需要用少量数据教它你的任务——这就是一张 RTX 3060 能微调 7B 模型的原因:大部分参数不动,只更新一小撮(LoRA)。
理解了这三层,后面六步流水线就不再是机械操作——你会知道为什么 loss 下降不等于学会了、为什么数据质量比数量重要、以及为什么微调后模型变了风格但没变知识。
硬件前提
先确认你的设备能跑:
- 最低配置:RTX 3060 (12GB) 或同等显存——可以 QLoRA 微调 7B 模型(QLoRA 把模型量化为 4-bit,显存需求降到 8GB 左右)
- 推荐配置:RTX 4060 Ti (16GB) 或 RTX 4090——可以 LoRA 微调 7B 甚至 14B 模型
- 纯 CPU 用户:可以跟着学流程,但不要尝试训练,跑完数据准备和部署部分
没有好显卡?可以用 Google Colab 的免费 T4(15GB 显存),足够跑通 QLoRA。
Windows 用户注意:Qwen2.5-7B-Instruct 模型权重约 14GB,加载时需要将整个模型文件映射到虚拟内存。如果 C 盘空间不足(< 40GB)或系统页面文件不够大,你会遇到
OSError: 页文件太小,无法完成操作 (os error 1455)。解决方案是将页面文件迁移到空间充足的 D 盘:
- 右键"此电脑" → 属性 → 高级系统设置 → 高级 → 性能设置 → 高级 → 更改
- 取消"自动管理所有驱动器的分页文件大小"
- 选中 C 盘,设为"无分页文件" → 点"设置"
- 选中 D 盘 → “自定义大小” → 初始 16384 MB,最大 32768 MB(或更大,取决于 D 盘空闲空间)
- 点"设置" → 重启电脑生效
整条流水线
从零到部署,分六步走:

第一步:装 LLaMA-Factory
LLaMA-Factory 是这篇的绝对主角。它的价值在于把微调从"需要看一堆论文和改一堆代码"变成了"Web 界面点几下"。
git clone https://github.com/hiyouga/LLaMA-Factory.git
cd LLaMA-Factory
pip install -e ".[torch,metrics]"
llamafactory-cli webui
浏览器打开 http://localhost:7860,你会看到一个 Web 界面。这个界面背后的能力:
- 支持几十种主流模型(Qwen、Llama、DeepSeek 等)
- 支持 LoRA / QLoRA / 全参微调
- 支持指令微调 SFT 和偏好对齐 DPO
- 单卡 + 4-bit量化 = 能跑 7B 模型
第二步:选模型和数据集
第一次实验,选最稳妥的配置:
- 模型:
Qwen2.5-7B-Instruct(通义千问,中文支持最好的开源模型之一) - 方法:
LoRA(显存友好,效果够用) - 数据集:
ruozhiba_gpt4(弱智吧问答,约 2k 条)
中科院等机构的研究发现,用弱智吧数据微调的模型在多项中文推理任务中表现突出。这类问题包含大量反直觉思维和模糊概念边界,能有效增强模型的逻辑推理能力。更重要的是——微调前后的回答风格差异非常明显,你一眼就能看出"模型变了"。
数据长什么样
Alpaca 格式就是三块:instruction(指令)、input(输入)、output(输出)。弱智吧数据集长这样——问题很刁钻,但回答一本正经:
{
"instruction": "众所周知,两点之间线段最短,那么人下楼为什么要走楼梯而不做自由落体运动呢?",
"output": "虽然几何上两点之间的直线最短,但在现实生活中,自由落体运动会产生巨大的冲击力,对人体造成严重伤害。走楼梯虽然路径更长,但安全得多。"
}
用自己的数据
假设你有一份实验室 SOP 问答表 lab_sop.xlsx:
| 问题 | 答案 |
|---|---|
| 门禁密码是多少? | 门禁密码是 2024#Lab,每周一更新 |
| 论文格式要求是什么? | 请参照最新版毕业论文模板,字号小四,行距 1.5 倍 |
| 内网服务器怎么连接? | SSH 连接 192.168.1.100,端口 2222 |
两步把它变成 LLaMA-Factory 能用的数据集:
第一步:转成 JSON 并注册
import pandas as pd
import json
df = pd.read_excel("lab_sop.xlsx")
with open("lab_sop.json", "w", encoding="utf-8") as f:
for _, row in df.iterrows():
item = {
"instruction": row["问题"],
"output": row["答案"]
}
f.write(json.dumps(item, ensure_ascii=False) + "\n")
把生成的 lab_sop.json 放到 LLaMA-Factory 的 data/ 目录下,然后在 data/dataset_info.json 末尾注册:
"lab_sop": {
"file_name": "lab_sop.json"
}
如果数据有
input字段,注册时需写明三字段映射:"columns": {"instruction": "...", "input": "...", "output": "..."}。另外如果提示缺openpyxl,先pip install openpyxl,或者把 Excel 另存为 CSV 用csv模块读取。
第二步:在 WebUI 中刷新
点右上角 🔄 刷新按钮,数据集下拉框就会出现 lab_sop,选中它开始训练。
建议:第一次微调先用弱智吧标准数据集跑通流程,确认环境没问题之后,再换成自己的数据。否则你会在"是代码出了 bug 还是数据格式不对"之间反复排查,浪费大量时间。
第三步:训练
第一次训练黄金参数
在 WebUI 选好模型和数据集后,按这张表配参数:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 学习率 (Learning rate) | 5e-5 | LoRA 常见范围 1e-5~1e-4,从 5e-5 起步。如果 loss 下降太慢再试着提到 1e-4 |
| 训练轮数 (Epochs) | 3 | ruozhiba 数据量小(2k 条),3 个 epoch 足够 |
| 最大梯度范数 | 1.0 | 防止梯度爆炸,默认值即可 |
| 最大样本数 | 100000 | 取全部数据,不用改 |
| 计算类型 | bf16 | 半精度训练,省显存(显卡需支持) |
| 截断长度 | 2048 | 大多数指令数据不超过这个长度 |
| 批处理大小 (Batch size) | 2 | 16GB+ 显存可设 2,12GB 设 1 |
| 梯度累积 | 8 | 等效 batch size = 2 × 8 = 16 |
| 学习率调节器 (LR scheduler) | cosine | 先大步学后小步微调,稳 |
| 验证集比例 | 0 | 小数据集不用分验证集 |
LoRA rank 和 alpha 在 LoRA 参数设置 折叠菜单里:rank 设为 8,alpha 设为 16。这是新手村黄金值,兼顾学习能力和稳定性。
训练前:记录基线
先不要点 Train——先加载原始模型测一遍,记录微调前的回答。这是后续判断微调效果的唯一依据。
操作步骤
WebUI → 点 Chat 标签页 → 选择 Qwen2.5-7B-Instruct → 点"加载模型"
→ 右侧参数面板:temperature 设 0.01,最大长度设 512,系统提示词留空
→ 输入下面 5 道测试题 → 逐题记录回答到表格(训练前列)
⏳ 加载模型要多久? Qwen2.5-7B-Instruct 约 14GB。已缓存的话 1-3 分钟,首次下载 10-40 分钟。下载源选
modelscope(国内推荐)。加载时留意右下角显存进度条。
📂 加载本地模型:如果你有自己的模型文件,在模型路径下拉框选择或直接输入路径(如
E:/models/Qwen2.5-7B-Instruct),然后点加载即可。
对比参数设置
训练前后参数必须完全一致,否则输出差异可能来自参数不同,而不是微调的效果:
| 参数 | 默认值 | 对比测试建议 |
|---|---|---|
| Temperature | 0.95 | 设为 0.01(回答确定不变) |
| Top-p | 0.7 | 保持不动 |
| 最大生成长度 | 1024 | 512 |
| 系统提示词 | 空 | 留空(否则分不清效果来源) |
测试题
设计原则:针对基座模型不太擅长、但微调后会有改善的场景。前 3 题是重点——ruozhiba 训练后,对这类问题的回答风格会从简短敷衍变成"一本正经地长篇分析"。
# 1. 逻辑推理——弱智吧经典
Q: "既然鱼不能离开水,那为什么带鱼被叫做带鱼?"
# 2. 逻辑推理——弱智吧经典
Q: "众所周知,两点之间线段最短,那么人下楼为什么要走楼梯而不做自由落体运动呢?"
# 3. 逻辑推理——弱智吧经典
Q: "如果蚊子不吸人血,只吸脂肪,那它会成为减肥神器吗?"
# 4. 指令精准度
Q: "用一句话解释什么是机器学习,不要超过20个字"
# 5. 灾难性遗忘检验
Q: "1+1等于几?"
开始训练
参数配好、基线记好后,回到 WebUI 点"开始训练"。一小时左右(取决于显卡),你会看到一个 loss 曲线在下降。
训练加速:如果你每次训练要等很久,可以试试 Unsloth。它通过优化的内核实现无损训练加速 2x,同时降低显存占用。LLaMA-Factory 已集成,安装后自动生效,无需改代码。
看 Loss 曲线
Loss 曲线应该像缓坡一样平滑下降:
| 现象 | 原因 | 解决 |
|---|---|---|
| 曲线剧烈震荡 | 学习率太高 | 降到 2e-5 试试 |
| 曲线平坦不下降 | 学习率太低或数据有问题 | 升到 1e-4,检查数据质量 |
| Loss 降到接近 0 | 严重过拟合(记住答案了) | 减少 epochs,增加数据量 |
| Val loss 在上升 | 开始过拟合 | 在 loss 最低点早停 |
⚠️ 最重要的提醒:loss 在下降 ≠ 模型变好了。loss 只说明模型在训练集上的预测误差在减少,不代表它学到了你想要的能力。真正的检验在后面的评测部分。但 loss 异常一定说明有问题,值得停训排查。
第四步:评测
训练后对比测试
训练完成后,回到 Chat 标签页加载微调后的模型,用完全相同的参数问同一套题:
点 Chat → 加载基座模型 + 训练好的 adapter
→ 参数设成和训练前完全一样(temperature 0.01,最大长度 512,系统提示词空)
→ 逐一输入 5 道测试题 → 记录回答(训练后列)
→ 和训练前的记录对比
操作检查清单
训练前 Chat 测试:
☐ 点 Chat → 选模型 → 加载(1-3 分钟)
☐ temperature 0.01,最大长度 512,系统提示词留空
☐ 逐一输入测试题 → 记录回答
训练后 Chat 测试:
☐ 加载训练后的模型,参数设成和训练前完全一样
☐ 问同一套题 → 记录回答
☐ 对比两列差异
一次真实的微调实验
以下来自 Qwen2.5-7B-Instruct + ruozhiba_gpt4 数据集的实验记录(temperature=0.01):
| 问题 | 训练前 | 训练后 |
|---|---|---|
| 带鱼为什么叫带鱼 | “带鱼"这个名字并不是因为它需要水才能生存,而是因为它身体修长、扁平,像一条带子,因此得名"带鱼”。这个名字来源于它的外形特征,而不是它的生存环境。 | “带鱼"这个名称并不是根据鱼的习性命名的。事实上,带鱼是一种生活在海洋中的鱼类,它们可以在深海和浅海中游动。虽然鱼需要水才能生存,但"带鱼"这个名称可能来源于其外形特征。带鱼的体型较长,形状像一条带子,因此得名"带鱼”。另外,带鱼的英文名称"hairtail"也是因为其长形的尾巴,像头发一样。所以,带鱼这个名字更多是基于其外貌特征,而不是其生活环境或习性。 |
| 下楼为什么不走自由落体 | 虽然两点之间线段最短,但自由落体运动在实际生活中是不安全的,可能会造成严重的伤害。因此,人们选择走楼梯以确保安全。安全总是优先于理论上的最短路径。 | 两点之间线段最短是几何学中的一个基本定理,适用于物理空间中的直线距离。然而,人在下楼时需要考虑安全和实际操作性。自由落体运动虽然在理论上是最短路径,但在现实中,它可能导致严重的身体伤害,甚至生命危险。因此,楼梯作为一种安全、方便的下楼方式被广泛使用。楼梯不仅提供了一个稳定的支撑结构,还允许人以可控的速度和方式下楼,避免了自由落体带来的风险。所以,人下楼选择走楼梯而不是做自由落体运动,是为了确保安全和便利。 |
| 蚊子吸脂肪会怎样 | 蚊子吸脂肪虽然听起来有趣,但实际上并不现实。蚊子的口器和消化系统是专门设计来吸血的,而不是吸脂肪。即使假设蚊子能够吸脂肪,它们的数量和效率也无法达到显著减肥的效果。此外,依赖蚊子吸脂肪来减肥也不是一个健康或科学的方法。保持健康的生活方式,包括合理饮食和适量运动,才是有效的减肥手段。 | 蚊子吸血的主要目的是为了获取蛋白质和其他营养物质,尤其是雌蚊需要血液中的蛋白质来孵化卵子。如果蚊子只吸脂肪,它们可能无法获得足够的营养来满足其繁殖和生存需求。因此,它们可能会因为营养不良而无法存活。此外,脂肪虽然含有高能量,但蚊子的消化系统可能无法有效处理脂肪,这会导致它们的代谢出现问题。因此,即使蚊子吸脂肪,它们也不会成为减肥神器,反而可能会威胁到它们的生存。 |
| 一句话解释机器学习 ≤20字 | 机器学习是让计算机通过数据学习规律并做出预测。 | 机器学习是让计算机通过数据自动学习和改进的一种算法技术。 |
| 1+1等于几 | 1+1等于2。 | 1+1等于2。 |
变化总结:
- 训练前回答简短直接,点到即止
- 训练后回答更长、更有条理,出现了"先分析 A 角度,再分析 B 角度"的结构化输出
- 对于荒谬问题,训练后更愿意接受前提并认真论证
- 第 4 题字数控制略有下降(训练后超了 20 字),说明指令跟随没有明显提升
- 第 5 题完全一致,说明没有灾难性遗忘
这就是微调的真实效果——模型学会了训练数据集的回答风格,而不是学到了新知识。
三层次考试
基本对比之外,再用三个层次检验微调的真实效果:
- 会做题了? —— 拿几条训练数据里的问题问模型。它应该能给出和训练集中类似的回答。如果这都做不到,说明根本没学会。
- 会泛化了? —— 问一些类似但没见过的场景。比如训练数据里有"SQL 注入检测",你可以问"检查这段代码有没有 XSS 漏洞"。这步过了,才算真的学到了能力。
- 变傻了没? —— 问一个完全无关的问题(“西红柿炒蛋怎么做?”)。微调后的模型不能连基本常识都丢了。如果这一步翻车,说明灾难性遗忘——回调学习率或减少 epochs。
怎么看结果
| 现象 | 结论 |
|---|---|
| 训练后回答更长、更有条理 | ✅ 微调成功——学到了训练数据风格 |
| 训练后和训练前完全一样 | ❌ 训练没收敛——检查 loss 曲线 |
| 训练后 1+1 答错了 | ❌ 灾难性遗忘——学习率太高或 epoch 太多 |
| 训练后直接背出训练数据原文 | ❌ 过拟合——减少 epochs |
这个测试集(5 条数据)从头到尾不参与训练,纯粹用来检验效果。它是"定性评估",和下面的 OpenCompass 定量评估互为补充。
OpenCompass 标准化评测
对比测试靠人看,OpenCompass 靠分数说话。OpenCompass 在多个标准测试集上给出量化分数,让你精确知道微调改变了模型的哪些能力。
pip install opencompass
python run.py --models hf Qwen2.5-7B-Instruct --datasets ceval mmlu
建议评测两个时间点:
- 原始模型(训练前)——记下基线分数
- 微调后的模型(训练后)——对比差距
重点关注两个指标:
- C-Eval(中文综合能力):微调后通常提升 5-15%,因为训练数据是中文
- MMLU(英文通用知识):通常会略微下降 1-3%,这是正常现象
如果 MMLU 下降超过 5%,说明学习率太高或训练步数太多——灾难性遗忘的信号。
OpenCompass 的命令行方式适合集成到实验脚本中。每次训练完自动跑一遍,结果追加到同一个 CSV 文件,方便追踪多轮实验的效果变化。
第五步:量化
训练完的模型由两部分组成:基座模型(FP16)+ LoRA adapter(几 MB 的小文件)。量化前必须先把 adapter 合并到基座模型,否则导出的 GGUF 不包含你微调的内容:
# 在 LLaMA-Factory 中导出合并后的模型
llamafactory-cli export \
--model_name_or_path Qwen/Qwen2.5-7B-Instruct \
--adapter_name_or_path ./saves/Qwen2.5-7B-Instruct/lora/your-checkpoint \
--template qwen \
--finetuning_type lora \
--export_dir ./exported-model
合并后的 ./exported-model 是完整 FP16 模型(约 14GB)。用 llama.cpp 把它量化到 4-bit,降到 4-5GB:
git clone https://github.com/ggerganov/llama.cpp.git
python convert.py ./exported-model --outfile model.gguf
./quantize model.gguf model-Q4_K_M.gguf q4_K_M
Q4_K_M 是效果和体积的最佳平衡点。量化后的模型在 CPU 上就能跑——./llama-cli -m model-Q4_K_M.gguf -p "你好"。
第六步:部署
把量化后的模型交给 Ollama 管理。Qwen2.5 使用 ChatML 格式,Modelfile 必须正确设置模板和停止词:
FROM ./model-Q4_K_M.gguf
TEMPLATE """{{ if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}<|im_start|>user
{{ .Prompt }}<|im_end|>
<|im_start|>assistant
"""
PARAMETER stop "<|im_end|>"
# 导入 Ollama
ollama create my-model -f Modelfile
ollama run my-model
模板不对会导致对话格式错乱。如果你在 Ollama 中看到模型回答带特殊 token 或格式混乱,先检查 Modelfile 里的 TEMPLATE 是否正确。
然后装 Open WebUI(Docker 一键部署),得到一个像 ChatGPT 一样的网页界面,但背后跑的是你自己的微调模型:
docker run -d -p 3000:8080 ghcr.io/open-webui/open-webui:main
浏览器打开 localhost:3000,选择你的模型开始对话。从这一刻起,你不再是一个"调 API 的人",你是一个"真正微调过模型的人"。
实验追踪
WebUI 点选很方便,但它不会帮你记住"这组参数是什么"。做第 1 次实验没问题——参数都在界面上,一目了然。但到第 3、4 次时,你会在下拉菜单和滑块之间反复确认"上次是不是这么配的"。
两个工具能解决这个问题:
- WandB(Weights & Biases)自动记录每次训练的 loss 曲线和参数。LLaMA-Factory WebUI 自带开关——在训练页面勾选
Enable W&B,填 API key 即可,不需要写任何代码。 - YAML 配置文件——你在文件里写参数,而不是在界面上点选。
用 YAML 代替 WebUI:
# train.yaml
model_name_or_path: Qwen/Qwen2.5-7B-Instruct
dataset: alpaca_zh
finetuning_type: lora
lora_rank: 8
learning_rate: 5e-5
num_train_epochs: 3
cutoff_len: 2048
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
llamafactory-cli train train.yaml
每次实验复制一份 YAML,改参数就是改文件。几天后回头看,你还能知道"v3 和 v4 的区别只是学习率从 5e-5 改到了 3e-5"。YAML 文件本身就是你的实验记录本。
本文涉及的项目——LLaMA-Factory、PEFT、OpenCompass、llama.cpp、Ollama、Open WebUI——覆盖了从训练到部署的全流程,是整个系列里含金量最高的一组工具链。
本系列 Notebook 及配套文章:github.com/ASPIRINH/hands-on-llm
常见问题
Q: RTX 3060 12GB 能不能跑 QLoRA?
A: 能。在 LLaMA-Factory WebUI 里选 QLoRA(4-bit 量化),模型选 Qwen2.5-7B-Instruct,batch size 设 1,gradient accumulation 设 4。显存占用约 8GB,你有余量。
Q: 训练到一半显存溢出(OOM)?
A: 降低 batch size(从 1 降到 1——已经是 1 了就降 gradient accumulation 步数),或者换更小的基座模型(Qwen2.5-1.5B)。如果还不行,检查有没有其他程序占用显存(nvidia-smi 查看)。
Q: 微调后模型回答质量反而变差了?
A: 最常见的两种原因:1) 训练步数太多导致灾难性遗忘——2k 条规模的数据集 1-2 个 epoch 通常就够了;2) 学习率太高——如果你用的学习率高于本文推荐的 5e-5,降下来试试。
Q: llama.cpp 的 convert.py 报 no module named 'gguf'?
A: 需要装依赖。在 llama.cpp 目录下运行 pip install -r requirements.txt,或者单独 pip install gguf。
Q: Ollama 导入模型后对话效果和微调前一样?
A: 检查 Modelfile 里的 TEMPLATE 格式是否正确。Qwen 系列需要特定的 chat template。去 LLaMA-Factory 的 export 目录找生成的 tokenizer_config.json,里面的 chat_template 就是答案。
Q: Colab 免费 T4 能否跑 LLaMA-Factory?
A: 可以。Colab T4 有 15GB 显存,足够 QLoRA 微调 7B 模型。注意 Colab 空闲 90 分钟会断开,建议分段训练或用 LLaMA-Factory 的 --resume_from_checkpoint 续训。
更多推荐

所有评论(0)