mirror of
https://github.com/vladmandic/automatic
synced 2026-09-18 16:54:33 +02:00
feat(flux2): native GLoRA loader
Loads the four ab.weight tensors plus alpha. Fused QKV is skipped: GLoRA's target-dependent term doesn't row-slice cleanly without recomputing against a fused proxy weight.
This commit is contained in:
@@ -38,7 +38,7 @@ import time
|
||||
import torch
|
||||
from modules import shared, sd_models
|
||||
from modules.logger import log
|
||||
from modules.lora import network, network_lora, network_lokr, network_hada, network_oft, network_ia3, lora_convert
|
||||
from modules.lora import network, network_lora, network_lokr, network_hada, network_oft, network_ia3, network_glora, lora_convert
|
||||
from modules.lora import lora_common as l
|
||||
|
||||
|
||||
@@ -86,12 +86,18 @@ IA3_SUFFIXES = (
|
||||
".weight", ".on_input",
|
||||
".alpha", ".scale",
|
||||
)
|
||||
GLORA_SUFFIXES = (
|
||||
".a1.weight", ".a2.weight",
|
||||
".b1.weight", ".b2.weight",
|
||||
".alpha", ".dora_scale", ".scale",
|
||||
)
|
||||
|
||||
LORA_MARKERS = (".lora_down.weight", ".lora_up.weight", ".lora_A.weight", ".lora_B.weight")
|
||||
LOKR_MARKERS = (".lokr_w1", ".lokr_w2")
|
||||
LOHA_MARKERS = (".hada_w1_a", ".hada_w1_b", ".hada_w2_a", ".hada_w2_b")
|
||||
OFT_MARKERS = (".oft_blocks", ".oft_diag")
|
||||
IA3_MARKERS = (".on_input",) # NOT .weight — too generic, overlaps every other family
|
||||
GLORA_MARKERS = (".a1.weight", ".a2.weight", ".b1.weight", ".b2.weight")
|
||||
|
||||
|
||||
# === BFL → diffusers mapping ===
|
||||
@@ -523,6 +529,50 @@ def try_load_ia3(name, network_on_disk, lora_scale):
|
||||
return finalize_network(net, name, 'IA3', lora_scale, t0, unmapped=unmapped, skipped=skipped)
|
||||
|
||||
|
||||
def try_load_glora(name, network_on_disk, lora_scale):
|
||||
"""Load a Flux2/Klein GLoRA adapter as native modules.
|
||||
|
||||
GLoRA stores four low-rank components (``a1``/``a2``/``b1``/``b2``) and
|
||||
computes ``ΔW = w2b @ w1b + (target @ w2a) @ w1a`` — the second term is
|
||||
target-dependent. Fused QKV in double_blocks is skipped with a warning
|
||||
because the target-dependent term doesn't slice cleanly without
|
||||
redirecting calc_updown to a fused proxy weight, and zero real-world
|
||||
GLoRA-on-DiT files exist.
|
||||
|
||||
Depends on the ``self.dim`` initialization fix in network_glora.py so
|
||||
that alpha-based ``calc_scale`` is honored.
|
||||
"""
|
||||
t0 = time.time()
|
||||
state_dict = sd_models.read_state_dict(network_on_disk.filename, what='network')
|
||||
if not has_marker(state_dict, GLORA_MARKERS):
|
||||
return None
|
||||
|
||||
mapping = resolve_mapping()
|
||||
net = new_network(name, network_on_disk)
|
||||
groups = group_by_suffixes(state_dict, GLORA_SUFFIXES)
|
||||
|
||||
unmapped = 0
|
||||
skipped = 0
|
||||
for (prefix, base), w in groups.items():
|
||||
if not all(k in w for k in ('a1.weight', 'a2.weight', 'b1.weight', 'b2.weight')):
|
||||
continue
|
||||
targets = resolve_targets(prefix, base)
|
||||
if any(t[1] is not None for t in targets):
|
||||
log.warning(f'Network load: type=GLoRA name="{name}" key={base} fused QKV skipped (unsupported)')
|
||||
skipped += 1
|
||||
continue
|
||||
for diffusers_path, _, _ in targets:
|
||||
network_key = "lora_transformer_" + diffusers_path.replace(".", "_")
|
||||
sd_module = mapping.get(network_key)
|
||||
if sd_module is None:
|
||||
unmapped += 1
|
||||
continue
|
||||
nw = network.NetworkWeights(network_key=network_key, sd_key=network_key, w=w, sd_module=sd_module)
|
||||
net.modules[network_key] = network_glora.NetworkModuleGLora(net, nw)
|
||||
|
||||
return finalize_network(net, name, 'GLoRA', lora_scale, t0, unmapped=unmapped, skipped=skipped)
|
||||
|
||||
|
||||
# === Diffusers-PEFT path helpers (used when lora_force_diffusers is on) ===
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user