onnxruntime
58527303 - Reject QDQ Gemm→QGemm fusion when alpha != 1 with bias (#28131)

Commit
103 days ago
Reject QDQ Gemm→QGemm fusion when alpha != 1 with bias (#28131) ### Description Add an `alpha == 1.0` check to `GemmNodeGroupSelector::Check` (symmetric to the existing `beta == 1.0` check) so the QDQ `Gemm → QGemm` fusion is skipped when a bias is present and `alpha != 1`. Extend the QDQ Gemm transformer tests (both the default and fastmath variants) with `alpha_not_one` cases. ### Motivation and Context Fixes #28130. When a Gemm has `alpha != 1` and a bias, the QDQ fusion produces incorrect results. The root cause is in the QGemm kernel: ``` acc_int32 = (A - zpA)(B - zpB) + C_int // bias added to int accumulator Y_float = (alpha * sa * sb) * acc_int32 // output scale applied to everything ``` The output scale (`alpha * a_scale * b_scale`) is applied to the whole int32 accumulator, which already contains the bias, so `alpha` ends up multiplying the bias too: ``` Y = alpha * A_dq * B_dq + alpha * C_float (buggy) Y = alpha * A_dq * B_dq + C_float (expected) ``` This matches the reporter's observation that the discrepancy vanishes when the bias is zero. This PR is the minimal correctness fix — it rejects the fusion in the broken case rather than producing wrong output. The follow-up discussed on the issue is to absorb `alpha` into the int32 bias at graph-transform time (`C_int_new = round(C_int / alpha)` inside `GemmReplaceWithQuant`), so `alpha != 1` cases can keep the fused path. That is a separate PR because the precision/overflow characteristics want their own review. ### Test plan - [x] Extend `QDQTransformerGemmTests` and its fastmath variant with an `alpha_not_one` parameter. Set `alpha=2.0` on the Gemm node when true, and adjust `check_binary_op_graph` so fusion is expected to be skipped when bias is present. - [x] On `main` the new cases fail — both on op count and on output (`expected -0.624, got -0.429, diff 0.195, tol 0.016`), matching the bug report. - [x] With the selector fix, `QDQTransformerTests.Gemm_*` (8), `QDQTransformerTests.*` (146), and `GraphTransformationTests.Gemm*` (16) all pass locally on macOS arm64. - [x] End-to-end with the reporter's `gemm_alpha.py` against a locally built wheel (macOS arm64) matches optimized vs unoptimized across all cases: ```bash alpha=1.0, optimize=False [[ 39 221 173]] alpha=1.0, optimize=True [[ 39 221 173]] alpha=2.0, optimize=False [[ 47 216 169]] alpha=2.0, optimize=True [[ 47 216 169]] (was [[0 255 218]] on main) alpha=2.0, optimize=False, bias=0 [[144 118 120]] alpha=2.0, optimize=True, bias=0 [[144 118 120]] ```
Author
Parents
Loading