dagloop5 commited on
Commit
6a20a66
·
verified ·
1 Parent(s): 864ec06

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -20
app.py CHANGED
@@ -37,7 +37,7 @@ GPU_SIZE = os.environ.get("H3_GPU_SIZE", "xlarge")
37
 
38
  LORA_REPO = os.environ.get("H3_LORA_REPO", "dagloop5/LoRA")
39
  LORA_FILES = {
40
- "lora1": os.environ.get("H3_LORA_A_FILE", "minimax_h3_turbo_v4_step600_ema.safetensors"),
41
  "loraa": os.environ.get("H3_LORA_A_FILE", "Mylo_lora_epoch31.safetensors"),
42
  "lorab": os.environ.get("H3_LORA_B_FILE", "VBVR_H3_attn_only.safetensors"),
43
  "lorac": os.environ.get("H3_LORA_C_FILE", "AIO_V2.safetensors"),
@@ -144,19 +144,27 @@ def _convert_diffusion_model_lora(raw: dict, base_shapes: dict) -> dict:
144
  import re
145
 
146
  out = {}
147
- pattern = re.compile(r"^diffusion_model\.blocks\.(\d+)\.(attn|mlp)\.(\w+)\.(lora_[AB])\.weight$")
148
 
149
- for key, raw_tensor in raw.items():
150
- match = pattern.match(key)
151
- if not match:
152
- print(f"[lora-convert] skipping unrecognized key: {key}", flush=True)
153
- continue
154
- # Some files (fp16-labeled ones especially) don't match the bf16 transformer's dtype; PEFT expects the
155
- # adapter's dtype to match the wrapped base layer's.
156
- tensor = raw_tensor.to(torch.bfloat16)
157
- block, kind, leaf, ab = match.groups()
 
 
 
 
 
 
 
 
 
 
158
  prefix = f"transformer_blocks.{block}."
159
-
160
  if kind == "attn" and leaf == "qkv_proj":
161
  if ab == "lora_A":
162
  # Shared low-rank input side — identical for q, k, v.
@@ -168,28 +176,55 @@ def _convert_diffusion_model_lora(raw: dict, base_shapes: dict) -> dict:
168
  k_out = base_shapes[f"{prefix}attn.to_k.weight"][0]
169
  v_out = base_shapes[f"{prefix}attn.to_v.weight"][0]
170
  assert tensor.shape[0] == q_out + k_out + v_out, (
171
- f"{key}: expected {q_out + k_out + v_out} rows (q{q_out}+k{k_out}+v{v_out}), "
172
- f"got {tensor.shape[0]}"
173
  )
174
  out[f"{prefix}attn.to_q.{ab}.weight"] = tensor[:q_out].clone()
175
  out[f"{prefix}attn.to_k.{ab}.weight"] = tensor[q_out:q_out + k_out].clone()
176
  out[f"{prefix}attn.to_v.{ab}.weight"] = tensor[q_out + k_out:].clone()
177
-
178
  elif kind == "attn" and leaf == "out_proj":
179
  out[f"{prefix}attn.to_out.0.{ab}.weight"] = tensor
180
-
181
  elif kind == "mlp" and leaf == "fc1":
182
  if ab == "lora_B" and SWAP_FC1_HALVES:
183
- # Some checkpoints store the SwiGLU gate/value halves in the opposite order diffusers expects.
184
  half = tensor.shape[0] // 2
185
  tensor = torch.cat([tensor[half:], tensor[:half]], dim=0)
186
  out[f"{prefix}ff.net.0.proj.{ab}.weight"] = tensor
187
-
188
  elif kind == "mlp" and leaf == "fc2":
189
  out[f"{prefix}ff.net.2.{ab}.weight"] = tensor
190
-
 
191
  else:
192
- print(f"[lora-convert] no mapping for {key}, skipping", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
  return out
195
 
 
37
 
38
  LORA_REPO = os.environ.get("H3_LORA_REPO", "dagloop5/LoRA")
39
  LORA_FILES = {
40
+ "lora1": os.environ.get("H3_LORA_1_FILE", "minimax_h3_turbo_v4_step600_ema.safetensors"),
41
  "loraa": os.environ.get("H3_LORA_A_FILE", "Mylo_lora_epoch31.safetensors"),
42
  "lorab": os.environ.get("H3_LORA_B_FILE", "VBVR_H3_attn_only.safetensors"),
43
  "lorac": os.environ.get("H3_LORA_C_FILE", "AIO_V2.safetensors"),
 
144
  import re
145
 
146
  out = {}
 
147
 
148
+ # Family A: `[diffusion_model.]blocks.N.(attn|mlp|adaln_proj).LEAF.(lora_A|lora_B).weight` — covers Mylo,
149
+ # VBVR, AIO_V2, moawxx, the Furry Realism LoRA, and (minus its `diffusion_model.` prefix) the Turbo LoRA.
150
+ standard = re.compile(
151
+ r"^(?:diffusion_model\.)?blocks\.(\d+)\.(attn|mlp|adaln_proj)\.([\w.]+)\.(lora_[AB])\.weight$"
152
+ )
153
+ # Family B (Kohya-style): `lora_unet_blocks_N_TARGET.(lora_down|lora_up).weight` covers SB and Fluid
154
+ # Enhancer. `lora_down`/`lora_up` are the same A/B convention under a different name.
155
+ kohya = re.compile(
156
+ r"^lora_unet_blocks_(\d+)_(attn_out_proj|attn_qkv_proj|mlp_fc1|mlp_fc2)\.(lora_down|lora_up)\.weight$"
157
+ )
158
+ kohya_targets = {
159
+ "attn_out_proj": ("attn", "out_proj"),
160
+ "attn_qkv_proj": ("attn", "qkv_proj"),
161
+ "mlp_fc1": ("mlp", "fc1"),
162
+ "mlp_fc2": ("mlp", "fc2"),
163
+ }
164
+ kohya_ab = {"lora_down": "lora_A", "lora_up": "lora_B"}
165
+
166
+ def emit(block: str, kind: str, leaf: str, ab: str, tensor) -> None:
167
  prefix = f"transformer_blocks.{block}."
 
168
  if kind == "attn" and leaf == "qkv_proj":
169
  if ab == "lora_A":
170
  # Shared low-rank input side — identical for q, k, v.
 
176
  k_out = base_shapes[f"{prefix}attn.to_k.weight"][0]
177
  v_out = base_shapes[f"{prefix}attn.to_v.weight"][0]
178
  assert tensor.shape[0] == q_out + k_out + v_out, (
179
+ f"blocks.{block}.attn.qkv_proj.{ab}: expected {q_out + k_out + v_out} rows "
180
+ f"(q{q_out}+k{k_out}+v{v_out}), got {tensor.shape[0]}"
181
  )
182
  out[f"{prefix}attn.to_q.{ab}.weight"] = tensor[:q_out].clone()
183
  out[f"{prefix}attn.to_k.{ab}.weight"] = tensor[q_out:q_out + k_out].clone()
184
  out[f"{prefix}attn.to_v.{ab}.weight"] = tensor[q_out + k_out:].clone()
 
185
  elif kind == "attn" and leaf == "out_proj":
186
  out[f"{prefix}attn.to_out.0.{ab}.weight"] = tensor
 
187
  elif kind == "mlp" and leaf == "fc1":
188
  if ab == "lora_B" and SWAP_FC1_HALVES:
 
189
  half = tensor.shape[0] // 2
190
  tensor = torch.cat([tensor[half:], tensor[:half]], dim=0)
191
  out[f"{prefix}ff.net.0.proj.{ab}.weight"] = tensor
 
192
  elif kind == "mlp" and leaf == "fc2":
193
  out[f"{prefix}ff.net.2.{ab}.weight"] = tensor
194
+ elif kind == "adaln_proj" and leaf == "linear":
195
+ out[f"{prefix}adaln_proj.linear.{ab}.weight"] = tensor
196
  else:
197
+ print(f"[lora-convert] no mapping for blocks.{block}.{kind}.{leaf}.{ab}, skipping", flush=True)
198
+
199
+ for key, raw_tensor in raw.items():
200
+ # Some files (fp16-labeled ones especially) don't match the bf16 transformer's dtype; PEFT expects the
201
+ # adapter's dtype to match the wrapped base layer's.
202
+ tensor = raw_tensor.to(torch.bfloat16)
203
+
204
+ match = standard.match(key)
205
+ if match:
206
+ block, kind, leaf, ab = match.groups()
207
+ emit(block, kind, leaf, ab, tensor)
208
+ continue
209
+
210
+ match = kohya.match(key)
211
+ if match:
212
+ block, target, direction = match.groups()
213
+ kind, leaf = kohya_targets[target]
214
+ emit(block, kind, leaf, kohya_ab[direction], tensor)
215
+ continue
216
+
217
+ if key.endswith(".alpha"):
218
+ # Per-module rank/alpha scaling isn't threaded through — matched modules get PEFT's default scaling
219
+ # (scale 1.0), and the UI slider is what actually controls each LoRA's visible strength here. This
220
+ # is a known simplification: a file's built-in alpha may have scaled it up or down from its raw
221
+ # rank, so its slider range that "feels right" may not match what the file's author intended or
222
+ # tested at. It isn't a bug — the Furry Realism LoRA's `.alpha` keys were already dropped the same
223
+ # way and it loads and works fine — just worth knowing if a LoRA's effect seems unexpectedly
224
+ # weak/strong across its whole slider range rather than at a specific value.
225
+ continue
226
+
227
+ print(f"[lora-convert] skipping unrecognized key: {key}", flush=True)
228
 
229
  return out
230