Repository navigation
npu: Qwen2's q/k/v biases are read from the container, so Qwen2.5-3B-Instruct-NPU2 answers (Refs #1786) - #2259
Conversation
…Instruct-NPU2 answers (Refs #1786) The forward adds the q/k/v projection biases only when attention_bias is on, and took that from config.json alone. Qwen2 configs never state the key (our importers write it, FastFlowLM's packs do not), so on Qwen2.5-3B-Instruct-NPU2 the biases were loaded and never added: the known-answer prompt answered noise. Now, as for q/k norm: the key absent and a layer carrying a q, k or v bias turns the biases on (said on the startup line); a config stating false against a container with biases is refused. After: 'Paris<|im_end|>' on the host. batch_check_state_qwen2: a fabricated Qwen2 with biases and no key, against a numpy Qwen2 forward on the container's own weights: corr 1.0000000, max |d| 6.6e-06. The binary without the fix fails it (corr 0.444, argmax 200 vs 7) while passing batched == sequential. Four probe checks added to q4nx_qk_norm_test. Of the 24 q4nx containers on the box with biases, only this pack and flm-whisper lack the key; Whisper does not go through this forward. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
ready for review at 32dcc97 |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 32dcc97: READY
Reviewed at 32dcc9782281c3acb8a8fae4f0fd6d31993f67dd. It is on main (0 behind) and merges clean. The diff is 7 files, +150/-7.
Checked
- The rule is narrow.
q4nx_semantics_attention_bias_from_tensorsacts only whenattention_biasis off and a layer carries a q, k or v bias.- A key that is absent turns the biases on.
- A stated key returns -2, and init refuses by name.
get_numparses JSONtrueandfalse, so a stated booleanfalsecounts as stated and reaches the refusal.
- The forward is safe on partial biases.
q_bias_,k_bias_andv_bias_are always allocated zero-filled (q4nx_forward.cpp:1832), so a layer with only some of the three adds zeros for the missing ones. Nothing reads out of bounds. - Tests.
q4nx_qk_norm_testreports ok. Everybatch_check_state_ci.pyarm passes withNPU_INFER_HOST_GEMM=1: qwen2, lfm3, lfm2, qwen3_5 and the default. On qwen2:corr 1.0000000 (needs 0.99999), max |d| 6.62e-06 of 9.6, argmax 7 vs 7.tools/copyright.py --checkreturns rc 0. - Blast radius, re-scanned. I read every
model.q4nxheader on the box and found 25 containers carrying q/k/v biases. 3 lack the key: the Qwen2.5-3B pack and two copies of Whisper (~/.config/flm/models/Whisper-V3-Turbo-NPU2andscratch-packs/flm-whisper). The body counts 24 and 2 because it missed the second Whisper copy. Both copies run on the audio path, so the conclusion holds.
My mutants (each restored, and the tests pass again)
| mutant | result |
|---|---|
the probe in init replaced by by = -1 (main's behaviour) |
qwen2 arm FAIL: corr 0.4443442, argmax 200 vs 7, which is the body's 0.444 |
the attention_bias_stated refusal dropped |
q4nx_qk_norm FAIL: "config.json says attention_bias=false and the container carries biases: a disagreement (-2, refused)" |
The known-answer row on the real pack (noise before, Paris<|im_end|> after) is the author's; I did not re-run it. The kernel set and the device row for this pack are owed once the NPU is back on.
CI build at this head: completed success, "100% tests passed, 0 tests failed out of 268". batch_check_state_qwen2 is test #251 there and passed.
What
FastFlowLM's
Qwen2.5-3B-Instruct-NPU2loads on the per-op forward but answers noise. On the known-answer prompt it givesUNS �leon�leon�ucz …. Refs #1786; host-side only, the NPU is off.Cause. The forward adds the q/k/v projection biases only when
attention_biasis on, and it took that fromconfig.jsonalone. The comment says a "tensor-level probe" decides it when the key is absent, but no such probe existed. Qwen2 configs never state the key, yet every Qwen2 layer has the three biases. Our own importers writeattention_bias: true; FastFlowLM's pack does not. So on this pack the biases were loaded and never added.Fix. The rule is now the one q/k normalisation already follows (engine#1788):
config.jsondoes not stateattention_biasand a layer carries a q, k or v bias, the biases are applied. The startup line says so:attention_bias=1 <- the container: layer 0 carries q/k/v projection biases.config.jsonstatesattention_bias: falseand the container carries biases, the pack is refused by name instead of guessed.trueis left as stated.Evidence (host, 2026-11-12)
UNS �leon�leon�ucz ZA …(68 tokens of noise)Blast radius. I scanned every q4nx container on the box: 24 carry q/k/v biases. Only two lack the key:
flm-whisper. Whisper runs on its own audio path (audio_ops.h), not this forward.The other 22 already state
attention_bias: true, so nothing changes for them.Tests
batch_check_state_qwen2(CI arm). It fabricates a two-layer Qwen2 with q/k/v biases and noattention_biaskey, then compares the forward's sequential logits with a numpy Qwen2 forward on the container's own weights. This uses the same reference mechanism the lfm3 arm got in npu: the per-op forward reads LFM3, so LFM3-1.7B-NPU2 loads and answers (Refs #1786) #2254.q4nx_qk_norm_test: four probe checks covering absent+biases → on, absent+none → off, stated false+biases → refusal, and stated true → unchanged.batch_check_state_ci.pyarm (qwen3, lfm2, lfm3, qwen3_5, qwen2) was run by hand: 0 FAIL.tools/copyright.py --checkrc 0.Not in this PR: a kernel set or device row for this pack. Both wait for the NPU.
🤖 Generated with Claude Code