mirror of
https://github.com/huggingface/lerobot.git
synced 2026-07-06 17:41:47 +00:00
more changes
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
python src/lerobot/processor/migrate_policy_normalization.py \
|
||||
--pretrained-path /raid/jade/models/xvla-libero-og \
|
||||
--output-dir /raid/jade/models/xvla-libero-og-migrated \
|
||||
--branch main
|
||||
@@ -2,6 +2,7 @@ lerobot-eval \
|
||||
--policy.path="/raid/jade/models/xvla-libero-og_migrated" \
|
||||
--env.type=libero \
|
||||
--env.task=libero_spatial \
|
||||
--env.action_type=abs \
|
||||
--eval.batch_size=1 \
|
||||
--eval.n_episodes=1 \
|
||||
--seed=142
|
||||
|
||||
@@ -56,4 +56,3 @@ target_eef = action[:, :3].to("cpu").numpy()
|
||||
target_axis = Rotate6D_to_AxisAngle(action[:, 3:9].to("cpu").numpy())
|
||||
target_act = action[:, 9:10].to("cpu").numpy()
|
||||
final_action = np.concatenate([target_eef, target_axis, target_act], axis=-1)
|
||||
breakpoint()
|
||||
@@ -0,0 +1,218 @@
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDatasetMetadata
|
||||
from lerobot.policies.factory import make_policy, make_policy_config
|
||||
import os
|
||||
cfg = make_policy_config("xvla")
|
||||
|
||||
dataset_id = "lerobot/svla_so101_pickplace"
|
||||
# This only downloads the metadata for the dataset, ~10s of MB even for large-scale datasets
|
||||
dataset_metadata = LeRobotDatasetMetadata(dataset_id)
|
||||
policy = make_policy(cfg=cfg, ds_meta=dataset_metadata)
|
||||
|
||||
for name, param in policy.state_dict().items():
|
||||
print(name, param.shape)
|
||||
|
||||
|
||||
# now let's load in safetensors
|
||||
import safetensors.torch
|
||||
from huggingface_hub import snapshot_download
|
||||
|
||||
cache_dir = snapshot_download(repo_id="2toINF/X-VLA-Libero", repo_type="model", cache_dir="/fsx/jade_choghari/.cache/huggingface/model")
|
||||
state_dict = safetensors.torch.load_file(os.path.join(cache_dir, "model.safetensors"))
|
||||
# policy.load_state_dict(state_dict)
|
||||
# 3. Add "model." prefix to every key
|
||||
new_state_dict = {f"model.{k}": v for k, v in state_dict.items()}
|
||||
keys_to_skip = [
|
||||
"model.transformer.action_encoder.fc.weight",
|
||||
"model.transformer.action_encoder.fc.bias",
|
||||
]
|
||||
|
||||
new_state_dict = {k: v for k, v in new_state_dict.items() if k not in keys_to_skip}
|
||||
# 4. Load into your model
|
||||
missing, unexpected = policy.load_state_dict(new_state_dict, strict=False)
|
||||
|
||||
print("missing keys:", missing)
|
||||
|
||||
print()
|
||||
print("unexpected keys:", unexpected)
|
||||
|
||||
|
||||
|
||||
from lerobot.policies.factory import make_policy, make_pre_post_processors
|
||||
# from lerobot.policies.xvla.configuration_xvla import XVLAConfig
|
||||
from lerobot.configs.policies import PreTrainedConfig
|
||||
from lerobot.envs.factory import make_env_config
|
||||
from lerobot.utils.constants import OBS_IMAGES, OBS_STATE
|
||||
from xvla.models.modeling_xvla import XVLA
|
||||
import torch
|
||||
import numpy as np
|
||||
import random
|
||||
torch.manual_seed(42)
|
||||
random.seed(42)
|
||||
np.random.seed(42)
|
||||
observation_height: int = 224
|
||||
observation_width: int = 224 # todo: jadechoghari, image size is different for the two models
|
||||
# create an observation dict
|
||||
OBS = {
|
||||
f"{OBS_IMAGES}.image": torch.randn(1, 3, observation_height, observation_width),
|
||||
f"{OBS_IMAGES}.image2": torch.randn(1, 3, observation_height, observation_width),
|
||||
OBS_STATE: torch.randn(1, 20), # ONLY if OBS_STATE is already a string
|
||||
"task": "put the object in the box",
|
||||
}
|
||||
|
||||
IMAGENET_MEAN = torch.tensor([0.485, 0.456, 0.406]).view(1,3,1,1)
|
||||
IMAGENET_STD = torch.tensor([0.229, 0.224, 0.225]).view(1,3,1,1)
|
||||
def fake_rgb(H, W):
|
||||
arr = np.random.randint(0, 255, (H, W, 3), dtype=np.uint8)
|
||||
t = torch.from_numpy(arr).permute(2, 0, 1) # CHW
|
||||
t = t.unsqueeze(0).float()
|
||||
# normalize pixel to imagenet
|
||||
return t
|
||||
|
||||
|
||||
OBS[f"{OBS_IMAGES}.image"] = fake_rgb(observation_height, observation_width)
|
||||
OBS[f"{OBS_IMAGES}.image2"] = fake_rgb(observation_height, observation_width)
|
||||
|
||||
cfg = PreTrainedConfig.from_pretrained("/raid/jade/models/xvla-libero-og_migrated")
|
||||
cfg.pretrained_path = "/raid/jade/models/xvla-libero-og_migrated"
|
||||
env_cfg = make_env_config("libero", task="libero_spatial")
|
||||
policy = make_policy(
|
||||
cfg=cfg,
|
||||
env_cfg=env_cfg,
|
||||
)
|
||||
|
||||
policy.eval()
|
||||
|
||||
preprocessor_overrides = {
|
||||
"device_processor": {"device": str(cfg.device)},
|
||||
}
|
||||
|
||||
preprocessor, postprocessor = make_pre_post_processors(
|
||||
policy_cfg=cfg,
|
||||
pretrained_path=cfg.pretrained_path,
|
||||
preprocessor_overrides=preprocessor_overrides,
|
||||
)
|
||||
|
||||
observation = preprocessor(OBS)
|
||||
inputs = policy._build_model_inputs(observation)
|
||||
|
||||
|
||||
#### now the og model ###########################################################
|
||||
from xvla.models.processing_xvla import XVLAProcessor
|
||||
|
||||
processor = XVLAProcessor.from_pretrained("/raid/jade/models/xvla-libero", num_views=2)
|
||||
inputs_1 = processor([OBS[f"{OBS_IMAGES}.image"], OBS[f"{OBS_IMAGES}.image2"]], OBS["task"])
|
||||
domain_id = torch.tensor([int(3)], dtype=torch.long)
|
||||
inputs.update({
|
||||
"proprio": OBS[OBS_STATE].to("cuda"),
|
||||
"domain_id": domain_id.to("cuda"),
|
||||
})
|
||||
|
||||
|
||||
for k in inputs.keys() & inputs_1.keys(): # intersection of keys
|
||||
a = inputs[k]
|
||||
b = inputs_1[k].to("cuda")
|
||||
|
||||
print(f"\n🔎 Key: {k}")
|
||||
|
||||
# Check shape
|
||||
print(" shape:", a.shape, b.shape)
|
||||
|
||||
# Check if close
|
||||
if torch.allclose(a, b, atol=1e-5, rtol=1e-5):
|
||||
print(" ✔️ tensors are equal (allclose)")
|
||||
else:
|
||||
diff = torch.abs(a - b)
|
||||
print(" ❌ tensors differ")
|
||||
print(" max diff:", diff.max().item())
|
||||
print(" mean diff:", diff.mean().item())
|
||||
|
||||
|
||||
model = XVLA.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
model.eval()
|
||||
model.to("cuda")
|
||||
|
||||
action = model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
# (Pdb) inputs['input_ids'].shape
|
||||
# torch.Size([1, 64])
|
||||
# (Pdb) inputs_1['input_ids'].shape
|
||||
# torch.Size([1, 50])
|
||||
# (Pdb) [0, 0, :, :4, 0]
|
||||
action_1 = policy.model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
|
||||
#np all close
|
||||
print(np.allclose(action, action_1, atol=1e-2, rtol=1e-2))
|
||||
print("max diff:", np.max(np.abs(action - action_1)))
|
||||
print("mean diff:", np.mean(np.abs(action - action_1)))
|
||||
|
||||
|
||||
from xvla.models.processor_xvla import XVLAProcessor
|
||||
from xvla.models.modeling_xvla import XVLA
|
||||
from xvla.models.configuration_xvla import XVLAConfig
|
||||
import torch
|
||||
import random
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from lerobot.policies.factory import make_policy
|
||||
from lerobot.configs.policies import PreTrainedConfig
|
||||
from lerobot.envs.factory import make_env_config
|
||||
cfg = XVLAConfig.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
model = XVLA.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
model.eval()
|
||||
model.to("cuda")
|
||||
processor = XVLAProcessor.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
# /raid/jade/models/xvla-libero
|
||||
# seet seed
|
||||
torch.manual_seed(42)
|
||||
random.seed(42)
|
||||
np.random.seed(42)
|
||||
|
||||
def make_random_pil_images(num_images=3, H=480, W=640):
|
||||
images = []
|
||||
for _ in range(num_images):
|
||||
# Random RGB image
|
||||
arr = np.random.randint(0, 256, (H, W, 3), dtype=np.uint8)
|
||||
img = Image.fromarray(arr)
|
||||
images.append(img)
|
||||
return images
|
||||
|
||||
# Example:
|
||||
images = make_random_pil_images()
|
||||
language_instruction = "This is a random image"
|
||||
# Multimodal preprocessing by processor
|
||||
inputs = processor(images, language_instruction)
|
||||
if not {"input_ids", "image_input", "image_mask"}.issubset(inputs):
|
||||
raise ValueError("Processor did not return the expected keys.")
|
||||
|
||||
proprio = torch.randn(1, 20)
|
||||
domain_id = torch.tensor([int(0)], dtype=torch.long)
|
||||
|
||||
# Align to model's device/dtype
|
||||
device = model.device
|
||||
dtype = next(model.parameters()).dtype
|
||||
|
||||
def to_model(t: torch.Tensor) -> torch.Tensor:
|
||||
if not isinstance(t, torch.Tensor):
|
||||
t = torch.as_tensor(t)
|
||||
# cast floats to model dtype, keep integral/bool as-is
|
||||
return t.to(device=device, dtype=dtype) if t.is_floating_point() else t.to(device=device)
|
||||
|
||||
inputs = {k: to_model(v) for k, v in inputs.items()}
|
||||
inputs.update({
|
||||
"proprio": to_model(proprio),
|
||||
"domain_id": domain_id.to(device),
|
||||
})
|
||||
|
||||
# Inference
|
||||
action = model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
|
||||
|
||||
#### now for lerobot model #####################################################
|
||||
|
||||
cfg = PreTrainedConfig.from_pretrained("/raid/jade/models/xvla-libero-og_migrated")
|
||||
env_cfg = make_env_config("libero", task="libero_spatial")
|
||||
cfg.pretrained_path = "/raid/jade/models/xvla-libero-og_migrated"
|
||||
policy = make_policy(cfg=cfg, env_cfg=env_cfg)
|
||||
policy.eval()
|
||||
policy.to("cuda")
|
||||
|
||||
action_1 = policy.model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 49 KiB |
-28
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import safetensors.torch as st
|
||||
import torch
|
||||
import argparse
|
||||
import os
|
||||
|
||||
def prefix_state_dict(input_path, output_path, prefix="model."):
|
||||
# Load original checkpoint
|
||||
state_dict = st.load_file(input_path)
|
||||
|
||||
print(f"Loaded {len(state_dict)} tensors from {input_path}")
|
||||
|
||||
# Add prefix to every key
|
||||
new_state_dict = {f"{prefix}{k}": v for k, v in state_dict.items()}
|
||||
|
||||
print(f"Writing prefixed checkpoint with {len(new_state_dict)} keys...")
|
||||
st.save_file(new_state_dict, output_path)
|
||||
|
||||
print(f"Saved to {output_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--input", type=str, required=True, help="Path to model.safetensors")
|
||||
parser.add_argument("--output", type=str, required=True, help="Output prefixed model.safetensors")
|
||||
parser.add_argument("--prefix", type=str, default="model.", help="Prefix to add to each key")
|
||||
args = parser.parse_args()
|
||||
|
||||
prefix_state_dict(args.input, args.output, args.prefix)
|
||||
@@ -251,6 +251,7 @@ class LiberoEnv(EnvConfig):
|
||||
"pixels/robot0_eye_in_hand_image": f"{OBS_IMAGES}.image2",
|
||||
}
|
||||
)
|
||||
action_type: str = "rel"
|
||||
|
||||
def __post_init__(self):
|
||||
if self.obs_type == "pixels":
|
||||
|
||||
@@ -97,6 +97,7 @@ def make_env(
|
||||
init_states=cfg.init_states,
|
||||
gym_kwargs=cfg.gym_kwargs,
|
||||
env_cls=env_cls,
|
||||
action_type=cfg.action_type,
|
||||
)
|
||||
elif "metaworld" in cfg.type:
|
||||
from lerobot.envs.metaworld import create_metaworld_envs
|
||||
|
||||
+26
-13
@@ -115,6 +115,7 @@ class LiberoEnv(gym.Env):
|
||||
episode_index: int = 0,
|
||||
camera_name_mapping: dict[str, str] | None = None,
|
||||
num_steps_wait: int = 10,
|
||||
action_type: str = "rel",
|
||||
):
|
||||
super().__init__()
|
||||
self.task_id = task_id
|
||||
@@ -185,6 +186,7 @@ class LiberoEnv(gym.Env):
|
||||
self.action_space = spaces.Box(
|
||||
low=ACTION_LOW, high=ACTION_HIGH, shape=(ACTION_DIM,), dtype=np.float32
|
||||
)
|
||||
self.action_type = action_type
|
||||
|
||||
def render(self):
|
||||
raw_obs = self._env.env._get_observations()
|
||||
@@ -213,18 +215,25 @@ class LiberoEnv(gym.Env):
|
||||
if camera_name == "agentview_image":
|
||||
image = image[::-1, ::-1] # rotate 180 degrees
|
||||
images[self.camera_name_mapping[camera_name]] = image
|
||||
state = np.concatenate(
|
||||
(
|
||||
raw_obs["robot0_eef_pos"],
|
||||
quat2axisangle(raw_obs["robot0_eef_quat"]),
|
||||
raw_obs["robot0_gripper_qpos"],
|
||||
|
||||
if self.action_type == "rel":
|
||||
state = np.concatenate(
|
||||
(
|
||||
raw_obs["robot0_eef_pos"],
|
||||
quat2axisangle(raw_obs["robot0_eef_quat"]),
|
||||
raw_obs["robot0_gripper_qpos"],
|
||||
)
|
||||
)
|
||||
)
|
||||
# add new obs for XVLA: jadechoghari
|
||||
robo_ori = Mat_to_Rotate6D(self._env.robots[0].controller.ee_ori_mat)
|
||||
robo_pos = self._env.robots[0].controller.ee_pos
|
||||
proprio = np.concatenate([robo_pos, robo_ori, np.array([0.0])], axis=-1)
|
||||
state = np.concatenate([proprio, np.zeros_like(proprio)], axis=-1)
|
||||
# TODO: jadechoghari, this is an ugly quick workaround for XVLA states.
|
||||
# we will open a new PR to handle this in a preprocessor.
|
||||
elif self.action_type == "abs":
|
||||
robo_ori = Mat_to_Rotate6D(self._env.robots[0].controller.ee_ori_mat)
|
||||
robo_pos = self._env.robots[0].controller.ee_pos
|
||||
proprio = np.concatenate([robo_pos, robo_ori, np.array([0.0])], axis=-1)
|
||||
state = np.concatenate([proprio, np.zeros_like(proprio)], axis=-1)
|
||||
else:
|
||||
raise NotImplementedError(f"The action type '{self.action_type}' is not supported in LiberoEnv. "
|
||||
"Please switch to an action type (e.g. 'rel', 'abs').")
|
||||
agent_pos = state
|
||||
if self.obs_type == "pixels":
|
||||
return {"pixels": images.copy()}
|
||||
@@ -250,8 +259,9 @@ class LiberoEnv(gym.Env):
|
||||
# Step the simulator with a no-op action for a few frames so everything settles.
|
||||
# Increasing this value can improve determinism and reproducibility across resets.
|
||||
for _ in range(self.num_steps_wait):
|
||||
action = np.array([0., 0., 0., 0., 0., 0., -1.0])
|
||||
action = np.array(get_libero_dummy_action())
|
||||
raw_obs, _, _, _ = self._env.step(action)
|
||||
|
||||
observation = self._format_raw_obs(raw_obs)
|
||||
for robot in self._env.robots:
|
||||
robot.controller.use_delta = False
|
||||
@@ -264,7 +274,6 @@ class LiberoEnv(gym.Env):
|
||||
f"Expected action to be 1-D (shape (action_dim,)), "
|
||||
f"but got shape {action.shape} with ndim={action.ndim}"
|
||||
)
|
||||
action[-1] = 1 if action[-1] > 0.5 else -1
|
||||
raw_obs, reward, done, info = self._env.step(action)
|
||||
|
||||
is_success = self._env.check_success()
|
||||
@@ -302,6 +311,7 @@ def _make_env_fns(
|
||||
camera_names: list[str],
|
||||
init_states: bool,
|
||||
gym_kwargs: Mapping[str, Any],
|
||||
action_type: str,
|
||||
) -> list[Callable[[], LiberoEnv]]:
|
||||
"""Build n_envs factory callables for a single (suite, task_id)."""
|
||||
|
||||
@@ -314,6 +324,7 @@ def _make_env_fns(
|
||||
camera_name=camera_names,
|
||||
init_states=init_states,
|
||||
episode_index=episode_index,
|
||||
action_type=action_type,
|
||||
**local_kwargs,
|
||||
)
|
||||
|
||||
@@ -333,6 +344,7 @@ def create_libero_envs(
|
||||
camera_name: str | Sequence[str] = "agentview_image,robot0_eye_in_hand_image",
|
||||
init_states: bool = True,
|
||||
env_cls: Callable[[Sequence[Callable[[], Any]]], Any] | None = None,
|
||||
action_type: str = "rel",
|
||||
) -> dict[str, dict[int, Any]]:
|
||||
"""
|
||||
Create vectorized LIBERO environments with a consistent return shape.
|
||||
@@ -382,6 +394,7 @@ def create_libero_envs(
|
||||
camera_names=camera_names,
|
||||
init_states=init_states,
|
||||
gym_kwargs=gym_kwargs,
|
||||
action_type=action_type,
|
||||
)
|
||||
out[suite_name][tid] = env_cls(fns)
|
||||
print(f"Built vec env | suite={suite_name} | task_id={tid} | n_envs={n_envs}")
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
# XVLA Custom Processor Steps
|
||||
|
||||
Three custom processor steps have been implemented for XVLA that encapsulate the preprocessing and postprocessing logic from `lerobot_eval.py`.
|
||||
|
||||
## Processor Steps
|
||||
|
||||
### 1. XVLAImageScaleProcessorStep
|
||||
**Registry Name:** `xvla_image_scale`
|
||||
|
||||
Scales image observations by 255 (from [0,1] to [0,255] range).
|
||||
|
||||
```python
|
||||
XVLAImageScaleProcessorStep(
|
||||
image_keys=None # Auto-detects "observation.images.*" or specify list
|
||||
)
|
||||
```
|
||||
|
||||
### 2. XVLAAddDomainIdProcessorStep
|
||||
**Registry Name:** `xvla_add_domain_id`
|
||||
|
||||
Adds `domain_id` tensor to complementary data for multi-domain support.
|
||||
|
||||
```python
|
||||
XVLAAddDomainIdProcessorStep(
|
||||
domain_id=3, # Domain identifier
|
||||
device="cuda" # Tensor device
|
||||
)
|
||||
```
|
||||
|
||||
### 3. XVLARotation6DToAxisAngleProcessorStep
|
||||
**Registry Name:** `xvla_rotation_6d_to_axis_angle`
|
||||
|
||||
Converts 6D rotation to axis-angle representation:
|
||||
- **Input:** [eef(3), rotation_6d(6), gripper(1)] = 10D
|
||||
- **Output:** [eef(3), axis_angle(3), gripper(1)] = 7D
|
||||
|
||||
```python
|
||||
XVLARotation6DToAxisAngleProcessorStep(
|
||||
expected_action_dim=10
|
||||
)
|
||||
```
|
||||
|
||||
## Integration with Config
|
||||
|
||||
These steps can be added to your XVLA policy configuration:
|
||||
|
||||
### In Preprocessing Pipeline:
|
||||
```python
|
||||
from lerobot.policies.xvla.processor_xvla import (
|
||||
XVLAImageScaleProcessorStep,
|
||||
XVLAAddDomainIdProcessorStep,
|
||||
)
|
||||
|
||||
preprocessor_steps = [
|
||||
RenameObservationsProcessorStep(rename_map={}),
|
||||
AddBatchDimensionProcessorStep(),
|
||||
XVLAImageScaleProcessorStep(), # Add this
|
||||
TokenizerProcessorStep(...),
|
||||
DeviceProcessorStep(device="cuda"),
|
||||
XVLAAddDomainIdProcessorStep(domain_id=3), # Add this
|
||||
NormalizerProcessorStep(...),
|
||||
]
|
||||
```
|
||||
|
||||
### In Postprocessing Pipeline:
|
||||
```python
|
||||
from lerobot.policies.xvla.processor_xvla import XVLARotation6DToAxisAngleProcessorStep
|
||||
|
||||
postprocessor_steps = [
|
||||
UnnormalizerProcessorStep(...),
|
||||
XVLARotation6DToAxisAngleProcessorStep(), # Add this
|
||||
DeviceProcessorStep(device="cpu"),
|
||||
]
|
||||
```
|
||||
|
||||
## Usage in Evaluation
|
||||
|
||||
Now your evaluation loop simplifies to:
|
||||
|
||||
```python
|
||||
# Before (from lerobot_eval.py lines 165-184)
|
||||
observation[f"observation.images.image"] = observation[f"observation.images.image"] * 255
|
||||
observation[f"observation.images.image2"] = observation[f"observation.images.image2"] * 255
|
||||
observation = add_envs_task(env, observation)
|
||||
observation = preprocessor(observation)
|
||||
observation["domain_id"] = torch.tensor([int(3)], dtype=torch.long).to("cuda")
|
||||
|
||||
with torch.inference_mode():
|
||||
action = policy.select_action(observation).to("cpu").numpy()
|
||||
target_eef = action[:, :3]
|
||||
target_axis = Rotate6D_to_AxisAngle(action[:, 3:9])
|
||||
target_act = action[:, 9:10]
|
||||
action_numpy = np.concatenate([target_eef, target_axis, target_act], axis=-1)
|
||||
|
||||
# After (clean and simple)
|
||||
observation = add_envs_task(env, observation) # Add task
|
||||
observation = preprocessor(observation) # Scales images + adds domain_id
|
||||
|
||||
with torch.inference_mode():
|
||||
action = policy.select_action(observation)
|
||||
action = postprocessor(action) # Converts rotation + moves to CPU
|
||||
action_numpy = action.numpy()
|
||||
```
|
||||
|
||||
## Configuration via Registry
|
||||
|
||||
All steps are registered and can be loaded from JSON/YAML config:
|
||||
|
||||
```json
|
||||
{
|
||||
"preprocessor": {
|
||||
"steps": [
|
||||
{"name": "xvla_image_scale"},
|
||||
{"name": "xvla_add_domain_id", "domain_id": 3, "device": "cuda"}
|
||||
]
|
||||
},
|
||||
"postprocessor": {
|
||||
"steps": [
|
||||
{"name": "xvla_rotation_6d_to_axis_angle", "expected_action_dim": 10}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Reference
|
||||
|
||||
See `processor_groot.py` for similar patterns - these XVLA processors follow the same design:
|
||||
- Registered with `@ProcessorStepRegistry.register()`
|
||||
- Implement `__call__`, `transform_features`, and `get_config`
|
||||
- Operate on `EnvTransition` objects
|
||||
- Properly handle `transition.copy()` to avoid side effects
|
||||
|
||||
@@ -402,38 +402,37 @@ class XVLAPolicy(PreTrainedPolicy):
|
||||
f"model.safetensors not found on the Hub at {model_id}"
|
||||
) from e
|
||||
|
||||
# --- Step 3: Load safetensor weights ---
|
||||
print(f"Loading checkpoint from {model_file}")
|
||||
state_dict = safetensors.torch.load_file(model_file)
|
||||
|
||||
# --- Step 4: Modify keys ---
|
||||
new_state_dict = {f"model.{k}": v for k, v in state_dict.items()}
|
||||
# # --- Step 4: Modify keys ---
|
||||
# new_state_dict = {f"model.{k}": v for k, v in state_dict.items()}
|
||||
|
||||
# Layers to skip (reinitialize)
|
||||
keys_to_skip = [
|
||||
"model.transformer.action_encoder.fc.weight",
|
||||
"model.transformer.action_encoder.fc.bias",
|
||||
"model.transformer.action_decoder.fc.weight",
|
||||
"model.transformer.action_decoder.bias.weight"
|
||||
]
|
||||
new_state_dict = {
|
||||
k: v for k, v in new_state_dict.items()
|
||||
if k not in keys_to_skip
|
||||
}
|
||||
# ---- ADD THIS: Fix shared embeddings ----
|
||||
# # Layers to skip (reinitialize)
|
||||
# keys_to_skip = [
|
||||
# "model.transformer.action_encoder.fc.weight",
|
||||
# "model.transformer.action_encoder.fc.bias",
|
||||
# "model.transformer.action_decoder.fc.weight",
|
||||
# "model.transformer.action_decoder.bias.weight"
|
||||
# ]
|
||||
# new_state_dict = {
|
||||
# k: v for k, v in new_state_dict.items()
|
||||
# if k not in keys_to_skip
|
||||
# }
|
||||
# # ---- ADD THIS: Fix shared embeddings ----
|
||||
encoder_key = "model.vlm.language_model.model.encoder.embed_tokens.weight"
|
||||
shared_key = "model.vlm.language_model.model.shared.weight"
|
||||
if encoder_key in state_dict:
|
||||
state_dict[shared_key] = state_dict[encoder_key]
|
||||
# --- Step 5: Load into instance ---
|
||||
# step 5: load into instance
|
||||
missing, unexpected = instance.load_state_dict(state_dict, strict=True)
|
||||
print("✅ Loaded XVLA checkpoint with modified keys.")
|
||||
print("Loaded XVLA checkpoint")
|
||||
if missing:
|
||||
print(f"Missing keys: {missing}")
|
||||
if unexpected:
|
||||
print(f"Unexpected keys: {unexpected}")
|
||||
|
||||
# --- Step 6: Finalize ---
|
||||
# step 6: finalize
|
||||
instance.to(config.device)
|
||||
instance.eval()
|
||||
return instance
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "policy_postprocessor",
|
||||
"steps": [
|
||||
{
|
||||
"registry_name": "unnormalizer_processor",
|
||||
"config": {
|
||||
"eps": 1e-08,
|
||||
"features": {
|
||||
"action": {
|
||||
"type": "ACTION",
|
||||
"shape": [
|
||||
20
|
||||
]
|
||||
}
|
||||
},
|
||||
"norm_map": {
|
||||
"VISUAL": "MEAN_STD",
|
||||
"STATE": "IDENTITY",
|
||||
"ACTION": "IDENTITY"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"registry_name": "xvla_rotation_6d_to_axis_angle",
|
||||
"config": {
|
||||
"expected_action_dim": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"registry_name": "device_processor",
|
||||
"config": {
|
||||
"device": "cpu",
|
||||
"float_dtype": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"name": "policy_preprocessor",
|
||||
"steps": [
|
||||
{
|
||||
"registry_name": "rename_observations_processor",
|
||||
"config": {
|
||||
"rename_map": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"registry_name": "to_batch_processor",
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"registry_name": "xvla_image_scale",
|
||||
"config": {
|
||||
"image_keys": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"registry_name": "tokenizer_processor",
|
||||
"config": {
|
||||
"max_length": 50,
|
||||
"task_key": "task",
|
||||
"padding_side": "right",
|
||||
"padding": "max_length",
|
||||
"truncation": true,
|
||||
"tokenizer_name": "facebook/bart-large"
|
||||
}
|
||||
},
|
||||
{
|
||||
"registry_name": "device_processor",
|
||||
"config": {
|
||||
"device": "cuda",
|
||||
"float_dtype": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"registry_name": "xvla_add_domain_id",
|
||||
"config": {
|
||||
"domain_id": 3,
|
||||
"device": "cuda"
|
||||
}
|
||||
},
|
||||
{
|
||||
"registry_name": "normalizer_processor",
|
||||
"config": {
|
||||
"eps": 1e-08,
|
||||
"features": {
|
||||
"observation.images.image": {
|
||||
"type": "VISUAL",
|
||||
"shape": [
|
||||
3,
|
||||
224,
|
||||
224
|
||||
]
|
||||
},
|
||||
"observation.images.image2": {
|
||||
"type": "VISUAL",
|
||||
"shape": [
|
||||
3,
|
||||
224,
|
||||
224
|
||||
]
|
||||
},
|
||||
"observation.state": {
|
||||
"type": "STATE",
|
||||
"shape": [
|
||||
8
|
||||
]
|
||||
},
|
||||
"action": {
|
||||
"type": "ACTION",
|
||||
"shape": [
|
||||
20
|
||||
]
|
||||
}
|
||||
},
|
||||
"norm_map": {
|
||||
"VISUAL": "IMAGENET",
|
||||
"STATE": "IDENTITY",
|
||||
"ACTION": "IDENTITY"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -235,6 +235,9 @@ class XVLARotation6DToAxisAngleProcessorStep(ProcessorStep):
|
||||
|
||||
# Concatenate: [eef (3), axis_angle (3), gripper (1)] = 7D
|
||||
action_np = np.concatenate([target_eef, target_axis, target_act], axis=-1)
|
||||
|
||||
# Convert gripper action to -1 or 1
|
||||
action_np[:, -1] = np.where(action_np[:, -1] > 0.5, 1.0, -1.0)
|
||||
|
||||
# Convert back to tensor
|
||||
action = torch.from_numpy(action_np).to(device=device, dtype=dtype)
|
||||
|
||||
@@ -167,14 +167,12 @@ def rollout(
|
||||
|
||||
# Preprocess observation (includes image scaling and domain_id addition)
|
||||
observation = preprocessor(observation)
|
||||
breakpoint()
|
||||
# Policy inference
|
||||
with torch.inference_mode():
|
||||
action = policy.select_action(observation)
|
||||
|
||||
# Postprocess action (includes rotation conversion and device transfer to CPU)
|
||||
action = postprocessor(action)
|
||||
|
||||
# Convert to numpy
|
||||
action_numpy: np.ndarray = action.numpy()
|
||||
assert action_numpy.ndim == 2, "Action dimensions should be (batch, action_dim)"
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
from xvla.models.processor_xvla import XVLAProcessor
|
||||
from xvla.models.modeling_xvla import XVLA
|
||||
from xvla.models.configuration_xvla import XVLAConfig
|
||||
import torch
|
||||
import random
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from lerobot.policies.factory import make_policy
|
||||
from lerobot.configs.policies import PreTrainedConfig
|
||||
from lerobot.envs.factory import make_env_config
|
||||
cfg = XVLAConfig.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
model = XVLA.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
model.eval()
|
||||
model.to("cuda")
|
||||
processor = XVLAProcessor.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
# /raid/jade/models/xvla-libero
|
||||
# seet seed
|
||||
torch.manual_seed(42)
|
||||
random.seed(42)
|
||||
np.random.seed(42)
|
||||
|
||||
def make_random_pil_images(num_images=3, H=480, W=640):
|
||||
images = []
|
||||
for _ in range(num_images):
|
||||
# Random RGB image
|
||||
arr = np.random.randint(0, 256, (H, W, 3), dtype=np.uint8)
|
||||
img = Image.fromarray(arr)
|
||||
images.append(img)
|
||||
return images
|
||||
|
||||
# Example:
|
||||
images = make_random_pil_images()
|
||||
language_instruction = "This is a random image"
|
||||
# Multimodal preprocessing by processor
|
||||
inputs = processor(images, language_instruction)
|
||||
if not {"input_ids", "image_input", "image_mask"}.issubset(inputs):
|
||||
raise ValueError("Processor did not return the expected keys.")
|
||||
|
||||
proprio = torch.randn(1, 20)
|
||||
domain_id = torch.tensor([int(0)], dtype=torch.long)
|
||||
|
||||
# Align to model's device/dtype
|
||||
device = model.device
|
||||
dtype = next(model.parameters()).dtype
|
||||
|
||||
def to_model(t: torch.Tensor) -> torch.Tensor:
|
||||
if not isinstance(t, torch.Tensor):
|
||||
t = torch.as_tensor(t)
|
||||
# cast floats to model dtype, keep integral/bool as-is
|
||||
return t.to(device=device, dtype=dtype) if t.is_floating_point() else t.to(device=device)
|
||||
|
||||
inputs = {k: to_model(v) for k, v in inputs.items()}
|
||||
inputs.update({
|
||||
"proprio": to_model(proprio),
|
||||
"domain_id": domain_id.to(device),
|
||||
})
|
||||
|
||||
# Inference
|
||||
action = model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
|
||||
|
||||
#### now for lerobot model #####################################################
|
||||
|
||||
cfg = PreTrainedConfig.from_pretrained("/raid/jade/models/xvla-libero-og_migrated")
|
||||
env_cfg = make_env_config("libero", task="libero_spatial")
|
||||
cfg.pretrained_path = "/raid/jade/models/xvla-libero-og_migrated"
|
||||
policy = make_policy(cfg=cfg, env_cfg=env_cfg)
|
||||
policy.eval()
|
||||
policy.to("cuda")
|
||||
|
||||
action_1 = policy.model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
breakpoint()
|
||||
@@ -1,107 +0,0 @@
|
||||
from lerobot.policies.factory import make_policy, make_pre_post_processors
|
||||
# from lerobot.policies.xvla.configuration_xvla import XVLAConfig
|
||||
from lerobot.configs.policies import PreTrainedConfig
|
||||
from lerobot.envs.factory import make_env_config
|
||||
from lerobot.utils.constants import OBS_IMAGES, OBS_STATE
|
||||
from xvla.models.modeling_xvla import XVLA
|
||||
import torch
|
||||
import numpy as np
|
||||
import random
|
||||
torch.manual_seed(42)
|
||||
random.seed(42)
|
||||
np.random.seed(42)
|
||||
observation_height: int = 224
|
||||
observation_width: int = 224 # todo: jadechoghari, image size is different for the two models
|
||||
# create an observation dict
|
||||
OBS = {
|
||||
f"{OBS_IMAGES}.image": torch.randn(1, 3, observation_height, observation_width),
|
||||
f"{OBS_IMAGES}.image2": torch.randn(1, 3, observation_height, observation_width),
|
||||
OBS_STATE: torch.randn(1, 20), # ONLY if OBS_STATE is already a string
|
||||
"task": "put the object in the box",
|
||||
}
|
||||
|
||||
IMAGENET_MEAN = torch.tensor([0.485, 0.456, 0.406]).view(1,3,1,1)
|
||||
IMAGENET_STD = torch.tensor([0.229, 0.224, 0.225]).view(1,3,1,1)
|
||||
def fake_rgb(H, W):
|
||||
arr = np.random.randint(0, 255, (H, W, 3), dtype=np.uint8)
|
||||
t = torch.from_numpy(arr).permute(2, 0, 1) # CHW
|
||||
t = t.unsqueeze(0).float()
|
||||
# normalize pixel to imagenet
|
||||
return t
|
||||
|
||||
|
||||
OBS[f"{OBS_IMAGES}.image"] = fake_rgb(observation_height, observation_width)
|
||||
OBS[f"{OBS_IMAGES}.image2"] = fake_rgb(observation_height, observation_width)
|
||||
|
||||
cfg = PreTrainedConfig.from_pretrained("/raid/jade/models/xvla-libero-og_migrated")
|
||||
cfg.pretrained_path = "/raid/jade/models/xvla-libero-og_migrated"
|
||||
env_cfg = make_env_config("libero", task="libero_spatial")
|
||||
policy = make_policy(
|
||||
cfg=cfg,
|
||||
env_cfg=env_cfg,
|
||||
)
|
||||
|
||||
policy.eval()
|
||||
|
||||
preprocessor_overrides = {
|
||||
"device_processor": {"device": str(cfg.device)},
|
||||
}
|
||||
|
||||
preprocessor, postprocessor = make_pre_post_processors(
|
||||
policy_cfg=cfg,
|
||||
pretrained_path=cfg.pretrained_path,
|
||||
preprocessor_overrides=preprocessor_overrides,
|
||||
)
|
||||
|
||||
observation = preprocessor(OBS)
|
||||
inputs = policy._build_model_inputs(observation)
|
||||
|
||||
|
||||
#### now the og model ###########################################################
|
||||
from xvla.models.processing_xvla import XVLAProcessor
|
||||
|
||||
processor = XVLAProcessor.from_pretrained("/raid/jade/models/xvla-libero", num_views=2)
|
||||
inputs_1 = processor([OBS[f"{OBS_IMAGES}.image"], OBS[f"{OBS_IMAGES}.image2"]], OBS["task"])
|
||||
domain_id = torch.tensor([int(3)], dtype=torch.long)
|
||||
inputs.update({
|
||||
"proprio": OBS[OBS_STATE].to("cuda"),
|
||||
"domain_id": domain_id.to("cuda"),
|
||||
})
|
||||
|
||||
breakpoint()
|
||||
for k in inputs.keys() & inputs_1.keys(): # intersection of keys
|
||||
a = inputs[k]
|
||||
b = inputs_1[k].to("cuda")
|
||||
|
||||
print(f"\n🔎 Key: {k}")
|
||||
|
||||
# Check shape
|
||||
print(" shape:", a.shape, b.shape)
|
||||
|
||||
# Check if close
|
||||
if torch.allclose(a, b, atol=1e-5, rtol=1e-5):
|
||||
print(" ✔️ tensors are equal (allclose)")
|
||||
else:
|
||||
diff = torch.abs(a - b)
|
||||
print(" ❌ tensors differ")
|
||||
print(" max diff:", diff.max().item())
|
||||
print(" mean diff:", diff.mean().item())
|
||||
|
||||
|
||||
model = XVLA.from_pretrained("/raid/jade/models/xvla-libero")
|
||||
model.eval()
|
||||
model.to("cuda")
|
||||
|
||||
action = model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
# (Pdb) inputs['input_ids'].shape
|
||||
# torch.Size([1, 64])
|
||||
# (Pdb) inputs_1['input_ids'].shape
|
||||
# torch.Size([1, 50])
|
||||
# (Pdb) [0, 0, :, :4, 0]
|
||||
action_1 = policy.model.generate_actions(**inputs, steps=10).squeeze(0).float().cpu().numpy()
|
||||
|
||||
#np all close
|
||||
print(np.allclose(action, action_1, atol=1e-2, rtol=1e-2))
|
||||
print("max diff:", np.max(np.abs(action - action_1)))
|
||||
print("mean diff:", np.mean(np.abs(action - action_1)))
|
||||
breakpoint()
|
||||
@@ -1,37 +0,0 @@
|
||||
from lerobot.datasets.lerobot_dataset import LeRobotDatasetMetadata
|
||||
from lerobot.policies.factory import make_policy, make_policy_config
|
||||
import os
|
||||
cfg = make_policy_config("xvla")
|
||||
|
||||
dataset_id = "lerobot/svla_so101_pickplace"
|
||||
# This only downloads the metadata for the dataset, ~10s of MB even for large-scale datasets
|
||||
dataset_metadata = LeRobotDatasetMetadata(dataset_id)
|
||||
policy = make_policy(cfg=cfg, ds_meta=dataset_metadata)
|
||||
|
||||
for name, param in policy.state_dict().items():
|
||||
print(name, param.shape)
|
||||
|
||||
|
||||
# now let's load in safetensors
|
||||
import safetensors.torch
|
||||
from huggingface_hub import snapshot_download
|
||||
|
||||
cache_dir = snapshot_download(repo_id="2toINF/X-VLA-Libero", repo_type="model", cache_dir="/fsx/jade_choghari/.cache/huggingface/model")
|
||||
state_dict = safetensors.torch.load_file(os.path.join(cache_dir, "model.safetensors"))
|
||||
# policy.load_state_dict(state_dict)
|
||||
# 3. Add "model." prefix to every key
|
||||
new_state_dict = {f"model.{k}": v for k, v in state_dict.items()}
|
||||
keys_to_skip = [
|
||||
"model.transformer.action_encoder.fc.weight",
|
||||
"model.transformer.action_encoder.fc.bias",
|
||||
]
|
||||
|
||||
new_state_dict = {k: v for k, v in new_state_dict.items() if k not in keys_to_skip}
|
||||
# 4. Load into your model
|
||||
missing, unexpected = policy.load_state_dict(new_state_dict, strict=False)
|
||||
|
||||
print("missing keys:", missing)
|
||||
|
||||
print()
|
||||
print("unexpected keys:", unexpected)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
lerobot-train \
|
||||
--dataset.repo_id=libero_dataset \
|
||||
--dataset.root=/fsx/jade_choghari/datasets/libero/ \
|
||||
--policy.type=xvla \
|
||||
--output_dir=/fsx/jade_choghari/outputs/train/xvla_libero \
|
||||
--job_name=xvla_libero \
|
||||
--policy.device=cuda \
|
||||
--policy.action_mode=franka_joint7 \
|
||||
--wandb.enable=true \
|
||||
--policy.repo_id=jadechoghari/X-VLA-Libero \
|
||||
--steps=10000
|
||||
|
||||
# # --policy.pretrained_path=/fsx/jade_choghari/.cache/huggingface/model/xvla-libero \
|
||||
@@ -1,18 +0,0 @@
|
||||
accelerate launch \
|
||||
--multi_gpu \
|
||||
--num_processes=4 \
|
||||
--mixed_precision=fp16 \
|
||||
$(which lerobot-train) \
|
||||
--batch_size=32 \
|
||||
--save_freq=5000 \
|
||||
--num_workers=32 \
|
||||
--dataset.repo_id=libero_dataset \
|
||||
--dataset.root=/fsx/jade_choghari/datasets/libero/ \
|
||||
--policy.type=xvla \
|
||||
--output_dir=/fsx/jade_choghari/outputs/train/xvla_libero_multi \
|
||||
--job_name=xvla_libero \
|
||||
--policy.device=cuda \
|
||||
--policy.action_mode=franka_joint7 \
|
||||
--wandb.enable=true \
|
||||
--policy.repo_id=jadechoghari/X-VLA-Libero \
|
||||
--steps=10000
|
||||
-1
Submodule xvla deleted from e2f0554f8c
Reference in New Issue
Block a user