#!/usr/bin/env python3 """Compare a candidate checkpoint's MTP head against a known-good reference head. WHY THIS EXISTS --------------- The expensive gate before quantizing a new Qwen3.8-27B candidate is "does its MTP head actually work?" — presence is not acceptance, and a dead head silently costs the entire +18% decode case. Measuring acceptance for real needs the model resident in VRAM; at bf16 that is ~56 GB, which on a full 97.9 GB card means downing a SECOND seat, not just `gen`. This sidesteps that for the common case. Most community abliterations never touch `mtp.*` at all: the `Qwen3_5ForConditionalGeneration` wrapper class does not load the MTP head, so PEFT merges, Heretic runs, and llm-compressor passes all leave it exactly as it came from the base. If a candidate's 15 `mtp.*` tensors are numerically identical to a head we have already measured in production, its MTP is that head — and the acceptance test is redundant. Reference to compare against: `/tank/aimodels/qwen38-27b-uncensored-bf16` (`model-mtp.safetensors`). Per its PROVENANCE that head was grafted verbatim from the base, and it measures **47.7% acceptance** on the live gen seat through our exact mixed-quant pipeline. That makes it a known-good baseline, not a guess. READ THE RESULT HONESTLY ------------------------ - **IDENTICAL** -> the candidate carries the pristine base head. The bf16 acceptance gate buys nothing; go to quant and verify acceptance on the quantized build inside the freed `gen` budget (~22 GB, no second seat). - **DIFFERENT** -> something edited the head. That is NOT automatically bad (an abliteration that deliberately includes `mtp.*` is a legitimate design — see hotdogs/Qwen3.8-27B-abliterated, which edits 2 mtp tensors on purpose), but it means the head is no longer one we have measured. Run the real acceptance gate. - **MISSING** -> the head was dropped. Known failure mode; needs a graft. CPU only. Reads just the shard(s) holding `mtp.*` — no full model load. """ import argparse import hashlib import json import sys from pathlib import Path from safetensors import safe_open def load_mtp(model_dir: Path) -> dict: """Return {tensor_name: tensor} for every mtp.* tensor, reading only the shards that actually hold them.""" index = model_dir / "model.safetensors.index.json" if index.exists(): weight_map = json.loads(index.read_text())["weight_map"] names = [k for k in weight_map if k.startswith("mtp")] shards = sorted({weight_map[n] for n in names}) else: # unsharded checkpoint shards = ["model.safetensors"] names = None out = {} for shard in shards: path = model_dir / shard if not path.exists(): raise SystemExit(f"missing shard: {path}") with safe_open(path, framework="pt") as f: for key in f.keys(): if key.startswith("mtp"): out[key] = f.get_tensor(key) if names is not None and len(out) != len(names): print(f" warning: index listed {len(names)} mtp tensors, read {len(out)}", file=sys.stderr) return out def digest(tensor) -> str: """SHA-256 over the raw tensor bytes. Dtype-sensitive by design — a head stored at a different precision is not the same head for our purposes. Goes through a uint8 reinterpret rather than `.numpy()`: numpy has no bfloat16, and these checkpoints are bf16, so the direct route raises `TypeError: Got unsupported ScalarType BFloat16`. """ import torch flat = tensor.contiguous().flatten() return hashlib.sha256(flat.view(torch.uint8).numpy().tobytes()).hexdigest() def main() -> int: ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("candidate", type=Path, help="candidate model dir") ap.add_argument("reference", type=Path, help="known-good reference model dir (e.g. qwen38-27b-uncensored-bf16)") args = ap.parse_args() cand = load_mtp(args.candidate) ref = load_mtp(args.reference) print(f"candidate : {args.candidate} ({len(cand)} mtp tensors)") print(f"reference : {args.reference} ({len(ref)} mtp tensors)") print() if not cand: print("VERDICT: MISSING — candidate has no mtp.* tensors. Needs a graft.") return 2 only_cand = sorted(set(cand) - set(ref)) only_ref = sorted(set(ref) - set(cand)) if only_cand or only_ref: print(" tensor-name mismatch:") for n in only_cand: print(f" only in candidate: {n}") for n in only_ref: print(f" only in reference: {n}") print() differing = [] for name in sorted(set(cand) & set(ref)): c, r = cand[name], ref[name] if c.dtype != r.dtype or c.shape != r.shape: differing.append((name, f"dtype/shape {c.dtype}{tuple(c.shape)} vs " f"{r.dtype}{tuple(r.shape)}")) continue if digest(c) != digest(r): # quantify it — a tiny delta is a different story from a rewritten head delta = (c.float() - r.float()).abs().max().item() denom = r.float().abs().max().item() or 1.0 differing.append((name, f"max|Δ| = {delta:.6g} (rel {delta / denom:.3%})")) for name, why in differing: print(f" DIFFERS {name}: {why}") print() if not differing and not only_cand and not only_ref: print("VERDICT: IDENTICAL — candidate carries the reference MTP head verbatim.") print(" The bf16 acceptance gate is redundant: this head is already measured") print(" at 47.7% acceptance in production through our mixed-quant pipeline.") print(" Proceed to quant; verify acceptance on the quantized build.") return 0 print(f"VERDICT: DIFFERENT — {len(differing)} of {len(cand)} mtp tensors diverge.") print(" The head is not one we have measured. Run the real bf16 acceptance gate") print(" (~56 GB resident) before spending quant GPU time.") return 1 if __name__ == "__main__": raise SystemExit(main())