vlanext-robolab-droid-eval / code /src /models /test_infer_claims.py
Cocoyawn32's picture
Add files using upload-large-folder tool
3f6e26d verified
Raw
History Blame Contribute Delete
3.99 kB
"""
Verify two claims about TTT inference at the deployment config (chunk_size=256):
CLAIM 1 (per-position independence): with chunk_size >= seq_len, the causal TTT
op does apply-then-update as ONE chunk, so apply uses only the VLM-pre-updated
weights => output[:, j] depends only on q_j and w_vlm, NOT on other positions'
k/v. Test: perturb position p's k/v, check outputs at j != p are unchanged.
CLAIM 2 (time split): in one full-prefix generator forward, how much time is in
the 7 TTT layers vs the 22 attention layers.
Run:
TORCHDYNAMO_DISABLE=1 PYTHONPATH="$PWD" CUDA_VISIBLE_DEVICES=<x> \
/mnt/afs-h200/yuyangcheng/venvs/fla_triton32/bin/python -m src.models.test_infer_claims
"""
import time
import torch
from src.models.ttt import causal_block_fast_weight_swish_glu
dev = "cuda"
def claim1_independence():
print("=== CLAIM 1: chunk=256 per-position independence ===")
B, L, d, dh, T = 4, 256, 64, 64, 16
g = torch.Generator(device=dev).manual_seed(0)
rk = lambda *s: torch.randn(*s, generator=g, device=dev, dtype=torch.float32)
pr = lambda *s: torch.rand(*s, generator=g, device=dev, dtype=torch.float32) * 0.02 + 0.001
w0, w1, w2 = rk(B, d, dh), rk(B, dh, d), rk(B, d, dh)
q, k, v = rk(B, L, d), rk(B, L, d), rk(B, L, d)
lr0, lr1, lr2 = pr(B, L, 1), pr(B, L, 1), pr(B, L, 1)
vk, vv = rk(B, T, d), rk(B, T, d)
vl0, vl1, vl2 = pr(B, T, 1), pr(B, T, 1), pr(B, T, 1)
args = dict(chunk_size=256, muon_update_steps=0, vlm_k=vk, vlm_v=vv,
vlm_lr0=vl0, vlm_lr1=vl1, vlm_lr2=vl2)
o0 = causal_block_fast_weight_swish_glu(w0.clone(), w1.clone(), w2.clone(),
q, k, v, lr0, lr1, lr2, **args)[0]
p = 100
k2, v2 = k.clone(), v.clone()
k2[:, p] += 5.0; v2[:, p] += 5.0
o1 = causal_block_fast_weight_swish_glu(w0.clone(), w1.clone(), w2.clone(),
q, k2, v2, lr0, lr1, lr2, **args)[0]
other0 = torch.cat([o0[:, :p], o0[:, p + 1:]], 1)
other1 = torch.cat([o1[:, :p], o1[:, p + 1:]], 1)
d_other = (other1 - other0).abs().max().item()
print(f" perturb k/v at p={p}: max delta at OTHER positions = {d_other:.2e}")
verdict = ("INDEPENDENT -> claim1 TRUE: chunk=256 has NO cross-position state"
if d_other < 1e-5 else "coupled -> claim1 FALSE")
print(f" => {verdict}")
q3 = q.clone(); q3[:, p] += 5.0
o2 = causal_block_fast_weight_swish_glu(w0.clone(), w1.clone(), w2.clone(),
q3, k, v, lr0, lr1, lr2, **args)[0]
d_p = (o2[:, p] - o0[:, p]).abs().max().item()
d_oth = (torch.cat([o2[:, :p], o2[:, p + 1:]], 1) - other0).abs().max().item()
print(f" perturb q at p: delta@p={d_p:.2e} (want>0), delta elsewhere={d_oth:.2e} (want~0)")
def claim2_timesplit():
print("\n=== CLAIM 2: TTT vs attention full-prefix(256) forward time ===")
from src.models.generator import ImageGeneratorTransformer
for mixer in ["attention", "ttt"]:
torch.manual_seed(0)
gen = ImageGeneratorTransformer(
vocab_size=1024, vlm_hidden_size=512, hidden_size=768, depth=29,
num_heads=12, mixer_type=mixer, mix_every_n=4, ttt_chunk_size=256,
).to(dev, torch.bfloat16).eval()
ids = torch.randint(0, 1024, (1, 256), device=dev)
vlm = [torch.randn(1, 16, 512, device=dev, dtype=torch.bfloat16) for _ in range(29)]
with torch.no_grad():
for _ in range(3):
gen(ids, vlm)
torch.cuda.synchronize(); t = time.time()
for _ in range(20):
gen(ids, vlm)
torch.cuda.synchronize()
ms = (time.time() - t) / 20 * 1000
n_ttt = sum(1 for b in gen.blocks if b.mixer_type == "ttt")
print(f" [{mixer:9s}] {ms:7.2f} ms | {n_ttt} TTT + {29 - n_ttt} attn layers")
del gen; torch.cuda.empty_cache()
if __name__ == "__main__":
claim1_independence()
claim2_timesplit()