DomSense / scripts /convert_to_hf.py
LIJINGHAI111's picture
Upload folder using huggingface_hub
f73f9b3 verified
Raw History Blame Contribute Delete
3.91 kB
"""把 DomSense 训练 checkpoint 转换为 Hugging Face 标准格式并导出。
用法:
python scripts/convert_to_hf.py
--ckpt ../outputs/rlcd_mdp_run1/best_model.pt
--out ./saved_model
产出(HF 标准目录):
saved_model/
config.json # SystemOneConfig 序列化
model.safetensors # 转换后的权重(自动去掉 base_model_prefix "core.")
tokenizer.json # DomSenseTokenizer
导出后,可这样加载:
import sys; sys.path.insert(0, "repo_root")
from huggingface_repo import SystemOneModelForDecision, DomSenseTokenizer
m = SystemOneModelForDecision.from_pretrained("saved_model_dir")
"""
import argparse
import sys
from pathlib import Path
import torch
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(REPO_ROOT))
from huggingface_repo import SystemOneModelForDecision, DomSenseTokenizer
from huggingface_repo.configuration_domsense import SystemOneConfig
def convert(ckpt_path: str, out_dir: str):
print(f"[1/4] 读取 checkpoint: {ckpt_path}")
ckpt = torch.load(ckpt_path, map_location="cpu")
state_dict = ckpt["model_state_dict"]
# 兼容两种 checkpoint:扁平 dict 或 {model: {...}} 嵌套
raw_cfg = ckpt.get("config", {})
if isinstance(raw_cfg, dict) and "model" in raw_cfg:
raw_cfg = raw_cfg["model"]
cfg_kwargs = {
k: v
for k, v in raw_cfg.items()
if v is not None and not isinstance(v, dict)
}
# 用权重形状自动修正关键结构维度,避免 config 与 checkpoint 不一致
if "mdp_head.action_embed_table.weight" in state_dict:
cfg_kwargs["max_actions"] = state_dict[
"mdp_head.action_embed_table.weight"
].shape[0]
# 领域数量由权重推断;领域名优先从 config 保留(若有),否则自动命名
if "domain_moe.gate.2.weight" in state_dict:
n_slots = state_dict["domain_moe.gate.2.weight"].shape[0]
n_domains = n_slots - 1
cfg_kwargs["use_domain_experts"] = n_domains > 0
if "domains" not in cfg_kwargs or not cfg_kwargs["domains"]:
cfg_kwargs["domains"] = [f"domain_{i}" for i in range(n_domains)]
# 确保领域列表长度与权重匹配
if len(cfg_kwargs["domains"]) != n_domains:
cfg_kwargs["domains"] = [f"domain_{i}" for i in range(n_domains)]
cfg = SystemOneConfig(**cfg_kwargs)
print(f"[2/4] 构建 HF 模型;hidden_dim={cfg.hidden_dim}, "
f"num_bellman_steps={cfg.num_bellman_steps}, "
f"use_domain_experts={cfg.use_domain_experts}")
model = SystemOneModelForDecision(cfg)
print("[3/4] 加载权重到内核 core.*(strict=False)")
missing, unexpected = model.core.load_state_dict(state_dict, strict=False)
if missing:
print(f" - missing {len(missing)} keys: {missing[:10]}")
if unexpected:
print(f" - unexpected {len(unexpected)} keys: {unexpected[:10]}")
total, trainable = model.count_parameters()
print(f" 总参数量: {total:,} | 可训练: {trainable:,}")
out = Path(out_dir)
out.mkdir(parents=True, exist_ok=True)
print(f"[4/4] 导出 HF 格式到: {out}")
# save_pretrained 基于 base_model_prefix 自动存储(保存去 core.,加载加回)
model.save_pretrained(str(out))
DomSenseTokenizer().save_pretrained(str(out))
print("完成!现在可以从该目录加载:")
print(f' from huggingface_repo import SystemOneModelForDecision, DomSenseTokenizer')
print(f' m = SystemOneModelForDecision.from_pretrained("{out}")')
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--ckpt", default="../outputs/rlcd_mdp_run1/best_model.pt")
parser.add_argument("--out", default="./saved_model")
args = parser.parse_args()
convert(args.ckpt, args.out)