sol-max-record / harness /scripts /check_checkpoint.py
simonycl's picture
Upload folder using huggingface_hub
9589849 verified
Raw
History Blame Contribute Delete
5.3 kB
#!/usr/bin/env python3
"""Validate an HF weight export and its terminal Prime-RL metrics."""
from __future__ import annotations
import argparse
import json
import math
from collections import defaultdict
from pathlib import Path
def merged_metrics(path: Path) -> dict[int, dict]:
by_step: dict[int, dict] = defaultdict(dict)
for line in path.read_text().splitlines():
if line.strip():
row = json.loads(line)
by_step[int(row["step"])].update(row)
return dict(by_step)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("model", type=Path)
parser.add_argument("metrics", type=Path)
parser.add_argument("--expected-step", type=int, required=True)
parser.add_argument(
"--scan-finite",
action="store_true",
help="load every floating tensor and reject NaN/Inf (slower)",
)
args = parser.parse_args()
model = args.model.resolve()
problems: list[str] = []
if not (model / "STABLE").is_file():
problems.append("missing STABLE marker")
index_path = model / "model.safetensors.index.json"
config_path = model / "config.json"
if not index_path.is_file():
problems.append("missing model.safetensors.index.json")
if not config_path.is_file():
problems.append("missing config.json")
if problems:
print(json.dumps({"healthy": False, "problems": problems}, indent=2))
raise SystemExit(1)
index = json.loads(index_path.read_text())
weight_map = index.get("weight_map") or {}
if not weight_map:
problems.append("empty weight_map")
expected_by_shard: dict[str, set[str]] = defaultdict(set)
for key, shard in weight_map.items():
expected_by_shard[str(shard)].add(str(key))
from safetensors import safe_open
tensor_count = 0
parameter_elements = 0
scanned_elements = 0
dtypes: dict[str, int] = defaultdict(int)
for shard_name, expected_keys in sorted(expected_by_shard.items()):
shard_path = model / shard_name
if not shard_path.is_file() or shard_path.stat().st_size == 0:
problems.append(f"missing or empty shard: {shard_name}")
continue
try:
with safe_open(shard_path, framework="pt", device="cpu") as handle:
actual_keys = set(handle.keys())
if actual_keys != expected_keys:
missing = len(expected_keys - actual_keys)
extra = len(actual_keys - expected_keys)
problems.append(
f"index/header mismatch in {shard_name}: missing={missing}, extra={extra}"
)
for key in sorted(actual_keys):
view = handle.get_slice(key)
shape = tuple(view.get_shape())
elements = math.prod(shape)
tensor_count += 1
parameter_elements += elements
dtypes[str(view.get_dtype())] += 1
if args.scan_finite:
tensor = handle.get_tensor(key)
if tensor.is_floating_point() or tensor.is_complex():
scanned_elements += tensor.numel()
if not tensor.isfinite().all().item():
problems.append(f"non-finite tensor: {key}")
except Exception as error: # a corrupt header/shard must fail the gate
problems.append(f"cannot load {shard_name}: {type(error).__name__}: {error}")
by_step = merged_metrics(args.metrics)
if args.expected_step not in by_step:
problems.append(f"metrics do not contain expected step {args.expected_step}")
terminal = {}
else:
terminal = by_step[args.expected_step]
for field in ("loss/mean", "optim/grad_norm", "optim/lr"):
value = terminal.get(field)
if not isinstance(value, (int, float)) or not math.isfinite(value):
problems.append(f"terminal metric {field} is not finite: {value!r}")
if terminal.get("loss/nan_count", 0) != 0:
problems.append(
f"terminal loss/nan_count is {terminal.get('loss/nan_count')!r}"
)
result = {
"healthy": not problems,
"model": str(model),
"expected_step": args.expected_step,
"stable_marker": (model / "STABLE").is_file(),
"shards": len(expected_by_shard),
"tensors": tensor_count,
"tensor_elements": parameter_elements,
"dtype_tensor_counts": dict(sorted(dtypes.items())),
"finite_scan_enabled": args.scan_finite,
"finite_scanned_elements": scanned_elements,
"terminal_metrics": {
key: terminal.get(key)
for key in (
"loss/mean",
"loss/nan_count",
"optim/grad_norm",
"optim/lr",
"perf/throughput",
"perf/peak_memory",
"progress/num_tokens",
"progress/num_samples",
)
},
"problems": problems,
}
print(json.dumps(result, indent=2))
if problems:
raise SystemExit(1)
if __name__ == "__main__":
main()