FastWAM_UR3 / README.md
arashakb's picture
Add README.md (NVFP4 deflated ASP)
bb4ec89 verified
|
Raw History Blame Contribute Delete
8.73 kB
---
license: apache-2.0
library_name: pytorch
tags:
- robotics
- quantization
- nvfp4
- fp4
- w4a4
- svdquant
- world-action-model
- fastwam
base_model: armanakbari4/fastwam-ur3-3task-10k
datasets:
- armanakbari4/ur3-3task-lerobot
---
# FastWAM UR3 3-task — 4-bit quantisations
Two post-training 4-bit quantisations of the UR3 3-task FastWAM fine-tune (step 7000). Both store
the weight **packed at 4 bits** and contract it on 4-bit or integer tensor cores. Neither is a
fake-quant checkpoint that keeps 4-bit values inside 16-bit tensors.
| | `ur3_step7000_asp_nvfp4.pt` | `ur3_step7000_svdquant_w4a4.pt` |
|---|---|---|
| method | **ĀFQ / ASP, deflated form** | SVDQuant (Li et al., ICLR 2025) |
| numeric format | **NVFP4** — E2M1 elements, block 16, E4M3 block scales | INT4, group 64 |
| weights / activations | W4A4, both at block 16 | W4A4, both at group 64 |
| tensor cores | **FP4** (`torch._scaled_mm_v2`, recipe `BlockWise1x16`) | INT8 (exact for 4-bit codes) |
| size | 4.6117 BPW, **3.38 GiB** | 4.5798 BPW, 3.36 GiB |
| quantised-vs-bf16 action NRMSE | **0.0006** | 0.0010 |
| target GPU | **RTX 5090 / Blackwell** (sm_100, sm_103, sm_120) | L40S / Ada, Hopper (sm_89 tuned) |
The NVFP4 checkpoint is the one to run on an RTX 5090.
Both quantise the same 600 Linears — the two experts' 2 × 30 blocks × {`self_attn` q/k/v/o,
`cross_attn` q/k/v/o, `ffn.0`, `ffn.2`}, **5.914 B parameters**. Patch/text/time embeddings, heads,
the action encoder, norms, modulation and the proprio encoder stay in bf16. Both are
**self-contained**: the quantised Linears *and* every unquantised `mot` tensor *and* the proprio
encoder are inside the file, so the 11.2 GiB bf16 checkpoint is not needed at inference.
Both were calibrated on `armanakbari4/ur3-3task-lerobot` with **10 episodes per task × 3 tasks,
every frame, seed 42 — 9 908 observations**.
## What ASP is, and what the deflated form means
Action-Subspace Protection keeps a rank-32 subspace of each action-expert layer out of the 4-bit
grid. The subspace is not chosen by activation magnitude: it is the top eigenspace of the **action
metric** `G = E_o[JᵀJ]`, `J = ∂action/∂x`, differentiated through all ten denoising steps, so it is
the set of directions the *emitted action* is most sensitive to rather than the ones that happen to
be large.
Per layer, with `s` the smoothing vector, `H` a block Hadamard, `V` the rank-32 basis and
`W̃ = W·diag(s)` the smoothed weight, the deflated contract is
```
x̃ = (x / s) H
y = (x̃ V)(W̃V)ᵀ + NVFP4GEMM( (I − VVᵀ) x̃ , W̃(I − VVᵀ) ) + bias
```
**Deflated** means the protected subspace is subtracted from the 4-bit path on *both* sides: the
low-rank branch carries the exact `W̃V` and the FP4 weight holds `W̃(I − VVᵀ)`. The cheaper
shared-weight variant, which quantises one weight and stores `V` alone, is a different arm and a
measurably worse one at these group sizes. The rank-32 branch stays in bf16 by design — it carries
the directions the action depends on, which is the point of protecting them.
The per-layer smoothing `(α, β)` is not a fixed 0.5. It comes from a 39-candidate grid search
(`α ∈ {0, 0.05…0.95}`, `β ∈ {0, 1−α}`, per SVDQuant's protocol) scored on **real calibration
activations** with the objective being this layer's own deflated-ASP NVFP4 output MSE — the same
objective the deployed arm minimises, at the same granularity it is applied.
## Files
| file | size | |
|---|---|---|
| `ur3_step7000_asp_nvfp4.pt` | 3.38 GiB | **NVFP4 deflated-ASP checkpoint.** Weights E2M1 packed, E4M3 block scales pre-swizzled to `SWIZZLE_32_4_4` |
| `ur3_step7000_svdquant_w4a4.pt` | 3.36 GiB | INT4 SVDQuant checkpoint |
| `nvfp4.py` | 8 KB | the NVFP4 quantiser and `_scaled_mm_v2` wrapper — quantise, dequantise, scale swizzle, GEMM |
| `asp_nvfp4_runtime.py` | 9.6 KB | `ASPNVFP4Linear`, `install_asp_nvfp4`, `load_quantized_asp_model`. Self-contained; imports only torch and `nvfp4.py` |
| `ur3_3task_10k_dataset_stats.json` | 170 KB | proprio z-scoring and action denormalisation — **the checkpoint cannot be run correctly without it** |
| `ur3_prompt_embeddings.pt` | 3.0 MiB | the three task instructions, pre-encoded, so the 11 GB umT5-XXL text encoder is not needed at inference |
Besides these you need the Wan2.2 VAE (`Wan2.2_VAE.pth`, 2.7 GiB) to encode the camera image to the
video latent. You do not need the bf16 checkpoint, the text encoder, or the Wan2.2 DiT weights.
## Running the NVFP4 checkpoint
Requires a GPU with **FP4 tensor cores** — RTX 5090 (sm_120), B200/B300 (sm_100/sm_103) — and a
PyTorch with `torch._scaled_mm_v2`. Verified on torch 2.12.0+cu130.
```python
from asp_nvfp4_runtime import load_quantized_asp_model
model, cfg = load_quantized_asp_model(
"ur3_step7000_asp_nvfp4.pt",
build_model, # your own bf16 FastWAM constructor -> (model, cfg)
)
```
`build_model` supplies the module graph only; every tensor comes from the checkpoint. To quantise a
model you already built, call `install_asp_nvfp4(model, ckpt_path)` instead — it raises rather than
swapping a subset, because a partial swap is not a defined arm.
Full pipeline, kernels and the deployment guide: **https://github.com/arashakb/QuantWAM**
(`adapters/fastwam/`, `quantwam/kernels/nvfp4.py`, `adapters/fastwam/README_ur3_5090.md`). The
`UR3QuantPolicy` wrapper there takes **raw** `HxWx3` uint8 frames and the **raw** 14-d state and
applies the whole observation contract itself (top → 320×256, wrists → 160×128,
`[top ; [left|right]]` → 384×320, `*2/255 − 1`; state z-scored and clamped to ±5), so a caller
cannot get the preprocessing subtly wrong.
## Verification — NVFP4 deflated ASP
Everything below is measured on **held-out** episodes; the calibration episodes are excluded,
because a check run on fitted data cannot detect the failure it exists to detect.
**It is really 4-bit, not a simulation.** Read off the loaded model:
| | |
|---|---|
| `wq` dtype, all 600 layers | `torch.float4_e2m1fn_x2` |
| weight bytes resident | 2 820 MiB for 5.914 B weights = **4.00 bits/weight** (bf16 would be 16.00) |
| E4M3 block scales | 352.5 MiB |
| any dequantised weight copy anywhere | **none** |
| `torch._scaled_mm_v2` calls in one full inference | **3 300** = 300 video prefill × 1 + 300 action × 10 steps |
| recipes those calls used | `BlockWise1x16` only — i.e. NVFP4 |
**It computes the intended contract.** Recomputing each layer independently from the stored
transforms and comparing against the runtime: worst relative error **1.7e-3** across sampled action
and video layers, which is the bf16 output cast and not a form mismatch.
**The actions are right.** Twelve held-out observations spanning all three tasks:
| | NRMSE |
|---|---|
| **NVFP4 ASP vs bf16, action chunk** | **0.0006** |
| bf16 vs recorded actions | 0.0068 ← the ceiling |
| **NVFP4 ASP vs recorded actions** | **0.0066** (corr 0.9997) |
The quantised model sits as close to the recorded actions as the bf16 model does — marginally
closer on these frames, which is noise, not an improvement.
**Not yet measured: real-robot success rate.** The bf16 model has been evaluated on the physical
UR3; neither quantised checkpoint has. Everything above is open-loop agreement with recorded
trajectories, which is necessary but not sufficient.
## Verification — INT4 SVDQuant
| check | result |
|---|---|
| base bf16 model vs recorded actions | NRMSE 0.0055, corr 0.9998 |
| packed kernels vs the reference SVDQuant formula, per layer | activation scales bit-identical; ≤ 0.10 % of 4-bit codes differ, every one by exactly 1 LSB |
| weight repacking fidelity at export | 0.0000 % of codes off by 1 LSB |
| quantised vs bf16, action chunk | NRMSE 0.0010 |
| quantised vs recorded actions | NRMSE 0.0064 — the same as bf16's own 0.0064 |
| end-to-end from raw camera frames | NRMSE 0.0061, corr 0.9997 |
Its Triton launch configurations are tuned per compute capability and only `sm_89` ships; on other
architectures the kernel prints `no tuned config for M=… K=… N=…` and falls back to an occupancy
heuristic — correct, but roughly 2× off the tuned optimum on narrow shapes. Run
`analysis/iw_gemm_tune.py` once on the target GPU to fix it. This does not apply to the NVFP4
checkpoint, which calls cuBLAS through `_scaled_mm_v2` rather than a hand-tuned Triton kernel.
## Tasks
* `blue_basket` — put the medicine then the measuring tape inside the blue basket
* `drawer` — open the drawer, put the white box inside the drawer then close the drawer
* `stacking_cubes` — put the green cube on top of the black cube and put the red cube on top of the green cube