Repository navigation
[hooks] Support Qwen Image 2.1 in MagCache - #15038
rootonchair wants to merge 3 commits into
Conversation
`QwenImage21TransformerBlock` was not registered in `TransformerBlockRegistry`, so enabling MagCache on `QwenImage21Transformer2DModel` raised `ValueError: Model class ... not registered`. Register it as a single-stream block (`return_hidden_states_index=0`, no encoder hidden states), matching how the model calls it. With the pipeline's KV cache, the hooked blocks see prefix + target tokens on the prefill step and only the target tokens afterwards. A step skipped right after the prefill therefore received a residual longer than its input and fell through to the "dimension mismatch" branch, returning the input without any residual; calibration crashed on the same shape change. Apply / compare the trailing (target) tokens of the residual instead, following the trailing-token alignment used for Flux2 in huggingface#14756. Tests: `MagCacheTesterMixin` for `QwenImage21Transformer2DModel` and `QwenImage21Pipeline`, plus KV-cache skip and calibration tests on the transformer. Refs: huggingface#14756
…tests Move `_run_denoising_loop` and the per-step input builder onto `QwenImage21TransformerTesterConfig` so the TaylorSeer and MagCache KV-cache tests reuse them instead of carrying copies, and fold the MagCache calibration check into the KV-cache skip test.
Match the Flux2 MagCache tests (huggingface#14756): drop the bespoke KV-cache test and keep the shared denoising helpers in the TaylorSeer test as on main.
Self-review notesReviewed against Blocking issuesNone.
Non-blocking issues
Dead codeNone. Every added line runs:
Documentation impact
VerdictREADY. Open for reviewer input: item 1 (KV-cache test for the hook change), item 2 (state reset vs. clearer error), and whether the |
| elif diff < 0: | ||
| # The residual was computed on a longer sequence (e.g. the QwenImage2.1 KV-cache prefill step | ||
| # covers prefix + target tokens, later steps only the target); the target tokens sit at the end. | ||
| output = output + res[:, -output.shape[1] :, :] |
There was a problem hiding this comment.
The existing guard checks rank, batch size, and channel count. This new branch takes the last input length tokens of the residual. Does it make sense to check whether the positions represent the same tokens that it assumes the trailing target image corresponds to.
|
Hi @rootonchair, thanks for the PR! It does not appear to link an issue it fixes. If this PR addresses an existing issue, please add a closing keyword (e.g. Please note that PRs without a linked issue are likely to be automatically closed 10 days after this notice. Once the PR links an issue (or gets the |
What does this PR do?
QwenImage21TransformerBlockwas not registered inTransformerBlockRegistry, soenable_cache(MagCacheConfig(...))raisedValueError: Model class ... not registered. It is now registered as a single-stream block (return_hidden_states_index=0, no encoder hidden states).use_kv_cache=True, the default), the blocks see prefix + target tokens on the prefill step and only the target tokens on later steps. If step 1 was skipped, the step-0 residual was longer than the input, so the head hook returned the input without any residual (the whole transformer was silently skipped). Calibration crashed on the same change in length. Both now use the trailing target tokens of the longer residual, which is the same slice the model's ownkv_cache_mode="cached"path takes (joint_hidden_states[:, prefix_len:]). Flux2 and other models are unaffected, since their sequence length does not change between steps.Usage
Calibrate once with the same
num_inference_stepsyou will use for inference, then pass the printed ratios:Before submitting
self-reviewskill on the diff?documentation guidelines, and
here are tips on formatting docstrings.
Who can review?
Anyone in the community is free to review the PR once the tests have passed. Feel free to tag
members/contributors who may be interested in your PR.
@sayakpaul @DN6