File size: 5,536 Bytes
6e802a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# export_release_weights.py: Training checkpoint -> weights-only model.safetensors (checked bit-exact) + config.json.

import argparse
import hashlib
import json
import os
import sys

import torch

ARCH_FLAGS = {
    "pure": [],
    "hyb": ["--hybrid_attention_top_k", "4"],
    "tfm": ["--backbone", "transformer", "--model_name", "EleutherAI/pythia-1.4b"],
}
DEPTH_FLAGS = ["--depth_arch", "mamba2", "--depth_layers", "4", "--depth_dim", "1024",
               "--depth_cond", "prefix", "--depth_feedback", "all"]
ENV = {"MVC_NUM_SPEECH_TOKENS": "2048", "MVC_NUM_CODEC_LEVELS": "10", "MVC_ENABLE_PLAN_TOKENS": "1"}
SPEAKER_SUFFIX = "speaker_encoder.embedding.weight"


def jsonable(v):
    if isinstance(v, (str, int, float, bool)) or v is None:
        return v
    if isinstance(v, (list, tuple)):
        return [jsonable(x) for x in v]
    if isinstance(v, dict):
        return {str(k): jsonable(x) for k, x in v.items()}
    return str(v)


def sha256(path, chunk=1 << 24):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for b in iter(lambda: f.read(chunk), b""):
            h.update(b)
    return h.hexdigest()


def main():
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--checkpoint", required=True)
    ap.add_argument("--arch", required=True, choices=sorted(ARCH_FLAGS))
    ap.add_argument("--name", required=True)
    ap.add_argument("--out", required=True)
    ap.add_argument("--description", default="")
    args = ap.parse_args()
    from safetensors.torch import load_file, save_file

    os.makedirs(args.out, exist_ok=True)
    ck = torch.load(args.checkpoint, map_location="cpu", weights_only=False, mmap=True)
    print(f"[export] {args.checkpoint}: top-level keys {sorted(ck)}", flush=True)
    sd = ck["model_state_dict"]
    targs = ck.get("args") or {}

    tk = int(targs.get("hybrid_attention_top_k") or 0)
    tb = str(targs.get("backbone") or "mamba")
    want = {"pure": (0, "mamba"), "hyb": (4, "mamba"), "tfm": (0, "transformer")}[args.arch]
    if (tk, "transformer" if tb == "transformer" else "mamba") != want:
        sys.exit(f"[export] FATAL: --arch {args.arch} but the checkpoint trained with "
                 f"hybrid_attention_top_k={tk}, backbone={tb}")
    for k, v in zip(DEPTH_FLAGS[0::2], DEPTH_FLAGS[1::2]):
        a = k[2:]
        if a in targs and str(targs[a]) != v:
            sys.exit(f"[export] FATAL: {k} {v} in the release flags but the checkpoint trained with {a}={targs[a]}")

    out, seen = {}, {}
    for k, v in sd.items():
        v = v.detach()
        key = (v.untyped_storage().data_ptr(), v.storage_offset(), tuple(v.shape), tuple(v.stride()))
        out[k] = v.clone().contiguous() if key in seen else v.contiguous()
        seen.setdefault(key, k)
    aliases = {k: seen[(v.untyped_storage().data_ptr(), v.storage_offset(), tuple(v.shape), tuple(v.stride()))]
               for k, v in sd.items()
               if seen[(v.untyped_storage().data_ptr(), v.storage_offset(), tuple(v.shape), tuple(v.stride()))] != k}

    wpath = os.path.join(args.out, "model.safetensors")
    save_file(out, wpath, metadata={"format": "pt", "source": os.path.basename(os.path.dirname(args.checkpoint)),
                                    "step": str(ck.get("step"))})
    del out

    back = load_file(wpath)
    if set(back) != set(sd):
        sys.exit(f"[export] FATAL: key sets differ ({len(set(sd) ^ set(back))} keys)")
    bad = [k for k in sd if back[k].dtype != sd[k].dtype or back[k].shape != sd[k].shape
           or not torch.equal(back[k], sd[k])]
    if bad:
        sys.exit(f"[export] FATAL: {len(bad)} tensors differ after the round trip, e.g. {bad[:3]}")

    spk = next((int(v.shape[0]) for k, v in sd.items() if k.endswith(SPEAKER_SUFFIX) and v.ndim == 2), None)
    dtypes = {}
    for v in sd.values():
        dtypes[str(v.dtype)] = dtypes.get(str(v.dtype), 0) + v.numel()
    flags = ARCH_FLAGS[args.arch] + DEPTH_FLAGS + (["--num_speakers", str(spk)] if spk else [])
    cfg = {
        "name": args.name,
        "description": args.description,
        "architecture": {"pure": "pure Mamba-2", "hyb": "hybrid (top-4 attention)", "tfm": "transformer"}[args.arch],
        "base_model": "EleutherAI/pythia-1.4b" if args.arch == "tfm" else "state-spaces/mamba2-1.3b",
        "source_checkpoint": args.checkpoint,
        "step": ck.get("step"),
        "stage": ck.get("stage"),
        "generator_flags": flags,
        "env": ENV,
        "speaker_table_rows": spk,
        "has_plan_injector": any(k.startswith("plan_injector.") for k in sd),
        "n_tensors": len(sd),
        "n_params": sum(v.numel() for v in sd.values()),
        "dtype_numel": dtypes,
        "tied_aliases": aliases,
        "weights_file": "model.safetensors",
        "weights_sha256": sha256(wpath),
        "weights_bytes": os.path.getsize(wpath),
        "bit_exact_vs_checkpoint": True,
    }
    with open(os.path.join(args.out, "config.json"), "w") as f:
        json.dump(cfg, f, indent=2)
    with open(os.path.join(args.out, "training_args.json"), "w") as f:
        json.dump(jsonable(targs), f, indent=2, sort_keys=True)
    print(f"[export] OK {args.name}: {cfg['n_tensors']} tensors, {cfg['n_params'] / 1e9:.3f} B params, "
          f"{cfg['weights_bytes'] / 1e9:.2f} GB, dtypes {dtypes}, speaker rows {spk}, "
          f"plan injector {cfg['has_plan_injector']}, aliases {len(aliases)}, bit-exact", flush=True)

if __name__ == "__main__":
    main()