dagloop5 commited on
Commit
048284b
·
verified ·
1 Parent(s): 46545b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -6
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: attach both LoRA adapters as PEFT layers on the transformer, inactive (weight 0) until a
 
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
- pipe.transformer.load_lora_adapter(
202
- LORA_REPO, weight_name=filename, adapter_name=name, prefix=None
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 MiniMaxH3Transformer3DModel — its LoRA "
214
- f"keys don't line up with this transformer's naming. Inspect its safetensors keys."
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(