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: 6,883 Bytes
b152c62 | 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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | #!/usr/bin/env python
"""Shared direct-to-webdataset writer for converters whose sources are NOT
LeRobot repos (rosbags, hdf5, processed nav parquet). Skips the intermediate
shard-builder layout and emits exactly what pack_wds.py emits:
shard-%05d.tar __key__ / cam0.jpg / cam1.jpg / meta.npz
manifest.json {name, samples, chunk, embodiment_id, norm_stats, shards, ...}
tasks.json {task_index(str) -> instruction}
Normalization contract matches pack_wds.py: state/action are normalized HERE
with per-dataset mean/std computed in the converter's cheap first pass (no
image decoding), and the applied stats land in manifest["norm_stats"].
Converters use:
stats = StatsAccum(); stats.add(states, actions) per episode; st = stats.finalize()
w = WdsWriter(out, name, st, chunk=50, embodiment_id=...)
w.add_episode(ep_idx, states, actions, task_index, cam0_jpegs, cam1_jpegs=None)
w.close(tasks={idx: text}, manifest_extra={...})
"""
from __future__ import annotations
import io
import json
from pathlib import Path
import numpy as np
def encode_jpeg(arr: np.ndarray, size: int = 256, quality: int = 92) -> bytes:
"""Same convention as build_shards_v2.encode_jpeg: plain squash resize."""
from PIL import Image
img = Image.fromarray(arr)
if img.size != (size, size):
img = img.resize((size, size), Image.BILINEAR)
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=quality, subsampling=0)
return buf.getvalue()
class StatsAccum:
"""Pooled mean/std over frames, same math as shards.py stats pooling.
Separate counters: callers may feed different numbers of state rows and
action rows (e.g. VAMOS feeds T states but T*chunk trajectory points)."""
def __init__(self):
self.s_n = self.a_n = 0
self.s_sum = self.s_sq = self.a_sum = self.a_sq = None
def add(self, states: np.ndarray, actions: np.ndarray):
states = np.asarray(states, np.float64)
actions = np.asarray(actions, np.float64)
if self.s_sum is None:
self.s_sum = states.sum(0); self.s_sq = (states ** 2).sum(0)
self.a_sum = actions.sum(0); self.a_sq = (actions ** 2).sum(0)
else:
self.s_sum += states.sum(0); self.s_sq += (states ** 2).sum(0)
self.a_sum += actions.sum(0); self.a_sq += (actions ** 2).sum(0)
self.s_n += len(states)
self.a_n += len(actions)
def finalize(self) -> dict:
sn, an = max(1, self.s_n), max(1, self.a_n)
sm = self.s_sum / sn
am = self.a_sum / an
sv = np.clip(self.s_sq / sn - sm ** 2, 0, None)
av = np.clip(self.a_sq / an - am ** 2, 0, None)
return {
"observation.state": {"mean": sm.tolist(), "std": np.sqrt(sv).tolist()},
"action": {"mean": am.tolist(), "std": np.sqrt(av).tolist()},
"count": int(self.s_n),
}
class WdsWriter:
def __init__(self, out: Path, name: str, stats: dict, chunk: int = 50,
embodiment_id: int = 0, maxcount: int = 3000):
import webdataset as wds
self.out = Path(out)
self.out.mkdir(parents=True, exist_ok=True)
self.name, self.chunk, self.embodiment_id = name, chunk, embodiment_id
self.stats = stats
self.s_mean = np.asarray(stats["observation.state"]["mean"], np.float32)
self.s_std = np.clip(np.asarray(stats["observation.state"]["std"], np.float32), 1e-6, None)
self.a_mean = np.asarray(stats["action"]["mean"], np.float32)
self.a_std = np.clip(np.asarray(stats["action"]["std"], np.float32), 1e-6, None)
self.sink = wds.ShardWriter(str(self.out / "shard-%05d.tar"),
maxcount=maxcount, verbose=0)
self.n = 0
self.episodes = 0
def add_episode(self, ep_idx: int, states, actions, task_index,
cam0_jpegs: list, cam1_jpegs: list | None = None,
action_chunks: np.ndarray | None = None):
"""states f32[T,S], actions f32[T,A], cam0_jpegs list[bytes] of length T.
task_index: one int for the whole episode, or an int array [T] when the
source labels every frame (VAMOS rows). action_chunks f32[T, chunk, A]
overrides the default consecutive-future chunking (used when each frame
carries its own precomputed trajectory).
"""
states = (np.asarray(states, np.float32) - self.s_mean) / self.s_std
actions = (np.asarray(actions, np.float32) - self.a_mean) / self.a_std
T = len(states)
assert len(cam0_jpegs) == T, f"ep {ep_idx}: {len(cam0_jpegs)} jpegs vs {T} frames"
task_per_frame = (np.full(T, task_index, np.int32)
if np.isscalar(task_index) else np.asarray(task_index, np.int32))
S, A, CH = states.shape[1], actions.shape[1], self.chunk
for f in range(T):
if action_chunks is not None:
chunk = ((np.asarray(action_chunks[f], np.float32) - self.a_mean)
/ self.a_std)
is_pad = np.zeros(CH, bool)
else:
avail = min(CH, T - f)
chunk = np.empty((CH, A), np.float32)
chunk[:avail] = actions[f : f + avail]
if avail < CH:
chunk[avail:] = actions[f + avail - 1]
is_pad = np.zeros(CH, bool)
is_pad[avail:] = True
buf = io.BytesIO()
np.savez(buf, state=states[f], action_chunk=chunk, action_is_pad=is_pad,
action_dim=np.int32(A), state_dim=np.int32(S),
task_index=task_per_frame[f],
embodiment_id=np.int32(self.embodiment_id))
cam1 = cam1_jpegs[f] if cam1_jpegs else b""
self.sink.write({"__key__": f"{self.name}_{ep_idx:06d}_{f:05d}",
"cam0.jpg": cam0_jpegs[f], "cam1.jpg": cam1,
"meta.npz": buf.getvalue()})
self.n += 1
self.episodes += 1
def close(self, tasks: dict, manifest_extra: dict | None = None):
self.sink.close()
(self.out / "tasks.json").write_text(
json.dumps({str(k): v for k, v in tasks.items()}, ensure_ascii=False))
(self.out / "manifest.json").write_text(json.dumps({
**(manifest_extra or {}),
"name": self.name, "samples": self.n, "chunk": self.chunk,
"episodes": self.episodes,
"embodiment_id": self.embodiment_id,
"norm_stats": self.stats,
"shards": sorted(p.name for p in self.out.glob("shard-*.tar")),
}, indent=2))
print(f"{self.name}: {self.n:,} семплов, {self.episodes} эпизодов -> "
f"{len(list(self.out.glob('shard-*.tar')))} тарболов в {self.out}")
|