Add model_params.md (inputs/outputs/config) for pytorch_30000
Browse files- pytorch_30000/model_params.md +171 -0
pytorch_30000/model_params.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# RoboPRO Οβ.β
(PyTorch) β step 30000 checkpoint
|
| 2 |
+
|
| 3 |
+
Fine-tuned **Οβ.β
(pi05)** VLA policy for the **Aloha-Agilex** bimanual robot, trained with [openpi](https://github.com/Physical-Intelligence/openpi) **PyTorch** trainer (`scripts/train_pytorch.py`, DDP) on the RoboPRO **top-cam** dataset (`roboreal_lerobot`). This folder holds the **eval weights** (`model.safetensors`); optimizer state is **not** included.
|
| 4 |
+
|
| 5 |
+
- **Base model:** `pi05_base` (Physical Intelligence), PyTorch port, ~3.6B params
|
| 6 |
+
- **Framework:** PyTorch, **safetensors** (`PI0Pytorch` keys) β this is **not** a JAX/orbax checkpoint
|
| 7 |
+
- **Precision:** bfloat16 (compute **and** stored master weights β see caveat below)
|
| 8 |
+
- **Training:** 30,000 steps, global batch 256 (3ΓH200, 85/GPU), cosine LR (peak 2.5e-5), ~2 epochs over 3.74M frames @ 25 Hz
|
| 9 |
+
- **Final train loss:** ~0.0043 (flow-matching)
|
| 10 |
+
- **JAX counterpart:** see [`jax_30000/`](../jax_30000) β same data/recipe, reached ~0.0021 (see caveat)
|
| 11 |
+
|
| 12 |
+
> β οΈ **Precision caveat.** This PyTorch run stores the model weights themselves in **bf16** (not just compute). In the low-LR tail, weight updates fall below bf16's mantissa resolution and stall, so the final loss plateaus ~2Γ higher than the JAX run (0.0043 vs 0.0021), which keeps **fp32 master weights**. The fix (fp32 master + bf16 autocast + EMA) is documented in the training repo at `docs/pytorch_fp32master_ema_fix.md`. Treat this checkpoint as the **bf16 baseline**.
|
| 13 |
+
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
## Folder contents
|
| 17 |
+
|
| 18 |
+
```
|
| 19 |
+
model.safetensors # PI0Pytorch weights (load these)
|
| 20 |
+
assets/roboreal_lerobot/norm_stats.json # input/output normalization stats (REQUIRED)
|
| 21 |
+
metadata.pt # {global_step, config dict, timestamp}
|
| 22 |
+
train_config.py # the exact TrainConfig used for this run
|
| 23 |
+
```
|
| 24 |
+
> `optimizer.pt` (13 GB) is **not** uploaded β this checkpoint is for **inference/eval**, not for exact-resume training.
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
## Model configuration
|
| 29 |
+
|
| 30 |
+
| field | value | notes |
|
| 31 |
+
|---|---|---|
|
| 32 |
+
| `model_type` | **pi05** (`PI0Pytorch`, `pi05=True`) | flow-matching action expert |
|
| 33 |
+
| `paligemma_variant` | `gemma_2b` | vision-language backbone |
|
| 34 |
+
| `action_expert_variant` | `gemma_300m` | action expert |
|
| 35 |
+
| `action_dim` | **32** | 14 real dims, padded to 32 |
|
| 36 |
+
| `action_horizon` | **50** | timesteps per action chunk |
|
| 37 |
+
| `max_token_len` | **200** | prompt token budget |
|
| 38 |
+
| `discrete_state_input` | `True` | pi05 discretizes state into the prompt |
|
| 39 |
+
| `dtype` | `bfloat16` | compute + master weights |
|
| 40 |
+
|
| 41 |
+
**Training hyperparameters** (full dump in `train_config.py`):
|
| 42 |
+
|
| 43 |
+
| field | value |
|
| 44 |
+
|---|---|
|
| 45 |
+
| dataset | `roboreal_lerobot` (robopro_top_cam) β 15,999 eps / 3.74M frames @ 25 Hz |
|
| 46 |
+
| steps | 30,000 (β2 epochs) |
|
| 47 |
+
| global batch | 256 (3ΓH200, 85/GPU) |
|
| 48 |
+
| LR schedule | cosine, warmup 1,000, peak `2.5e-5`, floor `2.5e-6` |
|
| 49 |
+
| optimizer | AdamW `b1=0.9 b2=0.95 eps=1e-8 wd=1e-10`, grad-clip `1.0` |
|
| 50 |
+
| precision | bfloat16 |
|
| 51 |
+
| EMA | none (not supported by the PyTorch trainer) |
|
| 52 |
+
| base weights | `pi05_base` (PyTorch), loaded via `pytorch_weight_path` |
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## Inputs
|
| 57 |
+
|
| 58 |
+
Single-timestep observation dict: **3 camera images + a 14-D robot state + a language prompt**.
|
| 59 |
+
|
| 60 |
+
### 1. Cameras (3Γ RGB)
|
| 61 |
+
| policy key | physical view | shape | dtype |
|
| 62 |
+
|---|---|---|---|
|
| 63 |
+
| `cam_high` | **overhead / countertop** camera (looking down at the table) | `[3, H, W]` (CHW) | `uint8`, 0β255 |
|
| 64 |
+
| `cam_left_wrist` | left-arm wrist camera | `[3, H, W]` | `uint8`, 0β255 |
|
| 65 |
+
| `cam_right_wrist` | right-arm wrist camera | `[3, H, W]` | `uint8`, 0β255 |
|
| 66 |
+
|
| 67 |
+
- **RGB**, channel-first `[3, H, W]`. Images are internally resized to **224Γ224**, so any input resolution works (training used 240Γ320).
|
| 68 |
+
- **Camera mapping is critical:** feed your **countertop/overhead** view as `cam_high` (trained with the top-cam view in that slot, *not* a robot-head camera). Wrist cams map by side. In the raw dataset these are the `countertop` / `left` / `right` video keys respectively.
|
| 69 |
+
- All three cameras are required.
|
| 70 |
+
|
| 71 |
+
### 2. State β `state`
|
| 72 |
+
- `float32[14]`, raw joint positions (radians) + gripper, **absolute**, in Aloha convention.
|
| 73 |
+
- Order (same for state and action):
|
| 74 |
+
```
|
| 75 |
+
0 left_waist 1 left_shoulder 2 left_elbow 3 left_forearm_roll
|
| 76 |
+
4 left_wrist_angle 5 left_wrist_rotate 6 left_gripper
|
| 77 |
+
7 right_waist 8 right_shoulder 9 right_elbow 10 right_forearm_roll
|
| 78 |
+
11 right_wrist_angle 12 right_wrist_rotate 13 right_gripper
|
| 79 |
+
```
|
| 80 |
+
- Feed **raw physical values** β normalization (quantile, from `norm_stats.json`) and the Alohaβpi convention conversion happen **inside** the policy.
|
| 81 |
+
|
| 82 |
+
### 3. Prompt β `prompt`
|
| 83 |
+
- Natural-language task instruction, e.g. `"put the mouse on the pad"`. Trained on 1,622 instruction variants.
|
| 84 |
+
|
| 85 |
+
### Observation dict shape
|
| 86 |
+
```python
|
| 87 |
+
observation = {
|
| 88 |
+
"state": np.ndarray, # float32 [14]
|
| 89 |
+
"images": {
|
| 90 |
+
"cam_high": np.ndarray, # uint8 [3, H, W] (countertop)
|
| 91 |
+
"cam_left_wrist": np.ndarray, # uint8 [3, H, W]
|
| 92 |
+
"cam_right_wrist":np.ndarray, # uint8 [3, H, W]
|
| 93 |
+
},
|
| 94 |
+
"prompt": str,
|
| 95 |
+
}
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
---
|
| 99 |
+
|
| 100 |
+
## Output
|
| 101 |
+
|
| 102 |
+
`policy.infer(observation)["actions"]` returns an **action chunk**:
|
| 103 |
+
|
| 104 |
+
- Shape **`[50, 14]`** β 50 future timesteps (`action_horizon=50`), 14-D per step.
|
| 105 |
+
- **Absolute joint-position targets** in Aloha convention, same 14-D order as `state`.
|
| 106 |
+
- De-normalized to physical units (you feed raw, you get raw).
|
| 107 |
+
- At **25 Hz**, the 50-step chunk β 2 s of motion. Typical control: execute the first *k* actions, then re-infer with the new observation.
|
| 108 |
+
|
| 109 |
+
### Delta vs. absolute
|
| 110 |
+
|
| 111 |
+
This config trains with `use_delta_joint_actions = True`, installing a paired transform around the model:
|
| 112 |
+
|
| 113 |
+
- **Training input** β `DeltaActions(mask)`: masked dims become **(target β current_state)** = deltas.
|
| 114 |
+
- **Inference output** β `AbsoluteActions(mask)`: masked dims become **(delta + current_state)** = absolute.
|
| 115 |
+
|
| 116 |
+
Mask `make_bool_mask(6, -1, 6, -1)` = `[TrueΓ6, False, TrueΓ6, False]`:
|
| 117 |
+
|
| 118 |
+
| dims | joints | mask | model learns | returned |
|
| 119 |
+
|---|---|---|---|---|
|
| 120 |
+
| 0β5, 7β12 | 6 arm joints per arm | `True` | **delta** | **absolute** (state re-added on output) |
|
| 121 |
+
| 6, 13 | grippers | `False` | absolute | absolute |
|
| 122 |
+
|
| 123 |
+
So the network internally predicts arm-joint **deltas**, but `AbsoluteActions` adds back the observation's `state`, so the policy returns **absolute joint-position targets**. Grippers are absolute throughout.
|
| 124 |
+
|
| 125 |
+
**Practical implications for eval:**
|
| 126 |
+
- Send the returned `actions` **directly** as target joint positions β do **not** add current state yourself; the output transform already did.
|
| 127 |
+
- `AbsoluteActions` broadcasts the single observation `state` across all 50 timesteps.
|
| 128 |
+
- Feed the robot's true current joint positions as `state` (it's the base the arm deltas are added to).
|
| 129 |
+
|
| 130 |
+
---
|
| 131 |
+
|
| 132 |
+
## How to run inference (openpi, PyTorch)
|
| 133 |
+
|
| 134 |
+
Requires an openpi env with **PyTorch** (this project's `pi05_pt` conda env) and the `pi05_robopro_top_cam_pt` train config. The exact config is in this folder as **`train_config.py`** β its entry already exists in the training repo's `src/openpi/training/config.py`.
|
| 135 |
+
|
| 136 |
+
`create_trained_policy` **auto-detects PyTorch** by the presence of `model.safetensors` in the checkpoint dir (no code change vs. the JAX call):
|
| 137 |
+
|
| 138 |
+
```python
|
| 139 |
+
from openpi.policies import policy_config as _policy_config
|
| 140 |
+
from openpi.training import config as _config
|
| 141 |
+
|
| 142 |
+
train_config = _config.get_config("pi05_robopro_top_cam_pt")
|
| 143 |
+
|
| 144 |
+
# checkpoint_dir must contain model.safetensors + assets/ (this folder after download)
|
| 145 |
+
policy = _policy_config.create_trained_policy(
|
| 146 |
+
train_config,
|
| 147 |
+
"/path/to/robopro_jax_30000/pytorch_30000", # dir with model.safetensors + assets/
|
| 148 |
+
robotwin_repo_id="roboreal_lerobot", # picks assets/roboreal_lerobot/norm_stats.json
|
| 149 |
+
pytorch_device="cuda", # or "cpu"
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
# Build the observation (feed COUNTERTOP cam as cam_high; images CHW uint8)
|
| 153 |
+
obs = {
|
| 154 |
+
"state": state_14, # float32[14], absolute joints
|
| 155 |
+
"images": {
|
| 156 |
+
"cam_high": countertop_chw, # uint8[3,H,W]
|
| 157 |
+
"cam_left_wrist": left_chw,
|
| 158 |
+
"cam_right_wrist": right_chw,
|
| 159 |
+
},
|
| 160 |
+
"prompt": "put the mouse on the pad",
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
actions = policy.infer(obs)["actions"] # np.ndarray [50, 14], absolute joint targets
|
| 164 |
+
# execute actions[:k] on the robot, then re-infer
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
Notes:
|
| 168 |
+
- Loading is **auto-detected** as PyTorch because the checkpoint dir has `model.safetensors` (not `params/`).
|
| 169 |
+
- `norm_stats.json` **must** be present/loaded; without it actions are unnormalized and wrong.
|
| 170 |
+
- If your runtime provides differently-named observation keys, apply a repack so images land under `cam_high` / `cam_left_wrist` / `cam_right_wrist`, state under `state`, and set `prompt`.
|
| 171 |
+
- Weights are bf16; `create_trained_policy` casts selected params for inference automatically.
|