AbstractPhil commited on
Commit
36c00a7
·
verified ·
1 Parent(s): ac13ae9

exp003 + exp007 shipped: sigma is the only live register axis; AMoE v1 monolith falsifier fires under uniform pressure (machinery green) — next: structural band assignment

Browse files
README.md CHANGED
@@ -72,10 +72,11 @@ resolution) so gaps live in a narrow band — the paired design is load-bearing.
72
  | exp000b_natural | natural-paradigm wall: format specialization is a double dissociation; SDXL core tops the NL wall | **shipped (candidate, s0)** |
73
  | exp001_sd15_relay | relay-all16 vs matched LoRA-r32 vs frozen: **relay beats both; LoRA lands below frozen; post-train toggle bit-exact** | **shipped (candidate, s0)** |
74
  | exp002_sd15_addrcond | addr-cond 4-arm causal: **GUIDEPOST with a pulse** — inert beside full text (real≈deranged), but the address ALONE steers (+0.029); redesign = complementarity | **shipped (candidate, s0)** |
75
- | exp003_sigma_registers | sign-code separations: what is a diffusion "register"? (+prompt77 register) | running |
76
  | exp004_anima_relay | relays on the 2B DiT via diffusion-pipe (bf16 per dtype law) | staged |
77
  | exp005_sdxl_tree4a | the SDXL capacity battery (guidepost/scaffold/skeleton) | designed |
78
  | exp006_sd15core_relay | CORE EPRED certification: **relay beats frozen (−2.5%) and matched LoRA 2-for-2; grounding gained (+0.036) where LoRA traded it; gates GROW on the core** | **shipped (candidate, s0)** |
 
79
 
80
  Code for each experiment ships in its folder with `results.json` from the run
81
  ledger. Substrate modules in `substrate/`.
 
72
  | exp000b_natural | natural-paradigm wall: format specialization is a double dissociation; SDXL core tops the NL wall | **shipped (candidate, s0)** |
73
  | exp001_sd15_relay | relay-all16 vs matched LoRA-r32 vs frozen: **relay beats both; LoRA lands below frozen; post-train toggle bit-exact** | **shipped (candidate, s0)** |
74
  | exp002_sd15_addrcond | addr-cond 4-arm causal: **GUIDEPOST with a pulse** — inert beside full text (real≈deranged), but the address ALONE steers (+0.029); redesign = complementarity | **shipped (candidate, s0)** |
75
+ | exp003_sigma_registers | register probe: **sigma is the ONLY live axis** caption/type/cond all compress to ≤0; ordering prereg MISS with design consequences | **shipped (candidate, s0)** |
76
  | exp004_anima_relay | relays on the 2B DiT via diffusion-pipe (bf16 per dtype law) | staged |
77
  | exp005_sdxl_tree4a | the SDXL capacity battery (guidepost/scaffold/skeleton) | designed |
78
  | exp006_sd15core_relay | CORE EPRED certification: **relay beats frozen (−2.5%) and matched LoRA 2-for-2; grounding gained (+0.036) where LoRA traded it; gates GROW on the core** | **shipped (candidate, s0)** |
79
+ | exp007_amoe_lora | AMoE v1 vs monolith falsifier: **falsifier fires — uniform pressure ⇒ flat usage, dispatch pays nothing (Tree-1b law on diffusion); machinery green; next = structural band assignment** | **shipped (candidate, s0)** |
80
 
81
  Code for each experiment ships in its folder with `results.json` from the run
82
  ledger. Substrate modules in `substrate/`.
exp003_sigma_registers/README.md ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # exp003_sigma_registers — what is a diffusion "register"? (read-only probe)
2
+
3
+ **Question.** Does the two-regime predictor (sign-code separation reads the
4
+ dispatch regime before training) transfer to diffusion states, and which
5
+ candidate register axes actually separate?
6
+
7
+ **Design.** Deterministic same-seed-init relays on the frozen Lune ckpt-2500
8
+ (zero-init ⇒ the forward is the frozen model's, bit-exact); codes read at 4
9
+ depth-spread sites from the relay's addressing cosines (winner half-axis id +
10
+ orientation — read-only argmax, gauge only). Gauge: inter − intra Hamming with
11
+ the intra diagonal EXCLUDED (the r2 restart — NOT comparable to qwen-line
12
+ numbers). Axes: caption register (prompt / json_prompt / vit_json_prompt /
13
+ **prompt77** — aligned, same 24 rows), sigma band (4 shift-warped centers,
14
+ aligned), cond vs uncond (aligned), subject buckets + generator_type groups
15
+ (domain-style, unaligned, documented).
16
+
17
+ **Results** (`results.json`, deep site = site12):
18
+
19
+ | axis | separation | reading |
20
+ |---|---|---|
21
+ | **sigma_band** | **+0.0108 (only positive; grows with depth)** | the live axis |
22
+ | cond_uncond | −0.0187 | compresses |
23
+ | caption_register | −0.0251 (negative at every band × site) | image content dominates; format invisible |
24
+ | generator_type (n=8×4) | ≈ 0 (−0.004..+0.001) | even scene TYPE doesn't separate |
25
+ | subject_bucket | honestly skipped | dataset max ≈6 rows/subject < gauge floor 8 |
26
+
27
+ **Verdict.** Preregistered ordering MISS (predicted subject > caption > sigma
28
+ > cond/uncond; measured sigma > cond/uncond > caption). On frozen-UNet block
29
+ outputs, **content compresses everything conditioning-side to zero-or-negative
30
+ separation, and sigma/state is the only axis the code surface sees** — the
31
+ multimodal-compression precedent taken to its limit on a third substrate
32
+ class. Design consequence (already acted on): dispatch registers for diffusion
33
+ MoE should be keyed on state/sigma (exp007 wired sigma into its dispatch key),
34
+ and imposed structurally (step-gated band assignment) rather than discovered;
35
+ subject-level registers need richer data (the fused datasets) and possibly
36
+ cond-pathway read sites rather than block outputs.
37
+
38
+ **Caveats.** Init-time codes (P1-probe precedent) on one substrate; n=24
39
+ aligned rows; negative separations partly reflect the (i,i) same-input
40
+ cross-register pairs (documented gauge property — near-identical registers
41
+ read ≈ −intra/n). GPU cost ≈ 4 min total across three runs (two crashes were
42
+ data-shape asserts, both converted to skip-not-crash; all axes final).
exp003_sigma_registers/dexp003_sigma_probe.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """dexp003_sigma_probe.py — exp003: sign-code separations on diffusion states.
2
+
3
+ THE TIER-C GATEKEEPER (read-only, no training): does the two-regime predictor
4
+ transfer to diffusion, and what is a diffusion "register"? Codes are read from
5
+ DETERMINISTIC same-seed-init relays (P1-probe precedent — init-time probing is
6
+ valid; relays are zero-init so the forward is the frozen model's bit-exact).
7
+
8
+ Register axes probed (prereg ordering, charter 2026-07-16:
9
+ subject-bucket > caption-register > sigma-band > cond/uncond):
10
+ caption_register — SAME 24 rows rendered as {prompt NL, json_prompt,
11
+ vit_json_prompt} (aligned inputs, the gauge's home case).
12
+ sigma_band — same rows + cond at 4 shift-warped band centers
13
+ (aligned; the conditioning axis text LMs lack).
14
+ cond_uncond — json_prompt cond vs zeroed cond (aligned).
15
+ subject_bucket — top-4 dominant-subject buckets (n=12 each, content
16
+ differs by construction — UNALIGNED, documented; the
17
+ domain-style axis).
18
+ Sites: 4 depth-spread relay sites of the 16; codes read at the relay INPUT
19
+ (block output). Gauge: separation_no_diag (intra diagonal EXCLUDED — the r2
20
+ restart; NOT comparable to qwen-line numbers).
21
+
22
+ Pod: python3 pod2/dexp003_sigma_probe.py --run
23
+ """
24
+ from __future__ import annotations
25
+
26
+ import io
27
+ import json
28
+ import os
29
+ import sys
30
+ from collections import Counter, defaultdict
31
+
32
+ sys.path[:0] = ["pod2", "."]
33
+
34
+ import torch
35
+
36
+ from pod_ledger import ledger_run, note
37
+ from aleph_diffusion_core import sign_codes, separation_no_diag
38
+ from d1_lune_sampler import encode_clip_225
39
+ from d1_substrate import attach_relays
40
+
41
+ DATASET = "AbstractPhil/synthetic-object-relations-json"
42
+ SD_BASE = "stable-diffusion-v1-5/stable-diffusion-v1-5"
43
+ SD_REPO = "AbstractPhil/sd15-flow-lune-json-prompt"
44
+ SD_SUB = "checkpoint-00002500/unet"
45
+ VAE_SCALE, SHIFT, SEED = 0.18215, 2.5, 0
46
+ # N_PER_BUCKET 12 was unreachable on this dataset (240 rows -> ~150 dominant
47
+ # subjects, max bucket 6 — single-object synthetic renders); 4 is the honest
48
+ # ceiling, variance noted in results. generator_type added as a second
49
+ # domain-style axis (bigger groups exist there).
50
+ N_ALIGNED, N_BUCKET_ROWS, N_PER_BUCKET = 24, 240, 4
51
+ PROBE_SITES = (0, 5, 8, 12) # depth spread of the 16 (asserted names)
52
+ BAND_CENTERS = (0.875, 0.625, 0.375, 0.125)
53
+ CAPTION_COLS = ("prompt", "json_prompt", "vit_json_prompt")
54
+ # natural-paradigm tweak (Phil 2026-07-16): the NL register is ALSO probed at
55
+ # the standard 77-token encode ("prompt77") alongside the 225-chunk encodes
56
+ NL77_REGISTER = "prompt77"
57
+ OUT_DIR = ("/workspace/data/dexp003" if os.path.isdir("/workspace")
58
+ else os.path.join(os.environ.get("GEOLIP_DATA", "./data"),
59
+ "dexp003"))
60
+
61
+
62
+ def load_tail_rows(n):
63
+ from huggingface_hub import HfApi, hf_hub_download
64
+ import pyarrow.parquet as pq
65
+ api = HfApi()
66
+ files = sorted(f for f in api.list_repo_files(DATASET, repo_type="dataset")
67
+ if f.endswith(".parquet"))
68
+ path = hf_hub_download(DATASET, files[-1], repo_type="dataset")
69
+ tbl = pq.read_table(path)
70
+ rows = tbl.slice(max(0, tbl.num_rows - n), n).to_pylist()
71
+ out = []
72
+ for r in rows:
73
+ img = r["image"]
74
+ row = {"image_bytes": img.get("bytes") if isinstance(img, dict)
75
+ else img,
76
+ "generator_type": str(r.get("generator_type", "?"))}
77
+ for c in CAPTION_COLS:
78
+ v = r.get(c)
79
+ row[c] = v if isinstance(v, str) else json.dumps(v)
80
+ out.append(row)
81
+ return out
82
+
83
+
84
+ def dominant_subject(jp: str) -> str:
85
+ try:
86
+ subs = json.loads(jp).get("subjects") or []
87
+ s = subs[0]
88
+ name = s.get("name") if isinstance(s, dict) else str(s)
89
+ return (name or "?").strip().lower().split()[-1]
90
+ except Exception:
91
+ return "?"
92
+
93
+
94
+ class SiteCapture:
95
+ """Forward hooks on the wrapped blocks: stash relay-INPUT hidden states."""
96
+
97
+ def __init__(self, unet, relays, names):
98
+ self.stash = {}
99
+ self.handles = []
100
+ self.sites = {}
101
+ from aleph_diffusion_core import BlockWithRelay
102
+ wrapped = [(n, m) for n, m in unet.named_modules()
103
+ if isinstance(m, BlockWithRelay)]
104
+ assert len(wrapped) == len(relays)
105
+ for si in PROBE_SITES:
106
+ name, mod = wrapped[si]
107
+ self.sites[si] = (name, relays[si])
108
+
109
+ def mk(si):
110
+ def hook(m, inp, out):
111
+ h = out[0] if isinstance(out, tuple) else out
112
+ self.stash[si] = h.detach()
113
+ return hook
114
+ self.handles.append(mod.block.register_forward_hook(mk(si)))
115
+
116
+ def codes(self):
117
+ return {si: sign_codes(self.sites[si][1], h).cpu()
118
+ for si, h in self.stash.items()}
119
+
120
+ def remove(self):
121
+ for h in self.handles:
122
+ h.remove()
123
+
124
+
125
+ def run(device="cuda"):
126
+ os.makedirs(OUT_DIR, exist_ok=True)
127
+ import numpy as np
128
+ from PIL import Image
129
+ from diffusers import UNet2DConditionModel, AutoencoderKL
130
+ from transformers import CLIPTextModel, CLIPTokenizer
131
+
132
+ torch.manual_seed(SEED) # deterministic relay init
133
+ unet = UNet2DConditionModel.from_pretrained(
134
+ SD_REPO, subfolder=SD_SUB, torch_dtype=torch.float32).to(device)
135
+ unet.requires_grad_(False)
136
+ unet.eval()
137
+ relays, names = attach_relays(unet)
138
+ for r in relays:
139
+ r.assert_zero_init() # forward stays frozen-exact
140
+ cap = SiteCapture(unet, relays, names)
141
+ vae = AutoencoderKL.from_pretrained(
142
+ SD_BASE, subfolder="vae", torch_dtype=torch.float32).to(device).eval()
143
+ tok = CLIPTokenizer.from_pretrained(SD_BASE, subfolder="tokenizer")
144
+ te = CLIPTextModel.from_pretrained(
145
+ SD_BASE, subfolder="text_encoder",
146
+ torch_dtype=torch.float32).to(device).eval()
147
+
148
+ @torch.no_grad()
149
+ def latents_of(rows):
150
+ px = torch.stack([
151
+ torch.from_numpy(np.asarray(
152
+ Image.open(io.BytesIO(r["image_bytes"])).convert("RGB")
153
+ .resize((512, 512)))).float().permute(2, 0, 1) / 127.5 - 1.0
154
+ for r in rows]).to(device)
155
+ g = torch.Generator(device=device).manual_seed(SEED)
156
+ return vae.encode(px).latent_dist.sample(generator=g) * VAE_SCALE
157
+
158
+ @torch.no_grad()
159
+ def probe(lat, ehs, sigma):
160
+ g = torch.Generator(device=device).manual_seed(SEED + 99)
161
+ noise = torch.randn(lat.shape, generator=g, device=device)
162
+ x = noise * sigma + lat * (1 - sigma)
163
+ t = torch.full((lat.shape[0],), sigma * 1000.0, device=device)
164
+ unet(x, t, ehs, return_dict=False)
165
+ return cap.codes()
166
+
167
+ results = {"gauge": "separation_no_diag (intra diagonal excluded; "
168
+ "NOT comparable to qwen-line numbers)",
169
+ "sites": {si: cap.sites[si][0] for si in PROBE_SITES},
170
+ "band_centers": BAND_CENTERS, "seed": SEED, "axes": {}}
171
+
172
+ with ledger_run("dexp003 register probe", budget_h=1.0) as h:
173
+ rows = load_tail_rows(N_ALIGNED)
174
+ lat = latents_of(rows)
175
+ ehs_by_col = {}
176
+ with torch.no_grad():
177
+ for c in CAPTION_COLS:
178
+ ehs_by_col[c] = encode_clip_225(
179
+ [r[c] for r in rows], tok, te, device)
180
+ # natural-paradigm register: standard 77-token encode of the NL col
181
+ ids = tok([r["prompt"] for r in rows], padding="max_length",
182
+ max_length=tok.model_max_length, truncation=True,
183
+ return_tensors="pt").input_ids.to(device)
184
+ ehs_by_col[NL77_REGISTER] = te(ids)[0]
185
+
186
+ # axis 1: caption register (aligned), per band x site
187
+ regs = list(CAPTION_COLS) + [NL77_REGISTER]
188
+ ax = {}
189
+ for b, s in enumerate(BAND_CENTERS):
190
+ codes = {c: probe(lat, ehs_by_col[c], s) for c in regs}
191
+ for si in PROBE_SITES:
192
+ ax[f"band{b}_site{si}"] = round(separation_no_diag(
193
+ {c: codes[c][si] for c in regs}), 4)
194
+ results["axes"]["caption_register"] = ax
195
+ print(f"[exp003] caption_register: {ax}", flush=True)
196
+
197
+ # axis 2: sigma band (aligned; json_prompt cond), per site
198
+ codes_by_band = {f"s{b}": probe(lat, ehs_by_col["json_prompt"], s)
199
+ for b, s in enumerate(BAND_CENTERS)}
200
+ ax = {f"site{si}": round(separation_no_diag(
201
+ {k: v[si] for k, v in codes_by_band.items()}), 4)
202
+ for si in PROBE_SITES}
203
+ results["axes"]["sigma_band"] = ax
204
+ print(f"[exp003] sigma_band: {ax}", flush=True)
205
+
206
+ # axis 3: cond vs uncond (aligned), mid band, per site
207
+ cu = {"cond": probe(lat, ehs_by_col["json_prompt"], 0.625),
208
+ "uncond": probe(lat, torch.zeros_like(
209
+ ehs_by_col["json_prompt"]), 0.625)}
210
+ ax = {f"site{si}": round(separation_no_diag(
211
+ {k: v[si] for k, v in cu.items()}), 4) for si in PROBE_SITES}
212
+ results["axes"]["cond_uncond"] = ax
213
+ print(f"[exp003] cond_uncond: {ax}", flush=True)
214
+
215
+ # axis 4: domain-style groups (UNALIGNED, documented): subject buckets
216
+ # (n small on this dataset — variance flag) + generator_type groups
217
+ pool = load_tail_rows(N_BUCKET_ROWS)
218
+
219
+ def group_axis(name, keyfn, min_n):
220
+ groups = defaultdict(list)
221
+ for r in pool:
222
+ groups[keyfn(r)].append(r)
223
+ top = [s for s, c in Counter(
224
+ {k: len(v) for k, v in groups.items()}).most_common(6)
225
+ if len(groups[s]) >= min_n and s != "?"][:4]
226
+ if len(top) < 2:
227
+ results["axes"][name] = {
228
+ "skipped": f"only {len(top)} groups >= {min_n}",
229
+ "sizes": dict(Counter(
230
+ {k: len(v) for k, v in groups.items()}))}
231
+ print(f"[exp003] {name}: SKIPPED (group sizes too small)",
232
+ flush=True)
233
+ return
234
+ codes = {}
235
+ for s in top:
236
+ rs = groups[s][:min_n]
237
+ l = latents_of(rs)
238
+ with torch.no_grad():
239
+ e = encode_clip_225([r["json_prompt"] for r in rs], tok,
240
+ te, device)
241
+ codes[s] = probe(l, e, 0.625)
242
+ try:
243
+ ax = {f"site{si}": round(separation_no_diag(
244
+ {k: v[si] for k, v in codes.items()}), 4)
245
+ for si in PROBE_SITES}
246
+ except AssertionError as e:
247
+ # gauge n-floor (>=8): axis honestly unmeasurable at this n —
248
+ # record, never crash (this dataset's subjects max out at ~6)
249
+ results["axes"][name] = {"groups": top, "n_per_group": min_n,
250
+ "skipped": f"gauge n-floor: {e}"}
251
+ print(f"[exp003] {name}: SKIPPED ({e})", flush=True)
252
+ return
253
+ results["axes"][name] = {"groups": top, "n_per_group": min_n, **ax}
254
+ print(f"[exp003] {name} ({top}): {ax}", flush=True)
255
+
256
+ group_axis("subject_bucket",
257
+ lambda r: dominant_subject(r["json_prompt"]), N_PER_BUCKET)
258
+ group_axis("generator_type", lambda r: r["generator_type"], 8)
259
+
260
+ # prereg ordering check at the deepest probed site
261
+ deep = f"site{PROBE_SITES[-1]}"
262
+ order = {
263
+ "caption_register": results["axes"]["caption_register"]
264
+ [f"band1_{deep}"],
265
+ "sigma_band": results["axes"]["sigma_band"][deep],
266
+ "cond_uncond": results["axes"]["cond_uncond"][deep],
267
+ }
268
+ sb = results["axes"].get("subject_bucket", {})
269
+ if deep in sb:
270
+ order["subject_bucket"] = sb[deep]
271
+ ranked = sorted(order, key=order.get, reverse=True)
272
+ results["prereg"] = {
273
+ "predicted_order": ["subject_bucket", "caption_register",
274
+ "sigma_band", "cond_uncond"],
275
+ "measured_order": ranked,
276
+ "measured_values_at_deep_site": order,
277
+ "order_hit": ranked[:len(order)] == [
278
+ k for k in ["subject_bucket", "caption_register",
279
+ "sigma_band", "cond_uncond"] if k in order],
280
+ }
281
+ h["verdict"] = f"order {'HIT' if results['prereg']['order_hit'] else 'MISS'}: {ranked}"
282
+
283
+ cap.remove()
284
+ with open(os.path.join(OUT_DIR, "results.json"), "w") as f:
285
+ json.dump(results, f, indent=2)
286
+ note(f"dexp003: {json.dumps(results['prereg'])}")
287
+ print(json.dumps(results["prereg"], indent=2))
288
+ return results
289
+
290
+
291
+ def smoke():
292
+ assert dominant_subject('{"subjects":[{"name":"a red car"}]}') == "car"
293
+ assert dominant_subject('{"subjects":["tall man"]}') == "man"
294
+ assert dominant_subject("not json") == "?"
295
+ print("dexp003 smoke PASSED (parse; GPU run is pod work)")
296
+
297
+
298
+ if __name__ == "__main__":
299
+ if "--run" in sys.argv:
300
+ run()
301
+ else:
302
+ smoke()
exp003_sigma_registers/results.json ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "gauge": "separation_no_diag (intra diagonal excluded; NOT comparable to qwen-line numbers)",
3
+ "sites": {
4
+ "0": "down_blocks.0.attentions.0.transformer_blocks.0",
5
+ "5": "down_blocks.2.attentions.1.transformer_blocks.0",
6
+ "8": "up_blocks.1.attentions.2.transformer_blocks.0",
7
+ "12": "up_blocks.3.attentions.0.transformer_blocks.0"
8
+ },
9
+ "band_centers": [
10
+ 0.875,
11
+ 0.625,
12
+ 0.375,
13
+ 0.125
14
+ ],
15
+ "seed": 0,
16
+ "axes": {
17
+ "caption_register": {
18
+ "band0_site0": -0.0272,
19
+ "band0_site5": -0.0094,
20
+ "band0_site8": -0.0106,
21
+ "band0_site12": -0.0185,
22
+ "band1_site0": -0.0312,
23
+ "band1_site5": -0.017,
24
+ "band1_site8": -0.0208,
25
+ "band1_site12": -0.0251,
26
+ "band2_site0": -0.035,
27
+ "band2_site5": -0.0234,
28
+ "band2_site8": -0.028,
29
+ "band2_site12": -0.0316,
30
+ "band3_site0": -0.0368,
31
+ "band3_site5": -0.0274,
32
+ "band3_site8": -0.0316,
33
+ "band3_site12": -0.0348
34
+ },
35
+ "sigma_band": {
36
+ "site0": 0.0039,
37
+ "site5": -0.0,
38
+ "site8": -0.0004,
39
+ "site12": 0.0108
40
+ },
41
+ "cond_uncond": {
42
+ "site0": -0.0269,
43
+ "site5": -0.0122,
44
+ "site8": -0.013,
45
+ "site12": -0.0187
46
+ },
47
+ "subject_bucket": {
48
+ "groups": [
49
+ "garlic",
50
+ "cucumber",
51
+ "potato",
52
+ "banana"
53
+ ],
54
+ "n_per_group": 4,
55
+ "skipped": "gauge n-floor: too few aligned inputs"
56
+ },
57
+ "generator_type": {
58
+ "groups": [
59
+ "produce_pair",
60
+ "two_on_surface",
61
+ "object_on_surface",
62
+ "item_in_container"
63
+ ],
64
+ "n_per_group": 8,
65
+ "site0": -0.0041,
66
+ "site5": -0.0001,
67
+ "site8": 0.0008,
68
+ "site12": -0.0009
69
+ }
70
+ },
71
+ "prereg": {
72
+ "predicted_order": [
73
+ "subject_bucket",
74
+ "caption_register",
75
+ "sigma_band",
76
+ "cond_uncond"
77
+ ],
78
+ "measured_order": [
79
+ "sigma_band",
80
+ "cond_uncond",
81
+ "caption_register"
82
+ ],
83
+ "measured_values_at_deep_site": {
84
+ "caption_register": -0.0251,
85
+ "sigma_band": 0.0108,
86
+ "cond_uncond": -0.0187
87
+ },
88
+ "order_hit": false
89
+ }
90
+ }
exp007_amoe_lora/README.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # exp007_amoe_lora — AMoE-LoRA v1: the first dispatch test (CANDIDATE, s0)
2
+
3
+ **Question.** Does a bank of experts under dense signed aleph dispatch (keyed
4
+ on hidden state + sigma) beat a parameter-matched monolith on the certified
5
+ core-epred substrate? First MoE-structure experiment of the line.
6
+
7
+ **Structure per site (16 sites).** A=4 zero-init rank-8 low-rank residual
8
+ experts; dispatch = dense SIGNED aleph coefficients per token (sinh/cosh over
9
+ A oriented axes — no softmax gate, no top-k, no balance loss; usage read-only)
10
+ with sigma Fourier features added to the dispatch key (the exp003 finding:
11
+ sigma is the live register axis). Combine: `x + Σ σ(gate_k)·w_k(x,σ)·Δ_k(x)`.
12
+ Falsifier: one rank-32 delta per site, same placement, no dispatch (the
13
+ matched-monolith control). 3000 steps each, batch 16, pure Adam wd=0.
14
+
15
+ **Results** (`results.json`):
16
+
17
+ | arm | trainable | val eps-MSE | Δ vs frozen |
18
+ |---|---|---|---|
19
+ | frozen | 0 | 0.124788 | — |
20
+ | amoe A4r8 | 899,392 | 0.122015 | −2.2% |
21
+ | **monolith r32** | 811,216 | **0.121777** | **−2.4%** |
22
+
23
+ | prereg | outcome |
24
+ |---|---|
25
+ | P1 bank ≥ monolith | **MISS** (monolith wins by 0.0002 with 11% fewer params) |
26
+ | P2 sigma-band usage differentiation | **MISS** — spread 0.0009; usage ≈ [0.25, 0.17, 0.33, 0.24] in EVERY band |
27
+ | P3 toggle bit-exact post-train | HIT (≡ frozen to full precision) |
28
+ | P4 no dead experts | HIT (all four alive, 0.17–0.33) |
29
+
30
+ **Verdict (the falsifier did its job).** Under UNIFORM training pressure — one
31
+ dataset, one objective, one register — dispatch pays nothing over a single
32
+ matched-width delta, and the sigma-keyed dispatch never uses its sigma
33
+ freedom. This reproduces the text line's Tree-1b/exp009 law on a diffusion
34
+ trunk: *specialization requires differentiated pressure; the aggregation
35
+ channel alone doesn't create it.* The experts blended (uniform usage) exactly
36
+ as the two-regime law predicts for register-distance-zero mixtures.
37
+
38
+ **What it buys the program.** The machinery is sound (parity, liveness,
39
+ signed dense dispatch all green) and per-param the bank units are efficient
40
+ (−2.2% at 0.9M vs the relay's −2.5% at 3.2M). The next MoE test must IMPOSE
41
+ structure rather than hope dispatch discovers it in uniform data — the
42
+ designed follow-on is the step-gated controller: experts ASSIGNED to sigma
43
+ bands with per-band loss masking (coarse-to-fine), judged against this same
44
+ monolith. Subject-bucketed expert assignment is the second axis (needs the
45
+ richer fused datasets; this synthetic set's subject buckets max out at n≈6).
46
+
47
+ **Caveats.** Single seed; 3k steps; A=4/r=8 single scale point; param match
48
+ 1.109 (bank heavier — disclosed); one substrate. Cost ≈ 3.0 GPU-h.
49
+ Checkpoints: `amoe_core_s0.pt`, `mono_core_s0.pt`.
exp007_amoe_lora/amoe_core_s0.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:37928cf2d2ce85b4ca38d160c428e2fc118aa884dc882238631716a1764a7c45
3
+ size 3681565
exp007_amoe_lora/dexp007_amoe_lora.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """dexp007_amoe_lora.py — exp007: AMoE-LoRA v1 — the first dispatch test.
2
+
3
+ THE FIRST MoE-STRUCTURE EXPERIMENT of the line (Phil: the MoE is the weekend
4
+ goal; hit it only as fast as results yield). GATED: launches only after
5
+ exp006 certifies the anchor unit on the epred core (env DEXP7_SUBSTRATE=core)
6
+ — else holds, or runs on the exp001-certified lune trunk (=lune).
7
+
8
+ Structure per site (16 BasicTransformerBlocks), doctrine-compliant
9
+ (progression-plan Part II.2 — no softmax gate, no top-k, no balance loss):
10
+ experts — A=4 zero-init low-rank residual deltas on the block output:
11
+ down Linear(d, r=8, bias=False) -> up Linear(r=8, d) zero W+b.
12
+ dispatch — DENSE SIGNED aleph coefficients: key = normalize(key_proj(
13
+ x_token) + sig_proj(fourier(sigma))) in D=4; codebook K=A axes;
14
+ w_k = sinh(u_k)/sum cosh(u_j) per token — signed, no selection
15
+ event; usage logged READ-ONLY.
16
+ combine — x + sum_k sigmoid(gate_k) * w_k * expert_k(x); gates init -3.0.
17
+ toggle — enabled=False returns x (bit-exact law preserved).
18
+ Sigma enters the DISPATCH KEY (exp003 finding: sigma/state is the live
19
+ register axis; caption registers compress).
20
+
21
+ Arms (shared cache, paired val, pure Adam wd=0, dtype matched to trunk):
22
+ frozen | amoe (A=4, r=8) | monolith LoRA-shape rank A*r=32 on the same
23
+ placement (THE FALSIFIER — matched total params, no dispatch; if it matches
24
+ the bank everywhere, dispatch pays nothing at this scale).
25
+
26
+ Prereg (charter): P1 amoe >= monolith on paired val; P2 dispatch usage
27
+ DIFFERENTIATES by sigma band (variance of per-band mean |w| exceeds a
28
+ shuffled-band null x3); P3 toggle bit-exact post-train; P4 no dead experts
29
+ (every expert's mean |w| > 0.02 of total). Honest negative: amoe ~ monolith
30
+ => the aggregation channel (Law 1) needs rework before any bigger MoE.
31
+
32
+ Pod: DEXP7_SUBSTRATE=core|lune bash pod2/run_exp007.sh (time-boxed: steps
33
+ sized so the run NEVER crosses the 8am PT halt)
34
+ """
35
+ from __future__ import annotations
36
+
37
+ import io
38
+ import json
39
+ import math
40
+ import os
41
+ import sys
42
+ import time
43
+
44
+ sys.path[:0] = ["pod2", "."]
45
+
46
+ import torch
47
+ import torch.nn as nn
48
+ import torch.nn.functional as F
49
+
50
+ from pod_ledger import ledger_run, note, burn_down
51
+ from aleph_diffusion_core import derangement, sigma_band, SIGMA_BANDS
52
+ from d1_substrate import enumerate_sd15_sites, MEM_FRACTION
53
+
54
+ SUBSTRATE = os.environ.get("DEXP7_SUBSTRATE", "core")
55
+ SD_BASE = "stable-diffusion-v1-5/stable-diffusion-v1-5"
56
+ LUNE_REPO = "AbstractPhil/sd15-flow-lune-json-prompt"
57
+ LUNE_SUB = "checkpoint-00002500/unet"
58
+ N_TRAIN, N_VAL = 4096, 256
59
+ A_EXPERTS, RANK = 4, 8
60
+ BATCH = int(os.environ.get("DEXP7_BATCH", "16"))
61
+ STEPS = int(os.environ.get("DEXP7_STEPS", "3000"))
62
+ LR, CFG_DROPOUT, SEED, SHIFT, VAE_SCALE = 1e-3, 0.1, 0, 2.5, 0.18215
63
+ DATA_DIR = ("/workspace/data/dexp007" if os.path.isdir("/workspace")
64
+ else os.path.join(os.environ.get("GEOLIP_DATA", "./data"),
65
+ "dexp007"))
66
+ CKPT_DIR = ("/workspace/ckpts2/dexp007" if os.path.isdir("/workspace")
67
+ else DATA_DIR)
68
+
69
+
70
+ def fourier_sigma(s, dim=8):
71
+ """s in [0,1] (or t/1000) -> small Fourier features [B, dim]."""
72
+ freqs = torch.pow(2.0, torch.arange(dim // 2, device=s.device))
73
+ ang = 2 * math.pi * s[:, None] * freqs[None]
74
+ return torch.cat([torch.sin(ang), torch.cos(ang)], dim=-1)
75
+
76
+
77
+ class AmoeBank(nn.Module):
78
+ """A experts + dense signed aleph dispatch, one bank per site."""
79
+
80
+ def __init__(self, d: int, A: int = A_EXPERTS, r: int = RANK,
81
+ D: int = 4, tau: float = 0.1):
82
+ super().__init__()
83
+ self.d, self.A, self.r, self.tau = d, A, r, tau
84
+ self.down = nn.ModuleList(nn.Linear(d, r, bias=False)
85
+ for _ in range(A))
86
+ self.up = nn.ModuleList(nn.Linear(r, d) for _ in range(A))
87
+ for dn, up in zip(self.down, self.up):
88
+ nn.init.orthogonal_(dn.weight)
89
+ nn.init.zeros_(up.weight) # zero-init out: weight
90
+ nn.init.zeros_(up.bias) # AND bias (leak law)
91
+ self.key_proj = nn.Linear(d, D, bias=False)
92
+ nn.init.orthogonal_(self.key_proj.weight)
93
+ self.sig_proj = nn.Linear(8, D, bias=False)
94
+ nn.init.orthogonal_(self.sig_proj.weight)
95
+ self.codebook = nn.Parameter(
96
+ F.normalize(torch.randn(A, D), dim=-1))
97
+ self.gates = nn.Parameter(torch.full((A,), -3.0))
98
+ self.enabled = True
99
+ self.last_usage = None # read-only telemetry
100
+
101
+ def assert_zero_init(self):
102
+ for up in self.up:
103
+ assert up.weight.abs().max().item() == 0.0
104
+ assert up.bias.abs().max().item() == 0.0
105
+
106
+ def dispatch(self, x, sig_feat):
107
+ """Dense signed coefficients per token: (..., A). No selection event."""
108
+ sig = self.sig_proj(sig_feat) # (B, D)
109
+ sig = sig.view(sig.shape[0], *([1] * (x.ndim - 2)), sig.shape[-1])
110
+ key = self.key_proj(x) + sig # rank-aware broadcast
111
+ Ax = F.normalize(self.codebook, dim=-1)
112
+ u = (F.normalize(key, dim=-1) @ Ax.transpose(-1, -2)) / self.tau
113
+ m = u.abs().amax(dim=-1, keepdim=True)
114
+ ep, en = torch.exp(u - m), torch.exp(-u - m)
115
+ return (ep - en) / (ep + en).sum(dim=-1, keepdim=True)
116
+
117
+ def forward(self, x, sig_feat):
118
+ if not self.enabled:
119
+ return x
120
+ w = self.dispatch(x, sig_feat) # (B, T, A) signed
121
+ with torch.no_grad():
122
+ self.last_usage = w.abs().mean(dim=(0, 1)).detach().cpu()
123
+ delta = 0
124
+ g = torch.sigmoid(self.gates)
125
+ for k in range(self.A):
126
+ delta = delta + g[k] * w[..., k:k + 1] * self.up[k](
127
+ self.down[k](x))
128
+ return x + delta
129
+
130
+
131
+ class BlockWithBank(nn.Module):
132
+ """Wrap: bank reads block output + the batch sigma stashed on self."""
133
+
134
+ def __init__(self, block, bank):
135
+ super().__init__()
136
+ self.block = block
137
+ self.bank = bank
138
+ self.sig_feat = None # set per step by the bed
139
+
140
+ def forward(self, *args, **kwargs):
141
+ out = self.block(*args, **kwargs)
142
+ h = out[0] if isinstance(out, tuple) else out
143
+ h = self.bank(h, self.sig_feat)
144
+ return (h,) + out[1:] if isinstance(out, tuple) else h
145
+
146
+
147
+ class MonoLora(nn.Module):
148
+ """The falsifier: one rank-A*r delta, same placement, no dispatch."""
149
+
150
+ def __init__(self, d: int, r: int = A_EXPERTS * RANK):
151
+ super().__init__()
152
+ self.down = nn.Linear(d, r, bias=False)
153
+ nn.init.orthogonal_(self.down.weight)
154
+ self.up = nn.Linear(r, d)
155
+ nn.init.zeros_(self.up.weight)
156
+ nn.init.zeros_(self.up.bias)
157
+ self.gate = nn.Parameter(torch.tensor(-3.0))
158
+ self.enabled = True
159
+
160
+ def forward(self, x, sig_feat=None):
161
+ if not self.enabled:
162
+ return x
163
+ return x + torch.sigmoid(self.gate) * self.up(self.down(x))
164
+
165
+
166
+ def load_unet(device):
167
+ from diffusers import UNet2DConditionModel
168
+ if SUBSTRATE == "lune":
169
+ unet = UNet2DConditionModel.from_pretrained(
170
+ LUNE_REPO, subfolder=LUNE_SUB, torch_dtype=torch.float32)
171
+ else:
172
+ unet = UNet2DConditionModel.from_pretrained(
173
+ SD_BASE, subfolder="unet", torch_dtype=torch.float32)
174
+ unet = unet.to(device)
175
+ unet.requires_grad_(False)
176
+ unet.eval()
177
+ unet.enable_gradient_checkpointing()
178
+ return unet
179
+
180
+
181
+ def attach_banks(unet, mk):
182
+ sites = enumerate_sd15_sites(unet)
183
+ assert len(sites) == 16
184
+ mods, wraps = nn.ModuleList(), []
185
+ for name, block, d in sites:
186
+ p0 = next(block.parameters())
187
+ m = mk(d).to(device=p0.device, dtype=p0.dtype)
188
+ w = BlockWithBank(block, m)
189
+ parent = unet
190
+ parts = name.split(".")
191
+ for p in parts[:-1]:
192
+ parent = getattr(parent, p) if not p.isdigit() else parent[int(p)]
193
+ if parts[-1].isdigit():
194
+ parent[int(parts[-1])] = w
195
+ else:
196
+ setattr(parent, parts[-1], w)
197
+ mods.append(m)
198
+ wraps.append(w)
199
+ return mods, wraps
200
+
201
+
202
+ def get_cache(device):
203
+ """Reuse the substrate-matching cache (dexp001 for lune / dexp006 core)."""
204
+ src = "/workspace/data/dexp001" if SUBSTRATE == "lune" \
205
+ else "/workspace/data/dexp006"
206
+ f = os.path.join(src, "cache.pt")
207
+ assert os.path.exists(f), f"needs {f} (run the substrate's exp first)"
208
+ return torch.load(f, map_location="cpu", weights_only=True)
209
+
210
+
211
+ def sigma_of(cache_has_t, gen, bsz, device):
212
+ if cache_has_t: # epred core: t in 0..999
213
+ t = torch.randint(0, 1000, (bsz,), generator=gen, device=device)
214
+ return t, t.float() / 1000.0
215
+ s = torch.rand(bsz, generator=gen, device=device)
216
+ s = (SHIFT * s) / (1 + (SHIFT - 1) * s)
217
+ return s * 1000, s
218
+
219
+
220
+ def run(device="cuda"):
221
+ torch.cuda.set_per_process_memory_fraction(MEM_FRACTION, 0)
222
+ os.makedirs(CKPT_DIR, exist_ok=True)
223
+ os.makedirs(DATA_DIR, exist_ok=True)
224
+ cache = get_cache(device)
225
+ epred = SUBSTRATE != "lune"
226
+ if epred:
227
+ from dexp006_sd15core_relay import make_schedule, add_noise
228
+ acp = make_schedule(device)
229
+
230
+ def loss_of(unet, wraps, lat, ehs, gen):
231
+ bsz = lat.shape[0]
232
+ drop = torch.rand(bsz, generator=gen, device=device) < CFG_DROPOUT
233
+ ehs = ehs.clone()
234
+ ehs[drop] = 0
235
+ t, s01 = sigma_of(epred, gen, bsz, device)
236
+ sf = fourier_sigma(s01).to(lat.dtype)
237
+ for w in wraps:
238
+ w.sig_feat = sf
239
+ noise = torch.randn(lat.shape, generator=gen, device=device)
240
+ if epred:
241
+ noisy = add_noise(lat, noise, t.long(), acp)
242
+ target = noise
243
+ else:
244
+ s4 = s01[:, None, None, None]
245
+ noisy = noise * s4 + lat * (1 - s4)
246
+ target = noise - lat
247
+ pred = unet(noisy, t, ehs, return_dict=False)[0]
248
+ return F.mse_loss(pred, target)
249
+
250
+ @torch.no_grad()
251
+ def val(unet, wraps):
252
+ tot, band_usage = [], {i: [] for i in range(len(SIGMA_BANDS))}
253
+ for i in range(0, N_VAL, 32):
254
+ lat = cache["val_lat"][i:i + 32].to(device)
255
+ ehs = cache["val_ehs"][i:i + 32].to(device)
256
+ noise = cache["val_noise"][i:i + 32].to(device)
257
+ if epred:
258
+ t = cache["val_t"][i:i + 32].to(device)
259
+ s01 = t.float() / 1000.0
260
+ noisy, target = add_noise(lat, noise, t, acp), noise
261
+ else:
262
+ s = cache["val_sigma"][i:i + 32].to(device)
263
+ s01 = s
264
+ s4 = s[:, None, None, None]
265
+ noisy, target = noise * s4 + lat * (1 - s4), noise - lat
266
+ t = s * 1000
267
+ sf = fourier_sigma(s01).to(lat.dtype)
268
+ for w in wraps:
269
+ w.sig_feat = sf
270
+ pred = unet(noisy, t, ehs, return_dict=False)[0]
271
+ tot += ((pred - target) ** 2).mean(dim=(1, 2, 3)).tolist()
272
+ for w in wraps:
273
+ u = getattr(w.bank, "last_usage", None)
274
+ if u is not None:
275
+ for j, sv in enumerate(s01.tolist()):
276
+ band_usage[sigma_band(sv)].append(u.tolist())
277
+ usage = {f"band{k}":
278
+ [round(sum(col) / len(col), 4) for col in zip(*v)]
279
+ if v else None for k, v in band_usage.items()}
280
+ return sum(tot) / len(tot), usage
281
+
282
+ def train(unet, wraps, params, label):
283
+ opt = torch.optim.Adam(params, lr=LR, weight_decay=0.0)
284
+ gen = torch.Generator(device=device).manual_seed(SEED + 42)
285
+ idx = torch.Generator().manual_seed(SEED + 7)
286
+ torch.cuda.reset_peak_memory_stats()
287
+ t0 = time.time()
288
+ for step in range(1, STEPS + 1):
289
+ sel = torch.randint(0, N_TRAIN, (BATCH,), generator=idx)
290
+ loss = loss_of(unet, wraps, cache["lat"][sel].to(device),
291
+ cache["ehs"][sel].to(device), gen)
292
+ loss.backward()
293
+ opt.step()
294
+ opt.zero_grad(set_to_none=True)
295
+ if step == 50 or step % 500 == 0:
296
+ print(f"[{label}] step {step}: loss {loss.item():.4f} | "
297
+ f"{(time.time() - t0) / step:.2f}s/step | peak "
298
+ f"{torch.cuda.max_memory_allocated() / 2**30:.1f}GB",
299
+ flush=True)
300
+ return round((time.time() - t0) / STEPS, 3)
301
+
302
+ results = {"config": {"substrate": SUBSTRATE, "A": A_EXPERTS, "r": RANK,
303
+ "steps": STEPS, "batch": BATCH, "lr": LR,
304
+ "seed": SEED, "epred": epred}}
305
+
306
+ with ledger_run(f"dexp007 frozen ({SUBSTRATE})", budget_h=0.2) as h:
307
+ unet = load_unet(device)
308
+ v, _ = val(unet, [])
309
+ results["frozen"] = {"val": v}
310
+ del unet
311
+ torch.cuda.empty_cache()
312
+ h["verdict"] = f"val {v:.5f}"
313
+
314
+ with ledger_run("dexp007 amoe A4r8", budget_h=2.5) as h:
315
+ unet = load_unet(device)
316
+ banks, wraps = attach_banks(unet, lambda d: AmoeBank(d))
317
+ for b in banks:
318
+ b.assert_zero_init()
319
+ n_params = sum(p.numel() for p in banks.parameters())
320
+ print(f"[amoe] 16 banks, {n_params:,} trainable", flush=True)
321
+ spd = train(unet, wraps, banks.parameters(), "amoe")
322
+ v_on, usage = val(unet, wraps)
323
+ for b in banks:
324
+ b.enabled = False
325
+ v_off, _ = val(unet, wraps)
326
+ d = abs(v_off - results["frozen"]["val"])
327
+ assert d < 1e-9, f"toggle parity broken: {d}"
328
+ for b in banks:
329
+ b.enabled = True
330
+ torch.save({"banks": [b.state_dict() for b in banks]},
331
+ os.path.join(CKPT_DIR, f"amoe_{SUBSTRATE}_s0.pt"))
332
+ # P2: sigma-band usage differentiation vs shuffled-band null
333
+ per_band = [u for u in usage.values() if u]
334
+ band_var = 0.0
335
+ if len(per_band) >= 2:
336
+ cols = list(zip(*per_band))
337
+ band_var = sum(
338
+ (max(c) - min(c)) for c in cols) / len(cols)
339
+ results["amoe"] = {"n_params": n_params, "val": v_on,
340
+ "val_toggled_off": v_off, "s_per_step": spd,
341
+ "usage_by_band": usage,
342
+ "band_usage_spread": round(band_var, 5),
343
+ "gates": [round(torch.sigmoid(g).item(), 4)
344
+ for g in banks[8].gates]}
345
+ del unet, banks
346
+ torch.cuda.empty_cache()
347
+ h["verdict"] = f"val {v_on:.5f} spread {band_var:.4f}"
348
+
349
+ with ledger_run("dexp007 monolith r32", budget_h=2.5) as h:
350
+ unet = load_unet(device)
351
+ monos, wraps = attach_banks(unet, lambda d: MonoLora(d))
352
+ n_params = sum(p.numel() for p in monos.parameters())
353
+ print(f"[mono] 16 sites, {n_params:,} trainable", flush=True)
354
+ spd = train(unet, wraps, monos.parameters(), "mono")
355
+ v, _ = val(unet, wraps)
356
+ torch.save({"monos": [m.state_dict() for m in monos]},
357
+ os.path.join(CKPT_DIR, f"mono_{SUBSTRATE}_s0.pt"))
358
+ results["monolith"] = {"n_params": n_params, "val": v,
359
+ "s_per_step": spd}
360
+ del unet, monos
361
+ torch.cuda.empty_cache()
362
+ h["verdict"] = f"val {v:.5f}"
363
+
364
+ av, mv = results["amoe"]["val"], results["monolith"]["val"]
365
+ results["verdict"] = {
366
+ "amoe_vs_monolith": "amoe" if av <= mv else "monolith",
367
+ "amoe_beats_frozen": av < results["frozen"]["val"],
368
+ "param_ratio": round(results["amoe"]["n_params"]
369
+ / results["monolith"]["n_params"], 3),
370
+ "band_usage_spread": results["amoe"]["band_usage_spread"],
371
+ "note": "P1 amoe>=monolith; P2 sigma-band usage differentiation; "
372
+ "1-seed CANDIDATE; first MoE-structure test of the line",
373
+ }
374
+ with open(os.path.join(DATA_DIR, "results.json"), "w") as f:
375
+ json.dump(results, f, indent=2)
376
+ note(f"dexp007 verdict: {json.dumps(results['verdict'])}")
377
+ print(json.dumps(results["verdict"], indent=2))
378
+ burn_down()
379
+ return results
380
+
381
+
382
+ def smoke():
383
+ b = AmoeBank(320)
384
+ b.assert_zero_init()
385
+ x = torch.randn(2, 9, 320)
386
+ sf = fourier_sigma(torch.tensor([0.3, 0.8]))
387
+ assert torch.equal(b(x, sf), x), "zero-init must be exact"
388
+ b.enabled = False
389
+ assert b(x, sf) is x
390
+ b.enabled = True
391
+ w = b.dispatch(x, sf)
392
+ assert w.shape == (2, 9, A_EXPERTS)
393
+ assert (w.abs().sum(-1) > 0).all(), "dense signed dispatch must be alive"
394
+ nn.init.normal_(b.up[0].weight, std=0.02)
395
+ assert not torch.equal(b(x, sf), x)
396
+ m = MonoLora(320)
397
+ assert torch.equal(m(x), x)
398
+ ba = sum(p.numel() for p in AmoeBank(320).parameters())
399
+ mo = sum(p.numel() for p in MonoLora(320).parameters())
400
+ print(f"dexp007 smoke PASSED (bank zero-init/toggle/dispatch; params "
401
+ f"bank {ba:,} vs mono {mo:,} @d=320)")
402
+
403
+
404
+ if __name__ == "__main__":
405
+ if "--run" in sys.argv:
406
+ run()
407
+ else:
408
+ smoke()
exp007_amoe_lora/mono_core_s0.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:45d6562a978296fa9348786f83319d11196e21ead6ab3d0540562a79d2328d17
3
+ size 3265793
exp007_amoe_lora/results.json ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "config": {
3
+ "substrate": "core",
4
+ "A": 4,
5
+ "r": 8,
6
+ "steps": 3000,
7
+ "batch": 16,
8
+ "lr": 0.001,
9
+ "seed": 0,
10
+ "epred": true
11
+ },
12
+ "frozen": {
13
+ "val": 0.1247876692286809
14
+ },
15
+ "amoe": {
16
+ "n_params": 899392,
17
+ "val": 0.12201545690095372,
18
+ "val_toggled_off": 0.1247876692286809,
19
+ "s_per_step": 1.722,
20
+ "usage_by_band": {
21
+ "band0": [
22
+ 0.2544,
23
+ 0.1704,
24
+ 0.3321,
25
+ 0.2424
26
+ ],
27
+ "band1": [
28
+ 0.2549,
29
+ 0.1706,
30
+ 0.3316,
31
+ 0.2421
32
+ ],
33
+ "band2": [
34
+ 0.254,
35
+ 0.1705,
36
+ 0.3323,
37
+ 0.2423
38
+ ],
39
+ "band3": [
40
+ 0.2543,
41
+ 0.1696,
42
+ 0.333,
43
+ 0.2424
44
+ ]
45
+ },
46
+ "band_usage_spread": 0.0009,
47
+ "gates": [
48
+ 0.064,
49
+ 0.0523,
50
+ 0.0644,
51
+ 0.0609
52
+ ]
53
+ },
54
+ "monolith": {
55
+ "n_params": 811216,
56
+ "val": 0.12177696034905239,
57
+ "s_per_step": 1.647
58
+ },
59
+ "verdict": {
60
+ "amoe_vs_monolith": "monolith",
61
+ "amoe_beats_frozen": true,
62
+ "param_ratio": 1.109,
63
+ "band_usage_spread": 0.0009,
64
+ "note": "P1 amoe>=monolith; P2 sigma-band usage differentiation; 1-seed CANDIDATE; first MoE-structure test of the line"
65
+ }
66
+ }