mzxuan commited on
Commit
0ffeea4
Β·
verified Β·
1 Parent(s): c5224a4

Add model_params.md (inputs/outputs/config) for pytorch_30000

Browse files
Files changed (1) hide show
  1. 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.