Upload folder using huggingface_hub
Browse files- README.md +2 -0
- action_head.pt +3 -0
- backbone/config.json +16 -0
- backbone/model.safetensors +3 -0
- backbone/preprocessor_config.json +22 -0
- flock_robotics_adapter.py +119 -0
- vla_config.json +110 -0
README.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Self-contained SigLIP2-base Robotics VLA policy for task 23.
|
| 2 |
+
The adapter loads only the bundled local backbone and action head.
|
action_head.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:30acb580f4bb5273216286eecb0ad380fb52a037c14e4a0df36988969a51f932
|
| 3 |
+
size 2878413
|
backbone/config.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"Siglip2Model"
|
| 4 |
+
],
|
| 5 |
+
"initializer_factor": 1.0,
|
| 6 |
+
"model_type": "siglip2",
|
| 7 |
+
"text_config": {
|
| 8 |
+
"model_type": "siglip2_text_model",
|
| 9 |
+
"vocab_size": 256000
|
| 10 |
+
},
|
| 11 |
+
"torch_dtype": "float32",
|
| 12 |
+
"transformers_version": "4.49.0.dev0",
|
| 13 |
+
"vision_config": {
|
| 14 |
+
"model_type": "siglip2_vision_model"
|
| 15 |
+
}
|
| 16 |
+
}
|
backbone/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ac5f28bbdf92c0c1696ccbd3ce716426049cd67ad8045b66d0d938b0f9c8bbec
|
| 3 |
+
size 1500985224
|
backbone/preprocessor_config.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"do_convert_rgb": null,
|
| 3 |
+
"do_normalize": true,
|
| 4 |
+
"do_rescale": true,
|
| 5 |
+
"do_resize": true,
|
| 6 |
+
"image_mean": [
|
| 7 |
+
0.5,
|
| 8 |
+
0.5,
|
| 9 |
+
0.5
|
| 10 |
+
],
|
| 11 |
+
"image_processor_type": "Siglip2ImageProcessorFast",
|
| 12 |
+
"image_std": [
|
| 13 |
+
0.5,
|
| 14 |
+
0.5,
|
| 15 |
+
0.5
|
| 16 |
+
],
|
| 17 |
+
"max_num_patches": 256,
|
| 18 |
+
"patch_size": 16,
|
| 19 |
+
"processor_class": "Siglip2Processor",
|
| 20 |
+
"resample": 2,
|
| 21 |
+
"rescale_factor": 0.00392156862745098
|
| 22 |
+
}
|
flock_robotics_adapter.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
import hashlib
|
| 3 |
+
import json
|
| 4 |
+
import math
|
| 5 |
+
import re
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import numpy as np
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
|
| 11 |
+
class SigLIPActionHead(nn.Module):
|
| 12 |
+
def __init__(self, config):
|
| 13 |
+
super().__init__()
|
| 14 |
+
self.chunk = int(config["chunk"])
|
| 15 |
+
self.proprio = nn.Sequential(nn.Linear(32, 128), nn.LayerNorm(128), nn.SiLU())
|
| 16 |
+
self.text = nn.Sequential(nn.Linear(128, 128), nn.LayerNorm(128), nn.SiLU())
|
| 17 |
+
self.task = nn.Embedding(int(config["task_count"]), 32)
|
| 18 |
+
self.difficulty = nn.Embedding(int(config["difficulty_count"]), 16)
|
| 19 |
+
self.head = nn.Sequential(
|
| 20 |
+
nn.Linear(768 + 128 + 128 + 32 + 16, 512),
|
| 21 |
+
nn.LayerNorm(512), nn.SiLU(), nn.Dropout(0.10),
|
| 22 |
+
nn.Linear(512, 256), nn.SiLU(), nn.Linear(256, self.chunk * 7)
|
| 23 |
+
)
|
| 24 |
+
def forward(self, features, proprio, text, task_id, difficulty_id):
|
| 25 |
+
fused = torch.cat([
|
| 26 |
+
features.float(), self.proprio(proprio.float()), self.text(text.float()),
|
| 27 |
+
self.task(task_id.clamp(0, self.task.num_embeddings - 1)),
|
| 28 |
+
self.difficulty(difficulty_id.clamp(0, self.difficulty.num_embeddings - 1)),
|
| 29 |
+
], dim=-1)
|
| 30 |
+
return torch.tanh(self.head(fused)).reshape(-1, self.chunk, 7)
|
| 31 |
+
|
| 32 |
+
def _text_vector(text, dim):
|
| 33 |
+
result = np.zeros(dim, dtype=np.float32)
|
| 34 |
+
for token in re.findall(r"[a-z0-9_]+", str(text).lower()):
|
| 35 |
+
value = int.from_bytes(hashlib.blake2b(token.encode("utf-8"), digest_size=8).digest(), "little")
|
| 36 |
+
result[value % dim] += 1.0 if value & 1 else -1.0
|
| 37 |
+
norm = float(np.linalg.norm(result))
|
| 38 |
+
return result / norm if norm else result
|
| 39 |
+
|
| 40 |
+
def _proprio(obs, config):
|
| 41 |
+
value = np.asarray(obs.get("proprio", np.zeros(25, dtype=np.float32)), dtype=np.float32).reshape(-1)
|
| 42 |
+
if value.size == 21:
|
| 43 |
+
value = np.pad(value, (0, 4))
|
| 44 |
+
if value.size != 25:
|
| 45 |
+
raise ValueError(f"proprio must have 25 values, got {value.size}")
|
| 46 |
+
step = float(obs.get("step", 0))
|
| 47 |
+
horizon = float(obs.get("horizon", 320) or 320)
|
| 48 |
+
value = np.concatenate([value, np.asarray([step / max(horizon, 1.0)], dtype=np.float32)])
|
| 49 |
+
value = np.pad(value, (0, max(0, int(config["proprio_dim"]) - value.size)))
|
| 50 |
+
value = value[: int(config["proprio_dim"])]
|
| 51 |
+
mean = np.asarray(config["proprio_mean"], dtype=np.float32)
|
| 52 |
+
std = np.asarray(config["proprio_std"], dtype=np.float32)
|
| 53 |
+
return ((value - mean) / np.maximum(std, 1e-4)).astype(np.float32)
|
| 54 |
+
|
| 55 |
+
class Policy:
|
| 56 |
+
def __init__(self, backbone, processor, head, config, device):
|
| 57 |
+
self.backbone, self.processor = backbone, processor
|
| 58 |
+
self.head, self.config, self.device = head, config, device
|
| 59 |
+
self.chunk = int(config["chunk"])
|
| 60 |
+
self.last_step, self.last_action = None, np.zeros(7, dtype=np.float32)
|
| 61 |
+
self.chunks = {}
|
| 62 |
+
self.task_to_id, self.difficulty_to_id = config["task_to_id"], config["difficulty_to_id"]
|
| 63 |
+
@torch.inference_mode()
|
| 64 |
+
def act(self, obs):
|
| 65 |
+
image = np.asarray(obs.get("image", np.zeros((224, 224, 3), dtype=np.uint8)))
|
| 66 |
+
if image.ndim == 2:
|
| 67 |
+
image = np.repeat(image[..., None], 3, axis=-1)
|
| 68 |
+
if image.shape[-1] == 4:
|
| 69 |
+
image = image[..., :3]
|
| 70 |
+
if image.shape[-1] != 3:
|
| 71 |
+
raise ValueError("image must have 3 or 4 channels")
|
| 72 |
+
step = int(obs.get("step", 0))
|
| 73 |
+
if self.last_step is None or step == 0 or step != self.last_step + 1:
|
| 74 |
+
self.chunks = {}
|
| 75 |
+
if step == self.last_step:
|
| 76 |
+
return self.last_action.copy()
|
| 77 |
+
inputs = self.processor(images=image, return_tensors="pt")
|
| 78 |
+
inputs = {key: value.to(self.device) for key, value in inputs.items()}
|
| 79 |
+
result = self.backbone.get_image_features(**inputs)
|
| 80 |
+
features = result.pooler_output if hasattr(result, "pooler_output") else result.last_hidden_state.mean(dim=1)
|
| 81 |
+
task = str(obs.get("task", ""))
|
| 82 |
+
difficulty = str(obs.get("difficulty", "") or "")
|
| 83 |
+
text = f"task {task} difficulty {difficulty} instruction {obs.get('instruction', '')}"
|
| 84 |
+
raw = self.head(
|
| 85 |
+
features, torch.from_numpy(_proprio(obs, self.config)).unsqueeze(0).to(self.device),
|
| 86 |
+
torch.from_numpy(_text_vector(text, 128)).unsqueeze(0).to(self.device),
|
| 87 |
+
torch.tensor([self.task_to_id.get(task, len(self.task_to_id))], device=self.device),
|
| 88 |
+
torch.tensor([self.difficulty_to_id.get(difficulty, len(self.difficulty_to_id))], device=self.device),
|
| 89 |
+
)[0].float().cpu().numpy()
|
| 90 |
+
self.chunks[step] = raw
|
| 91 |
+
candidates, weights = [], []
|
| 92 |
+
for start, chunk in list(self.chunks.items()):
|
| 93 |
+
offset = step - start
|
| 94 |
+
if 0 <= offset < self.chunk:
|
| 95 |
+
candidates.append(chunk[offset])
|
| 96 |
+
weights.append(math.exp(-0.55 * offset))
|
| 97 |
+
else:
|
| 98 |
+
del self.chunks[start]
|
| 99 |
+
action = np.average(np.asarray(candidates), axis=0, weights=np.asarray(weights))
|
| 100 |
+
action = np.clip(action, -1.0, 1.0).astype(np.float32)
|
| 101 |
+
if action[6] > 0.12:
|
| 102 |
+
action[6] = 1.0
|
| 103 |
+
elif action[6] < -0.12:
|
| 104 |
+
action[6] = -1.0
|
| 105 |
+
self.last_step, self.last_action = step, action.copy()
|
| 106 |
+
return action
|
| 107 |
+
|
| 108 |
+
def load_policy(model_dir: str, device: str, dtype: str):
|
| 109 |
+
from transformers import AutoImageProcessor, AutoModel
|
| 110 |
+
root = Path(model_dir)
|
| 111 |
+
config = json.loads((root / "vla_config.json").read_text())
|
| 112 |
+
selected = torch.device(device if str(device).startswith("cuda") and torch.cuda.is_available() else "cpu")
|
| 113 |
+
processor = AutoImageProcessor.from_pretrained(root / "backbone", local_files_only=True)
|
| 114 |
+
backbone = AutoModel.from_pretrained(root / "backbone", local_files_only=True).to(selected).eval()
|
| 115 |
+
head = SigLIPActionHead(config).to(selected)
|
| 116 |
+
state = torch.load(root / "action_head.pt", map_location=selected, weights_only=True)
|
| 117 |
+
head.load_state_dict(state["state_dict"], strict=True)
|
| 118 |
+
head.eval()
|
| 119 |
+
return Policy(backbone, processor, head, config, selected)
|
vla_config.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema_version": "task23_siglip2_self_contained_v1",
|
| 3 |
+
"model_type": "siglip2_frozen_vision_action_chunk",
|
| 4 |
+
"backbone": "google/siglip2-base-patch16-naflex",
|
| 5 |
+
"chunk": 8,
|
| 6 |
+
"feature_dim": 768,
|
| 7 |
+
"text_dim": 128,
|
| 8 |
+
"proprio_dim": 32,
|
| 9 |
+
"task_count": 14,
|
| 10 |
+
"difficulty_count": 5,
|
| 11 |
+
"proprio_mean": [
|
| 12 |
+
-0.01592460460960865,
|
| 13 |
+
0.7428314685821533,
|
| 14 |
+
0.010429546236991882,
|
| 15 |
+
-1.932236671447754,
|
| 16 |
+
0.024123214185237885,
|
| 17 |
+
2.8178389072418213,
|
| 18 |
+
0.7557806372642517,
|
| 19 |
+
0.02269606851041317,
|
| 20 |
+
0.05282147601246834,
|
| 21 |
+
0.00876456405967474,
|
| 22 |
+
0.07294361293315887,
|
| 23 |
+
-0.021329032257199287,
|
| 24 |
+
-0.015278438106179237,
|
| 25 |
+
0.04549413174390793,
|
| 26 |
+
0.07919428497552872,
|
| 27 |
+
-0.06642947345972061,
|
| 28 |
+
0.9300205707550049,
|
| 29 |
+
0.9961889982223511,
|
| 30 |
+
-0.004495386034250259,
|
| 31 |
+
0.0756562203168869,
|
| 32 |
+
-0.005136882420629263,
|
| 33 |
+
0.03150780126452446,
|
| 34 |
+
-0.03062213398516178,
|
| 35 |
+
0.0001331703970208764,
|
| 36 |
+
3.20150975312572e-05,
|
| 37 |
+
0.4979763627052307,
|
| 38 |
+
0.0,
|
| 39 |
+
0.0,
|
| 40 |
+
0.0,
|
| 41 |
+
0.0,
|
| 42 |
+
0.0,
|
| 43 |
+
0.0
|
| 44 |
+
],
|
| 45 |
+
"proprio_std": [
|
| 46 |
+
0.1833081692457199,
|
| 47 |
+
0.2757302522659302,
|
| 48 |
+
0.0830760970711708,
|
| 49 |
+
0.40317803621292114,
|
| 50 |
+
0.1892702728509903,
|
| 51 |
+
0.21985791623592377,
|
| 52 |
+
0.3677351772785187,
|
| 53 |
+
0.09155969321727753,
|
| 54 |
+
0.24703823029994965,
|
| 55 |
+
0.07063839584589005,
|
| 56 |
+
0.27435028553009033,
|
| 57 |
+
0.12254507839679718,
|
| 58 |
+
0.17527462542057037,
|
| 59 |
+
0.2087806612253189,
|
| 60 |
+
0.09155305474996567,
|
| 61 |
+
0.16622483730316162,
|
| 62 |
+
0.07347064465284348,
|
| 63 |
+
0.004743508528918028,
|
| 64 |
+
0.020507004112005234,
|
| 65 |
+
0.03240346908569336,
|
| 66 |
+
0.01718319021165371,
|
| 67 |
+
0.00835738331079483,
|
| 68 |
+
0.009069605730473995,
|
| 69 |
+
0.014785450883209705,
|
| 70 |
+
0.012382089160382748,
|
| 71 |
+
0.2886733412742615,
|
| 72 |
+
1.0,
|
| 73 |
+
1.0,
|
| 74 |
+
1.0,
|
| 75 |
+
1.0,
|
| 76 |
+
1.0,
|
| 77 |
+
1.0
|
| 78 |
+
],
|
| 79 |
+
"task_to_id": {
|
| 80 |
+
"lift_cube": 0,
|
| 81 |
+
"pick_place_can": 1,
|
| 82 |
+
"pick_place_milk": 2,
|
| 83 |
+
"pick_place_bread": 3,
|
| 84 |
+
"pick_place_cereal": 4,
|
| 85 |
+
"pick_place_clutter": 5,
|
| 86 |
+
"stack_blocks": 6,
|
| 87 |
+
"open_door": 7,
|
| 88 |
+
"nut_assembly": 8,
|
| 89 |
+
"nut_assembly_square": 9,
|
| 90 |
+
"nut_assembly_round": 10,
|
| 91 |
+
"wipe_table": 11,
|
| 92 |
+
"tool_hang": 12
|
| 93 |
+
},
|
| 94 |
+
"difficulty_to_id": {
|
| 95 |
+
"low": 0,
|
| 96 |
+
"medium": 1,
|
| 97 |
+
"hard": 2,
|
| 98 |
+
"very_high": 3
|
| 99 |
+
},
|
| 100 |
+
"local_files_only": true,
|
| 101 |
+
"adapter_inputs": [
|
| 102 |
+
"image",
|
| 103 |
+
"instruction",
|
| 104 |
+
"proprio",
|
| 105 |
+
"task",
|
| 106 |
+
"step",
|
| 107 |
+
"difficulty",
|
| 108 |
+
"horizon"
|
| 109 |
+
]
|
| 110 |
+
}
|