Spaces:
Running on Zero
Running on Zero
Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +9 -0
- README.md +29 -6
- app.py +288 -0
- assets/exemplar_origin_0.png +3 -0
- assets/exemplar_origin_1.png +3 -0
- assets/exemplar_origin_2.png +3 -0
- assets/exemplar_retouched_0.png +3 -0
- assets/exemplar_retouched_1.png +3 -0
- assets/exemplar_retouched_2.png +3 -0
- assets/query_0.png +3 -0
- assets/query_1.png +3 -0
- assets/query_2.png +3 -0
- diffsynth/__init__.py +1 -0
- diffsynth/configs/__init__.py +2 -0
- diffsynth/configs/model_configs.py +70 -0
- diffsynth/configs/vram_management_module_maps.py +24 -0
- diffsynth/core/__init__.py +4 -0
- diffsynth/core/attention/__init__.py +1 -0
- diffsynth/core/attention/attention.py +121 -0
- diffsynth/core/gradient/__init__.py +1 -0
- diffsynth/core/gradient/gradient_checkpoint.py +34 -0
- diffsynth/core/loader/__init__.py +3 -0
- diffsynth/core/loader/config.py +119 -0
- diffsynth/core/loader/file.py +121 -0
- diffsynth/core/loader/model.py +79 -0
- diffsynth/core/vram/__init__.py +2 -0
- diffsynth/core/vram/disk_map.py +93 -0
- diffsynth/core/vram/initialization.py +21 -0
- diffsynth/core/vram/layers.py +475 -0
- diffsynth/diffusion/__init__.py +1 -0
- diffsynth/diffusion/base_pipeline.py +439 -0
- diffsynth/diffusion/flow_match.py +179 -0
- diffsynth/models/__init__.py +0 -0
- diffsynth/models/general_modules.py +139 -0
- diffsynth/models/model_loader.py +111 -0
- diffsynth/models/qwen_image_connector.py +260 -0
- diffsynth/models/qwen_image_controlnet.py +56 -0
- diffsynth/models/qwen_image_dit.py +572 -0
- diffsynth/models/qwen_image_extractor.py +415 -0
- diffsynth/models/qwen_image_learnable_query.py +19 -0
- diffsynth/models/qwen_image_mae.py +106 -0
- diffsynth/models/qwen_image_rformer.py +512 -0
- diffsynth/models/qwen_image_text_encoder.py +251 -0
- diffsynth/models/qwen_image_vae.py +876 -0
- diffsynth/pipelines/__init__.py +0 -0
- diffsynth/pipelines/qwen_image.py +1374 -0
- diffsynth/utils/__init__.py +0 -0
- diffsynth/utils/controlnet/__init__.py +1 -0
- diffsynth/utils/controlnet/controlnet_input.py +13 -0
- diffsynth/utils/lora/__init__.py +1 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,12 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
assets/exemplar_origin_0.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
assets/exemplar_origin_1.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
assets/exemplar_origin_2.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
assets/exemplar_retouched_0.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
assets/exemplar_retouched_1.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
assets/exemplar_retouched_2.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
assets/query_0.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
assets/query_1.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
assets/query_2.png filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,13 +1,36 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: purple
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.20.0
|
| 8 |
-
python_version: '3.12'
|
| 9 |
app_file: app.py
|
| 10 |
-
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: MirrorPPR
|
| 3 |
+
emoji: 🪞
|
| 4 |
colorFrom: purple
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.20.0
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
+
short_description: Exemplar-based portrait photo retouching
|
| 10 |
+
python_version: "3.12"
|
| 11 |
+
startup_duration_timeout: 1h
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# MirrorPPR: Exemplar-Based Portrait Photo Retouching
|
| 15 |
+
|
| 16 |
+
This Space demonstrates [MirrorPPR](https://arxiv.org/abs/2606.29308), a novel framework for
|
| 17 |
+
exemplar-based portrait photo retouching. Given an exemplar pair (an original face image and its
|
| 18 |
+
retouched version), the model infers the retouching operations and applies them to a new query image.
|
| 19 |
+
|
| 20 |
+
## Usage
|
| 21 |
+
|
| 22 |
+
1. Upload a query face image you want to retouch.
|
| 23 |
+
2. Provide an exemplar pair: the original and retouched version of another face showing the desired
|
| 24 |
+
retouching style. You can quickly load a pre-packaged exemplar pair from the template gallery.
|
| 25 |
+
3. Click "Retouch" to apply the exemplar's retouching operations to your query image.
|
| 26 |
+
|
| 27 |
+
## Model
|
| 28 |
+
|
| 29 |
+
This Space uses the [SJTU-DENG-Lab/MirrorPPR-Face](https://huggingface.co/SJTU-DENG-Lab/MirrorPPR-Face)
|
| 30 |
+
model, built on top of Qwen-Image-Edit with a Retouching Operation Extractor (MAE + R-Former + Connector)
|
| 31 |
+
and LoRA adaptation.
|
| 32 |
+
|
| 33 |
+
## References
|
| 34 |
+
|
| 35 |
+
- Paper: [MirrorPPR: Exemplar-Based Portrait Photo Retouching](https://arxiv.org/abs/2606.29308)
|
| 36 |
+
- GitHub: [SJTU-DENG-Lab/MirrorPPR](https://github.com/SJTU-DENG-Lab/MirrorPPR)
|
app.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
|
| 4 |
+
|
| 5 |
+
import spaces # MUST come before torch / any CUDA-touching import
|
| 6 |
+
import torch
|
| 7 |
+
import gradio as gr
|
| 8 |
+
import glob
|
| 9 |
+
from PIL import Image
|
| 10 |
+
from huggingface_hub import snapshot_download
|
| 11 |
+
|
| 12 |
+
from mirrorppr.data.image_ops import round_to_multiple
|
| 13 |
+
from diffsynth import load_state_dict
|
| 14 |
+
from diffsynth.pipelines.qwen_image import ModelConfig, QwenImagePipeline
|
| 15 |
+
|
| 16 |
+
MODEL_ID = "SJTU-DENG-Lab/MirrorPPR-Face"
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _glob_required(pattern):
|
| 20 |
+
files = sorted(glob.glob(pattern))
|
| 21 |
+
if not files:
|
| 22 |
+
raise FileNotFoundError(f"No files matched: {pattern}")
|
| 23 |
+
return files
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _build_paths(weights_root, qwen_root):
|
| 27 |
+
qwen = qwen_root or os.path.join(weights_root, "qwen_image_edit")
|
| 28 |
+
face = os.path.join(weights_root, "mirrorppr_face")
|
| 29 |
+
return {
|
| 30 |
+
"dit": _glob_required(os.path.join(qwen, "transformer", "diffusion_pytorch_model*.safetensors")),
|
| 31 |
+
"text_encoder": _glob_required(os.path.join(qwen, "text_encoder", "model*.safetensors")),
|
| 32 |
+
"vae": os.path.join(qwen, "vae", "diffusion_pytorch_model.safetensors"),
|
| 33 |
+
"processor": os.path.join(qwen, "processor"),
|
| 34 |
+
"mae": os.path.join(face, "mae", "mae_pretrained.safetensors"),
|
| 35 |
+
"rformer": os.path.join(face, "rformer", "rformer.safetensors"),
|
| 36 |
+
"connector": os.path.join(face, "connector", "connector.safetensors"),
|
| 37 |
+
"lora": os.path.join(face, "lora", "lora.safetensors"),
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
print("Downloading model weights from Hugging Face Hub...")
|
| 42 |
+
_local_root = snapshot_download(repo_id=MODEL_ID)
|
| 43 |
+
_paths = _build_paths(_local_root, None)
|
| 44 |
+
print(f"Model downloaded to: {_local_root}")
|
| 45 |
+
|
| 46 |
+
pipe = QwenImagePipeline.from_pretrained(
|
| 47 |
+
torch_dtype=torch.bfloat16,
|
| 48 |
+
device="cuda",
|
| 49 |
+
model_configs=[
|
| 50 |
+
ModelConfig(path=_paths["dit"]),
|
| 51 |
+
ModelConfig(path=_paths["text_encoder"]),
|
| 52 |
+
ModelConfig(path=_paths["vae"]),
|
| 53 |
+
ModelConfig(path=_paths["mae"]),
|
| 54 |
+
ModelConfig(path=_paths["rformer"]),
|
| 55 |
+
ModelConfig(path=_paths["connector"]),
|
| 56 |
+
],
|
| 57 |
+
tokenizer_config=None,
|
| 58 |
+
processor_config=ModelConfig(path=_paths["processor"]),
|
| 59 |
+
)
|
| 60 |
+
if pipe.rformer is None:
|
| 61 |
+
raise RuntimeError("R-Former module failed to load.")
|
| 62 |
+
if not hasattr(pipe, "connector") or pipe.connector is None:
|
| 63 |
+
raise RuntimeError("Connector module failed to load.")
|
| 64 |
+
pipe.rformer.load_state_dict(load_state_dict(_paths["rformer"]))
|
| 65 |
+
pipe.connector.load_state_dict(load_state_dict(_paths["connector"]))
|
| 66 |
+
pipe.load_lora(pipe.dit, _paths["lora"])
|
| 67 |
+
print("MirrorPPR-Face pipeline loaded successfully.")
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
# Pre-packaged exemplar pairs for quick selection
|
| 71 |
+
EXEMPLAR_PAIRS = [
|
| 72 |
+
{
|
| 73 |
+
"name": "Style 1: Eye enlargement + mouth adjustments",
|
| 74 |
+
"origin": "assets/exemplar_origin_0.png",
|
| 75 |
+
"retouched": "assets/exemplar_retouched_0.png",
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"name": "Style 2: Eye enlargement + nose lengthening",
|
| 79 |
+
"origin": "assets/exemplar_origin_1.png",
|
| 80 |
+
"retouched": "assets/exemplar_retouched_1.png",
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"name": "Style 3: Eye enlargement + lip plump",
|
| 84 |
+
"origin": "assets/exemplar_origin_2.png",
|
| 85 |
+
"retouched": "assets/exemplar_retouched_2.png",
|
| 86 |
+
},
|
| 87 |
+
]
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _on_exemplar_select(evt: gr.SelectData):
|
| 91 |
+
"""Load a pre-packaged exemplar pair when the user clicks a gallery item."""
|
| 92 |
+
idx = evt.index
|
| 93 |
+
if isinstance(idx, list):
|
| 94 |
+
idx = idx[0] if idx else 0
|
| 95 |
+
idx = int(idx)
|
| 96 |
+
if 0 <= idx < len(EXEMPLAR_PAIRS):
|
| 97 |
+
pair = EXEMPLAR_PAIRS[idx]
|
| 98 |
+
return pair["origin"], pair["retouched"]
|
| 99 |
+
return None, None
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
@spaces.GPU(duration=180)
|
| 103 |
+
def retouch(
|
| 104 |
+
query_image,
|
| 105 |
+
exemplar_origin,
|
| 106 |
+
exemplar_retouched,
|
| 107 |
+
steps=40,
|
| 108 |
+
seed=123,
|
| 109 |
+
cfg_scale=4.0,
|
| 110 |
+
):
|
| 111 |
+
"""Apply exemplar-based portrait photo retouching to a query image.
|
| 112 |
+
|
| 113 |
+
Given an exemplar pair (an original face and its retouched version),
|
| 114 |
+
this function infers the retouching operations and applies them to
|
| 115 |
+
a new query face image.
|
| 116 |
+
|
| 117 |
+
Args:
|
| 118 |
+
query_image: The face image to be retouched.
|
| 119 |
+
exemplar_origin: The original (pre-retouch) exemplar image.
|
| 120 |
+
exemplar_retouched: The retouched exemplar image.
|
| 121 |
+
steps: Number of diffusion inference steps (default 40).
|
| 122 |
+
seed: Random seed for reproducibility (default 123).
|
| 123 |
+
cfg_scale: Classifier-free guidance scale (default 4.0).
|
| 124 |
+
|
| 125 |
+
Returns:
|
| 126 |
+
The retouched query image.
|
| 127 |
+
"""
|
| 128 |
+
if query_image is None:
|
| 129 |
+
raise gr.Error("Please provide a query image.")
|
| 130 |
+
if exemplar_origin is None or exemplar_retouched is None:
|
| 131 |
+
raise gr.Error("Please provide both exemplar images (origin and retouched).")
|
| 132 |
+
|
| 133 |
+
query = Image.fromarray(query_image).convert("RGB")
|
| 134 |
+
ex_origin = Image.fromarray(exemplar_origin).convert("RGB")
|
| 135 |
+
ex_target = Image.fromarray(exemplar_retouched).convert("RGB")
|
| 136 |
+
|
| 137 |
+
width, height = query.size
|
| 138 |
+
width = round_to_multiple(width, 16)
|
| 139 |
+
height = round_to_multiple(height, 16)
|
| 140 |
+
|
| 141 |
+
result = pipe(
|
| 142 |
+
"",
|
| 143 |
+
example_origin=ex_origin,
|
| 144 |
+
example_target=ex_target,
|
| 145 |
+
edit_image=query,
|
| 146 |
+
seed=int(seed),
|
| 147 |
+
num_inference_steps=int(steps),
|
| 148 |
+
height=height,
|
| 149 |
+
width=width,
|
| 150 |
+
edit_image_auto_resize=False,
|
| 151 |
+
cfg_scale=cfg_scale,
|
| 152 |
+
)
|
| 153 |
+
return result
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
CSS = """
|
| 157 |
+
#col-container { max-width: 1200px; margin: 0 auto; }
|
| 158 |
+
.dark .gradio-container { color: var(--body-text-color); }
|
| 159 |
+
"""
|
| 160 |
+
|
| 161 |
+
with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
|
| 162 |
+
gr.Markdown(
|
| 163 |
+
"""
|
| 164 |
+
# MirrorPPR: Exemplar-Based Portrait Photo Retouching
|
| 165 |
+
|
| 166 |
+
Upload a face image (query) and provide an exemplar pair (original → retouched).
|
| 167 |
+
The model infers the retouching operations from the exemplar pair and applies
|
| 168 |
+
them to your query image. Try a pre-packaged exemplar from the gallery below.
|
| 169 |
+
|
| 170 |
+
[Paper](https://arxiv.org/abs/2606.29308) · [GitHub](https://github.com/SJTU-DENG-Lab/MirrorPPR) · [Model](https://huggingface.co/SJTU-DENG-Lab/MirrorPPR-Face)
|
| 171 |
+
"""
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
with gr.Row():
|
| 175 |
+
# Left column: inputs
|
| 176 |
+
with gr.Column(scale=1):
|
| 177 |
+
gr.Markdown("### Query Image (to retouch)")
|
| 178 |
+
query_img = gr.Image(
|
| 179 |
+
label="Query Image",
|
| 180 |
+
type="numpy",
|
| 181 |
+
height=300,
|
| 182 |
+
)
|
| 183 |
+
gr.Markdown("### Exemplar Pair (reference retouching style)")
|
| 184 |
+
ex_origin_img = gr.Image(
|
| 185 |
+
label="Exemplar Original",
|
| 186 |
+
type="numpy",
|
| 187 |
+
height=200,
|
| 188 |
+
)
|
| 189 |
+
ex_retouched_img = gr.Image(
|
| 190 |
+
label="Exemplar Retouched",
|
| 191 |
+
type="numpy",
|
| 192 |
+
height=200,
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
gr.Markdown("### Quick Exemplar Templates")
|
| 196 |
+
exemplar_gallery = gr.Gallery(
|
| 197 |
+
label="Click a template to load an exemplar pair",
|
| 198 |
+
value=[
|
| 199 |
+
(pair["origin"], pair["name"])
|
| 200 |
+
for pair in EXEMPLAR_PAIRS
|
| 201 |
+
],
|
| 202 |
+
columns=3,
|
| 203 |
+
height=150,
|
| 204 |
+
show_label=False,
|
| 205 |
+
allow_preview=False,
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
with gr.Accordion("Advanced settings", open=False):
|
| 209 |
+
steps_slider = gr.Slider(
|
| 210 |
+
label="Inference steps",
|
| 211 |
+
minimum=10,
|
| 212 |
+
maximum=80,
|
| 213 |
+
value=40,
|
| 214 |
+
step=1,
|
| 215 |
+
)
|
| 216 |
+
seed_input = gr.Number(
|
| 217 |
+
label="Seed",
|
| 218 |
+
value=123,
|
| 219 |
+
precision=0,
|
| 220 |
+
)
|
| 221 |
+
cfg_slider = gr.Slider(
|
| 222 |
+
label="CFG scale",
|
| 223 |
+
minimum=1.0,
|
| 224 |
+
maximum=10.0,
|
| 225 |
+
value=4.0,
|
| 226 |
+
step=0.5,
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
run_btn = gr.Button("Retouch", variant="primary", size="lg")
|
| 230 |
+
|
| 231 |
+
# Right column: output
|
| 232 |
+
with gr.Column(scale=1):
|
| 233 |
+
gr.Markdown("### Retouched Result")
|
| 234 |
+
output_img = gr.Image(
|
| 235 |
+
label="Retouched Query",
|
| 236 |
+
type="pil",
|
| 237 |
+
height=400,
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
# Wire up exemplar gallery selection
|
| 241 |
+
exemplar_gallery.select(
|
| 242 |
+
fn=_on_exemplar_select,
|
| 243 |
+
outputs=[ex_origin_img, ex_retouched_img],
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
# Wire up the run button
|
| 247 |
+
run_btn.click(
|
| 248 |
+
fn=retouch,
|
| 249 |
+
inputs=[query_img, ex_origin_img, ex_retouched_img, steps_slider, seed_input, cfg_slider],
|
| 250 |
+
outputs=output_img,
|
| 251 |
+
api_name="retouch",
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
gr.Examples(
|
| 255 |
+
examples=[
|
| 256 |
+
[
|
| 257 |
+
"assets/query_0.png",
|
| 258 |
+
"assets/exemplar_origin_0.png",
|
| 259 |
+
"assets/exemplar_retouched_0.png",
|
| 260 |
+
40,
|
| 261 |
+
123,
|
| 262 |
+
4.0,
|
| 263 |
+
],
|
| 264 |
+
[
|
| 265 |
+
"assets/query_1.png",
|
| 266 |
+
"assets/exemplar_origin_1.png",
|
| 267 |
+
"assets/exemplar_retouched_1.png",
|
| 268 |
+
40,
|
| 269 |
+
123,
|
| 270 |
+
4.0,
|
| 271 |
+
],
|
| 272 |
+
[
|
| 273 |
+
"assets/query_2.png",
|
| 274 |
+
"assets/exemplar_origin_2.png",
|
| 275 |
+
"assets/exemplar_retouched_2.png",
|
| 276 |
+
40,
|
| 277 |
+
123,
|
| 278 |
+
4.0,
|
| 279 |
+
],
|
| 280 |
+
],
|
| 281 |
+
inputs=[query_img, ex_origin_img, ex_retouched_img, steps_slider, seed_input, cfg_slider],
|
| 282 |
+
outputs=output_img,
|
| 283 |
+
fn=retouch,
|
| 284 |
+
cache_examples=True,
|
| 285 |
+
cache_mode="lazy",
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
demo.launch(mcp_server=True)
|
assets/exemplar_origin_0.png
ADDED
|
Git LFS Details
|
assets/exemplar_origin_1.png
ADDED
|
Git LFS Details
|
assets/exemplar_origin_2.png
ADDED
|
Git LFS Details
|
assets/exemplar_retouched_0.png
ADDED
|
Git LFS Details
|
assets/exemplar_retouched_1.png
ADDED
|
Git LFS Details
|
assets/exemplar_retouched_2.png
ADDED
|
Git LFS Details
|
assets/query_0.png
ADDED
|
Git LFS Details
|
assets/query_1.png
ADDED
|
Git LFS Details
|
assets/query_2.png
ADDED
|
Git LFS Details
|
diffsynth/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .core import *
|
diffsynth/configs/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .model_configs import MODEL_CONFIGS
|
| 2 |
+
from .vram_management_module_maps import VRAM_MANAGEMENT_MODULE_MAPS
|
diffsynth/configs/model_configs.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
qwen_image_series = [
|
| 2 |
+
{
|
| 3 |
+
"model_hash": "0319a1cb19835fb510907dd3367c95ff",
|
| 4 |
+
"model_name": "qwen_image_dit",
|
| 5 |
+
"model_class": "diffsynth.models.qwen_image_dit.QwenImageDiT",
|
| 6 |
+
},
|
| 7 |
+
{
|
| 8 |
+
"model_hash": "8004730443f55db63092006dd9f7110e",
|
| 9 |
+
"model_name": "qwen_image_text_encoder",
|
| 10 |
+
"model_class": "diffsynth.models.qwen_image_text_encoder.QwenImageTextEncoder",
|
| 11 |
+
"state_dict_converter": "diffsynth.utils.state_dict_converters.qwen_image_text_encoder.QwenImageTextEncoderStateDictConverter",
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"model_hash": "ed4ea5824d55ec3107b09815e318123a",
|
| 15 |
+
"model_name": "qwen_image_vae",
|
| 16 |
+
"model_class": "diffsynth.models.qwen_image_vae.QwenImageVAE",
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"model_hash": "073bce9cf969e317e5662cd570c3e79c",
|
| 20 |
+
"model_name": "qwen_image_blockwise_controlnet",
|
| 21 |
+
"model_class": "diffsynth.models.qwen_image_controlnet.QwenImageBlockWiseControlNet",
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"model_hash": "a9e54e480a628f0b956a688a81c33bab",
|
| 25 |
+
"model_name": "qwen_image_blockwise_controlnet",
|
| 26 |
+
"model_class": "diffsynth.models.qwen_image_controlnet.QwenImageBlockWiseControlNet",
|
| 27 |
+
"extra_kwargs": {"additional_in_dim": 4},
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"model_hash": "49ae821b996dc7e19a5e8d9d4f3c5315",
|
| 31 |
+
"model_name": "qwen_image_connector",
|
| 32 |
+
"model_class": "diffsynth.models.qwen_image_connector.QwenImageConnector",
|
| 33 |
+
"extra_kwargs": {"input_dim": 768, "num_layers": 6},
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"model_hash": "7dd3640ddce93380be19b6c86f9e7e64",
|
| 37 |
+
"model_name": "qwen_image_connector",
|
| 38 |
+
"model_class": "diffsynth.models.qwen_image_connector.QwenImageConnector",
|
| 39 |
+
"extra_kwargs": {"num_layers": 6},
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"model_hash": "7b45ff1c1703e389ef61c8ab12babc4d",
|
| 43 |
+
"model_name": "qwen_image_learnable_query",
|
| 44 |
+
"model_class": "diffsynth.models.qwen_image_learnable_query.QwenImageLearnableQuery",
|
| 45 |
+
"extra_kwargs": {"query_length": 256},
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"model_hash": "5dbd9a25c517478a8d2f35b01da53e9a",
|
| 49 |
+
"model_name": "qwen_image_extractor",
|
| 50 |
+
"model_class": "diffsynth.models.qwen_image_extractor.QwenImageExtractor",
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"model_hash": "bc4b4d58554d31694168550738ae8286",
|
| 54 |
+
"model_name": "qwen_image_rformer",
|
| 55 |
+
"model_class": "diffsynth.models.qwen_image_rformer.RFormer",
|
| 56 |
+
},
|
| 57 |
+
{
|
| 58 |
+
"model_hash": "aea189b86b5df7cffc8ec81d76fde9a5",
|
| 59 |
+
"model_name": "qwen_image_rformer2D",
|
| 60 |
+
"model_class": "diffsynth.models.qwen_image_rformer.RFormer2D",
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"model_hash": "bc4aeac1e11041ca661685e9ed1e3ead",
|
| 64 |
+
"model_name": "qwen_image_mae",
|
| 65 |
+
"model_class": "diffsynth.models.qwen_image_mae.QwenImageMAE",
|
| 66 |
+
"state_dict_converter": "diffsynth.utils.state_dict_converters.qwen_image_mae.QwenImageMAEStateDictConverter",
|
| 67 |
+
},
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
MODEL_CONFIGS = qwen_image_series
|
diffsynth/configs/vram_management_module_maps.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
VRAM_MANAGEMENT_MODULE_MAPS = {
|
| 2 |
+
"diffsynth.models.qwen_image_dit.QwenImageDiT": {
|
| 3 |
+
"diffsynth.models.qwen_image_dit.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 4 |
+
"torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
|
| 5 |
+
},
|
| 6 |
+
"diffsynth.models.qwen_image_text_encoder.QwenImageTextEncoder": {
|
| 7 |
+
"torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
|
| 8 |
+
"torch.nn.Embedding": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 9 |
+
"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2_5_VLRotaryEmbedding": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 10 |
+
"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 11 |
+
"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2_5_VisionPatchEmbed": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 12 |
+
"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl.Qwen2_5_VisionRotaryEmbedding": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 13 |
+
},
|
| 14 |
+
"diffsynth.models.qwen_image_vae.QwenImageVAE": {
|
| 15 |
+
"torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
|
| 16 |
+
"torch.nn.Conv3d": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 17 |
+
"torch.nn.Conv2d": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 18 |
+
"diffsynth.models.qwen_image_vae.QwenImageRMS_norm": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 19 |
+
},
|
| 20 |
+
"diffsynth.models.qwen_image_controlnet.BlockWiseControlBlock": {
|
| 21 |
+
"diffsynth.models.qwen_image_dit.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
|
| 22 |
+
"torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
|
| 23 |
+
},
|
| 24 |
+
}
|
diffsynth/core/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .attention import *
|
| 2 |
+
from .gradient import *
|
| 3 |
+
from .loader import *
|
| 4 |
+
from .vram import *
|
diffsynth/core/attention/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .attention import attention_forward
|
diffsynth/core/attention/attention.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, os
|
| 2 |
+
from einops import rearrange
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
try:
|
| 6 |
+
import flash_attn_interface
|
| 7 |
+
FLASH_ATTN_3_AVAILABLE = True
|
| 8 |
+
except ModuleNotFoundError:
|
| 9 |
+
FLASH_ATTN_3_AVAILABLE = False
|
| 10 |
+
|
| 11 |
+
try:
|
| 12 |
+
import flash_attn
|
| 13 |
+
FLASH_ATTN_2_AVAILABLE = True
|
| 14 |
+
except ModuleNotFoundError:
|
| 15 |
+
FLASH_ATTN_2_AVAILABLE = False
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
from sageattention import sageattn
|
| 19 |
+
SAGE_ATTN_AVAILABLE = True
|
| 20 |
+
except ModuleNotFoundError:
|
| 21 |
+
SAGE_ATTN_AVAILABLE = False
|
| 22 |
+
|
| 23 |
+
try:
|
| 24 |
+
import xformers.ops as xops
|
| 25 |
+
XFORMERS_AVAILABLE = True
|
| 26 |
+
except ModuleNotFoundError:
|
| 27 |
+
XFORMERS_AVAILABLE = False
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def initialize_attention_priority():
|
| 31 |
+
if os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION') is not None:
|
| 32 |
+
return os.environ.get('DIFFSYNTH_ATTENTION_IMPLEMENTATION').lower()
|
| 33 |
+
elif FLASH_ATTN_3_AVAILABLE:
|
| 34 |
+
return "flash_attention_3"
|
| 35 |
+
elif FLASH_ATTN_2_AVAILABLE:
|
| 36 |
+
return "flash_attention_2"
|
| 37 |
+
elif SAGE_ATTN_AVAILABLE:
|
| 38 |
+
return "sage_attention"
|
| 39 |
+
elif XFORMERS_AVAILABLE:
|
| 40 |
+
return "xformers"
|
| 41 |
+
else:
|
| 42 |
+
return "torch"
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
ATTENTION_IMPLEMENTATION = initialize_attention_priority()
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def rearrange_qkv(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", required_in_pattern="b n s d", dims=None):
|
| 49 |
+
dims = {} if dims is None else dims
|
| 50 |
+
if q_pattern != required_in_pattern:
|
| 51 |
+
q = rearrange(q, f"{q_pattern} -> {required_in_pattern}", **dims)
|
| 52 |
+
if k_pattern != required_in_pattern:
|
| 53 |
+
k = rearrange(k, f"{k_pattern} -> {required_in_pattern}", **dims)
|
| 54 |
+
if v_pattern != required_in_pattern:
|
| 55 |
+
v = rearrange(v, f"{q_pattern} -> {required_in_pattern}", **dims)
|
| 56 |
+
return q, k, v
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def rearrange_out(out: torch.Tensor, out_pattern="b n s d", required_out_pattern="b n s d", dims=None):
|
| 60 |
+
dims = {} if dims is None else dims
|
| 61 |
+
if out_pattern != required_out_pattern:
|
| 62 |
+
out = rearrange(out, f"{required_out_pattern} -> {out_pattern}", **dims)
|
| 63 |
+
return out
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def torch_sdpa(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None):
|
| 67 |
+
required_in_pattern, required_out_pattern= "b n s d", "b n s d"
|
| 68 |
+
q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
|
| 69 |
+
out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask, scale=scale)
|
| 70 |
+
out = rearrange_out(out, out_pattern, required_out_pattern, dims)
|
| 71 |
+
return out
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def flash_attention_3(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
|
| 75 |
+
required_in_pattern, required_out_pattern= "b s n d", "b s n d"
|
| 76 |
+
q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
|
| 77 |
+
out = flash_attn_interface.flash_attn_func(q, k, v, softmax_scale=scale)
|
| 78 |
+
if isinstance(out, tuple):
|
| 79 |
+
out = out[0]
|
| 80 |
+
out = rearrange_out(out, out_pattern, required_out_pattern, dims)
|
| 81 |
+
return out
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def flash_attention_2(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
|
| 85 |
+
required_in_pattern, required_out_pattern= "b s n d", "b s n d"
|
| 86 |
+
q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
|
| 87 |
+
out = flash_attn.flash_attn_func(q, k, v, softmax_scale=scale)
|
| 88 |
+
out = rearrange_out(out, out_pattern, required_out_pattern, dims)
|
| 89 |
+
return out
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def sage_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
|
| 93 |
+
required_in_pattern, required_out_pattern= "b n s d", "b n s d"
|
| 94 |
+
q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
|
| 95 |
+
out = sageattn(q, k, v, sm_scale=scale)
|
| 96 |
+
out = rearrange_out(out, out_pattern, required_out_pattern, dims)
|
| 97 |
+
return out
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def xformers_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, scale=None):
|
| 101 |
+
required_in_pattern, required_out_pattern= "b s n d", "b s n d"
|
| 102 |
+
q, k, v = rearrange_qkv(q, k, v, q_pattern, k_pattern, v_pattern, required_in_pattern, dims)
|
| 103 |
+
out = xops.memory_efficient_attention(q, k, v, scale=scale)
|
| 104 |
+
out = rearrange_out(out, out_pattern, required_out_pattern, dims)
|
| 105 |
+
return out
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def attention_forward(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, q_pattern="b n s d", k_pattern="b n s d", v_pattern="b n s d", out_pattern="b n s d", dims=None, attn_mask=None, scale=None, compatibility_mode=False):
|
| 109 |
+
if compatibility_mode or (attn_mask is not None):
|
| 110 |
+
return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, attn_mask=attn_mask, scale=scale)
|
| 111 |
+
else:
|
| 112 |
+
if ATTENTION_IMPLEMENTATION == "flash_attention_3":
|
| 113 |
+
return flash_attention_3(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
|
| 114 |
+
elif ATTENTION_IMPLEMENTATION == "flash_attention_2":
|
| 115 |
+
return flash_attention_2(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
|
| 116 |
+
elif ATTENTION_IMPLEMENTATION == "sage_attention":
|
| 117 |
+
return sage_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
|
| 118 |
+
elif ATTENTION_IMPLEMENTATION == "xformers":
|
| 119 |
+
return xformers_attention(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
|
| 120 |
+
else:
|
| 121 |
+
return torch_sdpa(q, k, v, q_pattern, k_pattern, v_pattern, out_pattern, dims, scale=scale)
|
diffsynth/core/gradient/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .gradient_checkpoint import gradient_checkpoint_forward
|
diffsynth/core/gradient/gradient_checkpoint.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def create_custom_forward(module):
|
| 5 |
+
def custom_forward(*inputs, **kwargs):
|
| 6 |
+
return module(*inputs, **kwargs)
|
| 7 |
+
return custom_forward
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def gradient_checkpoint_forward(
|
| 11 |
+
model,
|
| 12 |
+
use_gradient_checkpointing,
|
| 13 |
+
use_gradient_checkpointing_offload,
|
| 14 |
+
*args,
|
| 15 |
+
**kwargs,
|
| 16 |
+
):
|
| 17 |
+
if use_gradient_checkpointing_offload:
|
| 18 |
+
with torch.autograd.graph.save_on_cpu():
|
| 19 |
+
model_output = torch.utils.checkpoint.checkpoint(
|
| 20 |
+
create_custom_forward(model),
|
| 21 |
+
*args,
|
| 22 |
+
**kwargs,
|
| 23 |
+
use_reentrant=False,
|
| 24 |
+
)
|
| 25 |
+
elif use_gradient_checkpointing:
|
| 26 |
+
model_output = torch.utils.checkpoint.checkpoint(
|
| 27 |
+
create_custom_forward(model),
|
| 28 |
+
*args,
|
| 29 |
+
**kwargs,
|
| 30 |
+
use_reentrant=False,
|
| 31 |
+
)
|
| 32 |
+
else:
|
| 33 |
+
model_output = model(*args, **kwargs)
|
| 34 |
+
return model_output
|
diffsynth/core/loader/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .file import load_state_dict, hash_state_dict_keys, hash_model_file
|
| 2 |
+
from .model import load_model, load_model_with_disk_offload
|
| 3 |
+
from .config import ModelConfig
|
diffsynth/core/loader/config.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, glob, os
|
| 2 |
+
from typing import Optional, Union
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
from typing import Optional
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@dataclass
|
| 8 |
+
class ModelConfig:
|
| 9 |
+
path: Union[str, list[str]] = None
|
| 10 |
+
model_id: str = None
|
| 11 |
+
origin_file_pattern: Union[str, list[str]] = None
|
| 12 |
+
download_source: str = None
|
| 13 |
+
local_model_path: str = None
|
| 14 |
+
skip_download: bool = None
|
| 15 |
+
offload_device: Optional[Union[str, torch.device]] = None
|
| 16 |
+
offload_dtype: Optional[torch.dtype] = None
|
| 17 |
+
onload_device: Optional[Union[str, torch.device]] = None
|
| 18 |
+
onload_dtype: Optional[torch.dtype] = None
|
| 19 |
+
preparing_device: Optional[Union[str, torch.device]] = None
|
| 20 |
+
preparing_dtype: Optional[torch.dtype] = None
|
| 21 |
+
computation_device: Optional[Union[str, torch.device]] = None
|
| 22 |
+
computation_dtype: Optional[torch.dtype] = None
|
| 23 |
+
clear_parameters: bool = False
|
| 24 |
+
|
| 25 |
+
def check_input(self):
|
| 26 |
+
if self.path is None and self.model_id is None:
|
| 27 |
+
raise ValueError(f"""No valid model files. Please use `ModelConfig(path="xxx")` or `ModelConfig(model_id="xxx/yyy", origin_file_pattern="zzz")`. `skip_download=True` only supports the first one.""")
|
| 28 |
+
|
| 29 |
+
def parse_original_file_pattern(self):
|
| 30 |
+
if self.origin_file_pattern is None or self.origin_file_pattern == "":
|
| 31 |
+
return "*"
|
| 32 |
+
elif self.origin_file_pattern.endswith("/"):
|
| 33 |
+
return self.origin_file_pattern + "*"
|
| 34 |
+
else:
|
| 35 |
+
return self.origin_file_pattern
|
| 36 |
+
|
| 37 |
+
def parse_download_source(self):
|
| 38 |
+
if self.download_source is None:
|
| 39 |
+
if os.environ.get('DIFFSYNTH_DOWNLOAD_SOURCE') is not None:
|
| 40 |
+
return os.environ.get('DIFFSYNTH_DOWNLOAD_SOURCE')
|
| 41 |
+
else:
|
| 42 |
+
return "modelscope"
|
| 43 |
+
else:
|
| 44 |
+
return self.download_source
|
| 45 |
+
|
| 46 |
+
def parse_skip_download(self):
|
| 47 |
+
if self.skip_download is None:
|
| 48 |
+
if os.environ.get('DIFFSYNTH_SKIP_DOWNLOAD') is not None:
|
| 49 |
+
if os.environ.get('DIFFSYNTH_SKIP_DOWNLOAD').lower() == "true":
|
| 50 |
+
return True
|
| 51 |
+
elif os.environ.get('DIFFSYNTH_SKIP_DOWNLOAD').lower() == "false":
|
| 52 |
+
return False
|
| 53 |
+
else:
|
| 54 |
+
return False
|
| 55 |
+
else:
|
| 56 |
+
return self.skip_download
|
| 57 |
+
|
| 58 |
+
def download(self):
|
| 59 |
+
origin_file_pattern = self.parse_original_file_pattern()
|
| 60 |
+
downloaded_files = glob.glob(origin_file_pattern, root_dir=os.path.join(self.local_model_path, self.model_id))
|
| 61 |
+
download_source = self.parse_download_source()
|
| 62 |
+
if download_source.lower() == "modelscope":
|
| 63 |
+
from modelscope import snapshot_download
|
| 64 |
+
|
| 65 |
+
snapshot_download(
|
| 66 |
+
self.model_id,
|
| 67 |
+
local_dir=os.path.join(self.local_model_path, self.model_id),
|
| 68 |
+
allow_file_pattern=origin_file_pattern,
|
| 69 |
+
ignore_file_pattern=downloaded_files,
|
| 70 |
+
local_files_only=False
|
| 71 |
+
)
|
| 72 |
+
elif download_source.lower() == "huggingface":
|
| 73 |
+
from huggingface_hub import snapshot_download as hf_snapshot_download
|
| 74 |
+
|
| 75 |
+
hf_snapshot_download(
|
| 76 |
+
self.model_id,
|
| 77 |
+
local_dir=os.path.join(self.local_model_path, self.model_id),
|
| 78 |
+
allow_patterns=origin_file_pattern,
|
| 79 |
+
ignore_patterns=downloaded_files,
|
| 80 |
+
local_files_only=False
|
| 81 |
+
)
|
| 82 |
+
else:
|
| 83 |
+
raise ValueError("`download_source` should be `modelscope` or `huggingface`.")
|
| 84 |
+
|
| 85 |
+
def require_downloading(self):
|
| 86 |
+
if self.path is not None:
|
| 87 |
+
return False
|
| 88 |
+
skip_download = self.parse_skip_download()
|
| 89 |
+
return not skip_download
|
| 90 |
+
|
| 91 |
+
def reset_local_model_path(self):
|
| 92 |
+
if os.environ.get('DIFFSYNTH_MODEL_BASE_PATH') is not None:
|
| 93 |
+
self.local_model_path = os.environ.get('DIFFSYNTH_MODEL_BASE_PATH')
|
| 94 |
+
elif self.local_model_path is None:
|
| 95 |
+
self.local_model_path = "./models"
|
| 96 |
+
|
| 97 |
+
def download_if_necessary(self):
|
| 98 |
+
self.check_input()
|
| 99 |
+
self.reset_local_model_path()
|
| 100 |
+
if self.require_downloading():
|
| 101 |
+
self.download()
|
| 102 |
+
if self.origin_file_pattern is None or self.origin_file_pattern == "":
|
| 103 |
+
self.path = os.path.join(self.local_model_path, self.model_id)
|
| 104 |
+
else:
|
| 105 |
+
self.path = glob.glob(os.path.join(self.local_model_path, self.model_id, self.origin_file_pattern))
|
| 106 |
+
if isinstance(self.path, list) and len(self.path) == 1:
|
| 107 |
+
self.path = self.path[0]
|
| 108 |
+
|
| 109 |
+
def vram_config(self):
|
| 110 |
+
return {
|
| 111 |
+
"offload_device": self.offload_device,
|
| 112 |
+
"offload_dtype": self.offload_dtype,
|
| 113 |
+
"onload_device": self.onload_device,
|
| 114 |
+
"onload_dtype": self.onload_dtype,
|
| 115 |
+
"preparing_device": self.preparing_device,
|
| 116 |
+
"preparing_dtype": self.preparing_dtype,
|
| 117 |
+
"computation_device": self.computation_device,
|
| 118 |
+
"computation_dtype": self.computation_dtype,
|
| 119 |
+
}
|
diffsynth/core/loader/file.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from safetensors import safe_open
|
| 2 |
+
import torch, hashlib
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def load_state_dict(file_path, torch_dtype=None, device="cpu"):
|
| 6 |
+
if isinstance(file_path, list):
|
| 7 |
+
state_dict = {}
|
| 8 |
+
for file_path_ in file_path:
|
| 9 |
+
state_dict.update(load_state_dict(file_path_, torch_dtype, device))
|
| 10 |
+
return state_dict
|
| 11 |
+
if file_path.endswith(".safetensors"):
|
| 12 |
+
return load_state_dict_from_safetensors(file_path, torch_dtype=torch_dtype, device=device)
|
| 13 |
+
else:
|
| 14 |
+
return load_state_dict_from_bin(file_path, torch_dtype=torch_dtype, device=device)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def load_state_dict_from_safetensors(file_path, torch_dtype=None, device="cpu"):
|
| 18 |
+
state_dict = {}
|
| 19 |
+
with safe_open(file_path, framework="pt", device=str(device)) as f:
|
| 20 |
+
for k in f.keys():
|
| 21 |
+
state_dict[k] = f.get_tensor(k)
|
| 22 |
+
if torch_dtype is not None:
|
| 23 |
+
state_dict[k] = state_dict[k].to(torch_dtype)
|
| 24 |
+
return state_dict
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def load_state_dict_from_bin(file_path, torch_dtype=None, device="cpu"):
|
| 28 |
+
state_dict = torch.load(file_path, map_location=device, weights_only=True)
|
| 29 |
+
if len(state_dict) == 1:
|
| 30 |
+
if "state_dict" in state_dict:
|
| 31 |
+
state_dict = state_dict["state_dict"]
|
| 32 |
+
elif "module" in state_dict:
|
| 33 |
+
state_dict = state_dict["module"]
|
| 34 |
+
elif "model_state" in state_dict:
|
| 35 |
+
state_dict = state_dict["model_state"]
|
| 36 |
+
if torch_dtype is not None:
|
| 37 |
+
for i in state_dict:
|
| 38 |
+
if isinstance(state_dict[i], torch.Tensor):
|
| 39 |
+
state_dict[i] = state_dict[i].to(torch_dtype)
|
| 40 |
+
return state_dict
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def convert_state_dict_keys_to_single_str(state_dict, with_shape=True):
|
| 44 |
+
keys = []
|
| 45 |
+
for key, value in state_dict.items():
|
| 46 |
+
if isinstance(key, str):
|
| 47 |
+
if isinstance(value, torch.Tensor):
|
| 48 |
+
if with_shape:
|
| 49 |
+
shape = "_".join(map(str, list(value.shape)))
|
| 50 |
+
keys.append(key + ":" + shape)
|
| 51 |
+
keys.append(key)
|
| 52 |
+
elif isinstance(value, dict):
|
| 53 |
+
keys.append(key + "|" + convert_state_dict_keys_to_single_str(value, with_shape=with_shape))
|
| 54 |
+
keys.sort()
|
| 55 |
+
keys_str = ",".join(keys)
|
| 56 |
+
return keys_str
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def hash_state_dict_keys(state_dict, with_shape=True):
|
| 60 |
+
keys_str = convert_state_dict_keys_to_single_str(state_dict, with_shape=with_shape)
|
| 61 |
+
keys_str = keys_str.encode(encoding="UTF-8")
|
| 62 |
+
return hashlib.md5(keys_str).hexdigest()
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def load_keys_dict(file_path):
|
| 66 |
+
if isinstance(file_path, list):
|
| 67 |
+
state_dict = {}
|
| 68 |
+
for file_path_ in file_path:
|
| 69 |
+
state_dict.update(load_keys_dict(file_path_))
|
| 70 |
+
return state_dict
|
| 71 |
+
if file_path.endswith(".safetensors"):
|
| 72 |
+
return load_keys_dict_from_safetensors(file_path)
|
| 73 |
+
else:
|
| 74 |
+
return load_keys_dict_from_bin(file_path)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def load_keys_dict_from_safetensors(file_path):
|
| 78 |
+
keys_dict = {}
|
| 79 |
+
with safe_open(file_path, framework="pt", device="cpu") as f:
|
| 80 |
+
for k in f.keys():
|
| 81 |
+
keys_dict[k] = f.get_slice(k).get_shape()
|
| 82 |
+
return keys_dict
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def convert_state_dict_to_keys_dict(state_dict):
|
| 86 |
+
keys_dict = {}
|
| 87 |
+
for k, v in state_dict.items():
|
| 88 |
+
if isinstance(v, torch.Tensor):
|
| 89 |
+
keys_dict[k] = list(v.shape)
|
| 90 |
+
else:
|
| 91 |
+
keys_dict[k] = convert_state_dict_to_keys_dict(v)
|
| 92 |
+
return keys_dict
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def load_keys_dict_from_bin(file_path):
|
| 96 |
+
state_dict = load_state_dict_from_bin(file_path)
|
| 97 |
+
keys_dict = convert_state_dict_to_keys_dict(state_dict)
|
| 98 |
+
return keys_dict
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def convert_keys_dict_to_single_str(state_dict, with_shape=True):
|
| 102 |
+
keys = []
|
| 103 |
+
for key, value in state_dict.items():
|
| 104 |
+
if isinstance(key, str):
|
| 105 |
+
if isinstance(value, dict):
|
| 106 |
+
keys.append(key + "|" + convert_keys_dict_to_single_str(value, with_shape=with_shape))
|
| 107 |
+
else:
|
| 108 |
+
if with_shape:
|
| 109 |
+
shape = "_".join(map(str, list(value)))
|
| 110 |
+
keys.append(key + ":" + shape)
|
| 111 |
+
keys.append(key)
|
| 112 |
+
keys.sort()
|
| 113 |
+
keys_str = ",".join(keys)
|
| 114 |
+
return keys_str
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def hash_model_file(path, with_shape=True):
|
| 118 |
+
keys_dict = load_keys_dict(path)
|
| 119 |
+
keys_str = convert_keys_dict_to_single_str(keys_dict, with_shape=with_shape)
|
| 120 |
+
keys_str = keys_str.encode(encoding="UTF-8")
|
| 121 |
+
return hashlib.md5(keys_str).hexdigest()
|
diffsynth/core/loader/model.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from ..vram.initialization import skip_model_initialization
|
| 2 |
+
from ..vram.disk_map import DiskMap
|
| 3 |
+
from ..vram.layers import enable_vram_management
|
| 4 |
+
from .file import load_state_dict
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def load_model(model_class, path, config=None, torch_dtype=torch.bfloat16, device="cpu", state_dict_converter=None, use_disk_map=False, module_map=None, vram_config=None, vram_limit=None):
|
| 9 |
+
config = {} if config is None else config
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
with skip_model_initialization():
|
| 14 |
+
model = model_class(**config)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
if module_map is not None:
|
| 18 |
+
devices = [vram_config["offload_device"], vram_config["onload_device"], vram_config["preparing_device"], vram_config["computation_device"]]
|
| 19 |
+
device = [d for d in devices if d != "disk"][0]
|
| 20 |
+
dtypes = [vram_config["offload_dtype"], vram_config["onload_dtype"], vram_config["preparing_dtype"], vram_config["computation_dtype"]]
|
| 21 |
+
dtype = [d for d in dtypes if d != "disk"][0]
|
| 22 |
+
if vram_config["offload_device"] != "disk":
|
| 23 |
+
state_dict = DiskMap(path, device, torch_dtype=dtype)
|
| 24 |
+
if state_dict_converter is not None:
|
| 25 |
+
state_dict = state_dict_converter(state_dict)
|
| 26 |
+
else:
|
| 27 |
+
state_dict = {i: state_dict[i] for i in state_dict}
|
| 28 |
+
model.load_state_dict(state_dict, assign=True)
|
| 29 |
+
model = enable_vram_management(model, module_map, vram_config=vram_config, disk_map=None, vram_limit=vram_limit)
|
| 30 |
+
else:
|
| 31 |
+
disk_map = DiskMap(path, device, state_dict_converter=state_dict_converter)
|
| 32 |
+
model = enable_vram_management(model, module_map, vram_config=vram_config, disk_map=disk_map, vram_limit=vram_limit)
|
| 33 |
+
else:
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
if use_disk_map:
|
| 39 |
+
state_dict = DiskMap(path, device, torch_dtype=torch_dtype)
|
| 40 |
+
else:
|
| 41 |
+
state_dict = load_state_dict(path, torch_dtype, device)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
if state_dict_converter is not None:
|
| 46 |
+
state_dict = state_dict_converter(state_dict)
|
| 47 |
+
else:
|
| 48 |
+
state_dict = {i: state_dict[i] for i in state_dict}
|
| 49 |
+
model.load_state_dict(state_dict, assign=True)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
model = model.to(dtype=torch_dtype, device=device)
|
| 54 |
+
if hasattr(model, "eval"):
|
| 55 |
+
model = model.eval()
|
| 56 |
+
return model
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def load_model_with_disk_offload(model_class, path, config=None, torch_dtype=torch.bfloat16, device="cpu", state_dict_converter=None, module_map=None):
|
| 60 |
+
if isinstance(path, str):
|
| 61 |
+
path = [path]
|
| 62 |
+
config = {} if config is None else config
|
| 63 |
+
with skip_model_initialization():
|
| 64 |
+
model = model_class(**config)
|
| 65 |
+
if hasattr(model, "eval"):
|
| 66 |
+
model = model.eval()
|
| 67 |
+
disk_map = DiskMap(path, device, state_dict_converter=state_dict_converter)
|
| 68 |
+
vram_config = {
|
| 69 |
+
"offload_dtype": "disk",
|
| 70 |
+
"offload_device": "disk",
|
| 71 |
+
"onload_dtype": "disk",
|
| 72 |
+
"onload_device": "disk",
|
| 73 |
+
"preparing_dtype": torch.float8_e4m3fn,
|
| 74 |
+
"preparing_device": device,
|
| 75 |
+
"computation_dtype": torch_dtype,
|
| 76 |
+
"computation_device": device,
|
| 77 |
+
}
|
| 78 |
+
enable_vram_management(model, module_map, vram_config=vram_config, disk_map=disk_map, vram_limit=80)
|
| 79 |
+
return model
|
diffsynth/core/vram/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .initialization import skip_model_initialization
|
| 2 |
+
from .layers import *
|
diffsynth/core/vram/disk_map.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from safetensors import safe_open
|
| 2 |
+
import torch, os
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class SafetensorsCompatibleTensor:
|
| 6 |
+
def __init__(self, tensor):
|
| 7 |
+
self.tensor = tensor
|
| 8 |
+
|
| 9 |
+
def get_shape(self):
|
| 10 |
+
return list(self.tensor.shape)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class SafetensorsCompatibleBinaryLoader:
|
| 14 |
+
def __init__(self, path, device):
|
| 15 |
+
print("Detected non-safetensors files, which may cause slower loading. It's recommended to convert it to a safetensors file.")
|
| 16 |
+
self.state_dict = torch.load(path, weights_only=True, map_location=device)
|
| 17 |
+
|
| 18 |
+
def keys(self):
|
| 19 |
+
return self.state_dict.keys()
|
| 20 |
+
|
| 21 |
+
def get_tensor(self, name):
|
| 22 |
+
return self.state_dict[name]
|
| 23 |
+
|
| 24 |
+
def get_slice(self, name):
|
| 25 |
+
return SafetensorsCompatibleTensor(self.state_dict[name])
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class DiskMap:
|
| 29 |
+
|
| 30 |
+
def __init__(self, path, device, torch_dtype=None, state_dict_converter=None, buffer_size=10**9):
|
| 31 |
+
self.path = path if isinstance(path, list) else [path]
|
| 32 |
+
self.device = device
|
| 33 |
+
self.torch_dtype = torch_dtype
|
| 34 |
+
if os.environ.get('DIFFSYNTH_DISK_MAP_BUFFER_SIZE') is not None:
|
| 35 |
+
self.buffer_size = int(os.environ.get('DIFFSYNTH_DISK_MAP_BUFFER_SIZE'))
|
| 36 |
+
else:
|
| 37 |
+
self.buffer_size = buffer_size
|
| 38 |
+
self.files = []
|
| 39 |
+
self.flush_files()
|
| 40 |
+
self.name_map = {}
|
| 41 |
+
for file_id, file in enumerate(self.files):
|
| 42 |
+
for name in file.keys():
|
| 43 |
+
self.name_map[name] = file_id
|
| 44 |
+
self.rename_dict = self.fetch_rename_dict(state_dict_converter)
|
| 45 |
+
|
| 46 |
+
def flush_files(self):
|
| 47 |
+
if len(self.files) == 0:
|
| 48 |
+
for path in self.path:
|
| 49 |
+
if path.endswith(".safetensors"):
|
| 50 |
+
self.files.append(safe_open(path, framework="pt", device=str(self.device)))
|
| 51 |
+
else:
|
| 52 |
+
self.files.append(SafetensorsCompatibleBinaryLoader(path, device=self.device))
|
| 53 |
+
else:
|
| 54 |
+
for i, path in enumerate(self.path):
|
| 55 |
+
if path.endswith(".safetensors"):
|
| 56 |
+
self.files[i] = safe_open(path, framework="pt", device=str(self.device))
|
| 57 |
+
self.num_params = 0
|
| 58 |
+
|
| 59 |
+
def __getitem__(self, name):
|
| 60 |
+
if self.rename_dict is not None: name = self.rename_dict[name]
|
| 61 |
+
file_id = self.name_map[name]
|
| 62 |
+
param = self.files[file_id].get_tensor(name)
|
| 63 |
+
if self.torch_dtype is not None and isinstance(param, torch.Tensor):
|
| 64 |
+
param = param.to(self.torch_dtype)
|
| 65 |
+
if isinstance(param, torch.Tensor) and param.device == "cpu":
|
| 66 |
+
param = param.clone()
|
| 67 |
+
if isinstance(param, torch.Tensor):
|
| 68 |
+
self.num_params += param.numel()
|
| 69 |
+
if self.num_params > self.buffer_size:
|
| 70 |
+
self.flush_files()
|
| 71 |
+
return param
|
| 72 |
+
|
| 73 |
+
def fetch_rename_dict(self, state_dict_converter):
|
| 74 |
+
if state_dict_converter is None:
|
| 75 |
+
return None
|
| 76 |
+
state_dict = {}
|
| 77 |
+
for file in self.files:
|
| 78 |
+
for name in file.keys():
|
| 79 |
+
state_dict[name] = name
|
| 80 |
+
state_dict = state_dict_converter(state_dict)
|
| 81 |
+
return state_dict
|
| 82 |
+
|
| 83 |
+
def __iter__(self):
|
| 84 |
+
if self.rename_dict is not None:
|
| 85 |
+
return self.rename_dict.__iter__()
|
| 86 |
+
else:
|
| 87 |
+
return self.name_map.__iter__()
|
| 88 |
+
|
| 89 |
+
def __contains__(self, x):
|
| 90 |
+
if self.rename_dict is not None:
|
| 91 |
+
return x in self.rename_dict
|
| 92 |
+
else:
|
| 93 |
+
return x in self.name_map
|
diffsynth/core/vram/initialization.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from contextlib import contextmanager
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@contextmanager
|
| 6 |
+
def skip_model_initialization(device=torch.device("meta")):
|
| 7 |
+
|
| 8 |
+
def register_empty_parameter(module, name, param):
|
| 9 |
+
old_register_parameter(module, name, param)
|
| 10 |
+
if param is not None:
|
| 11 |
+
param_cls = type(module._parameters[name])
|
| 12 |
+
kwargs = module._parameters[name].__dict__
|
| 13 |
+
kwargs["requires_grad"] = param.requires_grad
|
| 14 |
+
module._parameters[name] = param_cls(module._parameters[name].to(device), **kwargs)
|
| 15 |
+
|
| 16 |
+
old_register_parameter = torch.nn.Module.register_parameter
|
| 17 |
+
torch.nn.Module.register_parameter = register_empty_parameter
|
| 18 |
+
try:
|
| 19 |
+
yield
|
| 20 |
+
finally:
|
| 21 |
+
torch.nn.Module.register_parameter = old_register_parameter
|
diffsynth/core/vram/layers.py
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, copy
|
| 2 |
+
from typing import Union
|
| 3 |
+
from .initialization import skip_model_initialization
|
| 4 |
+
from .disk_map import DiskMap
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class AutoTorchModule(torch.nn.Module):
|
| 8 |
+
|
| 9 |
+
def __init__(
|
| 10 |
+
self,
|
| 11 |
+
offload_dtype: torch.dtype = None,
|
| 12 |
+
offload_device: Union[str, torch.device] = None,
|
| 13 |
+
onload_dtype: torch.dtype = None,
|
| 14 |
+
onload_device: Union[str, torch.device] = None,
|
| 15 |
+
preparing_dtype: torch.dtype = None,
|
| 16 |
+
preparing_device: Union[str, torch.device] = None,
|
| 17 |
+
computation_dtype: torch.dtype = None,
|
| 18 |
+
computation_device: Union[str, torch.device] = None,
|
| 19 |
+
vram_limit: float = None,
|
| 20 |
+
):
|
| 21 |
+
super().__init__()
|
| 22 |
+
self.set_dtype_and_device(
|
| 23 |
+
offload_dtype,
|
| 24 |
+
offload_device,
|
| 25 |
+
onload_dtype,
|
| 26 |
+
onload_device,
|
| 27 |
+
preparing_dtype,
|
| 28 |
+
preparing_device,
|
| 29 |
+
computation_dtype,
|
| 30 |
+
computation_device,
|
| 31 |
+
vram_limit,
|
| 32 |
+
)
|
| 33 |
+
self.state = 0
|
| 34 |
+
self.name = ""
|
| 35 |
+
|
| 36 |
+
def set_dtype_and_device(
|
| 37 |
+
self,
|
| 38 |
+
offload_dtype: torch.dtype = None,
|
| 39 |
+
offload_device: Union[str, torch.device] = None,
|
| 40 |
+
onload_dtype: torch.dtype = None,
|
| 41 |
+
onload_device: Union[str, torch.device] = None,
|
| 42 |
+
preparing_dtype: torch.dtype = None,
|
| 43 |
+
preparing_device: Union[str, torch.device] = None,
|
| 44 |
+
computation_dtype: torch.dtype = None,
|
| 45 |
+
computation_device: Union[str, torch.device] = None,
|
| 46 |
+
vram_limit: float = None,
|
| 47 |
+
):
|
| 48 |
+
self.offload_dtype = offload_dtype or computation_dtype
|
| 49 |
+
self.offload_device = offload_device or computation_dtype
|
| 50 |
+
self.onload_dtype = onload_dtype or computation_dtype
|
| 51 |
+
self.onload_device = onload_device or computation_dtype
|
| 52 |
+
self.preparing_dtype = preparing_dtype or computation_dtype
|
| 53 |
+
self.preparing_device = preparing_device or computation_dtype
|
| 54 |
+
self.computation_dtype = computation_dtype
|
| 55 |
+
self.computation_device = computation_device
|
| 56 |
+
self.vram_limit = vram_limit
|
| 57 |
+
|
| 58 |
+
def cast_to(self, weight, dtype, device):
|
| 59 |
+
r = torch.empty_like(weight, dtype=dtype, device=device)
|
| 60 |
+
r.copy_(weight)
|
| 61 |
+
return r
|
| 62 |
+
|
| 63 |
+
def check_free_vram(self):
|
| 64 |
+
gpu_mem_state = torch.cuda.mem_get_info(self.computation_device)
|
| 65 |
+
used_memory = (gpu_mem_state[1] - gpu_mem_state[0]) / (1024**3)
|
| 66 |
+
return used_memory < self.vram_limit
|
| 67 |
+
|
| 68 |
+
def offload(self):
|
| 69 |
+
if self.state != 0:
|
| 70 |
+
self.to(dtype=self.offload_dtype, device=self.offload_device)
|
| 71 |
+
self.state = 0
|
| 72 |
+
|
| 73 |
+
def onload(self):
|
| 74 |
+
if self.state != 1:
|
| 75 |
+
self.to(dtype=self.onload_dtype, device=self.onload_device)
|
| 76 |
+
self.state = 1
|
| 77 |
+
|
| 78 |
+
def param_name(self, name):
|
| 79 |
+
if self.name == "":
|
| 80 |
+
return name
|
| 81 |
+
else:
|
| 82 |
+
return self.name + "." + name
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class AutoWrappedModule(AutoTorchModule):
|
| 86 |
+
|
| 87 |
+
def __init__(
|
| 88 |
+
self,
|
| 89 |
+
module: torch.nn.Module,
|
| 90 |
+
offload_dtype: torch.dtype = None,
|
| 91 |
+
offload_device: Union[str, torch.device] = None,
|
| 92 |
+
onload_dtype: torch.dtype = None,
|
| 93 |
+
onload_device: Union[str, torch.device] = None,
|
| 94 |
+
preparing_dtype: torch.dtype = None,
|
| 95 |
+
preparing_device: Union[str, torch.device] = None,
|
| 96 |
+
computation_dtype: torch.dtype = None,
|
| 97 |
+
computation_device: Union[str, torch.device] = None,
|
| 98 |
+
vram_limit: float = None,
|
| 99 |
+
name: str = "",
|
| 100 |
+
disk_map: DiskMap = None,
|
| 101 |
+
**kwargs
|
| 102 |
+
):
|
| 103 |
+
super().__init__(
|
| 104 |
+
offload_dtype,
|
| 105 |
+
offload_device,
|
| 106 |
+
onload_dtype,
|
| 107 |
+
onload_device,
|
| 108 |
+
preparing_dtype,
|
| 109 |
+
preparing_device,
|
| 110 |
+
computation_dtype,
|
| 111 |
+
computation_device,
|
| 112 |
+
vram_limit,
|
| 113 |
+
)
|
| 114 |
+
self.module = module
|
| 115 |
+
if offload_dtype == "disk":
|
| 116 |
+
self.name = name
|
| 117 |
+
self.disk_map = disk_map
|
| 118 |
+
self.required_params = [name for name, _ in self.module.named_parameters()]
|
| 119 |
+
self.disk_offload = True
|
| 120 |
+
else:
|
| 121 |
+
self.disk_offload = False
|
| 122 |
+
|
| 123 |
+
def load_from_disk(self, torch_dtype, device, copy_module=False):
|
| 124 |
+
if copy_module:
|
| 125 |
+
module = copy.deepcopy(self.module)
|
| 126 |
+
else:
|
| 127 |
+
module = self.module
|
| 128 |
+
state_dict = {}
|
| 129 |
+
for name in self.required_params:
|
| 130 |
+
param = self.disk_map[self.param_name(name)]
|
| 131 |
+
param = param.to(dtype=torch_dtype, device=device)
|
| 132 |
+
state_dict[name] = param
|
| 133 |
+
module.load_state_dict(state_dict, assign=True)
|
| 134 |
+
module.to(dtype=torch_dtype, device=device)
|
| 135 |
+
return module
|
| 136 |
+
|
| 137 |
+
def offload_to_disk(self, model: torch.nn.Module):
|
| 138 |
+
for buf in model.buffers():
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
for children in model.children():
|
| 142 |
+
self.offload_to_disk(children)
|
| 143 |
+
break
|
| 144 |
+
else:
|
| 145 |
+
model.to("meta")
|
| 146 |
+
|
| 147 |
+
def offload(self):
|
| 148 |
+
|
| 149 |
+
if self.state != 0:
|
| 150 |
+
if self.disk_offload:
|
| 151 |
+
self.offload_to_disk(self.module)
|
| 152 |
+
else:
|
| 153 |
+
self.to(dtype=self.offload_dtype, device=self.offload_device)
|
| 154 |
+
self.state = 0
|
| 155 |
+
|
| 156 |
+
def onload(self):
|
| 157 |
+
|
| 158 |
+
if self.state < 1:
|
| 159 |
+
if self.disk_offload and self.onload_device != "disk" and self.offload_device == "disk":
|
| 160 |
+
self.load_from_disk(self.onload_dtype, self.onload_device)
|
| 161 |
+
elif self.onload_device != "disk":
|
| 162 |
+
self.to(dtype=self.onload_dtype, device=self.onload_device)
|
| 163 |
+
self.state = 1
|
| 164 |
+
|
| 165 |
+
def preparing(self):
|
| 166 |
+
|
| 167 |
+
if self.state != 2:
|
| 168 |
+
if self.disk_offload and self.preparing_device != "disk" and self.onload_device == "disk":
|
| 169 |
+
self.load_from_disk(self.preparing_dtype, self.preparing_device)
|
| 170 |
+
elif self.preparing_device != "disk":
|
| 171 |
+
self.to(dtype=self.preparing_dtype, device=self.preparing_device)
|
| 172 |
+
self.state = 2
|
| 173 |
+
|
| 174 |
+
def cast_to(self, module, dtype, device):
|
| 175 |
+
return copy.deepcopy(module).to(dtype=dtype, device=device)
|
| 176 |
+
|
| 177 |
+
def computation(self):
|
| 178 |
+
|
| 179 |
+
if self.state == 2:
|
| 180 |
+
torch_dtype, device = self.preparing_dtype, self.preparing_device
|
| 181 |
+
else:
|
| 182 |
+
torch_dtype, device = self.onload_dtype, self.onload_device
|
| 183 |
+
if torch_dtype == self.computation_dtype and device == self.computation_device:
|
| 184 |
+
module = self.module
|
| 185 |
+
elif self.disk_offload and device == "disk":
|
| 186 |
+
module = self.load_from_disk(self.computation_dtype, self.computation_device, copy_module=True)
|
| 187 |
+
else:
|
| 188 |
+
module = self.cast_to(self.module, dtype=self.computation_dtype, device=self.computation_device)
|
| 189 |
+
return module
|
| 190 |
+
|
| 191 |
+
def forward(self, *args, **kwargs):
|
| 192 |
+
if self.state == 1 and (self.vram_limit is None or self.check_free_vram()):
|
| 193 |
+
self.preparing()
|
| 194 |
+
module = self.computation()
|
| 195 |
+
return module(*args, **kwargs)
|
| 196 |
+
|
| 197 |
+
def __getattr__(self, name):
|
| 198 |
+
if name in self.__dict__ or name == "module":
|
| 199 |
+
return super().__getattr__(name)
|
| 200 |
+
else:
|
| 201 |
+
return getattr(self.module, name)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
class AutoWrappedNonRecurseModule(AutoWrappedModule):
|
| 205 |
+
|
| 206 |
+
def __init__(
|
| 207 |
+
self,
|
| 208 |
+
module: torch.nn.Module,
|
| 209 |
+
offload_dtype: torch.dtype = None,
|
| 210 |
+
offload_device: Union[str, torch.device] = None,
|
| 211 |
+
onload_dtype: torch.dtype = None,
|
| 212 |
+
onload_device: Union[str, torch.device] = None,
|
| 213 |
+
preparing_dtype: torch.dtype = None,
|
| 214 |
+
preparing_device: Union[str, torch.device] = None,
|
| 215 |
+
computation_dtype: torch.dtype = None,
|
| 216 |
+
computation_device: Union[str, torch.device] = None,
|
| 217 |
+
vram_limit: float = None,
|
| 218 |
+
name: str = "",
|
| 219 |
+
disk_map: DiskMap = None,
|
| 220 |
+
**kwargs
|
| 221 |
+
):
|
| 222 |
+
super().__init__(
|
| 223 |
+
module,
|
| 224 |
+
offload_dtype,
|
| 225 |
+
offload_device,
|
| 226 |
+
onload_dtype,
|
| 227 |
+
onload_device,
|
| 228 |
+
preparing_dtype,
|
| 229 |
+
preparing_device,
|
| 230 |
+
computation_dtype,
|
| 231 |
+
computation_device,
|
| 232 |
+
vram_limit,
|
| 233 |
+
name,
|
| 234 |
+
disk_map,
|
| 235 |
+
**kwargs
|
| 236 |
+
)
|
| 237 |
+
if self.disk_offload:
|
| 238 |
+
self.required_params = [name for name, _ in self.module.named_parameters(recurse=False)]
|
| 239 |
+
|
| 240 |
+
def load_from_disk(self, torch_dtype, device, copy_module=False):
|
| 241 |
+
if copy_module:
|
| 242 |
+
module = copy.deepcopy(self.module)
|
| 243 |
+
else:
|
| 244 |
+
module = self.module
|
| 245 |
+
state_dict = {}
|
| 246 |
+
for name in self.required_params:
|
| 247 |
+
param = self.disk_map[self.param_name(name)]
|
| 248 |
+
param = param.to(dtype=torch_dtype, device=device)
|
| 249 |
+
state_dict[name] = param
|
| 250 |
+
module.load_state_dict(state_dict, assign=True, strict=False)
|
| 251 |
+
return module
|
| 252 |
+
|
| 253 |
+
def offload_to_disk(self, model: torch.nn.Module):
|
| 254 |
+
for name in self.required_params:
|
| 255 |
+
getattr(self, name).to("meta")
|
| 256 |
+
|
| 257 |
+
def cast_to(self, module, dtype, device):
|
| 258 |
+
|
| 259 |
+
return module
|
| 260 |
+
|
| 261 |
+
def __getattr__(self, name):
|
| 262 |
+
if name in self.__dict__ or name == "module":
|
| 263 |
+
return super().__getattr__(name)
|
| 264 |
+
else:
|
| 265 |
+
return getattr(self.module, name)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
class AutoWrappedLinear(torch.nn.Linear, AutoTorchModule):
|
| 269 |
+
def __init__(
|
| 270 |
+
self,
|
| 271 |
+
module: torch.nn.Linear,
|
| 272 |
+
offload_dtype: torch.dtype = None,
|
| 273 |
+
offload_device: Union[str, torch.device] = None,
|
| 274 |
+
onload_dtype: torch.dtype = None,
|
| 275 |
+
onload_device: Union[str, torch.device] = None,
|
| 276 |
+
preparing_dtype: torch.dtype = None,
|
| 277 |
+
preparing_device: Union[str, torch.device] = None,
|
| 278 |
+
computation_dtype: torch.dtype = None,
|
| 279 |
+
computation_device: Union[str, torch.device] = None,
|
| 280 |
+
vram_limit: float = None,
|
| 281 |
+
name: str = "",
|
| 282 |
+
disk_map: DiskMap = None,
|
| 283 |
+
**kwargs
|
| 284 |
+
):
|
| 285 |
+
with skip_model_initialization():
|
| 286 |
+
super().__init__(
|
| 287 |
+
in_features=module.in_features,
|
| 288 |
+
out_features=module.out_features,
|
| 289 |
+
bias=module.bias is not None,
|
| 290 |
+
)
|
| 291 |
+
self.set_dtype_and_device(
|
| 292 |
+
offload_dtype,
|
| 293 |
+
offload_device,
|
| 294 |
+
onload_dtype,
|
| 295 |
+
onload_device,
|
| 296 |
+
preparing_dtype,
|
| 297 |
+
preparing_device,
|
| 298 |
+
computation_dtype,
|
| 299 |
+
computation_device,
|
| 300 |
+
vram_limit,
|
| 301 |
+
)
|
| 302 |
+
self.weight = module.weight
|
| 303 |
+
self.bias = module.bias
|
| 304 |
+
self.state = 0
|
| 305 |
+
self.name = name
|
| 306 |
+
self.lora_A_weights = []
|
| 307 |
+
self.lora_B_weights = []
|
| 308 |
+
self.lora_merger = None
|
| 309 |
+
self.enable_fp8 = computation_dtype in [torch.float8_e4m3fn, torch.float8_e4m3fnuz]
|
| 310 |
+
|
| 311 |
+
if offload_dtype == "disk":
|
| 312 |
+
self.disk_map = disk_map
|
| 313 |
+
self.disk_offload = True
|
| 314 |
+
else:
|
| 315 |
+
self.disk_offload = False
|
| 316 |
+
|
| 317 |
+
def fp8_linear(
|
| 318 |
+
self,
|
| 319 |
+
input: torch.Tensor,
|
| 320 |
+
weight: torch.Tensor,
|
| 321 |
+
bias: torch.Tensor = None,
|
| 322 |
+
) -> torch.Tensor:
|
| 323 |
+
device = input.device
|
| 324 |
+
origin_dtype = input.dtype
|
| 325 |
+
origin_shape = input.shape
|
| 326 |
+
input = input.reshape(-1, origin_shape[-1])
|
| 327 |
+
|
| 328 |
+
x_max = torch.max(torch.abs(input), dim=-1, keepdim=True).values
|
| 329 |
+
fp8_max = 448.0
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
if self.computation_dtype == torch.float8_e4m3fnuz:
|
| 335 |
+
fp8_max = fp8_max / 2.0
|
| 336 |
+
scale_a = torch.clamp(x_max / fp8_max, min=1.0).float().to(device=device)
|
| 337 |
+
scale_b = torch.ones((weight.shape[0], 1)).to(device=device)
|
| 338 |
+
input = input / (scale_a + 1e-8)
|
| 339 |
+
input = input.to(self.computation_dtype)
|
| 340 |
+
weight = weight.to(self.computation_dtype)
|
| 341 |
+
bias = bias.to(torch.bfloat16)
|
| 342 |
+
|
| 343 |
+
result = torch._scaled_mm(
|
| 344 |
+
input,
|
| 345 |
+
weight.T,
|
| 346 |
+
scale_a=scale_a,
|
| 347 |
+
scale_b=scale_b.T,
|
| 348 |
+
bias=bias,
|
| 349 |
+
out_dtype=origin_dtype,
|
| 350 |
+
)
|
| 351 |
+
new_shape = origin_shape[:-1] + result.shape[-1:]
|
| 352 |
+
result = result.reshape(new_shape)
|
| 353 |
+
return result
|
| 354 |
+
|
| 355 |
+
def load_from_disk(self, torch_dtype, device, assign=True):
|
| 356 |
+
weight = self.disk_map[self.name + ".weight"].to(dtype=torch_dtype, device=device)
|
| 357 |
+
bias = None if self.bias is None else self.disk_map[self.name + ".bias"].to(dtype=torch_dtype, device=device)
|
| 358 |
+
if assign:
|
| 359 |
+
state_dict = {"weight": weight}
|
| 360 |
+
if bias is not None: state_dict["bias"] = bias
|
| 361 |
+
self.load_state_dict(state_dict, assign=True)
|
| 362 |
+
return weight, bias
|
| 363 |
+
|
| 364 |
+
def offload(self):
|
| 365 |
+
|
| 366 |
+
if self.state != 0:
|
| 367 |
+
if self.disk_offload:
|
| 368 |
+
self.to("meta")
|
| 369 |
+
else:
|
| 370 |
+
self.to(dtype=self.offload_dtype, device=self.offload_device)
|
| 371 |
+
self.state = 0
|
| 372 |
+
|
| 373 |
+
def onload(self):
|
| 374 |
+
|
| 375 |
+
if self.state < 1:
|
| 376 |
+
if self.disk_offload and self.onload_device != "disk" and self.offload_device == "disk":
|
| 377 |
+
self.load_from_disk(self.onload_dtype, self.onload_device)
|
| 378 |
+
elif self.onload_device != "disk":
|
| 379 |
+
self.to(dtype=self.onload_dtype, device=self.onload_device)
|
| 380 |
+
self.state = 1
|
| 381 |
+
|
| 382 |
+
def preparing(self):
|
| 383 |
+
|
| 384 |
+
if self.state != 2:
|
| 385 |
+
if self.disk_offload and self.preparing_device != "disk" and self.onload_device == "disk":
|
| 386 |
+
self.load_from_disk(self.preparing_dtype, self.preparing_device)
|
| 387 |
+
elif self.preparing_device != "disk":
|
| 388 |
+
self.to(dtype=self.preparing_dtype, device=self.preparing_device)
|
| 389 |
+
self.state = 2
|
| 390 |
+
|
| 391 |
+
def computation(self):
|
| 392 |
+
|
| 393 |
+
if self.state == 2:
|
| 394 |
+
torch_dtype, device = self.preparing_dtype, self.preparing_device
|
| 395 |
+
else:
|
| 396 |
+
torch_dtype, device = self.onload_dtype, self.onload_device
|
| 397 |
+
if torch_dtype == self.computation_dtype and device == self.computation_device:
|
| 398 |
+
weight, bias = self.weight, self.bias
|
| 399 |
+
elif self.disk_offload and device == "disk":
|
| 400 |
+
weight, bias = self.load_from_disk(self.computation_dtype, self.computation_device, assign=False)
|
| 401 |
+
else:
|
| 402 |
+
weight = self.cast_to(self.weight, self.computation_dtype, self.computation_device)
|
| 403 |
+
bias = None if self.bias is None else self.cast_to(self.bias, self.computation_dtype, self.computation_device)
|
| 404 |
+
return weight, bias
|
| 405 |
+
|
| 406 |
+
def linear_forward(self, x, weight, bias):
|
| 407 |
+
if self.enable_fp8:
|
| 408 |
+
out = self.fp8_linear(x, weight, bias)
|
| 409 |
+
else:
|
| 410 |
+
out = torch.nn.functional.linear(x, weight, bias)
|
| 411 |
+
return out
|
| 412 |
+
|
| 413 |
+
def lora_forward(self, x, out):
|
| 414 |
+
if self.lora_merger is None:
|
| 415 |
+
for lora_A, lora_B in zip(self.lora_A_weights, self.lora_B_weights):
|
| 416 |
+
out = out + x @ lora_A.T @ lora_B.T
|
| 417 |
+
else:
|
| 418 |
+
lora_output = []
|
| 419 |
+
for lora_A, lora_B in zip(self.lora_A_weights, self.lora_B_weights):
|
| 420 |
+
lora_output.append(x @ lora_A.T @ lora_B.T)
|
| 421 |
+
lora_output = torch.stack(lora_output)
|
| 422 |
+
out = self.lora_merger(out, lora_output)
|
| 423 |
+
return out
|
| 424 |
+
|
| 425 |
+
def forward(self, x, *args, **kwargs):
|
| 426 |
+
if self.state == 1 and (self.vram_limit is None or self.check_free_vram()):
|
| 427 |
+
self.preparing()
|
| 428 |
+
weight, bias = self.computation()
|
| 429 |
+
out = self.linear_forward(x, weight, bias)
|
| 430 |
+
if len(self.lora_A_weights) > 0:
|
| 431 |
+
out = self.lora_forward(x, out)
|
| 432 |
+
return out
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
def enable_vram_management_recursively(model: torch.nn.Module, module_map: dict, vram_config: dict, vram_limit=None, name_prefix="", disk_map=None, **kwargs):
|
| 436 |
+
if isinstance(model, AutoWrappedNonRecurseModule):
|
| 437 |
+
model = model.module
|
| 438 |
+
for name, module in model.named_children():
|
| 439 |
+
layer_name = name if name_prefix == "" else name_prefix + "." + name
|
| 440 |
+
for source_module, target_module in module_map.items():
|
| 441 |
+
if isinstance(module, source_module):
|
| 442 |
+
module_ = target_module(module, **vram_config, vram_limit=vram_limit, name=layer_name, disk_map=disk_map, **kwargs)
|
| 443 |
+
if isinstance(module_, AutoWrappedNonRecurseModule):
|
| 444 |
+
enable_vram_management_recursively(module_, module_map, vram_config, vram_limit=vram_limit, name_prefix=layer_name, disk_map=disk_map, **kwargs)
|
| 445 |
+
setattr(model, name, module_)
|
| 446 |
+
break
|
| 447 |
+
else:
|
| 448 |
+
enable_vram_management_recursively(module, module_map, vram_config, vram_limit=vram_limit, name_prefix=layer_name, disk_map=disk_map, **kwargs)
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
def fill_vram_config(model, vram_config):
|
| 452 |
+
vram_config_ = vram_config.copy()
|
| 453 |
+
vram_config_["onload_dtype"] = vram_config["computation_dtype"]
|
| 454 |
+
vram_config_["onload_device"] = vram_config["computation_device"]
|
| 455 |
+
vram_config_["preparing_dtype"] = vram_config["computation_dtype"]
|
| 456 |
+
vram_config_["preparing_device"] = vram_config["computation_device"]
|
| 457 |
+
for k in vram_config:
|
| 458 |
+
if vram_config[k] != vram_config_[k]:
|
| 459 |
+
print(f"No fine-grained VRAM configuration is provided for {model.__class__.__name__}. [`onload`, `preparing`, `computation`] will be the same state. `vram_config` is set to {vram_config_}")
|
| 460 |
+
break
|
| 461 |
+
return vram_config_
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
def enable_vram_management(model: torch.nn.Module, module_map: dict, vram_config: dict, vram_limit=None, disk_map=None, **kwargs):
|
| 465 |
+
for source_module, target_module in module_map.items():
|
| 466 |
+
|
| 467 |
+
if isinstance(model, source_module):
|
| 468 |
+
vram_config = fill_vram_config(model, vram_config)
|
| 469 |
+
model = target_module(model, **vram_config, vram_limit=vram_limit, disk_map=disk_map, **kwargs)
|
| 470 |
+
break
|
| 471 |
+
else:
|
| 472 |
+
enable_vram_management_recursively(model, module_map, vram_config, vram_limit=vram_limit, disk_map=disk_map, **kwargs)
|
| 473 |
+
|
| 474 |
+
model.vram_management_enabled = True
|
| 475 |
+
return model
|
diffsynth/diffusion/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .flow_match import FlowMatchScheduler
|
diffsynth/diffusion/base_pipeline.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image
|
| 2 |
+
import torch
|
| 3 |
+
import numpy as np
|
| 4 |
+
from einops import repeat, reduce
|
| 5 |
+
from typing import Union
|
| 6 |
+
from ..core import AutoTorchModule, AutoWrappedLinear, load_state_dict, ModelConfig
|
| 7 |
+
from ..utils.lora import GeneralLoRALoader
|
| 8 |
+
from ..models.model_loader import ModelPool
|
| 9 |
+
from ..utils.controlnet import ControlNetInput
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class PipelineUnit:
|
| 13 |
+
def __init__(
|
| 14 |
+
self,
|
| 15 |
+
seperate_cfg: bool = False,
|
| 16 |
+
take_over: bool = False,
|
| 17 |
+
input_params: tuple[str] = None,
|
| 18 |
+
output_params: tuple[str] = None,
|
| 19 |
+
input_params_posi: dict[str, str] = None,
|
| 20 |
+
input_params_nega: dict[str, str] = None,
|
| 21 |
+
onload_model_names: tuple[str] = None
|
| 22 |
+
):
|
| 23 |
+
self.seperate_cfg = seperate_cfg
|
| 24 |
+
self.take_over = take_over
|
| 25 |
+
self.input_params = input_params
|
| 26 |
+
self.output_params = output_params
|
| 27 |
+
self.input_params_posi = input_params_posi
|
| 28 |
+
self.input_params_nega = input_params_nega
|
| 29 |
+
self.onload_model_names = onload_model_names
|
| 30 |
+
|
| 31 |
+
def fetch_input_params(self):
|
| 32 |
+
params = []
|
| 33 |
+
if self.input_params is not None:
|
| 34 |
+
for param in self.input_params:
|
| 35 |
+
params.append(param)
|
| 36 |
+
if self.input_params_posi is not None:
|
| 37 |
+
for _, param in self.input_params_posi.items():
|
| 38 |
+
params.append(param)
|
| 39 |
+
if self.input_params_nega is not None:
|
| 40 |
+
for _, param in self.input_params_nega.items():
|
| 41 |
+
params.append(param)
|
| 42 |
+
params = sorted(list(set(params)))
|
| 43 |
+
return params
|
| 44 |
+
|
| 45 |
+
def fetch_output_params(self):
|
| 46 |
+
params = []
|
| 47 |
+
if self.output_params is not None:
|
| 48 |
+
for param in self.output_params:
|
| 49 |
+
params.append(param)
|
| 50 |
+
return params
|
| 51 |
+
|
| 52 |
+
def process(self, pipe, **kwargs) -> dict:
|
| 53 |
+
return {}
|
| 54 |
+
|
| 55 |
+
def post_process(self, pipe, **kwargs) -> dict:
|
| 56 |
+
return {}
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class BasePipeline(torch.nn.Module):
|
| 60 |
+
|
| 61 |
+
def __init__(
|
| 62 |
+
self,
|
| 63 |
+
device="cuda", torch_dtype=torch.float16,
|
| 64 |
+
height_division_factor=64, width_division_factor=64,
|
| 65 |
+
time_division_factor=None, time_division_remainder=None,
|
| 66 |
+
):
|
| 67 |
+
super().__init__()
|
| 68 |
+
|
| 69 |
+
self.device = device
|
| 70 |
+
self.torch_dtype = torch_dtype
|
| 71 |
+
|
| 72 |
+
self.height_division_factor = height_division_factor
|
| 73 |
+
self.width_division_factor = width_division_factor
|
| 74 |
+
self.time_division_factor = time_division_factor
|
| 75 |
+
self.time_division_remainder = time_division_remainder
|
| 76 |
+
|
| 77 |
+
self.vram_management_enabled = False
|
| 78 |
+
|
| 79 |
+
self.unit_runner = PipelineUnitRunner()
|
| 80 |
+
|
| 81 |
+
self.lora_loader = GeneralLoRALoader
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def to(self, *args, **kwargs):
|
| 85 |
+
device, dtype, non_blocking, convert_to_format = torch._C._nn._parse_to(*args, **kwargs)
|
| 86 |
+
if device is not None:
|
| 87 |
+
self.device = device
|
| 88 |
+
if dtype is not None:
|
| 89 |
+
self.torch_dtype = dtype
|
| 90 |
+
super().to(*args, **kwargs)
|
| 91 |
+
return self
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def check_resize_height_width(self, height, width, num_frames=None):
|
| 95 |
+
|
| 96 |
+
if height % self.height_division_factor != 0:
|
| 97 |
+
height = (height + self.height_division_factor - 1) // self.height_division_factor * self.height_division_factor
|
| 98 |
+
print(f"height % {self.height_division_factor} != 0. We round it up to {height}.")
|
| 99 |
+
if width % self.width_division_factor != 0:
|
| 100 |
+
width = (width + self.width_division_factor - 1) // self.width_division_factor * self.width_division_factor
|
| 101 |
+
print(f"width % {self.width_division_factor} != 0. We round it up to {width}.")
|
| 102 |
+
if num_frames is None:
|
| 103 |
+
return height, width
|
| 104 |
+
else:
|
| 105 |
+
if num_frames % self.time_division_factor != self.time_division_remainder:
|
| 106 |
+
num_frames = (num_frames + self.time_division_factor - 1) // self.time_division_factor * self.time_division_factor + self.time_division_remainder
|
| 107 |
+
print(f"num_frames % {self.time_division_factor} != {self.time_division_remainder}. We round it up to {num_frames}.")
|
| 108 |
+
return height, width, num_frames
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def preprocess_image(self, image, torch_dtype=None, device=None, pattern="B C H W", min_value=-1, max_value=1):
|
| 112 |
+
|
| 113 |
+
image = torch.Tensor(np.array(image, dtype=np.float32))
|
| 114 |
+
image = image.to(dtype=torch_dtype or self.torch_dtype, device=device or self.device)
|
| 115 |
+
image = image * ((max_value - min_value) / 255) + min_value
|
| 116 |
+
image = repeat(image, f"H W C -> {pattern}", **({"B": 1} if "B" in pattern else {}))
|
| 117 |
+
return image
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def preprocess_video(self, video, torch_dtype=None, device=None, pattern="B C T H W", min_value=-1, max_value=1):
|
| 121 |
+
|
| 122 |
+
video = [self.preprocess_image(image, torch_dtype=torch_dtype, device=device, min_value=min_value, max_value=max_value) for image in video]
|
| 123 |
+
video = torch.stack(video, dim=pattern.index("T") // 2)
|
| 124 |
+
return video
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def vae_output_to_image(self, vae_output, pattern="B C H W", min_value=-1, max_value=1):
|
| 128 |
+
|
| 129 |
+
if pattern != "H W C":
|
| 130 |
+
vae_output = reduce(vae_output, f"{pattern} -> H W C", reduction="mean")
|
| 131 |
+
image = ((vae_output - min_value) * (255 / (max_value - min_value))).clip(0, 255)
|
| 132 |
+
image = image.to(device="cpu", dtype=torch.uint8)
|
| 133 |
+
image = Image.fromarray(image.numpy())
|
| 134 |
+
return image
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def vae_output_to_video(self, vae_output, pattern="B C T H W", min_value=-1, max_value=1):
|
| 138 |
+
|
| 139 |
+
if pattern != "T H W C":
|
| 140 |
+
vae_output = reduce(vae_output, f"{pattern} -> T H W C", reduction="mean")
|
| 141 |
+
video = [self.vae_output_to_image(image, pattern="H W C", min_value=min_value, max_value=max_value) for image in vae_output]
|
| 142 |
+
return video
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def load_models_to_device(self, model_names):
|
| 146 |
+
if self.vram_management_enabled:
|
| 147 |
+
|
| 148 |
+
for name, model in self.named_children():
|
| 149 |
+
if name not in model_names:
|
| 150 |
+
if hasattr(model, "vram_management_enabled") and model.vram_management_enabled:
|
| 151 |
+
if hasattr(model, "offload"):
|
| 152 |
+
model.offload()
|
| 153 |
+
else:
|
| 154 |
+
for module in model.modules():
|
| 155 |
+
if hasattr(module, "offload"):
|
| 156 |
+
module.offload()
|
| 157 |
+
torch.cuda.empty_cache()
|
| 158 |
+
|
| 159 |
+
for name, model in self.named_children():
|
| 160 |
+
if name in model_names:
|
| 161 |
+
if hasattr(model, "vram_management_enabled") and model.vram_management_enabled:
|
| 162 |
+
if hasattr(model, "onload"):
|
| 163 |
+
model.onload()
|
| 164 |
+
else:
|
| 165 |
+
for module in model.modules():
|
| 166 |
+
if hasattr(module, "onload"):
|
| 167 |
+
module.onload()
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def generate_noise(self, shape, seed=None, rand_device="cpu", rand_torch_dtype=torch.float32, device=None, torch_dtype=None):
|
| 171 |
+
|
| 172 |
+
generator = None if seed is None else torch.Generator(rand_device).manual_seed(seed)
|
| 173 |
+
noise = torch.randn(shape, generator=generator, device=rand_device, dtype=rand_torch_dtype)
|
| 174 |
+
noise = noise.to(dtype=torch_dtype or self.torch_dtype, device=device or self.device)
|
| 175 |
+
return noise
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def get_vram(self):
|
| 179 |
+
return torch.cuda.mem_get_info(self.device)[1] / (1024 ** 3)
|
| 180 |
+
|
| 181 |
+
def get_module(self, model, name):
|
| 182 |
+
if "." in name:
|
| 183 |
+
name, suffix = name[:name.index(".")], name[name.index(".") + 1:]
|
| 184 |
+
if name.isdigit():
|
| 185 |
+
return self.get_module(model[int(name)], suffix)
|
| 186 |
+
else:
|
| 187 |
+
return self.get_module(getattr(model, name), suffix)
|
| 188 |
+
else:
|
| 189 |
+
return getattr(model, name)
|
| 190 |
+
|
| 191 |
+
def freeze_except(self, model_names):
|
| 192 |
+
self.eval()
|
| 193 |
+
self.requires_grad_(False)
|
| 194 |
+
for name in model_names:
|
| 195 |
+
module = self.get_module(self, name)
|
| 196 |
+
if module is None:
|
| 197 |
+
print(f"No {name} models in the pipeline. We cannot enable training on the model. If this occurs during the data processing stage, it is normal.")
|
| 198 |
+
continue
|
| 199 |
+
module.train()
|
| 200 |
+
module.requires_grad_(True)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def blend_with_mask(self, base, addition, mask):
|
| 204 |
+
return base * (1 - mask) + addition * mask
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def step(self, scheduler, latents, progress_id, noise_pred, input_latents=None, inpaint_mask=None, **kwargs):
|
| 208 |
+
timestep = scheduler.timesteps[progress_id]
|
| 209 |
+
if inpaint_mask is not None:
|
| 210 |
+
noise_pred_expected = scheduler.return_to_timestep(scheduler.timesteps[progress_id], latents, input_latents)
|
| 211 |
+
noise_pred = self.blend_with_mask(noise_pred_expected, noise_pred, inpaint_mask)
|
| 212 |
+
latents_next = scheduler.step(noise_pred, timestep, latents)
|
| 213 |
+
return latents_next
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def split_pipeline_units(self, model_names: list[str]):
|
| 217 |
+
return PipelineUnitGraph().split_pipeline_units(self.units, model_names)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def flush_vram_management_device(self, device):
|
| 221 |
+
for module in self.modules():
|
| 222 |
+
if isinstance(module, AutoTorchModule):
|
| 223 |
+
module.offload_device = device
|
| 224 |
+
module.onload_device = device
|
| 225 |
+
module.preparing_device = device
|
| 226 |
+
module.computation_device = device
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def load_lora(
|
| 230 |
+
self,
|
| 231 |
+
module: torch.nn.Module,
|
| 232 |
+
lora_config: Union[ModelConfig, str] = None,
|
| 233 |
+
alpha=1,
|
| 234 |
+
hotload=None,
|
| 235 |
+
state_dict=None,
|
| 236 |
+
):
|
| 237 |
+
if state_dict is None:
|
| 238 |
+
if isinstance(lora_config, str):
|
| 239 |
+
lora = load_state_dict(lora_config, torch_dtype=self.torch_dtype, device=self.device)
|
| 240 |
+
else:
|
| 241 |
+
lora_config.download_if_necessary()
|
| 242 |
+
lora = load_state_dict(lora_config.path, torch_dtype=self.torch_dtype, device=self.device)
|
| 243 |
+
else:
|
| 244 |
+
lora = state_dict
|
| 245 |
+
lora_loader = self.lora_loader(torch_dtype=self.torch_dtype, device=self.device)
|
| 246 |
+
lora = lora_loader.convert_state_dict(lora)
|
| 247 |
+
if hotload is None:
|
| 248 |
+
hotload = hasattr(module, "vram_management_enabled") and getattr(module, "vram_management_enabled")
|
| 249 |
+
if hotload:
|
| 250 |
+
if not (hasattr(module, "vram_management_enabled") and getattr(module, "vram_management_enabled")):
|
| 251 |
+
raise ValueError("VRAM Management is not enabled. LoRA hotloading is not supported.")
|
| 252 |
+
updated_num = 0
|
| 253 |
+
for _, module in module.named_modules():
|
| 254 |
+
if isinstance(module, AutoWrappedLinear):
|
| 255 |
+
name = module.name
|
| 256 |
+
lora_a_name = f'{name}.lora_A.weight'
|
| 257 |
+
lora_b_name = f'{name}.lora_B.weight'
|
| 258 |
+
if lora_a_name in lora and lora_b_name in lora:
|
| 259 |
+
updated_num += 1
|
| 260 |
+
module.lora_A_weights.append(lora[lora_a_name] * alpha)
|
| 261 |
+
module.lora_B_weights.append(lora[lora_b_name])
|
| 262 |
+
print(f"{updated_num} tensors are patched by LoRA. You can use `pipe.clear_lora()` to clear all LoRA layers.")
|
| 263 |
+
else:
|
| 264 |
+
lora_loader.fuse_lora_to_base_model(module, lora, alpha=alpha)
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def clear_lora(self):
|
| 268 |
+
cleared_num = 0
|
| 269 |
+
for name, module in self.named_modules():
|
| 270 |
+
if isinstance(module, AutoWrappedLinear):
|
| 271 |
+
if hasattr(module, "lora_A_weights"):
|
| 272 |
+
if len(module.lora_A_weights) > 0:
|
| 273 |
+
cleared_num += 1
|
| 274 |
+
module.lora_A_weights.clear()
|
| 275 |
+
if hasattr(module, "lora_B_weights"):
|
| 276 |
+
module.lora_B_weights.clear()
|
| 277 |
+
print(f"{cleared_num} LoRA layers are cleared.")
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
def download_and_load_models(self, model_configs: list[ModelConfig] = [], vram_limit: float = None):
|
| 281 |
+
model_pool = ModelPool()
|
| 282 |
+
for model_config in model_configs:
|
| 283 |
+
model_config.download_if_necessary()
|
| 284 |
+
vram_config = model_config.vram_config()
|
| 285 |
+
vram_config["computation_dtype"] = vram_config["computation_dtype"] or self.torch_dtype
|
| 286 |
+
vram_config["computation_device"] = vram_config["computation_device"] or self.device
|
| 287 |
+
model_pool.auto_load_model(
|
| 288 |
+
model_config.path,
|
| 289 |
+
vram_config=vram_config,
|
| 290 |
+
vram_limit=vram_limit,
|
| 291 |
+
clear_parameters=model_config.clear_parameters,
|
| 292 |
+
)
|
| 293 |
+
return model_pool
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
def check_vram_management_state(self):
|
| 297 |
+
vram_management_enabled = False
|
| 298 |
+
for module in self.children():
|
| 299 |
+
if hasattr(module, "vram_management_enabled") and getattr(module, "vram_management_enabled"):
|
| 300 |
+
vram_management_enabled = True
|
| 301 |
+
return vram_management_enabled
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def cfg_guided_model_fn(self, model_fn, cfg_scale, inputs_shared, inputs_posi, inputs_nega, **inputs_others):
|
| 305 |
+
noise_pred_posi = model_fn(**inputs_posi, **inputs_shared, **inputs_others)
|
| 306 |
+
if cfg_scale != 1.0:
|
| 307 |
+
noise_pred_nega = model_fn(**inputs_nega, **inputs_shared, **inputs_others)
|
| 308 |
+
noise_pred = noise_pred_nega + cfg_scale * (noise_pred_posi - noise_pred_nega)
|
| 309 |
+
else:
|
| 310 |
+
noise_pred = noise_pred_posi
|
| 311 |
+
return noise_pred
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
class PipelineUnitGraph:
|
| 315 |
+
def __init__(self):
|
| 316 |
+
pass
|
| 317 |
+
|
| 318 |
+
def build_edges(self, units: list[PipelineUnit]):
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
last_compute_unit_id = {}
|
| 322 |
+
edges = []
|
| 323 |
+
for unit_id, unit in enumerate(units):
|
| 324 |
+
for input_param in unit.fetch_input_params():
|
| 325 |
+
if input_param in last_compute_unit_id:
|
| 326 |
+
edges.append((last_compute_unit_id[input_param], unit_id))
|
| 327 |
+
for output_param in unit.fetch_output_params():
|
| 328 |
+
last_compute_unit_id[output_param] = unit_id
|
| 329 |
+
return edges
|
| 330 |
+
|
| 331 |
+
def build_chains(self, units: list[PipelineUnit]):
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
params = sum([unit.fetch_input_params() + unit.fetch_output_params() for unit in units], [])
|
| 335 |
+
params = sorted(list(set(params)))
|
| 336 |
+
chains = {param: [] for param in params}
|
| 337 |
+
for unit_id, unit in enumerate(units):
|
| 338 |
+
for param in unit.fetch_output_params():
|
| 339 |
+
chains[param].append(unit_id)
|
| 340 |
+
return chains
|
| 341 |
+
|
| 342 |
+
def search_direct_unit_ids(self, units: list[PipelineUnit], model_names: list[str]):
|
| 343 |
+
|
| 344 |
+
related_unit_ids = []
|
| 345 |
+
for unit_id, unit in enumerate(units):
|
| 346 |
+
for model_name in model_names:
|
| 347 |
+
if unit.onload_model_names is not None and model_name in unit.onload_model_names:
|
| 348 |
+
related_unit_ids.append(unit_id)
|
| 349 |
+
break
|
| 350 |
+
return related_unit_ids
|
| 351 |
+
|
| 352 |
+
def search_related_unit_ids(self, edges, start_unit_ids, direction="target"):
|
| 353 |
+
|
| 354 |
+
related_unit_ids = [unit_id for unit_id in start_unit_ids]
|
| 355 |
+
while True:
|
| 356 |
+
neighbors = []
|
| 357 |
+
for source, target in edges:
|
| 358 |
+
if direction == "target" and source in related_unit_ids and target not in related_unit_ids:
|
| 359 |
+
neighbors.append(target)
|
| 360 |
+
elif direction == "source" and source not in related_unit_ids and target in related_unit_ids:
|
| 361 |
+
neighbors.append(source)
|
| 362 |
+
neighbors = sorted(list(set(neighbors)))
|
| 363 |
+
if len(neighbors) == 0:
|
| 364 |
+
break
|
| 365 |
+
else:
|
| 366 |
+
related_unit_ids.extend(neighbors)
|
| 367 |
+
related_unit_ids = sorted(list(set(related_unit_ids)))
|
| 368 |
+
return related_unit_ids
|
| 369 |
+
|
| 370 |
+
def search_updating_unit_ids(self, units: list[PipelineUnit], chains, related_unit_ids):
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
first_compute_unit_id = {}
|
| 374 |
+
for unit_id in related_unit_ids:
|
| 375 |
+
for param in units[unit_id].fetch_input_params():
|
| 376 |
+
if param not in first_compute_unit_id:
|
| 377 |
+
first_compute_unit_id[param] = unit_id
|
| 378 |
+
updating_unit_ids = []
|
| 379 |
+
for param in first_compute_unit_id:
|
| 380 |
+
unit_id = first_compute_unit_id[param]
|
| 381 |
+
chain = chains[param]
|
| 382 |
+
if unit_id in chain and chain.index(unit_id) != len(chain) - 1:
|
| 383 |
+
for unit_id_ in chain[chain.index(unit_id) + 1:]:
|
| 384 |
+
if unit_id_ not in related_unit_ids:
|
| 385 |
+
updating_unit_ids.append(unit_id_)
|
| 386 |
+
related_unit_ids.extend(updating_unit_ids)
|
| 387 |
+
related_unit_ids = sorted(list(set(related_unit_ids)))
|
| 388 |
+
return related_unit_ids
|
| 389 |
+
|
| 390 |
+
def split_pipeline_units(self, units: list[PipelineUnit], model_names: list[str]):
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
related_unit_ids = self.search_direct_unit_ids(units, model_names)
|
| 394 |
+
edges = self.build_edges(units)
|
| 395 |
+
chains = self.build_chains(units)
|
| 396 |
+
while True:
|
| 397 |
+
num_related_unit_ids = len(related_unit_ids)
|
| 398 |
+
related_unit_ids = self.search_related_unit_ids(edges, related_unit_ids, "target")
|
| 399 |
+
related_unit_ids = self.search_updating_unit_ids(units, chains, related_unit_ids)
|
| 400 |
+
if len(related_unit_ids) == num_related_unit_ids:
|
| 401 |
+
break
|
| 402 |
+
else:
|
| 403 |
+
num_related_unit_ids = len(related_unit_ids)
|
| 404 |
+
related_units = [units[i] for i in related_unit_ids]
|
| 405 |
+
unrelated_units = [units[i] for i in range(len(units)) if i not in related_unit_ids]
|
| 406 |
+
return related_units, unrelated_units
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
class PipelineUnitRunner:
|
| 410 |
+
def __init__(self):
|
| 411 |
+
pass
|
| 412 |
+
|
| 413 |
+
def __call__(self, unit: PipelineUnit, pipe: BasePipeline, inputs_shared: dict, inputs_posi: dict, inputs_nega: dict) -> tuple[dict, dict]:
|
| 414 |
+
if unit.take_over:
|
| 415 |
+
|
| 416 |
+
inputs_shared, inputs_posi, inputs_nega = unit.process(pipe, inputs_shared=inputs_shared, inputs_posi=inputs_posi, inputs_nega=inputs_nega)
|
| 417 |
+
elif unit.seperate_cfg:
|
| 418 |
+
|
| 419 |
+
processor_inputs = {name: inputs_posi.get(name_) for name, name_ in unit.input_params_posi.items()}
|
| 420 |
+
if unit.input_params is not None:
|
| 421 |
+
for name in unit.input_params:
|
| 422 |
+
processor_inputs[name] = inputs_shared.get(name)
|
| 423 |
+
processor_outputs = unit.process(pipe, **processor_inputs)
|
| 424 |
+
inputs_posi.update(processor_outputs)
|
| 425 |
+
|
| 426 |
+
if inputs_shared["cfg_scale"] != 1:
|
| 427 |
+
processor_inputs = {name: inputs_nega.get(name_) for name, name_ in unit.input_params_nega.items()}
|
| 428 |
+
if unit.input_params is not None:
|
| 429 |
+
for name in unit.input_params:
|
| 430 |
+
processor_inputs[name] = inputs_shared.get(name)
|
| 431 |
+
processor_outputs = unit.process(pipe, **processor_inputs)
|
| 432 |
+
inputs_nega.update(processor_outputs)
|
| 433 |
+
else:
|
| 434 |
+
inputs_nega.update(processor_outputs)
|
| 435 |
+
else:
|
| 436 |
+
processor_inputs = {name: inputs_shared.get(name) for name in unit.input_params}
|
| 437 |
+
processor_outputs = unit.process(pipe, **processor_inputs)
|
| 438 |
+
inputs_shared.update(processor_outputs)
|
| 439 |
+
return inputs_shared, inputs_posi, inputs_nega
|
diffsynth/diffusion/flow_match.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, math
|
| 2 |
+
from typing_extensions import Literal
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class FlowMatchScheduler():
|
| 6 |
+
|
| 7 |
+
def __init__(self, template: Literal["FLUX.1", "Wan", "Qwen-Image", "FLUX.2", "Z-Image"] = "FLUX.1"):
|
| 8 |
+
self.set_timesteps_fn = {
|
| 9 |
+
"FLUX.1": FlowMatchScheduler.set_timesteps_flux,
|
| 10 |
+
"Wan": FlowMatchScheduler.set_timesteps_wan,
|
| 11 |
+
"Qwen-Image": FlowMatchScheduler.set_timesteps_qwen_image,
|
| 12 |
+
"FLUX.2": FlowMatchScheduler.set_timesteps_flux2,
|
| 13 |
+
"Z-Image": FlowMatchScheduler.set_timesteps_z_image,
|
| 14 |
+
}.get(template, FlowMatchScheduler.set_timesteps_flux)
|
| 15 |
+
self.num_train_timesteps = 1000
|
| 16 |
+
|
| 17 |
+
@staticmethod
|
| 18 |
+
def set_timesteps_flux(num_inference_steps=100, denoising_strength=1.0, shift=None):
|
| 19 |
+
sigma_min = 0.003/1.002
|
| 20 |
+
sigma_max = 1.0
|
| 21 |
+
shift = 3 if shift is None else shift
|
| 22 |
+
num_train_timesteps = 1000
|
| 23 |
+
sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
|
| 24 |
+
sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps)
|
| 25 |
+
sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
|
| 26 |
+
timesteps = sigmas * num_train_timesteps
|
| 27 |
+
return sigmas, timesteps
|
| 28 |
+
|
| 29 |
+
@staticmethod
|
| 30 |
+
def set_timesteps_wan(num_inference_steps=100, denoising_strength=1.0, shift=None):
|
| 31 |
+
sigma_min = 0.0
|
| 32 |
+
sigma_max = 1.0
|
| 33 |
+
shift = 5 if shift is None else shift
|
| 34 |
+
num_train_timesteps = 1000
|
| 35 |
+
sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
|
| 36 |
+
sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps + 1)[:-1]
|
| 37 |
+
sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
|
| 38 |
+
timesteps = sigmas * num_train_timesteps
|
| 39 |
+
return sigmas, timesteps
|
| 40 |
+
|
| 41 |
+
@staticmethod
|
| 42 |
+
def _calculate_shift_qwen_image(image_seq_len, base_seq_len=256, max_seq_len=8192, base_shift=0.5, max_shift=0.9):
|
| 43 |
+
m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
|
| 44 |
+
b = base_shift - m * base_seq_len
|
| 45 |
+
mu = image_seq_len * m + b
|
| 46 |
+
return mu
|
| 47 |
+
|
| 48 |
+
@staticmethod
|
| 49 |
+
def set_timesteps_qwen_image(num_inference_steps=100, denoising_strength=1.0, exponential_shift_mu=None, dynamic_shift_len=None):
|
| 50 |
+
sigma_min = 0.0
|
| 51 |
+
sigma_max = 1.0
|
| 52 |
+
num_train_timesteps = 1000
|
| 53 |
+
shift_terminal = 0.02
|
| 54 |
+
|
| 55 |
+
sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
|
| 56 |
+
sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps + 1)[:-1]
|
| 57 |
+
|
| 58 |
+
if exponential_shift_mu is not None:
|
| 59 |
+
mu = exponential_shift_mu
|
| 60 |
+
elif dynamic_shift_len is not None:
|
| 61 |
+
mu = FlowMatchScheduler._calculate_shift_qwen_image(dynamic_shift_len)
|
| 62 |
+
else:
|
| 63 |
+
mu = 0.8
|
| 64 |
+
sigmas = math.exp(mu) / (math.exp(mu) + (1 / sigmas - 1))
|
| 65 |
+
|
| 66 |
+
one_minus_z = 1 - sigmas
|
| 67 |
+
scale_factor = one_minus_z[-1] / (1 - shift_terminal)
|
| 68 |
+
sigmas = 1 - (one_minus_z / scale_factor)
|
| 69 |
+
|
| 70 |
+
timesteps = sigmas * num_train_timesteps
|
| 71 |
+
return sigmas, timesteps
|
| 72 |
+
|
| 73 |
+
@staticmethod
|
| 74 |
+
def compute_empirical_mu(image_seq_len, num_steps):
|
| 75 |
+
a1, b1 = 8.73809524e-05, 1.89833333
|
| 76 |
+
a2, b2 = 0.00016927, 0.45666666
|
| 77 |
+
|
| 78 |
+
if image_seq_len > 4300:
|
| 79 |
+
mu = a2 * image_seq_len + b2
|
| 80 |
+
return float(mu)
|
| 81 |
+
|
| 82 |
+
m_200 = a2 * image_seq_len + b2
|
| 83 |
+
m_10 = a1 * image_seq_len + b1
|
| 84 |
+
|
| 85 |
+
a = (m_200 - m_10) / 190.0
|
| 86 |
+
b = m_200 - 200.0 * a
|
| 87 |
+
mu = a * num_steps + b
|
| 88 |
+
|
| 89 |
+
return float(mu)
|
| 90 |
+
|
| 91 |
+
@staticmethod
|
| 92 |
+
def set_timesteps_flux2(num_inference_steps=100, denoising_strength=1.0, dynamic_shift_len=1024//16*1024//16):
|
| 93 |
+
sigma_min = 1 / num_inference_steps
|
| 94 |
+
sigma_max = 1.0
|
| 95 |
+
num_train_timesteps = 1000
|
| 96 |
+
sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
|
| 97 |
+
sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps)
|
| 98 |
+
mu = FlowMatchScheduler.compute_empirical_mu(dynamic_shift_len, num_inference_steps)
|
| 99 |
+
sigmas = math.exp(mu) / (math.exp(mu) + (1 / sigmas - 1))
|
| 100 |
+
timesteps = sigmas * num_train_timesteps
|
| 101 |
+
return sigmas, timesteps
|
| 102 |
+
|
| 103 |
+
@staticmethod
|
| 104 |
+
def set_timesteps_z_image(num_inference_steps=100, denoising_strength=1.0, shift=None, target_timesteps=None):
|
| 105 |
+
sigma_min = 0.0
|
| 106 |
+
sigma_max = 1.0
|
| 107 |
+
shift = 3 if shift is None else shift
|
| 108 |
+
num_train_timesteps = 1000
|
| 109 |
+
sigma_start = sigma_min + (sigma_max - sigma_min) * denoising_strength
|
| 110 |
+
sigmas = torch.linspace(sigma_start, sigma_min, num_inference_steps + 1)[:-1]
|
| 111 |
+
sigmas = shift * sigmas / (1 + (shift - 1) * sigmas)
|
| 112 |
+
timesteps = sigmas * num_train_timesteps
|
| 113 |
+
if target_timesteps is not None:
|
| 114 |
+
target_timesteps = target_timesteps.to(dtype=timesteps.dtype, device=timesteps.device)
|
| 115 |
+
for timestep in target_timesteps:
|
| 116 |
+
timestep_id = torch.argmin((timesteps - timestep).abs())
|
| 117 |
+
timesteps[timestep_id] = timestep
|
| 118 |
+
return sigmas, timesteps
|
| 119 |
+
|
| 120 |
+
def set_training_weight(self):
|
| 121 |
+
steps = 1000
|
| 122 |
+
x = self.timesteps
|
| 123 |
+
y = torch.exp(-2 * ((x - steps / 2) / steps) ** 2)
|
| 124 |
+
y_shifted = y - y.min()
|
| 125 |
+
bsmntw_weighing = y_shifted * (steps / y_shifted.sum())
|
| 126 |
+
if len(self.timesteps) != 1000:
|
| 127 |
+
|
| 128 |
+
bsmntw_weighing = bsmntw_weighing * (len(self.timesteps) / steps)
|
| 129 |
+
bsmntw_weighing = bsmntw_weighing + bsmntw_weighing[1]
|
| 130 |
+
self.linear_timesteps_weights = bsmntw_weighing
|
| 131 |
+
|
| 132 |
+
def set_timesteps(self, num_inference_steps=100, denoising_strength=1.0, training=False, **kwargs):
|
| 133 |
+
self.sigmas, self.timesteps = self.set_timesteps_fn(
|
| 134 |
+
num_inference_steps=num_inference_steps,
|
| 135 |
+
denoising_strength=denoising_strength,
|
| 136 |
+
**kwargs,
|
| 137 |
+
)
|
| 138 |
+
if training:
|
| 139 |
+
self.set_training_weight()
|
| 140 |
+
self.training = True
|
| 141 |
+
else:
|
| 142 |
+
self.training = False
|
| 143 |
+
|
| 144 |
+
def step(self, model_output, timestep, sample, to_final=False, **kwargs):
|
| 145 |
+
if isinstance(timestep, torch.Tensor):
|
| 146 |
+
timestep = timestep.cpu()
|
| 147 |
+
timestep_id = torch.argmin((self.timesteps - timestep).abs())
|
| 148 |
+
sigma = self.sigmas[timestep_id]
|
| 149 |
+
if to_final or timestep_id + 1 >= len(self.timesteps):
|
| 150 |
+
sigma_ = 0
|
| 151 |
+
else:
|
| 152 |
+
sigma_ = self.sigmas[timestep_id + 1]
|
| 153 |
+
prev_sample = sample + model_output * (sigma_ - sigma)
|
| 154 |
+
return prev_sample
|
| 155 |
+
|
| 156 |
+
def return_to_timestep(self, timestep, sample, sample_stablized):
|
| 157 |
+
if isinstance(timestep, torch.Tensor):
|
| 158 |
+
timestep = timestep.cpu()
|
| 159 |
+
timestep_id = torch.argmin((self.timesteps - timestep).abs())
|
| 160 |
+
sigma = self.sigmas[timestep_id]
|
| 161 |
+
model_output = (sample - sample_stablized) / sigma
|
| 162 |
+
return model_output
|
| 163 |
+
|
| 164 |
+
def add_noise(self, original_samples, noise, timestep):
|
| 165 |
+
if isinstance(timestep, torch.Tensor):
|
| 166 |
+
timestep = timestep.cpu()
|
| 167 |
+
timestep_id = torch.argmin((self.timesteps - timestep).abs())
|
| 168 |
+
sigma = self.sigmas[timestep_id]
|
| 169 |
+
sample = (1 - sigma) * original_samples + sigma * noise
|
| 170 |
+
return sample
|
| 171 |
+
|
| 172 |
+
def training_target(self, sample, noise, timestep):
|
| 173 |
+
target = noise - sample
|
| 174 |
+
return target
|
| 175 |
+
|
| 176 |
+
def training_weight(self, timestep):
|
| 177 |
+
timestep_id = torch.argmin((self.timesteps - timestep.to(self.timesteps.device)).abs())
|
| 178 |
+
weights = self.linear_timesteps_weights[timestep_id]
|
| 179 |
+
return weights
|
diffsynth/models/__init__.py
ADDED
|
File without changes
|
diffsynth/models/general_modules.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, math
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def get_timestep_embedding(
|
| 5 |
+
timesteps: torch.Tensor,
|
| 6 |
+
embedding_dim: int,
|
| 7 |
+
flip_sin_to_cos: bool = False,
|
| 8 |
+
downscale_freq_shift: float = 1,
|
| 9 |
+
scale: float = 1,
|
| 10 |
+
max_period: int = 10000,
|
| 11 |
+
computation_device = None,
|
| 12 |
+
align_dtype_to_timestep = False,
|
| 13 |
+
):
|
| 14 |
+
assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array"
|
| 15 |
+
|
| 16 |
+
half_dim = embedding_dim // 2
|
| 17 |
+
exponent = -math.log(max_period) * torch.arange(
|
| 18 |
+
start=0, end=half_dim, dtype=torch.float32, device=timesteps.device if computation_device is None else computation_device
|
| 19 |
+
)
|
| 20 |
+
exponent = exponent / (half_dim - downscale_freq_shift)
|
| 21 |
+
|
| 22 |
+
emb = torch.exp(exponent).to(timesteps.device)
|
| 23 |
+
if align_dtype_to_timestep:
|
| 24 |
+
emb = emb.to(timesteps.dtype)
|
| 25 |
+
emb = timesteps[:, None].float() * emb[None, :]
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
emb = scale * emb
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
if flip_sin_to_cos:
|
| 35 |
+
emb = torch.cat([emb[:, half_dim:], emb[:, :half_dim]], dim=-1)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
if embedding_dim % 2 == 1:
|
| 39 |
+
emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))
|
| 40 |
+
return emb
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class TemporalTimesteps(torch.nn.Module):
|
| 44 |
+
def __init__(self, num_channels: int, flip_sin_to_cos: bool, downscale_freq_shift: float, computation_device = None, scale=1, align_dtype_to_timestep=False):
|
| 45 |
+
super().__init__()
|
| 46 |
+
self.num_channels = num_channels
|
| 47 |
+
self.flip_sin_to_cos = flip_sin_to_cos
|
| 48 |
+
self.downscale_freq_shift = downscale_freq_shift
|
| 49 |
+
self.computation_device = computation_device
|
| 50 |
+
self.scale = scale
|
| 51 |
+
self.align_dtype_to_timestep = align_dtype_to_timestep
|
| 52 |
+
|
| 53 |
+
def forward(self, timesteps):
|
| 54 |
+
t_emb = get_timestep_embedding(
|
| 55 |
+
timesteps,
|
| 56 |
+
self.num_channels,
|
| 57 |
+
flip_sin_to_cos=self.flip_sin_to_cos,
|
| 58 |
+
downscale_freq_shift=self.downscale_freq_shift,
|
| 59 |
+
computation_device=self.computation_device,
|
| 60 |
+
scale=self.scale,
|
| 61 |
+
align_dtype_to_timestep=self.align_dtype_to_timestep,
|
| 62 |
+
)
|
| 63 |
+
return t_emb
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class DiffusersCompatibleTimestepProj(torch.nn.Module):
|
| 67 |
+
def __init__(self, dim_in, dim_out):
|
| 68 |
+
super().__init__()
|
| 69 |
+
self.linear_1 = torch.nn.Linear(dim_in, dim_out)
|
| 70 |
+
self.act = torch.nn.SiLU()
|
| 71 |
+
self.linear_2 = torch.nn.Linear(dim_out, dim_out)
|
| 72 |
+
|
| 73 |
+
def forward(self, x):
|
| 74 |
+
x = self.linear_1(x)
|
| 75 |
+
x = self.act(x)
|
| 76 |
+
x = self.linear_2(x)
|
| 77 |
+
return x
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class TimestepEmbeddings(torch.nn.Module):
|
| 81 |
+
def __init__(self, dim_in, dim_out, computation_device=None, diffusers_compatible_format=False, scale=1, align_dtype_to_timestep=False):
|
| 82 |
+
super().__init__()
|
| 83 |
+
self.time_proj = TemporalTimesteps(num_channels=dim_in, flip_sin_to_cos=True, downscale_freq_shift=0, computation_device=computation_device, scale=scale, align_dtype_to_timestep=align_dtype_to_timestep)
|
| 84 |
+
if diffusers_compatible_format:
|
| 85 |
+
self.timestep_embedder = DiffusersCompatibleTimestepProj(dim_in, dim_out)
|
| 86 |
+
else:
|
| 87 |
+
self.timestep_embedder = torch.nn.Sequential(
|
| 88 |
+
torch.nn.Linear(dim_in, dim_out), torch.nn.SiLU(), torch.nn.Linear(dim_out, dim_out)
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
def forward(self, timestep, dtype):
|
| 92 |
+
time_emb = self.time_proj(timestep).to(dtype)
|
| 93 |
+
time_emb = self.timestep_embedder(time_emb)
|
| 94 |
+
return time_emb
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class RMSNorm(torch.nn.Module):
|
| 98 |
+
def __init__(self, dim, eps, elementwise_affine=True):
|
| 99 |
+
super().__init__()
|
| 100 |
+
self.eps = eps
|
| 101 |
+
if elementwise_affine:
|
| 102 |
+
self.weight = torch.nn.Parameter(torch.ones((dim,)))
|
| 103 |
+
else:
|
| 104 |
+
self.weight = None
|
| 105 |
+
|
| 106 |
+
def forward(self, hidden_states):
|
| 107 |
+
input_dtype = hidden_states.dtype
|
| 108 |
+
variance = hidden_states.to(torch.float32).square().mean(-1, keepdim=True)
|
| 109 |
+
hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
|
| 110 |
+
hidden_states = hidden_states.to(input_dtype)
|
| 111 |
+
if self.weight is not None:
|
| 112 |
+
hidden_states = hidden_states * self.weight
|
| 113 |
+
return hidden_states
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class AdaLayerNorm(torch.nn.Module):
|
| 117 |
+
def __init__(self, dim, single=False, dual=False):
|
| 118 |
+
super().__init__()
|
| 119 |
+
self.single = single
|
| 120 |
+
self.dual = dual
|
| 121 |
+
self.linear = torch.nn.Linear(dim, dim * [[6, 2][single], 9][dual])
|
| 122 |
+
self.norm = torch.nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
|
| 123 |
+
|
| 124 |
+
def forward(self, x, emb):
|
| 125 |
+
emb = self.linear(torch.nn.functional.silu(emb))
|
| 126 |
+
if self.single:
|
| 127 |
+
scale, shift = emb.unsqueeze(1).chunk(2, dim=2)
|
| 128 |
+
x = self.norm(x) * (1 + scale) + shift
|
| 129 |
+
return x
|
| 130 |
+
elif self.dual:
|
| 131 |
+
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp, shift_msa2, scale_msa2, gate_msa2 = emb.unsqueeze(1).chunk(9, dim=2)
|
| 132 |
+
norm_x = self.norm(x)
|
| 133 |
+
x = norm_x * (1 + scale_msa) + shift_msa
|
| 134 |
+
norm_x2 = norm_x * (1 + scale_msa2) + shift_msa2
|
| 135 |
+
return x, gate_msa, shift_mlp, scale_mlp, gate_mlp, norm_x2, gate_msa2
|
| 136 |
+
else:
|
| 137 |
+
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = emb.unsqueeze(1).chunk(6, dim=2)
|
| 138 |
+
x = self.norm(x) * (1 + scale_msa) + shift_msa
|
| 139 |
+
return x, gate_msa, shift_mlp, scale_mlp, gate_mlp
|
diffsynth/models/model_loader.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from ..core.loader import load_model, hash_model_file
|
| 2 |
+
from ..core.vram import AutoWrappedModule
|
| 3 |
+
from ..configs import MODEL_CONFIGS, VRAM_MANAGEMENT_MODULE_MAPS
|
| 4 |
+
import importlib, json, torch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class ModelPool:
|
| 8 |
+
def __init__(self):
|
| 9 |
+
self.model = []
|
| 10 |
+
self.model_name = []
|
| 11 |
+
self.model_path = []
|
| 12 |
+
|
| 13 |
+
def import_model_class(self, model_class):
|
| 14 |
+
split = model_class.rfind(".")
|
| 15 |
+
model_resource, model_class = model_class[:split], model_class[split+1:]
|
| 16 |
+
model_class = importlib.import_module(model_resource).__getattribute__(model_class)
|
| 17 |
+
return model_class
|
| 18 |
+
|
| 19 |
+
def need_to_enable_vram_management(self, vram_config):
|
| 20 |
+
return vram_config["offload_dtype"] is not None and vram_config["offload_device"] is not None
|
| 21 |
+
|
| 22 |
+
def fetch_module_map(self, model_class, vram_config):
|
| 23 |
+
if self.need_to_enable_vram_management(vram_config):
|
| 24 |
+
if model_class in VRAM_MANAGEMENT_MODULE_MAPS:
|
| 25 |
+
module_map = {self.import_model_class(source): self.import_model_class(target) for source, target in VRAM_MANAGEMENT_MODULE_MAPS[model_class].items()}
|
| 26 |
+
else:
|
| 27 |
+
module_map = {self.import_model_class(model_class): AutoWrappedModule}
|
| 28 |
+
else:
|
| 29 |
+
module_map = None
|
| 30 |
+
return module_map
|
| 31 |
+
|
| 32 |
+
def load_model_file(self, config, path, vram_config, vram_limit=None):
|
| 33 |
+
model_class = self.import_model_class(config["model_class"])
|
| 34 |
+
model_config = config.get("extra_kwargs", {})
|
| 35 |
+
if "state_dict_converter" in config:
|
| 36 |
+
state_dict_converter = self.import_model_class(config["state_dict_converter"])
|
| 37 |
+
else:
|
| 38 |
+
state_dict_converter = None
|
| 39 |
+
module_map = self.fetch_module_map(config["model_class"], vram_config)
|
| 40 |
+
model = load_model(
|
| 41 |
+
model_class, path, model_config,
|
| 42 |
+
vram_config["computation_dtype"], vram_config["computation_device"],
|
| 43 |
+
state_dict_converter,
|
| 44 |
+
use_disk_map=True,
|
| 45 |
+
vram_config=vram_config, module_map=module_map, vram_limit=vram_limit,
|
| 46 |
+
)
|
| 47 |
+
return model
|
| 48 |
+
|
| 49 |
+
def default_vram_config(self):
|
| 50 |
+
vram_config = {
|
| 51 |
+
"offload_dtype": None,
|
| 52 |
+
"offload_device": None,
|
| 53 |
+
"onload_dtype": torch.bfloat16,
|
| 54 |
+
"onload_device": "cpu",
|
| 55 |
+
"preparing_dtype": torch.bfloat16,
|
| 56 |
+
"preparing_device": "cpu",
|
| 57 |
+
"computation_dtype": torch.bfloat16,
|
| 58 |
+
"computation_device": "cpu",
|
| 59 |
+
}
|
| 60 |
+
return vram_config
|
| 61 |
+
|
| 62 |
+
def auto_load_model(self, path, vram_config=None, vram_limit=None, clear_parameters=False):
|
| 63 |
+
print(f"Loading models from: {json.dumps(path, indent=4)}")
|
| 64 |
+
if vram_config is None:
|
| 65 |
+
vram_config = self.default_vram_config()
|
| 66 |
+
model_hash = hash_model_file(path)
|
| 67 |
+
loaded = False
|
| 68 |
+
for config in MODEL_CONFIGS:
|
| 69 |
+
if config["model_hash"] == model_hash:
|
| 70 |
+
model = self.load_model_file(config, path, vram_config, vram_limit=vram_limit)
|
| 71 |
+
if clear_parameters: self.clear_parameters(model)
|
| 72 |
+
self.model.append(model)
|
| 73 |
+
model_name = config["model_name"]
|
| 74 |
+
self.model_name.append(model_name)
|
| 75 |
+
self.model_path.append(path)
|
| 76 |
+
model_info = {"model_name": model_name, "model_class": config["model_class"], "extra_kwargs": config.get("extra_kwargs")}
|
| 77 |
+
print(f"Loaded model: {json.dumps(model_info, indent=4)}")
|
| 78 |
+
loaded = True
|
| 79 |
+
if not loaded:
|
| 80 |
+
raise ValueError(f"Cannot detect the model type. File: {path}. Model hash: {model_hash}")
|
| 81 |
+
|
| 82 |
+
def fetch_model(self, model_name, index=None):
|
| 83 |
+
fetched_models = []
|
| 84 |
+
fetched_model_paths = []
|
| 85 |
+
for model, model_path, model_name_ in zip(self.model, self.model_path, self.model_name):
|
| 86 |
+
if model_name == model_name_:
|
| 87 |
+
fetched_models.append(model)
|
| 88 |
+
fetched_model_paths.append(model_path)
|
| 89 |
+
if len(fetched_models) == 0:
|
| 90 |
+
print(f"No {model_name} models available. This is not an error.")
|
| 91 |
+
model = None
|
| 92 |
+
elif len(fetched_models) == 1:
|
| 93 |
+
print(f"Using {model_name} from {json.dumps(fetched_model_paths[0], indent=4)}.")
|
| 94 |
+
model = fetched_models[0]
|
| 95 |
+
else:
|
| 96 |
+
if index is None:
|
| 97 |
+
model = fetched_models[0]
|
| 98 |
+
print(f"More than one {model_name} models are loaded: {fetched_model_paths}. Using {model_name} from {json.dumps(fetched_model_paths[0], indent=4)}.")
|
| 99 |
+
elif isinstance(index, int):
|
| 100 |
+
model = fetched_models[:index]
|
| 101 |
+
print(f"More than one {model_name} models are loaded: {fetched_model_paths}. Using {model_name} from {json.dumps(fetched_model_paths[:index], indent=4)}.")
|
| 102 |
+
else:
|
| 103 |
+
model = fetched_models
|
| 104 |
+
print(f"More than one {model_name} models are loaded: {fetched_model_paths}. Using {model_name} from {json.dumps(fetched_model_paths, indent=4)}.")
|
| 105 |
+
return model
|
| 106 |
+
|
| 107 |
+
def clear_parameters(self, model: torch.nn.Module):
|
| 108 |
+
for name, module in model.named_children():
|
| 109 |
+
self.clear_parameters(module)
|
| 110 |
+
for name, param in model.named_parameters(recurse=False):
|
| 111 |
+
setattr(model, name, None)
|
diffsynth/models/qwen_image_connector.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from typing import Optional, Tuple
|
| 4 |
+
|
| 5 |
+
from transformers.models.qwen2.configuration_qwen2 import Qwen2Config
|
| 6 |
+
from transformers.models.qwen2.modeling_qwen2 import (
|
| 7 |
+
Qwen2PreTrainedModel,
|
| 8 |
+
Qwen2Attention,
|
| 9 |
+
Qwen2MLP,
|
| 10 |
+
Qwen2RMSNorm,
|
| 11 |
+
Qwen2RotaryEmbedding,
|
| 12 |
+
apply_rotary_pos_emb,
|
| 13 |
+
)
|
| 14 |
+
from transformers.integrations.sdpa_attention import sdpa_attention_forward
|
| 15 |
+
from torch.nn import functional as F
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class MultiHeadRMSNorm(nn.Module):
|
| 19 |
+
def __init__(self, dim, heads=1):
|
| 20 |
+
super().__init__()
|
| 21 |
+
self.scale = dim**0.5
|
| 22 |
+
self.gamma = nn.Parameter(torch.ones(heads, 1, dim))
|
| 23 |
+
|
| 24 |
+
def forward(self, x):
|
| 25 |
+
return F.normalize(x, dim=-1) * self.gamma * self.scale
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class Qwen2BidirectionalSdpaAttention(Qwen2Attention):
|
| 29 |
+
"""
|
| 30 |
+
An SDPA-based attention that does NOT apply causal masking.
|
| 31 |
+
Inherits from Qwen2Attention, but sets self.is_causal = False.
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
def __init__(self, config: Qwen2Config, layer_idx: Optional[int] = None):
|
| 35 |
+
super().__init__(config, layer_idx)
|
| 36 |
+
self.is_causal = False
|
| 37 |
+
self.qk_norm = config.qk_norm
|
| 38 |
+
if self.qk_norm:
|
| 39 |
+
self.q_norm = MultiHeadRMSNorm(
|
| 40 |
+
config.hidden_size // config.num_attention_heads,
|
| 41 |
+
config.num_attention_heads,
|
| 42 |
+
)
|
| 43 |
+
self.k_norm = MultiHeadRMSNorm(
|
| 44 |
+
config.hidden_size // config.num_attention_heads,
|
| 45 |
+
config.num_key_value_heads,
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
def forward(
|
| 49 |
+
self,
|
| 50 |
+
hidden_states: torch.Tensor,
|
| 51 |
+
position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 52 |
+
):
|
| 53 |
+
input_shape = hidden_states.shape[:-1]
|
| 54 |
+
hidden_shape = (*input_shape, -1, self.head_dim)
|
| 55 |
+
|
| 56 |
+
query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 57 |
+
key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 58 |
+
value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
|
| 59 |
+
|
| 60 |
+
if position_embeddings is not None:
|
| 61 |
+
cos, sin = position_embeddings
|
| 62 |
+
query_states, key_states = apply_rotary_pos_emb(
|
| 63 |
+
query_states, key_states, cos, sin
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
if self.qk_norm:
|
| 67 |
+
query_states = self.q_norm(query_states)
|
| 68 |
+
key_states = self.k_norm(key_states)
|
| 69 |
+
|
| 70 |
+
attn_output, attn_weights = sdpa_attention_forward(
|
| 71 |
+
self,
|
| 72 |
+
query_states,
|
| 73 |
+
key_states,
|
| 74 |
+
value_states,
|
| 75 |
+
attention_mask=None,
|
| 76 |
+
dropout=0.0 if not self.training else self.attention_dropout,
|
| 77 |
+
scaling=self.scaling,
|
| 78 |
+
is_causal=False,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
attn_output = attn_output.reshape(*input_shape, -1).contiguous()
|
| 82 |
+
attn_output = self.o_proj(attn_output)
|
| 83 |
+
return attn_output
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class Qwen2EncoderLayer(nn.Module):
|
| 87 |
+
def __init__(self, config: Qwen2Config, layer_idx: int):
|
| 88 |
+
super().__init__()
|
| 89 |
+
self.hidden_size = config.hidden_size
|
| 90 |
+
self.self_attn = Qwen2BidirectionalSdpaAttention(config, layer_idx)
|
| 91 |
+
self.mlp = Qwen2MLP(config)
|
| 92 |
+
|
| 93 |
+
self.input_layernorm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 94 |
+
self.post_attention_layernorm = Qwen2RMSNorm(
|
| 95 |
+
config.hidden_size, eps=config.rms_norm_eps
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
def forward(
|
| 99 |
+
self,
|
| 100 |
+
hidden_states: torch.Tensor,
|
| 101 |
+
position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 102 |
+
):
|
| 103 |
+
|
| 104 |
+
residual = hidden_states
|
| 105 |
+
hidden_states = self.input_layernorm(hidden_states)
|
| 106 |
+
|
| 107 |
+
hidden_states = self.self_attn(
|
| 108 |
+
hidden_states=hidden_states,
|
| 109 |
+
position_embeddings=position_embeddings,
|
| 110 |
+
)
|
| 111 |
+
hidden_states = residual + hidden_states
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
residual = hidden_states
|
| 115 |
+
hidden_states = self.post_attention_layernorm(hidden_states)
|
| 116 |
+
hidden_states = self.mlp(hidden_states)
|
| 117 |
+
hidden_states = residual + hidden_states
|
| 118 |
+
|
| 119 |
+
return hidden_states
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class Qwen2Encoder(Qwen2PreTrainedModel):
|
| 123 |
+
supports_gradient_checkpointing = True
|
| 124 |
+
|
| 125 |
+
def __init__(self, config: Qwen2Config):
|
| 126 |
+
super().__init__(config)
|
| 127 |
+
self.layers = nn.ModuleList(
|
| 128 |
+
[Qwen2EncoderLayer(config, i) for i in range(self.config.num_hidden_layers)]
|
| 129 |
+
)
|
| 130 |
+
if config.rope:
|
| 131 |
+
self.rotary_emb = Qwen2RotaryEmbedding(config=config)
|
| 132 |
+
else:
|
| 133 |
+
self.rotary_emb = None
|
| 134 |
+
if hasattr(config, "norm") and config.norm:
|
| 135 |
+
self.norm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 136 |
+
else:
|
| 137 |
+
self.norm = None
|
| 138 |
+
self.gradient_checkpointing = True
|
| 139 |
+
self.post_init()
|
| 140 |
+
|
| 141 |
+
def _init_weights(self, module):
|
| 142 |
+
std = self.config.initializer_range
|
| 143 |
+
if isinstance(module, nn.Linear):
|
| 144 |
+
module.weight.data.normal_(mean=0.0, std=std)
|
| 145 |
+
if module.bias is not None:
|
| 146 |
+
module.bias.data.zero_()
|
| 147 |
+
|
| 148 |
+
def forward(self, hidden_states):
|
| 149 |
+
bsz, seq_len, _ = hidden_states.size()
|
| 150 |
+
position_ids = torch.arange(seq_len, device=hidden_states.device).unsqueeze(0)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
if self.rotary_emb is not None:
|
| 154 |
+
position_embeddings = self.rotary_emb(hidden_states, position_ids)
|
| 155 |
+
else:
|
| 156 |
+
position_embeddings = None
|
| 157 |
+
|
| 158 |
+
for layer in self.layers:
|
| 159 |
+
if self.gradient_checkpointing and self.training:
|
| 160 |
+
hidden_states = self._gradient_checkpointing_func(
|
| 161 |
+
layer.__call__,
|
| 162 |
+
hidden_states,
|
| 163 |
+
position_embeddings,
|
| 164 |
+
)
|
| 165 |
+
else:
|
| 166 |
+
hidden_states = layer(
|
| 167 |
+
hidden_states,
|
| 168 |
+
position_embeddings=position_embeddings,
|
| 169 |
+
)
|
| 170 |
+
if self.norm:
|
| 171 |
+
hidden_states = self.norm(hidden_states)
|
| 172 |
+
return hidden_states
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class RMSNorm(nn.Module):
|
| 176 |
+
def __init__(self, dim, eps: float, elementwise_affine: bool = True):
|
| 177 |
+
super().__init__()
|
| 178 |
+
self.eps = eps
|
| 179 |
+
if elementwise_affine:
|
| 180 |
+
self.weight = nn.Parameter(torch.ones(dim))
|
| 181 |
+
else:
|
| 182 |
+
self.weight = None
|
| 183 |
+
|
| 184 |
+
def forward(self, hidden_states):
|
| 185 |
+
input_dtype = hidden_states.dtype
|
| 186 |
+
variance = hidden_states.to(torch.float32).pow(2).mean(-1, keepdim=True)
|
| 187 |
+
hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
|
| 188 |
+
|
| 189 |
+
if self.weight is not None:
|
| 190 |
+
|
| 191 |
+
if self.weight.dtype in [torch.float16, torch.bfloat16]:
|
| 192 |
+
hidden_states = hidden_states.to(self.weight.dtype)
|
| 193 |
+
hidden_states = hidden_states * self.weight
|
| 194 |
+
else:
|
| 195 |
+
hidden_states = hidden_states.to(input_dtype)
|
| 196 |
+
|
| 197 |
+
return hidden_states
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
class QwenImageConnector(nn.Module):
|
| 201 |
+
def __init__(
|
| 202 |
+
self,
|
| 203 |
+
input_dim: int=3584,
|
| 204 |
+
output_dim: int=3584,
|
| 205 |
+
num_layers: int=6,
|
| 206 |
+
input_scale: float = 1.0,
|
| 207 |
+
):
|
| 208 |
+
super().__init__()
|
| 209 |
+
self.encoder = Qwen2Encoder(
|
| 210 |
+
Qwen2Config(
|
| 211 |
+
hidden_size=input_dim,
|
| 212 |
+
intermediate_size=input_dim * 4,
|
| 213 |
+
num_hidden_layers=num_layers,
|
| 214 |
+
num_attention_heads=input_dim // 64,
|
| 215 |
+
num_key_value_heads=input_dim // 64,
|
| 216 |
+
initializer_range=0.014,
|
| 217 |
+
use_cache=False,
|
| 218 |
+
rope=True,
|
| 219 |
+
qk_norm=True,
|
| 220 |
+
),
|
| 221 |
+
)
|
| 222 |
+
self.proj = nn.Sequential(
|
| 223 |
+
nn.Linear(input_dim, output_dim),
|
| 224 |
+
nn.GELU(approximate="tanh"),
|
| 225 |
+
nn.Linear(output_dim, output_dim),
|
| 226 |
+
RMSNorm(output_dim, eps=1e-5, elementwise_affine=True),
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
with torch.no_grad():
|
| 231 |
+
self.proj[-1].weight.fill_(input_scale)
|
| 232 |
+
|
| 233 |
+
def forward(self, x):
|
| 234 |
+
x = self.encoder(x)
|
| 235 |
+
x = self.proj(x)
|
| 236 |
+
return x
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
|
diffsynth/models/qwen_image_controlnet.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from .general_modules import RMSNorm
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class BlockWiseControlBlock(torch.nn.Module):
|
| 7 |
+
|
| 8 |
+
def __init__(self, dim: int = 3072):
|
| 9 |
+
super().__init__()
|
| 10 |
+
self.x_rms = RMSNorm(dim, eps=1e-6)
|
| 11 |
+
self.y_rms = RMSNorm(dim, eps=1e-6)
|
| 12 |
+
self.input_proj = nn.Linear(dim, dim)
|
| 13 |
+
self.act = nn.GELU()
|
| 14 |
+
self.output_proj = nn.Linear(dim, dim)
|
| 15 |
+
|
| 16 |
+
def forward(self, x, y):
|
| 17 |
+
x, y = self.x_rms(x), self.y_rms(y)
|
| 18 |
+
x = self.input_proj(x + y)
|
| 19 |
+
x = self.act(x)
|
| 20 |
+
x = self.output_proj(x)
|
| 21 |
+
return x
|
| 22 |
+
|
| 23 |
+
def init_weights(self):
|
| 24 |
+
|
| 25 |
+
nn.init.zeros_(self.output_proj.weight)
|
| 26 |
+
nn.init.zeros_(self.output_proj.bias)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class QwenImageBlockWiseControlNet(torch.nn.Module):
|
| 30 |
+
def __init__(
|
| 31 |
+
self,
|
| 32 |
+
num_layers: int = 60,
|
| 33 |
+
in_dim: int = 64,
|
| 34 |
+
additional_in_dim: int = 0,
|
| 35 |
+
dim: int = 3072,
|
| 36 |
+
):
|
| 37 |
+
super().__init__()
|
| 38 |
+
self.img_in = nn.Linear(in_dim + additional_in_dim, dim)
|
| 39 |
+
self.controlnet_blocks = nn.ModuleList(
|
| 40 |
+
[
|
| 41 |
+
BlockWiseControlBlock(dim)
|
| 42 |
+
for _ in range(num_layers)
|
| 43 |
+
]
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
def init_weight(self):
|
| 47 |
+
nn.init.zeros_(self.img_in.weight)
|
| 48 |
+
nn.init.zeros_(self.img_in.bias)
|
| 49 |
+
for block in self.controlnet_blocks:
|
| 50 |
+
block.init_weights()
|
| 51 |
+
|
| 52 |
+
def process_controlnet_conditioning(self, controlnet_conditioning):
|
| 53 |
+
return self.img_in(controlnet_conditioning)
|
| 54 |
+
|
| 55 |
+
def blockwise_forward(self, img, controlnet_conditioning, block_id):
|
| 56 |
+
return self.controlnet_blocks[block_id](img, controlnet_conditioning)
|
diffsynth/models/qwen_image_dit.py
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, math
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from typing import Tuple, Optional, Union, List
|
| 4 |
+
from einops import rearrange
|
| 5 |
+
from .general_modules import TimestepEmbeddings, RMSNorm, AdaLayerNorm
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
import flash_attn_interface
|
| 9 |
+
FLASH_ATTN_3_AVAILABLE = True
|
| 10 |
+
except ModuleNotFoundError:
|
| 11 |
+
FLASH_ATTN_3_AVAILABLE = False
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def qwen_image_flash_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, num_heads: int, attention_mask = None, enable_fp8_attention: bool = False):
|
| 15 |
+
if FLASH_ATTN_3_AVAILABLE and attention_mask is None:
|
| 16 |
+
if not enable_fp8_attention:
|
| 17 |
+
|
| 18 |
+
q = rearrange(q, "b n s d -> b s n d", n=num_heads)
|
| 19 |
+
k = rearrange(k, "b n s d -> b s n d", n=num_heads)
|
| 20 |
+
v = rearrange(v, "b n s d -> b s n d", n=num_heads)
|
| 21 |
+
x = flash_attn_interface.flash_attn_func(q, k, v)
|
| 22 |
+
if isinstance(x, tuple):
|
| 23 |
+
x = x[0]
|
| 24 |
+
x = rearrange(x, "b s n d -> b s (n d)", n=num_heads)
|
| 25 |
+
else:
|
| 26 |
+
origin_dtype = q.dtype
|
| 27 |
+
q_std, k_std, v_std = q.std(), k.std(), v.std()
|
| 28 |
+
q, k, v = (q / q_std).to(torch.float8_e4m3fn), (k / k_std).to(torch.float8_e4m3fn), (v / v_std).to(torch.float8_e4m3fn)
|
| 29 |
+
q = rearrange(q, "b n s d -> b s n d", n=num_heads)
|
| 30 |
+
k = rearrange(k, "b n s d -> b s n d", n=num_heads)
|
| 31 |
+
v = rearrange(v, "b n s d -> b s n d", n=num_heads)
|
| 32 |
+
x = flash_attn_interface.flash_attn_func(q, k, v, softmax_scale=q_std * k_std / math.sqrt(q.size(-1)))
|
| 33 |
+
if isinstance(x, tuple):
|
| 34 |
+
x = x[0]
|
| 35 |
+
x = x.to(origin_dtype) * v_std
|
| 36 |
+
x = rearrange(x, "b s n d -> b s (n d)", n=num_heads)
|
| 37 |
+
else:
|
| 38 |
+
x = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=attention_mask)
|
| 39 |
+
x = rearrange(x, "b n s d -> b s (n d)", n=num_heads)
|
| 40 |
+
return x
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class ApproximateGELU(nn.Module):
|
| 44 |
+
def __init__(self, dim_in: int, dim_out: int, bias: bool = True):
|
| 45 |
+
super().__init__()
|
| 46 |
+
self.proj = nn.Linear(dim_in, dim_out, bias=bias)
|
| 47 |
+
|
| 48 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 49 |
+
x = self.proj(x)
|
| 50 |
+
return x * torch.sigmoid(1.702 * x)
|
| 51 |
+
|
| 52 |
+
def apply_rotary_emb_qwen(
|
| 53 |
+
x: torch.Tensor,
|
| 54 |
+
freqs_cis: Union[torch.Tensor, Tuple[torch.Tensor]]
|
| 55 |
+
):
|
| 56 |
+
x_rotated = torch.view_as_complex(x.float().reshape(*x.shape[:-1], -1, 2))
|
| 57 |
+
x_out = torch.view_as_real(x_rotated * freqs_cis).flatten(3)
|
| 58 |
+
return x_out.type_as(x)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class QwenEmbedRope(nn.Module):
|
| 62 |
+
def __init__(self, theta: int, axes_dim: list[int], scale_rope=False):
|
| 63 |
+
super().__init__()
|
| 64 |
+
self.theta = theta
|
| 65 |
+
self.axes_dim = axes_dim
|
| 66 |
+
pos_index = torch.arange(4096)
|
| 67 |
+
neg_index = torch.arange(4096).flip(0) * -1 - 1
|
| 68 |
+
self.pos_freqs = torch.cat([
|
| 69 |
+
self.rope_params(pos_index, self.axes_dim[0], self.theta),
|
| 70 |
+
self.rope_params(pos_index, self.axes_dim[1], self.theta),
|
| 71 |
+
self.rope_params(pos_index, self.axes_dim[2], self.theta),
|
| 72 |
+
], dim=1)
|
| 73 |
+
self.neg_freqs = torch.cat([
|
| 74 |
+
self.rope_params(neg_index, self.axes_dim[0], self.theta),
|
| 75 |
+
self.rope_params(neg_index, self.axes_dim[1], self.theta),
|
| 76 |
+
self.rope_params(neg_index, self.axes_dim[2], self.theta),
|
| 77 |
+
], dim=1)
|
| 78 |
+
self.rope_cache = {}
|
| 79 |
+
self.scale_rope = scale_rope
|
| 80 |
+
|
| 81 |
+
def rope_params(self, index, dim, theta=10000):
|
| 82 |
+
"""
|
| 83 |
+
Args:
|
| 84 |
+
index: [0, 1, 2, 3] 1D Tensor representing the position index of the token
|
| 85 |
+
"""
|
| 86 |
+
assert dim % 2 == 0
|
| 87 |
+
freqs = torch.outer(
|
| 88 |
+
index,
|
| 89 |
+
1.0 / torch.pow(theta, torch.arange(0, dim, 2).to(torch.float32).div(dim))
|
| 90 |
+
)
|
| 91 |
+
freqs = torch.polar(torch.ones_like(freqs), freqs)
|
| 92 |
+
return freqs
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def _expand_pos_freqs_if_needed(self, video_fhw, txt_seq_lens):
|
| 96 |
+
if isinstance(video_fhw, list):
|
| 97 |
+
video_fhw = tuple(max([i[j] for i in video_fhw]) for j in range(3))
|
| 98 |
+
_, height, width = video_fhw
|
| 99 |
+
if self.scale_rope:
|
| 100 |
+
max_vid_index = max(height // 2, width // 2)
|
| 101 |
+
else:
|
| 102 |
+
max_vid_index = max(height, width)
|
| 103 |
+
required_len = max_vid_index + max(txt_seq_lens)
|
| 104 |
+
cur_max_len = self.pos_freqs.shape[0]
|
| 105 |
+
if required_len <= cur_max_len:
|
| 106 |
+
return
|
| 107 |
+
|
| 108 |
+
new_max_len = math.ceil(required_len / 512) * 512
|
| 109 |
+
pos_index = torch.arange(new_max_len)
|
| 110 |
+
neg_index = torch.arange(new_max_len).flip(0) * -1 - 1
|
| 111 |
+
self.pos_freqs = torch.cat([
|
| 112 |
+
self.rope_params(pos_index, self.axes_dim[0], self.theta),
|
| 113 |
+
self.rope_params(pos_index, self.axes_dim[1], self.theta),
|
| 114 |
+
self.rope_params(pos_index, self.axes_dim[2], self.theta),
|
| 115 |
+
], dim=1)
|
| 116 |
+
self.neg_freqs = torch.cat([
|
| 117 |
+
self.rope_params(neg_index, self.axes_dim[0], self.theta),
|
| 118 |
+
self.rope_params(neg_index, self.axes_dim[1], self.theta),
|
| 119 |
+
self.rope_params(neg_index, self.axes_dim[2], self.theta),
|
| 120 |
+
], dim=1)
|
| 121 |
+
return
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def forward(self, video_fhw, txt_seq_lens, device):
|
| 125 |
+
self._expand_pos_freqs_if_needed(video_fhw, txt_seq_lens)
|
| 126 |
+
if self.pos_freqs.device != device:
|
| 127 |
+
self.pos_freqs = self.pos_freqs.to(device)
|
| 128 |
+
self.neg_freqs = self.neg_freqs.to(device)
|
| 129 |
+
|
| 130 |
+
vid_freqs = []
|
| 131 |
+
max_vid_index = 0
|
| 132 |
+
for idx, fhw in enumerate(video_fhw):
|
| 133 |
+
frame, height, width = fhw
|
| 134 |
+
rope_key = f"{idx}_{height}_{width}"
|
| 135 |
+
|
| 136 |
+
if rope_key not in self.rope_cache:
|
| 137 |
+
seq_lens = frame * height * width
|
| 138 |
+
freqs_pos = self.pos_freqs.split([x // 2 for x in self.axes_dim], dim=1)
|
| 139 |
+
freqs_neg = self.neg_freqs.split([x // 2 for x in self.axes_dim], dim=1)
|
| 140 |
+
freqs_frame = freqs_pos[0][idx : idx + frame].view(frame, 1, 1, -1).expand(frame, height, width, -1)
|
| 141 |
+
if self.scale_rope:
|
| 142 |
+
freqs_height = torch.cat(
|
| 143 |
+
[freqs_neg[1][-(height - height // 2) :], freqs_pos[1][: height // 2]], dim=0
|
| 144 |
+
)
|
| 145 |
+
freqs_height = freqs_height.view(1, height, 1, -1).expand(frame, height, width, -1)
|
| 146 |
+
freqs_width = torch.cat([freqs_neg[2][-(width - width // 2) :], freqs_pos[2][: width // 2]], dim=0)
|
| 147 |
+
freqs_width = freqs_width.view(1, 1, width, -1).expand(frame, height, width, -1)
|
| 148 |
+
|
| 149 |
+
else:
|
| 150 |
+
freqs_height = freqs_pos[1][:height].view(1, height, 1, -1).expand(frame, height, width, -1)
|
| 151 |
+
freqs_width = freqs_pos[2][:width].view(1, 1, width, -1).expand(frame, height, width, -1)
|
| 152 |
+
|
| 153 |
+
freqs = torch.cat([freqs_frame, freqs_height, freqs_width], dim=-1).reshape(seq_lens, -1)
|
| 154 |
+
self.rope_cache[rope_key] = freqs.clone().contiguous()
|
| 155 |
+
vid_freqs.append(self.rope_cache[rope_key])
|
| 156 |
+
|
| 157 |
+
if self.scale_rope:
|
| 158 |
+
max_vid_index = max(height // 2, width // 2, max_vid_index)
|
| 159 |
+
else:
|
| 160 |
+
max_vid_index = max(height, width, max_vid_index)
|
| 161 |
+
|
| 162 |
+
max_len = max(txt_seq_lens)
|
| 163 |
+
txt_freqs = self.pos_freqs[max_vid_index : max_vid_index + max_len, ...]
|
| 164 |
+
vid_freqs = torch.cat(vid_freqs, dim=0)
|
| 165 |
+
|
| 166 |
+
return vid_freqs, txt_freqs
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
def forward_sampling(self, video_fhw, txt_seq_lens, device):
|
| 170 |
+
self._expand_pos_freqs_if_needed(video_fhw, txt_seq_lens)
|
| 171 |
+
if self.pos_freqs.device != device:
|
| 172 |
+
self.pos_freqs = self.pos_freqs.to(device)
|
| 173 |
+
self.neg_freqs = self.neg_freqs.to(device)
|
| 174 |
+
|
| 175 |
+
vid_freqs = []
|
| 176 |
+
max_vid_index = 0
|
| 177 |
+
for idx, fhw in enumerate(video_fhw):
|
| 178 |
+
frame, height, width = fhw
|
| 179 |
+
rope_key = f"{idx}_{height}_{width}"
|
| 180 |
+
if idx > 0 and f"{0}_{height}_{width}" not in self.rope_cache:
|
| 181 |
+
frame_0, height_0, width_0 = video_fhw[0]
|
| 182 |
+
|
| 183 |
+
rope_key_0 = f"0_{height_0}_{width_0}"
|
| 184 |
+
spatial_freqs_0 = self.rope_cache[rope_key_0].reshape(frame_0, height_0, width_0, -1)
|
| 185 |
+
h_indices = torch.linspace(0, height_0 - 1, height).long()
|
| 186 |
+
w_indices = torch.linspace(0, width_0 - 1, width).long()
|
| 187 |
+
h_grid, w_grid = torch.meshgrid(h_indices, w_indices, indexing='ij')
|
| 188 |
+
sampled_rope = spatial_freqs_0[:, h_grid, w_grid, :]
|
| 189 |
+
|
| 190 |
+
freqs_pos = self.pos_freqs.split([x // 2 for x in self.axes_dim], dim=1)
|
| 191 |
+
freqs_frame = freqs_pos[0][idx : idx + frame].view(frame, 1, 1, -1).expand(frame, height, width, -1)
|
| 192 |
+
sampled_rope[:, :, :, :freqs_frame.shape[-1]] = freqs_frame
|
| 193 |
+
|
| 194 |
+
seq_lens = frame * height * width
|
| 195 |
+
self.rope_cache[rope_key] = sampled_rope.reshape(seq_lens, -1).clone()
|
| 196 |
+
if rope_key not in self.rope_cache:
|
| 197 |
+
seq_lens = frame * height * width
|
| 198 |
+
freqs_pos = self.pos_freqs.split([x // 2 for x in self.axes_dim], dim=1)
|
| 199 |
+
freqs_neg = self.neg_freqs.split([x // 2 for x in self.axes_dim], dim=1)
|
| 200 |
+
freqs_frame = freqs_pos[0][idx : idx + frame].view(frame, 1, 1, -1).expand(frame, height, width, -1)
|
| 201 |
+
if self.scale_rope:
|
| 202 |
+
freqs_height = torch.cat(
|
| 203 |
+
[freqs_neg[1][-(height - height // 2) :], freqs_pos[1][: height // 2]], dim=0
|
| 204 |
+
)
|
| 205 |
+
freqs_height = freqs_height.view(1, height, 1, -1).expand(frame, height, width, -1)
|
| 206 |
+
freqs_width = torch.cat([freqs_neg[2][-(width - width // 2) :], freqs_pos[2][: width // 2]], dim=0)
|
| 207 |
+
freqs_width = freqs_width.view(1, 1, width, -1).expand(frame, height, width, -1)
|
| 208 |
+
|
| 209 |
+
else:
|
| 210 |
+
freqs_height = freqs_pos[1][:height].view(1, height, 1, -1).expand(frame, height, width, -1)
|
| 211 |
+
freqs_width = freqs_pos[2][:width].view(1, 1, width, -1).expand(frame, height, width, -1)
|
| 212 |
+
|
| 213 |
+
freqs = torch.cat([freqs_frame, freqs_height, freqs_width], dim=-1).reshape(seq_lens, -1)
|
| 214 |
+
self.rope_cache[rope_key] = freqs.clone()
|
| 215 |
+
vid_freqs.append(self.rope_cache[rope_key].contiguous())
|
| 216 |
+
|
| 217 |
+
if self.scale_rope:
|
| 218 |
+
max_vid_index = max(height // 2, width // 2, max_vid_index)
|
| 219 |
+
else:
|
| 220 |
+
max_vid_index = max(height, width, max_vid_index)
|
| 221 |
+
|
| 222 |
+
max_len = max(txt_seq_lens)
|
| 223 |
+
txt_freqs = self.pos_freqs[max_vid_index : max_vid_index + max_len, ...]
|
| 224 |
+
vid_freqs = torch.cat(vid_freqs, dim=0)
|
| 225 |
+
|
| 226 |
+
return vid_freqs, txt_freqs
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
class QwenFeedForward(nn.Module):
|
| 230 |
+
def __init__(
|
| 231 |
+
self,
|
| 232 |
+
dim: int,
|
| 233 |
+
dim_out: Optional[int] = None,
|
| 234 |
+
dropout: float = 0.0,
|
| 235 |
+
):
|
| 236 |
+
super().__init__()
|
| 237 |
+
inner_dim = int(dim * 4)
|
| 238 |
+
self.net = nn.ModuleList([])
|
| 239 |
+
self.net.append(ApproximateGELU(dim, inner_dim))
|
| 240 |
+
self.net.append(nn.Dropout(dropout))
|
| 241 |
+
self.net.append(nn.Linear(inner_dim, dim_out))
|
| 242 |
+
|
| 243 |
+
def forward(self, hidden_states: torch.Tensor, *args, **kwargs) -> torch.Tensor:
|
| 244 |
+
for module in self.net:
|
| 245 |
+
hidden_states = module(hidden_states)
|
| 246 |
+
return hidden_states
|
| 247 |
+
|
| 248 |
+
class QwenDoubleStreamAttention(nn.Module):
|
| 249 |
+
def __init__(
|
| 250 |
+
self,
|
| 251 |
+
dim_a,
|
| 252 |
+
dim_b,
|
| 253 |
+
num_heads,
|
| 254 |
+
head_dim,
|
| 255 |
+
):
|
| 256 |
+
super().__init__()
|
| 257 |
+
self.num_heads = num_heads
|
| 258 |
+
self.head_dim = head_dim
|
| 259 |
+
|
| 260 |
+
self.to_q = nn.Linear(dim_a, dim_a)
|
| 261 |
+
self.to_k = nn.Linear(dim_a, dim_a)
|
| 262 |
+
self.to_v = nn.Linear(dim_a, dim_a)
|
| 263 |
+
self.norm_q = RMSNorm(head_dim, eps=1e-6)
|
| 264 |
+
self.norm_k = RMSNorm(head_dim, eps=1e-6)
|
| 265 |
+
|
| 266 |
+
self.add_q_proj = nn.Linear(dim_b, dim_b)
|
| 267 |
+
self.add_k_proj = nn.Linear(dim_b, dim_b)
|
| 268 |
+
self.add_v_proj = nn.Linear(dim_b, dim_b)
|
| 269 |
+
self.norm_added_q = RMSNorm(head_dim, eps=1e-6)
|
| 270 |
+
self.norm_added_k = RMSNorm(head_dim, eps=1e-6)
|
| 271 |
+
|
| 272 |
+
self.to_out = torch.nn.Sequential(nn.Linear(dim_a, dim_a))
|
| 273 |
+
self.to_add_out = nn.Linear(dim_b, dim_b)
|
| 274 |
+
|
| 275 |
+
def forward(
|
| 276 |
+
self,
|
| 277 |
+
image: torch.FloatTensor,
|
| 278 |
+
text: torch.FloatTensor,
|
| 279 |
+
image_rotary_emb: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 280 |
+
attention_mask: Optional[torch.FloatTensor] = None,
|
| 281 |
+
enable_fp8_attention: bool = False,
|
| 282 |
+
) -> Tuple[torch.FloatTensor, torch.FloatTensor]:
|
| 283 |
+
img_q, img_k, img_v = self.to_q(image), self.to_k(image), self.to_v(image)
|
| 284 |
+
txt_q, txt_k, txt_v = self.add_q_proj(text), self.add_k_proj(text), self.add_v_proj(text)
|
| 285 |
+
seq_txt = txt_q.shape[1]
|
| 286 |
+
|
| 287 |
+
img_q = rearrange(img_q, 'b s (h d) -> b h s d', h=self.num_heads)
|
| 288 |
+
img_k = rearrange(img_k, 'b s (h d) -> b h s d', h=self.num_heads)
|
| 289 |
+
img_v = rearrange(img_v, 'b s (h d) -> b h s d', h=self.num_heads)
|
| 290 |
+
|
| 291 |
+
txt_q = rearrange(txt_q, 'b s (h d) -> b h s d', h=self.num_heads)
|
| 292 |
+
txt_k = rearrange(txt_k, 'b s (h d) -> b h s d', h=self.num_heads)
|
| 293 |
+
txt_v = rearrange(txt_v, 'b s (h d) -> b h s d', h=self.num_heads)
|
| 294 |
+
|
| 295 |
+
img_q, img_k = self.norm_q(img_q), self.norm_k(img_k)
|
| 296 |
+
txt_q, txt_k = self.norm_added_q(txt_q), self.norm_added_k(txt_k)
|
| 297 |
+
|
| 298 |
+
if image_rotary_emb is not None:
|
| 299 |
+
img_freqs, txt_freqs = image_rotary_emb
|
| 300 |
+
img_q = apply_rotary_emb_qwen(img_q, img_freqs)
|
| 301 |
+
img_k = apply_rotary_emb_qwen(img_k, img_freqs)
|
| 302 |
+
txt_q = apply_rotary_emb_qwen(txt_q, txt_freqs)
|
| 303 |
+
txt_k = apply_rotary_emb_qwen(txt_k, txt_freqs)
|
| 304 |
+
|
| 305 |
+
joint_q = torch.cat([txt_q, img_q], dim=2)
|
| 306 |
+
joint_k = torch.cat([txt_k, img_k], dim=2)
|
| 307 |
+
joint_v = torch.cat([txt_v, img_v], dim=2)
|
| 308 |
+
|
| 309 |
+
joint_attn_out = qwen_image_flash_attention(joint_q, joint_k, joint_v, num_heads=joint_q.shape[1], attention_mask=attention_mask, enable_fp8_attention=enable_fp8_attention).to(joint_q.dtype)
|
| 310 |
+
|
| 311 |
+
txt_attn_output = joint_attn_out[:, :seq_txt, :]
|
| 312 |
+
img_attn_output = joint_attn_out[:, seq_txt:, :]
|
| 313 |
+
|
| 314 |
+
img_attn_output = self.to_out(img_attn_output)
|
| 315 |
+
txt_attn_output = self.to_add_out(txt_attn_output)
|
| 316 |
+
|
| 317 |
+
return img_attn_output, txt_attn_output
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
class QwenImageTransformerBlock(nn.Module):
|
| 321 |
+
def __init__(
|
| 322 |
+
self,
|
| 323 |
+
dim: int,
|
| 324 |
+
num_attention_heads: int,
|
| 325 |
+
attention_head_dim: int,
|
| 326 |
+
eps: float = 1e-6,
|
| 327 |
+
):
|
| 328 |
+
super().__init__()
|
| 329 |
+
|
| 330 |
+
self.dim = dim
|
| 331 |
+
self.num_attention_heads = num_attention_heads
|
| 332 |
+
self.attention_head_dim = attention_head_dim
|
| 333 |
+
|
| 334 |
+
self.img_mod = nn.Sequential(
|
| 335 |
+
nn.SiLU(),
|
| 336 |
+
nn.Linear(dim, 6 * dim),
|
| 337 |
+
)
|
| 338 |
+
self.img_norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 339 |
+
self.attn = QwenDoubleStreamAttention(
|
| 340 |
+
dim_a=dim,
|
| 341 |
+
dim_b=dim,
|
| 342 |
+
num_heads=num_attention_heads,
|
| 343 |
+
head_dim=attention_head_dim,
|
| 344 |
+
)
|
| 345 |
+
self.img_norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 346 |
+
self.img_mlp = QwenFeedForward(dim=dim, dim_out=dim)
|
| 347 |
+
|
| 348 |
+
self.txt_mod = nn.Sequential(
|
| 349 |
+
nn.SiLU(),
|
| 350 |
+
nn.Linear(dim, 6 * dim, bias=True),
|
| 351 |
+
)
|
| 352 |
+
self.txt_norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 353 |
+
self.txt_norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=eps)
|
| 354 |
+
self.txt_mlp = QwenFeedForward(dim=dim, dim_out=dim)
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
def _modulate(self, x, mod_params, index=None):
|
| 362 |
+
shift, scale, gate = mod_params.chunk(3, dim=-1)
|
| 363 |
+
if index is not None:
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
actual_batch = shift.size(0) // 2
|
| 367 |
+
shift_0, shift_1 = shift[:actual_batch], shift[actual_batch:]
|
| 368 |
+
scale_0, scale_1 = scale[:actual_batch], scale[actual_batch:]
|
| 369 |
+
gate_0, gate_1 = gate[:actual_batch], gate[actual_batch:]
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
index_expanded = index.unsqueeze(-1)
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
shift_0_exp = shift_0.unsqueeze(1)
|
| 377 |
+
shift_1_exp = shift_1.unsqueeze(1)
|
| 378 |
+
scale_0_exp = scale_0.unsqueeze(1)
|
| 379 |
+
scale_1_exp = scale_1.unsqueeze(1)
|
| 380 |
+
gate_0_exp = gate_0.unsqueeze(1)
|
| 381 |
+
gate_1_exp = gate_1.unsqueeze(1)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
shift_result = torch.where(index_expanded == 0, shift_0_exp, shift_1_exp)
|
| 385 |
+
scale_result = torch.where(index_expanded == 0, scale_0_exp, scale_1_exp)
|
| 386 |
+
gate_result = torch.where(index_expanded == 0, gate_0_exp, gate_1_exp)
|
| 387 |
+
else:
|
| 388 |
+
shift_result = shift.unsqueeze(1)
|
| 389 |
+
scale_result = scale.unsqueeze(1)
|
| 390 |
+
gate_result = gate.unsqueeze(1)
|
| 391 |
+
|
| 392 |
+
return x * (1 + scale_result) + shift_result, gate_result
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
def forward(
|
| 396 |
+
self,
|
| 397 |
+
image: torch.Tensor,
|
| 398 |
+
text: torch.Tensor,
|
| 399 |
+
temb: torch.Tensor,
|
| 400 |
+
image_rotary_emb: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 401 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 402 |
+
enable_fp8_attention = False,
|
| 403 |
+
modulate_index: Optional[List[int]] = None,
|
| 404 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 405 |
+
|
| 406 |
+
img_mod_attn, img_mod_mlp = self.img_mod(temb).chunk(2, dim=-1)
|
| 407 |
+
if modulate_index is not None:
|
| 408 |
+
temb = torch.chunk(temb, 2, dim=0)[0]
|
| 409 |
+
txt_mod_attn, txt_mod_mlp = self.txt_mod(temb).chunk(2, dim=-1)
|
| 410 |
+
|
| 411 |
+
img_normed = self.img_norm1(image)
|
| 412 |
+
img_modulated, img_gate = self._modulate(img_normed, img_mod_attn, index=modulate_index)
|
| 413 |
+
|
| 414 |
+
txt_normed = self.txt_norm1(text)
|
| 415 |
+
txt_modulated, txt_gate = self._modulate(txt_normed, txt_mod_attn)
|
| 416 |
+
|
| 417 |
+
img_attn_out, txt_attn_out = self.attn(
|
| 418 |
+
image=img_modulated,
|
| 419 |
+
text=txt_modulated,
|
| 420 |
+
image_rotary_emb=image_rotary_emb,
|
| 421 |
+
attention_mask=attention_mask,
|
| 422 |
+
enable_fp8_attention=enable_fp8_attention,
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
image = image + img_gate * img_attn_out
|
| 426 |
+
text = text + txt_gate * txt_attn_out
|
| 427 |
+
|
| 428 |
+
img_normed_2 = self.img_norm2(image)
|
| 429 |
+
img_modulated_2, img_gate_2 = self._modulate(img_normed_2, img_mod_mlp, index=modulate_index)
|
| 430 |
+
|
| 431 |
+
txt_normed_2 = self.txt_norm2(text)
|
| 432 |
+
txt_modulated_2, txt_gate_2 = self._modulate(txt_normed_2, txt_mod_mlp)
|
| 433 |
+
|
| 434 |
+
img_mlp_out = self.img_mlp(img_modulated_2)
|
| 435 |
+
txt_mlp_out = self.txt_mlp(txt_modulated_2)
|
| 436 |
+
|
| 437 |
+
image = image + img_gate_2 * img_mlp_out
|
| 438 |
+
text = text + txt_gate_2 * txt_mlp_out
|
| 439 |
+
|
| 440 |
+
return text, image
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
class QwenImageDiT(torch.nn.Module):
|
| 444 |
+
def __init__(
|
| 445 |
+
self,
|
| 446 |
+
num_layers: int = 60,
|
| 447 |
+
):
|
| 448 |
+
super().__init__()
|
| 449 |
+
|
| 450 |
+
self.pos_embed = QwenEmbedRope(theta=10000, axes_dim=[16,56,56], scale_rope=True)
|
| 451 |
+
|
| 452 |
+
self.time_text_embed = TimestepEmbeddings(256, 3072, diffusers_compatible_format=True, scale=1000, align_dtype_to_timestep=True)
|
| 453 |
+
self.txt_norm = RMSNorm(3584, eps=1e-6)
|
| 454 |
+
|
| 455 |
+
self.img_in = nn.Linear(64, 3072)
|
| 456 |
+
self.txt_in = nn.Linear(3584, 3072)
|
| 457 |
+
|
| 458 |
+
self.transformer_blocks = nn.ModuleList(
|
| 459 |
+
[
|
| 460 |
+
QwenImageTransformerBlock(
|
| 461 |
+
dim=3072,
|
| 462 |
+
num_attention_heads=24,
|
| 463 |
+
attention_head_dim=128,
|
| 464 |
+
)
|
| 465 |
+
for _ in range(num_layers)
|
| 466 |
+
]
|
| 467 |
+
)
|
| 468 |
+
self.norm_out = AdaLayerNorm(3072, single=True)
|
| 469 |
+
self.proj_out = nn.Linear(3072, 64)
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
def process_entity_masks(self, latents, prompt_emb, prompt_emb_mask, entity_prompt_emb, entity_prompt_emb_mask, entity_masks, height, width, image, img_shapes):
|
| 473 |
+
|
| 474 |
+
all_prompt_emb = entity_prompt_emb + [prompt_emb]
|
| 475 |
+
all_prompt_emb = [self.txt_in(self.txt_norm(local_prompt_emb)) for local_prompt_emb in all_prompt_emb]
|
| 476 |
+
all_prompt_emb = torch.cat(all_prompt_emb, dim=1)
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
txt_seq_lens = prompt_emb_mask.sum(dim=1).tolist()
|
| 480 |
+
image_rotary_emb = self.pos_embed(img_shapes, txt_seq_lens, device=latents.device)
|
| 481 |
+
entity_seq_lens = [emb_mask.sum(dim=1).tolist() for emb_mask in entity_prompt_emb_mask]
|
| 482 |
+
entity_rotary_emb = [self.pos_embed(img_shapes, entity_seq_len, device=latents.device)[1] for entity_seq_len in entity_seq_lens]
|
| 483 |
+
txt_rotary_emb = torch.cat(entity_rotary_emb + [image_rotary_emb[1]], dim=0)
|
| 484 |
+
image_rotary_emb = (image_rotary_emb[0], txt_rotary_emb)
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
repeat_dim = latents.shape[1]
|
| 488 |
+
max_masks = entity_masks.shape[1]
|
| 489 |
+
entity_masks = entity_masks.repeat(1, 1, repeat_dim, 1, 1)
|
| 490 |
+
entity_masks = [entity_masks[:, i, None].squeeze(1) for i in range(max_masks)]
|
| 491 |
+
global_mask = torch.ones_like(entity_masks[0]).to(device=latents.device, dtype=latents.dtype)
|
| 492 |
+
entity_masks = entity_masks + [global_mask]
|
| 493 |
+
|
| 494 |
+
N = len(entity_masks)
|
| 495 |
+
batch_size = entity_masks[0].shape[0]
|
| 496 |
+
seq_lens = [mask_.sum(dim=1).item() for mask_ in entity_prompt_emb_mask] + [prompt_emb_mask.sum(dim=1).item()]
|
| 497 |
+
total_seq_len = sum(seq_lens) + image.shape[1]
|
| 498 |
+
patched_masks = []
|
| 499 |
+
for i in range(N):
|
| 500 |
+
patched_mask = rearrange(entity_masks[i], "B C (H P) (W Q) -> B (H W) (C P Q)", H=height//16, W=width//16, P=2, Q=2)
|
| 501 |
+
patched_masks.append(patched_mask)
|
| 502 |
+
attention_mask = torch.ones((batch_size, total_seq_len, total_seq_len), dtype=torch.bool).to(device=entity_masks[0].device)
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
image_start = sum(seq_lens)
|
| 506 |
+
image_end = total_seq_len
|
| 507 |
+
cumsum = [0]
|
| 508 |
+
single_image_seq = image_end - image_start
|
| 509 |
+
for length in seq_lens:
|
| 510 |
+
cumsum.append(cumsum[-1] + length)
|
| 511 |
+
for i in range(N):
|
| 512 |
+
prompt_start = cumsum[i]
|
| 513 |
+
prompt_end = cumsum[i+1]
|
| 514 |
+
image_mask = torch.sum(patched_masks[i], dim=-1) > 0
|
| 515 |
+
image_mask = image_mask.unsqueeze(1).repeat(1, seq_lens[i], 1)
|
| 516 |
+
|
| 517 |
+
repeat_time = single_image_seq // image_mask.shape[-1]
|
| 518 |
+
image_mask = image_mask.repeat(1, 1, repeat_time)
|
| 519 |
+
|
| 520 |
+
attention_mask[:, prompt_start:prompt_end, image_start:image_end] = image_mask
|
| 521 |
+
|
| 522 |
+
attention_mask[:, image_start:image_end, prompt_start:prompt_end] = image_mask.transpose(1, 2)
|
| 523 |
+
|
| 524 |
+
for i in range(N):
|
| 525 |
+
for j in range(N):
|
| 526 |
+
if i == j:
|
| 527 |
+
continue
|
| 528 |
+
start_i, end_i = cumsum[i], cumsum[i+1]
|
| 529 |
+
start_j, end_j = cumsum[j], cumsum[j+1]
|
| 530 |
+
attention_mask[:, start_i:end_i, start_j:end_j] = False
|
| 531 |
+
|
| 532 |
+
attention_mask = attention_mask.float()
|
| 533 |
+
attention_mask[attention_mask == 0] = float('-inf')
|
| 534 |
+
attention_mask[attention_mask == 1] = 0
|
| 535 |
+
attention_mask = attention_mask.to(device=latents.device, dtype=latents.dtype).unsqueeze(1)
|
| 536 |
+
|
| 537 |
+
return all_prompt_emb, image_rotary_emb, attention_mask
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
def forward(
|
| 541 |
+
self,
|
| 542 |
+
latents=None,
|
| 543 |
+
timestep=None,
|
| 544 |
+
prompt_emb=None,
|
| 545 |
+
prompt_emb_mask=None,
|
| 546 |
+
height=None,
|
| 547 |
+
width=None,
|
| 548 |
+
):
|
| 549 |
+
img_shapes = [(latents.shape[0], latents.shape[2]//2, latents.shape[3]//2)]
|
| 550 |
+
txt_seq_lens = prompt_emb_mask.sum(dim=1).tolist()
|
| 551 |
+
|
| 552 |
+
image = rearrange(latents, "B C (H P) (W Q) -> B (H W) (C P Q)", H=height//16, W=width//16, P=2, Q=2)
|
| 553 |
+
image = self.img_in(image)
|
| 554 |
+
text = self.txt_in(self.txt_norm(prompt_emb))
|
| 555 |
+
|
| 556 |
+
conditioning = self.time_text_embed(timestep, image.dtype)
|
| 557 |
+
|
| 558 |
+
image_rotary_emb = self.pos_embed(img_shapes, txt_seq_lens, device=latents.device)
|
| 559 |
+
|
| 560 |
+
for block in self.transformer_blocks:
|
| 561 |
+
text, image = block(
|
| 562 |
+
image=image,
|
| 563 |
+
text=text,
|
| 564 |
+
temb=conditioning,
|
| 565 |
+
image_rotary_emb=image_rotary_emb,
|
| 566 |
+
)
|
| 567 |
+
|
| 568 |
+
image = self.norm_out(image, conditioning)
|
| 569 |
+
image = self.proj_out(image)
|
| 570 |
+
|
| 571 |
+
latents = rearrange(image, "B (H W) (C P Q) -> B C (H P) (W Q)", H=height//16, W=width//16, P=2, Q=2)
|
| 572 |
+
return image
|
diffsynth/models/qwen_image_extractor.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from einops import rearrange
|
| 4 |
+
from timm.models.vision_transformer import PatchEmbed
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def rotate_half(x):
|
| 9 |
+
"""Rotates half the hidden dims of the input."""
|
| 10 |
+
x1 = x[..., : x.shape[-1] // 2]
|
| 11 |
+
x2 = x[..., x.shape[-1] // 2 :]
|
| 12 |
+
return torch.cat((-x2, x1), dim=-1)
|
| 13 |
+
|
| 14 |
+
def apply_multimodal_rotary_pos_emb(
|
| 15 |
+
q: torch.Tensor,
|
| 16 |
+
k: torch.Tensor,
|
| 17 |
+
cos: torch.Tensor,
|
| 18 |
+
sin: torch.Tensor,
|
| 19 |
+
mrope_section: list[int],
|
| 20 |
+
unsqueeze_dim: int = 2
|
| 21 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 22 |
+
"""
|
| 23 |
+
[重写] 严格按照 Qwen2.5-VL 的实现。
|
| 24 |
+
|
| 25 |
+
将 3D RoPE (T, H, W) 应用于 query 和 key。
|
| 26 |
+
|
| 27 |
+
Args:
|
| 28 |
+
q (`torch.Tensor`): query (B, N_img, H, Hc)
|
| 29 |
+
k (`torch.Tensor`): key (B, N_img, H, Hc)
|
| 30 |
+
cos (`torch.Tensor`): cosine (3, B, N_img, Hc)
|
| 31 |
+
sin (`torch.Tensor`): sine (3, B, N_img, Hc)
|
| 32 |
+
mrope_section (`List[int]`):
|
| 33 |
+
T, H, W 的通道维度列表, e.g., [16, 24, 24].
|
| 34 |
+
注意: 2 * sum(mrope_section) 必须等于 Hc.
|
| 35 |
+
unsqueeze_dim (`int`, *optional*, defaults to 2):
|
| 36 |
+
为 (cos, sin) 增加的广播维度。
|
| 37 |
+
在我们的 Attention 模块中, q/k 形状为 (B, N_img, H, Hc),
|
| 38 |
+
因此我们使用 unsqueeze_dim=2 使 cos/sin 形状变为 (B, N_img, 1, Hc)
|
| 39 |
+
以便在 H (头数) 维度上广播。
|
| 40 |
+
(Qwen-VL 原始代码默认为 1, 因为它在 (B, H, N_img, Hc) 上操作)
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
split_sections = mrope_section * 2
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
cos_chunks = cos.split(split_sections, dim=-1)
|
| 47 |
+
sin_chunks = sin.split(split_sections, dim=-1)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
cos_emb = torch.cat(
|
| 52 |
+
[m[i % 3] for i, m in enumerate(cos_chunks)],
|
| 53 |
+
dim=-1
|
| 54 |
+
).unsqueeze(unsqueeze_dim)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
sin_emb = torch.cat(
|
| 59 |
+
[m[i % 3] for i, m in enumerate(sin_chunks)],
|
| 60 |
+
dim=-1
|
| 61 |
+
).unsqueeze(unsqueeze_dim)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
q_embed = (q * cos_emb) + (rotate_half(q) * sin_emb)
|
| 66 |
+
k_embed = (k * cos_emb) + (rotate_half(k) * sin_emb)
|
| 67 |
+
|
| 68 |
+
return q_embed, k_embed
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class Multimodal3DRotaryEmbedding(nn.Module):
|
| 73 |
+
"""
|
| 74 |
+
Qwen2.5-VL 文本模型使用的 3D RoPE 实现。
|
| 75 |
+
它接收 3D 坐标 (T, H, W) 并为每个坐标计算独立的 RoPE。
|
| 76 |
+
"""
|
| 77 |
+
inv_freq: torch.Tensor
|
| 78 |
+
|
| 79 |
+
def __init__(self, head_dim: int, theta: float = 10000.0, device=None):
|
| 80 |
+
"""
|
| 81 |
+
dim: 应该是 head_dim (Hc)
|
| 82 |
+
"""
|
| 83 |
+
super().__init__()
|
| 84 |
+
self.head_dim = head_dim
|
| 85 |
+
self.theta = theta
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
inv_freq = 1.0 / (
|
| 90 |
+
self.theta ** (torch.arange(0, self.head_dim, 2, dtype=torch.int64).to(device=device, dtype=torch.float) / self.head_dim)
|
| 91 |
+
)
|
| 92 |
+
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
| 93 |
+
self.attention_scaling = 1.0
|
| 94 |
+
|
| 95 |
+
def forward(self, x: torch.Tensor, position_ids: torch.Tensor):
|
| 96 |
+
"""
|
| 97 |
+
x: 任意张量,仅用于获取 device 和 dtype
|
| 98 |
+
position_ids: [3, B, N_img] (T, H, W 坐标)
|
| 99 |
+
"""
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
inv_freq_expanded = self.inv_freq[None, None, :, None].float().expand(3, position_ids.shape[1], -1, 1)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
position_ids_expanded = position_ids.float().unsqueeze(2)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
device_type = x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
|
| 109 |
+
with torch.autocast(device_type=device_type, enabled=False):
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
freqs = (inv_freq_expanded @ position_ids_expanded).transpose(2, 3)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
emb = torch.cat((freqs, freqs), dim=-1)
|
| 116 |
+
|
| 117 |
+
cos = emb.cos() * self.attention_scaling
|
| 118 |
+
sin = emb.sin() * self.attention_scaling
|
| 119 |
+
|
| 120 |
+
return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
class RMSNorm(nn.Module):
|
| 125 |
+
def __init__(self, hidden_size, eps=1e-6):
|
| 126 |
+
super().__init__()
|
| 127 |
+
self.weight = nn.Parameter(torch.ones(hidden_size))
|
| 128 |
+
self.variance_epsilon = eps
|
| 129 |
+
|
| 130 |
+
def forward(self, hidden_states):
|
| 131 |
+
input_dtype = hidden_states.dtype
|
| 132 |
+
hidden_states = hidden_states.to(torch.float32)
|
| 133 |
+
variance = hidden_states.pow(2).mean(-1, keepdim=True)
|
| 134 |
+
hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
|
| 135 |
+
return self.weight * hidden_states.to(input_dtype)
|
| 136 |
+
|
| 137 |
+
class FeedForward(nn.Module):
|
| 138 |
+
def __init__(
|
| 139 |
+
self,
|
| 140 |
+
dim: int,
|
| 141 |
+
hidden_dim: int,
|
| 142 |
+
):
|
| 143 |
+
super().__init__()
|
| 144 |
+
self.fc1 = nn.Linear(dim, hidden_dim)
|
| 145 |
+
self.act = nn.GELU()
|
| 146 |
+
self.fc2 = nn.Linear(hidden_dim, dim)
|
| 147 |
+
|
| 148 |
+
def forward(self, x):
|
| 149 |
+
x = self.fc1(x)
|
| 150 |
+
x = self.act(x)
|
| 151 |
+
x = self.fc2(x)
|
| 152 |
+
return x
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class Attention(nn.Module):
|
| 157 |
+
def __init__(
|
| 158 |
+
self,
|
| 159 |
+
dim: int,
|
| 160 |
+
num_heads: int = 8,
|
| 161 |
+
qkv_bias: bool = False,
|
| 162 |
+
qk_norm: bool = True,
|
| 163 |
+
attn_drop: float = 0.,
|
| 164 |
+
proj_drop: float = 0.,
|
| 165 |
+
norm_layer: nn.Module = RMSNorm,
|
| 166 |
+
) -> None:
|
| 167 |
+
super().__init__()
|
| 168 |
+
assert dim % num_heads == 0, "dim should be divisible by num_heads"
|
| 169 |
+
|
| 170 |
+
self.dim = dim
|
| 171 |
+
self.num_heads = num_heads
|
| 172 |
+
self.head_dim = dim // num_heads
|
| 173 |
+
self.scale = self.head_dim ** -0.5
|
| 174 |
+
|
| 175 |
+
self.mrope_section = [8,12,12]
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 179 |
+
self.q_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
|
| 180 |
+
self.k_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
|
| 181 |
+
self.attn_drop = attn_drop
|
| 182 |
+
self.proj = nn.Linear(dim, dim)
|
| 183 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 184 |
+
|
| 185 |
+
def forward(self, x: torch.Tensor, pos: tuple[torch.Tensor, torch.Tensor], mask=None) -> torch.Tensor:
|
| 186 |
+
"""
|
| 187 |
+
x: [B, N_total, C] (N_total = N_images + N_query)
|
| 188 |
+
pos: (cos, sin)
|
| 189 |
+
cos/sin 形状为 [3, B, N_total, Hc]
|
| 190 |
+
"""
|
| 191 |
+
B, N_total, C = x.shape
|
| 192 |
+
qkv = self.qkv(x).reshape(B, N_total, 3, self.num_heads, C // self.num_heads).permute(2, 0, 1, 3, 4)
|
| 193 |
+
q, k, v = qkv[0], qkv[1], qkv[2]
|
| 194 |
+
|
| 195 |
+
q = self.q_norm(q)
|
| 196 |
+
k = self.k_norm(k)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
cos, sin = pos
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
q, k = apply_multimodal_rotary_pos_emb(
|
| 204 |
+
q,
|
| 205 |
+
k,
|
| 206 |
+
cos=cos,
|
| 207 |
+
sin=sin,
|
| 208 |
+
mrope_section=self.mrope_section,
|
| 209 |
+
unsqueeze_dim=2
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
q = q.transpose(1, 2)
|
| 218 |
+
k = k.transpose(1, 2)
|
| 219 |
+
v = v.transpose(1, 2)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
x = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=mask, dropout_p=self.attn_drop)
|
| 228 |
+
|
| 229 |
+
x = x.transpose(1, 2).reshape(B, N_total, C)
|
| 230 |
+
x = self.proj(x)
|
| 231 |
+
x = self.proj_drop(x)
|
| 232 |
+
return x
|
| 233 |
+
|
| 234 |
+
class Block(nn.Module):
|
| 235 |
+
def __init__(self, hidden_size, num_heads, mlp_ratio=4.0):
|
| 236 |
+
super().__init__()
|
| 237 |
+
self.norm1 = RMSNorm(hidden_size, eps=1e-6)
|
| 238 |
+
|
| 239 |
+
self.attn = Attention(hidden_size, num_heads=num_heads, qkv_bias=False)
|
| 240 |
+
self.norm2 = RMSNorm(hidden_size, eps=1e-6)
|
| 241 |
+
self.mlp = FeedForward(hidden_size, int(hidden_size * mlp_ratio))
|
| 242 |
+
|
| 243 |
+
def forward(self, x, pos, mask=None):
|
| 244 |
+
|
| 245 |
+
residual = x
|
| 246 |
+
x = self.norm1(x)
|
| 247 |
+
x = self.attn(x, pos, mask=mask)
|
| 248 |
+
x = residual + x
|
| 249 |
+
|
| 250 |
+
residual = x
|
| 251 |
+
x = self.norm2(x)
|
| 252 |
+
x = self.mlp(x)
|
| 253 |
+
x = residual + x
|
| 254 |
+
return x
|
| 255 |
+
|
| 256 |
+
class QwenImageExtractor(nn.Module):
|
| 257 |
+
def __init__(self, hidden_size=1024,num_layers=16,num_heads=16,query_length=256,patch_size=2,in_chans=16,output_dim=3584):
|
| 258 |
+
super().__init__()
|
| 259 |
+
self.hidden_size = hidden_size
|
| 260 |
+
self.num_layers = num_layers
|
| 261 |
+
self.num_heads = num_heads
|
| 262 |
+
self.head_dim = hidden_size // num_heads
|
| 263 |
+
self.output_dim=output_dim
|
| 264 |
+
|
| 265 |
+
self.patch_embed = PatchEmbed(
|
| 266 |
+
img_size = None,
|
| 267 |
+
patch_size = patch_size,
|
| 268 |
+
in_chans = in_chans,
|
| 269 |
+
embed_dim = hidden_size,
|
| 270 |
+
strict_img_size=False
|
| 271 |
+
)
|
| 272 |
+
self.query_length=query_length
|
| 273 |
+
self.query = nn.Parameter(torch.randn(1, self.query_length, self.hidden_size))
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
self.blocks = nn.ModuleList([Block(hidden_size, num_heads) for _ in range(self.num_layers)])
|
| 278 |
+
self.norm2 = nn.LayerNorm(hidden_size)
|
| 279 |
+
self.output_proj = nn.Linear(hidden_size, output_dim)
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
self.rotary_pos_emb = Multimodal3DRotaryEmbedding(self.head_dim)
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
self.cached_pos_ids_shape = None
|
| 289 |
+
self.cached_pos_ids = None
|
| 290 |
+
|
| 291 |
+
def _compute_dynamic_pos_ids(self, H: int, W: int, device: torch.device) -> torch.Tensor:
|
| 292 |
+
"""
|
| 293 |
+
⭐️ (修改点 4)
|
| 294 |
+
根据输入的 H 和 W 动态计算 3D RoPE 的 Position IDs
|
| 295 |
+
返回: [3, L_total] (L_images + L_query)
|
| 296 |
+
"""
|
| 297 |
+
patch_size_h, patch_size_w = self.patch_embed.patch_size
|
| 298 |
+
grid_h = H // patch_size_h
|
| 299 |
+
grid_w = W // patch_size_w
|
| 300 |
+
|
| 301 |
+
current_shape = (grid_h, grid_w)
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
if current_shape == self.cached_pos_ids_shape:
|
| 305 |
+
if self.cached_pos_ids.device == device:
|
| 306 |
+
return self.cached_pos_ids
|
| 307 |
+
else:
|
| 308 |
+
|
| 309 |
+
self.cached_pos_ids = self.cached_pos_ids.to(device)
|
| 310 |
+
return self.cached_pos_ids
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
num_patches_per_image = grid_h * grid_w
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
hpos_ids = torch.arange(grid_h, device=device).unsqueeze(1).expand(-1, grid_w).flatten()
|
| 318 |
+
wpos_ids = torch.arange(grid_w, device=device).unsqueeze(0).expand(grid_h, -1).flatten()
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
tpos_ids_0 = torch.zeros(num_patches_per_image, device=device, dtype=torch.long)
|
| 323 |
+
|
| 324 |
+
pos_ids_0 = torch.stack([tpos_ids_0, hpos_ids, wpos_ids], dim=0)
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
tpos_ids_1 = torch.ones(num_patches_per_image, device=device, dtype=torch.long)
|
| 329 |
+
|
| 330 |
+
pos_ids_1 = torch.stack([tpos_ids_1, hpos_ids, wpos_ids], dim=0)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
img_pos_ids = torch.cat([pos_ids_0, pos_ids_1], dim=1)
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
start_idx = max(grid_h, grid_w)
|
| 339 |
+
query_indices = torch.arange(start_idx, start_idx + self.query_length, device=device, dtype=torch.long)
|
| 340 |
+
|
| 341 |
+
query_pos_ids = query_indices.unsqueeze(0).expand(3, -1)
|
| 342 |
+
|
| 343 |
+
all_pos_ids = torch.cat([img_pos_ids, query_pos_ids], dim=1)
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
self.cached_pos_ids_shape = current_shape
|
| 347 |
+
self.cached_pos_ids = all_pos_ids
|
| 348 |
+
|
| 349 |
+
return all_pos_ids
|
| 350 |
+
|
| 351 |
+
def forward(self, x):
|
| 352 |
+
"""
|
| 353 |
+
x: [B, 2, C, H, W]
|
| 354 |
+
"""
|
| 355 |
+
B, N, C, H, W = x.shape
|
| 356 |
+
assert N == 2, "This model is hardcoded for N=2"
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
pos_ids = self._compute_dynamic_pos_ids(H, W, device=x.device)
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
pos_ids_batch = pos_ids.unsqueeze(1).expand(-1, B, -1)
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
pos_tuple = self.rotary_pos_emb(x, pos_ids_batch)
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
x = rearrange(x, "B N C H W -> (B N) C H W")
|
| 373 |
+
x = self.patch_embed(x)
|
| 374 |
+
x = rearrange(x, "(B N) l d -> B (N l) d", B=B, N=N)
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
x = torch.cat([x, self.query.repeat(B, 1, 1)], dim=1)
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
if x.shape[1] != pos_ids.shape[1]:
|
| 386 |
+
raise ValueError(
|
| 387 |
+
f"Total sequence length mismatch. "
|
| 388 |
+
f"Input tensor 'x' length is {x.shape[1]}, "
|
| 389 |
+
f"but calculated 3D RoPE length is {pos_ids.shape[1]}. "
|
| 390 |
+
f"Check H/W ({H}/{W}) vs patch_size ({self.patch_embed.patch_size})."
|
| 391 |
+
)
|
| 392 |
+
|
| 393 |
+
for block in self.blocks:
|
| 394 |
+
|
| 395 |
+
x = block(x, pos=pos_tuple)
|
| 396 |
+
x=x[:, -self.query_length:, :]
|
| 397 |
+
|
| 398 |
+
x = self.norm2(x)
|
| 399 |
+
x = self.output_proj(x)
|
| 400 |
+
|
| 401 |
+
return x
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
|
diffsynth/models/qwen_image_learnable_query.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
|
| 4 |
+
class QwenImageLearnableQuery(nn.Module):
|
| 5 |
+
def __init__(self, query_length: int = 64, hidden_state_dim: int = 3584, initializer_range: float = 0.02):
|
| 6 |
+
super().__init__()
|
| 7 |
+
self.query_length = query_length
|
| 8 |
+
self.hidden_state_dim = hidden_state_dim
|
| 9 |
+
self.learnable_query = nn.Parameter(torch.randn(query_length, hidden_state_dim) * initializer_range)
|
| 10 |
+
|
| 11 |
+
def forward(self, batch_size: int):
|
| 12 |
+
return self.learnable_query.unsqueeze(0).expand(batch_size, -1, -1)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
|
diffsynth/models/qwen_image_mae.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
|
| 4 |
+
from typing import Optional
|
| 5 |
+
|
| 6 |
+
class QwenImageMAE(nn.Module):
|
| 7 |
+
def __init__(
|
| 8 |
+
self,
|
| 9 |
+
):
|
| 10 |
+
super().__init__()
|
| 11 |
+
from transformers import ViTMAEConfig, ViTMAEModel
|
| 12 |
+
|
| 13 |
+
config=ViTMAEConfig(**{
|
| 14 |
+
"architectures": [
|
| 15 |
+
"ViTMAEForPreTraining"
|
| 16 |
+
],
|
| 17 |
+
"attention_probs_dropout_prob": 0.0,
|
| 18 |
+
"decoder_hidden_size": 512,
|
| 19 |
+
"decoder_intermediate_size": 2048,
|
| 20 |
+
"decoder_num_attention_heads": 16,
|
| 21 |
+
"decoder_num_hidden_layers": 8,
|
| 22 |
+
"hidden_act": "gelu",
|
| 23 |
+
"hidden_dropout_prob": 0.0,
|
| 24 |
+
"hidden_size": 1024,
|
| 25 |
+
"image_size": 224,
|
| 26 |
+
"initializer_range": 0.02,
|
| 27 |
+
"intermediate_size": 4096,
|
| 28 |
+
"layer_norm_eps": 1e-12,
|
| 29 |
+
"mask_ratio": 0.0,
|
| 30 |
+
"model_type": "vit_mae",
|
| 31 |
+
"norm_pix_loss": False,
|
| 32 |
+
"num_attention_heads": 16,
|
| 33 |
+
"num_channels": 3,
|
| 34 |
+
"num_hidden_layers": 24,
|
| 35 |
+
"patch_size": 16,
|
| 36 |
+
"qkv_bias": True,
|
| 37 |
+
"torch_dtype": "bfloat16",
|
| 38 |
+
"attn_implementation": "sdpa"
|
| 39 |
+
}
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
self.model = ViTMAEModel(config)
|
| 43 |
+
self.config=config
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def forward(self, pixel_values):
|
| 47 |
+
outputs=self.model(pixel_values,interpolate_pos_encoding=True)
|
| 48 |
+
return outputs.last_hidden_state
|
| 49 |
+
|
| 50 |
+
def new_forward(self, pixel_values,sample1_shapes):
|
| 51 |
+
outputs=self.model.new_forward(pixel_values,sample1_shapes,interpolate_pos_encoding=True)
|
| 52 |
+
return outputs.last_hidden_state
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
|
diffsynth/models/qwen_image_rformer.py
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers.models.vit.modeling_vit import (
|
| 2 |
+
ViTConfig,
|
| 3 |
+
ViTPreTrainedModel,
|
| 4 |
+
ViTEncoder
|
| 5 |
+
)
|
| 6 |
+
from torch import nn
|
| 7 |
+
import torch
|
| 8 |
+
from typing import Optional, Dict, List, Tuple, Union
|
| 9 |
+
from transformers.modeling_outputs import BaseModelOutputWithPooling
|
| 10 |
+
|
| 11 |
+
class RFormerEmbeddings(nn.Module):
|
| 12 |
+
def __init__(self, config: ViTConfig) -> None:
|
| 13 |
+
super().__init__()
|
| 14 |
+
|
| 15 |
+
query_num = config.query_num
|
| 16 |
+
self.query_num = query_num
|
| 17 |
+
self.latent_motion_token = nn.Parameter(torch.zeros(1, query_num, config.hidden_size))
|
| 18 |
+
self.sep_token = nn.Parameter(torch.zeros(1, 1, config.hidden_size))
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
self.projection = nn.Linear(config.input_hidden_size, config.hidden_size, bias=True)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
self.position_embeddings = nn.Parameter(torch.randn(1, config.num_patches*2 + 1 + query_num, config.hidden_size))
|
| 25 |
+
self.token_type_embeddings = nn.Parameter(torch.randn(2, config.hidden_size))
|
| 26 |
+
self.dropout = nn.Dropout(config.hidden_dropout_prob)
|
| 27 |
+
self.config = config
|
| 28 |
+
|
| 29 |
+
if hasattr(config, "legacy"):
|
| 30 |
+
self.legacy = config.legacy
|
| 31 |
+
else:
|
| 32 |
+
self.legacy = True
|
| 33 |
+
|
| 34 |
+
def forward(
|
| 35 |
+
self,
|
| 36 |
+
cond_hidden_states: torch.Tensor,
|
| 37 |
+
target_hidden_states: torch.Tensor,
|
| 38 |
+
) -> torch.Tensor:
|
| 39 |
+
batch_size, per_seq_length = cond_hidden_states.shape[:2]
|
| 40 |
+
|
| 41 |
+
cond_embeddings = self.projection(cond_hidden_states)
|
| 42 |
+
|
| 43 |
+
latent_motion_tokens = self.latent_motion_token.expand(batch_size, -1, -1)
|
| 44 |
+
sep_tokens = self.sep_token.expand(batch_size, -1, -1)
|
| 45 |
+
cond_embeddings = torch.cat((latent_motion_tokens, cond_embeddings, sep_tokens), dim=1)
|
| 46 |
+
|
| 47 |
+
target_embeddings = self.projection(target_hidden_states)
|
| 48 |
+
embeddings = torch.cat((cond_embeddings, target_embeddings), dim=1)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
embeddings = embeddings + self.position_embeddings
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
cond_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, per_seq_length + self.query_num + 1, -1)
|
| 55 |
+
if self.legacy:
|
| 56 |
+
target_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, per_seq_length, -1)
|
| 57 |
+
else:
|
| 58 |
+
target_token_type_embeddings = self.token_type_embeddings[1].expand(batch_size, per_seq_length, -1)
|
| 59 |
+
token_type_embeddings = torch.cat((cond_token_type_embeddings, target_token_type_embeddings), dim=1)
|
| 60 |
+
embeddings = embeddings + token_type_embeddings
|
| 61 |
+
|
| 62 |
+
embeddings = self.dropout(embeddings)
|
| 63 |
+
|
| 64 |
+
return embeddings
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class RFormer2DEmbeddings(nn.Module):
|
| 68 |
+
def __init__(self, config: ViTConfig) -> None:
|
| 69 |
+
super().__init__()
|
| 70 |
+
query_num = config.query_num
|
| 71 |
+
self.query_num = query_num
|
| 72 |
+
self.latent_motion_token = nn.Parameter(torch.zeros(1, query_num, config.hidden_size))
|
| 73 |
+
self.sep_token = nn.Parameter(torch.zeros(1, 1, config.hidden_size))
|
| 74 |
+
self.projection = nn.Linear(config.input_hidden_size, config.hidden_size, bias=True)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
self.patch_size = 16
|
| 78 |
+
self.max_side_len = 280
|
| 79 |
+
self.d_half = config.hidden_size // 2
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
self.pos_emb_x_cond = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
|
| 83 |
+
self.pos_emb_y_cond = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
|
| 84 |
+
|
| 85 |
+
self.cls_pos_emb_cond = nn.Parameter(torch.randn(1, 1, config.hidden_size))
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
self.pos_emb_x_target = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
|
| 89 |
+
self.pos_emb_y_target = nn.Parameter(torch.randn(1, self.max_side_len, self.d_half))
|
| 90 |
+
|
| 91 |
+
self.cls_pos_emb_target = nn.Parameter(torch.randn(1, 1, config.hidden_size))
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
self.query_pos_embedding = nn.Parameter(torch.randn(1, query_num, config.hidden_size))
|
| 95 |
+
self.sep_pos_embedding = nn.Parameter(torch.randn(1, 1, config.hidden_size))
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
self.token_type_embeddings = nn.Parameter(torch.randn(2, config.hidden_size))
|
| 99 |
+
self.dropout = nn.Dropout(config.hidden_dropout_prob)
|
| 100 |
+
self.config = config
|
| 101 |
+
if hasattr(config, "legacy"):
|
| 102 |
+
self.legacy = config.legacy
|
| 103 |
+
else:
|
| 104 |
+
self.legacy = True
|
| 105 |
+
|
| 106 |
+
def _get_grid_embedding(self, pos_emb_x, pos_emb_y, h, w):
|
| 107 |
+
"""辅助函数:生成纯粹的 2D 网格位置编码 (不含 CLS)"""
|
| 108 |
+
|
| 109 |
+
y_emb = pos_emb_y[:, :h, :]
|
| 110 |
+
x_emb = pos_emb_x[:, :w, :]
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
y_grid = y_emb.unsqueeze(2).expand(-1, -1, w, -1)
|
| 114 |
+
x_grid = x_emb.unsqueeze(1).expand(-1, h, -1, -1)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
grid_emb_2d = torch.cat([y_grid, x_grid], dim=-1)
|
| 118 |
+
return grid_emb_2d[0].flatten(0, 1)
|
| 119 |
+
|
| 120 |
+
def forward(
|
| 121 |
+
self,
|
| 122 |
+
cond_hidden_states: torch.Tensor,
|
| 123 |
+
target_hidden_states: torch.Tensor,
|
| 124 |
+
sample1_shapes: List[torch.Tensor] = None
|
| 125 |
+
) -> torch.Tensor:
|
| 126 |
+
batch_size = cond_hidden_states.shape[0]
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
cond_embeddings = self.projection(cond_hidden_states)
|
| 130 |
+
target_embeddings = self.projection(target_hidden_states)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
latent_motion_tokens = self.latent_motion_token.expand(batch_size, -1, -1)
|
| 135 |
+
sep_tokens = self.sep_token.expand(batch_size, -1, -1)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
cond_part = torch.cat((latent_motion_tokens, cond_embeddings, sep_tokens), dim=1)
|
| 139 |
+
embeddings = torch.cat((cond_part, target_embeddings), dim=1)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
batch_pixel_h = sample1_shapes[0]
|
| 144 |
+
batch_pixel_w = sample1_shapes[1]
|
| 145 |
+
|
| 146 |
+
batch_pos_embeddings = []
|
| 147 |
+
|
| 148 |
+
for b in range(batch_size):
|
| 149 |
+
|
| 150 |
+
pixel_h = int(batch_pixel_h[b].item())
|
| 151 |
+
pixel_w = int(batch_pixel_w[b].item())
|
| 152 |
+
h = pixel_h // self.patch_size
|
| 153 |
+
w = pixel_w // self.patch_size
|
| 154 |
+
num_patches = h * w
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
len_cond = cond_hidden_states.shape[1]
|
| 160 |
+
has_cls_cond = (len_cond == num_patches + 1)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
grid_cond = self._get_grid_embedding(self.pos_emb_x_cond, self.pos_emb_y_cond, h, w)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
if has_cls_cond:
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
pos_emb_cond = torch.cat([self.cls_pos_emb_cond[0], grid_cond], dim=0)
|
| 170 |
+
else:
|
| 171 |
+
pos_emb_cond = grid_cond
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
len_target = target_hidden_states.shape[1]
|
| 176 |
+
has_cls_target = (len_target == num_patches + 1)
|
| 177 |
+
|
| 178 |
+
grid_target = self._get_grid_embedding(self.pos_emb_x_target, self.pos_emb_y_target, h, w)
|
| 179 |
+
|
| 180 |
+
if has_cls_target:
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
pos_emb_target = torch.cat([self.cls_pos_emb_target[0], grid_target], dim=0)
|
| 184 |
+
else:
|
| 185 |
+
pos_emb_target = grid_target
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
full_pos_emb = torch.cat([
|
| 191 |
+
self.query_pos_embedding[0],
|
| 192 |
+
pos_emb_cond,
|
| 193 |
+
self.sep_pos_embedding[0],
|
| 194 |
+
pos_emb_target
|
| 195 |
+
], dim=0)
|
| 196 |
+
|
| 197 |
+
batch_pos_embeddings.append(full_pos_emb)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
pos_embeddings = torch.stack(batch_pos_embeddings).to(embeddings.device)
|
| 201 |
+
embeddings = embeddings + pos_embeddings
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
len_cond_total = latent_motion_tokens.shape[1] + cond_embeddings.shape[1] + sep_tokens.shape[1]
|
| 206 |
+
len_target_total = target_embeddings.shape[1]
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
cond_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, len_cond_total, -1)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
if self.legacy:
|
| 213 |
+
target_token_type_embeddings = self.token_type_embeddings[0].expand(batch_size, len_target_total, -1)
|
| 214 |
+
else:
|
| 215 |
+
target_token_type_embeddings = self.token_type_embeddings[1].expand(batch_size, len_target_total, -1)
|
| 216 |
+
|
| 217 |
+
token_type_embeddings = torch.cat((cond_token_type_embeddings, target_token_type_embeddings), dim=1)
|
| 218 |
+
embeddings = embeddings + token_type_embeddings
|
| 219 |
+
|
| 220 |
+
embeddings = self.dropout(embeddings)
|
| 221 |
+
|
| 222 |
+
return embeddings
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
class ViTPooler(nn.Module):
|
| 226 |
+
def __init__(self, config: ViTConfig):
|
| 227 |
+
super().__init__()
|
| 228 |
+
self.dense = nn.Linear(config.hidden_size, config.hidden_size)
|
| 229 |
+
self.activation = nn.Tanh()
|
| 230 |
+
|
| 231 |
+
def forward(self, hidden_states):
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
first_token_tensor = hidden_states[:, 0]
|
| 235 |
+
pooled_output = self.dense(first_token_tensor)
|
| 236 |
+
pooled_output = self.activation(pooled_output)
|
| 237 |
+
return pooled_output
|
| 238 |
+
|
| 239 |
+
class RFormer(ViTPreTrainedModel):
|
| 240 |
+
def __init__(self, add_pooling_layer: bool = False):
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
config = ViTConfig(
|
| 244 |
+
hidden_size=768,
|
| 245 |
+
num_hidden_layers=4,
|
| 246 |
+
num_attention_heads=12,
|
| 247 |
+
intermediate_size=3072,
|
| 248 |
+
hidden_act="gelu",
|
| 249 |
+
hidden_dropout_prob=0.0,
|
| 250 |
+
attention_probs_dropout_prob=0.0,
|
| 251 |
+
initializer_range=0.02,
|
| 252 |
+
layer_norm_eps=1e-12,
|
| 253 |
+
qkv_bias=True,
|
| 254 |
+
model_type="vit",
|
| 255 |
+
|
| 256 |
+
query_num=8,
|
| 257 |
+
input_hidden_size=1024,
|
| 258 |
+
num_patches=4097,
|
| 259 |
+
legacy=True
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
super().__init__(config)
|
| 264 |
+
self.config = config
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
self.query_num = config.query_num
|
| 268 |
+
self.embeddings = RFormerEmbeddings(config)
|
| 269 |
+
self.encoder = ViTEncoder(config)
|
| 270 |
+
|
| 271 |
+
self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
|
| 272 |
+
self.pooler = ViTPooler(config) if add_pooling_layer else None
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
self.post_init()
|
| 276 |
+
|
| 277 |
+
def _init_weights(self, module: Union[nn.Linear, nn.Conv2d, nn.LayerNorm]) -> None:
|
| 278 |
+
"""Initialize the weights"""
|
| 279 |
+
if isinstance(module, (nn.Linear, nn.Conv2d)):
|
| 280 |
+
module.weight.data = nn.init.trunc_normal_(
|
| 281 |
+
module.weight.data.to(torch.float32), mean=0.0, std=self.config.initializer_range
|
| 282 |
+
).to(module.weight.dtype)
|
| 283 |
+
if module.bias is not None:
|
| 284 |
+
module.bias.data.zero_()
|
| 285 |
+
elif isinstance(module, nn.LayerNorm):
|
| 286 |
+
module.bias.data.zero_()
|
| 287 |
+
module.weight.data.fill_(1.0)
|
| 288 |
+
elif isinstance(module, RFormerEmbeddings):
|
| 289 |
+
module.position_embeddings.data = nn.init.trunc_normal_(
|
| 290 |
+
module.position_embeddings.data.to(torch.float32),
|
| 291 |
+
mean=0.0,
|
| 292 |
+
std=self.config.initializer_range,
|
| 293 |
+
).to(module.position_embeddings.dtype)
|
| 294 |
+
|
| 295 |
+
module.token_type_embeddings.data = nn.init.trunc_normal_(
|
| 296 |
+
module.token_type_embeddings.data.to(torch.float32),
|
| 297 |
+
mean=0.0,
|
| 298 |
+
std=self.config.initializer_range,
|
| 299 |
+
).to(module.token_type_embeddings.dtype)
|
| 300 |
+
|
| 301 |
+
module.latent_motion_token.data = nn.init.trunc_normal_(
|
| 302 |
+
module.latent_motion_token.data.to(torch.float32),
|
| 303 |
+
mean=0.0,
|
| 304 |
+
std=self.config.initializer_range,
|
| 305 |
+
).to(module.latent_motion_token.dtype)
|
| 306 |
+
|
| 307 |
+
module.sep_token.data = nn.init.trunc_normal_(
|
| 308 |
+
module.sep_token.data.to(torch.float32),
|
| 309 |
+
mean=0.0,
|
| 310 |
+
std=self.config.initializer_range,
|
| 311 |
+
).to(module.sep_token.dtype)
|
| 312 |
+
|
| 313 |
+
def _prune_heads(self, heads_to_prune: Dict[int, List[int]]) -> None:
|
| 314 |
+
for layer, heads in heads_to_prune.items():
|
| 315 |
+
self.encoder.layer[layer].attention.prune_heads(heads)
|
| 316 |
+
|
| 317 |
+
def forward(
|
| 318 |
+
self,
|
| 319 |
+
cond_hidden_states: torch.Tensor,
|
| 320 |
+
target_hidden_states: torch.Tensor,
|
| 321 |
+
head_mask: Optional[torch.Tensor] = None,
|
| 322 |
+
output_attentions: Optional[bool] = None,
|
| 323 |
+
output_hidden_states: Optional[bool] = None,
|
| 324 |
+
return_dict: Optional[bool] = None,
|
| 325 |
+
) -> Union[Tuple, BaseModelOutputWithPooling]:
|
| 326 |
+
|
| 327 |
+
output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
|
| 328 |
+
output_hidden_states = (
|
| 329 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 330 |
+
)
|
| 331 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 332 |
+
|
| 333 |
+
head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers)
|
| 334 |
+
|
| 335 |
+
embedding_output = self.embeddings(
|
| 336 |
+
cond_hidden_states=cond_hidden_states,
|
| 337 |
+
target_hidden_states=target_hidden_states
|
| 338 |
+
)
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
encoder_outputs = self.encoder(
|
| 349 |
+
embedding_output,
|
| 350 |
+
head_mask=head_mask
|
| 351 |
+
)
|
| 352 |
+
sequence_output = encoder_outputs[0]
|
| 353 |
+
sequence_output = self.layernorm(sequence_output)
|
| 354 |
+
pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
|
| 355 |
+
|
| 356 |
+
if not return_dict:
|
| 357 |
+
head_outputs = (sequence_output, pooled_output) if pooled_output is not None else (sequence_output,)
|
| 358 |
+
return head_outputs + encoder_outputs[1:]
|
| 359 |
+
|
| 360 |
+
return BaseModelOutputWithPooling(
|
| 361 |
+
last_hidden_state=sequence_output,
|
| 362 |
+
pooler_output=pooled_output,
|
| 363 |
+
hidden_states=encoder_outputs.hidden_states,
|
| 364 |
+
attentions=encoder_outputs.attentions,
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
class RFormer2D(ViTPreTrainedModel):
|
| 368 |
+
def __init__(self, add_pooling_layer: bool = False):
|
| 369 |
+
|
| 370 |
+
config = ViTConfig(
|
| 371 |
+
attn_implementation="sdpa",
|
| 372 |
+
dtype="bfloat16",
|
| 373 |
+
output_attentions=False,
|
| 374 |
+
output_hidden_states=False,
|
| 375 |
+
return_dict=True,
|
| 376 |
+
hidden_size=768,
|
| 377 |
+
num_hidden_layers=4,
|
| 378 |
+
num_attention_heads=12,
|
| 379 |
+
intermediate_size=3072,
|
| 380 |
+
hidden_act="gelu",
|
| 381 |
+
hidden_dropout_prob=0.0,
|
| 382 |
+
attention_probs_dropout_prob=0.0,
|
| 383 |
+
initializer_range=0.02,
|
| 384 |
+
layer_norm_eps=1e-12,
|
| 385 |
+
qkv_bias=True,
|
| 386 |
+
model_type="vit",
|
| 387 |
+
|
| 388 |
+
query_num=8,
|
| 389 |
+
input_hidden_size=1024,
|
| 390 |
+
num_patches=16801,
|
| 391 |
+
legacy=True
|
| 392 |
+
)
|
| 393 |
+
|
| 394 |
+
super().__init__(config)
|
| 395 |
+
self.config = config
|
| 396 |
+
self.query_num = config.query_num
|
| 397 |
+
self.embeddings = RFormer2DEmbeddings(config)
|
| 398 |
+
self.encoder = ViTEncoder(config)
|
| 399 |
+
|
| 400 |
+
self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
|
| 401 |
+
self.pooler = ViTPooler(config) if add_pooling_layer else None
|
| 402 |
+
|
| 403 |
+
self.post_init()
|
| 404 |
+
|
| 405 |
+
def _init_weights(self, module: Union[nn.Linear, nn.Conv2d, nn.LayerNorm]) -> None:
|
| 406 |
+
"""Initialize the weights"""
|
| 407 |
+
if isinstance(module, (nn.Linear, nn.Conv2d)):
|
| 408 |
+
module.weight.data = nn.init.trunc_normal_(
|
| 409 |
+
module.weight.data.to(torch.float32), mean=0.0, std=self.config.initializer_range
|
| 410 |
+
).to(module.weight.dtype)
|
| 411 |
+
if module.bias is not None:
|
| 412 |
+
module.bias.data.zero_()
|
| 413 |
+
elif isinstance(module, nn.LayerNorm):
|
| 414 |
+
module.bias.data.zero_()
|
| 415 |
+
module.weight.data.fill_(1.0)
|
| 416 |
+
elif isinstance(module, RFormer2DEmbeddings):
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
def init_param(param):
|
| 421 |
+
param.data = nn.init.trunc_normal_(
|
| 422 |
+
param.data.to(torch.float32),
|
| 423 |
+
mean=0.0,
|
| 424 |
+
std=self.config.initializer_range,
|
| 425 |
+
).to(param.dtype)
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
init_param(module.pos_emb_x_cond)
|
| 429 |
+
init_param(module.pos_emb_y_cond)
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
init_param(module.pos_emb_x_target)
|
| 433 |
+
init_param(module.pos_emb_y_target)
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
init_param(module.query_pos_embedding)
|
| 437 |
+
init_param(module.sep_pos_embedding)
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
init_param(module.token_type_embeddings)
|
| 441 |
+
init_param(module.latent_motion_token)
|
| 442 |
+
init_param(module.sep_token)
|
| 443 |
+
|
| 444 |
+
init_param( module.cls_pos_emb_cond)
|
| 445 |
+
init_param( module.cls_pos_emb_target)
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def _prune_heads(self, heads_to_prune: Dict[int, List[int]]) -> None:
|
| 449 |
+
for layer, heads in heads_to_prune.items():
|
| 450 |
+
self.encoder.layer[layer].attention.prune_heads(heads)
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def forward(
|
| 454 |
+
self,
|
| 455 |
+
cond_hidden_states: torch.Tensor,
|
| 456 |
+
target_hidden_states: torch.Tensor,
|
| 457 |
+
sample1_shapes: List[torch.Tensor],
|
| 458 |
+
head_mask: Optional[torch.Tensor] = None,
|
| 459 |
+
output_attentions: Optional[bool] = None,
|
| 460 |
+
output_hidden_states: Optional[bool] = None,
|
| 461 |
+
return_dict: Optional[bool] = None,
|
| 462 |
+
) -> Union[Tuple, BaseModelOutputWithPooling]:
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers)
|
| 469 |
+
|
| 470 |
+
embedding_output = self.embeddings(
|
| 471 |
+
cond_hidden_states=cond_hidden_states,
|
| 472 |
+
target_hidden_states=target_hidden_states,
|
| 473 |
+
sample1_shapes=sample1_shapes
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
encoder_outputs = self.encoder(
|
| 477 |
+
embedding_output,
|
| 478 |
+
head_mask=head_mask
|
| 479 |
+
)
|
| 480 |
+
sequence_output = encoder_outputs.last_hidden_state
|
| 481 |
+
sequence_output = self.layernorm(sequence_output)
|
| 482 |
+
pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
return BaseModelOutputWithPooling(
|
| 489 |
+
last_hidden_state=sequence_output,
|
| 490 |
+
pooler_output=pooled_output,
|
| 491 |
+
hidden_states=encoder_outputs.hidden_states,
|
| 492 |
+
attentions=encoder_outputs.attentions,
|
| 493 |
+
)
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
|
diffsynth/models/qwen_image_text_encoder.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from typing import Optional, Union
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class QwenImageTextEncoder(torch.nn.Module):
|
| 6 |
+
def __init__(self):
|
| 7 |
+
super().__init__()
|
| 8 |
+
from transformers import Qwen2_5_VLConfig, Qwen2_5_VLModel
|
| 9 |
+
config = Qwen2_5_VLConfig(**{
|
| 10 |
+
"architectures": [
|
| 11 |
+
"Qwen2_5_VLForConditionalGeneration"
|
| 12 |
+
],
|
| 13 |
+
"attention_dropout": 0.0,
|
| 14 |
+
"bos_token_id": 151643,
|
| 15 |
+
"eos_token_id": 151645,
|
| 16 |
+
"hidden_act": "silu",
|
| 17 |
+
"hidden_size": 3584,
|
| 18 |
+
"image_token_id": 151655,
|
| 19 |
+
"initializer_range": 0.02,
|
| 20 |
+
"intermediate_size": 18944,
|
| 21 |
+
"max_position_embeddings": 128000,
|
| 22 |
+
"max_window_layers": 28,
|
| 23 |
+
"model_type": "qwen2_5_vl",
|
| 24 |
+
"num_attention_heads": 28,
|
| 25 |
+
"num_hidden_layers": 28,
|
| 26 |
+
"num_key_value_heads": 4,
|
| 27 |
+
"rms_norm_eps": 1e-06,
|
| 28 |
+
"rope_scaling": {
|
| 29 |
+
"mrope_section": [
|
| 30 |
+
16,
|
| 31 |
+
24,
|
| 32 |
+
24
|
| 33 |
+
],
|
| 34 |
+
"rope_type": "default",
|
| 35 |
+
"type": "default"
|
| 36 |
+
},
|
| 37 |
+
"rope_theta": 1000000.0,
|
| 38 |
+
"sliding_window": 32768,
|
| 39 |
+
"text_config": {
|
| 40 |
+
"architectures": [
|
| 41 |
+
"Qwen2_5_VLForConditionalGeneration"
|
| 42 |
+
],
|
| 43 |
+
"attention_dropout": 0.0,
|
| 44 |
+
"bos_token_id": 151643,
|
| 45 |
+
"eos_token_id": 151645,
|
| 46 |
+
"hidden_act": "silu",
|
| 47 |
+
"hidden_size": 3584,
|
| 48 |
+
"image_token_id": None,
|
| 49 |
+
"initializer_range": 0.02,
|
| 50 |
+
"intermediate_size": 18944,
|
| 51 |
+
"layer_types": [
|
| 52 |
+
"full_attention",
|
| 53 |
+
"full_attention",
|
| 54 |
+
"full_attention",
|
| 55 |
+
"full_attention",
|
| 56 |
+
"full_attention",
|
| 57 |
+
"full_attention",
|
| 58 |
+
"full_attention",
|
| 59 |
+
"full_attention",
|
| 60 |
+
"full_attention",
|
| 61 |
+
"full_attention",
|
| 62 |
+
"full_attention",
|
| 63 |
+
"full_attention",
|
| 64 |
+
"full_attention",
|
| 65 |
+
"full_attention",
|
| 66 |
+
"full_attention",
|
| 67 |
+
"full_attention",
|
| 68 |
+
"full_attention",
|
| 69 |
+
"full_attention",
|
| 70 |
+
"full_attention",
|
| 71 |
+
"full_attention",
|
| 72 |
+
"full_attention",
|
| 73 |
+
"full_attention",
|
| 74 |
+
"full_attention",
|
| 75 |
+
"full_attention",
|
| 76 |
+
"full_attention",
|
| 77 |
+
"full_attention",
|
| 78 |
+
"full_attention",
|
| 79 |
+
"full_attention"
|
| 80 |
+
],
|
| 81 |
+
"max_position_embeddings": 128000,
|
| 82 |
+
"max_window_layers": 28,
|
| 83 |
+
"model_type": "qwen2_5_vl_text",
|
| 84 |
+
"num_attention_heads": 28,
|
| 85 |
+
"num_hidden_layers": 28,
|
| 86 |
+
"num_key_value_heads": 4,
|
| 87 |
+
"rms_norm_eps": 1e-06,
|
| 88 |
+
"rope_scaling": {
|
| 89 |
+
"mrope_section": [
|
| 90 |
+
16,
|
| 91 |
+
24,
|
| 92 |
+
24
|
| 93 |
+
],
|
| 94 |
+
"rope_type": "default",
|
| 95 |
+
"type": "default"
|
| 96 |
+
},
|
| 97 |
+
"rope_theta": 1000000.0,
|
| 98 |
+
"sliding_window": None,
|
| 99 |
+
"attn_implementation": "sdpa",
|
| 100 |
+
"torch_dtype": "bfloat16",
|
| 101 |
+
"use_cache": True,
|
| 102 |
+
"use_sliding_window": False,
|
| 103 |
+
"video_token_id": None,
|
| 104 |
+
"vision_end_token_id": 151653,
|
| 105 |
+
"vision_start_token_id": 151652,
|
| 106 |
+
"vision_token_id": 151654,
|
| 107 |
+
"vocab_size": 152064
|
| 108 |
+
},
|
| 109 |
+
"tie_word_embeddings": False,
|
| 110 |
+
"attn_implementation": "sdpa",
|
| 111 |
+
"torch_dtype": "bfloat16",
|
| 112 |
+
"use_cache": True,
|
| 113 |
+
"use_sliding_window": False,
|
| 114 |
+
"video_token_id": 151656,
|
| 115 |
+
"vision_config": {
|
| 116 |
+
"depth": 32,
|
| 117 |
+
"fullatt_block_indexes": [
|
| 118 |
+
7,
|
| 119 |
+
15,
|
| 120 |
+
23,
|
| 121 |
+
31
|
| 122 |
+
],
|
| 123 |
+
"hidden_act": "silu",
|
| 124 |
+
"hidden_size": 1280,
|
| 125 |
+
"in_channels": 3,
|
| 126 |
+
"in_chans": 3,
|
| 127 |
+
"initializer_range": 0.02,
|
| 128 |
+
"intermediate_size": 3420,
|
| 129 |
+
"model_type": "qwen2_5_vl",
|
| 130 |
+
"num_heads": 16,
|
| 131 |
+
"out_hidden_size": 3584,
|
| 132 |
+
"patch_size": 14,
|
| 133 |
+
"spatial_merge_size": 2,
|
| 134 |
+
"spatial_patch_size": 14,
|
| 135 |
+
"temporal_patch_size": 2,
|
| 136 |
+
"tokens_per_second": 2,
|
| 137 |
+
"torch_dtype": "bfloat16",
|
| 138 |
+
"attn_implementation": "sdpa",
|
| 139 |
+
"window_size": 112
|
| 140 |
+
},
|
| 141 |
+
"vision_end_token_id": 151653,
|
| 142 |
+
"vision_start_token_id": 151652,
|
| 143 |
+
"vision_token_id": 151654,
|
| 144 |
+
"vocab_size": 152064
|
| 145 |
+
})
|
| 146 |
+
self.model = Qwen2_5_VLModel(config)
|
| 147 |
+
self.lm_head = torch.nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
|
| 148 |
+
self.config = config
|
| 149 |
+
|
| 150 |
+
def forward(
|
| 151 |
+
self,
|
| 152 |
+
input_ids: torch.LongTensor = None,
|
| 153 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 154 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 155 |
+
past_key_values = None,
|
| 156 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 157 |
+
labels: Optional[torch.LongTensor] = None,
|
| 158 |
+
use_cache: Optional[bool] = None,
|
| 159 |
+
output_attentions: Optional[bool] = None,
|
| 160 |
+
output_hidden_states: Optional[bool] = None,
|
| 161 |
+
pixel_values: Optional[torch.Tensor] = None,
|
| 162 |
+
pixel_values_videos: Optional[torch.FloatTensor] = None,
|
| 163 |
+
image_grid_thw: Optional[torch.LongTensor] = None,
|
| 164 |
+
video_grid_thw: Optional[torch.LongTensor] = None,
|
| 165 |
+
rope_deltas: Optional[torch.LongTensor] = None,
|
| 166 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 167 |
+
second_per_grid_ts: Optional[torch.Tensor] = None,
|
| 168 |
+
logits_to_keep: Union[int, torch.Tensor] = 0,
|
| 169 |
+
**kwargs,
|
| 170 |
+
):
|
| 171 |
+
output_attentions = False
|
| 172 |
+
output_hidden_states = True
|
| 173 |
+
|
| 174 |
+
outputs = self.model(
|
| 175 |
+
input_ids=input_ids,
|
| 176 |
+
pixel_values=pixel_values,
|
| 177 |
+
pixel_values_videos=pixel_values_videos,
|
| 178 |
+
image_grid_thw=image_grid_thw,
|
| 179 |
+
video_grid_thw=video_grid_thw,
|
| 180 |
+
second_per_grid_ts=second_per_grid_ts,
|
| 181 |
+
position_ids=position_ids,
|
| 182 |
+
attention_mask=attention_mask,
|
| 183 |
+
past_key_values=past_key_values,
|
| 184 |
+
inputs_embeds=inputs_embeds,
|
| 185 |
+
use_cache=use_cache,
|
| 186 |
+
output_attentions=output_attentions,
|
| 187 |
+
output_hidden_states=output_hidden_states,
|
| 188 |
+
return_dict=True,
|
| 189 |
+
cache_position=cache_position,
|
| 190 |
+
**kwargs,
|
| 191 |
+
)
|
| 192 |
+
return outputs.hidden_states
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def vision_forward(
|
| 196 |
+
self,
|
| 197 |
+
input_ids: torch.LongTensor = None,
|
| 198 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 199 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 200 |
+
past_key_values = None,
|
| 201 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 202 |
+
pixel_values: Optional[torch.Tensor] = None,
|
| 203 |
+
pixel_values_videos: Optional[torch.FloatTensor] = None,
|
| 204 |
+
image_grid_thw: Optional[torch.LongTensor] = None,
|
| 205 |
+
video_grid_thw: Optional[torch.LongTensor] = None,
|
| 206 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 207 |
+
second_per_grid_ts: Optional[torch.Tensor] = None,
|
| 208 |
+
**kwargs,
|
| 209 |
+
):
|
| 210 |
+
return self.model.vision_forward(
|
| 211 |
+
input_ids=input_ids,
|
| 212 |
+
inputs_embeds=inputs_embeds,
|
| 213 |
+
pixel_values=pixel_values,
|
| 214 |
+
pixel_values_videos=pixel_values_videos,
|
| 215 |
+
image_grid_thw=image_grid_thw,
|
| 216 |
+
video_grid_thw=video_grid_thw,
|
| 217 |
+
attention_mask=attention_mask,
|
| 218 |
+
position_ids=position_ids,
|
| 219 |
+
past_key_values=past_key_values,
|
| 220 |
+
cache_position=cache_position,
|
| 221 |
+
second_per_grid_ts=second_per_grid_ts,
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
def llm_forward(
|
| 225 |
+
self,
|
| 226 |
+
inputs_embeds: torch.FloatTensor,
|
| 227 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 228 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 229 |
+
past_key_values = None,
|
| 230 |
+
use_cache: Optional[bool] = None,
|
| 231 |
+
output_attentions: Optional[bool] = None,
|
| 232 |
+
output_hidden_states: Optional[bool] = None,
|
| 233 |
+
cache_position: Optional[torch.LongTensor] = None,
|
| 234 |
+
**kwargs,
|
| 235 |
+
):
|
| 236 |
+
output_attentions = False
|
| 237 |
+
output_hidden_states = True
|
| 238 |
+
|
| 239 |
+
outputs = self.model.llm_forward(
|
| 240 |
+
inputs_embeds=inputs_embeds,
|
| 241 |
+
position_ids=position_ids,
|
| 242 |
+
attention_mask=attention_mask,
|
| 243 |
+
past_key_values=past_key_values,
|
| 244 |
+
use_cache=use_cache,
|
| 245 |
+
output_attentions=output_attentions,
|
| 246 |
+
output_hidden_states=output_hidden_states,
|
| 247 |
+
return_dict=True,
|
| 248 |
+
cache_position=cache_position,
|
| 249 |
+
**kwargs,
|
| 250 |
+
)
|
| 251 |
+
return outputs.hidden_states
|
diffsynth/models/qwen_image_vae.py
ADDED
|
@@ -0,0 +1,876 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from typing import List, Optional, Tuple, Union
|
| 3 |
+
from torch import nn
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
CACHE_T = 2
|
| 7 |
+
|
| 8 |
+
class QwenImageCausalConv3d(torch.nn.Conv3d):
|
| 9 |
+
r"""
|
| 10 |
+
A custom 3D causal convolution layer with feature caching support.
|
| 11 |
+
|
| 12 |
+
This layer extends the standard Conv3D layer by ensuring causality in the time dimension and handling feature
|
| 13 |
+
caching for efficient inference.
|
| 14 |
+
|
| 15 |
+
Args:
|
| 16 |
+
in_channels (int): Number of channels in the input image
|
| 17 |
+
out_channels (int): Number of channels produced by the convolution
|
| 18 |
+
kernel_size (int or tuple): Size of the convolving kernel
|
| 19 |
+
stride (int or tuple, optional): Stride of the convolution. Default: 1
|
| 20 |
+
padding (int or tuple, optional): Zero-padding added to all three sides of the input. Default: 0
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
def __init__(
|
| 24 |
+
self,
|
| 25 |
+
in_channels: int,
|
| 26 |
+
out_channels: int,
|
| 27 |
+
kernel_size: Union[int, Tuple[int, int, int]],
|
| 28 |
+
stride: Union[int, Tuple[int, int, int]] = 1,
|
| 29 |
+
padding: Union[int, Tuple[int, int, int]] = 0,
|
| 30 |
+
) -> None:
|
| 31 |
+
super().__init__(
|
| 32 |
+
in_channels=in_channels,
|
| 33 |
+
out_channels=out_channels,
|
| 34 |
+
kernel_size=kernel_size,
|
| 35 |
+
stride=stride,
|
| 36 |
+
padding=padding,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
self._padding = (self.padding[2], self.padding[2], self.padding[1], self.padding[1], 2 * self.padding[0], 0)
|
| 41 |
+
self.padding = (0, 0, 0)
|
| 42 |
+
|
| 43 |
+
def forward(self, x, cache_x=None):
|
| 44 |
+
padding = list(self._padding)
|
| 45 |
+
if cache_x is not None and self._padding[4] > 0:
|
| 46 |
+
cache_x = cache_x.to(x.device)
|
| 47 |
+
x = torch.cat([cache_x, x], dim=2)
|
| 48 |
+
padding[4] -= cache_x.shape[2]
|
| 49 |
+
x = torch.nn.functional.pad(x, padding)
|
| 50 |
+
return super().forward(x)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class QwenImageRMS_norm(nn.Module):
|
| 55 |
+
r"""
|
| 56 |
+
A custom RMS normalization layer.
|
| 57 |
+
|
| 58 |
+
Args:
|
| 59 |
+
dim (int): The number of dimensions to normalize over.
|
| 60 |
+
channel_first (bool, optional): Whether the input tensor has channels as the first dimension.
|
| 61 |
+
Default is True.
|
| 62 |
+
images (bool, optional): Whether the input represents image data. Default is True.
|
| 63 |
+
bias (bool, optional): Whether to include a learnable bias term. Default is False.
|
| 64 |
+
"""
|
| 65 |
+
|
| 66 |
+
def __init__(self, dim: int, channel_first: bool = True, images: bool = True, bias: bool = False) -> None:
|
| 67 |
+
super().__init__()
|
| 68 |
+
broadcastable_dims = (1, 1, 1) if not images else (1, 1)
|
| 69 |
+
shape = (dim, *broadcastable_dims) if channel_first else (dim,)
|
| 70 |
+
|
| 71 |
+
self.channel_first = channel_first
|
| 72 |
+
self.scale = dim**0.5
|
| 73 |
+
self.gamma = nn.Parameter(torch.ones(shape))
|
| 74 |
+
self.bias = nn.Parameter(torch.zeros(shape)) if bias else 0.0
|
| 75 |
+
|
| 76 |
+
def forward(self, x):
|
| 77 |
+
return torch.nn.functional.normalize(x, dim=(1 if self.channel_first else -1)) * self.scale * self.gamma + self.bias
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class QwenImageResidualBlock(nn.Module):
|
| 82 |
+
r"""
|
| 83 |
+
A custom residual block module.
|
| 84 |
+
|
| 85 |
+
Args:
|
| 86 |
+
in_dim (int): Number of input channels.
|
| 87 |
+
out_dim (int): Number of output channels.
|
| 88 |
+
dropout (float, optional): Dropout rate for the dropout layer. Default is 0.0.
|
| 89 |
+
non_linearity (str, optional): Type of non-linearity to use. Default is "silu".
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
def __init__(
|
| 93 |
+
self,
|
| 94 |
+
in_dim: int,
|
| 95 |
+
out_dim: int,
|
| 96 |
+
dropout: float = 0.0,
|
| 97 |
+
non_linearity: str = "silu",
|
| 98 |
+
) -> None:
|
| 99 |
+
super().__init__()
|
| 100 |
+
self.in_dim = in_dim
|
| 101 |
+
self.out_dim = out_dim
|
| 102 |
+
self.nonlinearity = torch.nn.SiLU()
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
self.norm1 = QwenImageRMS_norm(in_dim, images=False)
|
| 106 |
+
self.conv1 = QwenImageCausalConv3d(in_dim, out_dim, 3, padding=1)
|
| 107 |
+
self.norm2 = QwenImageRMS_norm(out_dim, images=False)
|
| 108 |
+
self.dropout = nn.Dropout(dropout)
|
| 109 |
+
self.conv2 = QwenImageCausalConv3d(out_dim, out_dim, 3, padding=1)
|
| 110 |
+
self.conv_shortcut = QwenImageCausalConv3d(in_dim, out_dim, 1) if in_dim != out_dim else nn.Identity()
|
| 111 |
+
|
| 112 |
+
def forward(self, x, feat_cache=None, feat_idx=[0]):
|
| 113 |
+
|
| 114 |
+
h = self.conv_shortcut(x)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
x = self.norm1(x)
|
| 118 |
+
x = self.nonlinearity(x)
|
| 119 |
+
|
| 120 |
+
if feat_cache is not None:
|
| 121 |
+
idx = feat_idx[0]
|
| 122 |
+
cache_x = x[:, :, -CACHE_T:, :, :].clone()
|
| 123 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
|
| 124 |
+
cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
|
| 125 |
+
|
| 126 |
+
x = self.conv1(x, feat_cache[idx])
|
| 127 |
+
feat_cache[idx] = cache_x
|
| 128 |
+
feat_idx[0] += 1
|
| 129 |
+
else:
|
| 130 |
+
x = self.conv1(x)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
x = self.norm2(x)
|
| 134 |
+
x = self.nonlinearity(x)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
x = self.dropout(x)
|
| 138 |
+
|
| 139 |
+
if feat_cache is not None:
|
| 140 |
+
idx = feat_idx[0]
|
| 141 |
+
cache_x = x[:, :, -CACHE_T:, :, :].clone()
|
| 142 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
|
| 143 |
+
cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
|
| 144 |
+
|
| 145 |
+
x = self.conv2(x, feat_cache[idx])
|
| 146 |
+
feat_cache[idx] = cache_x
|
| 147 |
+
feat_idx[0] += 1
|
| 148 |
+
else:
|
| 149 |
+
x = self.conv2(x)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
return x + h
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class QwenImageAttentionBlock(nn.Module):
|
| 157 |
+
r"""
|
| 158 |
+
Causal self-attention with a single head.
|
| 159 |
+
|
| 160 |
+
Args:
|
| 161 |
+
dim (int): The number of channels in the input tensor.
|
| 162 |
+
"""
|
| 163 |
+
|
| 164 |
+
def __init__(self, dim):
|
| 165 |
+
super().__init__()
|
| 166 |
+
self.dim = dim
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
self.norm = QwenImageRMS_norm(dim)
|
| 170 |
+
self.to_qkv = nn.Conv2d(dim, dim * 3, 1)
|
| 171 |
+
self.proj = nn.Conv2d(dim, dim, 1)
|
| 172 |
+
|
| 173 |
+
def forward(self, x):
|
| 174 |
+
identity = x
|
| 175 |
+
batch_size, channels, time, height, width = x.size()
|
| 176 |
+
|
| 177 |
+
x = x.permute(0, 2, 1, 3, 4).reshape(batch_size * time, channels, height, width)
|
| 178 |
+
x = self.norm(x)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
qkv = self.to_qkv(x)
|
| 182 |
+
qkv = qkv.reshape(batch_size * time, 1, channels * 3, -1)
|
| 183 |
+
qkv = qkv.permute(0, 1, 3, 2).contiguous()
|
| 184 |
+
q, k, v = qkv.chunk(3, dim=-1)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
x = torch.nn.functional.scaled_dot_product_attention(q, k, v)
|
| 188 |
+
|
| 189 |
+
x = x.squeeze(1).permute(0, 2, 1).reshape(batch_size * time, channels, height, width)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
x = self.proj(x)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
x = x.view(batch_size, time, channels, height, width)
|
| 196 |
+
x = x.permute(0, 2, 1, 3, 4)
|
| 197 |
+
|
| 198 |
+
return x + identity
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class QwenImageUpsample(nn.Upsample):
|
| 203 |
+
r"""
|
| 204 |
+
Perform upsampling while ensuring the output tensor has the same data type as the input.
|
| 205 |
+
|
| 206 |
+
Args:
|
| 207 |
+
x (torch.Tensor): Input tensor to be upsampled.
|
| 208 |
+
|
| 209 |
+
Returns:
|
| 210 |
+
torch.Tensor: Upsampled tensor with the same data type as the input.
|
| 211 |
+
"""
|
| 212 |
+
|
| 213 |
+
def forward(self, x):
|
| 214 |
+
return super().forward(x.float()).type_as(x)
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
class QwenImageResample(nn.Module):
|
| 219 |
+
r"""
|
| 220 |
+
A custom resampling module for 2D and 3D data.
|
| 221 |
+
|
| 222 |
+
Args:
|
| 223 |
+
dim (int): The number of input/output channels.
|
| 224 |
+
mode (str): The resampling mode. Must be one of:
|
| 225 |
+
- 'none': No resampling (identity operation).
|
| 226 |
+
- 'upsample2d': 2D upsampling with nearest-exact interpolation and convolution.
|
| 227 |
+
- 'upsample3d': 3D upsampling with nearest-exact interpolation, convolution, and causal 3D convolution.
|
| 228 |
+
- 'downsample2d': 2D downsampling with zero-padding and convolution.
|
| 229 |
+
- 'downsample3d': 3D downsampling with zero-padding, convolution, and causal 3D convolution.
|
| 230 |
+
"""
|
| 231 |
+
|
| 232 |
+
def __init__(self, dim: int, mode: str) -> None:
|
| 233 |
+
super().__init__()
|
| 234 |
+
self.dim = dim
|
| 235 |
+
self.mode = mode
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
if mode == "upsample2d":
|
| 239 |
+
self.resample = nn.Sequential(
|
| 240 |
+
QwenImageUpsample(scale_factor=(2.0, 2.0), mode="nearest-exact"), nn.Conv2d(dim, dim // 2, 3, padding=1)
|
| 241 |
+
)
|
| 242 |
+
elif mode == "upsample3d":
|
| 243 |
+
self.resample = nn.Sequential(
|
| 244 |
+
QwenImageUpsample(scale_factor=(2.0, 2.0), mode="nearest-exact"), nn.Conv2d(dim, dim // 2, 3, padding=1)
|
| 245 |
+
)
|
| 246 |
+
self.time_conv = QwenImageCausalConv3d(dim, dim * 2, (3, 1, 1), padding=(1, 0, 0))
|
| 247 |
+
|
| 248 |
+
elif mode == "downsample2d":
|
| 249 |
+
self.resample = nn.Sequential(nn.ZeroPad2d((0, 1, 0, 1)), nn.Conv2d(dim, dim, 3, stride=(2, 2)))
|
| 250 |
+
elif mode == "downsample3d":
|
| 251 |
+
self.resample = nn.Sequential(nn.ZeroPad2d((0, 1, 0, 1)), nn.Conv2d(dim, dim, 3, stride=(2, 2)))
|
| 252 |
+
self.time_conv = QwenImageCausalConv3d(dim, dim, (3, 1, 1), stride=(2, 1, 1), padding=(0, 0, 0))
|
| 253 |
+
|
| 254 |
+
else:
|
| 255 |
+
self.resample = nn.Identity()
|
| 256 |
+
|
| 257 |
+
def forward(self, x, feat_cache=None, feat_idx=[0]):
|
| 258 |
+
b, c, t, h, w = x.size()
|
| 259 |
+
if self.mode == "upsample3d":
|
| 260 |
+
if feat_cache is not None:
|
| 261 |
+
idx = feat_idx[0]
|
| 262 |
+
if feat_cache[idx] is None:
|
| 263 |
+
feat_cache[idx] = "Rep"
|
| 264 |
+
feat_idx[0] += 1
|
| 265 |
+
else:
|
| 266 |
+
cache_x = x[:, :, -CACHE_T:, :, :].clone()
|
| 267 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None and feat_cache[idx] != "Rep":
|
| 268 |
+
|
| 269 |
+
cache_x = torch.cat(
|
| 270 |
+
[feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2
|
| 271 |
+
)
|
| 272 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None and feat_cache[idx] == "Rep":
|
| 273 |
+
cache_x = torch.cat([torch.zeros_like(cache_x).to(cache_x.device), cache_x], dim=2)
|
| 274 |
+
if feat_cache[idx] == "Rep":
|
| 275 |
+
x = self.time_conv(x)
|
| 276 |
+
else:
|
| 277 |
+
x = self.time_conv(x, feat_cache[idx])
|
| 278 |
+
feat_cache[idx] = cache_x
|
| 279 |
+
feat_idx[0] += 1
|
| 280 |
+
|
| 281 |
+
x = x.reshape(b, 2, c, t, h, w)
|
| 282 |
+
x = torch.stack((x[:, 0, :, :, :, :], x[:, 1, :, :, :, :]), 3)
|
| 283 |
+
x = x.reshape(b, c, t * 2, h, w)
|
| 284 |
+
t = x.shape[2]
|
| 285 |
+
x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
|
| 286 |
+
x = self.resample(x)
|
| 287 |
+
x = x.view(b, t, x.size(1), x.size(2), x.size(3)).permute(0, 2, 1, 3, 4)
|
| 288 |
+
|
| 289 |
+
if self.mode == "downsample3d":
|
| 290 |
+
if feat_cache is not None:
|
| 291 |
+
idx = feat_idx[0]
|
| 292 |
+
if feat_cache[idx] is None:
|
| 293 |
+
feat_cache[idx] = x.clone()
|
| 294 |
+
feat_idx[0] += 1
|
| 295 |
+
else:
|
| 296 |
+
cache_x = x[:, :, -1:, :, :].clone()
|
| 297 |
+
x = self.time_conv(torch.cat([feat_cache[idx][:, :, -1:, :, :], x], 2))
|
| 298 |
+
feat_cache[idx] = cache_x
|
| 299 |
+
feat_idx[0] += 1
|
| 300 |
+
return x
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
class QwenImageMidBlock(nn.Module):
|
| 305 |
+
"""
|
| 306 |
+
Middle block for WanVAE encoder and decoder.
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
dim (int): Number of input/output channels.
|
| 310 |
+
dropout (float): Dropout rate.
|
| 311 |
+
non_linearity (str): Type of non-linearity to use.
|
| 312 |
+
"""
|
| 313 |
+
|
| 314 |
+
def __init__(self, dim: int, dropout: float = 0.0, non_linearity: str = "silu", num_layers: int = 1):
|
| 315 |
+
super().__init__()
|
| 316 |
+
self.dim = dim
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
resnets = [QwenImageResidualBlock(dim, dim, dropout, non_linearity)]
|
| 320 |
+
attentions = []
|
| 321 |
+
for _ in range(num_layers):
|
| 322 |
+
attentions.append(QwenImageAttentionBlock(dim))
|
| 323 |
+
resnets.append(QwenImageResidualBlock(dim, dim, dropout, non_linearity))
|
| 324 |
+
self.attentions = nn.ModuleList(attentions)
|
| 325 |
+
self.resnets = nn.ModuleList(resnets)
|
| 326 |
+
|
| 327 |
+
self.gradient_checkpointing = False
|
| 328 |
+
|
| 329 |
+
def forward(self, x, feat_cache=None, feat_idx=[0]):
|
| 330 |
+
|
| 331 |
+
x = self.resnets[0](x, feat_cache, feat_idx)
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
for attn, resnet in zip(self.attentions, self.resnets[1:]):
|
| 335 |
+
if attn is not None:
|
| 336 |
+
x = attn(x)
|
| 337 |
+
|
| 338 |
+
x = resnet(x, feat_cache, feat_idx)
|
| 339 |
+
|
| 340 |
+
return x
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
class QwenImageEncoder3d(nn.Module):
|
| 345 |
+
r"""
|
| 346 |
+
A 3D encoder module.
|
| 347 |
+
|
| 348 |
+
Args:
|
| 349 |
+
dim (int): The base number of channels in the first layer.
|
| 350 |
+
z_dim (int): The dimensionality of the latent space.
|
| 351 |
+
dim_mult (list of int): Multipliers for the number of channels in each block.
|
| 352 |
+
num_res_blocks (int): Number of residual blocks in each block.
|
| 353 |
+
attn_scales (list of float): Scales at which to apply attention mechanisms.
|
| 354 |
+
temperal_downsample (list of bool): Whether to downsample temporally in each block.
|
| 355 |
+
dropout (float): Dropout rate for the dropout layers.
|
| 356 |
+
non_linearity (str): Type of non-linearity to use.
|
| 357 |
+
"""
|
| 358 |
+
|
| 359 |
+
def __init__(
|
| 360 |
+
self,
|
| 361 |
+
dim=128,
|
| 362 |
+
z_dim=4,
|
| 363 |
+
dim_mult=[1, 2, 4, 4],
|
| 364 |
+
num_res_blocks=2,
|
| 365 |
+
attn_scales=[],
|
| 366 |
+
temperal_downsample=[True, True, False],
|
| 367 |
+
dropout=0.0,
|
| 368 |
+
non_linearity: str = "silu",
|
| 369 |
+
):
|
| 370 |
+
super().__init__()
|
| 371 |
+
self.dim = dim
|
| 372 |
+
self.z_dim = z_dim
|
| 373 |
+
self.dim_mult = dim_mult
|
| 374 |
+
self.num_res_blocks = num_res_blocks
|
| 375 |
+
self.attn_scales = attn_scales
|
| 376 |
+
self.temperal_downsample = temperal_downsample
|
| 377 |
+
self.nonlinearity = torch.nn.SiLU()
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
dims = [dim * u for u in [1] + dim_mult]
|
| 381 |
+
scale = 1.0
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
self.conv_in = QwenImageCausalConv3d(3, dims[0], 3, padding=1)
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
self.down_blocks = torch.nn.ModuleList([])
|
| 388 |
+
for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])):
|
| 389 |
+
|
| 390 |
+
for _ in range(num_res_blocks):
|
| 391 |
+
self.down_blocks.append(QwenImageResidualBlock(in_dim, out_dim, dropout))
|
| 392 |
+
if scale in attn_scales:
|
| 393 |
+
self.down_blocks.append(QwenImageAttentionBlock(out_dim))
|
| 394 |
+
in_dim = out_dim
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
if i != len(dim_mult) - 1:
|
| 398 |
+
mode = "downsample3d" if temperal_downsample[i] else "downsample2d"
|
| 399 |
+
self.down_blocks.append(QwenImageResample(out_dim, mode=mode))
|
| 400 |
+
scale /= 2.0
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
self.mid_block = QwenImageMidBlock(out_dim, dropout, non_linearity, num_layers=1)
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
self.norm_out = QwenImageRMS_norm(out_dim, images=False)
|
| 407 |
+
self.conv_out = QwenImageCausalConv3d(out_dim, z_dim, 3, padding=1)
|
| 408 |
+
|
| 409 |
+
self.gradient_checkpointing = False
|
| 410 |
+
|
| 411 |
+
def forward(self, x, feat_cache=None, feat_idx=[0]):
|
| 412 |
+
if feat_cache is not None:
|
| 413 |
+
idx = feat_idx[0]
|
| 414 |
+
cache_x = x[:, :, -CACHE_T:, :, :].clone()
|
| 415 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
|
| 416 |
+
|
| 417 |
+
cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
|
| 418 |
+
x = self.conv_in(x, feat_cache[idx])
|
| 419 |
+
feat_cache[idx] = cache_x
|
| 420 |
+
feat_idx[0] += 1
|
| 421 |
+
else:
|
| 422 |
+
x = self.conv_in(x)
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
for layer in self.down_blocks:
|
| 426 |
+
if feat_cache is not None:
|
| 427 |
+
x = layer(x, feat_cache, feat_idx)
|
| 428 |
+
else:
|
| 429 |
+
x = layer(x)
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
x = self.mid_block(x, feat_cache, feat_idx)
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
x = self.norm_out(x)
|
| 436 |
+
x = self.nonlinearity(x)
|
| 437 |
+
if feat_cache is not None:
|
| 438 |
+
idx = feat_idx[0]
|
| 439 |
+
cache_x = x[:, :, -CACHE_T:, :, :].clone()
|
| 440 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
|
| 441 |
+
|
| 442 |
+
cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
|
| 443 |
+
x = self.conv_out(x, feat_cache[idx])
|
| 444 |
+
feat_cache[idx] = cache_x
|
| 445 |
+
feat_idx[0] += 1
|
| 446 |
+
else:
|
| 447 |
+
x = self.conv_out(x)
|
| 448 |
+
return x
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
class QwenImageUpBlock(nn.Module):
|
| 453 |
+
"""
|
| 454 |
+
A block that handles upsampling for the WanVAE decoder.
|
| 455 |
+
|
| 456 |
+
Args:
|
| 457 |
+
in_dim (int): Input dimension
|
| 458 |
+
out_dim (int): Output dimension
|
| 459 |
+
num_res_blocks (int): Number of residual blocks
|
| 460 |
+
dropout (float): Dropout rate
|
| 461 |
+
upsample_mode (str, optional): Mode for upsampling ('upsample2d' or 'upsample3d')
|
| 462 |
+
non_linearity (str): Type of non-linearity to use
|
| 463 |
+
"""
|
| 464 |
+
|
| 465 |
+
def __init__(
|
| 466 |
+
self,
|
| 467 |
+
in_dim: int,
|
| 468 |
+
out_dim: int,
|
| 469 |
+
num_res_blocks: int,
|
| 470 |
+
dropout: float = 0.0,
|
| 471 |
+
upsample_mode: Optional[str] = None,
|
| 472 |
+
non_linearity: str = "silu",
|
| 473 |
+
):
|
| 474 |
+
super().__init__()
|
| 475 |
+
self.in_dim = in_dim
|
| 476 |
+
self.out_dim = out_dim
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
resnets = []
|
| 480 |
+
|
| 481 |
+
current_dim = in_dim
|
| 482 |
+
for _ in range(num_res_blocks + 1):
|
| 483 |
+
resnets.append(QwenImageResidualBlock(current_dim, out_dim, dropout, non_linearity))
|
| 484 |
+
current_dim = out_dim
|
| 485 |
+
|
| 486 |
+
self.resnets = nn.ModuleList(resnets)
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
self.upsamplers = None
|
| 490 |
+
if upsample_mode is not None:
|
| 491 |
+
self.upsamplers = nn.ModuleList([QwenImageResample(out_dim, mode=upsample_mode)])
|
| 492 |
+
|
| 493 |
+
self.gradient_checkpointing = False
|
| 494 |
+
|
| 495 |
+
def forward(self, x, feat_cache=None, feat_idx=[0]):
|
| 496 |
+
"""
|
| 497 |
+
Forward pass through the upsampling block.
|
| 498 |
+
|
| 499 |
+
Args:
|
| 500 |
+
x (torch.Tensor): Input tensor
|
| 501 |
+
feat_cache (list, optional): Feature cache for causal convolutions
|
| 502 |
+
feat_idx (list, optional): Feature index for cache management
|
| 503 |
+
|
| 504 |
+
Returns:
|
| 505 |
+
torch.Tensor: Output tensor
|
| 506 |
+
"""
|
| 507 |
+
for resnet in self.resnets:
|
| 508 |
+
if feat_cache is not None:
|
| 509 |
+
x = resnet(x, feat_cache, feat_idx)
|
| 510 |
+
else:
|
| 511 |
+
x = resnet(x)
|
| 512 |
+
|
| 513 |
+
if self.upsamplers is not None:
|
| 514 |
+
if feat_cache is not None:
|
| 515 |
+
x = self.upsamplers[0](x, feat_cache, feat_idx)
|
| 516 |
+
else:
|
| 517 |
+
x = self.upsamplers[0](x)
|
| 518 |
+
return x
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
class QwenImageDecoder3d(nn.Module):
|
| 523 |
+
r"""
|
| 524 |
+
A 3D decoder module.
|
| 525 |
+
|
| 526 |
+
Args:
|
| 527 |
+
dim (int): The base number of channels in the first layer.
|
| 528 |
+
z_dim (int): The dimensionality of the latent space.
|
| 529 |
+
dim_mult (list of int): Multipliers for the number of channels in each block.
|
| 530 |
+
num_res_blocks (int): Number of residual blocks in each block.
|
| 531 |
+
attn_scales (list of float): Scales at which to apply attention mechanisms.
|
| 532 |
+
temperal_upsample (list of bool): Whether to upsample temporally in each block.
|
| 533 |
+
dropout (float): Dropout rate for the dropout layers.
|
| 534 |
+
non_linearity (str): Type of non-linearity to use.
|
| 535 |
+
"""
|
| 536 |
+
|
| 537 |
+
def __init__(
|
| 538 |
+
self,
|
| 539 |
+
dim=128,
|
| 540 |
+
z_dim=4,
|
| 541 |
+
dim_mult=[1, 2, 4, 4],
|
| 542 |
+
num_res_blocks=2,
|
| 543 |
+
attn_scales=[],
|
| 544 |
+
temperal_upsample=[False, True, True],
|
| 545 |
+
dropout=0.0,
|
| 546 |
+
non_linearity: str = "silu",
|
| 547 |
+
):
|
| 548 |
+
super().__init__()
|
| 549 |
+
self.dim = dim
|
| 550 |
+
self.z_dim = z_dim
|
| 551 |
+
self.dim_mult = dim_mult
|
| 552 |
+
self.num_res_blocks = num_res_blocks
|
| 553 |
+
self.attn_scales = attn_scales
|
| 554 |
+
self.temperal_upsample = temperal_upsample
|
| 555 |
+
|
| 556 |
+
self.nonlinearity = torch.nn.SiLU()
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
dims = [dim * u for u in [dim_mult[-1]] + dim_mult[::-1]]
|
| 560 |
+
scale = 1.0 / 2 ** (len(dim_mult) - 2)
|
| 561 |
+
|
| 562 |
+
|
| 563 |
+
self.conv_in = QwenImageCausalConv3d(z_dim, dims[0], 3, padding=1)
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
self.mid_block = QwenImageMidBlock(dims[0], dropout, non_linearity, num_layers=1)
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
self.up_blocks = nn.ModuleList([])
|
| 570 |
+
for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])):
|
| 571 |
+
|
| 572 |
+
if i > 0:
|
| 573 |
+
in_dim = in_dim // 2
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
upsample_mode = None
|
| 577 |
+
if i != len(dim_mult) - 1:
|
| 578 |
+
upsample_mode = "upsample3d" if temperal_upsample[i] else "upsample2d"
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
up_block = QwenImageUpBlock(
|
| 582 |
+
in_dim=in_dim,
|
| 583 |
+
out_dim=out_dim,
|
| 584 |
+
num_res_blocks=num_res_blocks,
|
| 585 |
+
dropout=dropout,
|
| 586 |
+
upsample_mode=upsample_mode,
|
| 587 |
+
non_linearity=non_linearity,
|
| 588 |
+
)
|
| 589 |
+
self.up_blocks.append(up_block)
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
if upsample_mode is not None:
|
| 593 |
+
scale *= 2.0
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
self.norm_out = QwenImageRMS_norm(out_dim, images=False)
|
| 597 |
+
self.conv_out = QwenImageCausalConv3d(out_dim, 3, 3, padding=1)
|
| 598 |
+
|
| 599 |
+
self.gradient_checkpointing = False
|
| 600 |
+
|
| 601 |
+
def forward(self, x, feat_cache=None, feat_idx=[0]):
|
| 602 |
+
|
| 603 |
+
if feat_cache is not None:
|
| 604 |
+
idx = feat_idx[0]
|
| 605 |
+
cache_x = x[:, :, -CACHE_T:, :, :].clone()
|
| 606 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
|
| 607 |
+
|
| 608 |
+
cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
|
| 609 |
+
x = self.conv_in(x, feat_cache[idx])
|
| 610 |
+
feat_cache[idx] = cache_x
|
| 611 |
+
feat_idx[0] += 1
|
| 612 |
+
else:
|
| 613 |
+
x = self.conv_in(x)
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
x = self.mid_block(x, feat_cache, feat_idx)
|
| 617 |
+
|
| 618 |
+
|
| 619 |
+
for up_block in self.up_blocks:
|
| 620 |
+
x = up_block(x, feat_cache, feat_idx)
|
| 621 |
+
|
| 622 |
+
|
| 623 |
+
x = self.norm_out(x)
|
| 624 |
+
x = self.nonlinearity(x)
|
| 625 |
+
if feat_cache is not None:
|
| 626 |
+
idx = feat_idx[0]
|
| 627 |
+
cache_x = x[:, :, -CACHE_T:, :, :].clone()
|
| 628 |
+
if cache_x.shape[2] < 2 and feat_cache[idx] is not None:
|
| 629 |
+
|
| 630 |
+
cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2)
|
| 631 |
+
x = self.conv_out(x, feat_cache[idx])
|
| 632 |
+
feat_cache[idx] = cache_x
|
| 633 |
+
feat_idx[0] += 1
|
| 634 |
+
else:
|
| 635 |
+
x = self.conv_out(x)
|
| 636 |
+
return x
|
| 637 |
+
|
| 638 |
+
|
| 639 |
+
|
| 640 |
+
class QwenImageVAE(torch.nn.Module):
|
| 641 |
+
def __init__(
|
| 642 |
+
self,
|
| 643 |
+
base_dim: int = 96,
|
| 644 |
+
z_dim: int = 16,
|
| 645 |
+
dim_mult: Tuple[int] = [1, 2, 4, 4],
|
| 646 |
+
num_res_blocks: int = 2,
|
| 647 |
+
attn_scales: List[float] = [],
|
| 648 |
+
temperal_downsample: List[bool] = [False, True, True],
|
| 649 |
+
dropout: float = 0.0,
|
| 650 |
+
) -> None:
|
| 651 |
+
super().__init__()
|
| 652 |
+
|
| 653 |
+
self.z_dim = z_dim
|
| 654 |
+
self.temperal_downsample = temperal_downsample
|
| 655 |
+
self.temperal_upsample = temperal_downsample[::-1]
|
| 656 |
+
|
| 657 |
+
self.encoder = QwenImageEncoder3d(
|
| 658 |
+
base_dim, z_dim * 2, dim_mult, num_res_blocks, attn_scales, self.temperal_downsample, dropout
|
| 659 |
+
)
|
| 660 |
+
self.quant_conv = QwenImageCausalConv3d(z_dim * 2, z_dim * 2, 1)
|
| 661 |
+
self.post_quant_conv = QwenImageCausalConv3d(z_dim, z_dim, 1)
|
| 662 |
+
|
| 663 |
+
self.decoder = QwenImageDecoder3d(
|
| 664 |
+
base_dim, z_dim, dim_mult, num_res_blocks, attn_scales, self.temperal_upsample, dropout
|
| 665 |
+
)
|
| 666 |
+
|
| 667 |
+
mean = [
|
| 668 |
+
-0.7571,
|
| 669 |
+
-0.7089,
|
| 670 |
+
-0.9113,
|
| 671 |
+
0.1075,
|
| 672 |
+
-0.1745,
|
| 673 |
+
0.9653,
|
| 674 |
+
-0.1517,
|
| 675 |
+
1.5508,
|
| 676 |
+
0.4134,
|
| 677 |
+
-0.0715,
|
| 678 |
+
0.5517,
|
| 679 |
+
-0.3632,
|
| 680 |
+
-0.1922,
|
| 681 |
+
-0.9497,
|
| 682 |
+
0.2503,
|
| 683 |
+
-0.2921,
|
| 684 |
+
]
|
| 685 |
+
std = [
|
| 686 |
+
2.8184,
|
| 687 |
+
1.4541,
|
| 688 |
+
2.3275,
|
| 689 |
+
2.6558,
|
| 690 |
+
1.2196,
|
| 691 |
+
1.7708,
|
| 692 |
+
2.6052,
|
| 693 |
+
2.0743,
|
| 694 |
+
3.2687,
|
| 695 |
+
2.1526,
|
| 696 |
+
2.8652,
|
| 697 |
+
1.5579,
|
| 698 |
+
1.6382,
|
| 699 |
+
1.1253,
|
| 700 |
+
2.8251,
|
| 701 |
+
1.9160,
|
| 702 |
+
]
|
| 703 |
+
self.mean = torch.tensor(mean).view(1, 16, 1, 1, 1)
|
| 704 |
+
self.std = 1 / torch.tensor(std).view(1, 16, 1, 1, 1)
|
| 705 |
+
|
| 706 |
+
def encode(self, x, **kwargs):
|
| 707 |
+
x = x.unsqueeze(2)
|
| 708 |
+
x = self.encoder(x)
|
| 709 |
+
x = self.quant_conv(x)
|
| 710 |
+
x = x[:, :16]
|
| 711 |
+
mean, std = self.mean.to(dtype=x.dtype, device=x.device), self.std.to(dtype=x.dtype, device=x.device)
|
| 712 |
+
x = (x - mean) * std
|
| 713 |
+
x = x.squeeze(2)
|
| 714 |
+
return x
|
| 715 |
+
|
| 716 |
+
def decode(self, x, **kwargs):
|
| 717 |
+
x = x.unsqueeze(2)
|
| 718 |
+
mean, std = self.mean.to(dtype=x.dtype, device=x.device), self.std.to(dtype=x.dtype, device=x.device)
|
| 719 |
+
x = x / std + mean
|
| 720 |
+
x = self.post_quant_conv(x)
|
| 721 |
+
x = self.decoder(x)
|
| 722 |
+
x = x.squeeze(2)
|
| 723 |
+
return x
|
| 724 |
+
|
| 725 |
+
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
|
| 729 |
+
|
| 730 |
+
|
| 731 |
+
|
| 732 |
+
|
| 733 |
+
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
|
| 740 |
+
|
| 741 |
+
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
|
| 745 |
+
|
| 746 |
+
|
| 747 |
+
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
|
| 751 |
+
|
| 752 |
+
|
| 753 |
+
|
| 754 |
+
|
| 755 |
+
|
| 756 |
+
|
| 757 |
+
|
| 758 |
+
|
| 759 |
+
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
|
| 763 |
+
|
| 764 |
+
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
|
| 768 |
+
|
| 769 |
+
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
|
| 773 |
+
|
| 774 |
+
|
| 775 |
+
|
| 776 |
+
|
| 777 |
+
|
| 778 |
+
|
| 779 |
+
|
| 780 |
+
|
| 781 |
+
|
| 782 |
+
|
| 783 |
+
|
| 784 |
+
|
| 785 |
+
|
| 786 |
+
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
|
| 790 |
+
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
|
| 794 |
+
|
| 795 |
+
|
| 796 |
+
|
| 797 |
+
|
| 798 |
+
|
| 799 |
+
|
| 800 |
+
|
| 801 |
+
|
| 802 |
+
|
| 803 |
+
|
| 804 |
+
|
| 805 |
+
|
| 806 |
+
|
| 807 |
+
|
| 808 |
+
|
| 809 |
+
|
| 810 |
+
|
| 811 |
+
|
| 812 |
+
|
| 813 |
+
|
| 814 |
+
|
| 815 |
+
|
| 816 |
+
|
| 817 |
+
|
| 818 |
+
|
| 819 |
+
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
|
| 825 |
+
|
| 826 |
+
|
| 827 |
+
|
| 828 |
+
|
| 829 |
+
|
| 830 |
+
|
| 831 |
+
|
| 832 |
+
|
| 833 |
+
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
|
| 837 |
+
|
| 838 |
+
|
| 839 |
+
|
| 840 |
+
|
| 841 |
+
|
| 842 |
+
|
| 843 |
+
|
| 844 |
+
|
| 845 |
+
|
| 846 |
+
|
| 847 |
+
|
| 848 |
+
|
| 849 |
+
|
| 850 |
+
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
|
| 854 |
+
|
| 855 |
+
|
| 856 |
+
|
| 857 |
+
|
| 858 |
+
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
|
| 862 |
+
|
| 863 |
+
|
| 864 |
+
|
| 865 |
+
|
| 866 |
+
|
| 867 |
+
|
| 868 |
+
|
| 869 |
+
|
| 870 |
+
|
| 871 |
+
|
| 872 |
+
|
| 873 |
+
|
| 874 |
+
|
| 875 |
+
|
| 876 |
+
|
diffsynth/pipelines/__init__.py
ADDED
|
File without changes
|
diffsynth/pipelines/qwen_image.py
ADDED
|
@@ -0,0 +1,1374 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, math
|
| 2 |
+
from PIL import Image
|
| 3 |
+
from typing import Union
|
| 4 |
+
from tqdm import tqdm
|
| 5 |
+
from einops import rearrange
|
| 6 |
+
import numpy as np
|
| 7 |
+
from math import prod
|
| 8 |
+
import cv2
|
| 9 |
+
from torchvision.transforms import v2, InterpolationMode
|
| 10 |
+
|
| 11 |
+
from ..diffusion import FlowMatchScheduler
|
| 12 |
+
from ..core import ModelConfig, gradient_checkpoint_forward
|
| 13 |
+
from ..diffusion.base_pipeline import BasePipeline, PipelineUnit, ControlNetInput
|
| 14 |
+
|
| 15 |
+
from ..models.qwen_image_dit import QwenImageDiT
|
| 16 |
+
from ..models.qwen_image_text_encoder import QwenImageTextEncoder
|
| 17 |
+
from ..models.qwen_image_vae import QwenImageVAE
|
| 18 |
+
from ..models.qwen_image_controlnet import QwenImageBlockWiseControlNet
|
| 19 |
+
from ..models.qwen_image_connector import QwenImageConnector
|
| 20 |
+
from ..models.qwen_image_learnable_query import QwenImageLearnableQuery
|
| 21 |
+
from ..models.qwen_image_extractor import QwenImageExtractor
|
| 22 |
+
from ..models.qwen_image_rformer import RFormer,RFormer2D
|
| 23 |
+
from ..models.qwen_image_mae import QwenImageMAE
|
| 24 |
+
|
| 25 |
+
import torch
|
| 26 |
+
import random
|
| 27 |
+
torch.set_printoptions(threshold=10_000_000)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def extract_polygons(item):
|
| 31 |
+
"""
|
| 32 |
+
递归解析 edit_region,提取所有的多边形轮廓。
|
| 33 |
+
目标是找到所有的 list of points [[x,y], [x,y], ...]
|
| 34 |
+
"""
|
| 35 |
+
if not isinstance(item, list):
|
| 36 |
+
return []
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
if len(item) == 2 and all(isinstance(x, (int, float)) for x in item):
|
| 40 |
+
return None
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
first_elem = item[0] if len(item) > 0 else None
|
| 45 |
+
if isinstance(first_elem, list) and len(first_elem) == 2 and all(isinstance(x, (int, float)) for x in first_elem):
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
return [np.array(item, dtype=np.float32)]
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
polys = []
|
| 52 |
+
for sub in item:
|
| 53 |
+
res = extract_polygons(sub)
|
| 54 |
+
if res:
|
| 55 |
+
polys.extend(res)
|
| 56 |
+
return polys
|
| 57 |
+
|
| 58 |
+
class QwenImagePipeline(BasePipeline):
|
| 59 |
+
|
| 60 |
+
def __init__(self, device="cuda", torch_dtype=torch.bfloat16):
|
| 61 |
+
super().__init__(
|
| 62 |
+
device=device, torch_dtype=torch_dtype,
|
| 63 |
+
height_division_factor=16, width_division_factor=16,
|
| 64 |
+
)
|
| 65 |
+
from transformers import Qwen2Tokenizer, Qwen2VLProcessor
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
self.scheduler = FlowMatchScheduler("Qwen-Image")
|
| 69 |
+
self.text_encoder: QwenImageTextEncoder = None
|
| 70 |
+
self.connector: QwenImageConnector=None
|
| 71 |
+
self.learnable_query: QwenImageLearnableQuery=None
|
| 72 |
+
self.extractor: QwenImageExtractor=None
|
| 73 |
+
self.rformer: RFormer=None
|
| 74 |
+
self.rformer2d: RFormer2D=None
|
| 75 |
+
self.mae: QwenImageMAE=None
|
| 76 |
+
self.dit: QwenImageDiT = None
|
| 77 |
+
self.vae: QwenImageVAE = None
|
| 78 |
+
self.blockwise_controlnet: QwenImageBlockwiseMultiControlNet = None
|
| 79 |
+
self.tokenizer: Qwen2Tokenizer = None
|
| 80 |
+
self.processor: Qwen2VLProcessor = None
|
| 81 |
+
self.in_iteration_models = ("dit", "blockwise_controlnet")
|
| 82 |
+
self.units = [
|
| 83 |
+
QwenImageUnit_ShapeChecker(),
|
| 84 |
+
QwenImageUnit_NoiseInitializer(),
|
| 85 |
+
QwenImageUnit_InputImageEmbedder(),
|
| 86 |
+
QwenImageUnit_Inpaint(),
|
| 87 |
+
QwenImageUnit_PromptEmbedderWithExtractor(),
|
| 88 |
+
QwenImageUnit_MirrorPPROperationEncoder(),
|
| 89 |
+
QwenImageUnit_MirrorPPROperationEncoder2D(),
|
| 90 |
+
QwenImageUnit_EditImageEmbedder(),
|
| 91 |
+
QwenImageUnit_ContextImageEmbedder(),
|
| 92 |
+
QwenImageUnit_PromptEmbedder(),
|
| 93 |
+
QwenImageUnit_EntityControl(),
|
| 94 |
+
QwenImageUnit_BlockwiseControlNet(),
|
| 95 |
+
]
|
| 96 |
+
self.model_fn = model_fn_qwen_image
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
@staticmethod
|
| 100 |
+
def from_pretrained(
|
| 101 |
+
torch_dtype: torch.dtype = torch.bfloat16,
|
| 102 |
+
device: Union[str, torch.device] = "cuda",
|
| 103 |
+
model_configs: list[ModelConfig] = [],
|
| 104 |
+
tokenizer_config: ModelConfig = ModelConfig(model_id="Qwen/Qwen-Image", origin_file_pattern="tokenizer/"),
|
| 105 |
+
processor_config: ModelConfig = None,
|
| 106 |
+
vram_limit: float = None,
|
| 107 |
+
):
|
| 108 |
+
|
| 109 |
+
pipe = QwenImagePipeline(device=device, torch_dtype=torch_dtype)
|
| 110 |
+
model_pool = pipe.download_and_load_models(model_configs, vram_limit)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
pipe.text_encoder = model_pool.fetch_model("qwen_image_text_encoder")
|
| 114 |
+
pipe.dit = model_pool.fetch_model("qwen_image_dit")
|
| 115 |
+
pipe.vae = model_pool.fetch_model("qwen_image_vae")
|
| 116 |
+
pipe.blockwise_controlnet = QwenImageBlockwiseMultiControlNet(model_pool.fetch_model("qwen_image_blockwise_controlnet", index="all"))
|
| 117 |
+
pipe.connector=model_pool.fetch_model("qwen_image_connector")
|
| 118 |
+
pipe.learnable_query=model_pool.fetch_model("qwen_image_learnable_query")
|
| 119 |
+
pipe.extractor=model_pool.fetch_model("qwen_image_extractor")
|
| 120 |
+
pipe.rformer=model_pool.fetch_model("qwen_image_rformer")
|
| 121 |
+
pipe.rformer2d=model_pool.fetch_model("qwen_image_rformer2D")
|
| 122 |
+
pipe.mae=model_pool.fetch_model("qwen_image_mae")
|
| 123 |
+
|
| 124 |
+
if tokenizer_config is not None:
|
| 125 |
+
tokenizer_config.download_if_necessary()
|
| 126 |
+
from transformers import Qwen2Tokenizer
|
| 127 |
+
pipe.tokenizer = Qwen2Tokenizer.from_pretrained(tokenizer_config.path)
|
| 128 |
+
if processor_config is not None:
|
| 129 |
+
processor_config.download_if_necessary()
|
| 130 |
+
from transformers import Qwen2VLProcessor
|
| 131 |
+
pipe.processor = Qwen2VLProcessor.from_pretrained(processor_config.path)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
pipe.vram_management_enabled = pipe.check_vram_management_state()
|
| 135 |
+
if pipe.vram_management_enabled:
|
| 136 |
+
print("enable vram management")
|
| 137 |
+
else:
|
| 138 |
+
print("disable vram management")
|
| 139 |
+
return pipe
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
@torch.no_grad()
|
| 143 |
+
def __call__(
|
| 144 |
+
self,
|
| 145 |
+
|
| 146 |
+
prompt: str,
|
| 147 |
+
negative_prompt: str = "",
|
| 148 |
+
cfg_scale: float = 4.0,
|
| 149 |
+
|
| 150 |
+
input_image: Image.Image = None,
|
| 151 |
+
denoising_strength: float = 1.0,
|
| 152 |
+
|
| 153 |
+
inpaint_mask: Image.Image = None,
|
| 154 |
+
inpaint_blur_size: int = None,
|
| 155 |
+
inpaint_blur_sigma: float = None,
|
| 156 |
+
|
| 157 |
+
height: int = 1328,
|
| 158 |
+
width: int = 1328,
|
| 159 |
+
|
| 160 |
+
seed: int = None,
|
| 161 |
+
rand_device: str = "cpu",
|
| 162 |
+
|
| 163 |
+
num_inference_steps: int = 30,
|
| 164 |
+
exponential_shift_mu: float = None,
|
| 165 |
+
|
| 166 |
+
blockwise_controlnet_inputs: list[ControlNetInput] = None,
|
| 167 |
+
|
| 168 |
+
eligen_entity_prompts: list[str] = None,
|
| 169 |
+
eligen_entity_masks: list[Image.Image] = None,
|
| 170 |
+
eligen_enable_on_negative: bool = False,
|
| 171 |
+
|
| 172 |
+
edit_image: Image.Image = None,
|
| 173 |
+
edit_image_auto_resize: bool = True,
|
| 174 |
+
edit_rope_interpolation: bool = False,
|
| 175 |
+
zero_cond_t: bool = False,
|
| 176 |
+
|
| 177 |
+
example_origin: Image.Image = None,
|
| 178 |
+
example_target: Image.Image = None,
|
| 179 |
+
shape_1=None,
|
| 180 |
+
no_qwenvl=False,
|
| 181 |
+
|
| 182 |
+
context_image: Image.Image = None,
|
| 183 |
+
|
| 184 |
+
tiled: bool = False,
|
| 185 |
+
tile_size: int = 128,
|
| 186 |
+
tile_stride: int = 64,
|
| 187 |
+
|
| 188 |
+
progress_bar_cmd = tqdm,
|
| 189 |
+
):
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
self.scheduler.set_timesteps(num_inference_steps, denoising_strength=denoising_strength, dynamic_shift_len=(height // 16) * (width // 16), exponential_shift_mu=exponential_shift_mu)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
inputs_posi = {
|
| 196 |
+
"prompt": prompt,
|
| 197 |
+
}
|
| 198 |
+
inputs_nega = {
|
| 199 |
+
"negative_prompt": negative_prompt,
|
| 200 |
+
}
|
| 201 |
+
inputs_shared = {
|
| 202 |
+
"cfg_scale": cfg_scale,
|
| 203 |
+
"input_image": input_image, "denoising_strength": denoising_strength,
|
| 204 |
+
"inpaint_mask": inpaint_mask, "inpaint_blur_size": inpaint_blur_size, "inpaint_blur_sigma": inpaint_blur_sigma,
|
| 205 |
+
"height": height, "width": width,
|
| 206 |
+
"seed": seed, "rand_device": rand_device,
|
| 207 |
+
"num_inference_steps": num_inference_steps,
|
| 208 |
+
"blockwise_controlnet_inputs": blockwise_controlnet_inputs,
|
| 209 |
+
"tiled": tiled, "tile_size": tile_size, "tile_stride": tile_stride,
|
| 210 |
+
"eligen_entity_prompts": eligen_entity_prompts, "eligen_entity_masks": eligen_entity_masks, "eligen_enable_on_negative": eligen_enable_on_negative,
|
| 211 |
+
"edit_image": edit_image, "edit_image_auto_resize": edit_image_auto_resize, "edit_rope_interpolation": edit_rope_interpolation,
|
| 212 |
+
"context_image": context_image,
|
| 213 |
+
"zero_cond_t": zero_cond_t,
|
| 214 |
+
"example_origin": example_origin, "example_target": example_target,"shape_1":shape_1,"no_qwenvl":no_qwenvl
|
| 215 |
+
}
|
| 216 |
+
for unit in self.units:
|
| 217 |
+
inputs_shared, inputs_posi, inputs_nega = self.unit_runner(unit, self, inputs_shared, inputs_posi, inputs_nega)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
self.load_models_to_device(self.in_iteration_models)
|
| 221 |
+
models = {name: getattr(self, name) for name in self.in_iteration_models}
|
| 222 |
+
for progress_id, timestep in enumerate(progress_bar_cmd(self.scheduler.timesteps)):
|
| 223 |
+
timestep = timestep.unsqueeze(0).to(dtype=self.torch_dtype, device=self.device)
|
| 224 |
+
noise_pred = self.cfg_guided_model_fn(
|
| 225 |
+
self.model_fn, cfg_scale,
|
| 226 |
+
inputs_shared, inputs_posi, inputs_nega,
|
| 227 |
+
**models, timestep=timestep, progress_id=progress_id
|
| 228 |
+
)
|
| 229 |
+
inputs_shared["latents"] = self.step(self.scheduler, progress_id=progress_id, noise_pred=noise_pred, **inputs_shared)
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
self.load_models_to_device(['vae'])
|
| 233 |
+
image = self.vae.decode(inputs_shared["latents"], device=self.device, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
|
| 234 |
+
image = self.vae_output_to_image(image)
|
| 235 |
+
self.load_models_to_device([])
|
| 236 |
+
|
| 237 |
+
return image
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
class QwenImageBlockwiseMultiControlNet(torch.nn.Module):
|
| 242 |
+
def __init__(self, models: list[QwenImageBlockWiseControlNet]):
|
| 243 |
+
super().__init__()
|
| 244 |
+
if not isinstance(models, list):
|
| 245 |
+
models = [models]
|
| 246 |
+
self.models = torch.nn.ModuleList(models)
|
| 247 |
+
for model in models:
|
| 248 |
+
if hasattr(model, "vram_management_enabled") and getattr(model, "vram_management_enabled"):
|
| 249 |
+
self.vram_management_enabled = True
|
| 250 |
+
|
| 251 |
+
def preprocess(self, controlnet_inputs: list[ControlNetInput], conditionings: list[torch.Tensor], **kwargs):
|
| 252 |
+
processed_conditionings = []
|
| 253 |
+
for controlnet_input, conditioning in zip(controlnet_inputs, conditionings):
|
| 254 |
+
conditioning = rearrange(conditioning, "B C (H P) (W Q) -> B (H W) (C P Q)", P=2, Q=2)
|
| 255 |
+
model_output = self.models[controlnet_input.controlnet_id].process_controlnet_conditioning(conditioning)
|
| 256 |
+
processed_conditionings.append(model_output)
|
| 257 |
+
return processed_conditionings
|
| 258 |
+
|
| 259 |
+
def blockwise_forward(self, image, conditionings: list[torch.Tensor], controlnet_inputs: list[ControlNetInput], progress_id, num_inference_steps, block_id, **kwargs):
|
| 260 |
+
res = 0
|
| 261 |
+
for controlnet_input, conditioning in zip(controlnet_inputs, conditionings):
|
| 262 |
+
progress = (num_inference_steps - 1 - progress_id) / max(num_inference_steps - 1, 1)
|
| 263 |
+
if progress > controlnet_input.start + (1e-4) or progress < controlnet_input.end - (1e-4):
|
| 264 |
+
continue
|
| 265 |
+
model_output = self.models[controlnet_input.controlnet_id].blockwise_forward(image, conditioning, block_id)
|
| 266 |
+
res = res + model_output * controlnet_input.scale
|
| 267 |
+
return res
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
class QwenImageUnit_Inpaint(PipelineUnit):
|
| 271 |
+
def __init__(self):
|
| 272 |
+
super().__init__(
|
| 273 |
+
input_params=("inpaint_mask", "height", "width", "inpaint_blur_size", "inpaint_blur_sigma"),
|
| 274 |
+
output_params=("inpaint_mask",),
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
def process(self, pipe: QwenImagePipeline, inpaint_mask, height, width, inpaint_blur_size, inpaint_blur_sigma):
|
| 278 |
+
if inpaint_mask is None:
|
| 279 |
+
return {}
|
| 280 |
+
inpaint_mask = pipe.preprocess_image(inpaint_mask.convert("RGB").resize((width // 8, height // 8)), min_value=0, max_value=1)
|
| 281 |
+
inpaint_mask = inpaint_mask.mean(dim=1, keepdim=True)
|
| 282 |
+
if inpaint_blur_size is not None and inpaint_blur_sigma is not None:
|
| 283 |
+
from torchvision.transforms import GaussianBlur
|
| 284 |
+
blur = GaussianBlur(kernel_size=inpaint_blur_size * 2 + 1, sigma=inpaint_blur_sigma)
|
| 285 |
+
inpaint_mask = blur(inpaint_mask)
|
| 286 |
+
return {"inpaint_mask": inpaint_mask}
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
class QwenImageUnit_EntityControl(PipelineUnit):
|
| 290 |
+
def __init__(self):
|
| 291 |
+
super().__init__(
|
| 292 |
+
take_over=True,
|
| 293 |
+
input_params=("eligen_entity_prompts", "width", "height", "eligen_enable_on_negative", "cfg_scale"),
|
| 294 |
+
output_params=("entity_prompt_emb", "entity_masks", "entity_prompt_emb_mask"),
|
| 295 |
+
onload_model_names=("text_encoder",)
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
def extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
|
| 299 |
+
bool_mask = mask.bool()
|
| 300 |
+
valid_lengths = bool_mask.sum(dim=1)
|
| 301 |
+
selected = hidden_states[bool_mask]
|
| 302 |
+
split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
|
| 303 |
+
return split_result
|
| 304 |
+
|
| 305 |
+
def get_prompt_emb(self, pipe: QwenImagePipeline, prompt) -> dict:
|
| 306 |
+
if pipe.text_encoder is not None:
|
| 307 |
+
prompt = [prompt]
|
| 308 |
+
template = "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
|
| 309 |
+
drop_idx = 34
|
| 310 |
+
txt = [template.format(e) for e in prompt]
|
| 311 |
+
txt_tokens = pipe.tokenizer(txt, max_length=1024+drop_idx, padding=True, truncation=True, return_tensors="pt").to(pipe.device)
|
| 312 |
+
hidden_states = pipe.text_encoder(input_ids=txt_tokens.input_ids, attention_mask=txt_tokens.attention_mask, output_hidden_states=True,)[-1]
|
| 313 |
+
|
| 314 |
+
split_hidden_states = self.extract_masked_hidden(hidden_states, txt_tokens.attention_mask)
|
| 315 |
+
split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
|
| 316 |
+
attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
|
| 317 |
+
max_seq_len = max([e.size(0) for e in split_hidden_states])
|
| 318 |
+
prompt_embeds = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states])
|
| 319 |
+
encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list])
|
| 320 |
+
prompt_embeds = prompt_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
|
| 321 |
+
return {"prompt_emb": prompt_embeds, "prompt_emb_mask": encoder_attention_mask}
|
| 322 |
+
else:
|
| 323 |
+
return {}
|
| 324 |
+
|
| 325 |
+
def preprocess_masks(self, pipe, masks, height, width, dim):
|
| 326 |
+
out_masks = []
|
| 327 |
+
for mask in masks:
|
| 328 |
+
mask = pipe.preprocess_image(mask.resize((width, height), resample=Image.NEAREST)).mean(dim=1, keepdim=True) > 0
|
| 329 |
+
mask = mask.repeat(1, dim, 1, 1).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 330 |
+
out_masks.append(mask)
|
| 331 |
+
return out_masks
|
| 332 |
+
|
| 333 |
+
def prepare_entity_inputs(self, pipe, entity_prompts, entity_masks, width, height):
|
| 334 |
+
entity_masks = self.preprocess_masks(pipe, entity_masks, height//8, width//8, 1)
|
| 335 |
+
entity_masks = torch.cat(entity_masks, dim=0).unsqueeze(0)
|
| 336 |
+
prompt_embs, prompt_emb_masks = [], []
|
| 337 |
+
for entity_prompt in entity_prompts:
|
| 338 |
+
prompt_emb_dict = self.get_prompt_emb(pipe, entity_prompt)
|
| 339 |
+
prompt_embs.append(prompt_emb_dict['prompt_emb'])
|
| 340 |
+
prompt_emb_masks.append(prompt_emb_dict['prompt_emb_mask'])
|
| 341 |
+
return prompt_embs, prompt_emb_masks, entity_masks
|
| 342 |
+
|
| 343 |
+
def prepare_eligen(self, pipe, prompt_emb_nega, eligen_entity_prompts, eligen_entity_masks, width, height, enable_eligen_on_negative, cfg_scale):
|
| 344 |
+
entity_prompt_emb_posi, entity_prompt_emb_posi_mask, entity_masks_posi = self.prepare_entity_inputs(pipe, eligen_entity_prompts, eligen_entity_masks, width, height)
|
| 345 |
+
if enable_eligen_on_negative and cfg_scale != 1.0:
|
| 346 |
+
entity_prompt_emb_nega = [prompt_emb_nega['prompt_emb']] * len(entity_prompt_emb_posi)
|
| 347 |
+
entity_prompt_emb_nega_mask = [prompt_emb_nega['prompt_emb_mask']] * len(entity_prompt_emb_posi)
|
| 348 |
+
entity_masks_nega = entity_masks_posi
|
| 349 |
+
else:
|
| 350 |
+
entity_prompt_emb_nega, entity_prompt_emb_nega_mask, entity_masks_nega = None, None, None
|
| 351 |
+
eligen_kwargs_posi = {"entity_prompt_emb": entity_prompt_emb_posi, "entity_masks": entity_masks_posi, "entity_prompt_emb_mask": entity_prompt_emb_posi_mask}
|
| 352 |
+
eligen_kwargs_nega = {"entity_prompt_emb": entity_prompt_emb_nega, "entity_masks": entity_masks_nega, "entity_prompt_emb_mask": entity_prompt_emb_nega_mask}
|
| 353 |
+
return eligen_kwargs_posi, eligen_kwargs_nega
|
| 354 |
+
|
| 355 |
+
def process(self, pipe: QwenImagePipeline, inputs_shared, inputs_posi, inputs_nega):
|
| 356 |
+
eligen_entity_prompts, eligen_entity_masks = inputs_shared.get("eligen_entity_prompts", None), inputs_shared.get("eligen_entity_masks", None)
|
| 357 |
+
if eligen_entity_prompts is None or eligen_entity_masks is None or len(eligen_entity_prompts) == 0 or len(eligen_entity_masks) == 0:
|
| 358 |
+
return inputs_shared, inputs_posi, inputs_nega
|
| 359 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 360 |
+
eligen_enable_on_negative = inputs_shared.get("eligen_enable_on_negative", False)
|
| 361 |
+
eligen_kwargs_posi, eligen_kwargs_nega = self.prepare_eligen(pipe, inputs_nega,
|
| 362 |
+
eligen_entity_prompts, eligen_entity_masks, inputs_shared["width"], inputs_shared["height"],
|
| 363 |
+
eligen_enable_on_negative, inputs_shared["cfg_scale"])
|
| 364 |
+
inputs_posi.update(eligen_kwargs_posi)
|
| 365 |
+
if inputs_shared.get("cfg_scale", 1.0) != 1.0:
|
| 366 |
+
inputs_nega.update(eligen_kwargs_nega)
|
| 367 |
+
return inputs_shared, inputs_posi, inputs_nega
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
class QwenImageUnit_BlockwiseControlNet(PipelineUnit):
|
| 371 |
+
def __init__(self):
|
| 372 |
+
super().__init__(
|
| 373 |
+
input_params=("blockwise_controlnet_inputs", "tiled", "tile_size", "tile_stride"),
|
| 374 |
+
output_params=("blockwise_controlnet_conditioning",),
|
| 375 |
+
onload_model_names=("vae",)
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
def apply_controlnet_mask_on_latents(self, pipe, latents, mask):
|
| 379 |
+
mask = (pipe.preprocess_image(mask) + 1) / 2
|
| 380 |
+
mask = mask.mean(dim=1, keepdim=True)
|
| 381 |
+
mask = 1 - torch.nn.functional.interpolate(mask, size=latents.shape[-2:])
|
| 382 |
+
latents = torch.concat([latents, mask], dim=1)
|
| 383 |
+
return latents
|
| 384 |
+
|
| 385 |
+
def apply_controlnet_mask_on_image(self, pipe, image, mask):
|
| 386 |
+
mask = mask.resize(image.size)
|
| 387 |
+
mask = pipe.preprocess_image(mask).mean(dim=[0, 1]).cpu()
|
| 388 |
+
image = np.array(image)
|
| 389 |
+
image[mask > 0] = 0
|
| 390 |
+
image = Image.fromarray(image)
|
| 391 |
+
return image
|
| 392 |
+
|
| 393 |
+
def process(self, pipe: QwenImagePipeline, blockwise_controlnet_inputs: list[ControlNetInput], tiled, tile_size, tile_stride):
|
| 394 |
+
if blockwise_controlnet_inputs is None:
|
| 395 |
+
return {}
|
| 396 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 397 |
+
conditionings = []
|
| 398 |
+
for controlnet_input in blockwise_controlnet_inputs:
|
| 399 |
+
image = controlnet_input.image
|
| 400 |
+
if controlnet_input.inpaint_mask is not None:
|
| 401 |
+
image = self.apply_controlnet_mask_on_image(pipe, image, controlnet_input.inpaint_mask)
|
| 402 |
+
|
| 403 |
+
image = pipe.preprocess_image(image).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 404 |
+
image = pipe.vae.encode(image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
|
| 405 |
+
|
| 406 |
+
if controlnet_input.inpaint_mask is not None:
|
| 407 |
+
image = self.apply_controlnet_mask_on_latents(pipe, image, controlnet_input.inpaint_mask)
|
| 408 |
+
conditionings.append(image)
|
| 409 |
+
|
| 410 |
+
return {"blockwise_controlnet_conditioning": conditionings}
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
class QwenImageUnit_ContextImageEmbedder(PipelineUnit):
|
| 415 |
+
def __init__(self):
|
| 416 |
+
super().__init__(
|
| 417 |
+
input_params=("context_image", "height", "width", "tiled", "tile_size", "tile_stride"),
|
| 418 |
+
output_params=("context_latents",),
|
| 419 |
+
onload_model_names=("vae",)
|
| 420 |
+
)
|
| 421 |
+
|
| 422 |
+
def process(self, pipe: QwenImagePipeline, context_image, height, width, tiled, tile_size, tile_stride):
|
| 423 |
+
if context_image is None:
|
| 424 |
+
return {}
|
| 425 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 426 |
+
context_image = pipe.preprocess_image(context_image.resize((width, height))).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 427 |
+
context_latents = pipe.vae.encode(context_image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
|
| 428 |
+
return {"context_latents": context_latents}
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
class QwenImageUnit_ShapeChecker(PipelineUnit):
|
| 433 |
+
def __init__(self):
|
| 434 |
+
super().__init__(
|
| 435 |
+
input_params=("height", "width"),
|
| 436 |
+
output_params=("height", "width"),
|
| 437 |
+
)
|
| 438 |
+
|
| 439 |
+
def process(self, pipe: QwenImagePipeline, height, width):
|
| 440 |
+
height, width = pipe.check_resize_height_width(height, width)
|
| 441 |
+
return {"height": height, "width": width}
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
class QwenImageUnit_NoiseInitializer(PipelineUnit):
|
| 446 |
+
def __init__(self):
|
| 447 |
+
super().__init__(
|
| 448 |
+
input_params=("height", "width", "seed", "rand_device"),
|
| 449 |
+
output_params=("noise",),
|
| 450 |
+
)
|
| 451 |
+
|
| 452 |
+
def process(self, pipe: QwenImagePipeline, height, width, seed, rand_device):
|
| 453 |
+
noise = pipe.generate_noise((1, 16, height//8, width//8), seed=seed, rand_device=rand_device, rand_torch_dtype=pipe.torch_dtype)
|
| 454 |
+
return {"noise": noise}
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
class QwenImageUnit_InputImageEmbedder(PipelineUnit):
|
| 459 |
+
def __init__(self):
|
| 460 |
+
super().__init__(
|
| 461 |
+
input_params=("input_image", "noise", "tiled", "tile_size", "tile_stride","edit_region","loss_weight_mask"),
|
| 462 |
+
output_params=("latents", "input_latents","loss_weight_mask"),
|
| 463 |
+
onload_model_names=("vae",)
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
def process(self, pipe: QwenImagePipeline, input_image, noise, tiled, tile_size, tile_stride, edit_region=None,loss_weight_mask=None):
|
| 480 |
+
if input_image is None:
|
| 481 |
+
return {"latents": noise, "input_latents": None, "loss_weight_mask": None}
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
|
| 487 |
+
pipe.load_models_to_device(['vae'])
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
image = pipe.preprocess_image(input_image).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 492 |
+
input_latents = pipe.vae.encode(image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
|
| 493 |
+
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
|
| 551 |
+
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
|
| 562 |
+
|
| 563 |
+
latent_mask = None
|
| 564 |
+
if edit_region is not None:
|
| 565 |
+
|
| 566 |
+
B, C, H_lat, W_lat = input_latents.shape
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
latent_mask = torch.ones((B, 1, H_lat, W_lat), device=input_latents.device, dtype=input_latents.dtype)
|
| 570 |
+
|
| 571 |
+
|
| 572 |
+
edit_weight = 100.0
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
scale_h = H_lat / image.shape[2]
|
| 577 |
+
scale_w = W_lat / image.shape[3]
|
| 578 |
+
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
for b in range(B):
|
| 582 |
+
|
| 583 |
+
if B == 1:
|
| 584 |
+
|
| 585 |
+
current_region = edit_region
|
| 586 |
+
else:
|
| 587 |
+
try:
|
| 588 |
+
current_region = edit_region[b]
|
| 589 |
+
except:
|
| 590 |
+
current_region = edit_region
|
| 591 |
+
|
| 592 |
+
if not current_region:
|
| 593 |
+
continue
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
polygons = extract_polygons(current_region)
|
| 597 |
+
|
| 598 |
+
if len(polygons) > 0:
|
| 599 |
+
mask_np = np.zeros((H_lat, W_lat), dtype=np.float32)
|
| 600 |
+
|
| 601 |
+
scaled_polys = []
|
| 602 |
+
for poly in polygons:
|
| 603 |
+
poly_scaled = poly * np.array([scale_w, scale_h], dtype=np.float32)
|
| 604 |
+
scaled_polys.append(poly_scaled.astype(np.int32))
|
| 605 |
+
|
| 606 |
+
cv2.fillPoly(mask_np, scaled_polys, color=1.0)
|
| 607 |
+
|
| 608 |
+
mask_tensor = torch.from_numpy(mask_np).to(latent_mask.device)
|
| 609 |
+
|
| 610 |
+
latent_mask[b, 0] = torch.where(mask_tensor > 0.5, torch.tensor(edit_weight, device=latent_mask.device), latent_mask[b, 0])
|
| 611 |
+
|
| 612 |
+
if loss_weight_mask is not None:
|
| 613 |
+
latent_mask=loss_weight_mask
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
if pipe.scheduler.training:
|
| 617 |
+
|
| 618 |
+
|
| 619 |
+
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
|
| 623 |
+
|
| 624 |
+
|
| 625 |
+
|
| 626 |
+
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
return {
|
| 633 |
+
"latents": noise,
|
| 634 |
+
"input_latents": input_latents,
|
| 635 |
+
"loss_weight_mask": latent_mask
|
| 636 |
+
}
|
| 637 |
+
else:
|
| 638 |
+
latents = pipe.scheduler.add_noise(input_latents, noise, timestep=pipe.scheduler.timesteps[0])
|
| 639 |
+
return {
|
| 640 |
+
"latents": latents,
|
| 641 |
+
"input_latents": input_latents
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
|
| 645 |
+
class QwenImageUnit_PromptEmbedder(PipelineUnit):
|
| 646 |
+
def __init__(self):
|
| 647 |
+
super().__init__(
|
| 648 |
+
seperate_cfg=True,
|
| 649 |
+
input_params_posi={"prompt": "prompt"},
|
| 650 |
+
input_params_nega={"prompt": "negative_prompt"},
|
| 651 |
+
input_params=("edit_image",),
|
| 652 |
+
output_params=("prompt_emb", "prompt_emb_mask"),
|
| 653 |
+
onload_model_names=("text_encoder",)
|
| 654 |
+
)
|
| 655 |
+
|
| 656 |
+
def extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
|
| 657 |
+
bool_mask = mask.bool()
|
| 658 |
+
valid_lengths = bool_mask.sum(dim=1)
|
| 659 |
+
selected = hidden_states[bool_mask]
|
| 660 |
+
split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
|
| 661 |
+
return split_result
|
| 662 |
+
|
| 663 |
+
def calculate_dimensions(self, target_area, ratio):
|
| 664 |
+
width = math.sqrt(target_area * ratio)
|
| 665 |
+
height = width / ratio
|
| 666 |
+
width = round(width / 32) * 32
|
| 667 |
+
height = round(height / 32) * 32
|
| 668 |
+
return width, height
|
| 669 |
+
|
| 670 |
+
def resize_image(self, image, target_area=384*384):
|
| 671 |
+
width, height = self.calculate_dimensions(target_area, image.size[0] / image.size[1])
|
| 672 |
+
return image.resize((width, height))
|
| 673 |
+
|
| 674 |
+
def encode_prompt(self, pipe: QwenImagePipeline, prompt):
|
| 675 |
+
template = "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background:<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
|
| 676 |
+
drop_idx = 34
|
| 677 |
+
txt = [template.format(e) for e in prompt]
|
| 678 |
+
model_inputs = pipe.tokenizer(txt, max_length=4096+drop_idx, padding=True, truncation=True, return_tensors="pt").to(pipe.device)
|
| 679 |
+
if model_inputs.input_ids.shape[1] >= 1024:
|
| 680 |
+
print(f"Warning!!! QwenImage model was trained on prompts up to 512 tokens. Current prompt requires {model_inputs['input_ids'].shape[1] - drop_idx} tokens, which may lead to unpredictable behavior.")
|
| 681 |
+
hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, output_hidden_states=True,)[-1]
|
| 682 |
+
split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
|
| 683 |
+
split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
|
| 684 |
+
return split_hidden_states
|
| 685 |
+
|
| 686 |
+
def encode_prompt_edit(self, pipe: QwenImagePipeline, prompt, edit_image):
|
| 687 |
+
template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>{}<|im_end|>\n<|im_start|>assistant\n"
|
| 688 |
+
drop_idx = 64
|
| 689 |
+
txt = [template.format(e) for e in prompt]
|
| 690 |
+
|
| 691 |
+
|
| 692 |
+
model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
|
| 693 |
+
hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw, output_hidden_states=True,)[-1]
|
| 694 |
+
split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
|
| 695 |
+
split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
|
| 696 |
+
return split_hidden_states
|
| 697 |
+
|
| 698 |
+
def encode_prompt_edit_multi(self, pipe: QwenImagePipeline, prompt, edit_image):
|
| 699 |
+
template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
|
| 700 |
+
drop_idx = 64
|
| 701 |
+
img_prompt_template = "Picture {}: <|vision_start|><|image_pad|><|vision_end|>"
|
| 702 |
+
base_img_prompt = "".join([img_prompt_template.format(i + 1) for i in range(len(edit_image))])
|
| 703 |
+
txt = [template.format(base_img_prompt + e) for e in prompt]
|
| 704 |
+
edit_image = [self.resize_image(image) for image in edit_image]
|
| 705 |
+
|
| 706 |
+
model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
|
| 707 |
+
hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw, output_hidden_states=True,)[-1]
|
| 708 |
+
split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
|
| 709 |
+
split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
|
| 710 |
+
return split_hidden_states
|
| 711 |
+
|
| 712 |
+
def process(self, pipe: QwenImagePipeline, prompt, edit_image=None) -> dict:
|
| 713 |
+
|
| 714 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 715 |
+
if pipe.text_encoder is not None:
|
| 716 |
+
prompt = [prompt]
|
| 717 |
+
if edit_image is None:
|
| 718 |
+
split_hidden_states = self.encode_prompt(pipe, prompt)
|
| 719 |
+
elif isinstance(edit_image, Image.Image):
|
| 720 |
+
split_hidden_states = self.encode_prompt_edit(pipe, prompt, edit_image)
|
| 721 |
+
else:
|
| 722 |
+
split_hidden_states = self.encode_prompt_edit_multi(pipe, prompt, edit_image)
|
| 723 |
+
attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
|
| 724 |
+
max_seq_len = max([e.size(0) for e in split_hidden_states])
|
| 725 |
+
prompt_embeds = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states])
|
| 726 |
+
encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list])
|
| 727 |
+
prompt_embeds = prompt_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
|
| 728 |
+
return {"prompt_emb": prompt_embeds, "prompt_emb_mask": encoder_attention_mask}
|
| 729 |
+
else:
|
| 730 |
+
return {}
|
| 731 |
+
|
| 732 |
+
|
| 733 |
+
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
|
| 740 |
+
|
| 741 |
+
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
|
| 745 |
+
|
| 746 |
+
|
| 747 |
+
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
|
| 751 |
+
|
| 752 |
+
|
| 753 |
+
|
| 754 |
+
|
| 755 |
+
|
| 756 |
+
|
| 757 |
+
|
| 758 |
+
|
| 759 |
+
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
|
| 763 |
+
|
| 764 |
+
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
|
| 768 |
+
|
| 769 |
+
|
| 770 |
+
|
| 771 |
+
|
| 772 |
+
|
| 773 |
+
|
| 774 |
+
|
| 775 |
+
|
| 776 |
+
|
| 777 |
+
|
| 778 |
+
|
| 779 |
+
|
| 780 |
+
|
| 781 |
+
|
| 782 |
+
|
| 783 |
+
|
| 784 |
+
|
| 785 |
+
|
| 786 |
+
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
|
| 790 |
+
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
|
| 794 |
+
|
| 795 |
+
|
| 796 |
+
|
| 797 |
+
|
| 798 |
+
|
| 799 |
+
|
| 800 |
+
|
| 801 |
+
|
| 802 |
+
|
| 803 |
+
|
| 804 |
+
|
| 805 |
+
|
| 806 |
+
|
| 807 |
+
|
| 808 |
+
|
| 809 |
+
|
| 810 |
+
|
| 811 |
+
|
| 812 |
+
|
| 813 |
+
|
| 814 |
+
|
| 815 |
+
|
| 816 |
+
|
| 817 |
+
|
| 818 |
+
|
| 819 |
+
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
|
| 825 |
+
|
| 826 |
+
|
| 827 |
+
|
| 828 |
+
|
| 829 |
+
|
| 830 |
+
|
| 831 |
+
|
| 832 |
+
|
| 833 |
+
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
|
| 837 |
+
|
| 838 |
+
|
| 839 |
+
|
| 840 |
+
|
| 841 |
+
|
| 842 |
+
|
| 843 |
+
|
| 844 |
+
|
| 845 |
+
|
| 846 |
+
|
| 847 |
+
|
| 848 |
+
|
| 849 |
+
|
| 850 |
+
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
|
| 854 |
+
|
| 855 |
+
|
| 856 |
+
|
| 857 |
+
|
| 858 |
+
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
|
| 862 |
+
|
| 863 |
+
class QwenImageUnit_EditImageEmbedder(PipelineUnit):
|
| 864 |
+
def __init__(self):
|
| 865 |
+
super().__init__(
|
| 866 |
+
input_params=("edit_image", "tiled", "tile_size", "tile_stride", "edit_image_auto_resize"),
|
| 867 |
+
output_params=("edit_latents", "edit_image"),
|
| 868 |
+
onload_model_names=("vae",)
|
| 869 |
+
)
|
| 870 |
+
|
| 871 |
+
|
| 872 |
+
def calculate_dimensions(self, target_area, ratio):
|
| 873 |
+
import math
|
| 874 |
+
width = math.sqrt(target_area * ratio)
|
| 875 |
+
height = width / ratio
|
| 876 |
+
width = round(width / 32) * 32
|
| 877 |
+
height = round(height / 32) * 32
|
| 878 |
+
|
| 879 |
+
|
| 880 |
+
return width, height
|
| 881 |
+
|
| 882 |
+
|
| 883 |
+
def edit_image_auto_resize(self, edit_image):
|
| 884 |
+
calculated_width, calculated_height = self.calculate_dimensions(1024 * 1024, edit_image.size[0] / edit_image.size[1])
|
| 885 |
+
return edit_image.resize((calculated_width, calculated_height))
|
| 886 |
+
|
| 887 |
+
|
| 888 |
+
def process(self, pipe: QwenImagePipeline, edit_image, tiled, tile_size, tile_stride, edit_image_auto_resize=False):
|
| 889 |
+
if edit_image is None:
|
| 890 |
+
return {}
|
| 891 |
+
if pipe.extractor is not None:
|
| 892 |
+
|
| 893 |
+
return {}
|
| 894 |
+
|
| 895 |
+
|
| 896 |
+
|
| 897 |
+
|
| 898 |
+
|
| 899 |
+
|
| 900 |
+
|
| 901 |
+
|
| 902 |
+
|
| 903 |
+
|
| 904 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 905 |
+
if isinstance(edit_image, Image.Image):
|
| 906 |
+
resized_edit_image = self.edit_image_auto_resize(edit_image) if edit_image_auto_resize else edit_image
|
| 907 |
+
edit_image = pipe.preprocess_image(resized_edit_image).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 908 |
+
edit_latents = pipe.vae.encode(edit_image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
|
| 909 |
+
else:
|
| 910 |
+
resized_edit_image, edit_latents = [], []
|
| 911 |
+
for image in edit_image:
|
| 912 |
+
if edit_image_auto_resize:
|
| 913 |
+
image = self.edit_image_auto_resize(image)
|
| 914 |
+
resized_edit_image.append(image)
|
| 915 |
+
|
| 916 |
+
image=resized_edit_image[0]
|
| 917 |
+
image = pipe.preprocess_image(image).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 918 |
+
latents = pipe.vae.encode(image, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
|
| 919 |
+
edit_latents.append(latents)
|
| 920 |
+
|
| 921 |
+
|
| 922 |
+
return {"edit_latents": edit_latents, "edit_image": resized_edit_image}
|
| 923 |
+
|
| 924 |
+
|
| 925 |
+
class QwenImageUnit_MirrorPPROperationEncoder(PipelineUnit):
|
| 926 |
+
def __init__(self):
|
| 927 |
+
super().__init__(
|
| 928 |
+
input_params=("example_origin","example_target"),
|
| 929 |
+
output_params=("operation_emb"),
|
| 930 |
+
onload_model_names=("mae","rformer","connector")
|
| 931 |
+
)
|
| 932 |
+
|
| 933 |
+
def process_image_for_tokenizer(self,image_input, target_size=(1024, 1024)):
|
| 934 |
+
"""
|
| 935 |
+
Args:
|
| 936 |
+
image_input: 单个 PIL.Image 对象,或者 PIL.Image 对象的列表。
|
| 937 |
+
target_size: 目标尺寸 (height, width),默认 (1024, 1024)。
|
| 938 |
+
Returns:
|
| 939 |
+
torch.Tensor:
|
| 940 |
+
- 如果输入是单个图片: 返回 (3, H, W)
|
| 941 |
+
- 如果输入是列表: 返回 (B, 3, H, W)
|
| 942 |
+
- 数据类型 float32,已归一化。
|
| 943 |
+
"""
|
| 944 |
+
mean = torch.tensor([0.485, 0.456, 0.406]).view(3, 1, 1)
|
| 945 |
+
std = torch.tensor([0.229, 0.224, 0.225]).view(3, 1, 1)
|
| 946 |
+
|
| 947 |
+
transforms = v2.Compose([
|
| 948 |
+
v2.ToImage(),
|
| 949 |
+
v2.ToDtype(torch.float32, scale=True),
|
| 950 |
+
v2.Resize(target_size, interpolation=InterpolationMode.BICUBIC, antialias=True),
|
| 951 |
+
])
|
| 952 |
+
|
| 953 |
+
|
| 954 |
+
def _process_one_tensor(img):
|
| 955 |
+
|
| 956 |
+
x = transforms(img)
|
| 957 |
+
|
| 958 |
+
|
| 959 |
+
if mean.device != x.device:
|
| 960 |
+
mean_dev = mean.to(x.device)
|
| 961 |
+
std_dev = std.to(x.device)
|
| 962 |
+
else:
|
| 963 |
+
mean_dev, std_dev = mean, std
|
| 964 |
+
|
| 965 |
+
|
| 966 |
+
x = (x - mean_dev) / (std_dev + 1e-6)
|
| 967 |
+
return x
|
| 968 |
+
|
| 969 |
+
if isinstance(image_input, list):
|
| 970 |
+
tensors = [_process_one_tensor(img) for img in image_input]
|
| 971 |
+
return torch.stack(tensors)
|
| 972 |
+
else:
|
| 973 |
+
|
| 974 |
+
return _process_one_tensor(image_input).unsqueeze(0)
|
| 975 |
+
|
| 976 |
+
|
| 977 |
+
def process(self, pipe: QwenImagePipeline,example_origin,example_target):
|
| 978 |
+
if pipe.rformer2d is not None:
|
| 979 |
+
return {}
|
| 980 |
+
if pipe.extractor is not None:
|
| 981 |
+
return {}
|
| 982 |
+
|
| 983 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 984 |
+
|
| 985 |
+
|
| 986 |
+
|
| 987 |
+
|
| 988 |
+
|
| 989 |
+
example_origin_pixel_values = self.process_image_for_tokenizer(example_origin).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 990 |
+
example_target_pixel_values = self.process_image_for_tokenizer(example_target).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 991 |
+
example_origin_hidden_states=pipe.mae(example_origin_pixel_values)
|
| 992 |
+
example_target_hidden_states=pipe.mae(example_target_pixel_values)
|
| 993 |
+
query_num = pipe.rformer.query_num
|
| 994 |
+
latent_motion_tokens = pipe.rformer(
|
| 995 |
+
cond_hidden_states=example_origin_hidden_states,
|
| 996 |
+
target_hidden_states=example_target_hidden_states).last_hidden_state[:, :query_num]
|
| 997 |
+
|
| 998 |
+
for layer in pipe.connector.encoder.layers:
|
| 999 |
+
latent_motion_tokens=gradient_checkpoint_forward(layer,True,False,latent_motion_tokens)
|
| 1000 |
+
operation_emb=pipe.connector.proj(latent_motion_tokens)
|
| 1001 |
+
|
| 1002 |
+
|
| 1003 |
+
return {"operation_emb": operation_emb}
|
| 1004 |
+
|
| 1005 |
+
|
| 1006 |
+
|
| 1007 |
+
class QwenImageUnit_MirrorPPROperationEncoder2D(PipelineUnit):
|
| 1008 |
+
def __init__(self):
|
| 1009 |
+
super().__init__(
|
| 1010 |
+
input_params=("example_origin","example_target","shape_1"),
|
| 1011 |
+
output_params=("operation_emb"),
|
| 1012 |
+
onload_model_names=("mae","rformer2d","connector")
|
| 1013 |
+
)
|
| 1014 |
+
|
| 1015 |
+
def process_image_for_tokenizer(self,image_input):
|
| 1016 |
+
"""
|
| 1017 |
+
Args:
|
| 1018 |
+
image_input: 单个 PIL.Image 对象,或者 PIL.Image 对象的列表。
|
| 1019 |
+
Returns:
|
| 1020 |
+
torch.Tensor:
|
| 1021 |
+
- 如果输入是单个图片: 返回 (3, H, W)
|
| 1022 |
+
- 如果输入是列表: 返回 (B, 3, H, W)
|
| 1023 |
+
- 数据类型 float32,已归一化。
|
| 1024 |
+
"""
|
| 1025 |
+
mean = torch.tensor([0.485, 0.456, 0.406]).view(3, 1, 1)
|
| 1026 |
+
std = torch.tensor([0.229, 0.224, 0.225]).view(3, 1, 1)
|
| 1027 |
+
|
| 1028 |
+
transforms = v2.Compose([
|
| 1029 |
+
v2.ToImage(),
|
| 1030 |
+
v2.ToDtype(torch.float32, scale=True),
|
| 1031 |
+
])
|
| 1032 |
+
|
| 1033 |
+
|
| 1034 |
+
def _process_one_tensor(img):
|
| 1035 |
+
|
| 1036 |
+
x = transforms(img)
|
| 1037 |
+
|
| 1038 |
+
|
| 1039 |
+
if mean.device != x.device:
|
| 1040 |
+
mean_dev = mean.to(x.device)
|
| 1041 |
+
std_dev = std.to(x.device)
|
| 1042 |
+
else:
|
| 1043 |
+
mean_dev, std_dev = mean, std
|
| 1044 |
+
|
| 1045 |
+
|
| 1046 |
+
x = (x - mean_dev) / (std_dev + 1e-6)
|
| 1047 |
+
return x
|
| 1048 |
+
|
| 1049 |
+
if isinstance(image_input, list):
|
| 1050 |
+
tensors = [_process_one_tensor(img) for img in image_input]
|
| 1051 |
+
return torch.stack(tensors)
|
| 1052 |
+
else:
|
| 1053 |
+
|
| 1054 |
+
return _process_one_tensor(image_input).unsqueeze(0)
|
| 1055 |
+
|
| 1056 |
+
|
| 1057 |
+
def process(self, pipe: QwenImagePipeline,example_origin,example_target,shape_1):
|
| 1058 |
+
if pipe.rformer is not None:
|
| 1059 |
+
return {}
|
| 1060 |
+
if pipe.extractor is not None:
|
| 1061 |
+
return {}
|
| 1062 |
+
|
| 1063 |
+
|
| 1064 |
+
h1,w1=shape_1
|
| 1065 |
+
new_shape_1 = [torch.tensor([h1]), torch.tensor([w1])]
|
| 1066 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 1067 |
+
|
| 1068 |
+
|
| 1069 |
+
|
| 1070 |
+
|
| 1071 |
+
|
| 1072 |
+
|
| 1073 |
+
|
| 1074 |
+
example_origin_pixel_values = self.process_image_for_tokenizer(example_origin).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 1075 |
+
example_target_pixel_values = self.process_image_for_tokenizer(example_target).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 1076 |
+
example_origin_hidden_states=pipe.mae(example_origin_pixel_values)
|
| 1077 |
+
example_target_hidden_states=pipe.mae(example_target_pixel_values)
|
| 1078 |
+
query_num = pipe.rformer2d.query_num
|
| 1079 |
+
latent_motion_tokens = pipe.rformer2d(
|
| 1080 |
+
cond_hidden_states=example_origin_hidden_states,
|
| 1081 |
+
target_hidden_states=example_target_hidden_states,sample1_shapes=new_shape_1).last_hidden_state[:, :query_num]
|
| 1082 |
+
|
| 1083 |
+
for layer in pipe.connector.encoder.layers:
|
| 1084 |
+
latent_motion_tokens=gradient_checkpoint_forward(layer,True,False,latent_motion_tokens)
|
| 1085 |
+
operation_emb=pipe.connector.proj(latent_motion_tokens)
|
| 1086 |
+
|
| 1087 |
+
|
| 1088 |
+
return {"operation_emb": operation_emb}
|
| 1089 |
+
|
| 1090 |
+
|
| 1091 |
+
class QwenImageUnit_PromptEmbedderWithExtractor(PipelineUnit):
|
| 1092 |
+
def __init__(self):
|
| 1093 |
+
super().__init__(
|
| 1094 |
+
input_params=("prompt", "edit_image", "tiled", "tile_size", "tile_stride", "edit_image_auto_resize"),
|
| 1095 |
+
output_params=("prompt_emb", "prompt_emb_mask", "image_vl_emb","edit_latents","edit_image"),
|
| 1096 |
+
onload_model_names=("vae", "extractor", "text_encoder")
|
| 1097 |
+
)
|
| 1098 |
+
|
| 1099 |
+
def extract_masked_hidden(self, hidden_states: torch.Tensor, mask: torch.Tensor):
|
| 1100 |
+
bool_mask = mask.bool()
|
| 1101 |
+
valid_lengths = bool_mask.sum(dim=1)
|
| 1102 |
+
selected = hidden_states[bool_mask]
|
| 1103 |
+
split_result = torch.split(selected, valid_lengths.tolist(), dim=0)
|
| 1104 |
+
return split_result
|
| 1105 |
+
|
| 1106 |
+
def calculate_dimensions(self, target_area, ratio):
|
| 1107 |
+
width = math.sqrt(target_area * ratio)
|
| 1108 |
+
height = width / ratio
|
| 1109 |
+
width = round(width / 32) * 32
|
| 1110 |
+
height = round(height / 32) * 32
|
| 1111 |
+
return width, height
|
| 1112 |
+
|
| 1113 |
+
def edit_image_auto_resize(self, edit_image):
|
| 1114 |
+
calculated_width, calculated_height = self.calculate_dimensions(1024 * 1024, edit_image.size[0] / edit_image.size[1])
|
| 1115 |
+
return edit_image.resize((calculated_width, calculated_height))
|
| 1116 |
+
|
| 1117 |
+
def encode_image_vl_emb(self, pipe: QwenImagePipeline, prompt, edit_image):
|
| 1118 |
+
template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>{}<|im_end|>\n<|im_start|>assistant\n"
|
| 1119 |
+
drop_idx = 64
|
| 1120 |
+
txt = [template.format(e) for e in prompt]
|
| 1121 |
+
model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
|
| 1122 |
+
hidden_states = pipe.text_encoder(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw, output_hidden_states=True,)[-1]
|
| 1123 |
+
split_hidden_states = self.extract_masked_hidden(hidden_states, model_inputs.attention_mask)
|
| 1124 |
+
split_hidden_states = [e[drop_idx:] for e in split_hidden_states]
|
| 1125 |
+
return split_hidden_states
|
| 1126 |
+
|
| 1127 |
+
|
| 1128 |
+
def encode_prompt_edit(self, pipe: QwenImagePipeline, prompt, edit_image,extractor_emb):
|
| 1129 |
+
template = "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>{}<|im_end|>\n<|im_start|>assistant\n"
|
| 1130 |
+
drop_idx = 64
|
| 1131 |
+
txt = [template.format(e) for e in prompt]
|
| 1132 |
+
model_inputs = pipe.processor(text=txt, images=edit_image, padding=True, return_tensors="pt").to(pipe.device)
|
| 1133 |
+
|
| 1134 |
+
|
| 1135 |
+
inputs_embeds,position_ids=pipe.text_encoder.vision_forward(input_ids=model_inputs.input_ids, attention_mask=model_inputs.attention_mask, pixel_values=model_inputs.pixel_values, image_grid_thw=model_inputs.image_grid_thw)
|
| 1136 |
+
batch_size = inputs_embeds.shape[0]
|
| 1137 |
+
num_queries = extractor_emb.shape[1]
|
| 1138 |
+
inputs_embeds = torch.cat([inputs_embeds, extractor_emb], dim=1)
|
| 1139 |
+
|
| 1140 |
+
max_pos = position_ids.max(dim=-1, keepdim=True).values
|
| 1141 |
+
query_pos = torch.arange(1, num_queries + 1, device=position_ids.device).view(1, 1, -1).expand(3, batch_size, -1)
|
| 1142 |
+
position_ids = torch.cat([position_ids, max_pos + query_pos], dim=-1)
|
| 1143 |
+
attention_mask = torch.cat([model_inputs.attention_mask, torch.ones((batch_size, num_queries), device=model_inputs.attention_mask.device, dtype=model_inputs.attention_mask.dtype)], dim=1)
|
| 1144 |
+
hidden_states=pipe.text_encoder.llm_forward(inputs_embeds=inputs_embeds,position_ids=position_ids,attention_mask=attention_mask,output_hidden_states=True)[-1]
|
| 1145 |
+
|
| 1146 |
+
return [h[-num_queries:] for h in hidden_states]
|
| 1147 |
+
|
| 1148 |
+
|
| 1149 |
+
def process(self, pipe: QwenImagePipeline, prompt, edit_image, tiled, tile_size, tile_stride, edit_image_auto_resize=False):
|
| 1150 |
+
if pipe.extractor is None:
|
| 1151 |
+
|
| 1152 |
+
return {}
|
| 1153 |
+
|
| 1154 |
+
print("using QwenImageUnit_PromptEmbedderWithExtractor")
|
| 1155 |
+
|
| 1156 |
+
pipe.load_models_to_device(self.onload_model_names)
|
| 1157 |
+
|
| 1158 |
+
|
| 1159 |
+
if isinstance(edit_image, list):
|
| 1160 |
+
edit_images_list = edit_image
|
| 1161 |
+
else:
|
| 1162 |
+
edit_images_list = [edit_image]
|
| 1163 |
+
|
| 1164 |
+
latents_list = []
|
| 1165 |
+
processed_images = []
|
| 1166 |
+
|
| 1167 |
+
for img in edit_images_list:
|
| 1168 |
+
if edit_image_auto_resize:
|
| 1169 |
+
img = self.edit_image_auto_resize(img)
|
| 1170 |
+
processed_images.append(img)
|
| 1171 |
+
|
| 1172 |
+
|
| 1173 |
+
img_tensor = pipe.preprocess_image(img).to(device=pipe.device, dtype=pipe.torch_dtype)
|
| 1174 |
+
latent = pipe.vae.encode(img_tensor, tiled=tiled, tile_size=tile_size, tile_stride=tile_stride)
|
| 1175 |
+
latents_list.append(latent)
|
| 1176 |
+
|
| 1177 |
+
|
| 1178 |
+
|
| 1179 |
+
|
| 1180 |
+
latents_stacked = torch.stack(latents_list, dim=1)
|
| 1181 |
+
|
| 1182 |
+
|
| 1183 |
+
|
| 1184 |
+
|
| 1185 |
+
extractor_emb = pipe.extractor(latents_stacked)
|
| 1186 |
+
|
| 1187 |
+
|
| 1188 |
+
|
| 1189 |
+
|
| 1190 |
+
|
| 1191 |
+
prompt1=[""]
|
| 1192 |
+
split_hidden_states = self.encode_prompt_edit(pipe, prompt1, edit_image[0],extractor_emb)
|
| 1193 |
+
prompt2=["Generate the retouched version of the input image."]
|
| 1194 |
+
image_split_hidden_states=self.encode_image_vl_emb(pipe, prompt2, edit_image[0])
|
| 1195 |
+
|
| 1196 |
+
|
| 1197 |
+
|
| 1198 |
+
|
| 1199 |
+
attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in split_hidden_states]
|
| 1200 |
+
max_seq_len = max([e.size(0) for e in split_hidden_states])
|
| 1201 |
+
prompt_embeds = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0), u.size(1))]) for u in split_hidden_states])
|
| 1202 |
+
encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(max_seq_len - u.size(0))]) for u in attn_mask_list])
|
| 1203 |
+
prompt_embeds = prompt_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
|
| 1204 |
+
|
| 1205 |
+
|
| 1206 |
+
image_attn_mask_list = [torch.ones(e.size(0), dtype=torch.long, device=e.device) for e in image_split_hidden_states]
|
| 1207 |
+
image_max_seq_len = max([e.size(0) for e in image_split_hidden_states])
|
| 1208 |
+
image_embeds = torch.stack([torch.cat([u, u.new_zeros(image_max_seq_len - u.size(0), u.size(1))]) for u in image_split_hidden_states])
|
| 1209 |
+
image_encoder_attention_mask = torch.stack([torch.cat([u, u.new_zeros(image_max_seq_len - u.size(0))]) for u in image_attn_mask_list])
|
| 1210 |
+
image_embeds = image_embeds.to(dtype=pipe.torch_dtype, device=pipe.device)
|
| 1211 |
+
|
| 1212 |
+
combined_attention_mask = torch.cat([image_encoder_attention_mask,encoder_attention_mask], dim=1)
|
| 1213 |
+
edit_latents=[latents_list[0]]
|
| 1214 |
+
return {"prompt_emb": prompt_embeds, "prompt_emb_mask": combined_attention_mask,"image_vl_emb":image_embeds,"edit_latents": edit_latents, "edit_image": processed_images}
|
| 1215 |
+
|
| 1216 |
+
|
| 1217 |
+
|
| 1218 |
+
|
| 1219 |
+
def model_fn_qwen_image(
|
| 1220 |
+
dit: QwenImageDiT = None,
|
| 1221 |
+
blockwise_controlnet: QwenImageBlockwiseMultiControlNet = None,
|
| 1222 |
+
|
| 1223 |
+
latents=None,
|
| 1224 |
+
timestep=None,
|
| 1225 |
+
prompt_emb=None,
|
| 1226 |
+
prompt_emb_mask=None,
|
| 1227 |
+
|
| 1228 |
+
operation_emb=None,
|
| 1229 |
+
height=None,
|
| 1230 |
+
width=None,
|
| 1231 |
+
blockwise_controlnet_conditioning=None,
|
| 1232 |
+
blockwise_controlnet_inputs=None,
|
| 1233 |
+
progress_id=0,
|
| 1234 |
+
num_inference_steps=1,
|
| 1235 |
+
entity_prompt_emb=None,
|
| 1236 |
+
entity_prompt_emb_mask=None,
|
| 1237 |
+
entity_masks=None,
|
| 1238 |
+
edit_latents=None,
|
| 1239 |
+
context_latents=None,
|
| 1240 |
+
enable_fp8_attention=False,
|
| 1241 |
+
use_gradient_checkpointing=False,
|
| 1242 |
+
use_gradient_checkpointing_offload=False,
|
| 1243 |
+
edit_rope_interpolation=False,
|
| 1244 |
+
zero_cond_t=False,
|
| 1245 |
+
output_connector_embeds=False,
|
| 1246 |
+
no_qwenvl=False,
|
| 1247 |
+
**kwargs
|
| 1248 |
+
):
|
| 1249 |
+
|
| 1250 |
+
|
| 1251 |
+
|
| 1252 |
+
|
| 1253 |
+
|
| 1254 |
+
|
| 1255 |
+
img_shapes = [(latents.shape[0], latents.shape[2]//2, latents.shape[3]//2)]
|
| 1256 |
+
|
| 1257 |
+
timestep = timestep / 1000
|
| 1258 |
+
|
| 1259 |
+
image = rearrange(latents, "B C (H P) (W Q) -> B (H W) (C P Q)", H=height//16, W=width//16, P=2, Q=2)
|
| 1260 |
+
image_seq_len = image.shape[1]
|
| 1261 |
+
|
| 1262 |
+
|
| 1263 |
+
|
| 1264 |
+
if context_latents is not None:
|
| 1265 |
+
img_shapes += [(context_latents.shape[0], context_latents.shape[2]//2, context_latents.shape[3]//2)]
|
| 1266 |
+
context_image = rearrange(context_latents, "B C (H P) (W Q) -> B (H W) (C P Q)", H=context_latents.shape[2]//2, W=context_latents.shape[3]//2, P=2, Q=2)
|
| 1267 |
+
image = torch.cat([image, context_image], dim=1)
|
| 1268 |
+
if edit_latents is not None:
|
| 1269 |
+
edit_latents_list = edit_latents if isinstance(edit_latents, list) else [edit_latents]
|
| 1270 |
+
img_shapes += [(e.shape[0], e.shape[2]//2, e.shape[3]//2) for e in edit_latents_list]
|
| 1271 |
+
edit_image = [rearrange(e, "B C (H P) (W Q) -> B (H W) (C P Q)", H=e.shape[2]//2, W=e.shape[3]//2, P=2, Q=2) for e in edit_latents_list]
|
| 1272 |
+
|
| 1273 |
+
image = torch.cat([image] + edit_image, dim=1)
|
| 1274 |
+
|
| 1275 |
+
|
| 1276 |
+
|
| 1277 |
+
|
| 1278 |
+
image = dit.img_in(image)
|
| 1279 |
+
if zero_cond_t:
|
| 1280 |
+
timestep = torch.cat([timestep, timestep * 0], dim=0)
|
| 1281 |
+
modulate_index = torch.tensor(
|
| 1282 |
+
[[0] * prod(sample[0]) + [1] * sum([prod(s) for s in sample[1:]]) for sample in [img_shapes]],
|
| 1283 |
+
device=timestep.device,
|
| 1284 |
+
dtype=torch.int,
|
| 1285 |
+
)
|
| 1286 |
+
else:
|
| 1287 |
+
modulate_index = None
|
| 1288 |
+
conditioning = dit.time_text_embed(timestep, image.dtype)
|
| 1289 |
+
|
| 1290 |
+
|
| 1291 |
+
|
| 1292 |
+
|
| 1293 |
+
|
| 1294 |
+
|
| 1295 |
+
|
| 1296 |
+
|
| 1297 |
+
|
| 1298 |
+
|
| 1299 |
+
if prompt_emb is not None and prompt_emb_mask is not None and not no_qwenvl:
|
| 1300 |
+
final_prompt_emb = torch.cat([prompt_emb,operation_emb], dim=1)
|
| 1301 |
+
txt_seq_lens = prompt_emb_mask.sum(dim=1).tolist()
|
| 1302 |
+
txt_seq_lens = [x + operation_emb.shape[1] for x in txt_seq_lens]
|
| 1303 |
+
else:
|
| 1304 |
+
|
| 1305 |
+
|
| 1306 |
+
final_prompt_emb = operation_emb
|
| 1307 |
+
txt_seq_lens = [operation_emb.shape[1]] * operation_emb.shape[0]
|
| 1308 |
+
|
| 1309 |
+
|
| 1310 |
+
|
| 1311 |
+
|
| 1312 |
+
|
| 1313 |
+
|
| 1314 |
+
|
| 1315 |
+
|
| 1316 |
+
|
| 1317 |
+
|
| 1318 |
+
if entity_prompt_emb is not None:
|
| 1319 |
+
|
| 1320 |
+
text, image_rotary_emb, attention_mask = dit.process_entity_masks(
|
| 1321 |
+
latents, final_prompt_emb, prompt_emb_mask, entity_prompt_emb, entity_prompt_emb_mask,
|
| 1322 |
+
entity_masks, height, width, image, img_shapes,
|
| 1323 |
+
)
|
| 1324 |
+
else:
|
| 1325 |
+
|
| 1326 |
+
text = dit.txt_in(dit.txt_norm(final_prompt_emb))
|
| 1327 |
+
if edit_rope_interpolation:
|
| 1328 |
+
image_rotary_emb = dit.pos_embed.forward_sampling(img_shapes, txt_seq_lens, device=latents.device)
|
| 1329 |
+
else:
|
| 1330 |
+
image_rotary_emb = dit.pos_embed(img_shapes, txt_seq_lens, device=latents.device)
|
| 1331 |
+
attention_mask = None
|
| 1332 |
+
|
| 1333 |
+
if blockwise_controlnet_conditioning is not None:
|
| 1334 |
+
blockwise_controlnet_conditioning = blockwise_controlnet.preprocess(
|
| 1335 |
+
blockwise_controlnet_inputs, blockwise_controlnet_conditioning)
|
| 1336 |
+
|
| 1337 |
+
for block_id, block in enumerate(dit.transformer_blocks):
|
| 1338 |
+
text, image = gradient_checkpoint_forward(
|
| 1339 |
+
block,
|
| 1340 |
+
use_gradient_checkpointing,
|
| 1341 |
+
use_gradient_checkpointing_offload,
|
| 1342 |
+
image=image,
|
| 1343 |
+
text=text,
|
| 1344 |
+
temb=conditioning,
|
| 1345 |
+
image_rotary_emb=image_rotary_emb,
|
| 1346 |
+
attention_mask=attention_mask,
|
| 1347 |
+
enable_fp8_attention=enable_fp8_attention,
|
| 1348 |
+
modulate_index=modulate_index,
|
| 1349 |
+
)
|
| 1350 |
+
if blockwise_controlnet_conditioning is not None:
|
| 1351 |
+
image_slice = image[:, :image_seq_len].clone()
|
| 1352 |
+
controlnet_output = blockwise_controlnet.blockwise_forward(
|
| 1353 |
+
image=image_slice, conditionings=blockwise_controlnet_conditioning,
|
| 1354 |
+
controlnet_inputs=blockwise_controlnet_inputs, block_id=block_id,
|
| 1355 |
+
progress_id=progress_id, num_inference_steps=num_inference_steps,
|
| 1356 |
+
)
|
| 1357 |
+
image[:, :image_seq_len] = image_slice + controlnet_output
|
| 1358 |
+
|
| 1359 |
+
if zero_cond_t:
|
| 1360 |
+
conditioning = conditioning.chunk(2, dim=0)[0]
|
| 1361 |
+
|
| 1362 |
+
image = dit.norm_out(image, conditioning)
|
| 1363 |
+
image = dit.proj_out(image)
|
| 1364 |
+
image = image[:, :image_seq_len]
|
| 1365 |
+
|
| 1366 |
+
latents = rearrange(image, "B (H W) (C P Q) -> B C (H P) (W Q)", H=height//16, W=width//16, P=2, Q=2)
|
| 1367 |
+
|
| 1368 |
+
|
| 1369 |
+
if output_connector_embeds:
|
| 1370 |
+
return latents,prompt_emb
|
| 1371 |
+
else:
|
| 1372 |
+
return latents
|
| 1373 |
+
|
| 1374 |
+
|
diffsynth/utils/__init__.py
ADDED
|
File without changes
|
diffsynth/utils/controlnet/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .controlnet_input import ControlNetInput
|
diffsynth/utils/controlnet/controlnet_input.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
from PIL import Image
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@dataclass
|
| 6 |
+
class ControlNetInput:
|
| 7 |
+
controlnet_id: int = 0
|
| 8 |
+
scale: float = 1.0
|
| 9 |
+
start: float = 1.0
|
| 10 |
+
end: float = 0.0
|
| 11 |
+
image: Image.Image = None
|
| 12 |
+
inpaint_mask: Image.Image = None
|
| 13 |
+
processor_id: str = None
|
diffsynth/utils/lora/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .general import GeneralLoRALoader
|