Bc-AI commited on
Commit
3c0a550
·
verified ·
1 Parent(s): 941847b

Upload App_fsdp.py

Browse files
Files changed (1) hide show
  1. 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, SM 120, ~96 GB VRAM.
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 H200s):
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", "hf_").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,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 Blackwell; base pretraining stays at 1K context.
 
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 test_optimized_kernels(device="cuda", dtypes=None):
 
 
 
 
 
 
 
 
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
- test_optimized_kernels()
820
- for dtype in (torch.float32, torch.bfloat16):
821
- tol = 0.04 if dtype == torch.bfloat16 else 3e-4
822
-
823
- x = torch.randn(
824
- 8, 2048, device="cuda", dtype=dtype, requires_grad=True
825
- )
826
- g = torch.randn_like(x)
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
- z = TritonSwiGLUFunction.apply(a, b)
844
- da, db = torch.autograd.grad(z, (a, b), g)
 
 
 
 
845
 
846
- ar = a.detach().float().requires_grad_(True)
847
- br = b.detach().float().requires_grad_(True)
848
- zr = F.silu(ar) * br
849
- dar, dbr = torch.autograd.grad(zr, (ar, br), g.float())
850
 
851
- torch.testing.assert_close(z.float(), zr, rtol=tol, atol=tol)
852
- torch.testing.assert_close(da.float(), dar, rtol=tol, atol=tol)
853
- torch.testing.assert_close(db.float(), dbr, rtol=tol, atol=tol)
854
 
855
- if USE_FUSED_MOE_COMBINE:
856
- for d in (127, 2048):
857
- n, k = 129, 2
858
- grouped = torch.randn(
859
- n * k, d, device="cuda",
860
- dtype=dtype, requires_grad=True,
861
- )
862
- weights = torch.softmax(
863
- torch.randn(n, k, device="cuda"), dim=-1
864
- ).detach().requires_grad_(True)
865
- inverse = torch.randperm(n * k, device="cuda")
866
- upstream = torch.randn(n, d, device="cuda")
867
-
868
- actual = FusedMoECombine.apply(grouped, weights, inverse)
869
- dg, dw = torch.autograd.grad(
870
- actual, (grouped, weights), upstream
871
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
872
 
873
- gr = grouped.detach().clone().requires_grad_(True)
874
- wr = weights.detach().clone().requires_grad_(True)
875
- reference = combine_reference(gr, wr, inverse)
876
- dgr, dwr = torch.autograd.grad(
877
- reference, (gr, wr), upstream
878
- )
879
 
880
- torch.testing.assert_close(
881
- actual, reference, rtol=1e-5, atol=1e-5
882
- )
883
- torch.testing.assert_close(
884
- dg.float(), dgr.float(), rtol=1e-5, atol=1e-5
885
- )
886
- torch.testing.assert_close(
887
- dw, dwr, rtol=3e-4, atol=3e-4
888
- )
889
 
890
- torch.cuda.synchronize()
 
 
 
 
 
 
 
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
- q = torch.randn(1, cfg["n_heads"], 256, hd,
 
 
1026
  device="cuda", dtype=torch.bfloat16, requires_grad=True)
1027
- k = torch.randn(1, cfg["n_kv_heads"], 256, hd,
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 exc:
1051
- print("Native GQA probe failed:", exc)
1052
- probe(False)
 
 
 
 
 
 
 
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
- MICRO_BATCH_SIZE, SEQUENCE_LENGTH, cfg["d_model"],
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
- groups = {
1715
- "cortex": [],
1716
- "procedure": [],
1717
- "vault": [],
1718
- "state": [],
1719
- "embedding": [],
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
- groups["procedure"].append(value)
1729
  elif name.startswith("vault"):
1730
- groups["vault"].append(value)
1731
  elif name.startswith("working") or name.startswith("state_seed"):
1732
- groups["state"].append(value)
1733
  elif name.startswith("embedding"):
1734
- groups["embedding"].append(value)
1735
  else:
1736
- groups["cortex"].append(value)
1737
-
1738
- result = {}
1739
- for key, values in groups.items():
1740
- if values:
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
- if not bool(torch.isfinite(loss).item()):
 
 
 
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 = model.run_telemetry_probe(x, y)
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
- x, y, cursor = stream.batch()
2283
- packed = torch.stack((x, y), dim=0)
2284
- else:
 
 
 
 
 
 
 
 
 
 
 
 
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
- if RANK != 0:
2425
- dist_barrier()
2426
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 = torch.load(
2525
- path / safe_relative_path(manifest["model_shards"][0]),
2526
- map_location="cpu", weights_only=False,
2527
  )
2528
- optimizer_state = torch.load(
2529
- path / safe_relative_path(manifest["optimizer_shards"][0]),
2530
- map_location="cpu", weights_only=False,
2531
  )
2532
- state_cfg = FullStateDictConfig(offload_to_cpu=True, rank0_only=True)
2533
- optim_cfg = FullOptimStateDictConfig(offload_to_cpu=True, rank0_only=True)
 
 
 
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 divide WORLD_SIZE.")
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
- _fresh_start_banner()
 
 
 
 
 
 
 
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: {gpu.name} | {gpu.total_memory / 1024**3:.1f} GiB\n"
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 capability != (12, 0):
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
- hub = HFCheckpoints() if RANK == 0 else None
3275
- if RANK == 0:
3276
- resume_path, downloaded = choose_resume(hub)
3277
- resume_payload = {
3278
- "path": str(resume_path) if resume_path is not None else None,
3279
- "downloaded": downloaded,
3280
- }
 
3281
  else:
3282
- resume_payload = None
3283
- resume_payload = broadcast_object(resume_payload)
3284
- resume_path = Path(resume_payload["path"]) if resume_payload["path"] else None
3285
- downloaded = bool(resume_payload["downloaded"])
 
 
 
 
 
 
 
 
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
- if RUN_KERNEL_TESTS:
3318
- test_kernels()
3319
- select_attention(cfg)
3320
-
3321
- rank0_print("Allocating FP32 T2.1 model...", flush=True)
3322
- with torch.device("meta"):
3323
- model = OrionFlagshipNanoT21(cfg)
3324
- model.to_empty(device=DEVICE)
3325
- for block in model.blocks:
3326
- block.attn.reset_rope()
3327
- if resume_path is None:
3328
- model.initialize_weights()
3329
- model.train()
3330
- assert sum(p.numel() for p in model.parameters()) == total
 
 
 
 
 
3331
 
3332
- model = FSDP(
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
- optimizer = torch.optim.AdamW(
3347
- model.parameters(),
3348
- lr=PEAK_LR,
3349
- betas=(0.9, 0.95),
3350
- eps=1e-8,
3351
- weight_decay=WEIGHT_DECAY,
3352
- fused=True,
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
- hub.wait()
3457
- staging_root = WORK / "upload-staging"
3458
- if staging_root.exists():
3459
- shutil.rmtree(staging_root)
 
 
 
 
 
 
 
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
- latest_local = path
3468
- checkpoint_size_estimate = folder_bytes(path)
3469
- prune_local_checkpoints(path)
3470
- last_save_time = time.monotonic()
3471
- dirty = False
3472
- rank0_print(
3473
- f"Local save finished in {time.monotonic() - start:.1f}s | "
3474
- f"{checkpoint_size_estimate / 1024**3:.2f} GiB\n"
3475
- f"Local checkpoint: {path}", flush=True,
3476
- )
3477
- hub.upload_async(path, progress)
 
 
 
 
 
 
 
 
 
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 MICRO_BATCH_SIZE=56 "
3525
- "(grad_accum becomes 2 while preserving 114,688 tokens/update) "
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 = model.run_telemetry_probe(probe_x, probe_y)
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 = model.run_ablation_probe(probe_x[:n], probe_y[:n])
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
- now = time.monotonic()
3662
- upload_estimate = (
3663
- hub.estimate_upload_seconds(checkpoint_size_estimate)
3664
- if hub is not None else 0.0
3665
- )
3666
- final_guard = max(
3667
- FINAL_SAVE_GUARD_MINUTES * 60,
3668
- 2 * upload_estimate + SAVE_RESERVE_MINUTES * 60,
3669
- )
3670
- if (
3671
- dirty and not STOP_REQUESTED
3672
- and (hub is None or not hub.busy())
3673
- and now - last_save_time >= CHECKPOINT_EVERY_MINUTES * 60
3674
- and train_deadline() - now > final_guard
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 MICRO_BATCH_SIZE=56 first; GRAD_ACCUM_STEPS will become 2 "
3730
- "and keep 114,688 tokens/update. If necessary try 28/4 next.\n"
 
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
- dist_barrier()
 
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