Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -17,6 +17,8 @@ import time
|
|
| 17 |
import traceback
|
| 18 |
from functools import cache
|
| 19 |
|
|
|
|
|
|
|
| 20 |
# Before anything that could initialize CUDA: `import spaces` patches `torch.cuda` so the 72 GiB load can happen at
|
| 21 |
# startup rather than on GPU time.
|
| 22 |
import spaces
|
|
@@ -103,6 +105,61 @@ def lower_duration_floor(seconds: float = MIN_UI_DURATION) -> None:
|
|
| 103 |
|
| 104 |
MiniMaxH3ModularPipeline.min_duration = property(lambda self: float(seconds))
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
PIPE = None
|
| 108 |
FILM = None
|
|
@@ -189,18 +246,23 @@ def load_models() -> str | None:
|
|
| 189 |
for k, shape in sorted(norm_out.items()):
|
| 190 |
print(f"[lora-debug] {k} {shape}", flush=True)
|
| 191 |
|
| 192 |
-
# Approach B:
|
|
|
|
| 193 |
# request asks for them. `load_lora_adapter` is the model-level loader (`PeftAdapterMixin`), used because
|
| 194 |
# `MiniMaxH3ModularPipeline` has no pipeline-level `load_lora_weights` of its own.
|
| 195 |
if LORA_REPO.lower() not in ("", "off", "none"):
|
|
|
|
| 196 |
from peft.tuners.tuners_utils import BaseTunerLayer
|
|
|
|
| 197 |
|
| 198 |
try:
|
| 199 |
counts = {}
|
| 200 |
for name, filename in LORA_FILES.items():
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
|
|
|
|
|
|
| 204 |
# `load_lora_adapter` warns-and-continues on a zero-key match instead of raising, so count
|
| 205 |
# matched layers ourselves and fail loudly if a file attached nothing.
|
| 206 |
counts[name] = sum(
|
|
@@ -210,8 +272,8 @@ def load_models() -> str | None:
|
|
| 210 |
)
|
| 211 |
if counts[name] == 0:
|
| 212 |
raise RuntimeError(
|
| 213 |
-
f"'{filename}' matched 0 target modules on
|
| 214 |
-
f"
|
| 215 |
)
|
| 216 |
pipe.transformer.set_adapters(list(LORA_FILES), weights=[0.0] * len(LORA_FILES))
|
| 217 |
LORA_STATUS = "LoRAs loaded: " + ", ".join(
|
|
|
|
| 17 |
import traceback
|
| 18 |
from functools import cache
|
| 19 |
|
| 20 |
+
import torch
|
| 21 |
+
|
| 22 |
# Before anything that could initialize CUDA: `import spaces` patches `torch.cuda` so the 72 GiB load can happen at
|
| 23 |
# startup rather than on GPU time.
|
| 24 |
import spaces
|
|
|
|
| 105 |
|
| 106 |
MiniMaxH3ModularPipeline.min_duration = property(lambda self: float(seconds))
|
| 107 |
|
| 108 |
+
def _convert_diffusion_model_lora(raw: dict, transformer) -> dict:
|
| 109 |
+
"""Rename a `diffusion_model.blocks.*` (original-checkpoint) LoRA state dict onto
|
| 110 |
+
`MiniMaxH3Transformer3DModel`'s (`transformer_blocks.*`) naming, so `load_lora_adapter` can attach it.
|
| 111 |
+
`raw` maps original key -> tensor. `transformer` is the already-loaded base model, used only to read its
|
| 112 |
+
real to_q/to_k/to_v output sizes for the qkv split, rather than assuming an even three-way split.
|
| 113 |
+
"""
|
| 114 |
+
import re
|
| 115 |
+
|
| 116 |
+
base = transformer.state_dict()
|
| 117 |
+
out = {}
|
| 118 |
+
pattern = re.compile(r"^diffusion_model\.blocks\.(\d+)\.(attn|mlp)\.(\w+)\.(lora_[AB])\.weight$")
|
| 119 |
+
|
| 120 |
+
for key, tensor in raw.items():
|
| 121 |
+
match = pattern.match(key)
|
| 122 |
+
if not match:
|
| 123 |
+
print(f"[lora-convert] skipping unrecognized key: {key}", flush=True)
|
| 124 |
+
continue
|
| 125 |
+
block, kind, leaf, ab = match.groups()
|
| 126 |
+
prefix = f"transformer_blocks.{block}."
|
| 127 |
+
|
| 128 |
+
if kind == "attn" and leaf == "qkv_proj":
|
| 129 |
+
if ab == "lora_A":
|
| 130 |
+
# Shared low-rank input side — identical for q, k, v.
|
| 131 |
+
out[f"{prefix}attn.to_q.{ab}.weight"] = tensor
|
| 132 |
+
out[f"{prefix}attn.to_k.{ab}.weight"] = tensor
|
| 133 |
+
out[f"{prefix}attn.to_v.{ab}.weight"] = tensor
|
| 134 |
+
else:
|
| 135 |
+
q_out = base[f"{prefix}attn.to_q.weight"].shape[0]
|
| 136 |
+
k_out = base[f"{prefix}attn.to_k.weight"].shape[0]
|
| 137 |
+
v_out = base[f"{prefix}attn.to_v.weight"].shape[0]
|
| 138 |
+
assert tensor.shape[0] == q_out + k_out + v_out, (
|
| 139 |
+
f"{key}: expected {q_out + k_out + v_out} rows (q{q_out}+k{k_out}+v{v_out}), "
|
| 140 |
+
f"got {tensor.shape[0]}"
|
| 141 |
+
)
|
| 142 |
+
out[f"{prefix}attn.to_q.{ab}.weight"] = tensor[:q_out].clone()
|
| 143 |
+
out[f"{prefix}attn.to_k.{ab}.weight"] = tensor[q_out:q_out + k_out].clone()
|
| 144 |
+
out[f"{prefix}attn.to_v.{ab}.weight"] = tensor[q_out + k_out:].clone()
|
| 145 |
+
|
| 146 |
+
elif kind == "attn" and leaf == "out_proj":
|
| 147 |
+
out[f"{prefix}attn.to_out.0.{ab}.weight"] = tensor
|
| 148 |
+
|
| 149 |
+
elif kind == "mlp" and leaf == "fc1":
|
| 150 |
+
if ab == "lora_B" and SWAP_FC1_HALVES:
|
| 151 |
+
# Some checkpoints store the SwiGLU gate/value halves in the opposite order diffusers expects.
|
| 152 |
+
half = tensor.shape[0] // 2
|
| 153 |
+
tensor = torch.cat([tensor[half:], tensor[:half]], dim=0)
|
| 154 |
+
out[f"{prefix}ff.net.0.proj.{ab}.weight"] = tensor
|
| 155 |
+
|
| 156 |
+
elif kind == "mlp" and leaf == "fc2":
|
| 157 |
+
out[f"{prefix}ff.net.2.{ab}.weight"] = tensor
|
| 158 |
+
|
| 159 |
+
else:
|
| 160 |
+
print(f"[lora-convert] no mapping for {key}, skipping", flush=True)
|
| 161 |
+
|
| 162 |
+
return out
|
| 163 |
|
| 164 |
PIPE = None
|
| 165 |
FILM = None
|
|
|
|
| 246 |
for k, shape in sorted(norm_out.items()):
|
| 247 |
print(f"[lora-debug] {k} {shape}", flush=True)
|
| 248 |
|
| 249 |
+
# Approach B: convert each LoRA from its original `diffusion_model.blocks.*` naming onto this
|
| 250 |
+
# transformer's `transformer_blocks.*` naming, then attach as PEFT layers, inactive (weight 0) until a
|
| 251 |
# request asks for them. `load_lora_adapter` is the model-level loader (`PeftAdapterMixin`), used because
|
| 252 |
# `MiniMaxH3ModularPipeline` has no pipeline-level `load_lora_weights` of its own.
|
| 253 |
if LORA_REPO.lower() not in ("", "off", "none"):
|
| 254 |
+
from huggingface_hub import hf_hub_download
|
| 255 |
from peft.tuners.tuners_utils import BaseTunerLayer
|
| 256 |
+
from safetensors import safe_open
|
| 257 |
|
| 258 |
try:
|
| 259 |
counts = {}
|
| 260 |
for name, filename in LORA_FILES.items():
|
| 261 |
+
path = hf_hub_download(LORA_REPO, filename)
|
| 262 |
+
with safe_open(path, framework="pt") as handle:
|
| 263 |
+
raw = {k: handle.get_tensor(k) for k in handle.keys()}
|
| 264 |
+
converted = _convert_diffusion_model_lora(raw, pipe.transformer)
|
| 265 |
+
pipe.transformer.load_lora_adapter(converted, adapter_name=name, prefix=None)
|
| 266 |
# `load_lora_adapter` warns-and-continues on a zero-key match instead of raising, so count
|
| 267 |
# matched layers ourselves and fail loudly if a file attached nothing.
|
| 268 |
counts[name] = sum(
|
|
|
|
| 272 |
)
|
| 273 |
if counts[name] == 0:
|
| 274 |
raise RuntimeError(
|
| 275 |
+
f"'{filename}' converted but matched 0 target modules on "
|
| 276 |
+
f"MiniMaxH3Transformer3DModel — the rename map needs another look."
|
| 277 |
)
|
| 278 |
pipe.transformer.set_adapters(list(LORA_FILES), weights=[0.0] * len(LORA_FILES))
|
| 279 |
LORA_STATUS = "LoRAs loaded: " + ", ".join(
|