[LoRA] add LoKr adapter support (Z-Image, Flux2/Klein)#14163
Open
christopher5106 wants to merge 1 commit into
Open
[LoRA] add LoKr adapter support (Z-Image, Flux2/Klein)#14163christopher5106 wants to merge 1 commit into
christopher5106 wants to merge 1 commit into
Conversation
Adds loading of LoKr (LyCORIS Kronecker product) adapters: - `load_lora_adapter` detects `lokr_` keys and injects a peft `LoKrConfig`, inferred from the tensor shapes via `_create_lokr_config` (decompose factor, per-module rank/alpha patterns). - State dict conversions for the formats in the wild: ai-toolkit Z-Image (dotted diffusers paths under `diffusion_model.`), ai-toolkit BFL Flux2 (fused qkv), LyCORIS underscore format, and bare dotted diffusers paths. - BFL fused-QKV LoKr cannot be split exactly into separate Q/K/V Kronecker factors, so `Flux2LoraLoaderMixin.load_lora_weights` fuses the model's QKV projections and maps the adapter 1:1 (exact). - Alpha follows the LyCORIS convention: scaling applies only to rank-decomposed factors and is baked into the weights at conversion. Fixes huggingface#13221
Member
|
Will review next week but triggering an AI review as well. |
Contributor
There was a problem hiding this comment.
🤗 Serge says:
Solid, well-tested addition of LoKr adapter loading for Z-Image and Flux2/Klein. The design is coherent: shape-inferred LoKrConfig, alpha baked into the left Kronecker factor per LyCORIS convention, and a fuse-then-map strategy for the fused-QKV case that avoids the lossy per-chunk re-factorization. I verified the key integration points and found no blocking issues.
Correctness (verified, no defects)
_create_lokr_config—collectionsis imported;factorization/LoKrConfigare imported lazily from peft; thedecompose_factorsearch andrank/rank_patternderivation are internally consistent and reproduced exactly by the tests._bake_lokr_alpha— rank is read from the inner dimension of the decomposed factor (w2_b.shape[0]/w1_b.shape[0]), and full-matrix modules correctly drop alpha via therank is Noneguard. Scaling matches the test expectations ((alpha/rank) * kron(w1, w2_a@w2_b)).- Flux2 fused-QKV path —
Flux2Transformer2DModelinheritsfuse_qkv_projectionsfromAttentionMixin; the"Added" in processor nameguard does not spuriously trip (Flux2 processors areFlux2AttnProcessor/Flux2KVAttnProcessor), andfuse_projectionscreatesto_qkv/to_added_qkvused by_get_fused_projectionsat inference. The.attn.to_qkv.substring check does not falsely matchto_qkv_mlp_proj.is_quantizedis a real attribute on the model. reis imported inlora_conversion_utils.py;loggerexists inlora_pipeline.py.
Minor (non-blocking)
- In
_bake_lokr_alpha, a malformed module that has alokr_w*_bbut neitherlokr_w1norlokr_w1_awould raiseKeyErrorat the final assignment. Not reachable for the four in-the-wild formats, so purely defensive — fine to leave.
Tests
Flux2LoKrTestsandZImageLoKrTestscover fused QKV (img+txt), rank-decomposed factors with meaningful alpha, full-rank factors with placeholder alpha, and the LyCORIS underscore format, assertingget_delta_weightequalskron(w1, w2). All referenced imports resolve.
LGTM.
serge v0.1.0 · model: claude-opus-4-8 · 19 LLM turns · 23 tool calls · 110.2s · 880940 in / 7039 out tokens
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.
What does this PR do?
Adds support for loading LoKr (LyCORIS Kronecker product) adapters, for Z-Image and Flux2/Klein checkpoints in the wild, plus a generic path that works for any model whose LoKr state dict already uses diffusers module names.
Fixes #13221. Also addresses #13261 and #13137 (Flux 2 Klein LoKr).
Follows up on the discussion in #13326 — see "Why not split the fused QKV" below for measurements that motivated a different approach for the fused-QKV part. cc @CalamitousFelicitousness whose prototype this builds on.
How it works
PeftAdapterMixin.load_lora_adapterdetectslokr_keys and injects a peftLoKrConfiginstead of aLoraConfig. The new_create_lokr_configinfers the config from tensor shapes: it reconstructs each module's Kronecker factorization and finds thedecompose_factorunder which peft's ownfactorization()reproduces every shape; per-modulerank_pattern/alpha_patternmake peft recreate full vs. rank-decomposed factors exactly as stored (for full-matrix factors the rank is set tomax(lokr_w2.shape)so peft also creates full matrices).alpha / rankscaling applies only when a factor is rank-decomposed and is baked intolokr_w1at conversion; full-rank modules ignore alpha (ai-toolkit stores a ~1e10 placeholder there). The config setsalpha = rso the runtime scaling is 1.0.diffusion_model.layers.0.attention.to_q.lokr_w1)F16/z-image-turbo-flow-dpo(the checkpoint from #13221)_convert_non_diffusers_lokr_to_diffusers(paths already match the model)diffusion_model.double_blocks.0.img_attn.qkv.lokr_w1)puttmorbidly233/loraklein_snofs_v1_2.safetensors(the checkpoint from #13261)_convert_non_diffusers_flux2_lokr_to_diffuserslycoris_transformer_blocks_0_attn_to_q.lokr_w1)gattaplayer/besch-flux2-klein-9b-lokr-lion-3e-6-bs2-ga2-v02_convert_lycoris_flux2_lokr_to_diffusers.alphakeysbghira/flux2-klein-9b-distillation-lokr_convert_non_diffusers_lokr_to_diffusersWhy not split the fused QKV
BFL-format checkpoints apply LoKr to the fused QKV projection of the double blocks, while the diffusers Flux2 model has separate Q/K/V. For LoRA this split is exact (shared
lora_A, chunkedlora_B), but a Kronecker product delta over the fused projection is mathematically not a Kronecker product per chunk. #13326 approximated each chunk with a rank-1 Van Loan re-factorization; measuring that on the realklein_snofs_v1_2checkpoint gives a mean relative delta error of 69% on the 48 QKV projections (max 79%, min 32%), i.e. the adapter loads but its QKV contribution is mostly destroyed — the same class of problem that got #13997 rejected.Instead, the converter maps these modules to the model's fused
attn.to_qkv/attn.to_added_qkvprojections, andFlux2LoraLoaderMixin.load_lora_weightscallstransformer.fuse_qkv_projections()(idempotent) before injection, with an info log. Loading is then exact. Quantized transformers raise a clear error for such checkpoints since fusing would concatenate packed quantized weights.Tests / verification
ZImageLoKrTestsandFlux2LoKrTests: injectedget_delta_weightequalskron(w1, w2)exactly, covering fused QKV (img + txt streams), rank-decomposed factors with meaningful alpha, full-rank factors with placeholder alpha, and the LyCORIS format.F16/z-image-turbo-flow-dpoloaded into a real-dimension (dim=3840) Z-Image transformer: all deltas exact, scaling 1.0.tests/lora/test_lora_layers_z_image.py(36 passed) andtests/lora/test_lora_layers_flux2.py(39 passed).Who can review?
@sayakpaul @asomoza