Chapter 04

核心概念:6 类 args / template / stage / data registry

📌 本章引用的源码路径与文件名基于 hiyouga/LlamaFactory · commit 01398eb18dd4 。

第 3 章你看到的那份 YAML 之所以"分成 5 个区段",是因为 LLaMA-Factory 内部把所有配置切成了 6 类 dataclass,YAML 里看到的每一行都会被分发到这 6 个 args 对象里。本章先把这 6 类讲清楚,再讲三个最容易让初学者卡住的概念:template、stage、data registry

4.1 六类 args 是什么、文件在哪

进入仓库:

ls src/llamafactory/hparams/
# data_args.py   evaluation_args.py   finetuning_args.py
# generating_args.py   model_args.py   training_args.py
# parser.py   __init__.py

这 6 个 dataclass 各自负责一类配置,互不重叠

Args 类文件管什么YAML 典型字段
ModelArguments model_args.py 选模型、tokenizer、量化、信任远程代码model_name_or_pathtrust_remote_codequantization_bit
DataArguments data_args.py 选数据集、模板、切块、cutoff datasettemplatecutoff_lenmax_samples
FinetuningArgumentsfinetuning_args.py 选训练算法 + 算法专属超参stagefinetuning_typelora_rankpref_betappo_*
TrainingArguments training_args.py 复用 HF Trainer 的训练循环超参 per_device_train_batch_sizelearning_ratebf16output_dir
GeneratingArgumentsgenerating_args.py 推理 / chat / api 时的生成参数 temperaturetop_pmax_new_tokens
EvaluationArgumentsevaluation_args.py 评测任务参数 tasklangn_shot

hparams/parser.py 里的 get_train_args() / get_infer_args() / get_eval_args() 是 YAML/CLI → 这 6 个对象的统一解析入口。当你看到任何"YAML 字段不识别"的报错,第一步永远是去这 6 个文件里搜该字段名,能立刻定位它属于哪一类、默认值是什么。

提示 TrainingArguments 是直接继承 HuggingFace transformers.TrainingArguments 的,因此你能用的字段集 = HF Trainer 全部字段 + LLaMA-Factory 加的几个。

4.2 stage:5 个训练阶段

FinetuningArguments.stage 这一个字段决定你跑哪个训练管线:

stage干什么对应代码
pt 继续预训练 (Pre-Training) src/llamafactory/train/pt/
sft 监督微调 (Supervised Fine-Tuning) src/llamafactory/train/sft/
rm 奖励模型训练 (Reward Modeling) src/llamafactory/train/rm/
ppo PPO(在线 RL) src/llamafactory/train/ppo/
dpo / kto / orpo / simpo偏好优化(具体看 pref_losssrc/llamafactory/train/dpo/

记忆口诀stage 选大方向、pref_loss 选 DPO 系内部的具体算法。

4.3 finetuning_type:怎么"动"模型

正交于 stage 的第二维:选全参 / LoRA / 冻结

finetuning_type含义
full 全参微调,更新所有权重
lora LoRA 低秩微调,只更新 LoRA 矩阵(最常用)
freeze 冻结大部分层,只放开少数 transformer block

加上量化 quantization_bit: 4 就是 QLoRA。

4.4 template:对话格式适配器

这是 LLaMA-Factory 最有特色、也最容易被踩坑的概念。

问题背景:每个对话模型都有自己的 prompt 拼接格式。Llama-3 用 <|begin_of_text|><|start_header_id|>user<|end_header_id|>...;Qwen 用 ChatML;ChatGLM 用 <|user|> <|assistant|>;Mistral / Yi / DeepSeek / InternLM 各有各的写法。如果你用 Llama-3 的格式去训 Qwen,模型不会报错,但会被训坏

LLaMA-Factory 的解决:在 src/llamafactory/data/template.py 里维护一个"模板注册表"。你在 YAML 里写 template: qwen3_nothink,框架就会拿对应模板去拼接对话。

# 想看支持哪些模板,grep 一下:
grep -E "^_register_template|^register_template" \
     src/llamafactory/data/template.py | head -20
两条铁律
  1. template 必须和 model_name_or_path 匹配:选错模板 = 训坏;
  2. chat / api / export 时也要传同一个 template,否则推理结果会乱码或质量塌方。

4.5 dataset registry:data/dataset_info.json

LLaMA-Factory 不让你"指定一个 jsonl 文件路径",而是要求你先把数据集注册到 data/dataset_info.json,然后 YAML 里用注册名引用。

注册项的最简形式(见仓库 data/dataset_info.json):

{
  "alpaca_en_demo": {
    "file_name": "alpaca_en_demo.json"
  }
}

更复杂的:

{
  "glaive_toolcall_en_demo": {
    "file_name": "glaive_toolcall_en_demo.json",
    "formatting": "sharegpt",          // alpaca 还是 sharegpt 格式
    "columns": {                       // 列名映射
      "messages": "conversations",
      "tools": "tools"
    }
  }
}

两种内置格式

formatting典型 schema适合
alpaca {"instruction":..., "input":..., "output":...}单轮 SFT 数据
sharegpt{"conversations":[{"from":"user","value":...}, ...]}多轮对话

YAML 里 dataset: identity,alpaca_en_demo 这种逗号分隔写法 = 多个数据集同时喂、自动拼接。

4.6 stage / finetuning_type / template / dataset 的相互约束

这四个字段经常一起出错,记下面这张"必须同时改"清单:

例子(错误诊断):你把 examples/train_lora/qwen3_lora_dpo.yaml 改成 SFT 但忘了换数据集 → dataset: dpo_en_demo 是 preference 格式,SFT 处理不了 → 启动报错 "missing field 'output'"。

4.7 launcher:CLI 到底是怎么调度的

src/llamafactory/launcher.pylaunch() 函数大致流程:

flowchart TB argv["sys.argv[1] = command"] --> sw{"command 是哪个?"} sw -->|train| chkTr{"需要 torchrun?
(多卡 / DeepSpeed)"} chkTr -->|是| torchrun["用 torchrun
重新拉起 child"] chkTr -->|否| inproc["in-process 调
train.workflow.run_exp()"] sw -->|chat| cli_chat["cli_chat"] sw -->|webui| webui_app["webui_app"] sw -->|api| api_app["api_app"] sw -->|export| export_model["export_model"]

这就是为什么有的命令需要 FORCE_TORCHRUN=1:单卡时框架会跳过 torchrun,但有时即使单卡你也想用 torchrun(比如 DeepSpeed 单卡 ZeRO-Offload),就强制打开。

v1/ 目录是一套新的入口(启用环境变量 USE_V1=1),是仓库正在向 1.0 API 演进的产物,新功能会先在 v1 里出现

4.8 一图收尾

flowchart TB YAML["YAML / CLI args"] --> Parser["hparams/parser.py 分发"] Parser --> MA[ModelArgs] Parser --> DA[DataArgs] Parser --> FA[FinetuningArgs] Parser --> TA[TrainingArgs] Parser --> GA[GeneratingArgs] Parser --> EA[EvalArgs] MA --> Load["模型加载 (HF / ModelScope)"] DA --> DataPipe["data/template.py +
dataset_info.json 拼数据"] FA --> PickTr["按 stage + finetuning_type
选 train/<stage>/"] TA --> Loop["HF Trainer 训练循环"] Load --> Loop DataPipe --> Loop PickTr --> Loop

记住这张图,后续配置、PEFT、DeepSpeed、多模态、加速集成等高级章节都只是在某一格里加细节。

4.9 6 类 args 的真实 dataclass 锚点

本节给"哪个字段在哪个文件的哪行定义"的查表。下次看到 YAML 字段不识别时去对应行 grep:

dataclassfile:line继承 / 组合关系
ModelArguments hparams/model_args.py:542 聚合 BaseModelArguments / QuantizationArguments / ProcessorArguments / ExportArguments / VllmArguments / SGLangArguments / KTransformersArguments 七个 mixin
DataArguments hparams/data_args.py:23 独立 dataclass;__post_init__(189) 自动启用 packing
TrainingArguments hparams/training_args.py:143继承 HF Seq2SeqTrainingArguments(或 USE_MCA=1 时的 McaSeq2SeqTrainingArguments)+ ProfilerArguments / Fp8Arguments / RayArguments
FinetuningArguments hparams/finetuning_args.py:444聚合 FreezeArguments / LoraArguments / OFTArguments / RLHFArguments / GaloreArguments / ApolloArguments / BAdamArgument / SwanLabArguments 八个 mixin
GeneratingArguments hparams/generating_args.py:22独立;to_dict(obey_generation_config)(70) 过滤 HF 不认的字段
EvaluationArguments hparams/evaluation_args.py 独立

统一入口:hparams/parser.pyget_train_args() / get_infer_args() / get_eval_args()(line 304–534),内部用 HfArgumentParser([ModelArguments, DataArguments, TrainingArguments, FinetuningArguments, GeneratingArguments])

4.10 "派生字段":YAML 里不写但运行时存在的字段

读源码经常会看到 model_args.compute_dtype 之类,但 YAML 里从来没见这个字段——它是 get_train_args() 在 line 512–519 自动算出来的:

派生字段计算规则
model_args.compute_dtype torch.bfloat16 if bf16 else torch.float16 if fp16 else None
model_args.device_map {"": get_current_device()}(单卡时)
model_args.model_max_length data_args.cutoff_len
model_args.block_diag_attn data_args.neat_packing
finetuning_args.lora_alpha lora_rank * 2 当用户没传时(finetuning_args.py:568)
finetuning_args.use_ref_model stage == "dpo" and pref_loss not in ["orpo", "simpo"](line 574)
data_args.packing Truestage == "pt"(data_args.py:189);neat_packing=True 时也自动设 True 并 cutoff_len -= 1

这些"看不见的派生字段"是新手最容易踩的坑:你以为只动了 bf16: true,实际上整条 dtype 链路都跟着变;改了 lora_rank 但忘了 lora_alpha——其实框架自动给你算了 2 * lora_rank

4.11 template 注册:_register_template 真实签名

4.4 提到了 template 概念,这里展开 register 函数的真实签名(data/template.py:490register_template()):

register_template(
    name,                          # 模板名,YAML 用
    format_user,                   # 用户消息 Formatter
    format_assistant,              # 助手消息 Formatter
    format_system,                 # system 消息 Formatter
    format_function,               # function call Formatter
    format_observation,            # tool 结果 Formatter
    format_tools,                  # tools 列表 Formatter
    format_prefix,                 # 整段对话前缀
    default_system,                # 默认 system prompt
    stop_words,                    # 额外的停止词
    thought_words,                 # <think></think> 等思考标记
    tool_call_words,               # tool call 触发词
    efficient_eos,                 # 是否把 eos_token 嵌进 slots
    replace_eos,                   # 替换 tokenizer 的 EOS
    enable_thinking,
    preserve_thinking,
    mm_plugin,                     # 多模态 plugin 对象
)

所有 Formatter 都是 StringFormatter(slots=[...]) 形式,slots 里能塞 "<|im_start|>user\n" 这种字面量,或 {"content"} 这种占位符。 line 487 的 TEMPLATES 全局 dict 是注册表,YAML template: qwen3_nothink 就是查这个 dict。

"自动从 tokenizer 的 chat_template 推 template"也支持(get_template_and_fix_tokenizer at line 630–641)—— 当 YAML 不写 template 字段时,框架会尝试解析 tokenizer 自带的 chat_template;但生产环境强烈建议写明确名字。

4.12 DatasetAttr:dataset_info.json 的真实字段

4.5 给了 dataset_info 的两个例子,下面是 data/parser.py:27 DatasetAttr 的全字段——把它和 dataset_info.json 的 JSON 对照看,你会发现 JSON 字段都是 dataclass 字段的镜像:

字段默认含义
load_from hf_hub / ms_hub / om_hub / script / file
dataset_name HF / MS / Modelers Hub 仓库 ID 或文件名
formatting "alpaca"alpaca / sharegpt / openai
ranking False是否是 pairwise(DPO / RM)数据
subset NoneHF dataset 的 subset
split "train"
folder None子目录
num_samples None采样数量上限
prompt / query / responseinstruction / input / outputalpaca 格式列名映射
history / messagesNone / conversations多轮对话字段
role_tag / content_tagfrom / valuesharegpt 格式标签
user_tag / assistant_tag / system_tag / observation_tag / function_taghuman / gpt / system / observation / function_callsharegpt 角色名
chosen / rejected / kto_tagDPO / KTO 的偏好字段
images / videos / audios多模态字段名

"我自定义了一份数据,列名不一样要怎么用?" → 不改数据,改 dataset_info.json 的 columns 字段映射上去即可,不用动 LLaMA-Factory 任何 Python 代码。