Chapter 12
Checkpoint 转换:NeMo ↔ HF
NeMo 训完的模型是 .nemo 格式(zip 包含 weight + config + tokenizer),跟 HF 生态不直接互通。本章给两个方向的转换脚本。
12.1.nemo 是什么
.nemo 是 NeMo 自定义的打包格式,本质是 tar/zip,内部结构:
my_model.nemo
├── model_config.yaml # Hydra 风格的完整配置
├── model_weights.ckpt # PyTorch state_dict(torch.save)
├── tokenizer.model # SentencePiece / HF tokenizer
└── (可选) extra files
大文件可以分 shard,每个 shard 一个文件夹。
12.2NeMo → HF:常见模型转换脚本
| 模型 | 脚本 |
|---|---|
| Llama 系 | scripts/checkpoint_converters/convert_llama_nemo_to_hf.py |
| Mistral / Mixtral | convert_mistral_*_to_hf.py |
| Gemma | convert_gemma_*_to_hf.py |
| Falcon | convert_falcon_*_to_hf.py |
| Qwen / Qwen2 | convert_qwen_*_to_hf.py |
| Starcoder2 | convert_starcoder2_*_to_hf.py |
| Baichuan2 | convert_baichuan2_*_to_hf.py |
典型命令
python scripts/checkpoint_converters/convert_llama_nemo_to_hf.py \
--input_name_or_path ./out/llama3_8b_sft.nemo \
--output_path ./hf_out/llama3_8b_sft \
--hf_input_path meta-llama/Meta-Llama-3-8B \
--precision bf16
--hf_input_path 是关键:从原 HF 模型抄走 tokenizer / config,再覆盖 weight。
12.3HF → NeMo
| 模型 | 脚本 |
|---|---|
| Llama | convert_llama_hf_to_nemo.py |
| Mistral | convert_mistral_hf_to_nemo.py |
| Gemma | convert_gemma_hf_to_nemo.py |
python scripts/checkpoint_converters/convert_llama_hf_to_nemo.py \
--input_name_or_path meta-llama/Meta-Llama-3-8B \
--output_path ./nemo_out/llama3_8b.nemo \
--precision bf16
12.4转换原理
核心是权重名映射。NeMo / Megatron 风格 vs HF 风格:
| NeMo 权重名 | HF 权重名 | 变换 |
|---|---|---|
| model.embedding.word_embeddings.weight | model.embed_tokens.weight | 直接对应 |
| model.decoder.layers.N.self_attention.linear_qkv.weight | 分成 q_proj / k_proj / v_proj | ★ 需要 split + 重排 |
| model.decoder.layers.N.self_attention.linear_proj.weight | o_proj.weight | 直接 |
| model.decoder.layers.N.mlp.linear_fc1.weight | 分成 gate_proj / up_proj | split |
| model.decoder.layers.N.mlp.linear_fc2.weight | down_proj.weight | 直接 |
| model.decoder.final_layernorm.weight | model.norm.weight | 直接 |
| model.output_layer.weight | lm_head.weight | 直接 |
关键难点:QKV 的合并和拆分。NeMo 把 q/k/v 合并成一个 fused tensor (3d, d) 排列,而且 GQA 时按 group 排,转的时候要按 head_dim、num_query_heads、num_key_value_heads 切对。
12.5转 GGUF(llama.cpp 用)
NeMo → HF 之后用 llama.cpp 工具继续转:
# 先 NeMo → HF
python convert_llama_nemo_to_hf.py ...
# 然后 HF → GGUF
python llama.cpp/convert_hf_to_gguf.py ./hf_out --outtype bf16 -o ./model.gguf
# 量化
./llama.cpp/llama-quantize ./model.gguf ./model-q4_k_m.gguf q4_k_m
12.6NeMo 内部 ckpt 形式
除了 .nemo 打包格式,NeMo 训练中产生:
- distributed checkpoint(默认):每张卡保自己那份,子目录结构;
- Lightning .ckpt:传统单文件(小模型用);
- resume checkpoint:含 optimizer / scheduler / step。
distributed ckpt 加载快、save 快,但不能直接转 HF,要先合并:
python scripts/checkpoint_converters/convert_zarr_to_torch_dist.py \
--input ./out/checkpoints/last.zarr \
--output ./out/last.nemo
12.7LoRA adapter 单独转
# NeMo 的 LoRA adapter 在 .nemo 包里
python scripts/checkpoint_converters/convert_lora_nemo_to_hf.py \
--input ./sft_lora_out/.nemo \
--output ./hf_lora
转完用 HF PEFT 直接加载:
from peft import PeftModel
model = PeftModel.from_pretrained(base_hf_model, "./hf_lora")
12.8常见错误
| 现象 | 处理 |
|---|---|
| 转完 HF 推理乱码 | QKV 拆分错;num_key_value_heads 没对 |
| shape mismatch | TP > 1 时要先合并到 TP=1;用 --tensor_model_parallel_size 1 |
| tokenizer 不一致 | 转时记得 --hf_input_path 指对原 HF 模型 |
| 转完后精度大降 | 用 bf16 转;或保 fp32 |
12.9这章你需要带走的
.nemo= zip 包,内含 config + weight + tokenizer;- NeMo ↔ HF 转换在
scripts/checkpoint_converters/,每个模型一份; - 核心难点是 QKV 的 fused ↔ 分开(GQA 要注意 group 排布);
- LoRA adapter 单独有转换脚本,转完能用 HF PEFT 加载;
- distributed checkpoint 要先合并再转 HF。