Skip to content

Commit 14bccfa

Browse files
kmbandyclaude
andcommitted
ml8: deterministic calibration (default-on) — fixes ~0.6 PPL run-to-run nondeterminism
The GPU Hessian forward had unforced reduction order; GPTQ's sequential error-feedback amplified the low-bit noise into different discrete weight assignments run-to-run (~0.6 PPL spread — swamping the ±0.05 levers). Same recipe + same data gave 151/188 differing blobs; only the deterministic .fp8 tier matched. Y_SNR stayed flat (~20.87) so it never surfaced. Fix (calibrate_ml8_paged.py, default-on, ML8_NONDETERMINISTIC=1 to opt out): - env before torch import: CUBLAS_WORKSPACE_CONFIG=:4096:8, HIPBLASLT_DETERMINISTIC=1 - torch.manual_seed/cuda.manual_seed_all, use_deterministic_algorithms(True), cudnn.deterministic, tf32 off, fp16/bf16 reduced-precision reduction off Verified gfx1201: warn_only diag flagged zero ops without deterministic impls; two 1-layer runs -> 6/6 blobs bit-identical; full-scale gate (--max-layers 12, incl self_attn SDPA + fla fp32 SSM + hipBLAS) -> 21/21 bit-identical. Adds diag_determinism.py (warn_only enumerator). Unlocks: (1) zero run-to-run noise -> faithful-acts/weights deltas now cleanly measurable; (2) --rotation-seed becomes a reproducible best-of-N lever (select on held-out, report on wiki). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 43ee012 commit 14bccfa

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

scripts/calibration/calibrate_ml8_paged.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,41 @@
5050

5151
print = functools.partial(print, flush=True)
5252

53+
# ── Deterministic calibration — env half (default-on; MUST precede `import torch`) ───
54+
# ml8 GPTQ calibration was nondeterministic: the GPU Hessian forward used unforced reduction
55+
# order and GPTQ's sequential error-feedback amplified the low-bit noise into DIFFERENT weight
56+
# assignments run-to-run (~0.6 PPL spread — swamps the ±0.05 levers). The fix is pinning the GEMM
57+
# workspace (here) + enabling deterministic algorithms (after the torch import). Verified
58+
# bit-identical across back-to-back runs on gfx1201. Opt out with ML8_NONDETERMINISTIC=1.
59+
_DETERMINISTIC = os.environ.get("ML8_NONDETERMINISTIC") != "1"
60+
if _DETERMINISTIC:
61+
os.environ.setdefault("CUBLAS_WORKSPACE_CONFIG", ":4096:8") # required for deterministic cuBLAS/hipBLAS GEMM
62+
os.environ.setdefault("HIPBLASLT_DETERMINISTIC", "1")
63+
5364
import numpy as np
5465
import torch
5566
from torch import nn
5667
from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
5768

69+
# ── Deterministic calibration — torch half (see env half above) ───
70+
if _DETERMINISTIC:
71+
torch.manual_seed(0)
72+
if torch.cuda.is_available():
73+
torch.cuda.manual_seed_all(0)
74+
torch.use_deterministic_algorithms(True)
75+
torch.backends.cudnn.deterministic = True
76+
torch.backends.cudnn.benchmark = False
77+
for _attr in ("allow_tf32",):
78+
try: setattr(torch.backends.cudnn, _attr, False)
79+
except AttributeError: pass
80+
for _attr, _val in (("allow_tf32", False),
81+
("allow_fp16_reduced_precision_reduction", False),
82+
("allow_bf16_reduced_precision_reduction", False)):
83+
try: setattr(torch.backends.cuda.matmul, _attr, _val)
84+
except AttributeError: pass
85+
print("[determinism] use_deterministic_algorithms(True) + seeded + pinned GEMM "
86+
"(set ML8_NONDETERMINISTIC=1 to disable)")
87+
5888
# Re-use existing calibration helpers (compute_hessian / gptq_quantize_linear / eval_ppl_wikitext / etc.)
5989
sys.path.insert(0, str(Path(__file__).parent))
6090
from calibrate_ml8 import ( # noqa: E402
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python3
2+
"""Determinism diagnostic for the ml8 GPTQ calibration path.
3+
4+
Enables torch.use_deterministic_algorithms(True, warn_only=True) + seeding + GEMM-determinism
5+
env BEFORE running the real calibrate_ml8_paged driver, so PyTorch logs every op in our path
6+
that has no deterministic implementation. Pass-through args go to the driver (use a tiny
7+
--max-layers / --n-samples for a fast run). Run with PYTHONWARNINGS=always to see every warning.
8+
9+
Read the result by grepping stderr for: "does not have a deterministic implementation"
10+
"""
11+
import os
12+
import sys
13+
import warnings
14+
15+
# GEMM / reduction determinism env (must be set before the first CUDA/HIP context).
16+
os.environ.setdefault("CUBLAS_WORKSPACE_CONFIG", ":4096:8")
17+
os.environ.setdefault("HIPBLASLT_DETERMINISTIC", "1") # hipBLASLt analog; harmless if ignored
18+
os.environ.setdefault("MIOPEN_DEBUG_CONV_IMPLICIT_GEMM", "0")
19+
20+
import torch # noqa: E402
21+
22+
torch.manual_seed(0)
23+
if torch.cuda.is_available():
24+
torch.cuda.manual_seed_all(0)
25+
try:
26+
torch.backends.cuda.matmul.allow_tf32 = False
27+
torch.backends.cudnn.allow_tf32 = False
28+
torch.backends.cudnn.deterministic = True
29+
torch.backends.cudnn.benchmark = False
30+
torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = False
31+
torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = False
32+
except Exception as e:
33+
print(f"[diag] backend flag note: {e}", file=sys.stderr)
34+
35+
# warn_only=True ⇒ nondeterministic ops WARN instead of raising, so we enumerate them all.
36+
torch.use_deterministic_algorithms(True, warn_only=True)
37+
warnings.simplefilter("always")
38+
print("[diag] determinism enabled (warn_only=True); seed=0; running calibrate driver...",
39+
file=sys.stderr, flush=True)
40+
41+
# Hand off to the real driver in THIS process (the flags above persist on the torch singleton).
42+
_here = os.path.dirname(os.path.abspath(__file__))
43+
sys.argv = [os.path.join(_here, "calibrate_ml8_paged.py")] + sys.argv[1:]
44+
import runpy # noqa: E402
45+
runpy.run_path(sys.argv[0], run_name="__main__")

0 commit comments

Comments
 (0)