File size: 11,500 Bytes
4ef8b0b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | """geobasin_bed.py — the CE-replacement geometric arm, reconstructed and
properly tested. #TAG:geobasin #TAG:ce_replacement #TAG:loss_campaign
RECONSTRUCTION PROVENANCE (2026-07-25): the Oct '25 arm survives VERBATIM in
the installed package — geofractal.losses.pure_geometric (PureGeometricLoss:
attraction (1-s_y)^2 + repulsion sum(s_c^2) + margin hinge + range clamp;
GeometricPrototypeLoss; HierarchicalGeometricLoss) and the GBC 4-factor
compatibility head in geofractal/model/experiment_geometric_basin.py.
L-031's "term forms NOT recorded" and L-032's "unbuilt" are both CLOSED.
Losses are IMPORTED from the package, never rewritten (reuse law).
THE TEST (the ce_fixedcode lesson: isolate the LOSS by holding the head
identical): all arms share one cosine-anchor score head on the certified
addr_msl64 read — s = normalize(feats); scores_c = (cos(s, A_c)+1)/2,
A: (256 classes x 256), ~param-matched to the ce head (65,536 vs 65,792).
CE arms consume cos*10 as logits (fixed scale, disclosed).
ARMS: geo_ce_scores (CE on the SAME head - the decisive control) | geo_pure
(verbatim, learned anchors) | geo_pure_frozen (L-108 cell) | geo_pure_norep
(attraction+range ONLY - the absolute-only, doctrine-clean variant; repulsion
+margin are roster-comparative terms) | geo_proto (verbatim, own projector -
extra params disclosed) | geo_hier (nibble hierarchy 16x16 - bytes' natural
coarse structure) | geo_hybrid (0.5 CE + 0.5 PureGeometric).
PREREG (3 seeds; baselines ce 2.4769/acc .505): P1 the Oct'25 trade was -12%
relative accuracy - geo_pure within -12% of geo_ce_scores matches history,
parity overturns it, acc<0.30 refutes viability. P2 (L-108): learned anchors
show a collapse signature vs frozen; falsifier: learned > frozen by >2 pts.
P3 (absolute-beats-relative): norep >= pure - noise; falsifier: pure beats
norep by >2 pts (the comparative terms would be load-bearing - a scope
amendment to the law). P4: hybrid bpb within 0.15 of geo_ce_scores.
Run: python tools/geobasin_bed.py --arm <name> --seed N | --list
"""
import json
import math
import os
import sys
import time
import zlib
import torch
import torch.nn as nn
import torch.nn.functional as F
def _root():
d = os.path.abspath(os.getcwd())
while True:
if os.path.exists(os.path.join(d, "MANIFEST.md")):
return d
p = os.path.dirname(d)
if p == d:
return os.getcwd()
d = p
ROOT = _root()
if os.path.join(ROOT, "tools") not in sys.path:
sys.path.insert(0, os.path.join(ROOT, "tools"))
import ar_differentiation_bed as bed # noqa: E402
from geofractal.losses.pure_geometric import ( # noqa: E402
GeometricPrototypeLoss, HierarchicalGeometricLoss, PureGeometricLoss)
from loss_forms import prim_ce # noqa: E402
torch.backends.cuda.matmul.allow_tf32 = False
torch.backends.cudnn.allow_tf32 = False
DEV = "cuda" if torch.cuda.is_available() else "cpu"
if DEV == "cuda":
torch.cuda.set_per_process_memory_fraction(0.73)
DATA_ROOT = os.environ.get("GEOLIP_DATA", "./data")
RUNS = os.path.join(ROOT, "tools", "geobasin_runs")
CE_SCALE = 10.0
def seed_for(name):
return zlib.crc32(name.encode()) & 0x7FFFFFFF
ARMS = ("geo_ce_scores", "geo_pure", "geo_pure_frozen", "geo_pure_norep",
"geo_proto", "geo_hier", "geo_hybrid")
class FastHierarchical(HierarchicalGeometricLoss):
"""Vectorizes ONLY the per-sample fine_mask loop (a 60x Python-loop
slowdown at B*T=8192); every formula identical — parity-asserted against
the verbatim original at build time."""
def forward(self, compatibility_scores, labels, mixed_labels=None,
lam=None):
if mixed_labels is not None:
return super().forward(compatibility_scores, labels,
mixed_labels, lam)
B = compatibility_scores.shape[0]
dev = compatibility_scores.device
sl = labels // self.subclasses_per_super
scores_r = compatibility_scores.view(B, self.num_superclasses,
self.subclasses_per_super)
ss = scores_r.sum(dim=2)
coarse_correct = ss[torch.arange(B, device=dev), sl]
ct = float(self.subclasses_per_super)
coarse_loss = ((coarse_correct - ct) / ct).pow(2).mean()
cmask = torch.ones_like(ss)
cmask[torch.arange(B, device=dev), sl] = 0
coarse_rep = (ss * cmask).pow(2).sum(dim=1).mean()
fine_correct = compatibility_scores[torch.arange(B, device=dev), labels]
fine_loss = (1.0 - fine_correct).pow(2).mean()
# vectorized fine_mask: ones on the label's superclass block, zero at
# the label column (identical to the original's per-sample loop)
cols = (sl * self.subclasses_per_super).unsqueeze(1) + \
torch.arange(self.subclasses_per_super, device=dev).unsqueeze(0)
fmask = torch.zeros_like(compatibility_scores)
fmask.scatter_(1, cols, 1.0)
fmask[torch.arange(B, device=dev), labels] = 0
fine_rep = (compatibility_scores * fmask).pow(2).sum(dim=1).mean()
consistency = F.relu(fine_correct * 2 - coarse_correct).mean()
w_c = torch.sigmoid(self.coarse_weight)
w_f = torch.sigmoid(self.fine_weight)
return (w_c * (coarse_loss + 0.3 * coarse_rep)
+ w_f * (fine_loss + 0.3 * fine_rep) + 0.2 * consistency)
class ScoreHead(nn.Module):
"""The shared cosine-anchor basin head. scores in [0,1] per class."""
def __init__(self, dim=256, classes=256, frozen=False, gen=None):
super().__init__()
A = torch.randn(classes, dim, generator=gen)
if frozen:
self.register_buffer("A", F.normalize(A, dim=-1))
else:
self.A = nn.Parameter(A)
def cos(self, feats):
return F.normalize(feats, dim=-1) @ F.normalize(self.A, dim=-1).t()
def forward(self, feats):
return (self.cos(feats) + 1) / 2
def build(arm, seed):
torch.manual_seed(seed_for(f"geobasin:{arm}:{seed}"))
lm = bed.ByteLM("addr_msl64").to(DEV)
lm.head = nn.Identity() # forward -> 256-d msl feats
gh = torch.Generator().manual_seed(seed_for(f"geobasin-head:{seed}"))
head = ScoreHead(frozen=(arm == "geo_pure_frozen"), gen=gh).to(DEV)
aux = None
if arm == "geo_proto":
aux = GeometricPrototypeLoss(num_classes=256, prototype_dim=64).to(DEV)
elif arm == "geo_hier":
aux = FastHierarchical(num_classes=256, num_superclasses=16).to(DEV)
# parity vs the VERBATIM original on a random batch (reference-check
# pattern): the vectorization must be mathematics-identical
ref = HierarchicalGeometricLoss(num_classes=256,
num_superclasses=16).to(DEV)
ref.load_state_dict(aux.state_dict())
gpar = torch.Generator().manual_seed(seed_for("hier-parity"))
sc = torch.rand(16, 256, generator=gpar).to(DEV)
yy = torch.randint(0, 256, (16,), generator=gpar).to(DEV)
assert torch.allclose(aux(sc, yy), ref(sc, yy), atol=1e-6), \
"FastHierarchical diverged from the verbatim original"
elif arm != "geo_ce_scores":
aux = PureGeometricLoss() # stateless
params = list(lm.parameters()) + list(head.parameters())
if aux is not None:
params += list(aux.parameters())
return lm, head, aux, [p for p in params if p.requires_grad]
def loss_of(arm, head, aux, feats, y):
scores = head(feats).reshape(-1, 256)
yy = y.reshape(-1)
if arm == "geo_ce_scores":
return prim_ce((scores * 2 - 1).reshape(*y.shape, 256) * CE_SCALE, y).mean()
if arm == "geo_hybrid":
ce = prim_ce((scores * 2 - 1).reshape(*y.shape, 256) * CE_SCALE, y).mean()
return 0.5 * ce + 0.5 * PureGeometricLoss()(scores, yy)
if arm == "geo_pure_norep":
B = scores.shape[0]
correct = scores[torch.arange(B, device=scores.device), yy]
attraction = (1.0 - correct).pow(2).mean() # verbatim term 1
rng = F.relu(scores - 1.0).pow(2).mean() \
+ F.relu(-scores).pow(2).mean() # verbatim term 4
return attraction + 0.1 * rng # comparative terms DROPPED
return aux(scores, yy) # verbatim package losses
@torch.no_grad()
def evaluate(head, lm, va, g):
lm.eval()
tot_ce, tot_ok, n = 0.0, 0, 0
for _ in range(8):
x, y = bed._batch(va, 32, 256, DEV, g)
cos = head.cos(lm(x))
lp = F.log_softmax(cos * CE_SCALE, -1)
tot_ce += float(-lp.gather(-1, y.unsqueeze(-1)).sum())
tot_ok += int((cos.argmax(-1) == y).sum())
n += y.numel()
x, _ = bed._batch(va, 8, 256, DEV, g)
_ = lm(x)
v = lm.head_addr.vitals(lm.head_proj(lm._last_h).view(
*lm._last_h.shape[:-1], lm.n_slots, 4))
lm.train()
return (tot_ce / n) / math.log(2), tot_ok / n, v
def train_arm(arm, seed, steps=2000):
assert arm in ARMS, f"unknown arm {arm}"
os.makedirs(RUNS, exist_ok=True)
tr, va = bed._wikitext_bytes(DATA_ROOT)
lm, head, aux, params = build(arm, seed)
g = torch.Generator().manual_seed(seed_for(f"geobasin-data:{arm}:{seed}"))
ge = torch.Generator().manual_seed(seed_for("geobasin-eval"))
opt = torch.optim.Adam(params, lr=3e-4, weight_decay=0.0)
t0 = time.time()
# anchor-collapse gauge: pairwise |cos| spread of A at start vs end
def anchor_spread():
An = F.normalize(head.A.detach(), dim=-1)
pc = (An @ An.t()).abs()
off = pc[~torch.eye(256, dtype=torch.bool, device=pc.device)]
return float(off.mean())
sp0 = anchor_spread()
for step in range(steps):
x, y = bed._batch(tr, 32, 256, DEV, g)
opt.zero_grad(set_to_none=True)
L = loss_of(arm, head, aux, lm(x), y)
L.backward()
opt.step()
if step == 10 and DEV == "cuda":
print(f"[{arm} s{seed}] step10 loss {float(L.detach()):.4f} "
f"peak {torch.cuda.max_memory_allocated()/2**30:.2f}GB",
flush=True)
bpb, acc, vit = evaluate(head, lm, va, ge)
rec = {"arm": arm, "seed": seed, "steps": steps, "bpb": round(bpb, 4),
"decoded_acc": round(acc, 4),
"anchor_abs_cos_mean_init": round(sp0, 4),
"anchor_abs_cos_mean_final": round(anchor_spread(), 4),
"vitals": vit, "wall_s": round(time.time() - t0, 1),
"n_params_extra": sum(p.numel() for p in aux.parameters())
if aux is not None and any(True for _ in aux.parameters()) else 0}
out = os.path.join(RUNS, f"{arm}_s{seed}_t{steps}.jsonl")
with open(out, "a", encoding="utf-8") as f:
f.write(json.dumps(rec) + "\n")
print(f"[DONE {arm} s{seed}] bpb {bpb:.4f} acc {acc:.4f} "
f"anchors |cos| {sp0:.3f}->{rec['anchor_abs_cos_mean_final']:.3f} "
f"({rec['wall_s']}s)", flush=True)
return rec
if __name__ == "__main__":
import argparse
ap = argparse.ArgumentParser()
ap.add_argument("--arm", default=None)
ap.add_argument("--seed", type=int, default=0)
ap.add_argument("--steps", type=int, default=2000)
ap.add_argument("--list", action="store_true")
a, _ = ap.parse_known_args()
if a.list or not a.arm:
print("arms:", " ".join(ARMS))
sys.exit(0)
train_arm(a.arm, a.seed, a.steps)
|