onnx: import GroupQueryAttention decode steps and its internal rotary - #2645
Open
czoli1976 wants to merge 1 commit into
Open
onnx: import GroupQueryAttention decode steps and its internal rotary#2645czoli1976 wants to merge 1 commit into
czoli1976 wants to merge 1 commit into
Conversation
…ry, so ORT-GenAI fused decode exports could not be imported at all. Concatenate the past cache onto the step and return it as present_key/present_value, apply the node's own RoPE from cos_cache/sin_cache at positions 0..S for a first prompt and at seqlens_k for a generated token, and fold the attention_bias input into the causal band. A multi-token step against a non-empty cache is the subsequent-prompt form, where past and present alias one buffer written from index 0, so it is rejected rather than approximated by a concatenation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Extends the
com.microsoft.GroupQueryAttentionimporter from prefill-only to the decode step and the node's own rotary, which is what ORT-GenAI fused exports emit — closing points 1–3 of #2345.What changed
past_key/past_valueare concatenated onto the step and returned aspresent_key/present_value. No new mask math was needed:wire_attention_maskalready builds a bottom-right-aligned band fromRange(kv_len - q_len, kv_len), so a single decode token attends the whole cache correctly.do_rotary=1. RoPE runs inside the node through the existingApplyRopeop, with thecos_cache/sin_cachehalves widened to head size. Positions follow the ORT CPU kernel:0..Sfor a first prompt (aSlice, so symbolic sequence lengths still work) andseqlens_kfor a generated token (aGather, so it works without static shapes either). K is rotated before it enters the cache.attention_biasis folded into the causal band, sinceSdpareplaces any supplied mask whenis_causalis set.causal=0is honoured;smooth_softmax,qk_output, quantized KV,sliding_window_cache, packed QKV,position_ids,head_sinkand fused Q/K norm are rejected with specific messages.Deliberately rejected: the subsequent-prompt case
A multi-token step against a non-empty cache is not a concatenation. Probing onnxruntime shows it writes the new K at cache offset 0, treating past/present as one aliased buffer — appending silently produces different numbers. The importer rejects that shape with an explanatory message rather than approximating it. Only a first prompt (empty cache) and single-token generation are accepted, which matches the envelope the CPU kernel documents.
On the mask sentinel
Summing the causal band with
attention_biasrelies on the band filling withdt.min_value(). Filling with-infinstead is not an option: the band is built asindicator * fill, and0 * -infisNaNon the kept entries. The finite sentinel is exact for any row with at least one unmasked key; the two sentinels only become indistinguishable on a row where every visible key is masked by the bias, which carries no meaningful output in any implementation. Noted in the code so the next reader does not repeat the experiment.Testing
A numpy reference was validated against onnxruntime first (prefill and decode agree to ~1e-7), then tract checked against it across prefill/decode × rotary/no-rotary × bias/no-bias × sliding-window: 10/10, max error 1.2e-7. Also covered the
[B, 1]shapedseqlens_kthat real exports emit, where the spec says[B].Two cases are added under
onnx/test_cases/, generated from onnxruntime, passing all four passes including the NNEF round-trip (theirvars.shsets--nnef-tract-transformers, needed forApplyRopeto serialize).onnx/test_cases/run_all.shshows one unrelated failure,qtdnn_10x5_101_i32_biases, which fails identically on an unmodified build of the same commit.🍍