exp008 shipped: multiband mechanism certifies (3/3 surgical band lesions; LOW expert wins its band; uniform objective doesn't pay - roles enter exp009)
Browse files- README.md +1 -0
- exp008_multiband/README.md +50 -0
- exp008_multiband/dexp008_multiband.py +370 -0
- exp008_multiband/mb3_s0.pt +3 -0
- exp008_multiband/results.json +99 -0
README.md
CHANGED
|
@@ -75,6 +75,7 @@ resolution) so gaps live in a narrow band — the paired design is load-bearing.
|
|
| 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 |
|
|
|
|
| 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 |
+
| exp008_multiband | band-ASSIGNED experts: **mechanism certifies — 3/3 surgical band lesions (50-200x), LOW-noise expert wins its band; uniform objective doesn't pay (roles enter exp009)** | **shipped (candidate, s0; s1 running)** |
|
| 79 |
| 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)** |
|
| 80 |
| 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)** |
|
| 81 |
|
exp008_multiband/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# exp008_multiband — band-assignment mechanism test (CANDIDATE, s0; s1 running)
|
| 2 |
+
|
| 3 |
+
**Question.** Do sigma-band-ASSIGNED experts (structural cosine-crossfade
|
| 4 |
+
windows, no selection event) become genuine band specialists, and does the
|
| 5 |
+
partition beat a matched monolith? First bed of the multiband stepwise stage
|
| 6 |
+
(stage plan: coarse-to-fine band roles — HIGH diversity/blobbing, MID
|
| 7 |
+
continuity/semantics, LOW fidelity/detail; roles enter as objectives in
|
| 8 |
+
exp009 — this bed certifies the MECHANISM under a uniform objective).
|
| 9 |
+
|
| 10 |
+
**Structure per site (16).** Three rank-16 zero-init deltas (W+b, gates
|
| 11 |
+
−3.0), combined by smooth cosine crossfade windows on s01 = t/1000 (edges
|
| 12 |
+
0.35/0.75, half-width 0.06, sum-to-1 everywhere — positional gating, never
|
| 13 |
+
comparative). Per-band gradients arise from the windows. Falsifier: matched
|
| 14 |
+
rank-48 monolith (param ratio 1.021). Core epred substrate (2-seed
|
| 15 |
+
certified), shared dexp006 cache, 3000 steps, pure Adam wd=0.
|
| 16 |
+
|
| 17 |
+
**Results** (`results.json`):
|
| 18 |
+
|
| 19 |
+
| arm | overall val | band0 (HIGH) | band1 (MID) | band2 (LOW) |
|
| 20 |
+
|---|---|---|---|---|
|
| 21 |
+
| frozen | 0.124788 | 0.256406 | 0.064227 | 0.008726 |
|
| 22 |
+
| multiband3 | 0.122004 | 0.250432 | 0.063046 | **0.008534** |
|
| 23 |
+
| monolith r48 | **0.121736** | **0.249881** | **0.062896** | 0.008539 |
|
| 24 |
+
|
| 25 |
+
| prereg | outcome |
|
| 26 |
+
|---|---|
|
| 27 |
+
| P1 multiband ≥ monolith overall | MISS (monolith by 0.0003) |
|
| 28 |
+
| P2 own-band wins | 1/3 — the LOW-noise expert wins its band |
|
| 29 |
+
| P3 toggle bit-exact | HIT |
|
| 30 |
+
| **P4 band lesions surgical** | **HIT 3/3** — own-band damage 50–200× cross-band (band0 0.0059 vs 0.00003; band1 0.0010 vs 0.000014; band2 0.00016 vs 0.000003) |
|
| 31 |
+
|
| 32 |
+
**Verdict.** THE MECHANISM CERTIFIES: band assignment produces genuine,
|
| 33 |
+
surgically-decoupled band specialists (P4 — the sigma-axis analogue of the
|
| 34 |
+
program's double-dissociation batteries), and the toggle/parity laws hold.
|
| 35 |
+
Under a UNIFORM objective the partition does not yet out-earn shared capacity
|
| 36 |
+
(P1/P2) — consistent with exp007 (2-seed) and the text line's Law 1: the
|
| 37 |
+
payer must be differentiated pressure, not architecture alone. The one
|
| 38 |
+
per-band win is the LOW/fine-detail expert — the first data point for the
|
| 39 |
+
stage's "higher-fidelity distilled expert on low noise" thesis. Measured
|
| 40 |
+
band difficulty spans 30× (0.256/0.064/0.0087) — coarse-to-fine is intrinsic
|
| 41 |
+
to the objective.
|
| 42 |
+
|
| 43 |
+
**Next (per the stage plan).** exp009 attaches ROLE objectives per band
|
| 44 |
+
(LOW: high-frequency-weighted loss; HIGH: coarse-structure weighting; MID:
|
| 45 |
+
standard) on this certified mechanism; exp010 mounts the step-gated
|
| 46 |
+
controller at inference with the generation-side lesion battery.
|
| 47 |
+
|
| 48 |
+
**Caveats.** s0 (s1 replicating now); uniform objective by design; one
|
| 49 |
+
substrate; band edges fixed at 0.35/0.75. Cost ≈ 2.8 GPU-h.
|
| 50 |
+
Checkpoints: `mb3_s0.pt`, `mono48_s0.pt`.
|
exp008_multiband/dexp008_multiband.py
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""dexp008_multiband.py — exp008: MULTIBAND STEPWISE AMoE, the band-assignment
|
| 2 |
+
mechanism test (stage plan: history/plans/2026-07-17_multiband_stage_plan.md).
|
| 3 |
+
|
| 4 |
+
Phil's band-role map (roles enter as OBJECTIVES in exp009; this bed certifies
|
| 5 |
+
the MECHANISM): HIGH (0.75,1] diversity/blobbing | MID (0.35,0.75]
|
| 6 |
+
continuity/semantics | LOW [0,0.35] fidelity/detail.
|
| 7 |
+
|
| 8 |
+
Per site (16): three rank-16 zero-init deltas (W+b, own gates -3.0), combined
|
| 9 |
+
by SMOOTH COSINE CROSSFADE WINDOWS on s01 (width 0.06, sum to 1 everywhere —
|
| 10 |
+
STRUCTURAL/positional gating, dense + differentiable, no selection event).
|
| 11 |
+
Per-band gradients arise from the windows themselves: a sample at s01=0.9
|
| 12 |
+
trains (almost) only the HIGH expert. Falsifier: matched monolith rank-48.
|
| 13 |
+
|
| 14 |
+
Prereg (stage plan):
|
| 15 |
+
P1 multiband >= monolith on overall paired val;
|
| 16 |
+
P2 SPECIALIZATION SIGNATURE — each band expert beats the monolith IN ITS
|
| 17 |
+
OWN band (per-band paired val);
|
| 18 |
+
P3 toggle bit-exact post-train;
|
| 19 |
+
P4 BAND LESION — disabling band-b's experts damages band-b val >> other
|
| 20 |
+
bands (surgical decoupling on the sigma axis).
|
| 21 |
+
Substrate: core epred SD1.5 (2-seed certified). Reuses the dexp006 cache.
|
| 22 |
+
|
| 23 |
+
Pod: bash pod2/run_exp008.sh [DEXP8_SEED=0 DEXP8_STEPS=3000]
|
| 24 |
+
"""
|
| 25 |
+
from __future__ import annotations
|
| 26 |
+
|
| 27 |
+
import json
|
| 28 |
+
import math
|
| 29 |
+
import os
|
| 30 |
+
import sys
|
| 31 |
+
import time
|
| 32 |
+
|
| 33 |
+
sys.path[:0] = ["pod2", "."]
|
| 34 |
+
|
| 35 |
+
import torch
|
| 36 |
+
import torch.nn as nn
|
| 37 |
+
import torch.nn.functional as F
|
| 38 |
+
|
| 39 |
+
from pod_ledger import ledger_run, note, burn_down
|
| 40 |
+
from d1_substrate import enumerate_sd15_sites, MEM_FRACTION
|
| 41 |
+
from dexp006_sd15core_relay import make_schedule, add_noise
|
| 42 |
+
|
| 43 |
+
SD_BASE = "stable-diffusion-v1-5/stable-diffusion-v1-5"
|
| 44 |
+
N_TRAIN, N_VAL = 4096, 256
|
| 45 |
+
RANK, N_BANDS = 16, 3
|
| 46 |
+
BAND_EDGES = (0.35, 0.75) # LOW [0,.35] | MID (.35,.75] | HIGH (.75,1]
|
| 47 |
+
XFADE = 0.06 # cosine crossfade half-width
|
| 48 |
+
BATCH = int(os.environ.get("DEXP8_BATCH", "16"))
|
| 49 |
+
STEPS = int(os.environ.get("DEXP8_STEPS", "3000"))
|
| 50 |
+
SEED = int(os.environ.get("DEXP8_SEED", "0"))
|
| 51 |
+
LR, CFG_DROPOUT = 1e-3, 0.1
|
| 52 |
+
DATA_DIR = ("/workspace/data/dexp008" if os.path.isdir("/workspace")
|
| 53 |
+
else os.path.join(os.environ.get("GEOLIP_DATA", "./data"),
|
| 54 |
+
"dexp008"))
|
| 55 |
+
DEXP6_DIR = ("/workspace/data/dexp006" if os.path.isdir("/workspace")
|
| 56 |
+
else os.path.join(os.environ.get("GEOLIP_DATA", "./data"),
|
| 57 |
+
"dexp006"))
|
| 58 |
+
CKPT_DIR = ("/workspace/ckpts2/dexp008" if os.path.isdir("/workspace")
|
| 59 |
+
else DATA_DIR)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def band_weights(s01: torch.Tensor) -> torch.Tensor:
|
| 63 |
+
"""Smooth structural band windows: (B, 3), sum to 1 everywhere.
|
| 64 |
+
Cosine crossfade of half-width XFADE around each edge — positional
|
| 65 |
+
gating on the sigma axis, never comparative."""
|
| 66 |
+
def ramp(x): # 0 below -XFADE, 1 above +XFADE, smooth
|
| 67 |
+
t = ((x / XFADE).clamp(-1, 1) + 1) / 2
|
| 68 |
+
return 0.5 - 0.5 * torch.cos(t * math.pi)
|
| 69 |
+
e1, e2 = BAND_EDGES
|
| 70 |
+
up1, up2 = ramp(s01 - e1), ramp(s01 - e2)
|
| 71 |
+
low = 1 - up1
|
| 72 |
+
mid = up1 * (1 - up2)
|
| 73 |
+
high = up1 * up2
|
| 74 |
+
return torch.stack([low, mid, high], dim=-1)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def band_of(s01: float) -> int:
|
| 78 |
+
e1, e2 = BAND_EDGES
|
| 79 |
+
return 0 if s01 <= e1 else (1 if s01 <= e2 else 2)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class MultibandDelta(nn.Module):
|
| 83 |
+
"""Three band experts per site, window-combined; per-expert enable flags
|
| 84 |
+
(band lesions) + global enable (toggle law)."""
|
| 85 |
+
|
| 86 |
+
def __init__(self, d: int, r: int = RANK):
|
| 87 |
+
super().__init__()
|
| 88 |
+
self.d, self.r = d, r
|
| 89 |
+
self.down = nn.ModuleList(nn.Linear(d, r, bias=False)
|
| 90 |
+
for _ in range(N_BANDS))
|
| 91 |
+
self.up = nn.ModuleList(nn.Linear(r, d) for _ in range(N_BANDS))
|
| 92 |
+
for dn, up in zip(self.down, self.up):
|
| 93 |
+
nn.init.orthogonal_(dn.weight)
|
| 94 |
+
nn.init.zeros_(up.weight)
|
| 95 |
+
nn.init.zeros_(up.bias)
|
| 96 |
+
self.gates = nn.Parameter(torch.full((N_BANDS,), -3.0))
|
| 97 |
+
self.enabled = True
|
| 98 |
+
self.band_enabled = [True] * N_BANDS
|
| 99 |
+
|
| 100 |
+
def assert_zero_init(self):
|
| 101 |
+
for up in self.up:
|
| 102 |
+
assert up.weight.abs().max().item() == 0.0
|
| 103 |
+
assert up.bias.abs().max().item() == 0.0
|
| 104 |
+
|
| 105 |
+
def forward(self, x, w_bands): # w_bands: (B, 3)
|
| 106 |
+
if not self.enabled:
|
| 107 |
+
return x
|
| 108 |
+
g = torch.sigmoid(self.gates)
|
| 109 |
+
delta = 0
|
| 110 |
+
w = w_bands.view(w_bands.shape[0],
|
| 111 |
+
*([1] * (x.ndim - 2)), N_BANDS)
|
| 112 |
+
for b in range(N_BANDS):
|
| 113 |
+
if not self.band_enabled[b]:
|
| 114 |
+
continue
|
| 115 |
+
delta = delta + g[b] * w[..., b:b + 1] * self.up[b](
|
| 116 |
+
self.down[b](x))
|
| 117 |
+
return x + delta if not isinstance(delta, int) else x
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class MonoDelta(nn.Module):
|
| 121 |
+
def __init__(self, d: int, r: int = RANK * N_BANDS):
|
| 122 |
+
super().__init__()
|
| 123 |
+
self.down = nn.Linear(d, r, bias=False)
|
| 124 |
+
nn.init.orthogonal_(self.down.weight)
|
| 125 |
+
self.up = nn.Linear(r, d)
|
| 126 |
+
nn.init.zeros_(self.up.weight)
|
| 127 |
+
nn.init.zeros_(self.up.bias)
|
| 128 |
+
self.gate = nn.Parameter(torch.tensor(-3.0))
|
| 129 |
+
self.enabled = True
|
| 130 |
+
|
| 131 |
+
def forward(self, x, w_bands=None):
|
| 132 |
+
if not self.enabled:
|
| 133 |
+
return x
|
| 134 |
+
return x + torch.sigmoid(self.gate) * self.up(self.down(x))
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
class BlockWrap(nn.Module):
|
| 138 |
+
def __init__(self, block, mod):
|
| 139 |
+
super().__init__()
|
| 140 |
+
self.block = block
|
| 141 |
+
self.mod = mod
|
| 142 |
+
self.w_bands = None
|
| 143 |
+
|
| 144 |
+
def forward(self, *args, **kwargs):
|
| 145 |
+
out = self.block(*args, **kwargs)
|
| 146 |
+
h = out[0] if isinstance(out, tuple) else out
|
| 147 |
+
h = self.mod(h, self.w_bands)
|
| 148 |
+
return (h,) + out[1:] if isinstance(out, tuple) else h
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def load_unet(device):
|
| 152 |
+
from diffusers import UNet2DConditionModel
|
| 153 |
+
unet = UNet2DConditionModel.from_pretrained(
|
| 154 |
+
SD_BASE, subfolder="unet", torch_dtype=torch.float32).to(device)
|
| 155 |
+
unet.requires_grad_(False)
|
| 156 |
+
unet.eval()
|
| 157 |
+
unet.enable_gradient_checkpointing()
|
| 158 |
+
return unet
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def attach(unet, mk):
|
| 162 |
+
sites = enumerate_sd15_sites(unet)
|
| 163 |
+
assert len(sites) == 16
|
| 164 |
+
mods, wraps = nn.ModuleList(), []
|
| 165 |
+
for name, block, d in sites:
|
| 166 |
+
p0 = next(block.parameters())
|
| 167 |
+
m = mk(d).to(device=p0.device, dtype=p0.dtype)
|
| 168 |
+
w = BlockWrap(block, m)
|
| 169 |
+
parent = unet
|
| 170 |
+
parts = name.split(".")
|
| 171 |
+
for p in parts[:-1]:
|
| 172 |
+
parent = getattr(parent, p) if not p.isdigit() else parent[int(p)]
|
| 173 |
+
if parts[-1].isdigit():
|
| 174 |
+
parent[int(parts[-1])] = w
|
| 175 |
+
else:
|
| 176 |
+
setattr(parent, parts[-1], w)
|
| 177 |
+
mods.append(m)
|
| 178 |
+
wraps.append(w)
|
| 179 |
+
return mods, wraps
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def run(device="cuda"):
|
| 183 |
+
torch.cuda.set_per_process_memory_fraction(MEM_FRACTION, 0)
|
| 184 |
+
os.makedirs(CKPT_DIR, exist_ok=True)
|
| 185 |
+
os.makedirs(DATA_DIR, exist_ok=True)
|
| 186 |
+
acp = make_schedule(device)
|
| 187 |
+
cache_f = os.path.join(DEXP6_DIR, "cache.pt")
|
| 188 |
+
assert os.path.exists(cache_f), "needs the dexp006 cache"
|
| 189 |
+
cache = torch.load(cache_f, map_location="cpu", weights_only=True)
|
| 190 |
+
|
| 191 |
+
def set_w(wraps, s01):
|
| 192 |
+
w = band_weights(s01)
|
| 193 |
+
for wr in wraps:
|
| 194 |
+
wr.w_bands = w
|
| 195 |
+
|
| 196 |
+
def loss_of(unet, wraps, lat, ehs, gen):
|
| 197 |
+
bsz = lat.shape[0]
|
| 198 |
+
drop = torch.rand(bsz, generator=gen, device=device) < CFG_DROPOUT
|
| 199 |
+
ehs = ehs.clone()
|
| 200 |
+
ehs[drop] = 0
|
| 201 |
+
t = torch.randint(0, 1000, (bsz,), generator=gen, device=device)
|
| 202 |
+
set_w(wraps, t.float() / 1000.0)
|
| 203 |
+
noise = torch.randn(lat.shape, generator=gen, device=device)
|
| 204 |
+
pred = unet(add_noise(lat, noise, t, acp), t, ehs,
|
| 205 |
+
return_dict=False)[0]
|
| 206 |
+
return F.mse_loss(pred, noise)
|
| 207 |
+
|
| 208 |
+
@torch.no_grad()
|
| 209 |
+
def val(unet, wraps):
|
| 210 |
+
tot, per_band = [], {0: [], 1: [], 2: []}
|
| 211 |
+
for i in range(0, N_VAL, 32):
|
| 212 |
+
lat = cache["val_lat"][i:i + 32].to(device)
|
| 213 |
+
ehs = cache["val_ehs"][i:i + 32].to(device)
|
| 214 |
+
noise = cache["val_noise"][i:i + 32].to(device)
|
| 215 |
+
t = cache["val_t"][i:i + 32].to(device)
|
| 216 |
+
set_w(wraps, t.float() / 1000.0)
|
| 217 |
+
pred = unet(add_noise(lat, noise, t, acp), t, ehs,
|
| 218 |
+
return_dict=False)[0]
|
| 219 |
+
mse = ((pred - noise) ** 2).mean(dim=(1, 2, 3))
|
| 220 |
+
tot += mse.tolist()
|
| 221 |
+
for j, tv in enumerate((t.float() / 1000.0).tolist()):
|
| 222 |
+
per_band[band_of(tv)].append(mse[j].item())
|
| 223 |
+
return (sum(tot) / len(tot),
|
| 224 |
+
{f"band{b}": round(sum(v) / max(len(v), 1), 6)
|
| 225 |
+
for b, v in per_band.items()},
|
| 226 |
+
{f"band{b}_n": len(v) for b, v in per_band.items()})
|
| 227 |
+
|
| 228 |
+
def train(unet, wraps, params, label):
|
| 229 |
+
opt = torch.optim.Adam(params, lr=LR, weight_decay=0.0)
|
| 230 |
+
gen = torch.Generator(device=device).manual_seed(SEED + 42)
|
| 231 |
+
idx = torch.Generator().manual_seed(SEED + 7)
|
| 232 |
+
torch.cuda.reset_peak_memory_stats()
|
| 233 |
+
t0 = time.time()
|
| 234 |
+
for step in range(1, STEPS + 1):
|
| 235 |
+
sel = torch.randint(0, N_TRAIN, (BATCH,), generator=idx)
|
| 236 |
+
loss = loss_of(unet, wraps, cache["lat"][sel].to(device),
|
| 237 |
+
cache["ehs"][sel].to(device), gen)
|
| 238 |
+
loss.backward()
|
| 239 |
+
opt.step()
|
| 240 |
+
opt.zero_grad(set_to_none=True)
|
| 241 |
+
if step == 50 or step % 500 == 0:
|
| 242 |
+
print(f"[{label}] step {step}: loss {loss.item():.4f} | "
|
| 243 |
+
f"{(time.time() - t0) / step:.2f}s/step | peak "
|
| 244 |
+
f"{torch.cuda.max_memory_allocated() / 2**30:.1f}GB",
|
| 245 |
+
flush=True)
|
| 246 |
+
return round((time.time() - t0) / STEPS, 3)
|
| 247 |
+
|
| 248 |
+
results = {"config": {"bands": 3, "edges": BAND_EDGES, "xfade": XFADE,
|
| 249 |
+
"rank": RANK, "steps": STEPS, "batch": BATCH,
|
| 250 |
+
"lr": LR, "seed": SEED, "substrate": "core epred"}}
|
| 251 |
+
|
| 252 |
+
with ledger_run(f"dexp008 frozen s{SEED}", budget_h=0.2) as h:
|
| 253 |
+
unet = load_unet(device)
|
| 254 |
+
v, pb, pn = val(unet, [])
|
| 255 |
+
results["frozen"] = {"val": v, "per_band": pb, "band_n": pn}
|
| 256 |
+
del unet
|
| 257 |
+
torch.cuda.empty_cache()
|
| 258 |
+
h["verdict"] = f"val {v:.5f}"
|
| 259 |
+
|
| 260 |
+
with ledger_run(f"dexp008 multiband3 s{SEED}", budget_h=2.5) as h:
|
| 261 |
+
unet = load_unet(device)
|
| 262 |
+
mods, wraps = attach(unet, lambda d: MultibandDelta(d))
|
| 263 |
+
for m in mods:
|
| 264 |
+
m.assert_zero_init()
|
| 265 |
+
n_params = sum(p.numel() for p in mods.parameters())
|
| 266 |
+
print(f"[mb3] 16 sites x 3 band experts, {n_params:,} trainable",
|
| 267 |
+
flush=True)
|
| 268 |
+
spd = train(unet, wraps, mods.parameters(), "mb3")
|
| 269 |
+
v_on, pb_on, _ = val(unet, wraps)
|
| 270 |
+
for m in mods: # P3 toggle
|
| 271 |
+
m.enabled = False
|
| 272 |
+
v_off, _, _ = val(unet, wraps)
|
| 273 |
+
d = abs(v_off - results["frozen"]["val"])
|
| 274 |
+
assert d < 1e-9, f"toggle parity broken: {d}"
|
| 275 |
+
for m in mods:
|
| 276 |
+
m.enabled = True
|
| 277 |
+
# P4 band lesions: disable band b everywhere, re-val per band
|
| 278 |
+
lesions = {}
|
| 279 |
+
for b in range(N_BANDS):
|
| 280 |
+
for m in mods:
|
| 281 |
+
m.band_enabled[b] = False
|
| 282 |
+
_, pb_les, _ = val(unet, wraps)
|
| 283 |
+
lesions[f"lesion_band{b}"] = pb_les
|
| 284 |
+
for m in mods:
|
| 285 |
+
m.band_enabled[b] = True
|
| 286 |
+
torch.save({"mods": [m.state_dict() for m in mods]},
|
| 287 |
+
os.path.join(CKPT_DIR, f"mb3_s{SEED}.pt"))
|
| 288 |
+
results["multiband3"] = {
|
| 289 |
+
"n_params": n_params, "val": v_on, "per_band": pb_on,
|
| 290 |
+
"val_toggled_off": v_off, "s_per_step": spd,
|
| 291 |
+
"lesions": lesions,
|
| 292 |
+
"gates": [[round(torch.sigmoid(g).item(), 4)
|
| 293 |
+
for g in m.gates] for m in mods[8:9]][0]}
|
| 294 |
+
del unet, mods
|
| 295 |
+
torch.cuda.empty_cache()
|
| 296 |
+
h["verdict"] = f"val {v_on:.5f}"
|
| 297 |
+
|
| 298 |
+
with ledger_run(f"dexp008 monolith r48 s{SEED}", budget_h=2.5) as h:
|
| 299 |
+
unet = load_unet(device)
|
| 300 |
+
mods, wraps = attach(unet, lambda d: MonoDelta(d))
|
| 301 |
+
n_params = sum(p.numel() for p in mods.parameters())
|
| 302 |
+
print(f"[mono48] {n_params:,} trainable", flush=True)
|
| 303 |
+
spd = train(unet, wraps, mods.parameters(), "mono48")
|
| 304 |
+
v, pb, _ = val(unet, wraps)
|
| 305 |
+
torch.save({"mods": [m.state_dict() for m in mods]},
|
| 306 |
+
os.path.join(CKPT_DIR, f"mono48_s{SEED}.pt"))
|
| 307 |
+
results["monolith"] = {"n_params": n_params, "val": v,
|
| 308 |
+
"per_band": pb, "s_per_step": spd}
|
| 309 |
+
del unet, mods
|
| 310 |
+
torch.cuda.empty_cache()
|
| 311 |
+
h["verdict"] = f"val {v:.5f}"
|
| 312 |
+
|
| 313 |
+
mb, mo = results["multiband3"], results["monolith"]
|
| 314 |
+
per_band_wins = {b: mb["per_band"][b] < mo["per_band"][b]
|
| 315 |
+
for b in ("band0", "band1", "band2")}
|
| 316 |
+
lesion_asym = {}
|
| 317 |
+
for b in range(N_BANDS):
|
| 318 |
+
les = mb["lesions"][f"lesion_band{b}"]
|
| 319 |
+
own = les[f"band{b}"] - mb["per_band"][f"band{b}"]
|
| 320 |
+
other = sum(les[f"band{o}"] - mb["per_band"][f"band{o}"]
|
| 321 |
+
for o in range(N_BANDS) if o != b) / (N_BANDS - 1)
|
| 322 |
+
lesion_asym[f"band{b}"] = {"own_damage": round(own, 6),
|
| 323 |
+
"other_damage": round(other, 6),
|
| 324 |
+
"surgical": own > 3 * abs(other) and own > 0}
|
| 325 |
+
results["verdict"] = {
|
| 326 |
+
"P1_multiband_vs_monolith": "multiband" if mb["val"] <= mo["val"]
|
| 327 |
+
else "monolith",
|
| 328 |
+
"P2_per_band_wins": per_band_wins,
|
| 329 |
+
"P4_lesion_asymmetry": lesion_asym,
|
| 330 |
+
"param_ratio": round(mb["n_params"] / mo["n_params"], 3),
|
| 331 |
+
"note": f"seed {SEED}; mechanism test — roles enter in exp009",
|
| 332 |
+
}
|
| 333 |
+
with open(os.path.join(DATA_DIR, "results.json" if SEED == 0
|
| 334 |
+
else f"results_s{SEED}.json"), "w") as f:
|
| 335 |
+
json.dump(results, f, indent=2)
|
| 336 |
+
note(f"dexp008 verdict: {json.dumps(results['verdict'])}")
|
| 337 |
+
print(json.dumps(results["verdict"], indent=2))
|
| 338 |
+
burn_down()
|
| 339 |
+
return results
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
def smoke():
|
| 343 |
+
w = band_weights(torch.tensor([0.1, 0.35, 0.55, 0.75, 0.9, 0.72, 0.78]))
|
| 344 |
+
assert w.shape == (7, 3)
|
| 345 |
+
assert torch.allclose(w.sum(-1), torch.ones(7), atol=1e-6), "windows must sum to 1"
|
| 346 |
+
assert w[0].argmax() == 0 and w[2].argmax() == 1 and w[4].argmax() == 2
|
| 347 |
+
m = MultibandDelta(320)
|
| 348 |
+
m.assert_zero_init()
|
| 349 |
+
x = torch.randn(2, 9, 320)
|
| 350 |
+
wb = band_weights(torch.tensor([0.2, 0.9]))
|
| 351 |
+
assert torch.equal(m(x, wb), x), "zero-init must be exact"
|
| 352 |
+
m.enabled = False
|
| 353 |
+
assert m(x, wb) is x
|
| 354 |
+
m.enabled = True
|
| 355 |
+
m.band_enabled = [False, False, False]
|
| 356 |
+
assert m(x, wb) is x, "all-band lesion must be code-path exact"
|
| 357 |
+
m.band_enabled = [True] * 3
|
| 358 |
+
nn.init.normal_(m.up[0].weight, std=0.02)
|
| 359 |
+
assert not torch.equal(m(x, wb), x)
|
| 360 |
+
mb = sum(p.numel() for p in MultibandDelta(320).parameters())
|
| 361 |
+
mo = sum(p.numel() for p in MonoDelta(320).parameters())
|
| 362 |
+
print(f"dexp008 smoke PASSED (windows sum-1 + band argmax, zero-init, "
|
| 363 |
+
f"toggle, lesion path; params mb {mb:,} vs mono {mo:,} @d=320)")
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
if __name__ == "__main__":
|
| 367 |
+
if "--run" in sys.argv:
|
| 368 |
+
run()
|
| 369 |
+
else:
|
| 370 |
+
smoke()
|
exp008_multiband/mb3_s0.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9537d2ae03e90ac8aa9fe559be1dc882660d5a47ef04f7f5683d788427b06ae1
|
| 3 |
+
size 4988505
|
exp008_multiband/results.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"config": {
|
| 3 |
+
"bands": 3,
|
| 4 |
+
"edges": [
|
| 5 |
+
0.35,
|
| 6 |
+
0.75
|
| 7 |
+
],
|
| 8 |
+
"xfade": 0.06,
|
| 9 |
+
"rank": 16,
|
| 10 |
+
"steps": 3000,
|
| 11 |
+
"batch": 16,
|
| 12 |
+
"lr": 0.001,
|
| 13 |
+
"seed": 0,
|
| 14 |
+
"substrate": "core epred"
|
| 15 |
+
},
|
| 16 |
+
"frozen": {
|
| 17 |
+
"val": 0.1247876692286809,
|
| 18 |
+
"per_band": {
|
| 19 |
+
"band0": 0.256406,
|
| 20 |
+
"band1": 0.064227,
|
| 21 |
+
"band2": 0.008726
|
| 22 |
+
},
|
| 23 |
+
"band_n": {
|
| 24 |
+
"band0_n": 98,
|
| 25 |
+
"band1_n": 98,
|
| 26 |
+
"band2_n": 60
|
| 27 |
+
}
|
| 28 |
+
},
|
| 29 |
+
"multiband3": {
|
| 30 |
+
"n_params": 1235568,
|
| 31 |
+
"val": 0.12200367445166194,
|
| 32 |
+
"per_band": {
|
| 33 |
+
"band0": 0.250432,
|
| 34 |
+
"band1": 0.063046,
|
| 35 |
+
"band2": 0.008534
|
| 36 |
+
},
|
| 37 |
+
"val_toggled_off": 0.1247876692286809,
|
| 38 |
+
"s_per_step": 1.685,
|
| 39 |
+
"lesions": {
|
| 40 |
+
"lesion_band0": {
|
| 41 |
+
"band0": 0.256343,
|
| 42 |
+
"band1": 0.063105,
|
| 43 |
+
"band2": 0.008534
|
| 44 |
+
},
|
| 45 |
+
"lesion_band1": {
|
| 46 |
+
"band0": 0.250449,
|
| 47 |
+
"band1": 0.06408,
|
| 48 |
+
"band2": 0.008546
|
| 49 |
+
},
|
| 50 |
+
"lesion_band2": {
|
| 51 |
+
"band0": 0.250432,
|
| 52 |
+
"band1": 0.063052,
|
| 53 |
+
"band2": 0.008691
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
"gates": [
|
| 57 |
+
0.069,
|
| 58 |
+
0.0608,
|
| 59 |
+
0.0583
|
| 60 |
+
]
|
| 61 |
+
},
|
| 62 |
+
"monolith": {
|
| 63 |
+
"n_params": 1210576,
|
| 64 |
+
"val": 0.12173647676991095,
|
| 65 |
+
"per_band": {
|
| 66 |
+
"band0": 0.249881,
|
| 67 |
+
"band1": 0.062896,
|
| 68 |
+
"band2": 0.008539
|
| 69 |
+
},
|
| 70 |
+
"s_per_step": 1.65
|
| 71 |
+
},
|
| 72 |
+
"verdict": {
|
| 73 |
+
"P1_multiband_vs_monolith": "monolith",
|
| 74 |
+
"P2_per_band_wins": {
|
| 75 |
+
"band0": false,
|
| 76 |
+
"band1": false,
|
| 77 |
+
"band2": true
|
| 78 |
+
},
|
| 79 |
+
"P4_lesion_asymmetry": {
|
| 80 |
+
"band0": {
|
| 81 |
+
"own_damage": 0.005911,
|
| 82 |
+
"other_damage": 2.9e-05,
|
| 83 |
+
"surgical": true
|
| 84 |
+
},
|
| 85 |
+
"band1": {
|
| 86 |
+
"own_damage": 0.001034,
|
| 87 |
+
"other_damage": 1.4e-05,
|
| 88 |
+
"surgical": true
|
| 89 |
+
},
|
| 90 |
+
"band2": {
|
| 91 |
+
"own_damage": 0.000157,
|
| 92 |
+
"other_damage": 3e-06,
|
| 93 |
+
"surgical": true
|
| 94 |
+
}
|
| 95 |
+
},
|
| 96 |
+
"param_ratio": 1.021,
|
| 97 |
+
"note": "seed 0; mechanism test \u2014 roles enter in exp009"
|
| 98 |
+
}
|
| 99 |
+
}
|