You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(qwen3_8): wire real NVFP4 + FP8 GEMM via Myelins add_dynamic_quantize (#1326)
feat(qwen3_8): wire real NVFP4 + FP8 GEMM via Myelin's add_dynamic_quantize
Wires families/qwen3_8 to build genuine W4A4 NVFP4 and W8A8 FP8 GEMM
engines for RadixArk/Qwen3.8-27B-NVFP4-style ModelOpt MIXED_PRECISION
checkpoints, without tensorrt-edge-llm's custom CUTLASS plugin.
families/qwen3_8/quantization.py (new):
- Qwen38QuantContext / calibrate_qwen3_8_nvfp4() reads the checkpoint's
own packed NVFP4 (MLP gate/up/down, lm_head) and FP8 (DeltaNet
in_proj_qkv/in_proj_z/out_proj, attention q/k/v/o) weights plus their
real calibrated weight_scale/weight_scale_2/input_scale tensors
directly -- bit-exact reuse, no dequantize-then-requantize round trip.
- NVFP4 activations use TensorRT's add_dynamic_quantize
(IDynamicQuantizeLayer): the standard add_quantize+block_shape path is
unconditionally rejected for FP4 output by Myelin's shape checker
(src/compiler/analysis/shape.cpp:3350, "Blockwise quantization
requires output type to be int8 or fp8e4m3", confirmed on TensorRT
11.1.0.106 and 11.3.0.99). add_dynamic_quantize is the one layer type
with a real fused FP4 tensor-core kernel.
- FP8 activations use the plain add_quantize/add_dequantize pattern
(no blockwise restriction applies), matching families/qwen's proven
FP8 approach.
- Weight constants are fed in native [out_features, in_features]
checkpoint layout with MatrixOperation.TRANSPOSE on the matmul, to
avoid unpack/transpose/repack of packed sub-byte FP4 data.
families/qwen3_8/engine_builder.py:
- Removes the quant_ctx NotImplementedError guard.
- Threads quant_ctx through DeltaNet (in_proj_qkv/in_proj_z/out_proj)
and attention (q/gate/k/v/o) matmuls via graph_blocks.make_matmul_fn
(already quant_ctx-aware, shared infra); MLP/lm_head already routed
through it once quant_ctx stopped being force-None.
- Also includes an unrelated-but-required precision-threading fix
(_transpose_2d's precision param was never passed through
load_weights()/_load_*_weights(), so every weight was stored as FP32
regardless of --precision, OOMing a full 27B build). This exact fix
also lives isolated on zhenshanx/qwen3_8-precision-threading-fix for
landing as its own PR -- drop this hunk on rebase once that merges.
- Sets ProfilingVerbosity.DETAILED for engine-inspector tactic/constant
visibility.
families/qwen3_8/model.py:
- Accepts quantization="nvfp4", builds quant_ctx via
calibrate_qwen3_8_nvfp4(), threads precision into load_weights().
Verified on a real B100/Blackwell (SM100) node against the actual
RadixArk/Qwen3.8-27B-NVFP4 checkpoint:
- Full 64-layer engine builds end-to-end (~500s, 20.2GB, down from a
naive-quantize 53.8GB and an unquantized ~54GB FP16 baseline).
- Engine inspector confirms 193 FP4E2M1-typed and 546 FP8-typed
constants (matching every quantized weight_name registered), and real
fused Blackwell tensor-core kernels (tensorop*/cga*/sm* tactics,
Myelin-auto-fused dual_gemm for gate+up, RMSNorm+DynamicQuantize
fused into single prologue kernels).
- Real generation test (RadixArk/Qwen3.8-27B-NVFP4 tokenizer + chat
template, hand-driven single-step decode loop matching the C++
runtime's exact mask/state/position semantics) produces correct,
coherent output for "What is the capital of France? Answer in one
word." -> "Paris<|im_end|>".
Known follow-up (tracked separately, not done here): checkpoint tensors
for quantized weight_names are still redundantly loaded+dequantized by
load_weights() even though maybe_quantized_matmul() never uses that
copy -- wasted CPU/memory, not a correctness issue, worth its own
perf-only PR.
Signed-off-by: Zhenshan Xie <zhenshanx@nvidia.com>
0 commit comments