[QNN:Bugfix] Preserve calibrated MUL activation ranges for QNN export - #4765
[QNN:Bugfix] Preserve calibrated MUL activation ranges for QNN export#4765yydhYYDH wants to merge 3 commits into
Conversation
8682b2b to
258edd3
Compare
|
Additional Qwen3-VL validation has been completed on Snapdragon 8 Elite. This PR now also fixes the Qwen3-VL QNN offline export and runtime path:
Qwen3-VL-2B was exported with 4-bit symmetric weights, asymmetric 16-bit activations, OmniQuant using 10 calibration samples for 5 epochs, and a fixed visual input size of 272×608. The model was compiled for Snapdragon 8 Elite with Reproduction commands: MODEL_DIR=/path/to/Qwen3-VL-2B-Instruct
OUT_DIR=/path/to/qwen3-vl-2b-mnn
BUILD_DIR=/path/to/MNN/build_qnn
CALIB_DATA=/path/to/wikitext-10.txt
python transformers/llm/export/llmexport.py \
--path "$MODEL_DIR" \
--export mnn \
--dst_path "$OUT_DIR" \
--mnnconvert "$BUILD_DIR/tools/converter/MNNConvert" \
--quant_bit 4 \
--quant_block 64 \
--lm_quant_bit 4 \
--lm_quant_block 64 \
--omni \
--omni_epochs 5 \
--calib_data "$CALIB_DATA" \
--generate_for_npu \
--seperate_embed \
--sym \
--act_bit 16
python transformers/llm/export/npu/generate_llm_qnn.py \
--model "$OUT_DIR" \
--soc_id 69 \
--dsp_arch v79 \
--vtcm_mb 8 \
--mnn_path "$BUILD_DIR" \
--chunk_size 128 \
--max_history_token 0 \
--image_sizes 272x608The test prompt was: The four device configurations were run with the same image and prompt: export LD_LIBRARY_PATH=/path/on/device/runtime/lib
/path/on/device/runtime/bin/llm_demo /path/on/device/model/config_qnn.json /path/on/device/cat_prompt.txt
/path/on/device/runtime/bin/llm_demo /path/on/device/model/config_cpu_llm_qnn_visual.json /path/on/device/cat_prompt.txt
/path/on/device/runtime/bin/llm_demo /path/on/device/model/config_qnn_cpu_visual.json /path/on/device/cat_prompt.txt
/path/on/device/runtime/bin/llm_demo /path/on/device/model/config.json /path/on/device/cat_prompt.txtThe same cat image and prompt were tested with four backend combinations on a OnePlus 13:
All four combinations completed prefill and decode without graph-shape, graph-execution, or runtime errors. The QNN-visual/CPU-LLM result confirms that the QNN visual graph is working correctly. The remaining visual-language quality loss is localized to the quantized QNN LLM path; the current text-only calibration dataset does not cover visual embedding and DeepStack activation distributions. |
Description
This PR fixes activation quantization propagation for LLM NPU export paths.
Binary MUL outputs, such as the
gate * upproduct in SwiGLU, cannot safely derive their quantization range from either input. The product range can be orders of magnitude larger than the operand ranges. Reusing an operand scale causes INT16/UINT16 saturation on QNN and corrupts downstream hidden states, which can result in garbled generation.This PR partially addresses #4716 by fixing the LLM export portion of the issue, specifically the OmniQuant/SmoothQuant activation-quantization propagation used for QNN/NPU models. It does not claim to resolve Qwen3-VL cases covered by #4716.
The changes:
The fix is split into two commits:
For Qwen3-0.6B, the old layer 2 SwiGLU product incorrectly inherited an input scale near
0.0018367. After the fix, the calibrated MUL output and down-projection input both use:Validation used a full Qwen3-0.6B OmniQuant export with 10 calibration samples, 5 epochs, 4-bit weights, and asymmetric 16-bit activations. All 28 MLP MUL tensors matched their corresponding down-projection input scale and zero point.
QNN offline compilation completed for Snapdragon 8 Elite with
soc_id=69,dsp_arch=v79,vtcm_mb=8, andmax_history_token=0, producing 30 context binary files. Host CPU, OnePlus 13 CPU, and OnePlus 13 QNN runs all exited with code 0 and generated coherent text.Reproduction commands
OUT=/tmp/qwen3-mnn python transformers/llm/export/llmexport.py \ --path /path/to/Qwen3-0.6B \ --export mnn \ --dst_path "$OUT" \ --mnnconvert /path/to/MNN/build/MNNConvert \ --quant_bit 4 \ --quant_block 64 \ --lm_quant_bit 4 \ --lm_quant_block 64 \ --omni \ --omni_epochs 5 \ --calib_data /tmp/qwen3_wikitext_10.txt \ --generate_for_npu \ --seperate_embed \ --sym \ --act_bit 16python transformers/llm/export/npu/generate_llm_qnn.py \ --model "$OUT" \ --soc_id 69 \ --dsp_arch v79 \ --vtcm_mb 8 \ --mnn_path /path/to/MNN/build \ --chunk_size 128 \ --max_history_token 0Test prompt
The same prompt was used for host CPU, OnePlus 13 CPU, and OnePlus 13 QNN:
Test results
Quantization metadata validation:
Host MNN CPU generated coherent text:
OnePlus 13 CPU result:
OnePlus 13 QNN result:
Both mobile runs generated coherent text without the previous garbled-token failure. Performance numbers are included only to identify the validated runs; this PR targets correctness rather than performance.
Module
LLM
Type
Checklist
[Module:Type] Descriptionformat