icarus112 commited on
Commit
5b5c422
·
verified ·
1 Parent(s): 7d0a5d8

Update Feather H200 training runtime image

Browse files
overlay/hydra/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/overlay/hydra/__pycache__/__init__.cpython-312.pyc and b/overlay/hydra/__pycache__/__init__.cpython-312.pyc differ
 
overlay/hydra/__pycache__/config.cpython-312.pyc CHANGED
Binary files a/overlay/hydra/__pycache__/config.cpython-312.pyc and b/overlay/hydra/__pycache__/config.cpython-312.pyc differ
 
overlay/hydra/__pycache__/engram.cpython-312.pyc CHANGED
Binary files a/overlay/hydra/__pycache__/engram.cpython-312.pyc and b/overlay/hydra/__pycache__/engram.cpython-312.pyc differ
 
overlay/hydra/__pycache__/eval.cpython-312.pyc ADDED
Binary file (12.1 kB). View file
 
overlay/hydra/__pycache__/model.cpython-312.pyc CHANGED
Binary files a/overlay/hydra/__pycache__/model.cpython-312.pyc and b/overlay/hydra/__pycache__/model.cpython-312.pyc differ
 
overlay/hydra/__pycache__/optimizer.cpython-312.pyc ADDED
Binary file (13.6 kB). View file
 
overlay/hydra/__pycache__/training.cpython-312.pyc CHANGED
Binary files a/overlay/hydra/__pycache__/training.cpython-312.pyc and b/overlay/hydra/__pycache__/training.cpython-312.pyc differ
 
overlay/hydra/config.py CHANGED
@@ -48,7 +48,10 @@ class PostSemClawConfig:
48
 
49
  # SemanticFoldingSDR (offline retina with STE; no-bypass, runs every step)
50
  sdr_n_bits: int = 16384 # retina width
51
- sdr_target_active: int = 327 # exact active bits (2% sparsity)
 
 
 
52
  sdr_delta_rank: int = 32 # low-rank STE delta rank
53
  sdr_som_warmup: int = 500
54
  sdr_som_interval: int = 100
 
48
 
49
  # SemanticFoldingSDR (offline retina with STE; no-bypass, runs every step)
50
  sdr_n_bits: int = 16384 # retina width
51
+ # Default 327 = 2% sparsity (Webber/Numenta canonical). Override with
52
+ # HYDRA_SDR_TARGET_ACTIVE env var; value MUST match subsystems/sdr_retina.py
53
+ # TARGET_ACTIVE (same env var is read there, so just setting it once works).
54
+ sdr_target_active: int = int(os.environ.get("HYDRA_SDR_TARGET_ACTIVE", "327"))
55
  sdr_delta_rank: int = 32 # low-rank STE delta rank
56
  sdr_som_warmup: int = 500
57
  sdr_som_interval: int = 100
overlay/hydra/model.py CHANGED
@@ -141,6 +141,29 @@ class PostSemClawModel(nn.Module):
141
  # Secondary metrics storage
142
  self._metrics = {}
143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  # Triton kernel integration gates (Phase 2 — deferred, see module docstring).
145
  self._fused_bcnorm = os.environ.get("HYDRA_FUSED_BCNORM", "0") == "1"
146
  self._fused_ssd = os.environ.get("HYDRA_FUSED_SSD", "0") == "1"
@@ -385,6 +408,16 @@ class PostSemClawModel(nn.Module):
385
  # mHC-routed Mamba-3 stack with Engram injection at configured layer.
386
  streams = self.mhc[0].init_streams(x)
387
  _engram_ev = None
 
 
 
 
 
 
 
 
 
 
388
  for i, (block, mhc_layer) in enumerate(zip(self.blocks, self.mhc)):
389
  def _block_fn(h, _block=block):
390
  return self.drop(_block(norm(h)))
@@ -399,6 +432,32 @@ class PostSemClawModel(nn.Module):
399
  self._metrics['engram_hit_rate'] = hit_rate
400
  if _profile: _engram_ev = _ev()
401
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  if _profile: _t_blocks = _ev()
403
 
404
  self._metrics['sdr_active_bits'] = sdr_active_bits
 
141
  # Secondary metrics storage
142
  self._metrics = {}
143
 
144
+ # Per-layer diagnostic panel. Env-gated; zero overhead when off.
145
+ # Emits residual-contribution (delta_ratio), feature std, effective rank,
146
+ # gradient norm per layer; used to identify minimum viable n_layer + find
147
+ # entropy leakage / dead layers. See docs/depth-sweep.md.
148
+ self._diag_enabled = os.environ.get("HYDRA_LAYER_DIAGNOSTICS", "0") == "1"
149
+ self._diag_step = 0
150
+ self._diag_svd_every = int(os.environ.get("HYDRA_LAYER_DIAG_SVD_EVERY", "100"))
151
+ if self._diag_enabled:
152
+ # Gradient-norm backward hooks on each Mamba3 block output.
153
+ # grad_output[0] is dL/d(block_out) — measures how much learning
154
+ # signal survives to reach this layer. Used to detect vanishing or
155
+ # exploding gradients per-depth.
156
+ for _i, _block in enumerate(self.blocks):
157
+ def _mk_grad_hook(_layer_idx):
158
+ def _hook(module, grad_input, grad_output):
159
+ if grad_output and grad_output[0] is not None:
160
+ g = grad_output[0].detach()
161
+ self._metrics[f'layer_{_layer_idx}_grad_norm'] = float(
162
+ g.pow(2).mean().sqrt().item()
163
+ )
164
+ return _hook
165
+ _block.register_full_backward_hook(_mk_grad_hook(_i))
166
+
167
  # Triton kernel integration gates (Phase 2 — deferred, see module docstring).
168
  self._fused_bcnorm = os.environ.get("HYDRA_FUSED_BCNORM", "0") == "1"
169
  self._fused_ssd = os.environ.get("HYDRA_FUSED_SSD", "0") == "1"
 
408
  # mHC-routed Mamba-3 stack with Engram injection at configured layer.
409
  streams = self.mhc[0].init_streams(x)
410
  _engram_ev = None
411
+
412
+ # Per-layer diagnostic panel. The pre-layer merged state h_pre lets us
413
+ # measure residual contribution of each layer: delta_N = h_post - h_pre.
414
+ # All reads are detached no-grad to avoid autograd graph pollution.
415
+ _diag = self._diag_enabled
416
+ if _diag:
417
+ with torch.no_grad():
418
+ h_pre = self.mhc[0].merge_streams(streams).detach()
419
+ _run_svd = (self._diag_step % self._diag_svd_every) == 0
420
+
421
  for i, (block, mhc_layer) in enumerate(zip(self.blocks, self.mhc)):
422
  def _block_fn(h, _block=block):
423
  return self.drop(_block(norm(h)))
 
432
  self._metrics['engram_hit_rate'] = hit_rate
433
  if _profile: _engram_ev = _ev()
434
 
435
+ if _diag:
436
+ with torch.no_grad():
437
+ h_post = mhc_layer.merge_streams(streams).detach()
438
+ in_n = h_pre.pow(2).mean().sqrt()
439
+ out_n = h_post.pow(2).mean().sqrt()
440
+ d_n = (h_post - h_pre).pow(2).mean().sqrt()
441
+ self._metrics[f'layer_{i}_in_norm'] = float(in_n.item())
442
+ self._metrics[f'layer_{i}_out_norm'] = float(out_n.item())
443
+ self._metrics[f'layer_{i}_delta_ratio'] = float((d_n / (in_n + 1e-6)).item())
444
+ self._metrics[f'layer_{i}_feat_std'] = float(h_post.std(dim=-1).mean().item())
445
+ if _run_svd:
446
+ # Effective rank via participation ratio of singular values.
447
+ # eff_rank = (Σσ)^2 / Σσ² — smooth rank proxy, bounded by d_model.
448
+ # Sampled to keep overhead low (SVD is O(min(B*T, D)^2·D)).
449
+ flat = h_post.reshape(-1, h_post.shape[-1])[:512].float()
450
+ try:
451
+ s = torch.linalg.svdvals(flat)
452
+ eff_rank = float(((s.sum() ** 2) / (s.pow(2).sum() + 1e-6)).item())
453
+ self._metrics[f'layer_{i}_eff_rank'] = eff_rank
454
+ except Exception:
455
+ pass
456
+ h_pre = h_post
457
+
458
+ if _diag:
459
+ self._diag_step += 1
460
+
461
  if _profile: _t_blocks = _ev()
462
 
463
  self._metrics['sdr_active_bits'] = sdr_active_bits
overlay/hydra/training.py CHANGED
@@ -7,6 +7,7 @@ preserved. Public entrypoint: `main()`.
7
  from __future__ import annotations
8
 
9
  import gc
 
10
  import math
11
  import os
12
  import sys
@@ -427,6 +428,30 @@ def main() -> None:
427
  _prepare_mod.EVAL_TOKENS = _orig_mid
428
  mid_ppl = 2.0 ** mid_bpb
429
  print(f"[MID_VAL] step={step} val_bpb={mid_bpb:.4f} val_ppl={mid_ppl:.3f}", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  except Exception as e:
431
  print(f"[MID_VAL] failed: {e}", flush=True)
432
  model.train()
@@ -509,6 +534,50 @@ def main() -> None:
509
  print(f"sdr_active_bits: {metrics.get('sdr_active_bits', 0):.1f}")
510
  print(f"htm_anomaly: {metrics.get('htm_anomaly', 0):.4f}")
511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  run_factual_english(model, tokenizer, MAX_SEQ_LEN)
513
  # startup_time is informative but not printed (preserve historical output)
514
  _ = startup_time
 
7
  from __future__ import annotations
8
 
9
  import gc
10
+ import json
11
  import math
12
  import os
13
  import sys
 
428
  _prepare_mod.EVAL_TOKENS = _orig_mid
429
  mid_ppl = 2.0 ** mid_bpb
430
  print(f"[MID_VAL] step={step} val_bpb={mid_bpb:.4f} val_ppl={mid_ppl:.3f}", flush=True)
431
+
432
+ # Per-layer diagnostic panel. Only printed when HYDRA_LAYER_DIAGNOSTICS=1
433
+ # is set (otherwise the layer_* keys are absent from _metrics).
434
+ _diag_metrics = model.get_secondary_metrics()
435
+ _layer_keys = sorted([k for k in _diag_metrics.keys() if k.startswith('layer_')])
436
+ if _layer_keys:
437
+ # Condense: one row per layer showing the four core signals.
438
+ n_layers = len(model.blocks)
439
+ print(f"[LAYER_DIAG] step={step}", flush=True)
440
+ for li in range(n_layers):
441
+ d_ratio = _diag_metrics.get(f'layer_{li}_delta_ratio', float('nan'))
442
+ out_n = _diag_metrics.get(f'layer_{li}_out_norm', float('nan'))
443
+ g_norm = _diag_metrics.get(f'layer_{li}_grad_norm', float('nan'))
444
+ eff_r = _diag_metrics.get(f'layer_{li}_eff_rank', float('nan'))
445
+ f_std = _diag_metrics.get(f'layer_{li}_feat_std', float('nan'))
446
+ print(
447
+ f"[LAYER_DIAG] L{li:02d} delta_ratio={d_ratio:.4f} "
448
+ f"out_norm={out_n:.4f} grad_norm={g_norm:.3e} "
449
+ f"eff_rank={eff_r:.1f} feat_std={f_std:.4f}",
450
+ flush=True,
451
+ )
452
+ htm_proj_g = _diag_metrics.get('htm_proj_grad_norm', None)
453
+ if htm_proj_g is not None:
454
+ print(f"[LAYER_DIAG] htm_proj grad_norm={htm_proj_g:.3e}", flush=True)
455
  except Exception as e:
456
  print(f"[MID_VAL] failed: {e}", flush=True)
457
  model.train()
 
534
  print(f"sdr_active_bits: {metrics.get('sdr_active_bits', 0):.1f}")
535
  print(f"htm_anomaly: {metrics.get('htm_anomaly', 0):.4f}")
536
 
537
+ # Per-layer summary panel — only printed when diagnostics were active.
538
+ _layer_keys = sorted([k for k in metrics.keys() if k.startswith('layer_')])
539
+ if _layer_keys:
540
+ n_layers = len(model.blocks)
541
+ print("--- per-layer diagnostic panel ---")
542
+ for li in range(n_layers):
543
+ d_ratio = metrics.get(f'layer_{li}_delta_ratio', float('nan'))
544
+ out_n = metrics.get(f'layer_{li}_out_norm', float('nan'))
545
+ g_norm = metrics.get(f'layer_{li}_grad_norm', float('nan'))
546
+ eff_r = metrics.get(f'layer_{li}_eff_rank', float('nan'))
547
+ f_std = metrics.get(f'layer_{li}_feat_std', float('nan'))
548
+ print(
549
+ f"L{li:02d} delta_ratio={d_ratio:.4f} out_norm={out_n:.4f} "
550
+ f"grad_norm={g_norm:.3e} eff_rank={eff_r:.1f} feat_std={f_std:.4f}"
551
+ )
552
+
553
+ # Emit full metrics dictionary as JSON for sweep aggregation. Path from
554
+ # HYDRA_METRICS_OUT env var; default=/tmp/hydra_run_metrics.json. Always
555
+ # written (even without diagnostics) so the aggregator can compare runs.
556
+ _metrics_out = os.environ.get("HYDRA_METRICS_OUT", "/tmp/hydra_run_metrics.json")
557
+ try:
558
+ _dump = dict(metrics)
559
+ _dump.update({
560
+ 'val_bpb': float(val_bpb),
561
+ 'val_ppl': float(val_ppl),
562
+ 'n_layer': int(N_LAYER),
563
+ 'd_model': int(D_MODEL),
564
+ 'num_params_M': float(num_params / 1e6),
565
+ 'num_steps': int(step),
566
+ 'total_tokens_M': float(total_tokens / 1e6),
567
+ 'peak_vram_mb': float(peak_vram_mb),
568
+ 'training_seconds': float(total_training_time),
569
+ 'sdr_target_active': int(os.environ.get("HYDRA_SDR_TARGET_ACTIVE", "327")),
570
+ })
571
+ Path(_metrics_out).parent.mkdir(parents=True, exist_ok=True)
572
+ with open(_metrics_out, 'w') as _f:
573
+ json.dump(_dump, _f, indent=2, sort_keys=True)
574
+ print(f"[METRICS] wrote {_metrics_out}", flush=True)
575
+ # Also emit a single-line JSON to stdout so the sweep aggregator can
576
+ # scrape it from HF Jobs logs without pulling files out of the container.
577
+ print("[METRICS_JSON] " + json.dumps(_dump, sort_keys=True), flush=True)
578
+ except Exception as _e:
579
+ print(f"[METRICS] write failed: {_e}", flush=True)
580
+
581
  run_factual_english(model, tokenizer, MAX_SEQ_LEN)
582
  # startup_time is informative but not printed (preserve historical output)
583
  _ = startup_time
overlay/prep_nemotron.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Nemotron Super3 pretraining data prep.
3
+
4
+ Downloads nvidia/Nemotron-Pretraining-Specialized-v1.1 configs, tokenizes with
5
+ our rustbpe/tiktoken tokenizer (trained by prepare.py), and writes
6
+ shard_{NNNNN}.parquet files consumable by the existing training pipeline —
7
+ identical layout to prepare.py: a single column named 'tokens' of dtype uint16,
8
+ with rows of length equal to --tokens-per-row (default: all tokens in one row
9
+ group, matching parquet convention used by training.py via _document_batches).
10
+
11
+ Phase 1 (diversity blend): equal weight across all 5 configs.
12
+ Phase 2 (quality blend): weighted toward Multiple-Choice/Economics/Formal-Logic.
13
+
14
+ Usage:
15
+ python prep_nemotron.py --phase phase1 --parts-per-config 8
16
+ python prep_nemotron.py --phase phase2 --parts-per-config 8 --shard-id-start 100
17
+
18
+ The --shard-id-start flag lets phase 2 append shards without colliding with
19
+ phase 1 output (phase 2 resumes from the checkpoint stored in HF Hub by
20
+ entrypoint.py, so the shard ids just need to be unique on-disk).
21
+ """
22
+
23
+ import argparse
24
+ import os
25
+ import pickle
26
+ import shutil
27
+
28
+ import pyarrow as pa
29
+ import pyarrow.parquet as pq
30
+ from huggingface_hub import HfApi, hf_hub_download
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # Import constants from prepare.py (tokenizer path, data dir, val shard id)
34
+ # ---------------------------------------------------------------------------
35
+ # prepare.py lives in the same directory; import at module level so
36
+ # DATA_DIR / TOKENIZER_DIR are always available.
37
+ import prepare as _p
38
+
39
+ NEMOTRON_REPO = "nvidia/Nemotron-Pretraining-Specialized-v1.1"
40
+
41
+ # The 5 configs per the Super3 recipe
42
+ ALL_CONFIGS = [
43
+ "Nemotron-Pretraining-Code-Concepts",
44
+ "Nemotron-Pretraining-Unconditional-Algorithmic",
45
+ "Nemotron-Pretraining-Economics",
46
+ "Nemotron-Pretraining-Formal-Logic",
47
+ "Nemotron-Pretraining-Multiple-Choice",
48
+ ]
49
+
50
+ CONFIGS_PHASE1: dict[str, float] = {
51
+ "Nemotron-Pretraining-Code-Concepts": 0.20,
52
+ "Nemotron-Pretraining-Unconditional-Algorithmic": 0.20,
53
+ "Nemotron-Pretraining-Economics": 0.20,
54
+ "Nemotron-Pretraining-Formal-Logic": 0.20,
55
+ "Nemotron-Pretraining-Multiple-Choice": 0.20,
56
+ }
57
+
58
+ CONFIGS_PHASE2: dict[str, float] = {
59
+ "Nemotron-Pretraining-Multiple-Choice": 0.45, # MMLU-style: high quality
60
+ "Nemotron-Pretraining-Economics": 0.20,
61
+ "Nemotron-Pretraining-Formal-Logic": 0.15,
62
+ "Nemotron-Pretraining-Code-Concepts": 0.10,
63
+ "Nemotron-Pretraining-Unconditional-Algorithmic": 0.10,
64
+ }
65
+
66
+ # Parquet files in this repo follow: {config}/part_{NNNNNN}.parquet
67
+ # Some configs also have plain 0.parquet, 1.parquet naming — handled by list_repo_files.
68
+ _TEXT_COLUMN_CANDIDATES = ["text", "content", "prompt_completion", "body", "input"]
69
+
70
+
71
+ # ---------------------------------------------------------------------------
72
+ # Helpers
73
+ # ---------------------------------------------------------------------------
74
+
75
+ def _load_tokenizer() -> "_p.Tokenizer":
76
+ """Load the tiktoken tokenizer produced by prepare.py."""
77
+ tokenizer_pkl = os.path.join(_p.TOKENIZER_DIR, "tokenizer.pkl")
78
+ if not os.path.exists(tokenizer_pkl):
79
+ raise RuntimeError(
80
+ f"Tokenizer not found at {tokenizer_pkl}. "
81
+ "Run `python prepare.py --num-shards 1` first to train the BPE tokenizer."
82
+ )
83
+ with open(tokenizer_pkl, "rb") as f:
84
+ enc = pickle.load(f)
85
+ return _p.Tokenizer(enc)
86
+
87
+
88
+ def download_nemotron_files(config: str, n_parts: int, token: str) -> list[str]:
89
+ """List parquet files for *config*, download up to *n_parts*. Return local paths."""
90
+ api = HfApi(token=token)
91
+ repo_files = list(api.list_repo_files(NEMOTRON_REPO, repo_type="dataset"))
92
+ prefix = f"{config}/"
93
+ config_files = sorted(
94
+ f for f in repo_files
95
+ if f.startswith(prefix) and f.endswith(".parquet")
96
+ )
97
+ if not config_files:
98
+ print(f" [warn] no parquet files found under {prefix} in {NEMOTRON_REPO}", flush=True)
99
+ return []
100
+ config_files = config_files[:n_parts]
101
+ local_paths: list[str] = []
102
+ for remote_path in config_files:
103
+ local = hf_hub_download(
104
+ repo_id=NEMOTRON_REPO,
105
+ filename=remote_path,
106
+ repo_type="dataset",
107
+ token=token,
108
+ )
109
+ local_paths.append(local)
110
+ print(f" [download] {remote_path} -> {local}", flush=True)
111
+ return local_paths
112
+
113
+
114
+ def _detect_text_column(schema: pa.Schema) -> str:
115
+ """Return the name of the text column from a parquet schema."""
116
+ col_names = schema.names
117
+ for candidate in _TEXT_COLUMN_CANDIDATES:
118
+ if candidate in col_names:
119
+ return candidate
120
+ # Fallback: first string column
121
+ for i, field in enumerate(schema):
122
+ if pa.types.is_string(field.type) or pa.types.is_large_string(field.type):
123
+ return field.name
124
+ # Last resort: first column
125
+ return col_names[0]
126
+
127
+
128
+ def tokenize_and_write_shards(
129
+ parquet_paths: list[str],
130
+ tokenizer: "_p.Tokenizer",
131
+ out_dir: str,
132
+ shard_id_start: int,
133
+ tokens_per_shard: int,
134
+ ) -> int:
135
+ """
136
+ Stream-tokenize all text from *parquet_paths*, write fixed-size token shards.
137
+
138
+ Shard format (identical to prepare.py):
139
+ - single column 'tokens', dtype uint16
140
+ - each row group contains *tokens_per_shard* tokens
141
+
142
+ Returns the next available shard_id (= shard_id_start + shards_written).
143
+ """
144
+ shard_id = shard_id_start
145
+ tokens_buf: list[int] = []
146
+
147
+ for path in parquet_paths:
148
+ pf = pq.ParquetFile(path)
149
+ text_col = _detect_text_column(pf.schema_arrow)
150
+ print(f" [tokenize] {os.path.basename(path)} column='{text_col}'", flush=True)
151
+ for rg_idx in range(pf.num_row_groups):
152
+ rg = pf.read_row_group(rg_idx, columns=[text_col])
153
+ texts: list[str] = rg.column(text_col).to_pylist()
154
+ # encode_ordinary_batch is faster (no special-token handling needed)
155
+ # tokenizer.encode() wraps enc.encode_ordinary for str input
156
+ token_lists: list[list[int]] = tokenizer.encode(texts)
157
+ for ids in token_lists:
158
+ tokens_buf.extend(ids)
159
+ # Flush complete shards
160
+ while len(tokens_buf) >= tokens_per_shard:
161
+ chunk = tokens_buf[:tokens_per_shard]
162
+ tokens_buf = tokens_buf[tokens_per_shard:]
163
+ _write_shard(out_dir, shard_id, chunk)
164
+ shard_id += 1
165
+
166
+ # Flush final partial shard (if any meaningful data remains)
167
+ if len(tokens_buf) >= 1024:
168
+ _write_shard(out_dir, shard_id, tokens_buf)
169
+ shard_id += 1
170
+
171
+ return shard_id
172
+
173
+
174
+ def _write_shard(out_dir: str, shard_id: int, tokens: list[int]) -> None:
175
+ filename = f"shard_{shard_id:05d}.parquet"
176
+ out_path = os.path.join(out_dir, filename)
177
+ tmp_path = out_path + ".tmp"
178
+ arr = pa.array(tokens, type=pa.uint16())
179
+ tbl = pa.table({"tokens": arr})
180
+ pq.write_table(tbl, tmp_path)
181
+ os.rename(tmp_path, out_path)
182
+ print(f" [shard] wrote {filename} ({len(tokens):,} tokens)", flush=True)
183
+
184
+
185
+ # ---------------------------------------------------------------------------
186
+ # Main
187
+ # ---------------------------------------------------------------------------
188
+
189
+ def main() -> None:
190
+ parser = argparse.ArgumentParser(
191
+ description="Nemotron Super3 data prep — tokenize and shard to prepare.py-compatible format"
192
+ )
193
+ parser.add_argument(
194
+ "--phase",
195
+ choices=["phase1", "phase2"],
196
+ required=True,
197
+ help="phase1 = equal blend; phase2 = quality-weighted blend",
198
+ )
199
+ parser.add_argument(
200
+ "--parts-per-config",
201
+ type=int,
202
+ default=4,
203
+ help="Base number of parquet parts to download per config (scaled by weight)",
204
+ )
205
+ parser.add_argument(
206
+ "--tokens-per-shard",
207
+ type=int,
208
+ default=10_000_000,
209
+ help="Tokens per output shard (default 10M, matching climbmix convention)",
210
+ )
211
+ parser.add_argument(
212
+ "--shard-id-start",
213
+ type=int,
214
+ default=0,
215
+ help="First shard index to write (use non-zero to append after phase1 shards)",
216
+ )
217
+ parser.add_argument(
218
+ "--hf-token",
219
+ default=os.environ.get("HF_TOKEN"),
220
+ help="HuggingFace token (also read from $HF_TOKEN)",
221
+ )
222
+ args = parser.parse_args()
223
+
224
+ if not args.hf_token:
225
+ # Try ~/.hf_token as fallback (per project convention)
226
+ hf_token_path = os.path.expanduser("~/.hf_token")
227
+ if os.path.exists(hf_token_path):
228
+ with open(hf_token_path) as f:
229
+ args.hf_token = f.read().strip()
230
+
231
+ configs = CONFIGS_PHASE1 if args.phase == "phase1" else CONFIGS_PHASE2
232
+
233
+ tokenizer = _load_tokenizer()
234
+ os.makedirs(_p.DATA_DIR, exist_ok=True)
235
+
236
+ shard_id = args.shard_id_start
237
+ for config, weight in configs.items():
238
+ # Scale parts proportionally to weight so heavier configs get more data
239
+ n_parts = max(1, round(args.parts_per_config * weight * len(configs)))
240
+ print(
241
+ f"\n[nemotron] {config} weight={weight:.2f} n_parts={n_parts}",
242
+ flush=True,
243
+ )
244
+ parquet_paths = download_nemotron_files(config, n_parts, args.hf_token)
245
+ if not parquet_paths:
246
+ print(f" [skip] no files downloaded for {config}", flush=True)
247
+ continue
248
+ shard_id = tokenize_and_write_shards(
249
+ parquet_paths,
250
+ tokenizer,
251
+ _p.DATA_DIR,
252
+ shard_id,
253
+ args.tokens_per_shard,
254
+ )
255
+
256
+ # Write validation shard — use Multiple-Choice (highest quality) as val source.
257
+ # Reserve the same VAL_SHARD index as prepare.py (6542) so training.py picks it up.
258
+ print("\n[nemotron] writing validation shard ...", flush=True)
259
+ val_paths = download_nemotron_files(
260
+ "Nemotron-Pretraining-Multiple-Choice", 1, args.hf_token
261
+ )
262
+ if val_paths:
263
+ tokenize_and_write_shards(
264
+ val_paths,
265
+ tokenizer,
266
+ _p.DATA_DIR,
267
+ _p.VAL_SHARD, # 6542 — matches prepare.py VAL_SHARD constant
268
+ args.tokens_per_shard,
269
+ )
270
+ else:
271
+ print(" [warn] could not download val shard; evaluation may fail", flush=True)
272
+
273
+ print(
274
+ f"\n[nemotron] done — wrote shards {args.shard_id_start}..{shard_id - 1}"
275
+ f" + val shard {_p.VAL_SHARD}",
276
+ flush=True,
277
+ )
278
+
279
+
280
+ if __name__ == "__main__":
281
+ main()
overlay/subsystems/__pycache__/hestia_mini.cpython-312.pyc ADDED
Binary file (4.75 kB). View file
 
overlay/subsystems/__pycache__/htm.cpython-312.pyc ADDED
Binary file (20.2 kB). View file
 
overlay/subsystems/__pycache__/mhc_mini.cpython-312.pyc ADDED
Binary file (6.87 kB). View file
 
overlay/subsystems/__pycache__/sdr_retina.cpython-312.pyc CHANGED
Binary files a/overlay/subsystems/__pycache__/sdr_retina.cpython-312.pyc and b/overlay/subsystems/__pycache__/sdr_retina.cpython-312.pyc differ
 
overlay/subsystems/__pycache__/sdr_semantic.cpython-312.pyc ADDED
Binary file (19.6 kB). View file
 
overlay/subsystems/__pycache__/train_engram.cpython-312.pyc ADDED
Binary file (46.1 kB). View file
 
overlay/subsystems/__pycache__/train_hestia.cpython-312.pyc ADDED
Binary file (50.3 kB). View file
 
overlay/subsystems/__pycache__/train_mamba3.cpython-312.pyc ADDED
Binary file (36.8 kB). View file
 
overlay/subsystems/__pycache__/train_mhc.cpython-312.pyc ADDED
Binary file (42.7 kB). View file
 
overlay/subsystems/__pycache__/train_sdr.cpython-312.pyc ADDED
Binary file (54.8 kB). View file
 
overlay/subsystems/sdr_retina.py CHANGED
@@ -54,10 +54,14 @@ RETINA_PATH = os.path.join(CACHE_DIR, "retina.npz")
54
  GRID_H = 128
55
  GRID_W = 128
56
  N_BITS = GRID_H * GRID_W # 16384
57
- TARGET_SPARSITY = 0.02 # 2%
58
- # int(floor(N_BITS * TARGET_SPARSITY)) = 327, matches the Cortical.io-style
59
- # "exactly 2% (327 / 16384) active bits per SDR" specification.
60
- TARGET_ACTIVE = int(N_BITS * TARGET_SPARSITY) # 327
 
 
 
 
61
 
62
  CONTEXT_WINDOW = 8 # +/- 8 tokens
63
  TOP_K_FEATURES = 64 # top-K context features per token
 
54
  GRID_H = 128
55
  GRID_W = 128
56
  N_BITS = GRID_H * GRID_W # 16384
57
+ TARGET_SPARSITY = 0.02 # 2% (default, Cortical.io-style)
58
+ # Default = int(floor(N_BITS * TARGET_SPARSITY)) = 327, matches Webber/Numenta.
59
+ # Override via HYDRA_SDR_TARGET_ACTIVE env var. The cache key encodes
60
+ # target_active, so changing this triggers automatic retina regeneration.
61
+ TARGET_ACTIVE = int(os.environ.get(
62
+ "HYDRA_SDR_TARGET_ACTIVE",
63
+ str(int(N_BITS * TARGET_SPARSITY)),
64
+ ))
65
 
66
  CONTEXT_WINDOW = 8 # +/- 8 tokens
67
  TOP_K_FEATURES = 64 # top-K context features per token
overlay/subsystems/sdr_semantic.py CHANGED
@@ -23,7 +23,10 @@ import torch.nn as nn
23
 
24
 
25
  DEFAULT_RETINA_PATH = os.path.expanduser("~/.cache/autoresearch/retina.npz")
26
- DEFAULT_TARGET_ACTIVE = 327 # 2% of 16384
 
 
 
27
 
28
 
29
  class _SDRSTE(torch.autograd.Function):
 
23
 
24
 
25
  DEFAULT_RETINA_PATH = os.path.expanduser("~/.cache/autoresearch/retina.npz")
26
+ # Default 327 = 2% of 16384 (Webber/Numenta canonical).
27
+ # Override via HYDRA_SDR_TARGET_ACTIVE env var (must match the value used when
28
+ # the retina cache was built — sdr_retina.py TARGET_ACTIVE reads the same var).
29
+ DEFAULT_TARGET_ACTIVE = int(os.environ.get("HYDRA_SDR_TARGET_ACTIVE", "327"))
30
 
31
 
32
  class _SDRSTE(torch.autograd.Function):
runtime_setup.sh ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Runtime setup for the stock pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel image.
3
+ # We avoid baking feather + mamba_ssm + htm_rust into a custom Docker image
4
+ # because build-time baking on HF's cpu-basic builder reliably corrupts CUDA
5
+ # state on h200 runtime ("Error 802: system not yet initialized" every time,
6
+ # even in a fresh python -c subprocess). Installing at runtime, on the h200
7
+ # itself, avoids that path and keeps CUDA healthy.
8
+ #
9
+ # Trade-off: ~5-8 min cold start per job vs ~1 min for a baked image. The
10
+ # training run is 12h long, so the overhead is negligible.
11
+
12
+ set -euo pipefail
13
+
14
+ echo "[runtime] $(date -u +%H:%M:%S) starting feather runtime setup on $(hostname)"
15
+
16
+ # 1. Confirm CUDA before we do anything else.
17
+ python -c 'import torch; assert torch.cuda.is_available(), "cuda unavailable at runtime start"; print("[runtime] cuda OK —", torch.cuda.get_device_name(0))'
18
+
19
+ # 2. Install system build deps (rustup/build-essential for htm_rust).
20
+ apt-get update -qq
21
+ apt-get install -y -qq --no-install-recommends git curl ca-certificates build-essential pkg-config libssl-dev
22
+ # Rust toolchain for htm_rust
23
+ curl -sSf https://sh.rustup.rs | bash -s -- -y --profile minimal --default-toolchain stable
24
+ export PATH=/root/.cargo/bin:$PATH
25
+
26
+ # 3. Install Python deps.
27
+ pip install --quiet --upgrade pip setuptools wheel
28
+ pip install --quiet \
29
+ maturin \
30
+ huggingface_hub \
31
+ requests \
32
+ pyarrow \
33
+ rustbpe \
34
+ pandas \
35
+ tiktoken \
36
+ pydantic \
37
+ ninja \
38
+ packaging \
39
+ einops
40
+
41
+ # 4. Install mamba_ssm + causal_conv1d (prebuilt wheels, matching torch2.6/cu12).
42
+ pip install --quiet \
43
+ 'https://github.com/Dao-AILab/causal-conv1d/releases/download/v1.6.1.post4/causal_conv1d-1.6.1+cu12torch2.6cxx11abiFALSE-cp311-cp311-linux_x86_64.whl' \
44
+ 'https://github.com/state-spaces/mamba/releases/download/v2.3.1/mamba_ssm-2.3.1+cu12torch2.6cxx11abiFALSE-cp311-cp311-linux_x86_64.whl'
45
+
46
+ # 5. Graft Mamba3 from main (pure Triton, not in v2.3.1 release).
47
+ SITE=/opt/conda/lib/python3.11/site-packages/mamba_ssm
48
+ BASE=https://raw.githubusercontent.com/state-spaces/mamba/main
49
+ curl -fsSL "$BASE/mamba_ssm/modules/mamba3.py" -o "$SITE/modules/mamba3.py"
50
+ mkdir -p "$SITE/ops/triton/mamba3"
51
+ for f in __init__.py angle_dt.py mamba3_mimo_rotary_step.py mamba3_mimo_utils.py \
52
+ mamba3_siso_bwd.py mamba3_siso_combined.py mamba3_siso_fwd.py \
53
+ mamba3_siso_step.py utils.py; do
54
+ curl -fsSL "$BASE/mamba_ssm/ops/triton/mamba3/$f" -o "$SITE/ops/triton/mamba3/$f"
55
+ done
56
+ # Replace the eager-init __init__.py with our minimal version.
57
+ cp /workspace/feather/hf_jobs/feather_h200_image/mamba_ssm_init.py "$SITE/__init__.py"
58
+
59
+ # 6. Confirm CUDA still works after all installs.
60
+ python -c 'import torch; assert torch.cuda.is_available(), "cuda broken by installs"; print("[runtime] cuda OK after deps —", torch.cuda.get_device_name(0))'
61
+
62
+ # 7. Build + install htm_rust with sm_90 PTX (h200 arch).
63
+ cd /workspace/feather
64
+ export HTM_CUDA_ARCH=sm_90
65
+ export LD_LIBRARY_PATH=/usr/local/cuda/lib64:${LD_LIBRARY_PATH:-}
66
+ maturin build --release --features gpu --manifest-path htm_rust/Cargo.toml 2>&1 | tail -5
67
+ pip install --quiet htm_rust/target/wheels/htm_rust-*.whl
68
+
69
+ # 8. Sanity: cuda still alive after htm_rust install.
70
+ python -c 'import torch; assert torch.cuda.is_available(), "cuda broken by htm_rust"; import htm_rust; print("[runtime] htm_rust OK, cuda OK")'
71
+
72
+ echo "[runtime] $(date -u +%H:%M:%S) runtime setup complete"