Yashp2003's picture
download
raw
8.99 kB
#!/usr/bin/env python3
"""DiffThinker reproduction on HF Jobs (T4).
Reproduces Claims 4-6 with toy-scale Flow Matching + CFG ablation + data scaling.
"""
import json, os, sys, time, io, base64
import numpy as np
from PIL import Image, ImageDraw
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
gpu_name = torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU"
gpu_mem = torch.cuda.get_device_properties(0).total_memory / 1e9 if torch.cuda.is_available() else 0
print(f"GPU: {gpu_name} | VRAM: {gpu_mem:.1f}GB")
print(f"PyTorch: {torch.__version__}")
def make_maze(grid_size=8, num=50):
data = []
for _ in range(num):
g = np.zeros((grid_size, grid_size), dtype=np.uint8)
s = (0, np.random.randint(0, grid_size))
goal = (grid_size-1, np.random.randint(0, grid_size))
for _ in range(int(grid_size*grid_size*0.15)):
wx, wy = np.random.randint(1, grid_size-1, 2)
g[wx, wy] = 1
inp = Image.new("RGB", (64, 64), (255, 255, 255))
d = ImageDraw.Draw(inp)
cw = 64 // grid_size
for r in range(grid_size):
for c in range(grid_size):
if g[r, c] == 1:
d.rectangle([c*cw, r*cw, (c+1)*cw, (r+1)*cw], fill=(100,)*3)
d.rectangle([s[1]*cw, s[0]*cw, (s[1]+1)*cw, (s[0]+1)*cw], fill=(0, 255, 0))
d.rectangle([goal[1]*cw, goal[0]*cw, (goal[1]+1)*cw, (goal[0]+1)*cw], fill=(255, 0, 0))
out = Image.new("RGB", (64, 64), (255, 255, 255))
d = ImageDraw.Draw(out)
for r in range(grid_size):
for c in range(grid_size):
if g[r, c] == 1:
d.rectangle([c*cw, r*cw, (c+1)*cw, (r+1)*cw], fill=(100,)*3)
d.rectangle([s[1]*cw, s[0]*cw, (s[1]+1)*cw, (s[0]+1)*cw], fill=(0, 255, 0))
d.rectangle([goal[1]*cw, goal[0]*cw, (goal[1]+1)*cw, (goal[0]+1)*cw], fill=(255, 0, 0))
py = np.linspace(s[0], goal[0], grid_size).astype(int)
px = np.linspace(s[1], goal[1], grid_size).astype(int)
for x, y in zip(px, py):
if 0 <= x < grid_size and 0 <= y < grid_size and g[y, x] != 1:
d.rectangle([x*cw, y*cw, (x+1)*cw, (y+1)*cw], fill=(0, 0, 255))
data.append({"input": inp, "output": out})
return data
class SimpleDiT(nn.Module):
def __init__(self):
super().__init__()
self.time_embed = nn.Sequential(nn.Linear(1, 64), nn.SiLU(), nn.Linear(64, 64))
self.time_proj = nn.Linear(64, 3)
self.cond_encoder = nn.Sequential(nn.Conv2d(3, 16, 3, padding=1), nn.SiLU(), nn.Conv2d(16, 32, 3, padding=1), nn.SiLU(), nn.Conv2d(32, 64, 3, padding=1))
self.down1 = nn.Conv2d(6, 32, 3, padding=1)
self.down2 = nn.Conv2d(32, 64, 3, stride=2, padding=1)
self.down3 = nn.Conv2d(64, 64, 3, stride=2, padding=1)
self.mid = nn.Sequential(nn.Conv2d(64, 64, 3, padding=1), nn.SiLU(), nn.Conv2d(64, 64, 3, padding=1))
self.up3 = nn.ConvTranspose2d(64, 64, 4, stride=2, padding=1)
self.up2 = nn.ConvTranspose2d(64, 32, 4, stride=2, padding=1)
self.up1 = nn.Conv2d(32, 3, 3, padding=1)
def forward(self, x_t, t, cond):
B = x_t.shape[0]
t_emb = self.time_embed(t.view(-1, 1).float() / 100.0)
t_emb = self.time_proj(t_emb).view(B, -1, 1, 1).expand(-1, -1, 64, 64)
h = torch.cat([x_t, t_emb], dim=1)
c = self.cond_encoder(cond)
c = F.interpolate(c, size=(64, 64), mode='bilinear', align_corners=False)
h = self.down1(h)
h = self.down2(h)
h = self.down3(h)
h = self.mid(h)
h = self.up3(h)
h = self.up2(h)
h = self.up1(h)
return h
class TaskDataset(Dataset):
def __init__(self, d):
self.d = d
def __len__(self):
return len(self.d)
def __getitem__(self, idx):
item = self.d[idx]
i = torch.tensor(np.array(item["input"]).transpose(2,0,1), dtype=torch.float32) / 255.0
o = torch.tensor(np.array(item["output"]).transpose(2,0,1), dtype=torch.float32) / 255.0
return i, o
print("\n=== DiffThinker Reproduction on HF Jobs ===\n")
# 1. Flow Matching training + CFG sweep
print("Claim 4: Flow Matching reformulation (training + inference)")
train_samples = 200
test_samples = 20
train_data = make_maze(8, train_samples)
test_data = make_maze(8, test_samples)
train_loader = DataLoader(TaskDataset(train_data), batch_size=4, shuffle=True)
test_dataset = TaskDataset(test_data)
model = SimpleDiT().to(device)
opt = torch.optim.AdamW(model.parameters(), lr=1e-4)
params = sum(p.numel() for p in model.parameters())
print(f"Model params: {params:,}")
print(f"Training samples: {train_samples}, Test samples: {test_samples}")
epochs = 40
losses = []
for ep in range(epochs):
model.train()
el = 0.0
for cond, target in train_loader:
cond, target = cond.to(device), target.to(device)
t = torch.randint(0, 100, (cond.shape[0],), device=device)
noise = torch.randn_like(target)
a = t.view(-1,1,1,1).float() / 100.0
xt = (1 - a) * target + a * noise
v_pred = model(xt, t, cond)
loss = F.mse_loss(v_pred, noise - target)
opt.zero_grad()
loss.backward()
opt.step()
el += loss.item()
avg = el / len(train_loader)
losses.append(avg)
if (ep+1) % 5 == 0:
print(f" Epoch {ep+1}/{epochs} | Loss: {avg:.6f}")
print(f"Final loss: {losses[-1]:.6f}")
print(f"Loss trajectory (first): {losses[0]:.6f} -> {losses[-1]:.6f}")
# 2. CFG sweep (Claim 6)
print("\nClaim 6: CFG ablation")
model.eval()
cfg_results = {}
for w in [1.0, 2.0, 4.0, 7.0]:
correct = 0
lats = []
for item in test_dataset:
inp = item[0].unsqueeze(0).to(device)
t_np = item[1].numpy().transpose(1,2,0) * 255
with torch.no_grad():
x = torch.randn(1, 3, 64, 64, device=device)
t0 = time.time()
for step in range(20):
tv = torch.full((1,), step * 5, device=device, dtype=torch.long)
vc = model(x, tv, inp)
vu = model(x, tv, torch.zeros_like(inp))
x = x + (1.0/20) * (vu + w * (vc - vu))
torch.cuda.synchronize()
lats.append(time.time() - t0)
out = (x.squeeze(0).cpu().numpy().transpose(1,2,0) * 255).clip(0, 255).astype(np.uint8)
mse = np.mean((out.astype(float) - t_np.astype(float))**2)
if mse < 500:
correct += 1
acc = correct / len(test_dataset) * 100
avg_lat = sum(lats) / len(lats)
cfg_results[f"w={w}"] = {"accuracy": acc, "avg_latency_s": avg_lat, "correct": correct, "total": len(test_dataset)}
print(f" CFG w={w}: acc={acc:.1f}% ({correct}/{len(test_dataset)}), lat={avg_lat:.3f}s")
# 3. Data scaling (Claim 6)
print("\nClaim 6: Data scaling ablation")
test_data2 = make_maze(8, 10)
test_dataset2 = TaskDataset(test_data2)
for sz in [20, 50, 100]:
train = make_maze(8, sz)
train_loader2 = DataLoader(TaskDataset(train), batch_size=4, shuffle=True)
m = SimpleDiT().to(device)
o = torch.optim.AdamW(m.parameters(), lr=1e-4)
for ep in range(15):
m.train()
el = 0.0
for cond, target in train_loader2:
cond, target = cond.to(device), target.to(device)
t = torch.randint(0, 100, (cond.shape[0],), device=device)
noise = torch.randn_like(target)
a = t.view(-1,1,1,1).float() / 100.0
xt = (1 - a) * target + a * noise
v_pred = m(xt, t, cond)
loss = F.mse_loss(v_pred, noise - target)
o.zero_grad()
loss.backward()
o.step()
el += loss.item()
m.eval()
c = 0
for item in test_dataset2:
inp = item[0].unsqueeze(0).to(device)
tn = item[1].numpy().transpose(1,2,0) * 255
with torch.no_grad():
x = torch.randn(1, 3, 64, 64, device=device)
for step in range(20):
tv = torch.full((1,), step * 5, device=device, dtype=torch.long)
vc = m(x, tv, inp)
vu = m(x, tv, torch.zeros_like(inp))
x = x + (1.0/20) * (vu + 4.0 * (vc - vu))
out = (x.squeeze(0).cpu().numpy().transpose(1,2,0) * 255).clip(0, 255).astype(np.uint8)
mse = np.mean((out.astype(float) - tn.astype(float))**2)
if mse < 500:
c += 1
print(f" N={sz}: acc={c/len(test_dataset2)*100:.1f}% ({c}/{len(test_dataset2)})")
print("\n=== Summary ===")
print(json.dumps({
"model_params": params,
"final_loss": losses[-1],
"loss_trajectory": [round(l, 4) for l in [losses[0], losses[-1]]],
"cfg_results": {k: {sk: sv for sk, sv in v.items()} for k, v in cfg_results.items()},
"gpu": gpu_name,
"vram_gb": round(gpu_mem, 1),
}, indent=2))
print("\n=== Done ===")

Xet Storage Details

Size:
8.99 kB
·
Xet hash:
3d71f37d6f5b355f9b08ce097defc6ff269e91f1d44d331f9b139946ded32b9e

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.