File size: 14,641 Bytes
c44adcd 9c5ac01 c44adcd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | # RoboPRO evaluation pipeline on GB10 β full reference (to recreate it)
This is the concrete, runnable description of the eval pipeline. On **this GB10 cluster** the whole
thing is already built and **group-readable** at `/shared_work/jack/eval_root` β same-cluster teams
can run it in place or copy it. To rebuild elsewhere, see the **Recreate checklist** at the end.
Companion docs: `GB10_EVAL_PIPELINE.md` (issues/gotchas + validated config), `ROBOPRO_EVAL_CROSSNODE_GB10.md`
(model+sim on separate nodes), `ROBOPRO_EVAL_SIM_AS_SERVER.md` (sim-as-server direction).
---
## 0. Layout
```
/shared_work/jack/eval_root
βββ env.sh # sets EVAL_ROOT, BUNDLE, HF_HOME, XVLA_PY, ROBOTWIN_PY, sources /shared_work/jack/.env (HF token)
βββ code/RoboPRO/ # the benchmark: benchmark/ (bench_task_config/*.yml + assets) + customized_robotwin/ (sim + scripts)
β βββ customized_robotwin/script/{eval_policy_client.py, policy_model_server.py} + policy/dxvla/deploy_policy.py
βββ code/DA3-XVLA/ # model code (DA3_XVLA_ROOT) β models/ etc.
βββ envs/{robotwin, xvla, robotwin_b1} # the two run envs + the SAPIEN 3.0.0b1 swap pkg
βββ checkpoints/ , ablation_ckpts/ # model weights (k320, giants, geostack, ...)
βββ slurm_eval_node.sh # per-NODE worker entrypoint (self-stage -> b1 swap -> launch lease workers)
βββ stage_k320_node.sh , stage_giants_node.sh # node-local stagers
βββ organize_results.py # raw metrics -> runs/<model>/<scene>/<task>/seed<N>.mp4 + results.txt
βββ orchestrate_*.sh # multi-node drivers (clutter sweep, parallel-giants, fill-in)
βββ runs/<RUN>/ # per-run output: queue.txt, queue.txt.leases/, eval_result/, logs
/shared_work/jack/robopro-eval-pipeline # = $BUNDLE
βββ harness/eval_worker_steal_dualgpu.sh # the per-WORKER lease loop (model server + sim client)
βββ seeds/ # pre-validated seed banks + queue files (clean + clutter d6..d15)
```
## 1. Architecture (per node)
Two processes over a localhost TCP socket (**4-byte big-endian length + JSON-with-numpy**):
- **Model server** (`policy_model_server.py`, `xvla` env) β loads the policy via `get_model()` once, listens, returns a 30-step action chunk per observation.
- **Sim client** (`eval_policy_client.py`, `robotwin` env) β runs SAPIEN + the RoboPRO task **in-process**, sends `(3-cam RGB + proprio + instruction)` each step, executes the returned actions.
The model is the server, the sim connects (default). Cross-node and sim-as-server variants are env-flag selectable β see the companion docs.
## 2. Node-local staging (the key perf trick)
NFS mmap of envs/ckpts across many nodes stalls. So every node **self-stages to `/tmp/dxvla_local`**
on first job and runs from there; only one-time bulk rsyncs touch NFS.
- `stage_k320_node.sh` β base: `envs/{xvla,robotwin}`, `code/`, the DA3-BASE HF backbone, curobo build, asset symlink fixes.
- `stage_giants_node.sh` β per-model giant checkpoints + their HF backbones; keeps only `GIANT_KEEP`, prunes the rest (cache self-bounds ~40 GB/node; node disks are 3.6 TB).
- The model code can also be read from NFS via `GIANT_CODE_DIR` (small .py imports fine over NFS; only weights need node-local).
## 3. Per-node worker: `slurm_eval_node.sh`
Driven entirely by env vars. On each node it: (1) self-stages if cold, (2) **swaps SAPIEN to 3.0.0b1**
if `DXVLA_SAPIEN_B1=1`, (3) fixes asset symlinks, (4) refresh-rsyncs `script/`+`policy/`+`envs/`+bench
code from NFS (so edits propagate without a re-stage), (5) exports the eval knobs, (6) launches
`WORKERS_PER_GPU` copies of the lease worker and `wait`s. Run it as the SLURM payload:
```bash
srun --partition=gb10 --nodelist=<nodes> --nodes=N --ntasks-per-node=1 --gres=gpu:1 \
--cpus-per-task=18 bash /shared_work/jack/eval_root/slurm_eval_node.sh
```
## 4. Work-stealing lease queue (how N nodes cooperate + resume)
All workers (across all nodes) share **one run dir** and steal from **`runs/<RUN>/queue.txt`** (lines = `scene task`):
- `queue.txt.leases/` is a **directory of per-cell lock dirs** (hashed). A worker claims a cell by an
**atomic `mkdir`**; it heartbeats a timestamp; a **reaper** reclaims leases with no heartbeat for
`LEASE_TTL` (or whose owner died). β `rm -rf queue.txt.leases` to force a clean re-lease; `rm -f` does NOT clear it.
- **Resume is free**: with `EVAL_RESUME_SEEDS=1`, before running a cell the worker counts already-recorded
seeds in `eval_result/.../_metrics.jsonl` and runs only the missing ones. Re-launching the same run dir
skips finished work β that's how the sweeps pause/resume and how you "fill in" extra seeds (just bump
`EVAL_TEST_NUM` and rerun).
- One worker = one persistent model server + a loop of: claim cell β run `eval_policy_client` for the
missing seeds β mark done β next cell. `WORKERS_PER_GPU=1` (MPS is broken on Blackwell; sharing corrupts the sim).
## 5. Env-var reference
**Per-run (you set these / the orchestrator sets them):**
| var | meaning |
|---|---|
| `STUDY_RUN` | run dir name under `runs/` (shared lease queue + results) |
| `CKPT_NAME` | checkpoint dir name under `checkpoints/` or `ablation_ckpts/` |
| `GIANT_CODE_DIR` | model code dir β `DA3_XVLA_ROOT` (for ablation/giant/geostack models; omit for k320) |
| `BENCH_CONFIG` | `clean` or clutter `d6..d15` (β task_config `bench_demo_<scene>_<CONFIG>`) |
| `SEEDS_ROOT` | seed bank dir (`USE_EVAL_SEEDS=1` reads `<root>/<task>/<task_config>.txt`) |
| `EVAL_TEST_NUM` | seeds per cell (clean usually 5/10; clutter 2) |
| `WORKERS_PER_GPU` | **1** (do not raise; no MPS) |
| `DXVLA_SAPIEN_B1` | **1** β swap to SAPIEN 3.0.0b1 (required for correct office/kitchen) |
**Validated defaults (exported by `slurm_eval_node.sh`, override via `${VAR:-default}`):**
`DXVLA_DENOISER=optix` (OIDN no-op on Blackwell) Β· `XVLA_POSED_DA3=1` Β· `XVLA_RGB_INPUT=1` Β·
`XVLA_SKIP_GEOMETRY_REINIT=1` Β· `DXVLA_ACTION_SMOOTH=0.7` `DXVLA_SMOOTH_ROT=1` (cross-chunk smoothing) Β·
`DXVLA_PRIMARY_CAM=countertop` `DXVLA_VIDEO_CAM=countertop_camera` Β· `DXVLA_FAKE_PROPRIO=1` Β·
`DXVLA_RT_SPP=32` Β· `DXVLA_EXEC_ACTIONS` (0=execute full 30; 20=receding-horizon) Β·
`EVAL_RESUME_SEEDS=1` `QUEUE_MODE=lease` `LEASE_TTL` Β· `STALL_TIMEOUT` `PER_TASK_TIMEOUT` Β·
`CUROBO_IK_FAIL_RETURN` Β· `EVAL_FAST_CTRL=1` Β· `VK_ICD_FILENAMES=.../nvidia_icd.json` Β·
node-local `WARP_CACHE_PATH`/`CUDA_CACHE_PATH`. Cross-node: `MODEL_SERVER_BIND`/`MODEL_SERVER_HOST`.
Sim-as-server: `SIM_AS_SERVER`/`MODEL_AS_CLIENT` + `SIM_SERVER_HOST`/`SIM_SERVER_PORT`.
## 6. Running it
**One config (e.g. k320, clean, 5 seeds) on a node pool** β set up the run dir + queue, then srun the pool:
```bash
source /shared_work/jack/eval_root/env.sh
RUN=k320_clean; mkdir -p runs/$RUN/eval_result
cp $BUNDLE/seeds/k320_first10_queue.txt runs/$RUN/queue.txt; rm -rf runs/$RUN/queue.txt.leases
DXVLA_SAPIEN_B1=1 STUDY_RUN=$RUN CKPT_NAME=da3-xvla-k320-ckpt90k BENCH_CONFIG=clean \
SEEDS_ROOT=$BUNDLE/seeds/k320_first10_seeds EVAL_TEST_NUM=5 WORKERS_PER_GPU=1 \
srun --partition=gb10 --nodelist=trt-gb10-[3-15] --nodes=13 --ntasks-per-node=1 \
--gres=gpu:1 --cpus-per-task=18 -t 600 --kill-on-bad-exit=0 \
bash /shared_work/jack/eval_root/slurm_eval_node.sh
python organize_results.py $RUN k320 # -> runs/k320/<scene>/<task>/seed<N>.mp4 + results.txt
```
All 13 workers share `runs/$RUN/queue.txt` and steal cells; resume by rerunning the same block.
Giants/geostack: add `GIANT_CODE_DIR=<code dir>` (+ `XVLA_DA3_NATIVE_INPUT=1` for the K=160 giants;
geostack uses `DXVLA_GEOSTACK_STEP=20000` and reloads DA3-Large β see its `EVAL_INTERFACE.md`).
Clutter sweep / parallel multi-model / fill-in: see `orchestrate_clutter_k320.sh`,
`orchestrate_parallel_giants.sh`, `orchestrate_fillin.sh` (patterns below).
## 6b. Reproduce OUR exact eval config
Copy these verbatim to match our numbers (k320 clean reference = **58% SR / 49% HSR**):
```bash
DXVLA_SAPIEN_B1=1 # SAPIEN 3.0.0b1 (NOT 3.0.3) β required for correct office/kitchen physics
WORKERS_PER_GPU=1 # no MPS on Blackwell; >1 corrupts the sim
DXVLA_DENOISER=optix # OptiX denoiser; OIDN is a SILENT no-op on Blackwell (don't use it)
DXVLA_RT_SPP=32 # ray-trace samples/pixel (render quality)
XVLA_POSED_DA3=1 # feed camera extrinsics+intrinsics to DA3 (geometry models)
XVLA_RGB_INPUT=1
XVLA_SKIP_GEOMETRY_REINIT=1 # keep trained geometry weights (do NOT re-init them)
DXVLA_FAKE_PROPRIO=1 # proprio = last commanded action (we eval without true proprio)
DXVLA_ACTION_SMOOTH=0.7 # cross-chunk EMA smoothing (see 6d)
DXVLA_SMOOTH_ROT=1 # smooth xyz + rotation, not just translation
DXVLA_EXEC_ACTIONS=0 # 0 = execute the full 30-action chunk; 20 = receding-horizon
DXVLA_PRIMARY_CAM=countertop DXVLA_VIDEO_CAM=countertop_camera # overhead main camera
USE_EVAL_SEEDS=1 EVAL_RESUME_SEEDS=1 EVAL_TEST_NUM=<N> # seeded + resume
```
k320 used `DXVLA_EXEC_ACTIONS=0` (full 30-chunk); the giant ablations used `=20` (generate 30 / execute 20 / re-query). Keep it consistent within any comparison. These are also the `slurm_eval_node.sh` defaults (override via env).
## 6c. Seed banks β use the SAME seeds as us
Everything under **`$BUNDLE/seeds/`** (`$BUNDLE=/shared_work/jack/robopro-eval-pipeline`, group-readable). Set `SEEDS_ROOT` + `USE_EVAL_SEEDS=1`; the eval reads `<SEEDS_ROOT>/<task>/<task_config>.txt` β space-separated, pre-validated **solvable** episode seeds.
| eval | `SEEDS_ROOT` | queue file | seeds/task |
|---|---|---|---|
| **clean** | `k320_first10_seeds` | `k320_first10_queue.txt` (79 tasks) | 10 (40000β40009) |
| **clutter d6βd15** | `clutter_seeds_hf` | `clutter_queue_hf_d<N>.txt` | 2 per (task, level) |
| clean seeds 5β9 (10-seed fill) | `k320_seeds5to9` | `k320_first10_queue.txt` | 5 (40005β40009) |
Example: `k320_first10_seeds/put_cup_in_box/bench_demo_study_clean.txt` β `40000 40001 β¦ 40009`. To reproduce our **exact episodes**, point `SEEDS_ROOT` at these and use the same `EVAL_TEST_NUM` (clean = 5 or 10; clutter = 2). The clutter banks were rebuilt to match the public vanilla reference (`JackLiu0406/xvla-vanilla-backup/.../2026-05-01_xvla_eval_clutter`), so clutter SR is directly comparable to it.
## 6d. Action smoothing (the 0.7)
`policy/dxvla/deploy_policy.py` (~L279) applies **cross-chunk EMA smoothing** so consecutive 30-action chunks don't jerk at the boundary (we saw a visible gap without it):
- `DXVLA_ACTION_SMOOTH=0.7` β EMA weight Ξ±. Each new chunk's first action = `Ξ±Β·(prev chunk's last EXECUTED action) + (1-Ξ±)Β·(raw first action)`, carried across chunks via `model._last_smoothed_action`.
- `DXVLA_SMOOTH_ROT=1` β smooth **xyz + rot6d** (`cont_idx = [0:9]+[10:19]`, skipping the gripper at idx 9/19); `=0` β translation only, orientation raw.
- `DXVLA_ACTION_SMOOTH=0` disables it. **All our final numbers used `0.7` + `SMOOTH_ROT=1`.**
## 6e. Swap in YOUR model (the wrapper)
The policy is loaded by **`policy/dxvla/deploy_policy.py` β `get_model(usr_args)`** (L230), which builds the **`DXVLA` wrapper in `policy/dxvla/dxvla_model.py`** (`XVLA.from_pretrained`, the DA3 weight reload, and the per-step `generate_actions`). `deploy_policy.yml` is its config. The model **server** (`policy_model_server.py`) calls this wrapper; the sim/socket/lease-queue/scoring are model-agnostic.
To run a different model, replace the wrapper:
1. Provide a `get_model(usr_args)` returning an object with
**`generate_actions(input_ids, image_input, image_mask, domain_id, proprio, ...) -> [B, num_actions, action_dim]`**
β inputs arrive already tokenized/preprocessed (`image_input` = `[B, V, 3, H, W]`, language in `input_ids`, `proprio`); output is a 30-step action chunk (`ee6d`: 10-D = xyz + rot6d + gripper).
2. Either drop your code under `policy/<name>/` and pass `--config policy/<name>/deploy_policy.yml --policy_name <name>`, or reuse the `dxvla` wrapper and just set `CKPT_NAME` (+ `GIANT_CODE_DIR` for a bundled-code model).
3. Post-load steps (e.g. reload a frozen backbone, set a schedule) go inside `get_model` β see the GeoStack branch in `dxvla_model.py` and each checkpoint's `EVAL_INTERFACE.md`.
## 7. Output & scoring
Raw per-episode metrics stream to `runs/<RUN>/eval_result/<task>/dxvla/bench_demo_<scene>_<cfg>/.../_metrics.jsonl`
(`{"task","seed","success","hard_success",...}`). `organize_results.py <RUN> <model_name>` dedups latest-per-seed
and writes:
- `runs/<model>/<scene>/<task>/seed<N>.mp4` (videos)
- `runs/<model>/<scene>/results.txt` (per-task SR/HSR)
- `runs/<model>/results.txt` (per-scene SR/HSR + TOTAL)
SR = success rate; HSR = "hard" success (success **and** no collision).
## 8. Orchestrator patterns (the 3 drivers)
- **Single-config sweep** (`orchestrate_clutter_k320.sh`): for each `BENCH_CONFIG` in a list, one blocking
`srun` over the pool on its queue; resume-skips completed configs; aggregates SR per config.
- **Parallel multi-model** (`orchestrate_parallel_giants.sh`): N dedicated 1-node workers (one model each,
continuous) + an M-node pool that sweeps the models in sequence, all cooperating per model via the shared
lease queue β every model makes progress at once.
- **Fill-in / resume** (`orchestrate_fillin.sh`): rerun a run dir at a higher `EVAL_TEST_NUM` (resume adds only
the missing seeds), or run a disjoint seed bank and merge β no redundant episodes.
## 9. Recreate checklist (to run on a DIFFERENT machine)
1. **Benchmark code+assets**: RoboPRO is a customized RoboTwin 2.0 β get RoboTwin 2.0 + its assets (~15 GB),
apply the `customized_robotwin` deltas (this repo's `code/RoboPRO`).
2. **Two envs**: `robotwin` (SAPIEN, curobo, gymnasium...) and `xvla` (torch, transformers, the model code).
On aarch64/CUDA-13/Blackwell, **build SAPIEN 3.0.0b1 from source** (see `SAPIEN_B1_ATTEMPT.md`); on x86 you
may not need the b1 swap but pin the simulator version to match reference numbers.
3. **Checkpoints**: the model weights + their code dirs (+ `EVAL_INTERFACE.md` per ckpt).
4. **Seeds**: pre-validated seed banks (`$BUNDLE/seeds/...`) or regenerate with `precollect_eval_seeds.py`.
5. **Scripts**: `slurm_eval_node.sh`, `stage_*_node.sh`, `harness/eval_worker_steal_dualgpu.sh`,
`organize_results.py`, the orchestrators β adjust node names/partition/paths.
6. Apply the **GB10 gotchas** in `GB10_EVAL_PIPELINE.md` (WPG=1, OptiX, node-local staging, ffmpeg on PATH, etc.).
```
|