Loading...
Loading...
Debug silent corruption when a MAX model loads, compiles, serves, and generates tokens but output disagrees with a reference implementation. Use whenever parity debugging stalls on scalar taps, the model returns gibberish or wrong greedy tokens, logit cosine is high but argmax differs, or generation is coherent then diverges — during an architecture port, a quantization bring-up, a multi-GPU conversion, or after a MAX upgrade. Triggers on "parity failure", "silent corruption", "logits match but tokens diverge", "top-1 mismatch", "greedy divergence", and "model serves but generates garbage". Not for crashes on load or pre-serve scaffolding (use import-model). Mandates reference-vs-MAX tensor-dump comparators first, verify fixes numerically before recompiling, and serve-vs-pipeline bisect when dumps match but text diverges.
npx skill4agent add modular/skills debug-modelops.printimport-modelimport-model| File | Read when |
|---|---|
| comparator-build.md | Building HF/MAX dumpers and the comparator |
| comparator-output-patterns.md | Interpreting comparator output, false cliffs, token-0 invariant |
| agent-workflow.md | Dispatching parallel investigation agents |
| stacked-failures.md | A fix helped but verification still fails |
max.nn.hooks.PrintHookmodel.generate(...)pixi run python -c "
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
tok = AutoTokenizer.from_pretrained('<repo>')
model = AutoModelForCausalLM.from_pretrained('<repo>', torch_dtype=torch.bfloat16, device_map='auto')
text = tok.apply_chat_template([{'role':'user','content':'Hello!'}], tokenize=False, add_generation_prompt=True)
out = model.generate(**tok(text, return_tensors='pt').to(model.device), max_new_tokens=32, do_sample=False)
print(tok.decode(out[0]))
"Guard: validate the dumpers before trusting them. Run both dumpers on a model MAX already serves correctly (any registered Llama works). Expect cos ≈ 0.999 at every layer, identicalon both sides, andprompt_tokens.npycos = 1.0. Anything less means the dumpers are broken — fix them before reading anything into a comparison on your port.post_embed
hidden_statesattention_maskcos_per_token = [cos(h[t], m[t]) for t in range(h.shape[0])]
cos_per_dim = [cos(h[:,d], m[:,d]) for d in range(h.shape[1])]max_diff| Check | Pass | Fail → |
|---|---|---|
| Teacher-forced dump @ K | cos ≥ 0.99, argmax matches | Steps 1 to 5 (graph bug) |
| Incremental pipeline decode @ K | token K matches HF | Decode-state bug (KV, conv cache) |
| Serve vs pipeline @ K | match | Harness bug (tokenizer, chat template, token recovery) |