Instructions to use AlexWortega/tinyvla with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use AlexWortega/tinyvla with LeRobot:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
File size: 5,611 Bytes
1601a2f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | #!/usr/bin/env python
"""Diagnose the env-obs adapter: compare policy predictions from env-rendered
observations vs dataset observations at the SAME init state.
If pred(dataset obs) is close to GT but pred(env obs) differs, the observation
adapter (image orientation/cameras/state) is the remaining gap.
"""
from __future__ import annotations
import argparse
import numpy as np
import torch
from scipy.spatial.transform import Rotation
@torch.no_grad()
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--checkpoint", default="outputs/libero_ft2/step_15000")
parser.add_argument("--suite", default="libero_spatial")
parser.add_argument("--tasks", type=int, default=3)
args = parser.parse_args()
from lerobot.datasets.lerobot_dataset import LeRobotDataset, LeRobotDatasetMetadata
from lerobot.envs.factory import make_env, make_env_config
from transformers import AutoTokenizer
from tinyvla.data.mixture import CanonicalSource
from tinyvla.modeling_tinyvla import TinyVLAPolicy
policy = TinyVLAPolicy.from_pretrained(args.checkpoint).cuda().eval()
cfg = policy.config
tok = AutoTokenizer.from_pretrained(cfg.lm_model_name)
meta = LeRobotDatasetMetadata("HuggingFaceVLA/libero")
ds = LeRobotDataset(
"HuggingFaceVLA/libero",
delta_timestamps={"action": [t / meta.fps for t in range(cfg.chunk_size)]},
video_backend="torchcodec",
)
src = CanonicalSource(ds, 2, cfg.image_size, cfg.max_state_dim, cfg.max_action_dim)
s_stats = meta.stats["observation.state"]
s_mean = torch.as_tensor(s_stats["mean"]).flatten().float()
s_std = torch.as_tensor(s_stats["std"]).flatten().float().clamp(min=1e-6)
env_cfg = make_env_config("libero", task=args.suite)
task_envs = make_env(env_cfg, n_envs=1)[args.suite]
env_by_task = {}
for tid, env in task_envs.items():
desc = env.get_attr("task_description")[0]
env_by_task[desc.strip().lower()] = (tid, env)
eps_meta = ds.meta.episodes
first_ep_by_task = {}
for ep in range(ds.num_episodes):
start = int(eps_meta["dataset_from_index"][ep])
task = ds[start]["task"].strip().lower()
if task in env_by_task and task not in first_ep_by_task:
first_ep_by_task[task] = ep
def tok_batch(task_text):
t = tok([task_text], padding=True, truncation=True,
max_length=cfg.tokenizer_max_length, return_tensors="pt")
return t["input_ids"].cuda(), t["attention_mask"].bool().cuda()
def env_to_batch(obs, task_text):
imgs = {}
for slot, key in (("cam0", "image"), ("cam1", "image2")):
x = torch.as_tensor(np.asarray(obs["pixels"][key]))[0].flip(0).flip(1)
x = x.permute(2, 0, 1).float() / 255.0
x = torch.nn.functional.interpolate(x[None], size=(cfg.image_size, cfg.image_size),
mode="bilinear", align_corners=False)[0]
imgs[slot] = x
rs = obs["robot_state"]
pos = np.asarray(rs["eef"]["pos"]).flatten()
quat = np.asarray(rs["eef"]["quat"]).flatten()
rotvec = Rotation.from_quat(quat).as_rotvec()
if rotvec[0] < 0:
th = np.linalg.norm(rotvec)
rotvec = rotvec * (th - 2 * np.pi) / th
grip = np.asarray(rs["gripper"]["qpos"]).flatten()
state = torch.tensor(np.concatenate([pos, rotvec, grip]), dtype=torch.float32)
state = (state - s_mean) / s_std
state = torch.nn.functional.pad(state, (0, cfg.max_state_dim - state.shape[-1]))
ids, mask = tok_batch(task_text)
return {
"observation.images.cam0": imgs["cam0"][None].cuda(),
"observation.images.cam1": imgs["cam1"][None].cuda(),
"observation.state": state[None].cuda(),
"observation.language.tokens": ids,
"observation.language.attention_mask": mask,
"embodiment_id": torch.tensor([2], device="cuda"),
}
def ds_to_batch(item):
ids, mask = tok_batch(item.pop("task"))
b = {k: v[None].cuda() for k, v in item.items() if torch.is_tensor(v)}
b["observation.language.tokens"] = ids
b["observation.language.attention_mask"] = mask
return b
for task, ep in list(first_ep_by_task.items())[: args.tasks]:
tid, env = env_by_task[task]
obs, _ = env.reset(seed=0)
start = int(eps_meta["dataset_from_index"][ep])
item = src[start]
gt = item["action"].clone()[None].cuda()
env_b = env_to_batch(obs, task)
ds_b = ds_to_batch(dict(item))
torch.manual_seed(0)
pred_env = policy.predict_action_chunk(env_b)
torch.manual_seed(0)
pred_ds = policy.predict_action_chunk(ds_b)
m = item["action_dim_mask"]
d_env_gt = ((pred_env[0, :, m].cpu() - gt[0, :, m].cpu()) ** 2).mean().sqrt().item()
d_ds_gt = ((pred_ds[0, :, m].cpu() - gt[0, :, m].cpu()) ** 2).mean().sqrt().item()
d_env_ds = ((pred_env[0, :, m] - pred_ds[0, :, m]) ** 2).mean().sqrt().item()
# also compare the raw inputs
img_gap = (env_b["observation.images.cam0"] - ds_b["observation.images.cam0"]).abs().mean().item()
st_gap = (env_b["observation.state"] - ds_b["observation.state"]).abs().max().item()
print(f"task {tid}: RMSE(pred_env, GT)={d_env_gt:.3f} | RMSE(pred_ds, GT)={d_ds_gt:.3f} "
f"| RMSE(pred_env, pred_ds)={d_env_ds:.3f} | img L1 gap={img_gap:.4f} | state max gap={st_gap:.2f}")
if __name__ == "__main__":
main()
|