add eval I/O contract README
Browse files
pi_behavior_100t_meta_ft_task54_da3/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PiBehavior DA3 β Evaluation I/O Contract
|
| 2 |
+
|
| 3 |
+
This checkpoint is a **single-task DA3 finetune** of the PiBehavior VLA (BEHAVIOR-1K 2026), initialized from the 100-task meta (`meta100-1epoch/step69999`). It is a pi0-style flow-matching policy: PaliGemma **gemma_2b** VLM + **gemma_300m** action expert (bf16), `num_tasks=100`, `action_horizon=30`, `action_dim=32`, 3 RGB views @ 224Β², **plus a DA3 depth/spatial branch**.
|
| 4 |
+
|
| 5 |
+
This doc is the exact **input/output contract** for your inference loop β what tensors to feed the model and how to decode what it returns. (It does not cover the sim wrapper; you have your own inference path.)
|
| 6 |
+
|
| 7 |
+
> **`task_index` for this checkpoint** β set the per-step task id to the trained task:
|
| 8 |
+
> `task54 = putting_away_toys` β **54** Β· `task56 = make_rose_centerpieces` β **56** Β· (use the id matching this repo's task).
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
## β οΈ Read first: the DA3 branch is opt-in and OFF unless you feed it
|
| 13 |
+
|
| 14 |
+
The model's DA3 branch (`spatial_bank_builder`) **contributes nothing unless the `da3_*` observation fields are populated.** Internally:
|
| 15 |
+
|
| 16 |
+
```python
|
| 17 |
+
# pi_behavior.py _compute_banks
|
| 18 |
+
if self.spatial_bank_builder is None or observation.da3_features is None:
|
| 19 |
+
return None # DA3 conditioning is skipped entirely
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
So if you feed only RGB+state+task (leaving `da3_features=None`), this DA3-trained checkpoint runs as an **RGB-only model** β a train/eval mismatch that will degrade it, since the base transformer was trained *with* DA3 conditioning active. **To evaluate DA3 faithfully you must populate the `da3_*` tensors** (Β§ Input B). If you want the RGB baseline instead, use the `noda3` checkpoint, not this one.
|
| 23 |
+
|
| 24 |
+
---
|
| 25 |
+
|
| 26 |
+
## 0. Model load
|
| 27 |
+
|
| 28 |
+
```python
|
| 29 |
+
import dataclasses, jax.numpy as jnp
|
| 30 |
+
from b1k.training import config as _config
|
| 31 |
+
import openpi.models.model as _model
|
| 32 |
+
|
| 33 |
+
cfg = _config.get_config("pi_behavior_b1k_fast")
|
| 34 |
+
# num_tasks and da3 are NOT stored in the checkpoint β you MUST reconstruct them
|
| 35 |
+
# to match training or model.load() fails the strict shape check.
|
| 36 |
+
model_cfg = dataclasses.replace(cfg.model, num_tasks=100, da3=<B1KDA3Config matching training>)
|
| 37 |
+
cfg = dataclasses.replace(cfg, model=model_cfg)
|
| 38 |
+
|
| 39 |
+
params = _model.restore_params(f"{ckpt}/params", dtype=jnp.bfloat16)
|
| 40 |
+
model = model_cfg.load(params)
|
| 41 |
+
model.load_correlation_matrix(norm_stats) # REQUIRED (use_correlated_noise=True)
|
| 42 |
+
model.eval()
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
- **`num_tasks=100` is mandatory** β the default config is 50; loading the 100-row task-embedding table into a 50-task model fails the shape check.
|
| 46 |
+
- **DA3 config must match training** so the `spatial_bank_builder` params exist to receive the weights. Flags for this checkpoint family:
|
| 47 |
+
`da3_channels=1536`, `out_layers=(19,27,33,39)`, `num_inject_layers=6`, `grid_hw=(16,16)`, `backbone_only=True`, `cam_frame_ray=True`, `spatial_inject_vlm=False`, `cross_view=False`, `use_perceiver=False`, `lang_fusion_depth=0`, `spatial_vec=True`, `spatial_film=True`, `spatial_use_da3=True`, `fourier_bands=10`, `qk_norm=True`, `inject_dropout=0.1`.
|
| 48 |
+
- **Assets** (bundled under `assets/IliaLarchenko/behavior_224_rgb/`): `norm_stats.json` (contains the correlation matrix β required by `load_correlation_matrix`) and the FAST `fast_tokenizer/` (training-aux; **not used at inference**). `asset_id = "IliaLarchenko/behavior_224_rgb"`.
|
| 49 |
+
|
| 50 |
+
---
|
| 51 |
+
|
| 52 |
+
## Input A β base observation (always required)
|
| 53 |
+
|
| 54 |
+
The model's `sample_actions(rng, Observation, num_steps=20)` consumes an `Observation` with these fields (batch dim `b`):
|
| 55 |
+
|
| 56 |
+
| field | shape | dtype | notes |
|
| 57 |
+
|---|---|---|---|
|
| 58 |
+
| `images["base_0_rgb"]` | `[b,224,224,3]` | float32 | head (zed) camera |
|
| 59 |
+
| `images["left_wrist_0_rgb"]` | `[b,224,224,3]` | float32 | left realsense |
|
| 60 |
+
| `images["right_wrist_0_rgb"]` | `[b,224,224,3]` | float32 | right realsense |
|
| 61 |
+
| `image_masks[*]` | `[b]` | bool | all `True` |
|
| 62 |
+
| `state` | `[b,32]` | float32 | normalized proprio (see below) |
|
| 63 |
+
| `tokenized_prompt` | `[b,2]` | int32 | **`[task_id (0β99), current_stage]`** β not text |
|
| 64 |
+
| `tokenized_prompt_mask` | `[b,2]` | bool | `[True,True]` |
|
| 65 |
+
| `subtask_state` | `[b]` | int32 | `= current_stage` (**required**, else the model raises) |
|
| 66 |
+
| `fast_tokens` | β | β | **must be `None`** (inference raises if present) |
|
| 67 |
+
|
| 68 |
+
**Images:** HWC, RGB order, values in **`[-1,1]`** (`Observation.from_dict` does `uint8/255*2 β 1` β if you build the `Observation` yourself, apply this). Resize/pad to 224Β² first.
|
| 69 |
+
|
| 70 |
+
**State (32-d):** build the 23 real dims in this order, then zero-pad to 32:
|
| 71 |
+
`[base_qvel(3), trunk_qpos(4), left_arm(7), left_gripper(1), right_arm(7), right_gripper(1)]`.
|
| 72 |
+
Grippers: rescale raw `[0,0.1] β [-1,1]` via `2*(raw/0.1)-1`. Then **z-score normalize** with `norm_stats["state"]` (regular mean/std, `use_quantile_norm=False`): `(x-mean)/(std+1e-6)`. Dims 23:32 stay 0.
|
| 73 |
+
|
| 74 |
+
**Task / stage:** `task_id` indexes the learned task table (must be a trained id, 0β99). `current_stage` is the subtask index; at t=0 use 0, then update it from the model's `subtask_logits` output (below). The model **always uses the stage you pass in** β it does not self-advance inside the forward.
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
## Input B β DA3 tensors (required to activate the depth branch)
|
| 79 |
+
|
| 80 |
+
Populate these (else the DA3 branch is skipped β see the warning at top). These mirror what the **training** data pipeline produced with a frozen **`depth-anything/DA3-GIANT-1.1`** backbone run on the 3 RGB views; you must reproduce that at eval:
|
| 81 |
+
|
| 82 |
+
| field | shape | dtype | notes |
|
| 83 |
+
|---|---|---|---|
|
| 84 |
+
| `da3_features` | `[b,4,3,1536,16,16]` | uint16 (bf16-bits) | DA3-GIANT backbone feats: 4 out-layers (19,27,33,39) Γ 3 views Γ 1536ch Γ 16Γ16 grid |
|
| 85 |
+
| `da3_ray` | `[b,3,3,16,16]` | float32 | per-view intrinsic ray directions, **camera frame** (`cam_frame_ray=True`) |
|
| 86 |
+
| `da3_depth` | `[b,3,1,16,16]` | float32 | **GT metric depth in METERS** per view β DA3-GIANT has no metric head, so this comes from the sim's depth render (`B1K_USE_GT_DEPTH=1`), downsampled to the 16Γ16 grid |
|
| 87 |
+
| `camera_extrinsics` | `[b,3,4,4]` | float32 | per-view cameraβworld 4Γ4 |
|
| 88 |
+
| `lang_feat` | `[b,32,1024]` | float32 | ModernBERT-large embedding of the task name |
|
| 89 |
+
| `lang_mask` | `[b,32]` | bool | token mask for `lang_feat` |
|
| 90 |
+
|
| 91 |
+
How to produce them (same as training): run DA3-GIANT-1.1 (via the `Depth-Anything-3` repo / `da3_for_geostack.py` wrapper) on the 3 RGB views to get backbone `feats` + intrinsic `rays`; supply the sim's GT depth as `da3_depth` (meters); provide camera extrinsics; and embed the task-name string with ModernBERT-large for `lang_feat`/`lang_mask` (a per-task cache is fine). `da3_features` is stored as the raw bf16 bit-pattern in uint16 β cast the bf16 feature tensor to uint16 bits, matching the training extractor.
|
| 92 |
+
|
| 93 |
+
> If reproducing full DA3 extraction is impractical for your harness, note that the RGB-only fallback (leave `da3_*` = `None`) does **not** give a valid DA3 eval β prefer the `noda3` checkpoint for the RGB number and this one only when you can feed DA3.
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
## Output β decoding
|
| 98 |
+
|
| 99 |
+
`sample_actions` returns a **tuple `(actions_raw, subtask_logits)`**.
|
| 100 |
+
|
| 101 |
+
**Actions**
|
| 102 |
+
- `actions_raw`: shape **`[b, 30, 32]`**, in **normalized** space (flow-matching sample; Euler-integrated, default **`num_steps=20`** denoise steps, correlated noise `beta=0.5`).
|
| 103 |
+
- **De-normalize** with the per-timestamp action stats (`norm_stats["actions"]`, mean/std shape `[30,32]`):
|
| 104 |
+
`actions = actions_raw * (per_timestamp_std + 1e-6) + per_timestamp_mean`
|
| 105 |
+
- **Keep the first 23 dims** (`actions[..., :23]`); dims 23:32 are zero-pad. Order matches the state:
|
| 106 |
+
`[base_vel(3), trunk(4), left_arm(7), left_gripper(1), right_arm(7), right_gripper(1)]`.
|
| 107 |
+
(Gripper action dims are in the normalized `[-1,1]` space β map back to your actuator range as your controller expects.)
|
| 108 |
+
- Execute as an action chunk (receding horizon); `initial_actions` may be passed on the next call to seed correlated inpainting (enforced while flow-time `t > 0.3`).
|
| 109 |
+
|
| 110 |
+
**Stage logits (for stage conditioning)**
|
| 111 |
+
- `subtask_logits`: shape **`[b, 15]`** (`MAX_NUM_STAGES=15`), invalid stages for the task set to `-inf`.
|
| 112 |
+
- `predicted_stage = int(argmax(subtask_logits))`. Feed the (optionally vote-smoothed) stage back as the next step's `subtask_state` / `tokenized_prompt[:,1]`, clamped to `TASK_NUM_STAGES[task_id]-1`.
|
| 113 |
+
|
| 114 |
+
**FAST tokens:** never produced or consumed at inference β ignore.
|
| 115 |
+
|
| 116 |
+
---
|
| 117 |
+
|
| 118 |
+
## Quick reference
|
| 119 |
+
- **Feed:** 3 RGB views `[-1,1]` + normalized 32-d state + `[task_id, stage]` + `subtask_state` **+ the 6 `da3_*` tensors** (to use DA3).
|
| 120 |
+
- **Get back:** `[b,30,32]` normalized actions β de-norm per-timestamp β take `[:, :23]`; plus `[b,15]` stage logits.
|
| 121 |
+
- **Denoise steps:** 20. **Correlated noise:** yes (matrix from `norm_stats`). **num_tasks:** 100. **FAST:** off.
|