LLaMA Factory:配置、数据模块与训练流水线
本系列第 9/17 章。第 8 章已经把命令送到 run_exp。本章解释这份入口之后的三份契约:参数如何变成五个 dataclass、原始数据如何变成 token、stage workflow 如何装配 Trainer。基线仍是 0.9.6.dev0 的默认 v0。读完应能独立写一份可解析的 SFT 配置,并知道 DPO 为什么看起来“像 RM”。
1. 配置:一种文件,两套语法
推荐路径是 YAML 加 OmegaConf 覆盖。覆盖项写在文件路径后面,没有前导 --:
1 | llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml \ |
这是仓库里真实存在的最短冒烟命令。它验证的是解析、数据对齐、模型加载与 Trainer 起步,不是完整收敛。需要隔离输出目录时,可以再覆盖 output_dir 与 overwrite_output_dir,但那不是验收所必需的最小集合。先跑通再加长,比一开始就写满超参更符合这条流水线的调试方式。
纯 CLI 则走 HfArgumentParser,必须写成 --learning_rate 这种形式。两种语法不要写在同一条命令里:YAML 后的 learning_rate=1e-5 与纯 CLI 的 --learning_rate 1e-5 不是同一套解析器。若在 Python 里直接调用 run_exp(args=dict),read_args() 会原样返回传入对象,不再读 sys.argv。
read_args() 看 sys.argv[1] 的后缀:.yaml/.yml 用 OmegaConf 加载;.json 先 json.load 再转 OmegaConf;随后把后面的 key=value 合并进去,后写的覆盖先写的。未知键默认报错,只有 ALLOW_EXTRA_ARGS=1 才允许多余字段。生产配置不应依赖这个逃生口,它会把拼写错误藏起来。
训练 parser 固定按这个顺序构造五个对象:ModelArguments、DataArguments、TrainingArguments、FinetuningArguments、GeneratingArguments。解析之后还有跨对象校验与派生,例如计算精度、device_map、截断长度。训练路径拒绝非 Hugging Face 的 infer_backend。infer_backend 的合法值是 huggingface|vllm|sglang,但后两个只属于推理配置;训练 YAML 里写 vllm 会被 parser 拒绝。
QLoRA 必须写对枚举。quantization_method 的 bitsandbytes 取值是 bnb,不是 bitsandbytes;位数写在 quantization_bit,通常是 4 或 8。当前准确示例是 examples/train_qlora/qwen3_lora_sft_bnb_npu.yaml。仓库里不存在名为 examples/train_qlora/qwen3_lora_sft.yaml 的文件,不要按旧笔记去找。
2. 数据:三次 schema,而不是一次读入
训练 workflow 调用 get_dataset()。数据不是“读完 JSON 直接喂 Trainer”,而是经过三次形态变化。第一次是用户原始列,由文件自己决定。第二次是 converter 对齐后的统一列:_prompt、_response、_system、_tools、_images、_videos、_audios。第三次是 stage processor 产出的 token 列,再交给 collator 做 padding 与 mask。
内置 converter 只有三种:alpaca、sharegpt、openai。它们注册在 data/converter.py,可以通过注册函数扩展,但不能指望任意 JSON 结构被自动识别。data/dataset_info.json 描述来源、formatting、列映射以及 ranking 等属性。来源可以是 Hub、脚本、本地文件或有限的对象存储路径;Hub 选择还受 USE_MODELSCOPE_HUB 等环境变量影响。默认目录仍是相对路径 data,这是第 8 章验收时已经强调过的。
三种 converter 对“对话”的理解并不相同,迁移数据时不能只改文件名。Alpaca 把 history 展开成多轮,再把当前 prompt 与 query 拼成最后一条用户话;普通样本只产生一条助手回答,ranking 样本则写出 chosen 与 rejected 两条,KTO 则按布尔标签把真实回答放进两个位置之一。ShareGPT 要求角色严格交替,奇数位是 user 或 observation,偶数位是 assistant 或 function;异常样本不会立刻从数据集删除,而是变成空 prompt,随后由 processor 丢弃。OpenAI 格式会处理 tool_calls,并把连续的 tool 结果合并为一条 observation;在没有 tools 时,这个版本还会注入或追加一段关闭详细思考的 system 文本。这是 converter 特有行为,不是模板层的通用默认值。
五个 dataclass 也可以按职责来记,避免把所有字段都塞进一个“训练配置”印象里。ModelArguments 管权重路径、量化、推理后端和导出;DataArguments 管数据集名、目录、截断长度与模板;TrainingArguments 继承 Hugging Face 的训练循环参数;FinetuningArguments 管 stage、微调类型和对齐损失;GeneratingArguments 管生成式评估时的解码。字段写错位置,表面上看 YAML 很完整,parser 仍会按类边界拒绝或忽略你的意图。
processor 由传入的数据 stage 决定,不由算法营销名决定:
| 传入数据 stage | 实际处理 |
|---|---|
pt |
预训练拼接 |
sft |
监督微调;开启 packing 时换 packed 实现 |
rm |
成对偏好 |
kto |
反馈样本,并构造 KL 样本与 kto_tags |
ppo 以及生成式评估 |
无监督 prompt |
数据层的 stage 类型里 没有 dpo。DPO workflow 调用的是 get_dataset(..., stage="rm"),与奖励模型共用 pairwise processor,并要求数据集声明 ranking=true。这只表示复用“同一 prompt 下两条回答”的数据形态,并不表示 DPO 在训练奖励模型。把 DPO 数据写成普通 SFT 对话、或不设 ranking,会在预处理阶段丢掉合法样本。
DatasetModule 只有 train_dataset 与 eval_dataset 两个键,没有 predict_dataset。预测复用验证集。显式 eval_dataset 与 val_size > 0 互斥。加载和 tokenize 包在 main_process_first 里,避免多进程同时写缓存。
3. 训练:先路由,再走公共骨架
run_exp() 先规范化参数,再决定是否进入 Ray。普通路径进入 _training_function():解析五元组、按固定顺序追加回调,然后路由到某个 workflow。回调始终包含日志;其后按需加入 PiSSA 转换、SwanLab、早停、性能分析,最后是上报配置的 Reporter。调用方传入的回调会留在列表前部。
路由优先级不是“只看 stage”。若 pt/sft 且开启 use_hyper_parallel,进入 HyperParallel;否则若 pt/sft/dpo 且开启 MCA,进入 MCA;都不命中,才按 pt|sft|rm|ppo|dpo|kto 进入默认目录。开关与 stage 不匹配时落回默认路径,而不是报“后端不支持”。因此不能只看 YAML 里出现过某个字段,就断定最终走了哪条后端。
六个默认 workflow 共享同一骨架:先 load_tokenizer,再 get_template_and_fix_tokenizer,然后 get_dataset 与 load_model,接着构造 stage 专用 collator 与 Trainer,最后按 do_train / do_eval / do_predict 执行并保存。模板修复必须发生在模型加载之前,因为它可能补充 EOS、PAD 与 stop words;模型再按 resize_vocab 调整 embedding。SFT 是最完整的一条,生成式预测会切到左 padding,并使用 predict_with_generate。RM 会加 value head,保存时还要把 v_head 拆到独立文件。
请把 stage 与损失再分开记一次。合法训练 stage 只有六个,多写一个名字不会被自动纠正。pref_loss 的精确取值是 sigmoid|hinge|ipo|kto_pair|orpo|simpo,它们全部挂在 stage: dpo 下面。ORPO 与 SimPO 尤其容易被写成独立阶段,这是文档宣传与源码枚举不一致造成的认知陷阱。
多卡仍然必须走 llamafactory-cli train,以便 launcher 自动 torchrun。src/train.py 不会替你拉起多进程。DeepSpeed 配置在解析阶段还可能要求 FORCE_TORCHRUN=1。这些进程问题属于第 11 章,但写 SFT YAML 时就应避免选错入口。
也可以把一次成功的 SFT 想象成四次“对上号”。第一次,文件路径与 key=value 对上 read_args 的合并规则。第二次,字段名对上五个 dataclass,而不是对上一份自己发明的扁平字典。第三次,原始列对上 converter,再对上 _prompt/_response 这组统一列。第四次,数据 stage 对上 processor,训练 stage 对上 workflow。任何一次对不上,失败点都不同:第一次是命令行语法,第二次是未知参数,第三次是空样本或字段缺失,第四次是训到了错误的损失或错误的模型头。冒烟命令的价值,就是用十六个样本把这四次对齐一起跑通。
4. 写配置时最容易错的四处
把配置、数据与训练看成三份契约,比记住几十个字段更有用。交作业前先核对:
- YAML 覆盖与纯 CLI 是两套语法;未知键默认失败,不要靠
ALLOW_EXTRA_ARGS蒙混。 - QLoRA 写
quantization_method: bnb,不要写bitsandbytes。 - DPO 的训练 stage 是
dpo,数据层看到的却是rm;ORPO/SimPO 只是pref_loss。 - 训练配置里的
infer_backend只能是 Hugging Face;vLLM 与 SGLang 留给下一章的推理入口。
下一章离开 Trainer,进入推理引擎、LLaMA Board 与 OpenAI 风格 API。请继续阅读:LLaMA Factory:推理、LLaMA Board 与 OpenAI API。
正在加载留言…