verapulse's picture
Upload report.md with huggingface_hub
7225630 verified
|
Raw
History Blame Contribute Delete
10.9 kB
# Decoder-at-Layer Sweep for OpenVLA-OFT — Technical Report
**Question studied:** when OpenVLA-OFT decodes robot actions from its final LLM layer,
is the final layer's advantage due to *better features* or to *feature–decoder
compatibility*? The answer determines whether the model's depth can be cut at inference.
**Summary of findings:** attaching and training the real action head at intermediate
layers recovers 94–100/100 closed-loop success from layer 16 upward (half the network);
an 8.4M-parameter adapter in front of the *frozen original* head recovers most of it;
and tuning as few as 4 transformer blocks recovers full baseline success from layer 12
— i.e. with 51% of the deployed parameters removed. The final layer holds no privileged
action information; its advantage is a small, learnable compatibility transformation.
**Setup:** OpenVLA-OFT 7B with the L1 regression action head
(`moojink/openvla-7b-oft-finetuned-libero-spatial`), LIBERO-Spatial,
`libero_spatial_no_noops` data, seed 7. Offline evaluation: 1,491 action-chunk samples
(first 10 episodes per task). Readout training: 20 disjoint episodes per task.
Closed-loop evaluation: 10 trials × 10 tasks per configuration, fixed benchmark initial
states. Hardware: A100-SXM4-40GB, torch 2.2.0+cu121 (exact pins: `env/pip_freeze.txt`).
All analysis code: `code_snapshot/`.
## 1. Experimental-stack validation
| Check | Result |
|---|---|
| Model + headless-simulator smoke tests | pass |
| Offline final-layer L1 (1,491 samples) | **0.008120** (reference value 0.0081, tolerance ±5e-4) |
| Live bitwise invariant (instrumented layer-32 decode == stock output) | 0.0 on every sample of every run |
| Stock predictions vs reference artifacts (`baseline_reference/`) | **bitwise identical (max dev 0.0)** |
| Control: layer-16 linear probe retrained on the fresh extraction | L1 0.1733 vs reference 0.1724 (tolerance 0.01) |
The evaluation stack reproduces the reference predictions exactly, so differences
reported below are attributable to the interventions.
**Replicate:** `python code_snapshot/gate2_check.py`, `python code_snapshot/gate3_probe_check.py`;
recomputed independently by `code_snapshot/verify_results.py` (checks 1–2).
## 2. Main result: success vs attach layer
The real `L1RegressionActionHead` (151M params) is fresh-initialized per layer and
trained on cached action-token hidden states with the LLM frozen (lr 3e-4, 60 epochs,
cosine schedule, early stopping on a 10%-episode validation split; the recipe was tuned
at layer 32 only and then applied unchanged to all layers).
| Attach layer | 1 | 2 | 6 | 12 | 16 | 20 | 24 | 28 | 30 | 31 | 32 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Offline eval L1 | .205 | .189 | .168 | .153 | .129 | .095 | .048 | .024 | .019 | .017 | .013 |
| **Rollout success /100** | **11** | **24** | **22** | **77** | **97** | **94** | **97** | **99** | **100** | **99** | **100** |
Reference points: full model with the original head 98/100; a 200k-parameter linear
probe at layer 16 achieves 77/100; the original head reused *frozen* at layer 31
achieves 70/100 (probe and reference records in `baseline_reference/`).
**Findings:**
1. **Reusing the pretrained head at intermediate layers fails for compatibility
reasons, not information reasons.** With a per-layer retrained head, offline L1 is
strictly monotonic in depth (0.205 → 0.013); the error spike that the frozen
pretrained head shows mid-network (peaking at layer 24) vanishes. Closed-loop,
layer 24 rolls out at 97/100.
2. **No cliff at the final layer.** Retrained heads at layers 16–32 all land at
94–100/100. **Verdict: the final-layer advantage is a compatibility artifact, fully
repaired by retraining the head at the attach layer.**
3. **The information boundary for static readouts sits between layers 12 and 16**
(77 → 97). Below it the curve collapses (22/100 at layer 6; the 11–24 floor reflects
tasks solvable by trivial policies). Early exit at half depth costs ~1 point of
success (97 vs the 98 full-model reference; the retrained head at layer 32 itself
reaches 100).
4. **Readout capacity matters closed-loop:** at layer 16, the real head reaches 97
vs the linear probe's 77 on identical training data.
5. **Offline action metrics do not predict closed-loop success** — the offline L1
ordering across layers 16–24 does not match the rollout plateau, and §4 shows a
starker case. All accept/reject conclusions here rest on rollouts only.
**Representation analysis (linear CKA, action-token positions, all 33 layers):**
adjacent-layer similarity is 0.95–0.99 through the mid-network, with three landmarks: a
large early reorganization at layers 2–3 (CKA 0.11–0.17), a dip at 27–28 (0.94), and
0.92 at 31→32 (the final entry includes the last RMSNorm). Similarity to the final
layer rises smoothly from 0.33 (layer 3) to 0.91 (layer 28+): representations converge
gradually — consistent with the smooth success curve and inconsistent with any single
"decision layer". Values: `cka_results.json`.
**Replicate:** every number above recomputes from the raw artifacts in this repository
via `python code_snapshot/verify_results.py --results-dir . --ref-dir baseline_reference --skip-cka`
(exit 0 = pass). Sweep table: `sweep_results.json`; per-episode records:
`rollout_head_layer*.json`.
## 3. Compatibility mechanism: adapters into the frozen original head
Adapter = token-wise residual MLP, LayerNorm→4096→1024→GELU→4096 (+x), **8.4M params**
(1/18 of the head's size), same training recipe. Variant A trains ONLY the adapter and
feeds the **frozen original checkpoint head**; variant B trains adapter + fresh head.
| Rollout success /100 at layer | 12 | 16 | 30 |
|---|---|---|---|
| A: adapter + frozen original head | 77 | 92 | 98 |
| B: adapter + trained head | 81 | 96 | 99 |
| Retrained full head (151M, §2) | 77 | 97 | 100 |
| Offline eval L1 (A) | .149 | .119 | .014 |
**Findings:**
1. **The compatibility artifact is a small, learnable transformation.** An 8.4M
projector suffices for the *untouched* original head to read layer-30 features at
98/100 (= full-model reference) and layer-16 features at 92/100.
2. **At layer 12, readout capacity is irrelevant:** adapter+frozen head (77),
adapter+trained head (81), and the 151M retrained head (77) are statistically
indistinguishable (SE ≈ 4). No *static readout* of frozen layer-12 features exceeds
~80/100 — §4 shows this ceiling belongs to the frozen features, not the information.
3. Offline, the adapter variants match or beat the retrained head at every layer —
another instance of offline metrics compressing real closed-loop differences.
**Replicate:** `adapter_results.json` + `rollout_adapter*_layer*.json`; recomputed by
the verify script. Training code: `code_snapshot/train_adapters2.py`.
## 4. Tuned attention blocks below layer 12
Attach at layer 12 and jointly train the head plus the last k decoder blocks below it
(blocks 12−k .. 11; block lr 2e-5, head lr 3e-4, 12 epochs, early stopping). Training
and deployment share an identical block-recompute path from a layer-8 full-sequence
cache, gated functionally: the recompute with original weights reproduces the layer-12
sweep result exactly (offline L1 0.1531) before any training.
| k tuned blocks | 0 (frozen, §2) | 1 | 2 | 4 |
|---|---|---|---|---|
| Trainable params | 151M (head) | +202M | +404M | +808M |
| Offline eval L1 | .153 | .142 | .142 | .140 |
| **Rollout success /100** | **77** | **93** | **95** | **98** |
**Findings:**
1. **The layer-12 ceiling is shallow.** One tuned block lifts closed-loop success
77 → 93; four tuned blocks reach **98/100 = full-model reference** while exiting at
layer 12 (20 of 32 blocks, the final norm, and the LM head removed).
2. Contrast with §3: at layer 12, more *readout* capacity never beat 81 — but letting
*attention over the full sequence* adapt recovers everything. What limits layer 12
is cross-token integration, not information in the action-token features.
3. **The sharpest offline↔rollout dissociation observed:** offline L1 improves 7%
(0.153 → 0.142, flat in k) while rollout success moves 77 → 93 → 95 → 98. Offline
metrics cannot rank these configurations at all.
**Replicate:** `unfreeze_results.json` + `rollout_unfreeze_k*.json`; checkpoints in
`unfreeze/`; training code `code_snapshot/train_unfreeze.py`, rollout code
`code_snapshot/rollout_unfreeze.py`. Note: the LLM uses **bidirectional** attention
(not causal) — `code_snapshot/unfreeze_common.py` documents the verified re-run
semantics and the bf16 kernel-noise bounds.
## 5. The assembled minimum-parameter model
`min_model/model.safetensors` packages the best size/accuracy point (§4, k=4): vision
encoders + projector + embeddings + LLM blocks 0–11 (8–11 tuned) + proprio projector +
action head.
| | Params | Size | LIBERO-Spatial success |
|---|---|---|---|
| Full OpenVLA-OFT stack | 7.709 B | ~15.5 GB | 98/100 |
| **min_model (exit@12, 4 tuned blocks)** | **3.772 B (48.9%)** | **7.9 GB** | **98/100** |
Parameter count, not latency, is the claim: the rollout above ran the full forward with
the tuned blocks applied on the side (`rollout_unfreeze_k4.json`); a truncated forward
implementation is required to realize wall-clock savings.
**Replicate/verify:** every tensor's SHA256 and source (public base checkpoint or
`unfreeze/unfreeze_k4.pt`) is recorded in `min_model/manifest.json`; check with
`python code_snapshot/verify_min_model.py --bundle min_model --unfreeze-ckpt unfreeze/unfreeze_k4.pt`
(add `--check-base` to byte-compare all frozen tensors against the base repo).
## 6. Included measurement data
Every rollout logged, per policy query, the executed normalized action chunk and the
stock (original head, layer 32) chunk (`rollout_*.queries.npz`). Mean |executed −
stock| grows with attach distance from the final layer (0.010 at layer 32 → 0.084 at
layer 16) and rises modestly within episodes (layer 16: 0.073 in the first third of an
episode → 0.087 in the last third) — bounded drift, consistent with the high success
rates. Complete run logs are in `logs/`.
## 7. Replication guide
Three tiers, detailed in `REPRODUCING.md`:
1. **Verify every reported number** (CPU, ~2 min): the two verify commands above.
2. **Re-run rollouts with the released checkpoints** (1 GPU, hours):
`env/setup_env.sh` rebuilds the exact environment; `code_snapshot/rollout_eval2.py`
/ `rollout_unfreeze.py` re-run any configuration with fixed seeds and benchmark
initial states.
3. **Re-derive everything from public sources** (~1 GPU-day): base checkpoint and
dataset are public; extraction, training, and rollout scripts include built-in
gates (bitwise stock-reproduction, probe-reproduction control, sweep-reproduction
check) that re-validate the chain at each stage.