Download code/scripts/ckpt_convert.py from viai957/align: direct link, hf CLI and curl.
- Browser
- Download file 5.67 kB
-
https://huggingface.co/viai957/align/resolve/main/code/scripts/ckpt_convert.py
- Command line
-
hf download hf://viai957/align/code/scripts/ckpt_convert.py
-
curl -L -o ckpt_convert.py https://huggingface.co/viai957/align/resolve/main/code/scripts/ckpt_convert.py
5.67 kB
| #!/usr/bin/env python | |
| """Sharded checkpoint tools: inspect, verify, export to HF safetensors, reshard. | |
| python scripts/ckpt_convert.py inspect RUN/checkpoints/micro_N | |
| python scripts/ckpt_convert.py verify RUN/checkpoints/micro_N | |
| python scripts/ckpt_convert.py export RUN/checkpoints/micro_N OUT_DIR | |
| python scripts/ckpt_convert.py reshard RUN/checkpoints/micro_N NEW_DIR --world-size 2 | |
| CPU only, no GPU, no process group, no heavy-job lock: `export` is a streaming | |
| byte copy (peak RSS ~ a few MiB plus the Python runtime, independent of model | |
| size) and `reshard` re-cuts the weights the same way. The ONE part that holds | |
| real memory is the optimizer re-cut inside `reshard` (one NEW rank's optimizer | |
| state, e.g. 12 B/param/new_ws for fp32-master AdamW); it prints that estimate | |
| and refuses to start when the box cannot afford it (CLAUDE.md: 45 GB | |
| MemAvailable floor for anything heavy, no swap). | |
| WHY THIS EXISTS. `train.checkpoint_format = "sharded"` writes per-rank files | |
| that eval, the rollout weight sync and external tools cannot read. `export` | |
| turns one into exactly the directory the gathered writer would have produced | |
| (byte-identical; tested by file hash) without ever holding the model in RAM -- | |
| the gathered path's rank-0 gather measured +28 GB RSS on OLMoE (docs/ | |
| MEASUREMENTS.md, Track M). `reshard` is the ONLY way to resume at a different | |
| world size: resume refuses it, because sharded weights are rank-local row | |
| slices and silently loading them under another partitioning trains plausibly | |
| on scrambled weights. | |
| Exit status: 0 ok, 1 refused/corrupt (message on stderr), 2 usage. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import sys | |
| os.environ.setdefault("CUDA_VISIBLE_DEVICES", "") # never touch a GPU from a converter | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from align.train import sharded_ckpt as sc # noqa: E402 | |
| def _mem_available_gib() -> float: | |
| with open("/proc/meminfo") as f: | |
| for line in f: | |
| if line.startswith("MemAvailable:"): | |
| return int(line.split()[1]) / 2**20 | |
| return 0.0 | |
| def _optimizer_bytes(src: str, ws: int) -> int: | |
| return sum(os.path.getsize(os.path.join(src, f"optimizer_rank{r}.pt")) for r in range(ws) if os.path.exists(os.path.join(src, f"optimizer_rank{r}.pt"))) | |
| def cmd_inspect(a) -> int: | |
| m = sc.read_manifest(a.src) | |
| total = sum(f["bytes"] for f in m["files"].values()) | |
| print( | |
| json.dumps( | |
| { | |
| "world_size": m["world_size"], | |
| "tensors": len(m["tensors"]), | |
| "weight_bytes": total, | |
| "export_family": (m.get("export") or {}).get("family"), | |
| "resharded_from": m.get("resharded_from"), | |
| "problem": sc.sharded_problem(a.src, lambda p: None), | |
| }, | |
| indent=1, | |
| ) | |
| ) | |
| return 0 | |
| def cmd_verify(a) -> int: | |
| m = sc.read_manifest(a.src) | |
| bad = [] | |
| for r in range(m["world_size"]): | |
| p = sc.verify_rank_file(a.src, r) | |
| print(f"rank {r}: {'OK' if p is None else p}") | |
| if p: | |
| bad.append(p) | |
| return 1 if bad else 0 | |
| def cmd_export(a) -> int: | |
| from align.utils.guard import assert_paths_off_root | |
| assert_paths_off_root([os.path.dirname(os.path.abspath(a.dst))]) | |
| res = sc.export_hf(a.src, a.dst, verify=not a.no_verify) | |
| print(json.dumps({**res, "peak_rss_kb": sc.peak_rss_kb()})) | |
| return 0 | |
| def cmd_reshard(a) -> int: | |
| from align.utils.guard import assert_paths_off_root | |
| assert_paths_off_root([os.path.dirname(os.path.abspath(a.dst))]) | |
| m = sc.read_manifest(a.src) | |
| if not a.drop_optimizer: | |
| est = _optimizer_bytes(a.src, m["world_size"]) / max(1, a.world_size) / 2**30 | |
| avail = _mem_available_gib() | |
| print(f"optimizer re-cut peak ~ {est:.1f} GiB; MemAvailable {avail:.0f} GiB", file=sys.stderr) | |
| if not a.allow_large and avail < est + 45: | |
| print( | |
| f"refusing: need MemAvailable >= {est + 45:.0f} GiB (estimate + the 45 GiB box floor) and have " | |
| f"{avail:.0f}. Wait for the box, or pass --allow-large if you accept the risk, or --drop-optimizer " | |
| "(fresh moments; NOT lossless).", | |
| file=sys.stderr, | |
| ) | |
| return 1 | |
| res = sc.reshard(a.src, a.dst, a.world_size, drop_optimizer=a.drop_optimizer) | |
| print(json.dumps({**res, "peak_rss_kb": sc.peak_rss_kb()})) | |
| return 0 | |
| def main(argv=None) -> int: | |
| ap = argparse.ArgumentParser(description=__doc__.split("\n")[0]) | |
| sub = ap.add_subparsers(dest="cmd", required=True) | |
| for name in ("inspect", "verify"): | |
| p = sub.add_parser(name) | |
| p.add_argument("src") | |
| p = sub.add_parser("export") | |
| p.add_argument("src") | |
| p.add_argument("dst") | |
| p.add_argument("--no-verify", action="store_true", help="skip the per-file sha256 pass (not recommended)") | |
| p = sub.add_parser("reshard") | |
| p.add_argument("src") | |
| p.add_argument("dst") | |
| p.add_argument("--world-size", type=int, required=True) | |
| p.add_argument("--drop-optimizer", action="store_true", help="omit optimizer files: resume starts FRESH moments (NOT lossless)") | |
| p.add_argument("--allow-large", action="store_true") | |
| a = ap.parse_args(argv) | |
| try: | |
| return {"inspect": cmd_inspect, "verify": cmd_verify, "export": cmd_export, "reshard": cmd_reshard}[a.cmd](a) | |
| except (sc.ShardedCheckpointError, FileExistsError, FileNotFoundError, KeyError) as e: | |
| print(f"{type(e).__name__}: {e}", file=sys.stderr) | |
| return 1 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |