把 LLaMA Factory 和 Qwen 2.5 放在一起用,是最近几个微调项目里我最顺手的组合。从环境搭建、数据准备,到 LoRA 训练、权重合并、线上部署,整条链路用 LLaMA Factory 一步走通,不用自己去碰数据加载、模板拼接、断点恢复这些容易翻车的角落。这篇文章就是一份按步骤可以直接照做的手册,我会把每个关键参数为什么这么设、每步容易踩的坑都摆出来。适合已经跑通基础推理、但还不太熟悉微调的读者,也适合想快速验证一个小模型效果的团队参考。
先说清楚一个认知:微调的目的从来不是“让模型从零学会中文”,而是“在它已有的能力基础上注入你的语料风格、知识边界和任务规范”。Qwen 2.5 本身已经具备很强的中文底子,我们要做的只是塑形。如果用对了工具和方法,一块 24G 的消费级显卡也能训练出可用的行业模型;如果姿势不对,哪怕给了 A100,也会被各种隐性细节卡到怀疑人生。
1. 这套组合能带来什么:思路先说透
1.1 LLaMA Factory 不是模型,而是一整套训练封装
很多刚接触微调的同学,第一反应是写 Trainer 脚本。写多了之后你会发现,真正耗时间的根本不是网络结构,而是数据格式、对话模板、训练参数管理、LoRA 合并、推理兼容这些重复劳动。LLaMA Factory 把这些东西全部收敛到了一个入口,用配置文件描述一次微调该怎么做,底层自动对接 Transformers、PEFT、Datasets 这些生态库。你只需要关心业务数据和评估结果,不再需要维护一堆版本混乱的胶水代码。
我最早用 LLaMA Factory 就是冲着它的 WebUI 去的。在页面里选好模型、勾上数据集、填几个参数,点一下就能开训,确实对新手友好。后来的版本把 CLI 和 YAML 配置也做得越来越顺手,同一个配置文件丢进 Git,一个项目跑完,复现成本几乎为零。所以这篇文章后面会同时讲 WebUI 和 YAML 两种跑法,你可以根据自己的习惯来选。
有一个观点我想放在最前面:微调框架的上限,取决于你对训练机制的理解程度。LLaMA Factory 简化了操作,但没有替代思考。你仍然需要明白 LoRA 和全参微调的区别、学习率为什么不能无脑调大、数据格式为什么错了模型就开始胡言乱语。后面每一节我都会解释背后的“为什么”,而不是只给你一串能跑的命令。
1.2 Qwen 2.5 为什么值得选
Qwen 2.5 是开源中文模型里非常能打的系列。从 0.5B 到 72B,每个规格都有 Base 和 Instruct 两个版本。0.5B 能跑在资源很受限的边缘设备上,7B 到 14B 是大多数团队微调的首选范围,32B 以上就需要多卡或者更大显存。中文指令理解、结构化输出、代码能力都不差,作为垂直领域微调的底子,综合素质很均衡。
我非常推荐优先选 Instruct 版。Instruct 版在预训练之外还做了指令对齐,微调样本量少的时候效果会明显更稳,因为你只是在它已经学会“怎么听话”的基础上,补充业务规则。Base 版不是不能用,但它要求你有足够多的高质量数据去重塑对话风格,通常只有数据团队比较成熟的项目才适合一上来就动 Base。
具体到微调方式,Qwen 2.5 官方生态里,LLaMA Factory 是被高频验证过的工具之一,模型名、模板、数据集映射都已经内置好了。这意味着你不需要自己去配对 ChatML 格式,不需要在代码里手写<|im_start|>这些特殊 token,框架会按模板自动处理。
1.3 模型规格和显存预算怎么匹配
我做选型时只看一件事:我的 GPU 到底能不能装下。显存不够,再好的模型也跑不起来。这里给你一张基于常见实践估算的表,注意它不是绝对值,因为显存消耗受序列长度、batch size、是否开启梯度检查点影响很大。
| 模型规格 | 微调方式 | 显存预估 | 适合场景 |
|---|---|---|---|
| 0.5B / 1.5B | 全参 | 2 ~ 6 GB | 玩具项目、极简单一任务 |
| 7B | LoRA / QLoRA | 8 ~ 16 GB | 单卡开发、客服、知识库问答 |
| 14B | QLoRA | 12 ~ 20 GB | 中等业务,需要更强综合能力 |
| 32B | QLoRA | 24 GB 以上 | 高要求场景,建议多卡 |
如果你的设备只有普通家用显卡,也没关系。QLoRA 是非常成熟的方案:把基座量化到 4bit 再加载,训练时就只更新低秩矩阵,显存占用能压到全参微调的四分之一甚至更低。用一个不严谨但好懂的类比:全参微调相当于把整家公司所有员工重新培训一遍;LoRA 相当于只给几个关键岗位的老师傅发一个“业务插件”,成本低、上线快,效果往往也不差。
2. 环境准备与依赖安装:起步不踩坑
2.1 先确认硬件和驱动
第一步不是装软件,是先看清机器。在终端里跑一句 nvidia-smi,看一下显卡型号、显存大小和驱动支持的 CUDA 版本。很多人后面报错“CUDA out of memory”或者“torch not compiled with CUDA”,八成是 PyTorch 和驱动版本不匹配,而不是代码写错了。
我建议 Python 版本选 3.10,别追新也别守旧。越新的 Python 版本,一些科学计算库的预编译轮子覆盖往往越慢。用 conda 建独立环境是最省心的做法,避免把系统 Python 弄乱。如果你在 Windows 上跑训练,我仍然建议用 WSL2 或者 Docker。Windows 原生的 PyTorch 也能用,但遇到 flash-attn 这种要编译的库时,原生环境里排错成本很高,遇到一次就会老老实实换回 WSL。
2.2 安装 PyTorch 和 LLaMA Factory
以 CUDA 11.8 或 12.1 为例,安装 PyTorch 的命令长这样:
conda create -n llama-factory python=3.10 -y conda activate llama-factory pip install torch==2.4.0 torchvision==0.19.0 torchaudio==2.4.0 --index-url https://download.pytorch.org/whl/cu121装完先验证一下 PyTorch 能不能正常调用 GPU:
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.device_count())"如果输出 True 且能看到显卡数量,再安装 LLaMA Factory:
pip install llama-factory装完执行llamafactory-cli version,能正常输出版本号就说明装好了。如果遇到“unrecognized model”这类问题,通常不是你的操作问题,而是安装的版本太老、模型映射表里没有 Qwen 2.5。解决办法就是把 LLaMA Factory 升级到最新版,或者直接源码安装:
git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e .源码安装的好处是方便改内部代码,缺点是需要自己拉代码和依赖。大多数用户只做微调,直接 pip 安装足够了。
2.3 下载模型与启动 WebUI
国内网络环境下载 Hugging Face 模型经常不稳定,建议优先用镜像或者 ModelScope。对于 ModelScope,你可以这样下载 7B Instruct 模型:
pip install modelscope modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir ./models/Qwen2.5-7B-Instruct如果坚持从 Hugging Face 下载,可以临时设置镜像:
export HF_ENDPOINT=https://hf-mirror.com下载完成之后,启动 WebUI:
llamafactory-cli webui浏览器打开http://localhost:8000,看到界面后,先在“模型名称”下拉框里找找有没有 Qwen2.5-7B-Instruct。能找到,说明版本和数据映射都正常;找不到,先升级工具本身,不要盲目怀疑模型路径写错。
3. 数据准备:微调效果的地基
3.1 选对数据格式:Alpaca 还是 ShareGPT
LLaMA Factory 里最常用的数据格式是 alpaca 和 sharegpt。Alpaca 格式适合纯指令任务,结构非常简单:
[ { "instruction": "把这句话翻译成中文:", "input": "Long time no see.", "output": "好久不见。" } ]如果涉及多轮对话、需要保留 system prompt,或者需要模拟真实客服场景里的上下文状态,就优先用 sharegpt 格式。它的 conversations 数组天然支持多轮,还能单独指定 system:
[ { "system": "你是一个耐心细致的银行客服助手。", "conversations": [ { "from": "human", "value": "我昨天申请的大额转账还没到账。" }, { "from": "gpt", "value": "您好,我帮您查一下。请确认这笔转账是在几点提交的?" } ] } ]数据格式的错误往往是隐性的。有时候训练不报错,loss 也在下降,但模型生成出来的内容前言不搭后语,就是因为模板和数据集格式不匹配。我的建议是:先不用急着把所有数据都做成多轮,如果业务场景的单轮问答居多,alpaca 完全够用,反而更容易控制变量。
3.2 数据质量与数据量怎么平衡
第一次做微调的人最容易犯的错,是到处收集几十万条数据,结果模型学会了噪音。垂直场景的 LoRA 微调,500 到 2000 条高质量样本通常就能看到显著效果。关键指标不是数量,而是这几点:
- 每条样本的任务意图清晰明确;
- 输入覆盖面足够广,包含用户真实表达里的口语、噪声、同义改写;
- 输出风格稳定、内容准确;
- 过滤掉重复和高相似样本;
- 独立留出验证集,验证集绝不要和训练集重叠。
如果业务里本来就有真实对话日志,先做一轮清洗。把包含敏感信息、乱码、太长太短的都筛掉。还要特别注意:不要只收集模型大概率能答对的样本。如果训练集里全是标准问法,验证集分数会很好看,一上线遇到用户改个说法,模型就完全不会了。数据覆盖度比数据量重要得多。
3.3 数据集怎么注册到 LLaMA Factory
LLaMA Factory 不会自动扫描你自己新增的 JSON 文件,需要先在 data/dataset_info.json 里注册。这个文件是数据集索引,注册之后 WebUI 的数据集列表里才会出现你的数据。一个最简单的注册条目长这样:
"my_bank_sft": { "file_name": "my_bank_sft.json", "formatting": "sharegpt", "columns": { "system": "system", "conversations": "conversations" } }把 JSON 文件放进 data 目录,注册完成后保存,回到 WebUI 刷新数据集列表。如果能勾选到my_bank_sft,说明注册成功。看不到的话,优先检查 dataset_info.json 是否合法,尤其是逗号和括号。这里想提醒一下,不同版本的 dataset_info.json 字段兼容性会有变化,如果遇到 schema 报错,直接去查官方 README 里对应版本的数据集注册说明,别硬试。
4. 微调实操:WebUI 和 YAML 两套跑法
4.1 核心训练参数到底是什么意思
微调界面上参数很多,但真正核心的就那么几个。我把常用的参数和推荐范围整理成了一张表,后面再逐个解释。
| 参数 | 推荐范围 | 一句话理解 |
|---|---|---|
| finetuning_type | lora / qlora | 训练方式,QLoRA 更省显存 |
| learning_rate | 1e-5 ~ 5e-5 | 权重更新的步长 |
| num_train_epochs | 3 ~ 5 | 数据过几遍 |
| lora_rank | 8 ~ 64 | 注入低秩矩阵的复杂度 |
| lora_alpha | 16 ~ 128 | 矩阵增量缩放系数 |
| cutoff_len | 1024 ~ 4096 | 输入序列最大截断长度 |
| per_device_train_batch_size | 1 / 2 / 4 | 每张卡每次喂几条样本 |
| gradient_accumulation_steps | 4 ~ 16 | 攒多少步再更新一次 |
learning_rate 是首选要调参数。LoRA 只更新一小部分参数,所以学习率不用像全参微调那样保守,但设得太高也会让模型在震荡中丢失已有能力。我的经验是从 5e-5 起步,loss 不稳定就降到 2e-5,再不行就 1e-5。lora_rank 可以理解为“业务插件的大小”:rank 越大,插件越复杂,能记住的内容越多,但也越容易过拟合。数据只有几百条时,rank 8 到 16 就很好;数据量大、任务复杂时,再上 32 或 64。lora_alpha 则控制这个插件的音量,一般保持 alpha 是 rank 的 1 到 2 倍比较稳妥,比如 rank 32,alpha 用 64。
cutoff_len 很关键,不只是防 OOM。有些业务场景的输入非常长,比如客服对话带历史记录,如果截断太短,模型根本看不到用户上下文,loss 再低也没用。建议先统计训练集中 token 长度的分布,再设 cutoff_len。
4.2 WebUI 配置与训练全流程
WebUI 是探索参数最方便的工具。我的操作顺序是:
- 在“模型名称”里选择 Qwen2.5-7B-Instruct,并填好模型路径。
- 检查模板是否自动识别为 qwen。没有自动识别就手动选。
- 在“数据集”里勾选你在 dataset_info.json 注册的 my_bank_sft。
- 训练方式选 lora;如果显存紧张就选 qlora,并打开 4bit 量化。
- 设置输出目录,建议用带项目名的路径,比如 output/qwen25-bank-lora。
- 把 cutoff_len 设为业务需要的长度,学习率设为 5e-5,epoch 设为 3 到 5。
- 点击“开始”,观察终端或者页面上的日志。
训练开始后,不要盯着屏幕发呆,重点看两个东西:显存占用和 loss 曲线。显存接近上限时考虑减小 batch size,loss 如果完全不降,先停掉排查数据格式和模板,不要硬等到训练结束。首次跑通全链路时,建议只留几百条数据、epoch 设 1,先把流程走通,再上全量数据。
4.3 用 YAML 配置固化训练流程
WebUI 适合探索,但一旦参数确定,我强烈建议固化成 YAML 文件。这样跨机器复现、团队协作、Git 追踪都会清晰很多。一个基础配置大致长这样:
model_name_or_path: /data/models/Qwen2.5-7B-Instruct template: qwen stage: sft finetuning_type: lora dataset: my_bank_sft cutoff_len: 2048 learning_rate: 5e-5 num_train_epochs: 3.0 per_device_train_batch_size: 2 gradient_accumulation_steps: 8 lora_rank: 32 lora_alpha: 64 warmup_ratio: 0.1 lr_scheduler_type: cosine optim: adamw_torch logging_steps: 10 save_steps: 100 output_dir: output/qwen25-bank-lora然后在命令行执行:
llamafactory-cli train qwen25_bank_sft.yaml这套配置跑起来后,你在代码评审里可以把 YAML 当配置变更来 review,而不是甩给别人一屏静态文档。注意 YAML 字段名可能随着工具版本微调,遇到参数不识别,直接看版本对应的配置说明。
5. 评估、合并与部署:让模型真正被用起来
5.1 训练完先做生成测试,不要只看 Loss
训练结束后,先不要急着导出部署。把 WebUI 切换到 Chat 页签,加载训练输出目录里的模型,输入几条验证集里的问题,看模型生成的回答是否符合预期。重点检查三个方面:格式是否稳定、语气是否统一、是否出现胡编乱造。只看 loss 很容易被骗,因为 loss 低只能说明模型在训练集上拟合得好,不能证明它在没见过的输入上表现好。
如果业务里有明确的标准答案,可以准备一个评测集,统计准确率或者关键字段命中率。生成式任务不需要硬套 BLEU,我甚至建议少用 BLEU 作为主要指标,因为它不适合衡量中文开放式回答的质量。相对务实的做法是:把评测集里的输出全部记录下来,人工抽样打标,看正确率。
5.2 合并 LoRA 权重并导出完整模型
LoRA 训练出来的产物是一组低秩增量矩阵,不是完整的模型权重。要部署到普通推理框架,必须先把增量和 Qwen 2.5 基座合并。合并命令如下:
llamafactory-cli export \ --model_name_or_path /data/models/Qwen2.5-7B-Instruct \ --adapter_name_or_path output/qwen25-bank-lora \ --template qwen \ --finetuning_type lora \ --export_dir output/qwen25-bank-merged导出时有一个常见大坑:基础模型路径必须和训练时完全一致,模板也不能换。如果你训练时用的是 Instruct 版,导出时却误填成 Base 版,张量和词表都对不上,模型直接废掉。导出成功后,output/qwen25-bank-merged 就是一个标准 Hugging Face 格式的完整模型目录,可以直接被加载。
5.3 vLLM 和 Ollama 部署路线
导出完成后,最简单的部署方式是用 vLLM 起一个 OpenAI 兼容的服务。当前版本推荐用 serve 命令:
vllm serve output/qwen25-bank-merged \ --served-model-name qwen25-bank \ --tensor-parallel-size 1 \ --port 8001起来之后,业务代码可以直接用 OpenAI SDK 指向这个地址,迁移成本非常低。如果你的部署场景是资源受限的边缘设备,或者想本地跑 CPU 推理,可以把合并后的模型转成 GGUF,再用 Ollama 加载。但千万不要一上来就 GGUF,性能瓶颈跑的这段路没必要重复走。
6. 常见问题与排查技巧实录
6.1 显存不足与 OOM
这是新手遇到最多的问题。解决顺序建议从便宜的开始试:
- 调小 per_device_train_batch_size;
- 降低 cutoff_len,先看训练数据里的最长 token 数,别在无关长文本上浪费显存;
- 打开 gradient_checkpointing;
- 从 lora 换成 qlora,启用 4bit 量化;
- 再不行就换小一号模型。
如果 batch size 已经降到 1,甚至序列长度也降下来了,还是 OOM,下一步要考虑你的业务数据里是不是存在异常长的文本。先清洗数据,比硬调训练参数更划算。这里有一个很容易被忽略的点:LLaMA Factory 会在训练日志里打印显存占用峰值,跑完一步可以瞄一眼,不要等到满屏报错才发现。
6.2 Loss 完全不降、NaN 或者梯度爆炸
loss 不降的原因通常很直接:数据格式错了、模板选错了、学习率太高。排查顺序我建议按下面来:
- 用 python -m json.tool 校验数据 JSON,确认结构合法;
- 确认 template 选的是 qwen,而不是其他模型的模板;
- 把 learning_rate 降到 2e-5 再试;
- 看日志里有没有 NaN,有就立刻停,关闭 fp16,或者换成 bf16。
bf16 在大多数现代 GPU 上确实比 fp16 更稳定,数值动态范围更大。如果硬件支持,直接用 bf16 能省掉很多诡异问题。
6.3 中文乱码和模板 token 重复
中文乱码里最典型的一种,是模型输出了<|im_start|>这种特殊 token 字符串。出现这个的原因,多半是你在数据里手动写了这些标记,而 LLaMA Factory 的模板又会自动再拼一次,于是模型学到的是“双份分隔符”。我的建议非常明确:数据里永远不要手写 BOS、EOS、<|im_start|>这类 token。它应该是框架和模板自动处理的事情。你要是手动加了,轻则多出奇怪字符,重则模型生成逻辑彻底乱掉。
6.4 合并后效果反而变差了
有一种情况很让人生气:训练时对话效果不错,导出合并之后反而变差。常见原因有四个:
- Adapter 路径填错了,加载了旧实验的 LoRA;
- 导出时模板和训练时不一致;
- 训练时用的 Instruct 版,导出时误用 Base 版;
- lora_alpha 设置太大,增量矩阵被过度放大。
最后一条尤其隐蔽。alpha/rank 比例最好保持在 1 到 2 之间,如果你设了 rank 8 但 alpha 128,模型输出往往带着很大的随机幻觉。遇到合并后变差,先不要怀疑模型坏了,把这四个原因按顺序检查一遍。
6.5 模型下载慢和找不到模型
模型权重动辄十几个 GB,直接用在线下载很容易失败。我的建议是:用 ModelScope 下载 Qwen 2.5 系列,然后通过本地路径传给 LLaMA Factory。不要反复在命令里写模型 ID 走远程拉取,本地路径一次设好,后面训练、导出、部署全都用这个目录,既快又准。如果非要用 Hugging Face 生态,记得把 HF_ENDPOINT 指向镜像,别硬碰超时。
7. 我的工作流与个人建议
我现在做微调项目有一个固定动作:先在 data 目录里放一个小验证集,用几百条数据把全链路跑通,确认能训练、能导出、能对话,然后再去清洗全量数据。这套流程帮我挡掉了大量无意义的返工。很多问题如果发生在全量训练后,排查成本是小时级别的;但发生在小数据冒烟测试阶段,可能十分钟就定位了。
训练配置我一定会纳入 Git 管理,数据集版本我也会用独立目录配合简单规则标记清楚。训练日志、loss 截图、评测结果这些材料,每次实验都归档一份。这看起来琐碎,实际上非常救命。过了两周你翻回来看,如果没有这些记录,你根本说不清楚当时这个效果好的模型到底是哪次训练跑出来的。
最后再说一个我非常坚持的判断:微调项目里,数据质量永远是第一优先级,参数调优只能锦上添花。如果你发现 loss 也降了,生成结果表面也对,但一上线就是不行,先回头审查数据,而不是急着把 learning_rate 改成什么天顶星数值。数据里的噪音、重复、问答不同构,才是绝大多数“微调没有效果”的真凶。这一条,值得所有在这个领域里摸索的人认真对待。