geolip-aleph-diffusion / exp013_blob_flow /dexp013_blob_flow.py
AbstractPhil's picture
exp013 shipped: CONDITIONING HYPOTHESIS CONFIRMED - blob coupling ~200x more effective on flow than eps (prereg 5x bar); blob work migrates to flow/v-pred substrates
e8f08ec verified
Raw
History Blame Contribute Delete
10.5 kB
"""dexp013_blob_flow.py — exp013: BLOB SUPERVISION ON THE FLOW SUBSTRATE
(Phil's hypothesis 2026-07-18: "Blob likely will be less effective for eps
than it will be for flow matching").
Mechanism under test: eps-prediction recovers structure through
x0_hat = (x_t - sqrt(1-abar)*eps_hat)/sqrt(abar) — ILL-CONDITIONED at high
noise (divide by vanishing sqrt(abar)) exactly where blob supervision
applies; exp012 measured the resulting inertia (+0.03%). Rectified flow
recovers x0_hat = x_t - sigma*v_hat — EXACT AND LINEAR at every sigma.
Prediction: the same blob coupling moves the blob gauge substantially more
on the flow substrate.
Substrate: base_lune UNet (flow-trained; its strongest judged arm was nl77)
+ the dexp011 FUSED cache (same SD15 VAE, latents + nl77 ehs + blob targets).
Objective: trainer-verbatim flow (sigma~U -> SHIFT-2.5 warp, x_t =
noise*s + lat*(1-s), v = noise - lat), blob term = foreground-weighted
LP-x0 with x0_hat = x_t - s*v_hat, HIGH band only, lambda 0.5 (exp012-dosed
for direct comparison).
Arms (shared cache): frozen | uniform-mb3-flow | blob-mb3-flow. Gauges:
common flow-MSE per band + the role-aligned foreground-LP-x0 gauge (all
arms, in-bed).
Prereg: P1 blob-vs-uniform blob-gauge movement in the HIGH band >= 5x
exp012's relative +0.03% (i.e. >= +0.15%); P2 common gauge undegraded
(<0.5%); P3 toggle bit-exact; P5 (mechanistic) the blob-gauge SCALE is
better-conditioned: report gauge magnitudes per band vs exp012's.
Pod: bash pod2/run_exp013.sh [DEXP13_SEED=0]
"""
from __future__ import annotations
import json
import os
import sys
import time
sys.path[:0] = ["pod2", "."]
import torch
import torch.nn.functional as F
from pod_ledger import ledger_run, note, burn_down
from d1_substrate import MEM_FRACTION
from dexp008_multiband import (MultibandDelta, band_weights, band_of, attach,
N_BANDS)
from dexp009_bandroles import lp
LUNE_REPO = "AbstractPhil/sd15-flow-lune-flux"
LUNE_SUB = "flux_t2_6_pose_t4_6_port_t1_4/checkpoint-00018765/unet"
SHIFT = 2.5
N_TRAIN, N_VAL = 4096, 256
BATCH = int(os.environ.get("DEXP13_BATCH", "16"))
STEPS = int(os.environ.get("DEXP13_STEPS", "3000"))
SEED = int(os.environ.get("DEXP13_SEED", "0"))
LAM = float(os.environ.get("DEXP13_LAMBDA", "0.5"))
LR, CFG_DROPOUT = 1e-3, 0.1
D11 = ("/workspace/data/dexp011" if os.path.isdir("/workspace")
else "./data/dexp011")
DATA_DIR = ("/workspace/data/dexp013" if os.path.isdir("/workspace")
else os.path.join(os.environ.get("GEOLIP_DATA", "./data"),
"dexp013"))
CKPT_DIR = ("/workspace/ckpts2/dexp013" if os.path.isdir("/workspace")
else DATA_DIR)
def load_lune(device):
from diffusers import UNet2DConditionModel
unet = UNet2DConditionModel.from_pretrained(
LUNE_REPO, subfolder=LUNE_SUB, torch_dtype=torch.float32).to(device)
unet.requires_grad_(False)
unet.eval()
unet.enable_gradient_checkpointing()
return unet
def blob_lp_err(x0_hat, x0, blob):
d2 = (lp(x0_hat) - lp(x0)) ** 2
m = blob[:, None]
denom = m.sum(dim=(1, 2, 3)).clamp_min(1.0) * d2.shape[1]
return (d2 * m).sum(dim=(1, 2, 3)) / denom
def run(device="cuda"):
torch.cuda.set_per_process_memory_fraction(MEM_FRACTION, 0)
os.makedirs(CKPT_DIR, exist_ok=True)
os.makedirs(DATA_DIR, exist_ok=True)
cache = torch.load(os.path.join(D11, "cache.pt"), map_location="cpu",
weights_only=True)
assert int(cache["blob_empty_count"]) == 0, "need the rebuilt blob targets"
# fixed val sigmas for the FLOW substrate (warped uniform grid, paired)
gv = torch.Generator().manual_seed(SEED + 11)
u = torch.linspace(0.02, 0.98, N_VAL)
val_sigma = (SHIFT * u) / (1 + (SHIFT - 1) * u)
def set_w(wraps, s01):
w = band_weights(s01)
for wr in wraps:
wr.w_bands = w
return w
def flow_pieces(lat, s, noise):
s4 = s[:, None, None, None]
return noise * s4 + lat * (1 - s4), noise - lat # x_t, v
def loss_of(unet, wraps, lat, ehs, blob, gen, use_blob):
bsz = lat.shape[0]
drop = torch.rand(bsz, generator=gen, device=device) < CFG_DROPOUT
ehs = ehs.clone()
ehs[drop] = 0
s = torch.rand(bsz, generator=gen, device=device)
s = (SHIFT * s) / (1 + (SHIFT - 1) * s)
w = set_w(wraps, s)
noise = torch.randn(lat.shape, generator=gen, device=device)
x_t, v = flow_pieces(lat, s, noise)
pred = unet(x_t, s * 1000, ehs, return_dict=False)[0]
base = ((pred - v) ** 2).mean(dim=(1, 2, 3))
if not use_blob:
return base.mean()
x0_hat = x_t - s[:, None, None, None] * pred # EXACT linear
blob_term = blob_lp_err(x0_hat, lat, blob)
return (base + LAM * w[:, 2] * blob_term).mean()
@torch.no_grad()
def val(unet, wraps):
tot = []
per_band = {0: [], 1: [], 2: []}
blob_band = {0: [], 1: [], 2: []}
for i in range(0, N_VAL, 32):
lat = cache["val_lat"][i:i + 32].to(device)
ehs = cache["val_ehs"][i:i + 32].to(device)
noise = cache["val_noise"][i:i + 32].to(device)
blob = cache["val_blob"][i:i + 32].float().to(device)
s = val_sigma[i:i + 32].to(device)
w = set_w(wraps, s)
x_t, v = flow_pieces(lat, s, noise)
pred = unet(x_t, s * 1000, ehs, return_dict=False)[0]
mse = ((pred - v) ** 2).mean(dim=(1, 2, 3))
x0_hat = x_t - s[:, None, None, None] * pred
bg = blob_lp_err(x0_hat, lat, blob)
tot += mse.tolist()
for j, sv in enumerate(s.tolist()):
b = band_of(sv)
per_band[b].append(mse[j].item())
blob_band[b].append(bg[j].item())
return (sum(tot) / len(tot),
{f"band{b}": round(sum(v_) / max(len(v_), 1), 6)
for b, v_ in per_band.items()},
{f"band{b}": round(sum(v_) / max(len(v_), 1), 6)
for b, v_ in blob_band.items()})
def train(unet, wraps, params, label, use_blob):
opt = torch.optim.Adam(params, lr=LR, weight_decay=0.0)
gen = torch.Generator(device=device).manual_seed(SEED + 42)
idx = torch.Generator().manual_seed(SEED + 7)
t0 = time.time()
for step in range(1, STEPS + 1):
sel = torch.randint(0, N_TRAIN, (BATCH,), generator=idx)
loss = loss_of(unet, wraps, cache["lat"][sel].to(device),
cache["ehs"][sel].to(device),
cache["blob"][sel].float().to(device), gen,
use_blob)
loss.backward()
opt.step()
opt.zero_grad(set_to_none=True)
if step == 50 or step % 500 == 0:
print(f"[{label}] step {step}: loss {loss.item():.4f} | "
f"{(time.time() - t0) / step:.2f}s/step", flush=True)
results = {"config": {"lambda": LAM, "steps": STEPS, "batch": BATCH,
"seed": SEED, "trunk": "base_lune 18765 (flow)",
"x0_recovery": "x0_hat = x_t - sigma*v (exact)"}}
with ledger_run(f"dexp013 frozen s{SEED}", budget_h=0.2) as h:
unet = load_lune(device)
v, pb, bb = val(unet, [])
results["frozen"] = {"val": v, "per_band": pb, "blob_gauge": bb}
del unet
torch.cuda.empty_cache()
h["verdict"] = f"val {v:.5f}"
for label, use_blob in (("uniform_mb3", False), ("blob_mb3", True)):
with ledger_run(f"dexp013 {label} s{SEED}", budget_h=2.5) as h:
unet = load_lune(device)
mods, wraps = attach(unet, lambda d: MultibandDelta(d))
for m in mods:
m.assert_zero_init()
train(unet, wraps, mods.parameters(), label, use_blob)
v, pb, bb = val(unet, wraps)
for m in mods:
m.enabled = False
v_off, _, _ = val(unet, wraps)
d = abs(v_off - results["frozen"]["val"])
assert d < 1e-9, f"toggle parity broken: {d}"
for m in mods:
m.enabled = True
torch.save({"mods": [m.state_dict() for m in mods]},
os.path.join(CKPT_DIR, f"{label}_s{SEED}.pt"))
results[label] = {"val": v, "per_band": pb, "blob_gauge": bb,
"val_toggled_off": v_off}
del unet, mods
torch.cuda.empty_cache()
h["verdict"] = f"val {v:.5f} blobH {bb['band2']}"
bm, um = results["blob_mb3"], results["uniform_mb3"]
rel_move = (um["blob_gauge"]["band2"] - bm["blob_gauge"]["band2"]) \
/ max(um["blob_gauge"]["band2"], 1e-12)
results["verdict"] = {
"P1_blob_gauge_high_band": {
"blob": bm["blob_gauge"]["band2"],
"uniform": um["blob_gauge"]["band2"],
"relative_move": round(rel_move, 5),
"exp012_reference_move": 0.0003,
"hit_5x": rel_move >= 0.0015},
"P2_common_undegraded":
bm["val"] <= um["val"] * 1.005,
"common_val": {"blob": bm["val"], "uniform": um["val"],
"frozen": results["frozen"]["val"]},
"note": "flow-substrate blob test (Phil's conditioning hypothesis); "
"1-seed CANDIDATE",
}
with open(os.path.join(DATA_DIR, "results.json" if SEED == 0
else f"results_s{SEED}.json"), "w") as f:
json.dump(results, f, indent=2)
note(f"dexp013: {json.dumps(results['verdict']['P1_blob_gauge_high_band'])}")
print(json.dumps(results["verdict"], indent=2))
burn_down()
return results
def smoke():
lat = torch.randn(2, 4, 64, 64)
noise = torch.randn_like(lat)
s = torch.tensor([0.9, 0.2])
s4 = s[:, None, None, None]
x_t = noise * s4 + lat * (1 - s4)
v = noise - lat
x0 = x_t - s4 * v
assert (x0 - lat).abs().max() < 1e-5, "flow x0 recovery must be exact"
print("dexp013 smoke PASSED (exact linear x0 recovery on the flow path)")
if __name__ == "__main__":
if "--run" in sys.argv:
run()
else:
smoke()