
这个系列写到第三篇前两篇把数据准备和训练原理聊得差不多了后台一直有人在问到底怎么用 LLaMA Factory 把 LLaMA 3 或者 Qwen 微调起来有没有一套零门槛的操作流程今天这篇我就把所有步骤串起来从环境准备、数据集格式、LoRA 参数配置到训练启动、模型导出、本地验证全程按实战顺序走一遍。我自己用这套流程调过不少开源模型踩过的坑也会一并标出来看完你基本可以直接照着抄。这篇主要面向有一定 Python 和深度学习基础、但没怎么碰过大模型微调的朋友。如果你只是想把一个开源模型改造成能回答你领域问题的助手又不想去啃漫长的训练框架源码那 LLaMA Factory 应该是最省事的入口之一。它能用 Web 界面点点点也能用命令行自动跑关键是把 LoRA 这类高效微调流程封得很干净。1. 环境准备先把 GPU、CUDA 和依赖装顺了1.1 硬件与驱动检查微调不是只要有显卡就行先说个很多人容易忽略的前提微调大模型并不是单纯看显存大小还要看 PyTorch、CUDA、驱动三者是否匹配。如果你是用 LLaMA 3 8B 或者 Qwen 7B 这类模型做 LoRA实际显存消耗通常在 16GB 到 24GB 之间取决于序列长度、batch size 和量化方式。如果你手里是 24GB 的显卡可以比较舒服地跑起来如果是 16GB建议直接上 4bit 量化如果是 12GB 以下建议选 7B 模型并降低 batch size。在开始装任何东西之前先跑一遍nvidia-smi确认驱动能识别显卡记下 CUDA 版本。然后要知道 PyTorch 版本要对应 CUDA 版本比如你本地 CUDA 是 11.8那 PyTorch 也要装 cu118 对应的版本不能随意安装默认的 CPU 版本否则后面模型根本跑不到 GPU 上或者训练速度慢到怀疑人生。1.2 创建虚拟环境并安装 LLaMA Factory我习惯用 conda 建一个独立环境避免把系统 Python 环境搞乱。下面的命令可以按自己版本微调但思路是通用的conda create -n llama-factory python3.10 -y conda activate llama-factory接着安装 PyTorch注意版本必须匹配你的 CUDA 版本。以 CUDA 11.8 为例pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118然后拉取 LLaMA Factory 源码并安装。不建议直接pip install最新包就算完事因为源码仓库通常有更完整的示例配置文件排查问题也更方便。到项目主页复制仓库地址后执行git clone LLaMA-Factory仓库地址 cd LLaMA-Factory pip install -e .如果你只是想要命令行接口也可以只安装核心依赖但这里为了 Web 界面和 export 功能建议直接装全。安装过程如果出现bitsandbytes编译报错一般是因为没有对应 CUDA 的扩展这种情况可以选择先忽略因为 LoRA 本身不一定需要量化只有想跑 QLoRA 时才需要 bitsandbytes。装完后验证一下python -c import transformers; print(transformers.__version__)能正常输出版本号说明基础环境已经通了一半。顺便检查 transformers 版本不要太老某些模型配置需要比较新的版本支持。2. 数据准备模型想吃的“食粮”长什么样2.1 Alpaca 格式与自定义字段LLaMA Factory 最常用的数据格式是 Alpaca 格式本质就是一个 JSON 数组每个元素包含instruction、input、output三个字段。其中input是可选的当指令本身已经完整时可以填空字符串。举一个最简单的例子[ { instruction: 把下面的句子改写成更口语化的表达。, input: 我现在感到非常疲惫需要休息。, output: 我累坏了真想歇会儿。 }, { instruction: 判断这句话的情感是正面、负面还是中性。, input: 这个产品的售后服务让我很失望。, output: 负面 } ]这个格式的好处是直观、好写而且不需要你懂复杂的对话结构。如果你想做多轮对话微调则可以用 ShareGPT 格式字段会复杂一些但从入门角度来说先使用 Alpaca 格式是完全够用的。数据条数不用贪多质量优先。我之前用一两百条精挑细选的指令数据配合 LoRA就能让一个通用模型在某个垂直场景下的表现发生明显变化。2.2 注册数据集dataset_info.json 这一步容易被忽略很多人把 JSON 文件放进data目录后直接就在 Web 界面里找发现列表里没有自己的数据集。这个坑非常典型LLaMA Factory 不会自动扫描目录下的所有 JSON它需要通过dataset_info.json注册数据集的元信息。在项目根目录的data/dataset_info.json里按这个格式追加一个条目{ custom_demo: { file_name: custom_demo.json, format: alpaca, columns: { prompt: instruction, query: input, response: output } } }字段解释一下最外层的键名custom_demo是你在训练界面里看到的数据集名称file_name是数据文件的实际文件名format告诉框架这是什么格式columns是把当前数据文件里的字段映射到框架内部字段。如果你的 JSON 里就是把字段命名为conversations这类其他名字通过columns映射一下就好不必改动原文件。注册完之后重启 Web 界面或者重新进入训练页面你就能在下拉框里看到custom_demo了。命令行调用时也使用这个注册名传入--dataset custom_demo。这一步是新手最容易卡住的地方我几乎是看着周围每个人都会在这里折腾一会。2.3 数据质量的最基本判断在动手训练前可以把数据集随机抽取几十条自己先做一轮“人类预审”。我判断数据是否合格有三个标准指令是否明确答案是否准确有没有前后矛盾。输入输出是否贴合目标任务比如你要做客服助手就不要混太多代码问答。是否存在明显重复或低质量文本这类数据会直接拉低训练效果。另外建议把数据文件按 UTF-8 编码保存不要有 BOM 头。Windows 记事本保存时容易带 BOMJSON 解析时轻则报错重则字段名识别异常。我自己就曾经因为 BOM 头问题训练出来一个行为怪怪的模型排查半天才发现是数据编码问题。3. 配置模型与 LoRA 参数3.1 选择 LLaMA 3 还是 Qwen模型选择会直接决定训练时的显存、速度、效果尤其是中文场景。我一般会先问自己微调后的模型主要给谁用如果偏中文任务Qwen 系列往往更好因为它的词表对中文更友好生成中文也更自然。如果偏英文、代码或者想贴近 LLaMA 生态LLaMA 3 是稳妥的选择。从 LLaMA Factory 的使用角度两者差别不大你在 Web 界面填入对应的model_name_or_path就可以。这里我建议优先使用 8B 或 7B 级别。以 LLaMA 3 8B Instruct 为例模型路径填meta-llama/Meta-Llama-3-8B-Instruct如果网络环境不支持从 Hugging Face 下载可以先用脚本把模型下载到本地目录再填本地路径。对比项LLaMA 3 8BQwen 7B中文能力尚可但词表偏向英文更强中文语料占比较高显存占用LoRA约 18GB 以上约 16GB 以上对话模板需要指定 llama3一般自动匹配 qwen适用场景英文、逻辑、代码中文、中文交互3.2 LoRA 关键参数怎么给LoRA 不是把所有参数都冻结而是以低秩矩阵的形式训练一部分参数微调完只生成一个很小的适配器文件。关键参数包括lora_rank、lora_alpha、lora_target我常用的基准值是finetuning_type: lora lora_rank: 16 lora_alpha: 32 lora_target: alllora_rank决定低秩矩阵的宽度数值越大表示模型可学习的参数越多拟合能力越强但也更容易过拟合。lora_alpha是缩放系数一般取 rank 的 1 到 2 倍。lora_target如果设为all会覆盖所有适合 LoRA 的层效果通常更好但显存和训练时间会涨。如果显存紧张可以只对注意力层生效比如q_proj, v_proj不过在效果上可能会打一点折扣。学习率建议从2e-4起步这是 LoRA 微调常见区间。如果你的数据量很少且担心过拟合可以再降到1e-4。Epoch 不需要特别多3 到 5 个 epoch 通常足够关键是看验证集上的表现。3.3 显存不够时QLoRA 4bit 量化兜底如果你手上是 16GB 以下的卡建议使用 4bit 量化。LLaMA Factory 里只需要把quantization_bit设为 4框架会在加载基座模型时进行量化然后用 QLoRA 的方式微调。这样显存占用会明显下降但训练速度也会慢一些。我在实际测试中发现4bit 量化对最终微调效果的损失在可接受范围内尤其是在任务本身偏“改变说话风格”或“领域知识注入”时效果依然很稳。要注意的是使用量化时还需要安装bitsandbytes并且确认它是适配本地 CUDA 版本的版本否则会在加载模型时报错。4. Web 界面与命令行两种启动方式4.1 图形界面一把梭适合想快速看到完整流程的人LLaMA Factory 的 Web 界面确实很友好启动方式很简单python src/train_web.py然后浏览器访问http://localhost:7860你会看到训练、评估、导出等几个大区块。在训练区块里先选模型名称再选数据集然后把 LoRA 参数填进去。界面会动态生成对应命令你可以先看一眼命令再点“开始”。如果用新版本入口可能是llamafactory-cli webui我在不同环境下两种都遇到过。Web 界面的好处是零门槛所有参数都有直观选项。但要注意网页上显示的训练命令不会完全等同于实际执行逻辑最好还是自己在本地跑一下命令行配置这样出问题时更容易排查。界面逻辑在大批量试验时有一点繁琐每个新配置都要手动点一遍所以很多人最终还是回归命令行。4.2 命令行方式更适合自动化命令行适合固定套路、反复试验的场景。我一般会写一个 YAML 配置文件内容大致如下model_name_or_path: meta-llama/Meta-Llama-3-8B-Instruct template: llama3 stage: sft finetuning_type: lora dataset: custom_demo cutoff_len: 2048 learning_rate: 2e-4 num_train_epochs: 3.0 per_device_train_batch_size: 4 gradient_accumulation_steps: 4 lr_scheduler_type: cosine max_grad_norm: 1.0 logging_steps: 20 save_steps: 200 output_dir: outputs/llama3-8b-demo保存为train_demo.yaml然后执行llamafactory-cli train train_demo.yaml如果你的版本入口还是旧形式也可以直接运行python src/train_bash.py config.yaml。这里有几个配置项需要特别留意cutoff_len控制输入序列长度建议先设 2048如果显存不足再降到 1024。per_device_train_batch_size是每张卡上的批大小显存不够就调小比如 1 或 2。gradient_accumulation_steps用来模拟更大的 batch size比如 batch_size4、累积步数4等效 batch 就是 16但显存占用不会线性增长。要提醒的是这两个入口在不同版本之间略有差异如果你跑命令时提示找不到参数建议先git pull更新源码再重新安装依赖。5. 训练过程与效果监控5.1 训练日志到底看什么训练启动后终端会持续输出日志最重要的指标是loss。刚开始训练时 loss 通常会快速下降这是正常现象如果 loss 从一开始就停在一个极高位或者抖动剧烈大概率是数据格式或学习率出了问题。除了 loss还要看grad_norm和learning_rate。grad_norm如果突然变得非常大接近无穷说明梯度爆炸了大概率是学习率太高或者数据里有异常值。学习率曲线在lr_scheduler_type: cosine下会呈现先平稳后下降的形状这也是一种判断是否正常的手段。LLaMA Factory 会在output_dir目录下保存训练日志和 checkpoint。用 TensorBoard 也是可以的但训练本身已经够花了我通常直接盯终端里的阶段性输出。5.2 从 loss 曲线判断要不要中断很多人看到 loss 不高就以为训练很好这是误区。loss 低只能代表模型在训练集上拟合得好不代表在真实使用时效果好。尤其是数据量很少时3 个 epoch 可能就已经在过拟合了。我自己的习惯是训练完跑一次测试集如果发现模型开始“复读机”式输出某几句训练数据里的话就说明过拟合严重。遇到这种情况可以把 epoch 降到 2或者增大lora_rank的惩罚也可以通过早停机制来解决。LLaMA Factory 本身不一定内置早停所以你要手动盯日志。5.3 一次实际训练参数参考下面这套参数是我在 24GB 显存的机器上跑 LLaMA 3 8B 的实际参考值数据量约 800 条中文 QA训练耗时大约 40 分钟一个 epochper_device_train_batch_size: 4 gradient_accumulation_steps: 4 cutoff_len: 1024 lora_rank: 16 lora_alpha: 32 learning_rate: 2e-4 num_train_epochs: 4.0如果你的显存只有 16GB那 batch size 调成 2cutoff_len调成 1024再用 4bit 量化同样能跑起来只是每个 epoch 会慢不少。训练速度和数据平均长度强相关长短差异大的文本在同一个 batch 里会按最长序列计算所以尽量把过长的样本剪掉或截断对显存和速度都有好处。6. 导出合并模型与本地验证6.1 把 LoRA 适配器合并回底座训练结束后输出目录里保存的并不是一个完整的可部署模型而是一个 LoRA 适配器。直接加载这个适配器需要配合底座权重所以为了方便后续部署和测试我建议先合并导出为完整的 HF 模型。用 Web 界面的“Export”标签页可以完成导出但我更推荐命令行因为可以写进脚本反复使用。导出的核心概念很简单输入基座模型路径和训练好的 checkpoint 路径输出一个合并后的模型目录。假如你的 checkpoint 在outputs/llama3-8b-demo/checkpoint-600就可以指定它为导出的模型目录。注意检查model_name_or_path要与你训练时的基座完全一致否则合并会产生诡异的结果。导出完成之后不要在训练目录里原地跑测试尽量复制到另一个目录避免后期删改 checkpoint 时影响结果。6.2 用 Transformers 写脚本做冒烟测试拿到合并后的模型我一般会写一个几十行的 Python 脚本用来快速验证模型的基本生成能力。脚本核心代码如下from transformers import AutoModelForCausalLM, AutoTokenizer model_path outputs/llama3-8b-demo-merged tokenizer AutoTokenizer.from_pretrained(model_path) model AutoModelForCausalLM.from_pretrained(model_path, device_mapauto) messages [ {role: user, content: 请把这句话改得更自然我今天感觉非常疲惫想要休息。} ] inputs tokenizer.apply_chat_template( messages, tokenizeTrue, add_generation_promptTrue, return_tensorspt ).to(model.device) outputs model.generate(inputs, max_new_tokens256, temperature0.7, top_p0.9) response tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokensTrue) print(response)这里要注意apply_chat_template是依赖模型对话模板的如果你的微调数据不是多轮对话格式而是简单指令也可以直接拼接instruction input 你的回答这种样式但用模板更正规不会出现角色混淆问题。生成时如果输出空内容或者一上来就是|endoftext|大概率是对话模板没匹配对回到训练配置里检查template字段。7. 踩坑清单这些坑我基本上都替你们踩过7.1 显存不足和 OOMOOM 是最常见的报错尤其是在 batch size 刚设成默认值又没调低的情况下。处理优先级是先降per_device_train_batch_size再降cutoff_len最后才考虑关掉一些日志记录或者减少累积步数。为什么要把cutoff_len放前面因为显存占用跟序列长度的平方相关序列长度从 2048 降到 1024KV Cache 的显存占用会大幅下降。如果用了 4bit 量化还是 OOM可以进一步把gradient_checkpointing开启。LLaMA Factory 通常默认启用但如果关闭了你可以在配置里显式开启。代价是训练会变慢不过总比跑不起来强。7.2 pad token 与生成的奇怪输出生成测试的时候如果你的模型一直在输出pad符号或者明明任务是对话却把整条训练数据原样吐出来多半是 padding 和特殊 token 处理有问题。用 LoRA 微调时模型并不一定自带 pad token有些模型甚至没有专门的 pad token这时你需要在训练前的模型加载阶段把它设置成eos_token或者一个明确的填充 token否则训练时可能出现 batch 内长度不齐导致的异常。这个坑在 Web 界面看起来不明显因为框架内部通常有默认处理。但当你自己写推理脚本时就会突然冒出来。解决办法是在加载 tokenizer 后手动设置if tokenizer.pad_token is None: tokenizer.pad_token tokenizer.eos_token7.3 数据量少、过拟合和“复读机”如果你的数据量只有几十条那不要奢望模型能学会多少新知识它更可能只是改变了一些表达习惯。这时如果num_train_epochs设得很大模型就会把训练数据背下来看起来回答很标准但对没见过的问题会开始回避式回答甚至反复输出同一句话。我的建议是数据在精不在多但最少也尽量凑到 200 条左右。如果真的只有几十条那可以把学习率再调低比如1e-4epoch 控制在 2 到 3防止模型学“歪”。7.4 自定义模型路径却找不到文件如果你下载的是本地模型模型路径里不能包含中文字符否则部分读写组件会报路径错误。同时确认路径里包含config.json和tokenizer.json等必要文件。下载模型时不建议一个文件一个文件手动点直接使用下载脚本会省事得多。把模型放到固定目录后在model_name_or_path里填绝对路径比相对路径稳妥。7.5 对话模板不匹配导致输出变味指令微调看起来只是让模型学会“问什么答什么”但如果模板写错了模型会搞不清楚谁是助手谁是用户甚至把自己当成用户。比如你用 Qwen 模型却配了 LLaMA 模板生成结果大概率是没规矩的。LLaMA Factory 对每种底座模型都有对应的templateLLaMA 3 对应llama3Qwen 系列对应qwen直接选错会有很明显的表现差异。7.6 中文效果不如预期如果你微调后的中文模型在中文回答上仍然很“英文化”可以先看看基座模型到底是什么。有些模型虽然名字带中文但词表里中文覆盖一般。更常见的问题是你的训练数据里混合了大量英文把模型的输出风格带偏了。这时候不要急着调参数先审视数据分布。命令模型在中文任务上的差异非常大如果你要的是中文助手选择 Qwen 这条路线通常比在 LLaMA 上硬调更容易出效果。8. 一些零碎但好用的习惯我自己的训练流程发展到这里已经形成了一个固定的习惯每次跑训练前都会先在 Web 界面里用“预览”功能看一眼命令再切到命令行跑。这样做的好处是一旦训练结果不满意我还能知道是哪一步配置有偏差。另外给模型起名字这件事也别太随意。我在output_dir里会带上基座模型名、日期、数据规模比如llama3-8b-lora-0328-800d这样多轮实验下来自己也能一眼分辨到底哪个 checkpoint 是哪个数据训练出来的。训练完成后第一时间跑一个最小测试脚本别急着做完整评估先看一个样本好不好再决定要不要继续叠加数据。最后再分享一个实操心得LoRA 训练完成之后想给模型“升级”更多能力不需要重头训练完全可以沿用原来的 LoRA 适配器的基础上继续微调。但这要求你在最初训练时保留与底座完全一致的模型配置所以环境里不要随便乱换 transformers 版本。不同 transformers 版本之间加载 checkpoint 时偶尔会出现权重名不匹配的问题这类问题排查起来很烦最好从一开始就锁住版本。