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:
- Notebooks
- Google Colab
- Kaggle
| #!/usr/bin/env python | |
| """Grow a checkpoint's embodiment/state/action dimensions without losing what it learned. | |
| Adding the unitree + navigation packs widens three things: | |
| max_state_dim 16 -> 32 (G1 Dex3 has 28 joint channels) | |
| max_action_dim 8 -> 32 (same, and nav actions are 3-dim) | |
| num_embodiments 16 -> 24 (packed ids reach 19) | |
| Every affected weight is grown by ZERO padding, never re-initialised: | |
| state_proj.weight (d, 16) -> (d, 32) new input columns = 0 | |
| action_in.weight (d, 8) -> (d, 32) new input columns = 0 | |
| action_out.weight (8, d) -> (32, d) new output rows = 0 | |
| action_out.bias (8,) -> (32,) new outputs = 0 | |
| embodiment emb (16, d) -> (24, d) new rows = 0 | |
| Zeroed input columns receive zero-padded inputs anyway, and zeroed output rows | |
| predict 0 for dims no old robot had — so on the OLD data the expanded model is | |
| bit-for-bit the old model, and the new dims start from a clean slate. That makes | |
| "did the new data help or hurt?" answerable, which a re-initialised head would not. | |
| Usage: | |
| python scripts/expand_checkpoint.py outputs/tv2_C_scaled/final outputs/tv2_C_scaled_x32 | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| import torch | |
| from safetensors.torch import load_file, save_file | |
| # param name suffix -> axis that must grow (0 = rows/out, 1 = cols/in) | |
| GROW = { | |
| "state_proj.weight": 1, | |
| "action_in.weight": 1, | |
| "action_out.weight": 0, | |
| "action_out.bias": 0, | |
| } | |
| def main(): | |
| src = Path(sys.argv[1]) | |
| dst = Path(sys.argv[2]) | |
| new_state = int(sys.argv[3]) if len(sys.argv) > 3 else 32 | |
| new_action = int(sys.argv[4]) if len(sys.argv) > 4 else 32 | |
| new_emb = int(sys.argv[5]) if len(sys.argv) > 5 else 24 | |
| dst.mkdir(parents=True, exist_ok=True) | |
| for f in src.iterdir(): | |
| if f.suffix in (".json", ".txt") or f.name.startswith("README"): | |
| shutil.copy(f, dst / f.name) | |
| cfg_path = dst / "config.json" | |
| cfg = json.loads(cfg_path.read_text()) | |
| old_state = cfg.get("max_state_dim", 16) | |
| old_action = cfg.get("max_action_dim", 8) | |
| old_emb = cfg.get("num_embodiments", 16) | |
| cfg["max_state_dim"] = new_state | |
| cfg["max_action_dim"] = new_action | |
| cfg["num_embodiments"] = new_emb | |
| # input/output_features carry their own copy of the dims. Leaving them stale | |
| # makes the checkpoint load with a 32-wide expert but an 8-wide declared | |
| # action, so anything reading the feature spec silently truncates. | |
| if "action" in cfg.get("output_features", {}): | |
| cfg["output_features"]["action"]["shape"] = [new_action] | |
| if "observation.state" in cfg.get("input_features", {}): | |
| cfg["input_features"]["observation.state"]["shape"] = [new_state] | |
| cfg_path.write_text(json.dumps(cfg, indent=2)) | |
| print(f"config: state {old_state}->{new_state} action {old_action}->{new_action} " | |
| f"embodiments {old_emb}->{new_emb}") | |
| weights = src / "model.safetensors" | |
| sd = load_file(str(weights)) | |
| grown = [] | |
| for k, v in list(sd.items()): | |
| target = None | |
| for suffix, axis in GROW.items(): | |
| if k.endswith(suffix): | |
| want = new_state if "state_proj" in k else new_action | |
| if v.shape[axis] < want: | |
| target = (axis, want) | |
| break | |
| # embodiment table: a 2-D embedding whose row count equals num_embodiments | |
| if target is None and v.dim() == 2 and v.shape[0] == old_emb and "emb" in k.lower(): | |
| target = (0, new_emb) | |
| if target is None: | |
| continue | |
| axis, want = target | |
| shape = list(v.shape) | |
| pad = want - shape[axis] | |
| if pad <= 0: | |
| continue | |
| shape[axis] = pad | |
| sd[k] = torch.cat([v, v.new_zeros(*shape)], dim=axis) | |
| grown.append(f" {k}: {tuple(v.shape)} -> {tuple(sd[k].shape)}") | |
| save_file(sd, str(dst / "model.safetensors"), metadata={"format": "pt"}) | |
| print("grown tensors:" if grown else "nothing to grow (already wide enough)") | |
| print("\n".join(grown)) | |
| print(f"wrote {dst}") | |
| if __name__ == "__main__": | |
| main() | |