mimicgen_coffee_d0_224x224_dinowm / bench_action_grad.py
chomeed's picture
DINO-WM multi-view dynamics, MimicGen coffee_d0, 60k steps
8acc5ae verified
Raw
History Blame Contribute Delete
3.82 kB
#!/usr/bin/env python
"""Cost of d(loss)/d(action) -- the inner loop of gradient-based planning.
Three regimes:
dino-wm full encode context frames, predict, backprop to the action chunk
dino-wm cached context features precomputed once (what a planner actually does across
CEM/GD iterations, since the context is fixed), predict + backprop only
mlp 3x256 a 3-layer 256-wide state-space dynamics model, (state, action) -> next state
policy_learning/lerobot/.venv/bin/python dynamics_model/bench_action_grad.py
"""
import time
import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import AutoModel
from dino_dynamics import DINO_ID, DinoDynamics
from sd3_dynamics import HORIZON
V, ACT, ST, HIST, SZ = 2, 7, 9, 2, 224
BATCHES = (1, 32, 256, 1024)
REPS = 10
def timeit(fn, reps=REPS, warmup=3):
for _ in range(warmup):
fn()
torch.cuda.synchronize()
t0 = time.time()
for _ in range(reps):
fn()
torch.cuda.synchronize()
return (time.time() - t0) / reps * 1000 # ms
def main():
dev = "cuda"
torch.manual_seed(0)
dino = AutoModel.from_pretrained(DINO_ID, dtype=torch.float32)
model = DinoDynamics(dino, None, V, ACT, ST, history=HIST).to(dev).eval()
for p in model.parameters():
p.requires_grad_(False) # planning optimises actions, not weights
mlp = nn.Sequential(
nn.Linear(ST + HORIZON * ACT, 256), nn.ReLU(),
nn.Linear(256, 256), nn.ReLU(),
nn.Linear(256, 256), nn.ReLU(),
nn.Linear(256, ST),
).to(dev).eval()
for p in mlp.parameters():
p.requires_grad_(False)
print(f"dino-wm predictor params: {sum(p.numel() for p in model.parameters()) / 1e6:.1f}M "
f"(incl. frozen DINOv2) mlp params: {sum(p.numel() for p in mlp.parameters()) / 1e3:.0f}k")
print(f"\n{'batch':>6} {'dino full':>12} {'dino cached':>13} {'mlp 3x256':>11} "
f"{'cached/mlp':>11} {'full/cached':>12}")
for b in BATCHES:
ctx = torch.rand(b, HIST, V, 3, SZ, SZ, device=dev)
state = torch.randn(b, ST, device=dev)
goal = torch.randn(b, V, model.n_patches, model.dim, device=dev)
goal_s = torch.randn(b, ST, device=dev)
def dino_full():
act = torch.randn(b, HORIZON, ACT, device=dev, requires_grad=True)
loss = F.mse_loss(model({"context": ctx, "action": act, "state": state}), goal)
return torch.autograd.grad(loss, act)[0]
# cached: encode once, reuse the features across every planner iteration
with torch.no_grad():
z = model.encode_views(ctx)
def dino_cached():
act = torch.randn(b, HORIZON, ACT, device=dev, requires_grad=True)
loss = F.mse_loss(model.predict_from_features(z, act, state), goal)
return torch.autograd.grad(loss, act)[0]
def mlp_grad():
act = torch.randn(b, HORIZON, ACT, device=dev, requires_grad=True)
loss = F.mse_loss(mlp(torch.cat([state, act.flatten(1)], 1)), goal_s)
return torch.autograd.grad(loss, act)[0]
def safe(fn):
try:
return timeit(fn)
except torch.OutOfMemoryError:
torch.cuda.empty_cache()
return float("nan")
t_full, t_cached, t_mlp = safe(dino_full), safe(dino_cached), safe(mlp_grad)
fmt = lambda x, w, p: ("OOM" if x != x else f"{x:.{p}f}ms").rjust(w)
ratio = lambda x, y: "-" if (x != x or y != y) else f"{x / y:.0f}x"
print(f"{b:>6} {fmt(t_full, 12, 2)} {fmt(t_cached, 13, 2)} {fmt(t_mlp, 11, 3)} "
f"{ratio(t_cached, t_mlp):>11} {ratio(t_full, t_cached):>12}")
del ctx, z
torch.cuda.empty_cache()
if __name__ == "__main__":
main()