Core AI model zoo

Stream D — Quantization (FP4 + QAT-int4): feasibility, first result, and plan

Companion to accel-levers-survey-and-plan.md (Part 2, Stream D). This doc records the toolchain reality and the first de-risk result: a faithful FP4-vs-int4 quality gate.


TL;DR (the bankable result)

On the pipe-cleaner LFM2.5-1.2B-Instruct (same family as the real target LFM2.5-8B-A1B), weight-only quant, per-block(32), lm_head kept fp16:

scheme weight rel-err ppl (reasoning corpus) Δppl vs fp16
fp16 0.0000 3.384 +0.0%
int8 0.0055 3.389 +0.1%
int4 (sym + MSE-clip) 0.0949 3.729 +10.2%
fp4 (E2M1, e8m0 scale) 0.1173 3.416 +1.0%

The headline (and the whole point of Stream D): fp4 has higher raw weight error than int4, yet its model-level degradation is ~10× smaller and nearly matches int8. Weight-RMSE is a misleading proxy — uniform int4 wins on average reconstruction but catastrophically mis-quantizes the outlier channels that drive the forward pass; FP4’s exponent preserves that dynamic range. This empirically confirms the survey’s claim that the zoo’s int4 collapse is a property of int4 RTN, not of 4-bit, and that FP4 (E2M1) is “the int4 answer.”

Reproduce: .venv/bin/python coreai-models-community/conversion/quant_fp4/fp4_vs_int4_quality_gate.py (MPS for the forward; FP4 fake-quant runs on CPU — torchao’s fp4 kernel is not MPS-safe).


1. Toolchain reality (what exists today)

FP4 conversion — EXISTS in coreai-opt

FP4 RUNTIME — the gap = Stream B dependency

📅 Update 2026-07-01 — the OS half of the gate is now satisfied. macOS 27 dev beta 2 (26A5368g, 2026-06-22) is shipping and installed here (Darwin 27.0.0); the OS27 TensorOps runtime (fp4/fp8 matmul2d, MX·E8M0 scale plane, coop-tensor matmul input) is present on device, and Metal Toolchain v27.1 already compiles half × metal_fp4_e2m1_format → half (see tensorops-quantized-kernels.md §”The real matmul2d API”). ⇒ the FP4-runtime blocker is now purely Stream B kernel work, not OS availability. ⚠️ Still convert on macOS 26.4 (the coreai-core wheel mis-converts on 27); 27 = runtime / AOT only.

int4/int8 MoE gather — PRODUCTION today (Mac GPU, no OS27)

QAT — EXISTS in coreai-opt

Convert invocation

Env


2. The de-risk experiment (D1: FP4 quality gate)

Script: conversion/quant_fp4/fp4_vs_int4_quality_gate.py. Fake-quantizes all decoder nn.Linear weights (lm_head kept fp16) four ways and measures degradation with no device and no Stream B (weights are dequantized back to fp32, so the model runs as a normal fp32 graph):

Result = the TL;DR table above.

Why int4 uses MSE-optimal clip here

That is the favorable (shipped) int4, so the +10.2% is a conservative, best-case int4; vanilla int4 RTN would be worse and the fp4 gap larger. The fp4 win is therefore not an artifact of a weak int4 baseline.


3. Caveats (honest)

  1. N=1, 1.2B. One pipe-cleaner model. The result must be confirmed on the real targets (LFM2.5-8B-A1B MoE, Qwen3.6) where int4 craters harder.
  2. Reasoning-accuracy saturated (11/12 for all schemes incl. fp16): at 1.2B on easy problems the int4 cliff does not flip the final answer. Perplexity is the discriminating signal here; a harder reasoning set (or a bigger model) is needed to show answer-flips.
  3. Quality only. This gate validates the payoff; FP4 cannot yet run on device (Stream B / OS27). The point is to prove the payoff is real before investing in the runtime.
  4. lm_head fp16 and block_size 32 held constant across schemes (apples-to-apples).

4. Plan / next steps (in order)

D1-next — confirm on the real targets (Mac, _GPU_LOCK for MPS):

D2 — coreai-opt QAT-int4 pipe-cleaner (the training path):

Runtime (blocked on Stream B):

Coordination: Mac GPU serialized with Stream A via _GPU_LOCK (use an absolute lock path — a relative path in a trap removes the wrong file). fp4 runtime shares the A19 with Stream B. OS gating: int4/int8 = OS26; fp4/fp8 = OS27.

Strategic read: two shippable products fall out — (a) QAT-int4 (OS26, today’s kernel, near-term) and (b) FP4 (OS27 + Stream B, the quality win shown above). D2 is the faster ship; D1’s fp4 result is what justifies the Stream B investment.


5. Start-here (next session) — read this first

0. Orient. Read memory project_quant_d_port + §1 and §4 above. Check no other session (Stream A) is on the Mac GPU: ls coreai-models-community/_GPU_LOCK. Stream C (spec-decode) is in a separate session — do not touch its files.

1. First move (recommended): confirm D1 on the real iPhone target — LFM2.5-8B-A1B (MoE).

2. Then D2 — QAT-int4 pipe-cleaner (the near-term SHIP; OS26, needs NO Stream B):

3. Gotchas (all hit this session):

4. Blocked / parallel: fp4 on-device runtime needs Stream B (TensorOps fp4 matmul2d dequant, A19/OS27). Do not attempt fp4 device bench until B lands. Device OS is now confirmed = macOS/iOS 27 (dev beta 2, 26A5368g, 2026-06-22) — the OS gate is satisfied, so the only remaining blocker is B’s kernel. Everything in §5.1–5.2 is Mac-only and needs neither Stream B nor OS27.