dreamzero / scripts /eval /raptor_multiseed.py
zhicao's picture
Scheduled Commit
ea0c4d8 verified
Raw History Blame Contribute Delete
4.02 kB
#!/usr/bin/env python3
"""Multi-seed open-loop test: is the RAPTOR cascade's error bias or sampling noise?
Samples S action chunks per anchor (different seeds), scores each sample, the per-anchor mean of
samples, and the hold-current-pose baseline, on the anchors of ablation_runs/openloop_compare.
python scripts/eval/raptor_multiseed.py <checkpoint> <out_npz> [seeds=8]
"""
import json, sys, time
from pathlib import Path
import numpy as np, torch
ROOT = Path(__file__).resolve().parents[2]
for sub in ("scripts", "scripts/eval"):
sys.path.insert(0, str(ROOT / sub))
import trex_ablation_eval as tae
import openloop_compare as oc
from groot.vla.model.trex_track_force.runtime import delta_base_to_absolute
from groot.vla.model.trex_track_force.vla import TrexTrackForceVLA
ckpt, out_npz = Path(sys.argv[1]), Path(sys.argv[2]); S = int(sys.argv[3]) if len(sys.argv) > 3 else 8
A = json.load(open("ablation_runs/openloop_compare/anchors.json")); root = A["dataset_root"]; anchors = [(int(e), float(t)) for e, t in A["anchors"]]
device = torch.device("cuda")
model = TrexTrackForceVLA.load_lora(str(ckpt)).to(device=device, dtype=torch.bfloat16).eval(); policy = model.action_head
transform_eval, _ = tae.load_pipeline(ckpt, training=False)
builders = {}; preds, gts, holds = [], [], []
for i, (ep, anchor) in enumerate(anchors):
if ep not in builders: builders[ep] = tae.ChunkBuilder(root, ep)
b = builders[ep]; gt_eef, _ = oc.ground_truth(b, anchor)
raw = b.build(anchor, blocks=1, prompt=oc.episode_task(b), with_deform=False, history_only_video=True)
reference = np.asarray(raw["state.eef62"], np.float64).reshape(-1, 62)[0]; raw.pop("action.eef62")
collated = transform_eval(tae.unsqueeze_dict_values(dict(raw))); batch = {}
for k, v in collated.items():
if torch.is_tensor(v): v = v.to(device=device, dtype=torch.bfloat16) if v.is_floating_point() else v.to(device)
batch[k] = v
t0 = time.perf_counter(); samples = []
for s in range(S):
with torch.inference_mode():
res = policy.sample(batch, seed=500 + i + 1000 * (s + 1), run_force_refinement=True)
samples.append(delta_base_to_absolute(reference, b.stats.denormalize_action(res["action_pred"][0, :16, :62].float().cpu().numpy())))
preds.append(np.stack(samples)); gts.append(gt_eef); holds.append(np.repeat(reference[None], 16, 0))
print(f"[{i + 1}/{len(anchors)}] ep{ep} t={anchor:.2f}s {S} samples in {time.perf_counter() - t0:.1f}s", flush=True)
P = np.stack(preds); G = np.stack(gts); Hd = np.stack(holds) # P: [N,S,16,62]
np.savez(out_npz, pred=P, gt=G, hold=Hd)
e_single = oc.errors(P[:, 0].astype(np.float64), G); e_mean = oc.errors(P.mean(1).astype(np.float64), G); e_hold = oc.errors(Hd, G)
spread = {m: float(np.mean([oc.errors(P[:, s].astype(np.float64), P.mean(1).astype(np.float64))[m].mean() for s in range(S)])) for m in ("pos", "rot", "hand")}
H = np.r_[9:31, 40:62]; Pp = np.r_[0:3, 31:34]
cos = lambda a, b: ((a * b).sum(-1) / (np.linalg.norm(a, axis=-1) * np.linalg.norm(b, axis=-1) + 1e-9))
gdh, gdp = G[:, 15, H] - G[:, 0, H], G[:, 15, Pp] - G[:, 0, Pp]; M = P.mean(1)
print("\n## Multi-seed test on %s (%d anchors x %d samples)\n" % (ckpt, len(anchors), S))
print("| prediction | EEF pos err [cm] | EEF rot err [deg] | hand RMSE [rad] | hand disp cos | EEF disp cos |"); print("|---|---|---|---|---|---|")
for name, e, pr in (("single sample (seed 0)", e_single, P[:, 0]), (f"mean of {S} samples", e_mean, M), ("hold current pose", e_hold, Hd)):
dh, dp = pr[:, 15, H] - pr[:, 0, H], pr[:, 15, Pp] - pr[:, 0, Pp]
print(f"| {name} | {100 * e['pos'].mean():.2f} | {e['rot'].mean():.2f} | {e['hand'].mean():.4f} | {np.nanmean(cos(dh, gdh)):.2f} | {np.nanmean(cos(dp, gdp)):.2f} |")
print(f"\nsample-to-sample spread around the per-anchor mean: pos {100 * spread['pos']:.2f} cm, rot {spread['rot']:.2f} deg, hand {spread['hand']:.4f} rad "
f"(compare: GT motion within a chunk {100 * np.linalg.norm(gdp, axis=-1).mean():.2f} cm)")