`MAX_RESTARTS` / `RDZV_ID` / `MIN_NNODES`:被标为弹性启动的那组变量
本文事实以
hiyouga/LlamaFactory官方仓库 2026-08-09 的源码与文档为准。我们没有安装、训练或部署过任何模型,文中所有默认值均为实读源码得到的值。
LlamaFactory 的分布式启动有一件反直觉的事:决定它行为的那些开关,绝大多数不是你写在 YAML 里的字段,也不是 llamafactory-cli train 后面跟的选项,而是一串环境变量。它们躺在 src/llamafactory/launcher.py 里,README 对这块只说”分布式用法见 examples/README.md”,于是很多人是在多机训练起不来的时候,才第一次知道 RDZV_ID 这个名字。
这篇只干一件事:把 launcher.py 里那张环境变量清单逐个核对一遍,重点说清被代码注释标为 elastic launch support(弹性启动支持)的那四个——MAX_RESTARTS、RDZV_ID、MIN_NNODES、MAX_NNODES。
先确认你会不会走到这条路上
在关心弹性启动之前,得先知道 LlamaFactory 什么时候才会进入分布式分支。launcher.py 里的判断链条是这样的:
第一步,如果 USE_MCA 或 USE_MEGATRON_BRIDGE 任意一个被启用,代码会强制把 FORCE_TORCHRUN 设成 1,源码里的原文注释就是 # force use torchrun。也就是说,你一旦启用 Megatron-core 或 Megatron-Bridge 这条路,是不是走分布式就不由你决定了。
第二步,真正触发分布式训练的条件是三者合成的:command == "train",并且(FORCE_TORCHRUN 被启用,或者 检测到的设备数大于 1 且没在用 ray 且没在用 kt)。
这个条件里有两处值得单独记住。一是子命令必须是 train——chat、api、export、webui 这些不会走这条分支,所以”我推理时多卡没吃满”跟这套变量没关系。二是多卡场景下它是自动触发的:只要设备数大于 1,且你没在用 ray 或 kt 路径,它就会自己拉起分布式,不需要你显式开启。反过来,如果你只有一张卡但想强制走这条路,就得自己把 FORCE_TORCHRUN 打开。
这张表是实读出来的,可以直接抄
下面这些变量全部由 launcher.py 从环境读取,默认值来自源码:
| 环境变量 | 默认值 |
|---|---|
NNODES | "1" |
NODE_RANK | "0" |
NPROC_PER_NODE | str(get_device_count()),即检测到的设备数 |
MASTER_ADDR | "127.0.0.1" |
MASTER_PORT | str(find_available_port()),即自动找一个空闲端口 |
MAX_RESTARTS | "0" |
RDZV_ID | 无默认(os.getenv 不带默认值) |
MIN_NNODES | 无默认 |
MAX_NNODES | 无默认 |
OPTIM_TORCH | is_env_enabled("OPTIM_TORCH", "1"),即默认启用 |
这张表怎么读,比表本身重要。
前五行是”单机也会用到”的那批:节点数默认 1、当前节点编号默认 0、每节点进程数跟着检测到的设备数走、主地址默认回环地址、主端口是运行时找一个空闲的。MASTER_PORT 默认值不是一个固定数字,而是一次函数调用——这意味着你如果指望它每次都落在同一个固定端口上(比如在别处把这个端口写死),那个假设从一开始就不成立,得自己显式指定。
OPTIM_TORCH 是这张表里唯一一个默认就是启用的开关。这类”默认开着”的项在排查性能异常时容易被忽略,因为你从没设过它,很难想到它在起作用。
弹性那四个:一个有默认值,三个没有
MAX_RESTARTS、RDZV_ID、MIN_NNODES、MAX_NNODES 这四个在源码注释里被单独标为 elastic launch support。它们的状态并不对称:
MAX_RESTARTS 有默认值,是字符串 "0"。注意它是字符串而不是数字,因为它本来就是从环境变量读进来的。至于 "0" 这个取值在启动器语义下具体意味着什么,请以仓库源码与 PyTorch 官方文档的说明为准,我们没有跑过任何一次训练,不替它下结论。
另外三个——RDZV_ID、MIN_NNODES、MAX_NNODES——根本没有默认值,源码里的 os.getenv 调用不带第二个参数。这一点是这篇最实用的信息:它们不是”默认为空字符串”,也不是”默认等于 NNODES”,而是你不设就是 None。所以当你看到某篇教程说”把 MIN_NNODES 调小一点”时,前提是你已经把它设出来了;在完全没设的情况下讨论”它默认是多少”,是个不成立的问题。
这也解释了为什么弹性这套东西不会被误触发:你不主动提供这几个变量,它们就一直是空的。
为什么要把 examples/README.md 里的一行拎出来
examples/README.md 把配方按 LoRA / QLoRA / 全参 / 合并导出 / 推理 / Extras 分了几大块。这篇只需要其中一行:Full-Parameter Fine-Tuning 这一块下面,“多节点弹性容错监督微调(Elastic and Fault-Tolerant)“是单独列出的一节,与单节点、多节点这两条并列。
之所以只引这一行,是因为它是文档侧唯一与 launcher.py 里那组 elastic 变量直接对得上的锚点:代码里有一组标着 elastic launch support 的环境变量,示例侧就有一节专门的弹性容错配方。这两处能对上,是我们能核实到的全部;这条路径的完成度、维护状态如何,没有依据,我们不推断。至于那节配方文件里具体写了什么,我们没有读过对应的 YAML,不做描述。examples/ 下完整的配方矩阵我们另有一篇专门讲,这里不铺开。
怎么设:Windows 和 Linux/macOS 不一样
这些值全部走环境变量,所以设置方式取决于你的 shell,跟 LlamaFactory 本身无关。
Linux / macOS:
export FORCE_TORCHRUN=1
export NNODES=<你的节点数>
export NODE_RANK=<当前节点编号>
export MASTER_ADDR=<主节点地址>
export MASTER_PORT=<你指定的端口>
llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml
Windows(cmd):
:: 注意 set 后面等号两边不要留空格
set FORCE_TORCHRUN=1
set NNODES=<你的节点数>
set NODE_RANK=<当前节点编号>
set MASTER_ADDR=<主节点地址>
set MASTER_PORT=<你指定的端口>
llamafactory-cli train examples/train_lora/qwen3_lora_sft.yaml
以上为按官方参数语义组合的示例,未逐项实测,以官方文档与 --help 的实际输出为准。尖括号里的值该填多少,取决于你的集群规模和硬件,官方没给通用值,这篇也不替你猜。
顺带记两个能省事的小事实:llamafactory-cli 有官方短别名 lmf,写在 USAGE 的 Hint 行里;另外 launcher.py 的命令解析是 command = sys.argv.pop(1) if len(sys.argv) > 1 else "help",不带任何参数时默认走 help,直接敲 llamafactory-cli 就能看到那八条子命令的清单。
起来之后,怎么确认走的是哪条分支
排查这类问题最怕的是”我以为我在跑分布式”。launcher.py 里有两行日志可以当判据:
进入分布式分支时会打印 Initializing {nproc_per_node} distributed tasks at: {master_addr}:{master_port};只有在 int(nnodes) > 1 时,才会额外打印 Multi-node training enabled: num nodes: {nnodes}, node rank: {node_rank}。
所以判定动作很明确:看到第一行,说明分布式分支确实被触发了,而且能顺带确认它最终用的进程数、主地址、主端口是什么——这三个值如果和你以为设的不一样,问题就在环境变量没传进去,而不在训练配置。只看到第一行、没有第二行,说明 NNODES 仍是默认的 "1",你的多机配置根本没生效。两行都没有,说明压根没进分布式分支,回上面那个三条件判断逐条对一遍:子命令是不是 train、FORCE_TORCHRUN 有没有开、设备数是不是真的大于 1、是不是走了 ray 或 kt 路径。
什么情况说明不是这个原因:如果日志里两行都正常打印、进程数与地址端口也都对得上,那问题就不在这组启动变量身上了,往训练配置、数据侧或依赖环境去查。同理,chat / api / export 这几个子命令下遇到的多卡问题,与本文这组变量无关,它们连分布式分支的门都没进。
两个会影响你读到的默认值的前提
第一,本文这张表读的是 src/llamafactory/launcher.py。仓库里另有一套 src/llamafactory/v1/launcher.py,src/llamafactory/cli.py 的逻辑是:环境变量 USE_V1 启用时加载 llamafactory.v1.launcher,否则加载原来的 llamafactory.launcher。我们只核实了 v1/ 的目录结构和这个开关的存在,没有核实 v1 那套 launcher 里这组变量的取值,所以如果你开了 USE_V1,上面这张表就不能直接套用。仓库同时存在旧实现与一套 v1/ 重写,这是可以确认的事实,别的我们不推断。
第二个是个纯粹的认路问题。launcher.py 在启动时会打一个 WELCOME 横幅,里面写着 | Welcome to LLaMA Factory, version {VERSION} 和 | Project page: https://github.com/hiyouga/LLaMA-Factory |——注意这里是带连字符的旧名 LLaMA-Factory,而仓库当前的真实路径是 hiyouga/LlamaFactory。仓库内部新旧两种写法并存,README 正文里也是混用的。这里只陈述差异,不推断原因。对你的实际影响只有一条:翻 issue、搜报错的时候两种拼法都试一遍。
一句话收尾
这组变量的价值不在于”该设成多少”,而在于它揭示了 LlamaFactory 的一层设计:YAML 管训练怎么训,环境变量管进程怎么起。这两层是分开的,你在 YAML 里翻遍了也找不到 MASTER_PORT。知道该去 launcher.py 而不是去 finetuning_args.py 找这些开关,多机训练起不来的时候,排查方向至少不会一开始就找错地方。
本文依据 LlamaFactory 官方仓库(github.com/hiyouga/LlamaFactory)的 README、data/README.md、examples/ 下的配置与 src/llamafactory/hparams/ 的参数定义整理,核对日 2026-08-09。本文内容为仓库源码与文档口径,我们没有安装、训练或部署过任何模型,文中显存数字均为官方标注的估算值(README 原文标 * estimated)而非实测占用。参数与默认值随版本变动,请以 llamafactory-cli train -h 的实际输出为准。