量化五个参数,以及 NPU 场景为什么要关掉 `double_quantization`
本文所有事实以
hiyouga/LlamaFactory官方仓库 2026-08-09 的内容为准。我们没有安装、训练或部署过任何模型,文中数字均为仓库源码与文档里写着的值。
很多人以为 LlamaFactory 的量化配置是一大坨,实际上在 src/llamafactory/hparams/model_args.py 里,量化这一组只有五个字段。真正让人踩坑的不是字段多,而是它们和导出侧那几个同前缀的字段长得太像,以及其中一个字段的默认值恰好是 True——README 在 Ascend NPU 那节让你显式写一行关掉它,原因就出在这个默认值上。
五个参数,一次列全
以下每个默认值都来自 model_args.py 的 field(default=...),逐字照抄:
| 参数 | 默认值 |
|---|---|
quantization_method | QuantizationMethod.BNB |
quantization_bit | None |
quantization_type | "nf4" |
double_quantization | True |
quantization_device_map | None |
五个里有两个默认是 None,一个默认是字符串 "nf4",一个默认是布尔 True,还有一个默认是枚举成员 QuantizationMethod.BNB。
枚举这件事值得多看一眼。model_args.py 里不止这一处用枚举成员当默认:注意力那组的 flash_attn 默认是 AttentionFunction.AUTO,推理后端的 infer_backend 默认是 EngineName.HF。而在示例配置那一侧,examples/inference/qwen3_lora_sft.yaml 里 infer_backend 写的是字符串 huggingface,注释里给的四个取值是 [huggingface, vllm, sglang, ktransformers]。同一个字段,源码默认值记成枚举成员、YAML 里写成小写字符串,两处的字面形态本来就不是一套——所以核对默认值时看源码,写配置时照示例注释里给的取值抄。至于源码怎么把两者对上,那要读实现,我们没读,也不在这儿猜。
它们坐在哪一层:三处最容易混的地方
第一处:这五个字段全部在 model_args.py,不在 finetuning_args.py。仓库把超参按数据、模型、微调、生成、评测、训练、Megatron 桥接分成了 8 个文件放在 src/llamafactory/hparams/ 下,量化归”模型加载”这一层。所以它约束的是模型怎么被载进来,而不是训练用什么方法——finetuning_type(默认 "lora")、lora_rank(默认 8)那一批是另一层的事。
第二处,也是最容易写错配置的一处:quantization_bit 和 export_quantization_bit 是两个不同的字段。后者属于导出那一组,默认同样是 None,同组还有 export_quantization_dataset(默认 None)、export_quantization_nsamples(默认 128)、export_quantization_maxlen(默认 1024)。前缀带不带 export_,决定了这个值作用在加载模型的时候还是 llamafactory-cli export 的时候。复制别人配置片段时如果把前缀吃掉了,YAML 本身不会报你抄错了。
第三处:quantization_device_map(默认 None)和模型加载那组的 device_map(默认也是 None)是两个字段,名字只差一个前缀。README 的 Optional 依赖表里 bitsandbytes 一行标的是 Minimum 0.39.0、Recommend 0.43.1,量化这条路径上的依赖版本以那张表为准。
还有一处不在 model_args.py 里、但名字里同样带 quantization_bit 的:finetuning_args.py 的偏好对齐那一组有 ref_model_quantization_bit 和 reward_model_quantization_bit,默认都是 None,它们各自跟着 ref_model / reward_model(默认都是 None)和对应的 *_adapters 字段走。换句话说,“量化位数”这个概念在这个仓库里一共出现在四个字段上:主模型、导出、参考模型、奖励模型。四个默认值全是 None,四个作用对象各不相同。
真要确认自己写的某一项落在哪一层,最省事的判定动作是回 src/llamafactory/hparams/ 下按文件名找:模型加载相关的去 model_args.py,训练方法相关的去 finetuning_args.py,生成相关的去 generating_args.py,评测相关的去 evaluation_args.py。找不到,说明你记的那个名字在当前版本里可能压根不存在,别继续照着改配置。
quantization_bit 默认 None:和 quantization_method 分开看
这一项默认 None,意思很朴素:源码没有给它预设任何位数,你不显式写,配置里就没有位数值。而同一组里的 quantization_method 默认已经是 QuantizationMethod.BNB,是个有具体落点的枚举成员。两件事得分开看——“方法这一项有默认值”和”位数这一项也有默认值”不是一回事。至于 quantization_bit 为 None 时代码具体怎么走,那要读实现,我们没读,不推断。
顺带把 README 那边的口径对上。README 的 Features 里,Scalable resources 一条写的是 16-bit 全参微调、freeze-tuning、LoRA,以及通过 AQLM、AWQ、GPTQ、LLM.int8、HQQ、EETQ 实现的 2/3/4/5/6/8-bit QLoRA。也就是说,README 层面枚举了多种量化后端,而源码里 quantization_method 的默认落点是 BNB 这一个。这两句都是各自出处的原话,我们只做并列陈述,不推断哪种后端对应哪个参数组合,也不推断为什么默认选它——那需要读实现,我们没读。
仓库的 examples/ 下确实单列了一个 examples/train_qlora/ 目录,README 在 NPU 那节点名的 qwen3_lora_sft_bnb_npu.yaml 就是这个路径下的文件。examples/README.md 的 QLoRA Fine-Tuning 一节列了五条配方,其中只有”4/8-bit Bitsandbytes/HQQ/EETQ 量化下的监督微调”那一条被 README 标了 (Recommended),GPTQ、AWQ、AQLM 三条没有这个标注——这是 README 自己的标注,不是我们的判断,也别把它读成”其余三条不能用”。
NPU 那条提示:README 让你写的那一行,和源码默认值的关系
README 的折叠块里给 Ascend NPU 用户的提示只有一句:在配置里设 double_quantization: false,并给了参考示例 examples/train_qlora/qwen3_lora_sft_bnb_npu.yaml。
把这句话和上面那张表放在一起看,事情就清楚了一半:
double_quantization: false
double_quantization 的源码默认值是 True。这意味着如果你什么都不写,它就是开着的。所以 README 那条提示不是”NPU 上多加一个开关”,而是”NPU 上需要显式把一个默认开着的东西关掉”——不写这一行,等于选择了默认值 True。这是本篇唯一一处可以确定的因果:那条提示之所以必须存在,是因为默认值不是 false。
至于”为什么在 NPU 上要关”,README 没有给出理由,我们也没有在 NPU 上跑过任何训练,所以这里就停在这儿,不去推断是内核实现、算子支持还是别的什么。任何声称知道原因的说法,都需要有能核对的出处,而这条提示本身没提供。
有一点要提醒:这条提示的适用范围就是 README 写明的那个场景(Ascend NPU)。它不是一条通用建议,别把 double_quantization: false 当成默认该写的一行抄进所有配置里。
显存表里和量化直接相关的三行
README 有一张 Hardware Requirement 表,整张表另有一篇专门讲,这里只取与量化直接相关的三行——因为它们是这五个参数在文档侧唯一对应得上的量化档位:
| 方法 | Bits | 7B | 14B | 30B | 70B | xB |
|---|---|---|---|---|---|---|
| QLoRA / QOFT | 8 | 10GB | 20GB | 40GB | 80GB | xGB |
| QLoRA / QOFT | 4 | 6GB | 12GB | 24GB | 48GB | x/2GB |
| QLoRA / QOFT | 2 | 4GB | 8GB | 16GB | 24GB | x/4GB |
读这三行有两个硬限定,缺一条都会读歪。
第一,这张表在表头上方被 README 自己标了 * estimated(估算)。它不是实测占用,我们也没有跑过任何一次训练,所以这里只转述表里写的数字,不推导”你那张卡够不够”。
第二,2-bit 那行的 70B 格子写的是 24GB,而同一行通用列给的系数是 x/4,按系数算是 17.5GB。两处对不上。这是表里可核实的差异,如实指出到这一句为止——哪个数是对的、为什么会这样,我们不推断,也不拿它评价什么。
合并 LoRA 时那句全大写的 DO NOT
量化这一组还牵着一条明确的操作禁令。examples/merge_lora/qwen3_lora_sft.yaml 顶部有一行原文注释:
### Note: DO NOT use quantized model or quantization_bit when merging lora adapters
翻成中文就是:合并 LoRA 适配器时,不要使用量化模型或 quantization_bit。这句话是仓库自己用全大写写的,属于示例文件里少见的强措辞,也是这五个参数中 quantization_bit 唯一一处被明文限制使用场景的地方。同一个示例文件里,export_size 是 5、export_device 是 cpu(注释里写了可选 [cpu, auto])、export_legacy_format 是 false,这三项与源码默认值一致。
那到底该不该开、开几 bit
不写。该不该量化、选哪种 quantization_method、quantization_bit 填几,取决于你的模型、数据和硬件,官方没给通用值,我们也没有任何本机运行依据可以支撑一个推荐值。这一篇能给你的确定性只有三条:五个字段的确切名字和默认值、它们和导出侧同前缀字段的分界、以及 NPU 那条提示与 double_quantization 默认 True 之间的对应关系。
真要动手,先用 llamafactory-cli train -h 把当前版本的实际参数列表打出来对一遍。源码默认值会随版本变,本文的核对日是 2026-08-09。
本文依据 LlamaFactory 官方仓库(github.com/hiyouga/LlamaFactory)的 README、data/README.md、examples/ 下的配置与 src/llamafactory/hparams/ 的参数定义整理,核对日 2026-08-09。本文内容为仓库源码与文档口径,我们没有安装、训练或部署过任何模型,文中显存数字均为官方标注的估算值(README 原文标 * estimated)而非实测占用。参数与默认值随版本变动,请以 llamafactory-cli train -h 的实际输出为准。