ImageWAM Unitree-G1 two-task β W4A4 real packed INT4 at group 32, two arms
The ImageWAM-FLUX.2-4B Unitree-G1 two-task fine-tune (LGG100/imagewam-sc-10k, step 10000)
quantised to INT4, group 32 for GPUs with 4-bit tensor cores, by two methods calibrated
identically and packed identically β so they differ in their mathematics, not their plumbing.
Real quantisation, not a simulation of it. The weight ships PACKED at 4 bits, two output
channels per byte, in the [K, N/2] layout the fused Triton kernels contract on β the same packing
the FastWAM Unitree-G1 checkpoints use, so the arms are comparable across models.
sc_step10000_asp_w4a4_g32.pt |
sc_step10000_svdquant_w4a4_g32.pt |
|
|---|---|---|
| method | AFQ / ASP, deflated form | SVDQuant (Li et al., ICLR 2025) |
| rotation | block Hadamard | none |
| low-rank branch | rank 32 on the action expert, from the action metric | rank 32 on every layer, from the quantisation residual |
| BPW | 4.5789 | 4.7523 |
| resident weights | 2.637 GiB (3.25x less than bf16) | 2.716 GiB (3.16x) |
| vs bf16, mean | 0.00326 | 0.00623 |
| vs bf16, worst frame | 0.01291 | 0.01485 |
| vs recorded teleoperation | 0.06588 | 0.06523 |
bf16's own distance to the demonstrator is 0.06592. ASP tracks the unquantised model to 0.00326 β half SVDQuant's movement β and lands within 4e-5 of bf16's own gap to the demonstrator, while spending 0.173 fewer bits per weight. (Both arms sitting either side of bf16 on the last row is noise; the row that separates them is movement against bf16.)
Both quantise the same 153 of 156 MoT Linears, 4.498 B parameters.
Calibration
The same 30 episodes as the FastWAM Unitree-G1 arms β 15 per task from
item-classification-new and sort-tools-new, seed 42, all frames, 14,469 observations β so
the two models' quantised rows are comparable. Activation absmax feeds SmoothQuant; an
800-row-per-layer reservoir of real layer inputs feeds the smoothing search, whose objective is
SVDQuant's own OutputsError with the INT4 quantiser and the refitted low-rank branch inside the
scored loop.
SVDQuant: 124 layers chose (alpha, 0), 30 (alpha, 1-alpha); split-half 69.5%; searching the
smoothing is worth 1.076x over a fixed alpha = 0.5 and the rank-32 branch 1.061x over no branch.
ASP searches the smoothing and the subspace jointly, because the protected subspace is the top eigenspace of the metric in the coordinates being quantised and those coordinates move with the smoothing: split-half 77.3%, and its branch is worth 1.574x β half again as much as SVDQuant's, on the same model and the same activations.
Where the action metric points
G_l = E[J_l^T J_l], J_l = d(action chunk)/d(x_l) differentiated through all ten denoising steps,
estimated with random probes over 905 frames x 12 probes. 97.9% of tr(G) sits in six layers,
and none is a transformer block: single_stream_modulation 37.8%, double_stream_modulation_img
34.0%, action_encoder 15.7%, time_in.out_layer 8.0%, the two head layers 2.5%. Top-32
eigenvectors capture 99.93% of tr(G) on the dominant layer.
mixtures.action.time_in.in_layer gets rank 0, not rank 32: it consumes the timestep, not the
action latents, so its Jacobian to the action is genuinely empty and a "protected subspace" there
would be 32 directions chosen by LAPACK out of a zero matrix. 65 layers carry a subspace, 88 do not.
Verification
REAL β 153 packed Linears, all torch.int8 at [K, N/2]; 2413.1 MiB for 4.498 B weights is
4.50 bits/weight exactly (4 plus 16/32 for the per-group scale); zero dequantised weight
copies anywhere in the module.
PACKING β per layer, on random inputs, against each method's own fake-quant formula written out directly. SVDQuant: worst layer 1.1e-2, 0.086% of codes differ by exactly 1 LSB, scales bit-identical. A wrong nibble pairing or a transposed scale shows up here and nowhere else, because at the model level it would still emit a plausible action chunk.
For ASP the criterion has to change, and the reason is worth stating. Bit-identical scales are achievable for SVDQuant, whose prologue is an elementwise multiply a fp32 reference reproduces exactly, and are not achievable for ASP, whose prologue is a tensor-core Hadamard: the rotated activation rounds differently, so its per-group amax does too. The honest criterion is distance to the unquantised layer, and ASP's kernel is at worst +0.8% further from the true bf16 layer than a fp32 reference β on layers where both sit at ~1.5e-1, because that is W4A4's own error. Codes are still within 1 LSB and scales within 5%.
ACCURATE β on episodes disjoint from the calibration selection: the table at the top. The model's own distance to the demonstrator is ~20x what ASP moved and ~10x what SVDQuant moved.
No closed-loop success rate has been measured. Open-loop agreement is necessary and not sufficient.
What is quantised
153 of 156 MoT Linears, 4.498 B weights β 99.6% of mot's parameters. Three stay bf16 and the
checkpoint records them:
| layer | shape | why |
|---|---|---|
mixtures.action.action_encoder |
(1024, 16) | K = 16 is below the kernel's group granularity of 32 |
mixtures.video.transformer.final_layer.linear |
(128, 3072) | the video output head predicts the next frame; it never runs during action inference, so it has no calibration |
β¦final_layer.adaLN_modulation.1 |
(6144, 3072) | same |
Running it
Needs triton and a GPU with INT4-on-INT8 tensor cores (built and verified on sm_89 and H100). The
file is self-contained β the quantised Linears, every unquantised mot tensor and the proprio
encoder β so the source checkpoint is not needed at inference. You still need the FLUX.2
klein-base-4B backbone and autoencoder, dataset_stats.json, and the pre-encoded prompt.
from iw_w4a4_runtime import load_quantized_model
model, cfg = load_quantized_model("sc_step10000_svdquant_w4a4_g32.pt", build_model=my_builder)
install_iw_w4a4 raises unless every name resolves to an nn.Linear, and dispatches on
lowrank_mode: SVDQuant and deflated ASP produce the same shapes, so nothing else tells them
apart, and running one through the other's class gives a well-formed GEMM of the wrong bilinear
form that still emits a plausible action chunk.
The runtime files here are standalone copies of the in-repo versions, checked on real exported layers (see the note on reproducibility below).
Four things that will bite you
- The source checkpoint is in LoRA form. Its
motcarries...base.weight,...lora_Aand...lora_Bfor every adapted video Linear, and onlyload_checkpointmerges them. Read the weights straight out of the file and you quantise the pretrain and silently drop the fine-tune β a checkpoint that loads, runs, emits plausible actions, and is the wrong model. This export readsmodule.weight.dataoff the live model after the merge, and turnsload_checkpoint's missing/unexpected report (which it only logs at INFO) into an exception. - The INT4 path is NOT bit-reproducible. The rank-r projection is a reduction over the whole
contraction axis and its partial sums are combined with fp32 atomics, so the low bits depend
on the order thread blocks finish in: the same runtime disagrees with itself between
consecutive calls on identical input, by up to
4e-3 on a single element (1e-3 relative). That is far below the 4-bit grid's own ~1e-2, but it means an accuracy number will not reproduce to the last digit, and "bit-identical" is not an available acceptance criterion for this path. - The MoT exposes the video expert twice.
mixtures.video.Xandmixtures.video.transformer.Xare the same tensor β 140 of 267 storages carry two names. Deduplicate by storage or a "quantised" export comes out larger than the bf16 original. - The mosaic is 288x256 (
compact_288x256):cam_left_highat 256x192 on top, the two wrists at 128x96 side by side below, order[high, left, right], pixels mapped to (-1, 1). Actions are 16-D and are not padded or un-padded β this robot is natively 16-D, unlike the UR3 ImageWAM checkpoints which pad 14 to 16.
Tasks
Both recorded on Unitree_G1_Dex1_Sim, 100 episodes each, 30 fps, three cameras at 480x640, and
both declaring the same instruction "pick up cube." in their own metadata.
Model tree for arashakb/IMAGEWAM-Unitree-G1
Base model
LGG100/imagewam-sc-10k