Upload App_fsdp.py
Browse files- App_fsdp.py +591 -252
App_fsdp.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
Orion Flagship Nano — T2.1 single-file base-pretraining trainer.
|
| 4 |
|
| 5 |
Target hardware:
|
| 6 |
-
NVIDIA RTX PRO 6000 Blackwell
|
| 7 |
|
| 8 |
T2.1 changes over the original Mini prototype:
|
| 9 |
- ~135M-parameter Nano configuration.
|
|
@@ -31,7 +31,7 @@ Safety / resumability:
|
|
| 31 |
|
| 32 |
This is a new architecture/run. It intentionally does NOT load Mini-T2 weights.
|
| 33 |
|
| 34 |
-
Distributed launch (one node with eight
|
| 35 |
torchrun --standalone --nproc_per_node=8 App_fsdp.py
|
| 36 |
|
| 37 |
The configured micro-batch is global (112 sequences); each rank processes 14
|
|
@@ -60,7 +60,7 @@ HF_REPO_ID = os.environ.get(
|
|
| 60 |
"Project-Prism/Orion-Flagship-Nano-T2.1",
|
| 61 |
)
|
| 62 |
HF_PRIVATE = True
|
| 63 |
-
HF_TOKEN = os.environ.get("HF_TOKEN", "
|
| 64 |
|
| 65 |
# 50B-token BASE corpus built by orion_nano_base_pretokenizer_v1.py.
|
| 66 |
DATA_REPO_ID = "smilyai-large-team/nano-orion-ultra"
|
|
@@ -80,9 +80,11 @@ START_FROM_SCRATCH = False
|
|
| 80 |
ALLOW_FRESH_START_WITH_EXISTING_REMOTE = False
|
| 81 |
ALLOW_REMOTE_POINTER_ROLLBACK = False
|
| 82 |
|
| 83 |
-
# Large physical batch on the 96GB
|
|
|
|
| 84 |
SEQUENCE_LENGTH = 1024
|
| 85 |
GLOBAL_MICRO_BATCH_SIZE = 112
|
|
|
|
| 86 |
# Set to the per-rank value after torch.distributed is initialized. Keeping
|
| 87 |
# the global value explicit prevents accidentally multiplying the update batch
|
| 88 |
# by eight when launching with torchrun.
|
|
@@ -123,6 +125,17 @@ RUN_KERNEL_TESTS = True
|
|
| 123 |
GROUPED_EXPERT_GEMM_ACTIVE = False
|
| 124 |
AUTOCAST_CACHE = True
|
| 125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
CPU_THREADS = 8
|
| 127 |
PREFETCH_BATCHES = 4
|
| 128 |
DATA_SEED = 42
|
|
@@ -167,6 +180,17 @@ HF_API_RETRY_MAX_SECONDS = 120.0
|
|
| 167 |
MAX_CONSECUTIVE_NONFINITE = 10
|
| 168 |
AUTO_INSTALL_MISSING = True
|
| 169 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
# ===========================================================================
|
| 171 |
# Internal runtime configuration — no external configuration needed
|
| 172 |
# ===========================================================================
|
|
@@ -295,6 +319,82 @@ def rank0_print(*args, **kwargs):
|
|
| 295 |
print(*args, **kwargs)
|
| 296 |
|
| 297 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
def dist_barrier():
|
| 299 |
if dist.is_initialized():
|
| 300 |
dist.barrier()
|
|
@@ -780,13 +880,22 @@ class FusedCrossEntropy(torch.autograd.Function):
|
|
| 780 |
# ===========================================================================
|
| 781 |
|
| 782 |
|
| 783 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 784 |
# Test odd tails, routing collisions, cast-backward rounding, ignored
|
| 785 |
# labels, large logits, and the actual vocabulary-sized reduction.
|
| 786 |
if dtypes is None:
|
| 787 |
dtypes = (torch.float32, torch.bfloat16)
|
| 788 |
for dtype in dtypes:
|
| 789 |
for d in (127, 2048):
|
|
|
|
| 790 |
n, k = 17, 2
|
| 791 |
x = torch.randn(n, d, device=device, requires_grad=True)
|
| 792 |
order = torch.randperm(n * k, device=device)
|
|
@@ -800,6 +909,7 @@ def test_optimized_kernels(device="cuda", dtypes=None):
|
|
| 800 |
torch.testing.assert_close(dx, dxr, rtol=0, atol=0)
|
| 801 |
|
| 802 |
for v in (127, 32768):
|
|
|
|
| 803 |
logits = (torch.randn(9, v, device=device) * 4 + 80).to(dtype)
|
| 804 |
logits.requires_grad_(True)
|
| 805 |
targets = torch.randint(v, (9,), device=device)
|
|
@@ -815,79 +925,92 @@ def test_optimized_kernels(device="cuda", dtypes=None):
|
|
| 815 |
atol=2e-7)
|
| 816 |
|
| 817 |
|
| 818 |
-
def test_kernels():
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
| 823 |
-
|
| 824 |
-
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
y = TritonRMSFunction.apply(x, 1e-6)
|
| 828 |
-
dx = torch.autograd.grad(y, x, g)[0]
|
| 829 |
-
|
| 830 |
-
xr = x.detach().float().requires_grad_(True)
|
| 831 |
-
yr = xr * torch.rsqrt(xr.square().mean(-1, keepdim=True) + 1e-6)
|
| 832 |
-
dxr = torch.autograd.grad(yr, xr, g.float())[0]
|
| 833 |
-
|
| 834 |
-
torch.testing.assert_close(y.float(), yr, rtol=tol, atol=tol)
|
| 835 |
-
torch.testing.assert_close(dx.float(), dxr, rtol=tol, atol=tol)
|
| 836 |
-
|
| 837 |
-
a = torch.randn(
|
| 838 |
-
4096, device="cuda", dtype=dtype, requires_grad=True
|
| 839 |
-
)
|
| 840 |
-
b = torch.randn_like(a, requires_grad=True)
|
| 841 |
-
g = torch.randn_like(a)
|
| 842 |
|
| 843 |
-
|
| 844 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 845 |
|
| 846 |
-
|
| 847 |
-
|
| 848 |
-
|
| 849 |
-
dar, dbr = torch.autograd.grad(zr, (ar, br), g.float())
|
| 850 |
|
| 851 |
-
|
| 852 |
-
|
| 853 |
-
torch.testing.assert_close(db.float(), dbr, rtol=tol, atol=tol)
|
| 854 |
|
| 855 |
-
|
| 856 |
-
|
| 857 |
-
|
| 858 |
-
|
| 859 |
-
|
| 860 |
-
|
| 861 |
-
|
| 862 |
-
|
| 863 |
-
|
| 864 |
-
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
|
| 868 |
-
|
| 869 |
-
|
| 870 |
-
|
| 871 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 872 |
|
| 873 |
-
|
| 874 |
-
|
| 875 |
-
|
| 876 |
-
|
| 877 |
-
|
| 878 |
-
|
| 879 |
|
| 880 |
-
|
| 881 |
-
|
| 882 |
-
|
| 883 |
-
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
|
| 890 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 891 |
print("All enabled Triton forward/backward checks passed.", flush=True)
|
| 892 |
|
| 893 |
|
|
@@ -1018,13 +1141,18 @@ def attention_expand(self, x):
|
|
| 1018 |
Attention.forward = attention_gqa
|
| 1019 |
|
| 1020 |
|
| 1021 |
-
def select_attention(cfg):
|
|
|
|
|
|
|
| 1022 |
hd = cfg["d_model"] // cfg["n_heads"]
|
|
|
|
| 1023 |
|
| 1024 |
def probe(native):
|
| 1025 |
-
|
|
|
|
|
|
|
| 1026 |
device="cuda", dtype=torch.bfloat16, requires_grad=True)
|
| 1027 |
-
k = torch.randn(1, cfg["n_kv_heads"],
|
| 1028 |
device="cuda", dtype=torch.bfloat16, requires_grad=True)
|
| 1029 |
v = torch.randn_like(k, requires_grad=True)
|
| 1030 |
with sdpa_kernel(SDPBackend.FLASH_ATTENTION):
|
|
@@ -1042,14 +1170,22 @@ def select_attention(cfg):
|
|
| 1042 |
)
|
| 1043 |
out.float().square().mean().backward()
|
| 1044 |
torch.cuda.synchronize()
|
|
|
|
| 1045 |
|
| 1046 |
try:
|
| 1047 |
probe(True)
|
| 1048 |
eager = attention_gqa
|
| 1049 |
print("Native Flash GQA forward/backward supported.")
|
| 1050 |
-
except Exception as
|
| 1051 |
-
print("Native GQA probe failed:",
|
| 1052 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1053 |
eager = attention_expand
|
| 1054 |
print("Using explicit KV expansion with Flash attention.")
|
| 1055 |
|
|
@@ -1059,11 +1195,14 @@ def select_attention(cfg):
|
|
| 1059 |
|
| 1060 |
trial = x = out = None
|
| 1061 |
try:
|
|
|
|
| 1062 |
with torch.device("cuda"):
|
| 1063 |
trial = Attention(cfg)
|
| 1064 |
compiled = torch.compile(eager, fullgraph=True, dynamic=False)
|
|
|
|
|
|
|
| 1065 |
x = torch.randn(
|
| 1066 |
-
|
| 1067 |
device="cuda", dtype=torch.float32, requires_grad=True,
|
| 1068 |
)
|
| 1069 |
with torch.autocast("cuda", dtype=torch.bfloat16,
|
|
@@ -1071,8 +1210,14 @@ def select_attention(cfg):
|
|
| 1071 |
out = compiled(trial, x)
|
| 1072 |
out.float().square().mean().backward()
|
| 1073 |
torch.cuda.synchronize()
|
|
|
|
| 1074 |
Attention.forward = compiled
|
| 1075 |
print("Compiled attention startup forward/backward passed.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1076 |
except Exception as exc:
|
| 1077 |
Attention.forward = eager
|
| 1078 |
print("Attention compilation failed; using eager attention:", exc)
|
|
@@ -1513,45 +1658,6 @@ class OrionFlagshipNanoT21(nn.Module):
|
|
| 1513 |
"banks": banks,
|
| 1514 |
}
|
| 1515 |
|
| 1516 |
-
@torch.no_grad()
|
| 1517 |
-
def run_telemetry_probe(self, input_ids, labels):
|
| 1518 |
-
was_training = self.training
|
| 1519 |
-
self.eval()
|
| 1520 |
-
self.reset_telemetry()
|
| 1521 |
-
self.set_telemetry(True)
|
| 1522 |
-
try:
|
| 1523 |
-
with torch.autocast(
|
| 1524 |
-
"cuda", dtype=torch.bfloat16, cache_enabled=AUTOCAST_CACHE
|
| 1525 |
-
):
|
| 1526 |
-
ce, auxiliary = self(input_ids, labels)
|
| 1527 |
-
stats = self.telemetry_summary()
|
| 1528 |
-
stats["probe_ce"] = float(ce)
|
| 1529 |
-
stats["probe_aux"] = float(auxiliary)
|
| 1530 |
-
return stats
|
| 1531 |
-
finally:
|
| 1532 |
-
self.set_telemetry(False)
|
| 1533 |
-
if was_training:
|
| 1534 |
-
self.train()
|
| 1535 |
-
|
| 1536 |
-
@torch.no_grad()
|
| 1537 |
-
def run_ablation_probe(self, input_ids, labels):
|
| 1538 |
-
was_training = self.training
|
| 1539 |
-
self.eval()
|
| 1540 |
-
try:
|
| 1541 |
-
results = {}
|
| 1542 |
-
with torch.autocast(
|
| 1543 |
-
"cuda", dtype=torch.bfloat16, cache_enabled=AUTOCAST_CACHE
|
| 1544 |
-
):
|
| 1545 |
-
base, _ = self(input_ids, labels)
|
| 1546 |
-
results["full"] = float(base)
|
| 1547 |
-
for name in ("vault", "state", "procedure", "deliberation"):
|
| 1548 |
-
ce, _ = self(input_ids, labels, ablate={name})
|
| 1549 |
-
results[f"no_{name}"] = float(ce)
|
| 1550 |
-
return results
|
| 1551 |
-
finally:
|
| 1552 |
-
if was_training:
|
| 1553 |
-
self.train()
|
| 1554 |
-
|
| 1555 |
def initialize_weights(self):
|
| 1556 |
for module in self.modules():
|
| 1557 |
if isinstance(module, (nn.Linear, nn.Embedding)):
|
|
@@ -1688,6 +1794,48 @@ class OrionFlagshipNanoT21(nn.Module):
|
|
| 1688 |
return ce, auxiliary / max(1, executed_stages)
|
| 1689 |
|
| 1690 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1691 |
def parameter_counts(cfg):
|
| 1692 |
"""Exact meta count + approximate per-stage active compute proxy."""
|
| 1693 |
with torch.device("meta"):
|
|
@@ -1711,37 +1859,35 @@ def parameter_counts(cfg):
|
|
| 1711 |
|
| 1712 |
|
| 1713 |
def gradient_health_summary(model):
|
| 1714 |
-
|
| 1715 |
-
|
| 1716 |
-
|
| 1717 |
-
|
| 1718 |
-
|
| 1719 |
-
|
| 1720 |
-
}
|
| 1721 |
for name, p in model.named_parameters():
|
| 1722 |
if p.grad is None:
|
| 1723 |
continue
|
|
|
|
|
|
|
|
|
|
| 1724 |
g = p.grad.detach().float()
|
| 1725 |
value = g.square().sum()
|
| 1726 |
if name.startswith("procedure_banks") or name.startswith("route_state_proj") \
|
| 1727 |
or name.startswith("stage_embedding") or name.startswith("cycle_embedding"):
|
| 1728 |
-
|
| 1729 |
elif name.startswith("vault"):
|
| 1730 |
-
|
| 1731 |
elif name.startswith("working") or name.startswith("state_seed"):
|
| 1732 |
-
|
| 1733 |
elif name.startswith("embedding"):
|
| 1734 |
-
|
| 1735 |
else:
|
| 1736 |
-
|
| 1737 |
-
|
| 1738 |
-
|
| 1739 |
-
|
| 1740 |
-
|
| 1741 |
-
result[key] = float(torch.stack(values).sum().sqrt())
|
| 1742 |
-
else:
|
| 1743 |
-
result[key] = 0.0
|
| 1744 |
-
return result
|
| 1745 |
|
| 1746 |
|
| 1747 |
def health_warnings(stats, grad_stats, step):
|
|
@@ -1788,7 +1934,10 @@ def run_startup_model_probe(model, vocab_size):
|
|
| 1788 |
with torch.autocast("cuda", dtype=torch.bfloat16, cache_enabled=AUTOCAST_CACHE):
|
| 1789 |
ce, aux = model(x, y)
|
| 1790 |
loss = ce + aux
|
| 1791 |
-
|
|
|
|
|
|
|
|
|
|
| 1792 |
raise RuntimeError("Startup model probe produced nonfinite loss.")
|
| 1793 |
loss.backward()
|
| 1794 |
grads = gradient_health_summary(model)
|
|
@@ -1796,7 +1945,7 @@ def run_startup_model_probe(model, vocab_size):
|
|
| 1796 |
if not math.isfinite(grads[key]) or grads[key] <= 0.0:
|
| 1797 |
raise RuntimeError(f"Startup probe: invalid {key} gradient norm {grads[key]}")
|
| 1798 |
model.zero_grad(set_to_none=True)
|
| 1799 |
-
stats =
|
| 1800 |
print(
|
| 1801 |
f"Startup probe PASS: ce={float(ce):.4f} aux={float(aux):.4f} "
|
| 1802 |
f"grads={grads} vault_gate={stats['vault_gate']:.3f} "
|
|
@@ -2278,10 +2427,24 @@ class PrefetchedStream:
|
|
| 2278 |
def distributed_batch(stream):
|
| 2279 |
"""Fetch one global batch on rank zero and give every rank a disjoint slice."""
|
| 2280 |
local_batch = GLOBAL_MICRO_BATCH_SIZE // WORLD_SIZE
|
|
|
|
|
|
|
| 2281 |
if RANK == 0:
|
| 2282 |
-
|
| 2283 |
-
|
| 2284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2285 |
packed = torch.empty(
|
| 2286 |
(2, GLOBAL_MICRO_BATCH_SIZE, SEQUENCE_LENGTH),
|
| 2287 |
device=DEVICE,
|
|
@@ -2405,13 +2568,6 @@ def save_local_checkpoint(
|
|
| 2405 |
model, optimizer, tokenizer, progress, data_state
|
| 2406 |
):
|
| 2407 |
cfg = model.module.cfg if isinstance(model, FSDP) else model.cfg
|
| 2408 |
-
root = WORK / "checkpoints"
|
| 2409 |
-
root.mkdir(parents=True, exist_ok=True)
|
| 2410 |
-
|
| 2411 |
-
name = f"step-{progress['step']:09d}-{uuid.uuid4().hex[:8]}"
|
| 2412 |
-
temporary = root / (".building-" + name)
|
| 2413 |
-
final = root / name
|
| 2414 |
-
|
| 2415 |
# FULL_STATE_DICT is collective: every rank must enter this context, but
|
| 2416 |
# rank zero is the only writer. This keeps the existing portable checkpoint
|
| 2417 |
# layout while making it valid for sharded parameters.
|
|
@@ -2421,9 +2577,31 @@ def save_local_checkpoint(
|
|
| 2421 |
state_cfg, optim_cfg):
|
| 2422 |
full_model = model.state_dict()
|
| 2423 |
full_optimizer = FSDP.optim_state_dict(model, optimizer)
|
| 2424 |
-
|
| 2425 |
-
|
| 2426 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2427 |
required = nested_bytes(full_model) + nested_bytes(full_optimizer) + 2 * 1024**3
|
| 2428 |
free = shutil.disk_usage(root).free
|
| 2429 |
if free < required:
|
|
@@ -2494,7 +2672,6 @@ def save_local_checkpoint(
|
|
| 2494 |
"step": progress["step"],
|
| 2495 |
"tokens": progress["tokens"],
|
| 2496 |
})
|
| 2497 |
-
dist_barrier()
|
| 2498 |
return final
|
| 2499 |
|
| 2500 |
except BaseException:
|
|
@@ -2511,6 +2688,34 @@ def prune_local_checkpoints(keep):
|
|
| 2511 |
shutil.rmtree(path)
|
| 2512 |
|
| 2513 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2514 |
def restore_checkpoint(path, model, optimizer):
|
| 2515 |
manifest = json.loads((path / "manifest.json").read_text())
|
| 2516 |
if manifest.get("run_id") != RUN_ID:
|
|
@@ -2521,16 +2726,19 @@ def restore_checkpoint(path, model, optimizer):
|
|
| 2521 |
|
| 2522 |
if manifest.get("checkpoint_format") != "fsdp_full_state":
|
| 2523 |
raise ValueError("This FSDP trainer only accepts format-3 checkpoints.")
|
| 2524 |
-
model_state =
|
| 2525 |
-
path
|
| 2526 |
-
|
| 2527 |
)
|
| 2528 |
-
optimizer_state =
|
| 2529 |
-
path
|
| 2530 |
-
map_location="cpu", weights_only=False,
|
| 2531 |
)
|
| 2532 |
-
|
| 2533 |
-
|
|
|
|
|
|
|
|
|
|
| 2534 |
with FSDP.state_dict_type(model, StateDictType.FULL_STATE_DICT,
|
| 2535 |
state_cfg, optim_cfg):
|
| 2536 |
model.load_state_dict(model_state)
|
|
@@ -3219,15 +3427,22 @@ def main():
|
|
| 3219 |
dist.init_process_group("nccl")
|
| 3220 |
torch.cuda.set_device(LOCAL_RANK)
|
| 3221 |
if GLOBAL_MICRO_BATCH_SIZE % WORLD_SIZE:
|
| 3222 |
-
raise ValueError("GLOBAL_MICRO_BATCH_SIZE must
|
| 3223 |
MICRO_BATCH_SIZE = GLOBAL_MICRO_BATCH_SIZE // WORLD_SIZE
|
| 3224 |
GRAD_ACCUM_STEPS = TOKENS_PER_UPDATE // (
|
| 3225 |
GLOBAL_MICRO_BATCH_SIZE * SEQUENCE_LENGTH
|
| 3226 |
)
|
| 3227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3228 |
STOP_REQUESTED = False
|
| 3229 |
|
| 3230 |
-
if not HF_TOKEN:
|
| 3231 |
raise ValueError(
|
| 3232 |
"Set HF_TOKEN to a Hugging Face token with read access to the data "
|
| 3233 |
"and write access to the checkpoint repo. Do not paste it into this file."
|
|
@@ -3237,24 +3452,37 @@ def main():
|
|
| 3237 |
if not torch.cuda.is_bf16_supported():
|
| 3238 |
raise RuntimeError("BF16 GPU support required.")
|
| 3239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3240 |
process_start = time.monotonic()
|
| 3241 |
hard_deadline = process_start + SESSION_HOURS * 3600
|
| 3242 |
nominal_train_deadline = process_start + MAX_TRAIN_HOURS * 3600
|
| 3243 |
WORK.mkdir(parents=True, exist_ok=True)
|
| 3244 |
|
| 3245 |
-
gpu = torch.cuda.get_device_properties(LOCAL_RANK)
|
| 3246 |
-
capability = torch.cuda.get_device_capability(LOCAL_RANK)
|
| 3247 |
rank0_print(
|
| 3248 |
-
f"GPU: {
|
| 3249 |
f"SM: {capability[0]}.{capability[1]}\n"
|
| 3250 |
f"PyTorch: {torch.__version__} | CUDA: {torch.version.cuda}\n"
|
| 3251 |
-
f"Batch: {MICRO_BATCH_SIZE} × {GRAD_ACCUM_STEPS} × "
|
| 3252 |
f"{SEQUENCE_LENGTH} = {TOKENS_PER_UPDATE:,} tokens/update",
|
| 3253 |
flush=True,
|
| 3254 |
)
|
| 3255 |
-
if
|
| 3256 |
-
rank0_print("Warning: this profile was tuned for SM 120 Blackwell.")
|
| 3257 |
-
if gpu.total_memory / 1024**3 < 85:
|
| 3258 |
rank0_print("Warning: defaults target a 96GB-class GPU.")
|
| 3259 |
|
| 3260 |
random.seed(42)
|
|
@@ -3271,18 +3499,27 @@ def main():
|
|
| 3271 |
old_handlers = {}
|
| 3272 |
|
| 3273 |
try:
|
| 3274 |
-
|
| 3275 |
-
|
| 3276 |
-
|
| 3277 |
-
|
| 3278 |
-
|
| 3279 |
-
|
| 3280 |
-
|
|
|
|
| 3281 |
else:
|
| 3282 |
-
|
| 3283 |
-
|
| 3284 |
-
|
| 3285 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3286 |
dist_barrier()
|
| 3287 |
|
| 3288 |
tokenizer_source = (
|
|
@@ -3290,7 +3527,7 @@ def main():
|
|
| 3290 |
if resume_path is not None else TOKENIZER_NAME
|
| 3291 |
)
|
| 3292 |
tokenizer = AutoTokenizer.from_pretrained(
|
| 3293 |
-
tokenizer_source, token=HF_TOKEN, use_fast=True
|
| 3294 |
)
|
| 3295 |
if tokenizer.eos_token_id is None:
|
| 3296 |
raise ValueError("Tokenizer requires an EOS token.")
|
|
@@ -3314,43 +3551,44 @@ def main():
|
|
| 3314 |
f"Nano parameter guard failed: expected ~135M, got {total:,}."
|
| 3315 |
)
|
| 3316 |
|
| 3317 |
-
|
| 3318 |
-
|
| 3319 |
-
|
| 3320 |
-
|
| 3321 |
-
|
| 3322 |
-
|
| 3323 |
-
|
| 3324 |
-
|
| 3325 |
-
|
| 3326 |
-
|
| 3327 |
-
|
| 3328 |
-
|
| 3329 |
-
|
| 3330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3331 |
|
| 3332 |
-
model =
|
| 3333 |
-
model,
|
| 3334 |
-
sharding_strategy=ShardingStrategy.FULL_SHARD,
|
| 3335 |
-
use_orig_params=True,
|
| 3336 |
-
device_id=DEVICE,
|
| 3337 |
-
sync_module_states=False,
|
| 3338 |
-
limit_all_gathers=True,
|
| 3339 |
-
)
|
| 3340 |
|
| 3341 |
# Probe before creating/consuming a real data stream. This catches wiring,
|
| 3342 |
# gradient, NaN, or memory failures before the 50B run starts.
|
| 3343 |
if resume_path is None:
|
| 3344 |
run_startup_model_probe(model, len(tokenizer))
|
| 3345 |
|
| 3346 |
-
|
| 3347 |
-
|
| 3348 |
-
|
| 3349 |
-
|
| 3350 |
-
|
| 3351 |
-
|
| 3352 |
-
|
| 3353 |
-
|
|
|
|
|
|
|
|
|
|
| 3354 |
|
| 3355 |
progress = {
|
| 3356 |
"step": 0,
|
|
@@ -3359,6 +3597,86 @@ def main():
|
|
| 3359 |
"skipped_updates": 0,
|
| 3360 |
"consecutive_skips": 0,
|
| 3361 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3362 |
saved_data = None
|
| 3363 |
if resume_path is not None:
|
| 3364 |
rank0_print("Restoring Nano model + optimizer...", flush=True)
|
|
@@ -3453,28 +3771,45 @@ def main():
|
|
| 3453 |
def save_checkpoint(reason):
|
| 3454 |
nonlocal latest_local, last_save_time, dirty, checkpoint_size_estimate
|
| 3455 |
if RANK == 0:
|
| 3456 |
-
|
| 3457 |
-
|
| 3458 |
-
|
| 3459 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3460 |
optimizer.zero_grad(set_to_none=True)
|
| 3461 |
start = time.monotonic()
|
| 3462 |
rank0_print(f"\nSaving ({reason}) at step {progress['step']:,}...", flush=True)
|
| 3463 |
path = save_local_checkpoint(
|
| 3464 |
model, optimizer, tokenizer, progress, committed_data
|
| 3465 |
)
|
|
|
|
| 3466 |
if RANK == 0:
|
| 3467 |
-
|
| 3468 |
-
|
| 3469 |
-
|
| 3470 |
-
|
| 3471 |
-
|
| 3472 |
-
|
| 3473 |
-
|
| 3474 |
-
|
| 3475 |
-
|
| 3476 |
-
|
| 3477 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3478 |
|
| 3479 |
metrics = torch.zeros(2, device=DEVICE, dtype=torch.float32)
|
| 3480 |
telemetry_probe = None
|
|
@@ -3521,8 +3856,8 @@ def main():
|
|
| 3521 |
loss = (ce + auxiliary) / GRAD_ACCUM_STEPS
|
| 3522 |
except torch.cuda.OutOfMemoryError as exc:
|
| 3523 |
raise RuntimeError(
|
| 3524 |
-
"Nano turbo profile OOM. Set
|
| 3525 |
-
"(
|
| 3526 |
"before changing the architecture."
|
| 3527 |
) from exc
|
| 3528 |
|
|
@@ -3611,7 +3946,7 @@ def main():
|
|
| 3611 |
and progress["step"] % T2_TELEMETRY_EVERY_STEPS == 0
|
| 3612 |
):
|
| 3613 |
probe_x, probe_y = telemetry_probe
|
| 3614 |
-
t2 =
|
| 3615 |
bank_text = " ".join(
|
| 3616 |
f"B{b['bank']}:H={b['router_entropy']:.2f},"
|
| 3617 |
f"load={100*b['min_load']:.1f}-{100*b['max_load']:.1f}%,"
|
|
@@ -3647,7 +3982,7 @@ def main():
|
|
| 3647 |
):
|
| 3648 |
probe_x, probe_y = telemetry_probe
|
| 3649 |
n = min(ABLATION_PROBE_BATCH, probe_x.shape[0])
|
| 3650 |
-
a =
|
| 3651 |
rank0_print(
|
| 3652 |
"ABLATE "
|
| 3653 |
f"step={progress['step']:,} full={a['full']:.4f} "
|
|
@@ -3658,28 +3993,30 @@ def main():
|
|
| 3658 |
flush=True,
|
| 3659 |
)
|
| 3660 |
|
| 3661 |
-
|
| 3662 |
-
|
| 3663 |
-
|
| 3664 |
-
|
| 3665 |
-
|
| 3666 |
-
|
| 3667 |
-
|
| 3668 |
-
|
| 3669 |
-
|
| 3670 |
-
|
| 3671 |
-
|
| 3672 |
-
|
| 3673 |
-
|
| 3674 |
-
|
| 3675 |
-
|
|
|
|
|
|
|
| 3676 |
save_checkpoint("periodic")
|
| 3677 |
metrics.zero_()
|
| 3678 |
log_updates = 0
|
| 3679 |
log_tokens = 0
|
| 3680 |
log_start = time.monotonic()
|
| 3681 |
|
| 3682 |
-
if dirty:
|
| 3683 |
save_checkpoint("final")
|
| 3684 |
elif RANK == 0 and latest_local is not None:
|
| 3685 |
pointer = hub.pointer
|
|
@@ -3713,7 +4050,7 @@ def main():
|
|
| 3713 |
"Data pipeline failed. Preserving the last complete optimizer boundary.",
|
| 3714 |
flush=True,
|
| 3715 |
)
|
| 3716 |
-
if dirty and not in_optimizer_step:
|
| 3717 |
try:
|
| 3718 |
save_checkpoint("data pipeline recovery")
|
| 3719 |
except Exception as exc:
|
|
@@ -3726,8 +4063,9 @@ def main():
|
|
| 3726 |
torch.cuda.empty_cache()
|
| 3727 |
rank0_print(
|
| 3728 |
"\nCUDA OOM. Existing checkpoints remain valid.\n"
|
| 3729 |
-
"Set
|
| 3730 |
-
"
|
|
|
|
| 3731 |
"No checkpoint is taken from a partial optimizer update.",
|
| 3732 |
flush=True,
|
| 3733 |
)
|
|
@@ -3751,7 +4089,8 @@ def main():
|
|
| 3751 |
if hub is not None:
|
| 3752 |
rank0_print("Waiting for any pending checkpoint upload...", flush=True)
|
| 3753 |
hub.close()
|
| 3754 |
-
|
|
|
|
| 3755 |
if dist.is_initialized():
|
| 3756 |
dist.destroy_process_group()
|
| 3757 |
|
|
|
|
| 3 |
Orion Flagship Nano — T2.1 single-file base-pretraining trainer.
|
| 4 |
|
| 5 |
Target hardware:
|
| 6 |
+
NVIDIA H200 (SM 90) or RTX PRO 6000 Blackwell (SM 120), ~96 GB VRAM.
|
| 7 |
|
| 8 |
T2.1 changes over the original Mini prototype:
|
| 9 |
- ~135M-parameter Nano configuration.
|
|
|
|
| 31 |
|
| 32 |
This is a new architecture/run. It intentionally does NOT load Mini-T2 weights.
|
| 33 |
|
| 34 |
+
Distributed launch (one node with eight supported GPUs):
|
| 35 |
torchrun --standalone --nproc_per_node=8 App_fsdp.py
|
| 36 |
|
| 37 |
The configured micro-batch is global (112 sequences); each rank processes 14
|
|
|
|
| 60 |
"Project-Prism/Orion-Flagship-Nano-T2.1",
|
| 61 |
)
|
| 62 |
HF_PRIVATE = True
|
| 63 |
+
HF_TOKEN = os.environ.get("HF_TOKEN", "").strip()
|
| 64 |
|
| 65 |
# 50B-token BASE corpus built by orion_nano_base_pretokenizer_v1.py.
|
| 66 |
DATA_REPO_ID = "smilyai-large-team/nano-orion-ultra"
|
|
|
|
| 80 |
ALLOW_FRESH_START_WITH_EXISTING_REMOTE = False
|
| 81 |
ALLOW_REMOTE_POINTER_ROLLBACK = False
|
| 82 |
|
| 83 |
+
# Large physical batch on the target ~96GB-class GPUs; base pretraining stays at
|
| 84 |
+
# 1K context.
|
| 85 |
SEQUENCE_LENGTH = 1024
|
| 86 |
GLOBAL_MICRO_BATCH_SIZE = 112
|
| 87 |
+
# For OOMs, set this to 56: 7 sequences/GPU × 8 GPUs × 2 accumulation steps.
|
| 88 |
# Set to the per-rank value after torch.distributed is initialized. Keeping
|
| 89 |
# the global value explicit prevents accidentally multiplying the update batch
|
| 90 |
# by eight when launching with torchrun.
|
|
|
|
| 125 |
GROUPED_EXPERT_GEMM_ACTIVE = False
|
| 126 |
AUTOCAST_CACHE = True
|
| 127 |
|
| 128 |
+
# The fused kernels and attention path are intentionally gated to the two
|
| 129 |
+
# architectures exercised by this run. Do not silently run on a nearby
|
| 130 |
+
# compute capability: Triton/PTX support and BF16/Flash behavior can differ.
|
| 131 |
+
SUPPORTED_COMPUTE_CAPABILITIES = ((9, 0), (12, 0))
|
| 132 |
+
HARDWARE_PROFILE_NAMES = {
|
| 133 |
+
(9, 0): "Hopper / H200",
|
| 134 |
+
(12, 0): "Blackwell",
|
| 135 |
+
}
|
| 136 |
+
KERNEL_TEST_TIMEOUT_SECONDS = 120.0
|
| 137 |
+
ATTENTION_PROBE_TIMEOUT_SECONDS = 180.0
|
| 138 |
+
|
| 139 |
CPU_THREADS = 8
|
| 140 |
PREFETCH_BATCHES = 4
|
| 141 |
DATA_SEED = 42
|
|
|
|
| 180 |
MAX_CONSECUTIVE_NONFINITE = 10
|
| 181 |
AUTO_INSTALL_MISSING = True
|
| 182 |
|
| 183 |
+
# Explicitly bounded validation path. Production training is unchanged unless
|
| 184 |
+
# this is enabled in the launch environment, for example:
|
| 185 |
+
# ORION_NANO_PREFLIGHT_SMOKE=1 torchrun --standalone --nproc_per_node=8 App_fsdp.py
|
| 186 |
+
PREFLIGHT_SMOKE = os.environ.get("ORION_NANO_PREFLIGHT_SMOKE", "").strip().lower() in {
|
| 187 |
+
"1", "true", "yes", "on",
|
| 188 |
+
}
|
| 189 |
+
if PREFLIGHT_SMOKE:
|
| 190 |
+
# Keep the synthetic checkpoint and its metadata out of the production
|
| 191 |
+
# resume directory when both modes share ORION_NANO_WORK_DIR.
|
| 192 |
+
WORK_DIR = os.path.join(WORK_DIR, "preflight-smoke")
|
| 193 |
+
|
| 194 |
# ===========================================================================
|
| 195 |
# Internal runtime configuration — no external configuration needed
|
| 196 |
# ===========================================================================
|
|
|
|
| 319 |
print(*args, **kwargs)
|
| 320 |
|
| 321 |
|
| 322 |
+
def _format_capability(capability):
|
| 323 |
+
return f"SM {capability[0]}.{capability[1]}"
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def _hardware_inventory():
|
| 327 |
+
"""Return visible GPU details without making a profile assumption."""
|
| 328 |
+
inventory = []
|
| 329 |
+
for index in range(torch.cuda.device_count()):
|
| 330 |
+
properties = torch.cuda.get_device_properties(index)
|
| 331 |
+
capability = tuple(torch.cuda.get_device_capability(index))
|
| 332 |
+
inventory.append({
|
| 333 |
+
"index": index,
|
| 334 |
+
"name": properties.name,
|
| 335 |
+
"memory_gib": properties.total_memory / 1024**3,
|
| 336 |
+
"capability": capability,
|
| 337 |
+
})
|
| 338 |
+
return inventory
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
def _format_hardware_inventory(inventory):
|
| 342 |
+
return "\n".join(
|
| 343 |
+
f" GPU {item['index']}: {item['name']} | "
|
| 344 |
+
f"{_format_capability(item['capability'])} | "
|
| 345 |
+
f"{item['memory_gib']:.1f} GiB"
|
| 346 |
+
for item in inventory
|
| 347 |
+
) or " (no visible CUDA GPUs)"
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def validate_hardware():
|
| 351 |
+
"""Validate the visible rank devices before any real data is touched."""
|
| 352 |
+
if not torch.cuda.is_available():
|
| 353 |
+
raise RuntimeError("CUDA GPU required; torch.cuda.is_available() is false.")
|
| 354 |
+
|
| 355 |
+
inventory = _hardware_inventory()
|
| 356 |
+
visible_count = len(inventory)
|
| 357 |
+
if visible_count < WORLD_SIZE:
|
| 358 |
+
raise RuntimeError(
|
| 359 |
+
f"GPU count mismatch: torchrun requested {WORLD_SIZE} ranks but "
|
| 360 |
+
f"only {visible_count} CUDA GPU(s) are visible.\n"
|
| 361 |
+
f"Visible devices:\n{_format_hardware_inventory(inventory)}"
|
| 362 |
+
)
|
| 363 |
+
if LOCAL_RANK >= visible_count:
|
| 364 |
+
raise RuntimeError(
|
| 365 |
+
f"LOCAL_RANK={LOCAL_RANK} is outside the {visible_count} visible "
|
| 366 |
+
"CUDA GPU(s).\n"
|
| 367 |
+
f"Visible devices:\n{_format_hardware_inventory(inventory)}"
|
| 368 |
+
)
|
| 369 |
+
|
| 370 |
+
participating = inventory[:WORLD_SIZE]
|
| 371 |
+
unsupported = [
|
| 372 |
+
item for item in participating
|
| 373 |
+
if item["capability"] not in SUPPORTED_COMPUTE_CAPABILITIES
|
| 374 |
+
]
|
| 375 |
+
if unsupported:
|
| 376 |
+
supported = ", ".join(
|
| 377 |
+
_format_capability(capability)
|
| 378 |
+
for capability in SUPPORTED_COMPUTE_CAPABILITIES
|
| 379 |
+
)
|
| 380 |
+
raise RuntimeError(
|
| 381 |
+
"Unsupported CUDA compute capability for this run. Supported "
|
| 382 |
+
f"capabilities are {supported}; every participating rank must use "
|
| 383 |
+
"one of them.\n"
|
| 384 |
+
f"Visible devices:\n{_format_hardware_inventory(inventory)}"
|
| 385 |
+
)
|
| 386 |
+
|
| 387 |
+
capabilities = {item["capability"] for item in participating}
|
| 388 |
+
if len(capabilities) != 1:
|
| 389 |
+
raise RuntimeError(
|
| 390 |
+
"Mixed GPU compute capabilities are not supported for one FSDP run; "
|
| 391 |
+
"use homogeneous H200/SM 90 or Blackwell/SM 120 ranks.\n"
|
| 392 |
+
f"Visible devices:\n{_format_hardware_inventory(inventory)}"
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
return inventory[LOCAL_RANK], inventory
|
| 396 |
+
|
| 397 |
+
|
| 398 |
def dist_barrier():
|
| 399 |
if dist.is_initialized():
|
| 400 |
dist.barrier()
|
|
|
|
| 880 |
# ===========================================================================
|
| 881 |
|
| 882 |
|
| 883 |
+
def _check_preflight_deadline(deadline, label):
|
| 884 |
+
if deadline is not None and time.monotonic() >= deadline:
|
| 885 |
+
raise TimeoutError(
|
| 886 |
+
f"{label} exceeded its preflight time limit; "
|
| 887 |
+
"reduce the smoke-test scope or inspect the CUDA/Triton setup."
|
| 888 |
+
)
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
def test_optimized_kernels(device="cuda", dtypes=None, deadline=None):
|
| 892 |
# Test odd tails, routing collisions, cast-backward rounding, ignored
|
| 893 |
# labels, large logits, and the actual vocabulary-sized reduction.
|
| 894 |
if dtypes is None:
|
| 895 |
dtypes = (torch.float32, torch.bfloat16)
|
| 896 |
for dtype in dtypes:
|
| 897 |
for d in (127, 2048):
|
| 898 |
+
_check_preflight_deadline(deadline, "Triton kernel preflight")
|
| 899 |
n, k = 17, 2
|
| 900 |
x = torch.randn(n, d, device=device, requires_grad=True)
|
| 901 |
order = torch.randperm(n * k, device=device)
|
|
|
|
| 909 |
torch.testing.assert_close(dx, dxr, rtol=0, atol=0)
|
| 910 |
|
| 911 |
for v in (127, 32768):
|
| 912 |
+
_check_preflight_deadline(deadline, "Triton kernel preflight")
|
| 913 |
logits = (torch.randn(9, v, device=device) * 4 + 80).to(dtype)
|
| 914 |
logits.requires_grad_(True)
|
| 915 |
targets = torch.randint(v, (9,), device=device)
|
|
|
|
| 925 |
atol=2e-7)
|
| 926 |
|
| 927 |
|
| 928 |
+
def test_kernels(timeout_seconds=None):
|
| 929 |
+
if timeout_seconds is None:
|
| 930 |
+
timeout_seconds = KERNEL_TEST_TIMEOUT_SECONDS
|
| 931 |
+
deadline = time.monotonic() + timeout_seconds
|
| 932 |
+
try:
|
| 933 |
+
test_optimized_kernels(deadline=deadline)
|
| 934 |
+
for dtype in (torch.float32, torch.bfloat16):
|
| 935 |
+
_check_preflight_deadline(deadline, "Triton kernel preflight")
|
| 936 |
+
tol = 0.04 if dtype == torch.bfloat16 else 3e-4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 937 |
|
| 938 |
+
x = torch.randn(
|
| 939 |
+
8, 2048, device="cuda", dtype=dtype, requires_grad=True
|
| 940 |
+
)
|
| 941 |
+
g = torch.randn_like(x)
|
| 942 |
+
y = TritonRMSFunction.apply(x, 1e-6)
|
| 943 |
+
dx = torch.autograd.grad(y, x, g)[0]
|
| 944 |
|
| 945 |
+
xr = x.detach().float().requires_grad_(True)
|
| 946 |
+
yr = xr * torch.rsqrt(xr.square().mean(-1, keepdim=True) + 1e-6)
|
| 947 |
+
dxr = torch.autograd.grad(yr, xr, g.float())[0]
|
|
|
|
| 948 |
|
| 949 |
+
torch.testing.assert_close(y.float(), yr, rtol=tol, atol=tol)
|
| 950 |
+
torch.testing.assert_close(dx.float(), dxr, rtol=tol, atol=tol)
|
|
|
|
| 951 |
|
| 952 |
+
a = torch.randn(
|
| 953 |
+
4096, device="cuda", dtype=dtype, requires_grad=True
|
| 954 |
+
)
|
| 955 |
+
b = torch.randn_like(a, requires_grad=True)
|
| 956 |
+
g = torch.randn_like(a)
|
| 957 |
+
|
| 958 |
+
z = TritonSwiGLUFunction.apply(a, b)
|
| 959 |
+
da, db = torch.autograd.grad(z, (a, b), g)
|
| 960 |
+
|
| 961 |
+
ar = a.detach().float().requires_grad_(True)
|
| 962 |
+
br = b.detach().float().requires_grad_(True)
|
| 963 |
+
zr = F.silu(ar) * br
|
| 964 |
+
dar, dbr = torch.autograd.grad(zr, (ar, br), g.float())
|
| 965 |
+
|
| 966 |
+
torch.testing.assert_close(z.float(), zr, rtol=tol, atol=tol)
|
| 967 |
+
torch.testing.assert_close(da.float(), dar, rtol=tol, atol=tol)
|
| 968 |
+
torch.testing.assert_close(db.float(), dbr, rtol=tol, atol=tol)
|
| 969 |
+
|
| 970 |
+
if USE_FUSED_MOE_COMBINE:
|
| 971 |
+
for d in (127, 2048):
|
| 972 |
+
_check_preflight_deadline(deadline, "Triton kernel preflight")
|
| 973 |
+
n, k = 129, 2
|
| 974 |
+
grouped = torch.randn(
|
| 975 |
+
n * k, d, device="cuda",
|
| 976 |
+
dtype=dtype, requires_grad=True,
|
| 977 |
+
)
|
| 978 |
+
weights = torch.softmax(
|
| 979 |
+
torch.randn(n, k, device="cuda"), dim=-1
|
| 980 |
+
).detach().requires_grad_(True)
|
| 981 |
+
inverse = torch.randperm(n * k, device="cuda")
|
| 982 |
+
upstream = torch.randn(n, d, device="cuda")
|
| 983 |
+
|
| 984 |
+
actual = FusedMoECombine.apply(grouped, weights, inverse)
|
| 985 |
+
dg, dw = torch.autograd.grad(
|
| 986 |
+
actual, (grouped, weights), upstream
|
| 987 |
+
)
|
| 988 |
|
| 989 |
+
gr = grouped.detach().clone().requires_grad_(True)
|
| 990 |
+
wr = weights.detach().clone().requires_grad_(True)
|
| 991 |
+
reference = combine_reference(gr, wr, inverse)
|
| 992 |
+
dgr, dwr = torch.autograd.grad(
|
| 993 |
+
reference, (gr, wr), upstream
|
| 994 |
+
)
|
| 995 |
|
| 996 |
+
torch.testing.assert_close(
|
| 997 |
+
actual, reference, rtol=1e-5, atol=1e-5
|
| 998 |
+
)
|
| 999 |
+
torch.testing.assert_close(
|
| 1000 |
+
dg.float(), dgr.float(), rtol=1e-5, atol=1e-5
|
| 1001 |
+
)
|
| 1002 |
+
torch.testing.assert_close(
|
| 1003 |
+
dw, dwr, rtol=3e-4, atol=3e-4
|
| 1004 |
+
)
|
| 1005 |
|
| 1006 |
+
_check_preflight_deadline(deadline, "Triton kernel preflight")
|
| 1007 |
+
torch.cuda.synchronize()
|
| 1008 |
+
_check_preflight_deadline(deadline, "Triton kernel preflight")
|
| 1009 |
+
except Exception as exc:
|
| 1010 |
+
raise RuntimeError(
|
| 1011 |
+
"Triton kernel preflight failed on the active GPU. Check the "
|
| 1012 |
+
"CUDA/Triton/PyTorch versions and the reported assertion or timeout."
|
| 1013 |
+
) from exc
|
| 1014 |
print("All enabled Triton forward/backward checks passed.", flush=True)
|
| 1015 |
|
| 1016 |
|
|
|
|
| 1141 |
Attention.forward = attention_gqa
|
| 1142 |
|
| 1143 |
|
| 1144 |
+
def select_attention(cfg, timeout_seconds=None):
|
| 1145 |
+
if timeout_seconds is None:
|
| 1146 |
+
timeout_seconds = ATTENTION_PROBE_TIMEOUT_SECONDS
|
| 1147 |
hd = cfg["d_model"] // cfg["n_heads"]
|
| 1148 |
+
deadline = time.monotonic() + timeout_seconds
|
| 1149 |
|
| 1150 |
def probe(native):
|
| 1151 |
+
_check_preflight_deadline(deadline, "Flash attention preflight")
|
| 1152 |
+
probe_tokens = max(1, min(256, cfg["sequence_length"]))
|
| 1153 |
+
q = torch.randn(1, cfg["n_heads"], probe_tokens, hd,
|
| 1154 |
device="cuda", dtype=torch.bfloat16, requires_grad=True)
|
| 1155 |
+
k = torch.randn(1, cfg["n_kv_heads"], probe_tokens, hd,
|
| 1156 |
device="cuda", dtype=torch.bfloat16, requires_grad=True)
|
| 1157 |
v = torch.randn_like(k, requires_grad=True)
|
| 1158 |
with sdpa_kernel(SDPBackend.FLASH_ATTENTION):
|
|
|
|
| 1170 |
)
|
| 1171 |
out.float().square().mean().backward()
|
| 1172 |
torch.cuda.synchronize()
|
| 1173 |
+
_check_preflight_deadline(deadline, "Flash attention preflight")
|
| 1174 |
|
| 1175 |
try:
|
| 1176 |
probe(True)
|
| 1177 |
eager = attention_gqa
|
| 1178 |
print("Native Flash GQA forward/backward supported.")
|
| 1179 |
+
except Exception as native_exc:
|
| 1180 |
+
print("Native GQA probe failed; trying explicit KV expansion:", native_exc)
|
| 1181 |
+
try:
|
| 1182 |
+
probe(False)
|
| 1183 |
+
except Exception as fallback_exc:
|
| 1184 |
+
raise RuntimeError(
|
| 1185 |
+
"Flash attention preflight failed for both native GQA and "
|
| 1186 |
+
"explicit KV expansion. Check GPU capability, BF16 support, "
|
| 1187 |
+
"and the PyTorch CUDA attention backend."
|
| 1188 |
+
) from fallback_exc
|
| 1189 |
eager = attention_expand
|
| 1190 |
print("Using explicit KV expansion with Flash attention.")
|
| 1191 |
|
|
|
|
| 1195 |
|
| 1196 |
trial = x = out = None
|
| 1197 |
try:
|
| 1198 |
+
_check_preflight_deadline(deadline, "Attention compilation preflight")
|
| 1199 |
with torch.device("cuda"):
|
| 1200 |
trial = Attention(cfg)
|
| 1201 |
compiled = torch.compile(eager, fullgraph=True, dynamic=False)
|
| 1202 |
+
probe_batch = max(1, min(MICRO_BATCH_SIZE, 2))
|
| 1203 |
+
probe_tokens = max(1, min(SEQUENCE_LENGTH, 256))
|
| 1204 |
x = torch.randn(
|
| 1205 |
+
probe_batch, probe_tokens, cfg["d_model"],
|
| 1206 |
device="cuda", dtype=torch.float32, requires_grad=True,
|
| 1207 |
)
|
| 1208 |
with torch.autocast("cuda", dtype=torch.bfloat16,
|
|
|
|
| 1210 |
out = compiled(trial, x)
|
| 1211 |
out.float().square().mean().backward()
|
| 1212 |
torch.cuda.synchronize()
|
| 1213 |
+
_check_preflight_deadline(deadline, "Attention compilation preflight")
|
| 1214 |
Attention.forward = compiled
|
| 1215 |
print("Compiled attention startup forward/backward passed.")
|
| 1216 |
+
except TimeoutError as exc:
|
| 1217 |
+
raise RuntimeError(
|
| 1218 |
+
"Attention compilation preflight exceeded its time limit; "
|
| 1219 |
+
"inspect the CUDA/PyTorch compiler setup before training."
|
| 1220 |
+
) from exc
|
| 1221 |
except Exception as exc:
|
| 1222 |
Attention.forward = eager
|
| 1223 |
print("Attention compilation failed; using eager attention:", exc)
|
|
|
|
| 1658 |
"banks": banks,
|
| 1659 |
}
|
| 1660 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1661 |
def initialize_weights(self):
|
| 1662 |
for module in self.modules():
|
| 1663 |
if isinstance(module, (nn.Linear, nn.Embedding)):
|
|
|
|
| 1794 |
return ce, auxiliary / max(1, executed_stages)
|
| 1795 |
|
| 1796 |
|
| 1797 |
+
@torch.no_grad()
|
| 1798 |
+
def run_telemetry_probe(model, input_ids, labels):
|
| 1799 |
+
"""All ranks must enter; forwards must go through the FSDP wrapper."""
|
| 1800 |
+
module = model.module if isinstance(model, FSDP) else model
|
| 1801 |
+
was_training = model.training
|
| 1802 |
+
try:
|
| 1803 |
+
model.eval()
|
| 1804 |
+
module.reset_telemetry()
|
| 1805 |
+
module.set_telemetry(True)
|
| 1806 |
+
with torch.autocast(
|
| 1807 |
+
"cuda", dtype=torch.bfloat16, cache_enabled=AUTOCAST_CACHE
|
| 1808 |
+
):
|
| 1809 |
+
ce, auxiliary = model(input_ids, labels)
|
| 1810 |
+
stats = module.telemetry_summary()
|
| 1811 |
+
stats["probe_ce"] = float(ce)
|
| 1812 |
+
stats["probe_aux"] = float(auxiliary)
|
| 1813 |
+
return stats
|
| 1814 |
+
finally:
|
| 1815 |
+
module.set_telemetry(False)
|
| 1816 |
+
model.train(was_training)
|
| 1817 |
+
|
| 1818 |
+
|
| 1819 |
+
@torch.no_grad()
|
| 1820 |
+
def run_ablation_probe(model, input_ids, labels):
|
| 1821 |
+
"""Run each ablation through the wrapper so FSDP gathers its parameters."""
|
| 1822 |
+
was_training = model.training
|
| 1823 |
+
try:
|
| 1824 |
+
model.eval()
|
| 1825 |
+
results = {}
|
| 1826 |
+
with torch.autocast(
|
| 1827 |
+
"cuda", dtype=torch.bfloat16, cache_enabled=AUTOCAST_CACHE
|
| 1828 |
+
):
|
| 1829 |
+
base, _ = model(input_ids, labels)
|
| 1830 |
+
results["full"] = float(base)
|
| 1831 |
+
for name in ("vault", "state", "procedure", "deliberation"):
|
| 1832 |
+
ce, _ = model(input_ids, labels, ablate={name})
|
| 1833 |
+
results[f"no_{name}"] = float(ce)
|
| 1834 |
+
return results
|
| 1835 |
+
finally:
|
| 1836 |
+
model.train(was_training)
|
| 1837 |
+
|
| 1838 |
+
|
| 1839 |
def parameter_counts(cfg):
|
| 1840 |
"""Exact meta count + approximate per-stage active compute proxy."""
|
| 1841 |
with torch.device("meta"):
|
|
|
|
| 1859 |
|
| 1860 |
|
| 1861 |
def gradient_health_summary(model):
|
| 1862 |
+
# Original parameters expose only this rank's shard under FULL_SHARD.
|
| 1863 |
+
# Keep every group in the collective even when this rank has no gradients.
|
| 1864 |
+
groups = ("cortex", "procedure", "vault", "state", "embedding")
|
| 1865 |
+
first_parameter = next(model.parameters(), None)
|
| 1866 |
+
device = first_parameter.device if first_parameter is not None else DEVICE
|
| 1867 |
+
squared_norms = torch.zeros(len(groups), device=device, dtype=torch.float32)
|
|
|
|
| 1868 |
for name, p in model.named_parameters():
|
| 1869 |
if p.grad is None:
|
| 1870 |
continue
|
| 1871 |
+
name = ".".join(
|
| 1872 |
+
part for part in name.split(".") if part != "_fsdp_wrapped_module"
|
| 1873 |
+
)
|
| 1874 |
g = p.grad.detach().float()
|
| 1875 |
value = g.square().sum()
|
| 1876 |
if name.startswith("procedure_banks") or name.startswith("route_state_proj") \
|
| 1877 |
or name.startswith("stage_embedding") or name.startswith("cycle_embedding"):
|
| 1878 |
+
group = "procedure"
|
| 1879 |
elif name.startswith("vault"):
|
| 1880 |
+
group = "vault"
|
| 1881 |
elif name.startswith("working") or name.startswith("state_seed"):
|
| 1882 |
+
group = "state"
|
| 1883 |
elif name.startswith("embedding"):
|
| 1884 |
+
group = "embedding"
|
| 1885 |
else:
|
| 1886 |
+
group = "cortex"
|
| 1887 |
+
squared_norms[groups.index(group)] += value
|
| 1888 |
+
if dist.is_initialized():
|
| 1889 |
+
dist.all_reduce(squared_norms, op=dist.ReduceOp.SUM)
|
| 1890 |
+
return dict(zip(groups, squared_norms.sqrt().tolist()))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1891 |
|
| 1892 |
|
| 1893 |
def health_warnings(stats, grad_stats, step):
|
|
|
|
| 1934 |
with torch.autocast("cuda", dtype=torch.bfloat16, cache_enabled=AUTOCAST_CACHE):
|
| 1935 |
ce, aux = model(x, y)
|
| 1936 |
loss = ce + aux
|
| 1937 |
+
finite_loss = torch.isfinite(loss.detach()).to(dtype=torch.int32)
|
| 1938 |
+
if dist.is_initialized():
|
| 1939 |
+
dist.all_reduce(finite_loss, op=dist.ReduceOp.MIN)
|
| 1940 |
+
if not bool(finite_loss.item()):
|
| 1941 |
raise RuntimeError("Startup model probe produced nonfinite loss.")
|
| 1942 |
loss.backward()
|
| 1943 |
grads = gradient_health_summary(model)
|
|
|
|
| 1945 |
if not math.isfinite(grads[key]) or grads[key] <= 0.0:
|
| 1946 |
raise RuntimeError(f"Startup probe: invalid {key} gradient norm {grads[key]}")
|
| 1947 |
model.zero_grad(set_to_none=True)
|
| 1948 |
+
stats = run_telemetry_probe(model, x, y)
|
| 1949 |
print(
|
| 1950 |
f"Startup probe PASS: ce={float(ce):.4f} aux={float(aux):.4f} "
|
| 1951 |
f"grads={grads} vault_gate={stats['vault_gate']:.3f} "
|
|
|
|
| 2427 |
def distributed_batch(stream):
|
| 2428 |
"""Fetch one global batch on rank zero and give every rank a disjoint slice."""
|
| 2429 |
local_batch = GLOBAL_MICRO_BATCH_SIZE // WORLD_SIZE
|
| 2430 |
+
failure = None
|
| 2431 |
+
status = None
|
| 2432 |
if RANK == 0:
|
| 2433 |
+
try:
|
| 2434 |
+
x, y, cursor = stream.batch()
|
| 2435 |
+
packed = torch.stack((x, y), dim=0)
|
| 2436 |
+
status = "ok"
|
| 2437 |
+
except Exception as exc:
|
| 2438 |
+
failure = exc
|
| 2439 |
+
status = "oom" if isinstance(exc, torch.cuda.OutOfMemoryError) else "failed"
|
| 2440 |
+
# Never leave peers waiting for a batch when its producer has failed. All
|
| 2441 |
+
# ranks take the same recovery path, using the last committed data cursor.
|
| 2442 |
+
status = broadcast_object(status)
|
| 2443 |
+
if status == "oom":
|
| 2444 |
+
raise torch.cuda.OutOfMemoryError("Rank-zero batch preparation ran out of memory.") from failure
|
| 2445 |
+
if status != "ok":
|
| 2446 |
+
raise DataPipelineError("Rank-zero batch preparation failed.") from failure
|
| 2447 |
+
if RANK != 0:
|
| 2448 |
packed = torch.empty(
|
| 2449 |
(2, GLOBAL_MICRO_BATCH_SIZE, SEQUENCE_LENGTH),
|
| 2450 |
device=DEVICE,
|
|
|
|
| 2568 |
model, optimizer, tokenizer, progress, data_state
|
| 2569 |
):
|
| 2570 |
cfg = model.module.cfg if isinstance(model, FSDP) else model.cfg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2571 |
# FULL_STATE_DICT is collective: every rank must enter this context, but
|
| 2572 |
# rank zero is the only writer. This keeps the existing portable checkpoint
|
| 2573 |
# layout while making it valid for sharded parameters.
|
|
|
|
| 2577 |
state_cfg, optim_cfg):
|
| 2578 |
full_model = model.state_dict()
|
| 2579 |
full_optimizer = FSDP.optim_state_dict(model, optimizer)
|
| 2580 |
+
final = None
|
| 2581 |
+
if RANK == 0:
|
| 2582 |
+
try:
|
| 2583 |
+
final = _write_local_checkpoint(
|
| 2584 |
+
full_model, full_optimizer, tokenizer, progress, data_state, cfg
|
| 2585 |
+
)
|
| 2586 |
+
except BaseException as exc:
|
| 2587 |
+
# Peers wait for this outcome, including failures before file writing
|
| 2588 |
+
# (directory creation, disk-space checks, CUDA synchronization).
|
| 2589 |
+
broadcast_object(f"{type(exc).__name__}: {exc}")
|
| 2590 |
+
raise
|
| 2591 |
+
error = broadcast_object(None)
|
| 2592 |
+
if error is not None:
|
| 2593 |
+
raise RuntimeError(f"Rank-zero checkpoint save failed: {error}")
|
| 2594 |
+
return final
|
| 2595 |
+
|
| 2596 |
+
|
| 2597 |
+
def _write_local_checkpoint(
|
| 2598 |
+
full_model, full_optimizer, tokenizer, progress, data_state, cfg
|
| 2599 |
+
):
|
| 2600 |
+
root = WORK / "checkpoints"
|
| 2601 |
+
root.mkdir(parents=True, exist_ok=True)
|
| 2602 |
+
name = f"step-{progress['step']:09d}-{uuid.uuid4().hex[:8]}"
|
| 2603 |
+
temporary = root / (".building-" + name)
|
| 2604 |
+
final = root / name
|
| 2605 |
required = nested_bytes(full_model) + nested_bytes(full_optimizer) + 2 * 1024**3
|
| 2606 |
free = shutil.disk_usage(root).free
|
| 2607 |
if free < required:
|
|
|
|
| 2672 |
"step": progress["step"],
|
| 2673 |
"tokens": progress["tokens"],
|
| 2674 |
})
|
|
|
|
| 2675 |
return final
|
| 2676 |
|
| 2677 |
except BaseException:
|
|
|
|
| 2688 |
shutil.rmtree(path)
|
| 2689 |
|
| 2690 |
|
| 2691 |
+
def _load_checkpoint_shards(path, manifest, key, load_shard):
|
| 2692 |
+
filenames = manifest.get(key)
|
| 2693 |
+
if not isinstance(filenames, list) or not filenames:
|
| 2694 |
+
raise ValueError(f"Checkpoint manifest requires a non-empty {key} list.")
|
| 2695 |
+
merged = {}
|
| 2696 |
+
seen = set()
|
| 2697 |
+
for filename in filenames:
|
| 2698 |
+
if not isinstance(filename, str):
|
| 2699 |
+
raise ValueError(f"Invalid filename in checkpoint {key}: {filename!r}")
|
| 2700 |
+
filename = safe_relative_path(filename)
|
| 2701 |
+
if filename in seen:
|
| 2702 |
+
raise ValueError(f"Duplicate checkpoint shard in {key}: {filename}")
|
| 2703 |
+
seen.add(filename)
|
| 2704 |
+
shard = load_shard(path / filename)
|
| 2705 |
+
if not isinstance(shard, dict):
|
| 2706 |
+
raise ValueError(f"Checkpoint shard {filename} must contain a dictionary.")
|
| 2707 |
+
duplicates = merged.keys() & shard.keys()
|
| 2708 |
+
if duplicates:
|
| 2709 |
+
raise ValueError(
|
| 2710 |
+
f"Duplicate keys in checkpoint {key} shard {filename}: "
|
| 2711 |
+
f"{sorted(map(str, duplicates))}"
|
| 2712 |
+
)
|
| 2713 |
+
# shard_items splits top-level entries, including optimizer state and
|
| 2714 |
+
# param_groups; every listed shard contributes to the full dictionary.
|
| 2715 |
+
merged.update(shard)
|
| 2716 |
+
return merged
|
| 2717 |
+
|
| 2718 |
+
|
| 2719 |
def restore_checkpoint(path, model, optimizer):
|
| 2720 |
manifest = json.loads((path / "manifest.json").read_text())
|
| 2721 |
if manifest.get("run_id") != RUN_ID:
|
|
|
|
| 2726 |
|
| 2727 |
if manifest.get("checkpoint_format") != "fsdp_full_state":
|
| 2728 |
raise ValueError("This FSDP trainer only accepts format-3 checkpoints.")
|
| 2729 |
+
model_state = _load_checkpoint_shards(
|
| 2730 |
+
path, manifest, "model_shards",
|
| 2731 |
+
lambda shard: load_file(str(shard), device="cpu"),
|
| 2732 |
)
|
| 2733 |
+
optimizer_state = _load_checkpoint_shards(
|
| 2734 |
+
path, manifest, "optimizer_shards",
|
| 2735 |
+
lambda shard: torch.load(shard, map_location="cpu", weights_only=False),
|
| 2736 |
)
|
| 2737 |
+
# Every rank reads the complete state from the shared filesystem. Model
|
| 2738 |
+
# loading needs the full dictionary on each rank, and optimizer conversion
|
| 2739 |
+
# must shard each rank's dictionary rather than expect rank-zero-only input.
|
| 2740 |
+
state_cfg = FullStateDictConfig(offload_to_cpu=True, rank0_only=False)
|
| 2741 |
+
optim_cfg = FullOptimStateDictConfig(offload_to_cpu=True, rank0_only=False)
|
| 2742 |
with FSDP.state_dict_type(model, StateDictType.FULL_STATE_DICT,
|
| 2743 |
state_cfg, optim_cfg):
|
| 2744 |
model.load_state_dict(model_state)
|
|
|
|
| 3427 |
dist.init_process_group("nccl")
|
| 3428 |
torch.cuda.set_device(LOCAL_RANK)
|
| 3429 |
if GLOBAL_MICRO_BATCH_SIZE % WORLD_SIZE:
|
| 3430 |
+
raise ValueError("GLOBAL_MICRO_BATCH_SIZE must be divisible by WORLD_SIZE.")
|
| 3431 |
MICRO_BATCH_SIZE = GLOBAL_MICRO_BATCH_SIZE // WORLD_SIZE
|
| 3432 |
GRAD_ACCUM_STEPS = TOKENS_PER_UPDATE // (
|
| 3433 |
GLOBAL_MICRO_BATCH_SIZE * SEQUENCE_LENGTH
|
| 3434 |
)
|
| 3435 |
+
if PREFLIGHT_SMOKE:
|
| 3436 |
+
rank0_print(
|
| 3437 |
+
"ORION FLAGSHIP NANO — bounded 8-rank preflight smoke "
|
| 3438 |
+
"(no data stream, no Hugging Face upload)",
|
| 3439 |
+
flush=True,
|
| 3440 |
+
)
|
| 3441 |
+
else:
|
| 3442 |
+
_fresh_start_banner()
|
| 3443 |
STOP_REQUESTED = False
|
| 3444 |
|
| 3445 |
+
if not HF_TOKEN and not PREFLIGHT_SMOKE:
|
| 3446 |
raise ValueError(
|
| 3447 |
"Set HF_TOKEN to a Hugging Face token with read access to the data "
|
| 3448 |
"and write access to the checkpoint repo. Do not paste it into this file."
|
|
|
|
| 3452 |
if not torch.cuda.is_bf16_supported():
|
| 3453 |
raise RuntimeError("BF16 GPU support required.")
|
| 3454 |
|
| 3455 |
+
# Hardware and kernel preflight must complete before checkpoint discovery,
|
| 3456 |
+
# tokenizer downloads, or construction of a real data stream.
|
| 3457 |
+
hardware, inventory = validate_hardware()
|
| 3458 |
+
capability = hardware["capability"]
|
| 3459 |
+
rank0_print(
|
| 3460 |
+
f"Visible CUDA GPUs: {len(inventory)}; participating ranks: {WORLD_SIZE}\n"
|
| 3461 |
+
f"{_format_hardware_inventory(inventory)}\n"
|
| 3462 |
+
f"Active hardware profile: {HARDWARE_PROFILE_NAMES[capability]} "
|
| 3463 |
+
f"({_format_capability(capability)})",
|
| 3464 |
+
flush=True,
|
| 3465 |
+
)
|
| 3466 |
+
rank0_print("Running bounded CUDA/Triton startup preflight...", flush=True)
|
| 3467 |
+
if RUN_KERNEL_TESTS:
|
| 3468 |
+
test_kernels()
|
| 3469 |
+
select_attention(dict(ARCH))
|
| 3470 |
+
dist_barrier()
|
| 3471 |
+
|
| 3472 |
process_start = time.monotonic()
|
| 3473 |
hard_deadline = process_start + SESSION_HOURS * 3600
|
| 3474 |
nominal_train_deadline = process_start + MAX_TRAIN_HOURS * 3600
|
| 3475 |
WORK.mkdir(parents=True, exist_ok=True)
|
| 3476 |
|
|
|
|
|
|
|
| 3477 |
rank0_print(
|
| 3478 |
+
f"GPU: {hardware['name']} | {hardware['memory_gib']:.1f} GiB\n"
|
| 3479 |
f"SM: {capability[0]}.{capability[1]}\n"
|
| 3480 |
f"PyTorch: {torch.__version__} | CUDA: {torch.version.cuda}\n"
|
| 3481 |
+
f"Batch: {MICRO_BATCH_SIZE}/GPU × {WORLD_SIZE} GPUs × {GRAD_ACCUM_STEPS} × "
|
| 3482 |
f"{SEQUENCE_LENGTH} = {TOKENS_PER_UPDATE:,} tokens/update",
|
| 3483 |
flush=True,
|
| 3484 |
)
|
| 3485 |
+
if hardware["memory_gib"] < 85:
|
|
|
|
|
|
|
| 3486 |
rank0_print("Warning: defaults target a 96GB-class GPU.")
|
| 3487 |
|
| 3488 |
random.seed(42)
|
|
|
|
| 3499 |
old_handlers = {}
|
| 3500 |
|
| 3501 |
try:
|
| 3502 |
+
if PREFLIGHT_SMOKE:
|
| 3503 |
+
# Do not instantiate HFCheckpoints: its constructor creates the
|
| 3504 |
+
# remote repository and the normal path may discover/download/upload
|
| 3505 |
+
# production checkpoints. The tokenizer read below is still needed
|
| 3506 |
+
# to construct the production-shape model and is not an upload.
|
| 3507 |
+
resume_path = None
|
| 3508 |
+
downloaded = False
|
| 3509 |
+
resume_payload = {"path": None, "downloaded": False}
|
| 3510 |
else:
|
| 3511 |
+
hub = HFCheckpoints() if RANK == 0 else None
|
| 3512 |
+
if RANK == 0:
|
| 3513 |
+
resume_path, downloaded = choose_resume(hub)
|
| 3514 |
+
resume_payload = {
|
| 3515 |
+
"path": str(resume_path) if resume_path is not None else None,
|
| 3516 |
+
"downloaded": downloaded,
|
| 3517 |
+
}
|
| 3518 |
+
else:
|
| 3519 |
+
resume_payload = None
|
| 3520 |
+
resume_payload = broadcast_object(resume_payload)
|
| 3521 |
+
resume_path = Path(resume_payload["path"]) if resume_payload["path"] else None
|
| 3522 |
+
downloaded = bool(resume_payload["downloaded"])
|
| 3523 |
dist_barrier()
|
| 3524 |
|
| 3525 |
tokenizer_source = (
|
|
|
|
| 3527 |
if resume_path is not None else TOKENIZER_NAME
|
| 3528 |
)
|
| 3529 |
tokenizer = AutoTokenizer.from_pretrained(
|
| 3530 |
+
tokenizer_source, token=HF_TOKEN or None, use_fast=True
|
| 3531 |
)
|
| 3532 |
if tokenizer.eos_token_id is None:
|
| 3533 |
raise ValueError("Tokenizer requires an EOS token.")
|
|
|
|
| 3551 |
f"Nano parameter guard failed: expected ~135M, got {total:,}."
|
| 3552 |
)
|
| 3553 |
|
| 3554 |
+
def allocate_model(initialize_weights):
|
| 3555 |
+
rank0_print("Allocating FP32 T2.1 model...", flush=True)
|
| 3556 |
+
with torch.device("meta"):
|
| 3557 |
+
candidate = OrionFlagshipNanoT21(cfg)
|
| 3558 |
+
candidate.to_empty(device=DEVICE)
|
| 3559 |
+
for block in candidate.blocks:
|
| 3560 |
+
block.attn.reset_rope()
|
| 3561 |
+
if initialize_weights:
|
| 3562 |
+
candidate.initialize_weights()
|
| 3563 |
+
candidate.train()
|
| 3564 |
+
assert sum(p.numel() for p in candidate.parameters()) == total
|
| 3565 |
+
return FSDP(
|
| 3566 |
+
candidate,
|
| 3567 |
+
sharding_strategy=ShardingStrategy.FULL_SHARD,
|
| 3568 |
+
use_orig_params=True,
|
| 3569 |
+
device_id=DEVICE,
|
| 3570 |
+
sync_module_states=False,
|
| 3571 |
+
limit_all_gathers=True,
|
| 3572 |
+
)
|
| 3573 |
|
| 3574 |
+
model = allocate_model(resume_path is None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3575 |
|
| 3576 |
# Probe before creating/consuming a real data stream. This catches wiring,
|
| 3577 |
# gradient, NaN, or memory failures before the 50B run starts.
|
| 3578 |
if resume_path is None:
|
| 3579 |
run_startup_model_probe(model, len(tokenizer))
|
| 3580 |
|
| 3581 |
+
def create_optimizer(model):
|
| 3582 |
+
return torch.optim.AdamW(
|
| 3583 |
+
model.parameters(),
|
| 3584 |
+
lr=PEAK_LR,
|
| 3585 |
+
betas=(0.9, 0.95),
|
| 3586 |
+
eps=1e-8,
|
| 3587 |
+
weight_decay=WEIGHT_DECAY,
|
| 3588 |
+
fused=True,
|
| 3589 |
+
)
|
| 3590 |
+
|
| 3591 |
+
optimizer = create_optimizer(model)
|
| 3592 |
|
| 3593 |
progress = {
|
| 3594 |
"step": 0,
|
|
|
|
| 3597 |
"skipped_updates": 0,
|
| 3598 |
"consecutive_skips": 0,
|
| 3599 |
}
|
| 3600 |
+
|
| 3601 |
+
if PREFLIGHT_SMOKE:
|
| 3602 |
+
rank0_print("Running one bounded synthetic optimizer update...", flush=True)
|
| 3603 |
+
optimizer.zero_grad(set_to_none=True)
|
| 3604 |
+
x = torch.randint(
|
| 3605 |
+
0, len(tokenizer), (MICRO_BATCH_SIZE, SEQUENCE_LENGTH),
|
| 3606 |
+
device=DEVICE, dtype=torch.long,
|
| 3607 |
+
)
|
| 3608 |
+
y = torch.randint(
|
| 3609 |
+
0, len(tokenizer), (MICRO_BATCH_SIZE, SEQUENCE_LENGTH),
|
| 3610 |
+
device=DEVICE, dtype=torch.long,
|
| 3611 |
+
)
|
| 3612 |
+
model.set_specialization_scale(
|
| 3613 |
+
min(1.0, 1.0 / max(1, SPEC_WARMUP_STEPS))
|
| 3614 |
+
)
|
| 3615 |
+
with torch.autocast(
|
| 3616 |
+
"cuda", dtype=torch.bfloat16, cache_enabled=AUTOCAST_CACHE
|
| 3617 |
+
):
|
| 3618 |
+
ce, auxiliary = model(x, y)
|
| 3619 |
+
loss = ce + auxiliary
|
| 3620 |
+
finite_loss = torch.isfinite(loss.detach()).to(dtype=torch.int32)
|
| 3621 |
+
dist.all_reduce(finite_loss, op=dist.ReduceOp.MIN)
|
| 3622 |
+
if not bool(finite_loss.item()):
|
| 3623 |
+
raise RuntimeError("Preflight smoke produced a nonfinite loss.")
|
| 3624 |
+
loss.backward()
|
| 3625 |
+
norm = model.clip_grad_norm_(GRAD_CLIP)
|
| 3626 |
+
finite_grad = torch.isfinite(norm).to(dtype=torch.int32)
|
| 3627 |
+
dist.all_reduce(finite_grad, op=dist.ReduceOp.MIN)
|
| 3628 |
+
if not bool(finite_grad.item()):
|
| 3629 |
+
raise RuntimeError("Preflight smoke produced nonfinite gradients.")
|
| 3630 |
+
lr = learning_rate(1)
|
| 3631 |
+
for group in optimizer.param_groups:
|
| 3632 |
+
group["lr"] = lr
|
| 3633 |
+
optimizer.step()
|
| 3634 |
+
optimizer.zero_grad(set_to_none=True)
|
| 3635 |
+
progress.update({
|
| 3636 |
+
"step": 1,
|
| 3637 |
+
"tokens": TOKENS_PER_UPDATE,
|
| 3638 |
+
"sessions": 1,
|
| 3639 |
+
})
|
| 3640 |
+
del x, y, ce, auxiliary, loss, norm
|
| 3641 |
+
gc.collect()
|
| 3642 |
+
torch.cuda.empty_cache()
|
| 3643 |
+
|
| 3644 |
+
# The existing checkpoint format is exercised locally. The data
|
| 3645 |
+
# state is deliberately absent because this path never opens the
|
| 3646 |
+
# 50B-token stream.
|
| 3647 |
+
smoke_data_state = {"mode": "preflight_smoke", "cursor": None}
|
| 3648 |
+
smoke_path = save_local_checkpoint(
|
| 3649 |
+
model, optimizer, tokenizer, progress, smoke_data_state
|
| 3650 |
+
)
|
| 3651 |
+
smoke_path = broadcast_object(
|
| 3652 |
+
str(smoke_path) if RANK == 0 else None
|
| 3653 |
+
)
|
| 3654 |
+
dist_barrier()
|
| 3655 |
+
rank0_print("Local preflight checkpoint saved:", smoke_path, flush=True)
|
| 3656 |
+
|
| 3657 |
+
del model, optimizer
|
| 3658 |
+
gc.collect()
|
| 3659 |
+
torch.cuda.empty_cache()
|
| 3660 |
+
|
| 3661 |
+
fresh_model = allocate_model(False)
|
| 3662 |
+
fresh_optimizer = create_optimizer(fresh_model)
|
| 3663 |
+
restored = restore_checkpoint(
|
| 3664 |
+
Path(smoke_path), fresh_model, fresh_optimizer
|
| 3665 |
+
)
|
| 3666 |
+
if (
|
| 3667 |
+
restored.get("progress", {}).get("step") != 1
|
| 3668 |
+
or restored.get("progress", {}).get("tokens") != TOKENS_PER_UPDATE
|
| 3669 |
+
):
|
| 3670 |
+
raise RuntimeError("Preflight smoke restore returned unexpected progress.")
|
| 3671 |
+
dist_barrier()
|
| 3672 |
+
rank0_print(
|
| 3673 |
+
"PREFLIGHT SMOKE PASS: startup probe, one optimizer update, "
|
| 3674 |
+
"local save, and fresh model/optimizer restore completed.",
|
| 3675 |
+
flush=True,
|
| 3676 |
+
)
|
| 3677 |
+
del fresh_model, fresh_optimizer, restored
|
| 3678 |
+
return
|
| 3679 |
+
|
| 3680 |
saved_data = None
|
| 3681 |
if resume_path is not None:
|
| 3682 |
rank0_print("Restoring Nano model + optimizer...", flush=True)
|
|
|
|
| 3771 |
def save_checkpoint(reason):
|
| 3772 |
nonlocal latest_local, last_save_time, dirty, checkpoint_size_estimate
|
| 3773 |
if RANK == 0:
|
| 3774 |
+
try:
|
| 3775 |
+
hub.wait()
|
| 3776 |
+
staging_root = WORK / "upload-staging"
|
| 3777 |
+
if staging_root.exists():
|
| 3778 |
+
shutil.rmtree(staging_root)
|
| 3779 |
+
except BaseException as exc:
|
| 3780 |
+
broadcast_object(f"Checkpoint preparation failed: {type(exc).__name__}")
|
| 3781 |
+
raise
|
| 3782 |
+
preparation_error = broadcast_object(None)
|
| 3783 |
+
if preparation_error is not None:
|
| 3784 |
+
raise RuntimeError(preparation_error)
|
| 3785 |
optimizer.zero_grad(set_to_none=True)
|
| 3786 |
start = time.monotonic()
|
| 3787 |
rank0_print(f"\nSaving ({reason}) at step {progress['step']:,}...", flush=True)
|
| 3788 |
path = save_local_checkpoint(
|
| 3789 |
model, optimizer, tokenizer, progress, committed_data
|
| 3790 |
)
|
| 3791 |
+
saved = None
|
| 3792 |
if RANK == 0:
|
| 3793 |
+
try:
|
| 3794 |
+
size = folder_bytes(path)
|
| 3795 |
+
prune_local_checkpoints(path)
|
| 3796 |
+
rank0_print(
|
| 3797 |
+
f"Local save finished in {time.monotonic() - start:.1f}s | "
|
| 3798 |
+
f"{size / 1024**3:.2f} GiB\n"
|
| 3799 |
+
f"Local checkpoint: {path}", flush=True,
|
| 3800 |
+
)
|
| 3801 |
+
hub.upload_async(path, progress)
|
| 3802 |
+
saved = {"path": str(path), "size": size, "time": time.monotonic()}
|
| 3803 |
+
except BaseException as exc:
|
| 3804 |
+
broadcast_object({"error": f"Checkpoint finalization failed: {type(exc).__name__}"})
|
| 3805 |
+
raise
|
| 3806 |
+
saved = broadcast_object(saved)
|
| 3807 |
+
if "error" in saved:
|
| 3808 |
+
raise RuntimeError(saved["error"])
|
| 3809 |
+
latest_local = Path(saved["path"])
|
| 3810 |
+
checkpoint_size_estimate = saved["size"]
|
| 3811 |
+
last_save_time = saved["time"]
|
| 3812 |
+
dirty = False
|
| 3813 |
|
| 3814 |
metrics = torch.zeros(2, device=DEVICE, dtype=torch.float32)
|
| 3815 |
telemetry_probe = None
|
|
|
|
| 3856 |
loss = (ce + auxiliary) / GRAD_ACCUM_STEPS
|
| 3857 |
except torch.cuda.OutOfMemoryError as exc:
|
| 3858 |
raise RuntimeError(
|
| 3859 |
+
"Nano turbo profile OOM. Set GLOBAL_MICRO_BATCH_SIZE=56 "
|
| 3860 |
+
"(7 sequences/GPU, grad_accum=2, 114,688 tokens/update) "
|
| 3861 |
"before changing the architecture."
|
| 3862 |
) from exc
|
| 3863 |
|
|
|
|
| 3946 |
and progress["step"] % T2_TELEMETRY_EVERY_STEPS == 0
|
| 3947 |
):
|
| 3948 |
probe_x, probe_y = telemetry_probe
|
| 3949 |
+
t2 = run_telemetry_probe(model, probe_x, probe_y)
|
| 3950 |
bank_text = " ".join(
|
| 3951 |
f"B{b['bank']}:H={b['router_entropy']:.2f},"
|
| 3952 |
f"load={100*b['min_load']:.1f}-{100*b['max_load']:.1f}%,"
|
|
|
|
| 3982 |
):
|
| 3983 |
probe_x, probe_y = telemetry_probe
|
| 3984 |
n = min(ABLATION_PROBE_BATCH, probe_x.shape[0])
|
| 3985 |
+
a = run_ablation_probe(model, probe_x[:n], probe_y[:n])
|
| 3986 |
rank0_print(
|
| 3987 |
"ABLATE "
|
| 3988 |
f"step={progress['step']:,} full={a['full']:.4f} "
|
|
|
|
| 3993 |
flush=True,
|
| 3994 |
)
|
| 3995 |
|
| 3996 |
+
# Only rank zero owns the upload state and scheduling clock.
|
| 3997 |
+
# All ranks must nevertheless enter every collective save.
|
| 3998 |
+
should_save = False
|
| 3999 |
+
if RANK == 0:
|
| 4000 |
+
now = time.monotonic()
|
| 4001 |
+
upload_estimate = hub.estimate_upload_seconds(checkpoint_size_estimate)
|
| 4002 |
+
final_guard = max(
|
| 4003 |
+
FINAL_SAVE_GUARD_MINUTES * 60,
|
| 4004 |
+
2 * upload_estimate + SAVE_RESERVE_MINUTES * 60,
|
| 4005 |
+
)
|
| 4006 |
+
should_save = (
|
| 4007 |
+
dirty and not STOP_REQUESTED
|
| 4008 |
+
and not hub.busy()
|
| 4009 |
+
and now - last_save_time >= CHECKPOINT_EVERY_MINUTES * 60
|
| 4010 |
+
and train_deadline() - now > final_guard
|
| 4011 |
+
)
|
| 4012 |
+
if broadcast_object(should_save):
|
| 4013 |
save_checkpoint("periodic")
|
| 4014 |
metrics.zero_()
|
| 4015 |
log_updates = 0
|
| 4016 |
log_tokens = 0
|
| 4017 |
log_start = time.monotonic()
|
| 4018 |
|
| 4019 |
+
if broadcast_object(dirty if RANK == 0 else None):
|
| 4020 |
save_checkpoint("final")
|
| 4021 |
elif RANK == 0 and latest_local is not None:
|
| 4022 |
pointer = hub.pointer
|
|
|
|
| 4050 |
"Data pipeline failed. Preserving the last complete optimizer boundary.",
|
| 4051 |
flush=True,
|
| 4052 |
)
|
| 4053 |
+
if broadcast_object(dirty and not in_optimizer_step if RANK == 0 else None):
|
| 4054 |
try:
|
| 4055 |
save_checkpoint("data pipeline recovery")
|
| 4056 |
except Exception as exc:
|
|
|
|
| 4063 |
torch.cuda.empty_cache()
|
| 4064 |
rank0_print(
|
| 4065 |
"\nCUDA OOM. Existing checkpoints remain valid.\n"
|
| 4066 |
+
"Set GLOBAL_MICRO_BATCH_SIZE=56 first (7 sequences/GPU); "
|
| 4067 |
+
"GRAD_ACCUM_STEPS becomes 2, keeping 114,688 tokens/update. "
|
| 4068 |
+
"If necessary try GLOBAL_MICRO_BATCH_SIZE=16 (2/GPU, accumulation=7).\n"
|
| 4069 |
"No checkpoint is taken from a partial optimizer update.",
|
| 4070 |
flush=True,
|
| 4071 |
)
|
|
|
|
| 4089 |
if hub is not None:
|
| 4090 |
rank0_print("Waiting for any pending checkpoint upload...", flush=True)
|
| 4091 |
hub.close()
|
| 4092 |
+
# Do not enter a new collective during exception unwinding: another
|
| 4093 |
+
# rank may still be in a different collective when torchrun aborts it.
|
| 4094 |
if dist.is_initialized():
|
| 4095 |
dist.destroy_process_group()
|
| 4096 |
|