Upload folder using huggingface_hub (part 3)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- ComfyUI/comfy/ldm/flux/__pycache__/redux.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/flux/controlnet.py +208 -0
- ComfyUI/comfy/ldm/flux/layers.py +372 -0
- ComfyUI/comfy/ldm/flux/math.py +58 -0
- ComfyUI/comfy/ldm/flux/model.py +408 -0
- ComfyUI/comfy/ldm/flux/redux.py +25 -0
- ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/asymm_models_joint.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/layers.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/rope_mixed.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/temporal_rope.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/utils.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/genmo/joint_model/asymm_models_joint.py +561 -0
- ComfyUI/comfy/ldm/genmo/joint_model/layers.py +153 -0
- ComfyUI/comfy/ldm/genmo/joint_model/rope_mixed.py +88 -0
- ComfyUI/comfy/ldm/genmo/joint_model/temporal_rope.py +34 -0
- ComfyUI/comfy/ldm/genmo/joint_model/utils.py +102 -0
- ComfyUI/comfy/ldm/genmo/vae/__pycache__/model.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/genmo/vae/model.py +711 -0
- ComfyUI/comfy/ldm/hidream/__pycache__/model.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hidream/model.py +829 -0
- ComfyUI/comfy/ldm/hidream_o1/__pycache__/attention.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hidream_o1/__pycache__/conditioning.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hidream_o1/__pycache__/model.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hidream_o1/__pycache__/utils.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hidream_o1/attention.py +41 -0
- ComfyUI/comfy/ldm/hidream_o1/conditioning.py +230 -0
- ComfyUI/comfy/ldm/hidream_o1/model.py +306 -0
- ComfyUI/comfy/ldm/hidream_o1/utils.py +173 -0
- ComfyUI/comfy/ldm/hunyuan3d/__pycache__/model.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hunyuan3d/__pycache__/vae.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hunyuan3d/model.py +148 -0
- ComfyUI/comfy/ldm/hunyuan3d/vae.py +988 -0
- ComfyUI/comfy/ldm/hunyuan3dv2_1/__pycache__/hunyuandit.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hunyuan3dv2_1/hunyuandit.py +668 -0
- ComfyUI/comfy/ldm/hunyuan_video/__pycache__/model.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hunyuan_video/__pycache__/upsampler.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hunyuan_video/__pycache__/vae.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hunyuan_video/__pycache__/vae_refiner.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hunyuan_video/model.py +497 -0
- ComfyUI/comfy/ldm/hunyuan_video/upsampler.py +122 -0
- ComfyUI/comfy/ldm/hunyuan_video/vae.py +136 -0
- ComfyUI/comfy/ldm/hunyuan_video/vae_refiner.py +313 -0
- ComfyUI/comfy/ldm/hydit/__pycache__/attn_layers.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hydit/__pycache__/controlnet.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hydit/__pycache__/models.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hydit/__pycache__/poolers.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hydit/__pycache__/posemb_layers.cpython-312.pyc +0 -0
- ComfyUI/comfy/ldm/hydit/attn_layers.py +218 -0
- ComfyUI/comfy/ldm/hydit/controlnet.py +311 -0
.gitattributes
CHANGED
|
@@ -39,3 +39,4 @@ ComfyUI/comfy/__pycache__/model_patcher.cpython-312.pyc filter=lfs diff=lfs merg
|
|
| 39 |
ComfyUI/comfy/__pycache__/sd.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 40 |
ComfyUI/comfy/__pycache__/supported_models.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 41 |
ComfyUI/comfy/k_diffusion/__pycache__/sampling.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 39 |
ComfyUI/comfy/__pycache__/sd.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 40 |
ComfyUI/comfy/__pycache__/supported_models.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 41 |
ComfyUI/comfy/k_diffusion/__pycache__/sampling.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
ComfyUI/comfy/ldm/sam3/__pycache__/tracker.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
|
ComfyUI/comfy/ldm/flux/__pycache__/redux.cpython-312.pyc
ADDED
|
Binary file (1.63 kB). View file
|
|
|
ComfyUI/comfy/ldm/flux/controlnet.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Original code can be found on: https://github.com/XLabs-AI/x-flux/blob/main/src/flux/controlnet.py
|
| 2 |
+
#modified to support different types of flux controlnets
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import math
|
| 6 |
+
from torch import Tensor, nn
|
| 7 |
+
from einops import rearrange, repeat
|
| 8 |
+
|
| 9 |
+
from .layers import (timestep_embedding)
|
| 10 |
+
|
| 11 |
+
from .model import Flux
|
| 12 |
+
import comfy.ldm.common_dit
|
| 13 |
+
|
| 14 |
+
class MistolineCondDownsamplBlock(nn.Module):
|
| 15 |
+
def __init__(self, dtype=None, device=None, operations=None):
|
| 16 |
+
super().__init__()
|
| 17 |
+
self.encoder = nn.Sequential(
|
| 18 |
+
operations.Conv2d(3, 16, 3, padding=1, dtype=dtype, device=device),
|
| 19 |
+
nn.SiLU(),
|
| 20 |
+
operations.Conv2d(16, 16, 1, dtype=dtype, device=device),
|
| 21 |
+
nn.SiLU(),
|
| 22 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
|
| 23 |
+
nn.SiLU(),
|
| 24 |
+
operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
|
| 25 |
+
nn.SiLU(),
|
| 26 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
|
| 27 |
+
nn.SiLU(),
|
| 28 |
+
operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
|
| 29 |
+
nn.SiLU(),
|
| 30 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
|
| 31 |
+
nn.SiLU(),
|
| 32 |
+
operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
|
| 33 |
+
nn.SiLU(),
|
| 34 |
+
operations.Conv2d(16, 16, 1, dtype=dtype, device=device),
|
| 35 |
+
nn.SiLU(),
|
| 36 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device)
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
def forward(self, x):
|
| 40 |
+
return self.encoder(x)
|
| 41 |
+
|
| 42 |
+
class MistolineControlnetBlock(nn.Module):
|
| 43 |
+
def __init__(self, hidden_size, dtype=None, device=None, operations=None):
|
| 44 |
+
super().__init__()
|
| 45 |
+
self.linear = operations.Linear(hidden_size, hidden_size, dtype=dtype, device=device)
|
| 46 |
+
self.act = nn.SiLU()
|
| 47 |
+
|
| 48 |
+
def forward(self, x):
|
| 49 |
+
return self.act(self.linear(x))
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class ControlNetFlux(Flux):
|
| 53 |
+
def __init__(self, latent_input=False, num_union_modes=0, mistoline=False, control_latent_channels=None, image_model=None, dtype=None, device=None, operations=None, **kwargs):
|
| 54 |
+
super().__init__(final_layer=False, dtype=dtype, device=device, operations=operations, **kwargs)
|
| 55 |
+
|
| 56 |
+
self.main_model_double = 19
|
| 57 |
+
self.main_model_single = 38
|
| 58 |
+
|
| 59 |
+
self.mistoline = mistoline
|
| 60 |
+
# add ControlNet blocks
|
| 61 |
+
if self.mistoline:
|
| 62 |
+
control_block = lambda : MistolineControlnetBlock(self.hidden_size, dtype=dtype, device=device, operations=operations)
|
| 63 |
+
else:
|
| 64 |
+
control_block = lambda : operations.Linear(self.hidden_size, self.hidden_size, dtype=dtype, device=device)
|
| 65 |
+
|
| 66 |
+
self.controlnet_blocks = nn.ModuleList([])
|
| 67 |
+
for _ in range(self.params.depth):
|
| 68 |
+
self.controlnet_blocks.append(control_block())
|
| 69 |
+
|
| 70 |
+
self.controlnet_single_blocks = nn.ModuleList([])
|
| 71 |
+
for _ in range(self.params.depth_single_blocks):
|
| 72 |
+
self.controlnet_single_blocks.append(control_block())
|
| 73 |
+
|
| 74 |
+
self.num_union_modes = num_union_modes
|
| 75 |
+
self.controlnet_mode_embedder = None
|
| 76 |
+
if self.num_union_modes > 0:
|
| 77 |
+
self.controlnet_mode_embedder = operations.Embedding(self.num_union_modes, self.hidden_size, dtype=dtype, device=device)
|
| 78 |
+
|
| 79 |
+
self.gradient_checkpointing = False
|
| 80 |
+
self.latent_input = latent_input
|
| 81 |
+
if control_latent_channels is None:
|
| 82 |
+
control_latent_channels = self.in_channels
|
| 83 |
+
else:
|
| 84 |
+
control_latent_channels *= 2 * 2 #patch size
|
| 85 |
+
|
| 86 |
+
self.pos_embed_input = operations.Linear(control_latent_channels, self.hidden_size, bias=True, dtype=dtype, device=device)
|
| 87 |
+
if not self.latent_input:
|
| 88 |
+
if self.mistoline:
|
| 89 |
+
self.input_cond_block = MistolineCondDownsamplBlock(dtype=dtype, device=device, operations=operations)
|
| 90 |
+
else:
|
| 91 |
+
self.input_hint_block = nn.Sequential(
|
| 92 |
+
operations.Conv2d(3, 16, 3, padding=1, dtype=dtype, device=device),
|
| 93 |
+
nn.SiLU(),
|
| 94 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
|
| 95 |
+
nn.SiLU(),
|
| 96 |
+
operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
|
| 97 |
+
nn.SiLU(),
|
| 98 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
|
| 99 |
+
nn.SiLU(),
|
| 100 |
+
operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
|
| 101 |
+
nn.SiLU(),
|
| 102 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
|
| 103 |
+
nn.SiLU(),
|
| 104 |
+
operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
|
| 105 |
+
nn.SiLU(),
|
| 106 |
+
operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device)
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
def forward_orig(
|
| 110 |
+
self,
|
| 111 |
+
img: Tensor,
|
| 112 |
+
img_ids: Tensor,
|
| 113 |
+
controlnet_cond: Tensor,
|
| 114 |
+
txt: Tensor,
|
| 115 |
+
txt_ids: Tensor,
|
| 116 |
+
timesteps: Tensor,
|
| 117 |
+
y: Tensor,
|
| 118 |
+
guidance: Tensor = None,
|
| 119 |
+
control_type: Tensor = None,
|
| 120 |
+
) -> Tensor:
|
| 121 |
+
if img.ndim != 3 or txt.ndim != 3:
|
| 122 |
+
raise ValueError("Input img and txt tensors must have 3 dimensions.")
|
| 123 |
+
|
| 124 |
+
if y is None:
|
| 125 |
+
y = torch.zeros((img.shape[0], self.params.vec_in_dim), device=img.device, dtype=img.dtype)
|
| 126 |
+
else:
|
| 127 |
+
y = y[:, :self.params.vec_in_dim]
|
| 128 |
+
|
| 129 |
+
# running on sequences img
|
| 130 |
+
img = self.img_in(img)
|
| 131 |
+
|
| 132 |
+
controlnet_cond = self.pos_embed_input(controlnet_cond)
|
| 133 |
+
img = img + controlnet_cond
|
| 134 |
+
vec = self.time_in(timestep_embedding(timesteps, 256))
|
| 135 |
+
if self.params.guidance_embed:
|
| 136 |
+
vec = vec + self.guidance_in(timestep_embedding(guidance, 256))
|
| 137 |
+
vec = vec + self.vector_in(y)
|
| 138 |
+
txt = self.txt_in(txt)
|
| 139 |
+
|
| 140 |
+
if self.controlnet_mode_embedder is not None and len(control_type) > 0:
|
| 141 |
+
control_cond = self.controlnet_mode_embedder(torch.tensor(control_type, device=img.device), out_dtype=img.dtype).unsqueeze(0).repeat((txt.shape[0], 1, 1))
|
| 142 |
+
txt = torch.cat([control_cond, txt], dim=1)
|
| 143 |
+
txt_ids = torch.cat([txt_ids[:,:1], txt_ids], dim=1)
|
| 144 |
+
|
| 145 |
+
ids = torch.cat((txt_ids, img_ids), dim=1)
|
| 146 |
+
pe = self.pe_embedder(ids)
|
| 147 |
+
|
| 148 |
+
controlnet_double = ()
|
| 149 |
+
|
| 150 |
+
for i in range(len(self.double_blocks)):
|
| 151 |
+
img, txt = self.double_blocks[i](img=img, txt=txt, vec=vec, pe=pe)
|
| 152 |
+
controlnet_double = controlnet_double + (self.controlnet_blocks[i](img),)
|
| 153 |
+
|
| 154 |
+
img = torch.cat((txt, img), 1)
|
| 155 |
+
|
| 156 |
+
controlnet_single = ()
|
| 157 |
+
|
| 158 |
+
for i in range(len(self.single_blocks)):
|
| 159 |
+
img = self.single_blocks[i](img, vec=vec, pe=pe)
|
| 160 |
+
controlnet_single = controlnet_single + (self.controlnet_single_blocks[i](img[:, txt.shape[1] :, ...]),)
|
| 161 |
+
|
| 162 |
+
repeat = math.ceil(self.main_model_double / len(controlnet_double))
|
| 163 |
+
if self.latent_input:
|
| 164 |
+
out_input = ()
|
| 165 |
+
for x in controlnet_double:
|
| 166 |
+
out_input += (x,) * repeat
|
| 167 |
+
else:
|
| 168 |
+
out_input = (controlnet_double * repeat)
|
| 169 |
+
|
| 170 |
+
out = {"input": out_input[:self.main_model_double]}
|
| 171 |
+
if len(controlnet_single) > 0:
|
| 172 |
+
repeat = math.ceil(self.main_model_single / len(controlnet_single))
|
| 173 |
+
out_output = ()
|
| 174 |
+
if self.latent_input:
|
| 175 |
+
for x in controlnet_single:
|
| 176 |
+
out_output += (x,) * repeat
|
| 177 |
+
else:
|
| 178 |
+
out_output = (controlnet_single * repeat)
|
| 179 |
+
out["output"] = out_output[:self.main_model_single]
|
| 180 |
+
return out
|
| 181 |
+
|
| 182 |
+
def forward(self, x, timesteps, context, y=None, guidance=None, hint=None, **kwargs):
|
| 183 |
+
patch_size = 2
|
| 184 |
+
if self.latent_input:
|
| 185 |
+
hint = comfy.ldm.common_dit.pad_to_patch_size(hint, (patch_size, patch_size))
|
| 186 |
+
elif self.mistoline:
|
| 187 |
+
hint = hint * 2.0 - 1.0
|
| 188 |
+
hint = self.input_cond_block(hint)
|
| 189 |
+
else:
|
| 190 |
+
hint = hint * 2.0 - 1.0
|
| 191 |
+
hint = self.input_hint_block(hint)
|
| 192 |
+
|
| 193 |
+
hint = rearrange(hint, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
|
| 194 |
+
|
| 195 |
+
bs, c, h, w = x.shape
|
| 196 |
+
x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size))
|
| 197 |
+
|
| 198 |
+
img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
|
| 199 |
+
|
| 200 |
+
h_len = ((h + (patch_size // 2)) // patch_size)
|
| 201 |
+
w_len = ((w + (patch_size // 2)) // patch_size)
|
| 202 |
+
img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype)
|
| 203 |
+
img_ids[..., 1] = img_ids[..., 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype)[:, None]
|
| 204 |
+
img_ids[..., 2] = img_ids[..., 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype)[None, :]
|
| 205 |
+
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
| 206 |
+
|
| 207 |
+
txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
|
| 208 |
+
return self.forward_orig(img, img_ids, hint, context, txt_ids, timesteps, y, guidance, control_type=kwargs.get("control_type", []))
|
ComfyUI/comfy/ldm/flux/layers.py
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
from dataclasses import dataclass
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from torch import Tensor, nn
|
| 6 |
+
|
| 7 |
+
from .math import attention, rope
|
| 8 |
+
|
| 9 |
+
# Fix import for some custom nodes, TODO: delete eventually.
|
| 10 |
+
RMSNorm = None
|
| 11 |
+
|
| 12 |
+
class EmbedND(nn.Module):
|
| 13 |
+
def __init__(self, dim: int, theta: int, axes_dim: list):
|
| 14 |
+
super().__init__()
|
| 15 |
+
self.dim = dim
|
| 16 |
+
self.theta = theta
|
| 17 |
+
self.axes_dim = axes_dim
|
| 18 |
+
|
| 19 |
+
def forward(self, ids: Tensor) -> Tensor:
|
| 20 |
+
n_axes = ids.shape[-1]
|
| 21 |
+
emb = torch.cat(
|
| 22 |
+
[rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)],
|
| 23 |
+
dim=-3,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
return emb.unsqueeze(1)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 1000.0):
|
| 30 |
+
"""
|
| 31 |
+
Create sinusoidal timestep embeddings.
|
| 32 |
+
:param t: a 1-D Tensor of N indices, one per batch element.
|
| 33 |
+
These may be fractional.
|
| 34 |
+
:param dim: the dimension of the output.
|
| 35 |
+
:param max_period: controls the minimum frequency of the embeddings.
|
| 36 |
+
:return: an (N, D) Tensor of positional embeddings.
|
| 37 |
+
"""
|
| 38 |
+
t = time_factor * t
|
| 39 |
+
half = dim // 2
|
| 40 |
+
freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=t.device) / half)
|
| 41 |
+
|
| 42 |
+
args = t[:, None].float() * freqs[None]
|
| 43 |
+
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 44 |
+
if dim % 2:
|
| 45 |
+
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
| 46 |
+
if torch.is_floating_point(t):
|
| 47 |
+
embedding = embedding.to(t)
|
| 48 |
+
return embedding
|
| 49 |
+
|
| 50 |
+
class MLPEmbedder(nn.Module):
|
| 51 |
+
def __init__(self, in_dim: int, hidden_dim: int, bias=True, dtype=None, device=None, operations=None):
|
| 52 |
+
super().__init__()
|
| 53 |
+
self.in_layer = operations.Linear(in_dim, hidden_dim, bias=bias, dtype=dtype, device=device)
|
| 54 |
+
self.silu = nn.SiLU()
|
| 55 |
+
self.out_layer = operations.Linear(hidden_dim, hidden_dim, bias=bias, dtype=dtype, device=device)
|
| 56 |
+
|
| 57 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 58 |
+
return self.out_layer(self.silu(self.in_layer(x)))
|
| 59 |
+
|
| 60 |
+
class YakMLP(nn.Module):
|
| 61 |
+
def __init__(self, hidden_size: int, intermediate_size: int, dtype=None, device=None, operations=None):
|
| 62 |
+
super().__init__()
|
| 63 |
+
self.hidden_size = hidden_size
|
| 64 |
+
self.intermediate_size = intermediate_size
|
| 65 |
+
self.gate_proj = operations.Linear(self.hidden_size, self.intermediate_size, bias=True, dtype=dtype, device=device)
|
| 66 |
+
self.up_proj = operations.Linear(self.hidden_size, self.intermediate_size, bias=True, dtype=dtype, device=device)
|
| 67 |
+
self.down_proj = operations.Linear(self.intermediate_size, self.hidden_size, bias=True, dtype=dtype, device=device)
|
| 68 |
+
self.act_fn = nn.SiLU()
|
| 69 |
+
|
| 70 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 71 |
+
down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
|
| 72 |
+
return down_proj
|
| 73 |
+
|
| 74 |
+
def build_mlp(hidden_size, mlp_hidden_dim, mlp_silu_act=False, yak_mlp=False, dtype=None, device=None, operations=None):
|
| 75 |
+
if yak_mlp:
|
| 76 |
+
return YakMLP(hidden_size, mlp_hidden_dim, dtype=dtype, device=device, operations=operations)
|
| 77 |
+
if mlp_silu_act:
|
| 78 |
+
return nn.Sequential(
|
| 79 |
+
operations.Linear(hidden_size, mlp_hidden_dim * 2, bias=False, dtype=dtype, device=device),
|
| 80 |
+
SiLUActivation(),
|
| 81 |
+
operations.Linear(mlp_hidden_dim, hidden_size, bias=False, dtype=dtype, device=device),
|
| 82 |
+
)
|
| 83 |
+
else:
|
| 84 |
+
return nn.Sequential(
|
| 85 |
+
operations.Linear(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype, device=device),
|
| 86 |
+
nn.GELU(approximate="tanh"),
|
| 87 |
+
operations.Linear(mlp_hidden_dim, hidden_size, bias=True, dtype=dtype, device=device),
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class QKNorm(torch.nn.Module):
|
| 92 |
+
def __init__(self, dim: int, dtype=None, device=None, operations=None):
|
| 93 |
+
super().__init__()
|
| 94 |
+
self.query_norm = operations.RMSNorm(dim, dtype=dtype, device=device)
|
| 95 |
+
self.key_norm = operations.RMSNorm(dim, dtype=dtype, device=device)
|
| 96 |
+
|
| 97 |
+
def forward(self, q: Tensor, k: Tensor, v: Tensor) -> tuple:
|
| 98 |
+
q = self.query_norm(q)
|
| 99 |
+
k = self.key_norm(k)
|
| 100 |
+
return q.to(v), k.to(v)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class SelfAttention(nn.Module):
|
| 104 |
+
def __init__(self, dim: int, num_heads: int = 8, qkv_bias: bool = False, proj_bias: bool = True, dtype=None, device=None, operations=None):
|
| 105 |
+
super().__init__()
|
| 106 |
+
self.num_heads = num_heads
|
| 107 |
+
head_dim = dim // num_heads
|
| 108 |
+
|
| 109 |
+
self.qkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device)
|
| 110 |
+
self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations)
|
| 111 |
+
self.proj = operations.Linear(dim, dim, bias=proj_bias, dtype=dtype, device=device)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
@dataclass
|
| 115 |
+
class ModulationOut:
|
| 116 |
+
shift: Tensor
|
| 117 |
+
scale: Tensor
|
| 118 |
+
gate: Tensor
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
class Modulation(nn.Module):
|
| 122 |
+
def __init__(self, dim: int, double: bool, bias=True, dtype=None, device=None, operations=None):
|
| 123 |
+
super().__init__()
|
| 124 |
+
self.is_double = double
|
| 125 |
+
self.multiplier = 6 if double else 3
|
| 126 |
+
self.lin = operations.Linear(dim, self.multiplier * dim, bias=bias, dtype=dtype, device=device)
|
| 127 |
+
|
| 128 |
+
def forward(self, vec: Tensor) -> tuple:
|
| 129 |
+
if vec.ndim == 2:
|
| 130 |
+
vec = vec[:, None, :]
|
| 131 |
+
out = self.lin(nn.functional.silu(vec)).chunk(self.multiplier, dim=-1)
|
| 132 |
+
|
| 133 |
+
return (
|
| 134 |
+
ModulationOut(*out[:3]),
|
| 135 |
+
ModulationOut(*out[3:]) if self.is_double else None,
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def apply_mod(tensor, m_mult, m_add=None, modulation_dims=None):
|
| 140 |
+
if modulation_dims is None:
|
| 141 |
+
if m_add is not None:
|
| 142 |
+
return torch.addcmul(m_add, tensor, m_mult)
|
| 143 |
+
else:
|
| 144 |
+
return tensor * m_mult
|
| 145 |
+
else:
|
| 146 |
+
for d in modulation_dims:
|
| 147 |
+
tensor[:, d[0]:d[1]] *= m_mult[:, d[2]:d[2] + 1]
|
| 148 |
+
if m_add is not None:
|
| 149 |
+
tensor[:, d[0]:d[1]] += m_add[:, d[2]:d[2] + 1]
|
| 150 |
+
return tensor
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
class SiLUActivation(nn.Module):
|
| 154 |
+
def __init__(self):
|
| 155 |
+
super().__init__()
|
| 156 |
+
self.gate_fn = nn.SiLU()
|
| 157 |
+
|
| 158 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 159 |
+
x1, x2 = x.chunk(2, dim=-1)
|
| 160 |
+
return self.gate_fn(x1) * x2
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
class DoubleStreamBlock(nn.Module):
|
| 164 |
+
def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float, qkv_bias: bool = False, modulation=True, mlp_silu_act=False, proj_bias=True, yak_mlp=False, dtype=None, device=None, operations=None):
|
| 165 |
+
super().__init__()
|
| 166 |
+
|
| 167 |
+
mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
| 168 |
+
self.num_heads = num_heads
|
| 169 |
+
self.hidden_size = hidden_size
|
| 170 |
+
self.modulation = modulation
|
| 171 |
+
|
| 172 |
+
if self.modulation:
|
| 173 |
+
self.img_mod = Modulation(hidden_size, double=True, dtype=dtype, device=device, operations=operations)
|
| 174 |
+
|
| 175 |
+
self.img_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 176 |
+
self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, proj_bias=proj_bias, dtype=dtype, device=device, operations=operations)
|
| 177 |
+
|
| 178 |
+
self.img_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 179 |
+
|
| 180 |
+
self.img_mlp = build_mlp(hidden_size, mlp_hidden_dim, mlp_silu_act=mlp_silu_act, yak_mlp=yak_mlp, dtype=dtype, device=device, operations=operations)
|
| 181 |
+
|
| 182 |
+
if self.modulation:
|
| 183 |
+
self.txt_mod = Modulation(hidden_size, double=True, dtype=dtype, device=device, operations=operations)
|
| 184 |
+
|
| 185 |
+
self.txt_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 186 |
+
self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, proj_bias=proj_bias, dtype=dtype, device=device, operations=operations)
|
| 187 |
+
|
| 188 |
+
self.txt_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 189 |
+
|
| 190 |
+
self.txt_mlp = build_mlp(hidden_size, mlp_hidden_dim, mlp_silu_act=mlp_silu_act, yak_mlp=yak_mlp, dtype=dtype, device=device, operations=operations)
|
| 191 |
+
|
| 192 |
+
def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor, attn_mask=None, modulation_dims_img=None, modulation_dims_txt=None, transformer_options={}):
|
| 193 |
+
if self.modulation:
|
| 194 |
+
img_mod1, img_mod2 = self.img_mod(vec)
|
| 195 |
+
txt_mod1, txt_mod2 = self.txt_mod(vec)
|
| 196 |
+
else:
|
| 197 |
+
(img_mod1, img_mod2), (txt_mod1, txt_mod2) = vec
|
| 198 |
+
|
| 199 |
+
transformer_patches = transformer_options.get("patches", {})
|
| 200 |
+
extra_options = transformer_options.copy()
|
| 201 |
+
|
| 202 |
+
# prepare image for attention
|
| 203 |
+
img_modulated = self.img_norm1(img)
|
| 204 |
+
img_modulated = apply_mod(img_modulated, (1 + img_mod1.scale), img_mod1.shift, modulation_dims_img)
|
| 205 |
+
img_qkv = self.img_attn.qkv(img_modulated)
|
| 206 |
+
del img_modulated
|
| 207 |
+
img_q, img_k, img_v = img_qkv.view(img_qkv.shape[0], img_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
| 208 |
+
del img_qkv
|
| 209 |
+
img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
|
| 210 |
+
|
| 211 |
+
# prepare txt for attention
|
| 212 |
+
txt_modulated = self.txt_norm1(txt)
|
| 213 |
+
txt_modulated = apply_mod(txt_modulated, (1 + txt_mod1.scale), txt_mod1.shift, modulation_dims_txt)
|
| 214 |
+
txt_qkv = self.txt_attn.qkv(txt_modulated)
|
| 215 |
+
del txt_modulated
|
| 216 |
+
txt_q, txt_k, txt_v = txt_qkv.view(txt_qkv.shape[0], txt_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
| 217 |
+
del txt_qkv
|
| 218 |
+
txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
|
| 219 |
+
|
| 220 |
+
q = torch.cat((txt_q, img_q), dim=2)
|
| 221 |
+
del txt_q, img_q
|
| 222 |
+
k = torch.cat((txt_k, img_k), dim=2)
|
| 223 |
+
del txt_k, img_k
|
| 224 |
+
v = torch.cat((txt_v, img_v), dim=2)
|
| 225 |
+
del txt_v, img_v
|
| 226 |
+
|
| 227 |
+
extra_options["img_slice"] = [txt.shape[1], q.shape[2]]
|
| 228 |
+
if "attn1_patch" in transformer_patches:
|
| 229 |
+
patch = transformer_patches["attn1_patch"]
|
| 230 |
+
for p in patch:
|
| 231 |
+
out = p(q, k, v, pe=pe, attn_mask=attn_mask, extra_options=extra_options)
|
| 232 |
+
q, k, v, pe, attn_mask = out.get("q", q), out.get("k", k), out.get("v", v), out.get("pe", pe), out.get("attn_mask", attn_mask)
|
| 233 |
+
|
| 234 |
+
# run actual attention
|
| 235 |
+
attn = attention(q, k, v, pe=pe, mask=attn_mask, transformer_options=transformer_options)
|
| 236 |
+
del q, k, v
|
| 237 |
+
|
| 238 |
+
if "attn1_output_patch" in transformer_patches:
|
| 239 |
+
patch = transformer_patches["attn1_output_patch"]
|
| 240 |
+
for p in patch:
|
| 241 |
+
attn = p(attn, extra_options)
|
| 242 |
+
|
| 243 |
+
txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1]:]
|
| 244 |
+
|
| 245 |
+
# calculate the img bloks
|
| 246 |
+
img += apply_mod(self.img_attn.proj(img_attn), img_mod1.gate, None, modulation_dims_img)
|
| 247 |
+
del img_attn
|
| 248 |
+
img += apply_mod(self.img_mlp(apply_mod(self.img_norm2(img), (1 + img_mod2.scale), img_mod2.shift, modulation_dims_img)), img_mod2.gate, None, modulation_dims_img)
|
| 249 |
+
|
| 250 |
+
# calculate the txt bloks
|
| 251 |
+
txt += apply_mod(self.txt_attn.proj(txt_attn), txt_mod1.gate, None, modulation_dims_txt)
|
| 252 |
+
del txt_attn
|
| 253 |
+
txt += apply_mod(self.txt_mlp(apply_mod(self.txt_norm2(txt), (1 + txt_mod2.scale), txt_mod2.shift, modulation_dims_txt)), txt_mod2.gate, None, modulation_dims_txt)
|
| 254 |
+
|
| 255 |
+
if txt.dtype == torch.float16:
|
| 256 |
+
txt = torch.nan_to_num(txt, nan=0.0, posinf=65504, neginf=-65504)
|
| 257 |
+
|
| 258 |
+
return img, txt
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
class SingleStreamBlock(nn.Module):
|
| 262 |
+
"""
|
| 263 |
+
A DiT block with parallel linear layers as described in
|
| 264 |
+
https://arxiv.org/abs/2302.05442 and adapted modulation interface.
|
| 265 |
+
"""
|
| 266 |
+
|
| 267 |
+
def __init__(
|
| 268 |
+
self,
|
| 269 |
+
hidden_size: int,
|
| 270 |
+
num_heads: int,
|
| 271 |
+
mlp_ratio: float = 4.0,
|
| 272 |
+
qk_scale: float = None,
|
| 273 |
+
modulation=True,
|
| 274 |
+
mlp_silu_act=False,
|
| 275 |
+
bias=True,
|
| 276 |
+
yak_mlp=False,
|
| 277 |
+
dtype=None,
|
| 278 |
+
device=None,
|
| 279 |
+
operations=None
|
| 280 |
+
):
|
| 281 |
+
super().__init__()
|
| 282 |
+
self.hidden_dim = hidden_size
|
| 283 |
+
self.num_heads = num_heads
|
| 284 |
+
head_dim = hidden_size // num_heads
|
| 285 |
+
self.scale = qk_scale or head_dim**-0.5
|
| 286 |
+
|
| 287 |
+
self.mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
| 288 |
+
|
| 289 |
+
self.mlp_hidden_dim_first = self.mlp_hidden_dim
|
| 290 |
+
self.yak_mlp = yak_mlp
|
| 291 |
+
if mlp_silu_act:
|
| 292 |
+
self.mlp_hidden_dim_first = int(hidden_size * mlp_ratio * 2)
|
| 293 |
+
self.mlp_act = SiLUActivation()
|
| 294 |
+
else:
|
| 295 |
+
self.mlp_act = nn.GELU(approximate="tanh")
|
| 296 |
+
|
| 297 |
+
if self.yak_mlp:
|
| 298 |
+
self.mlp_hidden_dim_first *= 2
|
| 299 |
+
self.mlp_act = nn.SiLU()
|
| 300 |
+
|
| 301 |
+
# qkv and mlp_in
|
| 302 |
+
self.linear1 = operations.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim_first, bias=bias, dtype=dtype, device=device)
|
| 303 |
+
# proj and mlp_out
|
| 304 |
+
self.linear2 = operations.Linear(hidden_size + self.mlp_hidden_dim, hidden_size, bias=bias, dtype=dtype, device=device)
|
| 305 |
+
|
| 306 |
+
self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations)
|
| 307 |
+
|
| 308 |
+
self.hidden_size = hidden_size
|
| 309 |
+
self.pre_norm = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 310 |
+
|
| 311 |
+
if modulation:
|
| 312 |
+
self.modulation = Modulation(hidden_size, double=False, dtype=dtype, device=device, operations=operations)
|
| 313 |
+
else:
|
| 314 |
+
self.modulation = None
|
| 315 |
+
|
| 316 |
+
def forward(self, x: Tensor, vec: Tensor, pe: Tensor, attn_mask=None, modulation_dims=None, transformer_options={}) -> Tensor:
|
| 317 |
+
if self.modulation:
|
| 318 |
+
mod, _ = self.modulation(vec)
|
| 319 |
+
else:
|
| 320 |
+
mod = vec
|
| 321 |
+
|
| 322 |
+
transformer_patches = transformer_options.get("patches", {})
|
| 323 |
+
extra_options = transformer_options.copy()
|
| 324 |
+
|
| 325 |
+
qkv, mlp = torch.split(self.linear1(apply_mod(self.pre_norm(x), (1 + mod.scale), mod.shift, modulation_dims)), [3 * self.hidden_size, self.mlp_hidden_dim_first], dim=-1)
|
| 326 |
+
|
| 327 |
+
q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
| 328 |
+
del qkv
|
| 329 |
+
q, k = self.norm(q, k, v)
|
| 330 |
+
|
| 331 |
+
if "attn1_patch" in transformer_patches:
|
| 332 |
+
patch = transformer_patches["attn1_patch"]
|
| 333 |
+
for p in patch:
|
| 334 |
+
out = p(q, k, v, pe=pe, attn_mask=attn_mask, extra_options=extra_options)
|
| 335 |
+
q, k, v, pe, attn_mask = out.get("q", q), out.get("k", k), out.get("v", v), out.get("pe", pe), out.get("attn_mask", attn_mask)
|
| 336 |
+
|
| 337 |
+
# compute attention
|
| 338 |
+
attn = attention(q, k, v, pe=pe, mask=attn_mask, transformer_options=transformer_options)
|
| 339 |
+
del q, k, v
|
| 340 |
+
|
| 341 |
+
if "attn1_output_patch" in transformer_patches:
|
| 342 |
+
patch = transformer_patches["attn1_output_patch"]
|
| 343 |
+
for p in patch:
|
| 344 |
+
attn = p(attn, extra_options)
|
| 345 |
+
|
| 346 |
+
# compute activation in mlp stream, cat again and run second linear layer
|
| 347 |
+
if self.yak_mlp:
|
| 348 |
+
mlp = self.mlp_act(mlp[..., self.mlp_hidden_dim_first // 2:]) * mlp[..., :self.mlp_hidden_dim_first // 2]
|
| 349 |
+
else:
|
| 350 |
+
mlp = self.mlp_act(mlp)
|
| 351 |
+
output = self.linear2(torch.cat((attn, mlp), 2))
|
| 352 |
+
x += apply_mod(output, mod.gate, None, modulation_dims)
|
| 353 |
+
if x.dtype == torch.float16:
|
| 354 |
+
x = torch.nan_to_num(x, nan=0.0, posinf=65504, neginf=-65504)
|
| 355 |
+
return x
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
class LastLayer(nn.Module):
|
| 359 |
+
def __init__(self, hidden_size: int, patch_size: int, out_channels: int, bias=True, dtype=None, device=None, operations=None):
|
| 360 |
+
super().__init__()
|
| 361 |
+
self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 362 |
+
self.linear = operations.Linear(hidden_size, patch_size * patch_size * out_channels, bias=bias, dtype=dtype, device=device)
|
| 363 |
+
self.adaLN_modulation = nn.Sequential(nn.SiLU(), operations.Linear(hidden_size, 2 * hidden_size, bias=bias, dtype=dtype, device=device))
|
| 364 |
+
|
| 365 |
+
def forward(self, x: Tensor, vec: Tensor, modulation_dims=None) -> Tensor:
|
| 366 |
+
if vec.ndim == 2:
|
| 367 |
+
vec = vec[:, None, :]
|
| 368 |
+
|
| 369 |
+
shift, scale = self.adaLN_modulation(vec).chunk(2, dim=-1)
|
| 370 |
+
x = apply_mod(self.norm_final(x), (1 + scale), shift, modulation_dims)
|
| 371 |
+
x = self.linear(x)
|
| 372 |
+
return x
|
ComfyUI/comfy/ldm/flux/math.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from einops import rearrange
|
| 3 |
+
from torch import Tensor
|
| 4 |
+
|
| 5 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 6 |
+
import comfy.model_management
|
| 7 |
+
import comfy.quant_ops
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def attention(q: Tensor, k: Tensor, v: Tensor, pe: Tensor, mask=None, transformer_options={}) -> Tensor:
|
| 11 |
+
if pe is not None:
|
| 12 |
+
q, k = apply_rope(q, k, pe)
|
| 13 |
+
heads = q.shape[1]
|
| 14 |
+
x = optimized_attention(q, k, v, heads, skip_reshape=True, mask=mask, transformer_options=transformer_options)
|
| 15 |
+
return x
|
| 16 |
+
|
| 17 |
+
def rope(pos: Tensor, dim: int, theta: int) -> Tensor:
|
| 18 |
+
assert dim % 2 == 0
|
| 19 |
+
if not comfy.model_management.supports_fp64(pos.device):
|
| 20 |
+
device = torch.device("cpu")
|
| 21 |
+
else:
|
| 22 |
+
device = pos.device
|
| 23 |
+
|
| 24 |
+
scale = torch.linspace(0, (dim - 2) / dim, steps=dim//2, dtype=torch.float64, device=device)
|
| 25 |
+
omega = 1.0 / (theta**scale)
|
| 26 |
+
out = torch.einsum("...n,d->...nd", pos.to(dtype=torch.float32, device=device), omega)
|
| 27 |
+
out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1)
|
| 28 |
+
out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2)
|
| 29 |
+
return out.to(dtype=torch.float32, device=pos.device)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _apply_rope1(x: Tensor, freqs_cis: Tensor):
|
| 33 |
+
x_ = x.to(dtype=freqs_cis.dtype).reshape(*x.shape[:-1], -1, 1, 2)
|
| 34 |
+
if x_.shape[2] != 1 and freqs_cis.shape[2] != 1 and x_.shape[2] != freqs_cis.shape[2]:
|
| 35 |
+
freqs_cis = freqs_cis[:, :, :x_.shape[2]]
|
| 36 |
+
|
| 37 |
+
x_out = freqs_cis[..., 0] * x_[..., 0]
|
| 38 |
+
x_out.addcmul_(freqs_cis[..., 1], x_[..., 1])
|
| 39 |
+
|
| 40 |
+
return x_out.reshape(*x.shape).type_as(x)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def _apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor):
|
| 44 |
+
return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def apply_rope(xq, xk, freqs_cis):
|
| 48 |
+
if comfy.model_management.in_training:
|
| 49 |
+
return _apply_rope(xq, xk, freqs_cis)
|
| 50 |
+
else:
|
| 51 |
+
return comfy.quant_ops.ck.apply_rope(xq, xk, freqs_cis)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def apply_rope1(x, freqs_cis):
|
| 55 |
+
if comfy.model_management.in_training:
|
| 56 |
+
return _apply_rope1(x, freqs_cis)
|
| 57 |
+
else:
|
| 58 |
+
return comfy.quant_ops.ck.apply_rope1(x, freqs_cis)
|
ComfyUI/comfy/ldm/flux/model.py
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Original code can be found on: https://github.com/black-forest-labs/flux
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from torch import Tensor, nn
|
| 7 |
+
from einops import rearrange, repeat
|
| 8 |
+
import comfy.ldm.common_dit
|
| 9 |
+
import comfy.patcher_extension
|
| 10 |
+
|
| 11 |
+
from .layers import (
|
| 12 |
+
DoubleStreamBlock,
|
| 13 |
+
EmbedND,
|
| 14 |
+
LastLayer,
|
| 15 |
+
MLPEmbedder,
|
| 16 |
+
SingleStreamBlock,
|
| 17 |
+
timestep_embedding,
|
| 18 |
+
Modulation,
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class FluxParams:
|
| 23 |
+
in_channels: int
|
| 24 |
+
out_channels: int
|
| 25 |
+
vec_in_dim: int
|
| 26 |
+
context_in_dim: int
|
| 27 |
+
hidden_size: int
|
| 28 |
+
mlp_ratio: float
|
| 29 |
+
num_heads: int
|
| 30 |
+
depth: int
|
| 31 |
+
depth_single_blocks: int
|
| 32 |
+
axes_dim: list
|
| 33 |
+
theta: int
|
| 34 |
+
patch_size: int
|
| 35 |
+
qkv_bias: bool
|
| 36 |
+
guidance_embed: bool
|
| 37 |
+
txt_ids_dims: list
|
| 38 |
+
global_modulation: bool = False
|
| 39 |
+
mlp_silu_act: bool = False
|
| 40 |
+
ops_bias: bool = True
|
| 41 |
+
default_ref_method: str = "offset"
|
| 42 |
+
ref_index_scale: float = 1.0
|
| 43 |
+
yak_mlp: bool = False
|
| 44 |
+
txt_norm: bool = False
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def invert_slices(slices, length):
|
| 48 |
+
sorted_slices = sorted(slices)
|
| 49 |
+
result = []
|
| 50 |
+
current = 0
|
| 51 |
+
|
| 52 |
+
for start, end in sorted_slices:
|
| 53 |
+
if current < start:
|
| 54 |
+
result.append((current, start))
|
| 55 |
+
current = max(current, end)
|
| 56 |
+
|
| 57 |
+
if current < length:
|
| 58 |
+
result.append((current, length))
|
| 59 |
+
|
| 60 |
+
return result
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class Flux(nn.Module):
|
| 64 |
+
"""
|
| 65 |
+
Transformer model for flow matching on sequences.
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
|
| 69 |
+
super().__init__()
|
| 70 |
+
self.dtype = dtype
|
| 71 |
+
params = FluxParams(**kwargs)
|
| 72 |
+
self.params = params
|
| 73 |
+
self.patch_size = params.patch_size
|
| 74 |
+
self.in_channels = params.in_channels * params.patch_size * params.patch_size
|
| 75 |
+
self.out_channels = params.out_channels * params.patch_size * params.patch_size
|
| 76 |
+
if params.hidden_size % params.num_heads != 0:
|
| 77 |
+
raise ValueError(
|
| 78 |
+
f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
|
| 79 |
+
)
|
| 80 |
+
pe_dim = params.hidden_size // params.num_heads
|
| 81 |
+
if sum(params.axes_dim) != pe_dim:
|
| 82 |
+
raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
|
| 83 |
+
self.hidden_size = params.hidden_size
|
| 84 |
+
self.num_heads = params.num_heads
|
| 85 |
+
self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
|
| 86 |
+
self.img_in = operations.Linear(self.in_channels, self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device)
|
| 87 |
+
self.time_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device, operations=operations)
|
| 88 |
+
if params.vec_in_dim is not None:
|
| 89 |
+
self.vector_in = MLPEmbedder(params.vec_in_dim, self.hidden_size, dtype=dtype, device=device, operations=operations)
|
| 90 |
+
else:
|
| 91 |
+
self.vector_in = None
|
| 92 |
+
|
| 93 |
+
self.guidance_in = (
|
| 94 |
+
MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device, operations=operations) if params.guidance_embed else nn.Identity()
|
| 95 |
+
)
|
| 96 |
+
self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device)
|
| 97 |
+
|
| 98 |
+
if params.txt_norm:
|
| 99 |
+
self.txt_norm = operations.RMSNorm(params.context_in_dim, dtype=dtype, device=device)
|
| 100 |
+
else:
|
| 101 |
+
self.txt_norm = None
|
| 102 |
+
|
| 103 |
+
self.double_blocks = nn.ModuleList(
|
| 104 |
+
[
|
| 105 |
+
DoubleStreamBlock(
|
| 106 |
+
self.hidden_size,
|
| 107 |
+
self.num_heads,
|
| 108 |
+
mlp_ratio=params.mlp_ratio,
|
| 109 |
+
qkv_bias=params.qkv_bias,
|
| 110 |
+
modulation=params.global_modulation is False,
|
| 111 |
+
mlp_silu_act=params.mlp_silu_act,
|
| 112 |
+
proj_bias=params.ops_bias,
|
| 113 |
+
yak_mlp=params.yak_mlp,
|
| 114 |
+
dtype=dtype, device=device, operations=operations
|
| 115 |
+
)
|
| 116 |
+
for _ in range(params.depth)
|
| 117 |
+
]
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
self.single_blocks = nn.ModuleList(
|
| 121 |
+
[
|
| 122 |
+
SingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, modulation=params.global_modulation is False, mlp_silu_act=params.mlp_silu_act, bias=params.ops_bias, yak_mlp=params.yak_mlp, dtype=dtype, device=device, operations=operations)
|
| 123 |
+
for _ in range(params.depth_single_blocks)
|
| 124 |
+
]
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
if final_layer:
|
| 128 |
+
self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, bias=params.ops_bias, dtype=dtype, device=device, operations=operations)
|
| 129 |
+
|
| 130 |
+
if params.global_modulation:
|
| 131 |
+
self.double_stream_modulation_img = Modulation(
|
| 132 |
+
self.hidden_size,
|
| 133 |
+
double=True,
|
| 134 |
+
bias=False,
|
| 135 |
+
dtype=dtype, device=device, operations=operations
|
| 136 |
+
)
|
| 137 |
+
self.double_stream_modulation_txt = Modulation(
|
| 138 |
+
self.hidden_size,
|
| 139 |
+
double=True,
|
| 140 |
+
bias=False,
|
| 141 |
+
dtype=dtype, device=device, operations=operations
|
| 142 |
+
)
|
| 143 |
+
self.single_stream_modulation = Modulation(
|
| 144 |
+
self.hidden_size, double=False, bias=False, dtype=dtype, device=device, operations=operations
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
def forward_orig(
|
| 148 |
+
self,
|
| 149 |
+
img: Tensor,
|
| 150 |
+
img_ids: Tensor,
|
| 151 |
+
txt: Tensor,
|
| 152 |
+
txt_ids: Tensor,
|
| 153 |
+
timesteps: Tensor,
|
| 154 |
+
y: Tensor,
|
| 155 |
+
guidance: Tensor = None,
|
| 156 |
+
control = None,
|
| 157 |
+
timestep_zero_index=None,
|
| 158 |
+
transformer_options={},
|
| 159 |
+
attn_mask: Tensor = None,
|
| 160 |
+
) -> Tensor:
|
| 161 |
+
|
| 162 |
+
transformer_options = transformer_options.copy()
|
| 163 |
+
patches = transformer_options.get("patches", {})
|
| 164 |
+
patches_replace = transformer_options.get("patches_replace", {})
|
| 165 |
+
if img.ndim != 3 or txt.ndim != 3:
|
| 166 |
+
raise ValueError("Input img and txt tensors must have 3 dimensions.")
|
| 167 |
+
|
| 168 |
+
# running on sequences img
|
| 169 |
+
img = self.img_in(img)
|
| 170 |
+
vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype))
|
| 171 |
+
if self.params.guidance_embed:
|
| 172 |
+
if guidance is not None:
|
| 173 |
+
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
|
| 174 |
+
|
| 175 |
+
if self.vector_in is not None:
|
| 176 |
+
if y is None:
|
| 177 |
+
y = torch.zeros((img.shape[0], self.params.vec_in_dim), device=img.device, dtype=img.dtype)
|
| 178 |
+
vec = vec + self.vector_in(y[:, :self.params.vec_in_dim])
|
| 179 |
+
|
| 180 |
+
if self.txt_norm is not None:
|
| 181 |
+
txt = self.txt_norm(txt)
|
| 182 |
+
txt = self.txt_in(txt)
|
| 183 |
+
|
| 184 |
+
if "post_input" in patches:
|
| 185 |
+
for p in patches["post_input"]:
|
| 186 |
+
out = p({"img": img, "txt": txt, "img_ids": img_ids, "txt_ids": txt_ids, "transformer_options": transformer_options})
|
| 187 |
+
img = out["img"]
|
| 188 |
+
txt = out["txt"]
|
| 189 |
+
img_ids = out["img_ids"]
|
| 190 |
+
txt_ids = out["txt_ids"]
|
| 191 |
+
|
| 192 |
+
if img_ids is not None:
|
| 193 |
+
ids = torch.cat((txt_ids, img_ids), dim=1)
|
| 194 |
+
pe = self.pe_embedder(ids)
|
| 195 |
+
else:
|
| 196 |
+
pe = None
|
| 197 |
+
|
| 198 |
+
vec_orig = vec
|
| 199 |
+
txt_vec = vec
|
| 200 |
+
extra_kwargs = {}
|
| 201 |
+
if timestep_zero_index is not None:
|
| 202 |
+
modulation_dims = []
|
| 203 |
+
batch = vec.shape[0] // 2
|
| 204 |
+
vec_orig = vec_orig.reshape(2, batch, vec.shape[1]).movedim(0, 1)
|
| 205 |
+
invert = invert_slices(timestep_zero_index, img.shape[1])
|
| 206 |
+
for s in invert:
|
| 207 |
+
modulation_dims.append((s[0], s[1], 0))
|
| 208 |
+
for s in timestep_zero_index:
|
| 209 |
+
modulation_dims.append((s[0], s[1], 1))
|
| 210 |
+
extra_kwargs["modulation_dims_img"] = modulation_dims
|
| 211 |
+
txt_vec = vec[:batch]
|
| 212 |
+
|
| 213 |
+
if self.params.global_modulation:
|
| 214 |
+
vec = (self.double_stream_modulation_img(vec_orig), self.double_stream_modulation_txt(txt_vec))
|
| 215 |
+
|
| 216 |
+
blocks_replace = patches_replace.get("dit", {})
|
| 217 |
+
transformer_options["total_blocks"] = len(self.double_blocks)
|
| 218 |
+
transformer_options["block_type"] = "double"
|
| 219 |
+
for i, block in enumerate(self.double_blocks):
|
| 220 |
+
transformer_options["block_index"] = i
|
| 221 |
+
if ("double_block", i) in blocks_replace:
|
| 222 |
+
def block_wrap(args):
|
| 223 |
+
out = {}
|
| 224 |
+
out["img"], out["txt"] = block(img=args["img"],
|
| 225 |
+
txt=args["txt"],
|
| 226 |
+
vec=args["vec"],
|
| 227 |
+
pe=args["pe"],
|
| 228 |
+
attn_mask=args.get("attn_mask"),
|
| 229 |
+
transformer_options=args.get("transformer_options"),
|
| 230 |
+
**extra_kwargs)
|
| 231 |
+
return out
|
| 232 |
+
|
| 233 |
+
out = blocks_replace[("double_block", i)]({"img": img,
|
| 234 |
+
"txt": txt,
|
| 235 |
+
"vec": vec,
|
| 236 |
+
"pe": pe,
|
| 237 |
+
"attn_mask": attn_mask,
|
| 238 |
+
"transformer_options": transformer_options},
|
| 239 |
+
{"original_block": block_wrap})
|
| 240 |
+
txt = out["txt"]
|
| 241 |
+
img = out["img"]
|
| 242 |
+
else:
|
| 243 |
+
img, txt = block(img=img,
|
| 244 |
+
txt=txt,
|
| 245 |
+
vec=vec,
|
| 246 |
+
pe=pe,
|
| 247 |
+
attn_mask=attn_mask,
|
| 248 |
+
transformer_options=transformer_options,
|
| 249 |
+
**extra_kwargs)
|
| 250 |
+
|
| 251 |
+
if control is not None: # Controlnet
|
| 252 |
+
control_i = control.get("input")
|
| 253 |
+
if i < len(control_i):
|
| 254 |
+
add = control_i[i]
|
| 255 |
+
if add is not None:
|
| 256 |
+
img[:, :add.shape[1]] += add
|
| 257 |
+
|
| 258 |
+
if img.dtype == torch.float16:
|
| 259 |
+
img = torch.nan_to_num(img, nan=0.0, posinf=65504, neginf=-65504)
|
| 260 |
+
|
| 261 |
+
img = torch.cat((txt, img), 1)
|
| 262 |
+
|
| 263 |
+
if self.params.global_modulation:
|
| 264 |
+
vec, _ = self.single_stream_modulation(vec_orig)
|
| 265 |
+
|
| 266 |
+
extra_kwargs = {}
|
| 267 |
+
if timestep_zero_index is not None:
|
| 268 |
+
lambda a: 0 if a == 0 else a + txt.shape[1]
|
| 269 |
+
modulation_dims_combined = list(map(lambda x: (0 if x[0] == 0 else x[0] + txt.shape[1], x[1] + txt.shape[1], x[2]), modulation_dims))
|
| 270 |
+
extra_kwargs["modulation_dims"] = modulation_dims_combined
|
| 271 |
+
|
| 272 |
+
transformer_options["total_blocks"] = len(self.single_blocks)
|
| 273 |
+
transformer_options["block_type"] = "single"
|
| 274 |
+
transformer_options["img_slice"] = [txt.shape[1], img.shape[1]]
|
| 275 |
+
for i, block in enumerate(self.single_blocks):
|
| 276 |
+
transformer_options["block_index"] = i
|
| 277 |
+
if ("single_block", i) in blocks_replace:
|
| 278 |
+
def block_wrap(args):
|
| 279 |
+
out = {}
|
| 280 |
+
out["img"] = block(args["img"],
|
| 281 |
+
vec=args["vec"],
|
| 282 |
+
pe=args["pe"],
|
| 283 |
+
attn_mask=args.get("attn_mask"),
|
| 284 |
+
transformer_options=args.get("transformer_options"),
|
| 285 |
+
**extra_kwargs)
|
| 286 |
+
return out
|
| 287 |
+
|
| 288 |
+
out = blocks_replace[("single_block", i)]({"img": img,
|
| 289 |
+
"vec": vec,
|
| 290 |
+
"pe": pe,
|
| 291 |
+
"attn_mask": attn_mask,
|
| 292 |
+
"transformer_options": transformer_options},
|
| 293 |
+
{"original_block": block_wrap})
|
| 294 |
+
img = out["img"]
|
| 295 |
+
else:
|
| 296 |
+
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask, transformer_options=transformer_options, **extra_kwargs)
|
| 297 |
+
|
| 298 |
+
if control is not None: # Controlnet
|
| 299 |
+
control_o = control.get("output")
|
| 300 |
+
if i < len(control_o):
|
| 301 |
+
add = control_o[i]
|
| 302 |
+
if add is not None:
|
| 303 |
+
img[:, txt.shape[1] : txt.shape[1] + add.shape[1], ...] += add
|
| 304 |
+
|
| 305 |
+
img = img[:, txt.shape[1] :, ...]
|
| 306 |
+
|
| 307 |
+
extra_kwargs = {}
|
| 308 |
+
if timestep_zero_index is not None:
|
| 309 |
+
extra_kwargs["modulation_dims"] = modulation_dims
|
| 310 |
+
|
| 311 |
+
img = self.final_layer(img, vec_orig, **extra_kwargs) # (N, T, patch_size ** 2 * out_channels)
|
| 312 |
+
return img
|
| 313 |
+
|
| 314 |
+
def process_img(self, x, index=0, h_offset=0, w_offset=0, transformer_options={}):
|
| 315 |
+
bs, c, h, w = x.shape
|
| 316 |
+
patch_size = self.patch_size
|
| 317 |
+
x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size))
|
| 318 |
+
|
| 319 |
+
img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
|
| 320 |
+
h_len = ((h + (patch_size // 2)) // patch_size)
|
| 321 |
+
w_len = ((w + (patch_size // 2)) // patch_size)
|
| 322 |
+
|
| 323 |
+
h_offset = ((h_offset + (patch_size // 2)) // patch_size)
|
| 324 |
+
w_offset = ((w_offset + (patch_size // 2)) // patch_size)
|
| 325 |
+
|
| 326 |
+
steps_h = h_len
|
| 327 |
+
steps_w = w_len
|
| 328 |
+
|
| 329 |
+
rope_options = transformer_options.get("rope_options", None)
|
| 330 |
+
if rope_options is not None:
|
| 331 |
+
h_len = (h_len - 1.0) * rope_options.get("scale_y", 1.0) + 1.0
|
| 332 |
+
w_len = (w_len - 1.0) * rope_options.get("scale_x", 1.0) + 1.0
|
| 333 |
+
|
| 334 |
+
index += rope_options.get("shift_t", 0.0)
|
| 335 |
+
h_offset += rope_options.get("shift_y", 0.0)
|
| 336 |
+
w_offset += rope_options.get("shift_x", 0.0)
|
| 337 |
+
|
| 338 |
+
img_ids = torch.zeros((steps_h, steps_w, len(self.params.axes_dim)), device=x.device, dtype=torch.float32)
|
| 339 |
+
img_ids[:, :, 0] = img_ids[:, :, 1] + index
|
| 340 |
+
img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(h_offset, h_len - 1 + h_offset, steps=steps_h, device=x.device, dtype=torch.float32).unsqueeze(1)
|
| 341 |
+
img_ids[:, :, 2] = img_ids[:, :, 2] + torch.linspace(w_offset, w_len - 1 + w_offset, steps=steps_w, device=x.device, dtype=torch.float32).unsqueeze(0)
|
| 342 |
+
return img, repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
| 343 |
+
|
| 344 |
+
def forward(self, x, timestep, context, y=None, guidance=None, ref_latents=None, control=None, transformer_options={}, **kwargs):
|
| 345 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 346 |
+
self._forward,
|
| 347 |
+
self,
|
| 348 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
|
| 349 |
+
).execute(x, timestep, context, y, guidance, ref_latents, control, transformer_options, **kwargs)
|
| 350 |
+
|
| 351 |
+
def _forward(self, x, timestep, context, y=None, guidance=None, ref_latents=None, control=None, transformer_options={}, **kwargs):
|
| 352 |
+
bs, c, h_orig, w_orig = x.shape
|
| 353 |
+
patch_size = self.patch_size
|
| 354 |
+
|
| 355 |
+
h_len = ((h_orig + (patch_size // 2)) // patch_size)
|
| 356 |
+
w_len = ((w_orig + (patch_size // 2)) // patch_size)
|
| 357 |
+
img, img_ids = self.process_img(x, transformer_options=transformer_options)
|
| 358 |
+
img_tokens = img.shape[1]
|
| 359 |
+
timestep_zero_index = None
|
| 360 |
+
if ref_latents is not None:
|
| 361 |
+
ref_num_tokens = []
|
| 362 |
+
h = 0
|
| 363 |
+
w = 0
|
| 364 |
+
index = 0
|
| 365 |
+
ref_latents_method = kwargs.get("ref_latents_method", self.params.default_ref_method)
|
| 366 |
+
timestep_zero = ref_latents_method == "index_timestep_zero"
|
| 367 |
+
for ref in ref_latents:
|
| 368 |
+
if ref_latents_method in ("index", "index_timestep_zero"):
|
| 369 |
+
index += self.params.ref_index_scale
|
| 370 |
+
h_offset = 0
|
| 371 |
+
w_offset = 0
|
| 372 |
+
elif ref_latents_method == "uxo":
|
| 373 |
+
index = 0
|
| 374 |
+
h_offset = h_len * patch_size + h
|
| 375 |
+
w_offset = w_len * patch_size + w
|
| 376 |
+
h += ref.shape[-2]
|
| 377 |
+
w += ref.shape[-1]
|
| 378 |
+
else:
|
| 379 |
+
index = 1
|
| 380 |
+
h_offset = 0
|
| 381 |
+
w_offset = 0
|
| 382 |
+
if ref.shape[-2] + h > ref.shape[-1] + w:
|
| 383 |
+
w_offset = w
|
| 384 |
+
else:
|
| 385 |
+
h_offset = h
|
| 386 |
+
h = max(h, ref.shape[-2] + h_offset)
|
| 387 |
+
w = max(w, ref.shape[-1] + w_offset)
|
| 388 |
+
|
| 389 |
+
kontext, kontext_ids = self.process_img(ref, index=index, h_offset=h_offset, w_offset=w_offset, transformer_options=transformer_options)
|
| 390 |
+
img = torch.cat([img, kontext], dim=1)
|
| 391 |
+
img_ids = torch.cat([img_ids, kontext_ids], dim=1)
|
| 392 |
+
ref_num_tokens.append(kontext.shape[1])
|
| 393 |
+
if timestep_zero:
|
| 394 |
+
if index > 0:
|
| 395 |
+
timestep = torch.cat([timestep, timestep * 0], dim=0)
|
| 396 |
+
timestep_zero_index = [[img_tokens, img_ids.shape[1]]]
|
| 397 |
+
transformer_options = transformer_options.copy()
|
| 398 |
+
transformer_options["reference_image_num_tokens"] = ref_num_tokens
|
| 399 |
+
|
| 400 |
+
txt_ids = torch.zeros((bs, context.shape[1], len(self.params.axes_dim)), device=x.device, dtype=torch.float32)
|
| 401 |
+
|
| 402 |
+
if len(self.params.txt_ids_dims) > 0:
|
| 403 |
+
for i in self.params.txt_ids_dims:
|
| 404 |
+
txt_ids[:, :, i] = torch.linspace(0, context.shape[1] - 1, steps=context.shape[1], device=x.device, dtype=torch.float32)
|
| 405 |
+
|
| 406 |
+
out = self.forward_orig(img, img_ids, context, txt_ids, timestep, y, guidance, control, timestep_zero_index=timestep_zero_index, transformer_options=transformer_options, attn_mask=kwargs.get("attention_mask", None))
|
| 407 |
+
out = out[:, :img_tokens]
|
| 408 |
+
return rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=self.patch_size, pw=self.patch_size)[:,:,:h_orig,:w_orig]
|
ComfyUI/comfy/ldm/flux/redux.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import comfy.ops
|
| 3 |
+
|
| 4 |
+
ops = comfy.ops.manual_cast
|
| 5 |
+
|
| 6 |
+
class ReduxImageEncoder(torch.nn.Module):
|
| 7 |
+
def __init__(
|
| 8 |
+
self,
|
| 9 |
+
redux_dim: int = 1152,
|
| 10 |
+
txt_in_features: int = 4096,
|
| 11 |
+
device=None,
|
| 12 |
+
dtype=None,
|
| 13 |
+
) -> None:
|
| 14 |
+
super().__init__()
|
| 15 |
+
|
| 16 |
+
self.redux_dim = redux_dim
|
| 17 |
+
self.device = device
|
| 18 |
+
self.dtype = dtype
|
| 19 |
+
|
| 20 |
+
self.redux_up = ops.Linear(redux_dim, txt_in_features * 3, dtype=dtype)
|
| 21 |
+
self.redux_down = ops.Linear(txt_in_features * 3, txt_in_features, dtype=dtype)
|
| 22 |
+
|
| 23 |
+
def forward(self, sigclip_embeds) -> torch.Tensor:
|
| 24 |
+
projected_x = self.redux_down(torch.nn.functional.silu(self.redux_up(sigclip_embeds)))
|
| 25 |
+
return projected_x
|
ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/asymm_models_joint.cpython-312.pyc
ADDED
|
Binary file (20.1 kB). View file
|
|
|
ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/layers.cpython-312.pyc
ADDED
|
Binary file (7.92 kB). View file
|
|
|
ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/rope_mixed.cpython-312.pyc
ADDED
|
Binary file (3.21 kB). View file
|
|
|
ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/temporal_rope.cpython-312.pyc
ADDED
|
Binary file (1.55 kB). View file
|
|
|
ComfyUI/comfy/ldm/genmo/joint_model/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (4.75 kB). View file
|
|
|
ComfyUI/comfy/ldm/genmo/joint_model/asymm_models_joint.py
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#original code from https://github.com/genmoai/models under apache 2.0 license
|
| 2 |
+
#adapted to ComfyUI
|
| 3 |
+
|
| 4 |
+
from typing import Dict, List, Optional, Tuple
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
from einops import rearrange
|
| 10 |
+
# from flash_attn import flash_attn_varlen_qkvpacked_func
|
| 11 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 12 |
+
|
| 13 |
+
from .layers import (
|
| 14 |
+
FeedForward,
|
| 15 |
+
PatchEmbed,
|
| 16 |
+
TimestepEmbedder,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
from .rope_mixed import (
|
| 20 |
+
compute_mixed_rotation,
|
| 21 |
+
create_position_matrix,
|
| 22 |
+
)
|
| 23 |
+
from .temporal_rope import apply_rotary_emb_qk_real
|
| 24 |
+
from .utils import (
|
| 25 |
+
AttentionPool,
|
| 26 |
+
modulate,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
import comfy.ldm.common_dit
|
| 30 |
+
import comfy.ops
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def modulated_rmsnorm(x, scale, eps=1e-6):
|
| 34 |
+
# Normalize and modulate
|
| 35 |
+
x_normed = comfy.ldm.common_dit.rms_norm(x, eps=eps)
|
| 36 |
+
x_modulated = x_normed * (1 + scale.unsqueeze(1))
|
| 37 |
+
|
| 38 |
+
return x_modulated
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def residual_tanh_gated_rmsnorm(x, x_res, gate, eps=1e-6):
|
| 42 |
+
# Apply tanh to gate
|
| 43 |
+
tanh_gate = torch.tanh(gate).unsqueeze(1)
|
| 44 |
+
|
| 45 |
+
# Normalize and apply gated scaling
|
| 46 |
+
x_normed = comfy.ldm.common_dit.rms_norm(x_res, eps=eps) * tanh_gate
|
| 47 |
+
|
| 48 |
+
# Apply residual connection
|
| 49 |
+
output = x + x_normed
|
| 50 |
+
|
| 51 |
+
return output
|
| 52 |
+
|
| 53 |
+
class AsymmetricAttention(nn.Module):
|
| 54 |
+
def __init__(
|
| 55 |
+
self,
|
| 56 |
+
dim_x: int,
|
| 57 |
+
dim_y: int,
|
| 58 |
+
num_heads: int = 8,
|
| 59 |
+
qkv_bias: bool = True,
|
| 60 |
+
qk_norm: bool = False,
|
| 61 |
+
attn_drop: float = 0.0,
|
| 62 |
+
update_y: bool = True,
|
| 63 |
+
out_bias: bool = True,
|
| 64 |
+
attend_to_padding: bool = False,
|
| 65 |
+
softmax_scale: Optional[float] = None,
|
| 66 |
+
device: Optional[torch.device] = None,
|
| 67 |
+
dtype=None,
|
| 68 |
+
operations=None,
|
| 69 |
+
):
|
| 70 |
+
super().__init__()
|
| 71 |
+
self.dim_x = dim_x
|
| 72 |
+
self.dim_y = dim_y
|
| 73 |
+
self.num_heads = num_heads
|
| 74 |
+
self.head_dim = dim_x // num_heads
|
| 75 |
+
self.attn_drop = attn_drop
|
| 76 |
+
self.update_y = update_y
|
| 77 |
+
self.attend_to_padding = attend_to_padding
|
| 78 |
+
self.softmax_scale = softmax_scale
|
| 79 |
+
if dim_x % num_heads != 0:
|
| 80 |
+
raise ValueError(
|
| 81 |
+
f"dim_x={dim_x} should be divisible by num_heads={num_heads}"
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
# Input layers.
|
| 85 |
+
self.qkv_bias = qkv_bias
|
| 86 |
+
self.qkv_x = operations.Linear(dim_x, 3 * dim_x, bias=qkv_bias, device=device, dtype=dtype)
|
| 87 |
+
# Project text features to match visual features (dim_y -> dim_x)
|
| 88 |
+
self.qkv_y = operations.Linear(dim_y, 3 * dim_x, bias=qkv_bias, device=device, dtype=dtype)
|
| 89 |
+
|
| 90 |
+
# Query and key normalization for stability.
|
| 91 |
+
assert qk_norm
|
| 92 |
+
self.q_norm_x = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
|
| 93 |
+
self.k_norm_x = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
|
| 94 |
+
self.q_norm_y = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
|
| 95 |
+
self.k_norm_y = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
|
| 96 |
+
|
| 97 |
+
# Output layers. y features go back down from dim_x -> dim_y.
|
| 98 |
+
self.proj_x = operations.Linear(dim_x, dim_x, bias=out_bias, device=device, dtype=dtype)
|
| 99 |
+
self.proj_y = (
|
| 100 |
+
operations.Linear(dim_x, dim_y, bias=out_bias, device=device, dtype=dtype)
|
| 101 |
+
if update_y
|
| 102 |
+
else nn.Identity()
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
def forward(
|
| 106 |
+
self,
|
| 107 |
+
x: torch.Tensor, # (B, N, dim_x)
|
| 108 |
+
y: torch.Tensor, # (B, L, dim_y)
|
| 109 |
+
scale_x: torch.Tensor, # (B, dim_x), modulation for pre-RMSNorm.
|
| 110 |
+
scale_y: torch.Tensor, # (B, dim_y), modulation for pre-RMSNorm.
|
| 111 |
+
crop_y,
|
| 112 |
+
transformer_options={},
|
| 113 |
+
**rope_rotation,
|
| 114 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 115 |
+
rope_cos = rope_rotation.get("rope_cos")
|
| 116 |
+
rope_sin = rope_rotation.get("rope_sin")
|
| 117 |
+
# Pre-norm for visual features
|
| 118 |
+
x = modulated_rmsnorm(x, scale_x) # (B, M, dim_x) where M = N / cp_group_size
|
| 119 |
+
|
| 120 |
+
# Process visual features
|
| 121 |
+
# qkv_x = self.qkv_x(x) # (B, M, 3 * dim_x)
|
| 122 |
+
# assert qkv_x.dtype == torch.bfloat16
|
| 123 |
+
# qkv_x = all_to_all_collect_tokens(
|
| 124 |
+
# qkv_x, self.num_heads
|
| 125 |
+
# ) # (3, B, N, local_h, head_dim)
|
| 126 |
+
|
| 127 |
+
# Process text features
|
| 128 |
+
y = modulated_rmsnorm(y, scale_y) # (B, L, dim_y)
|
| 129 |
+
q_y, k_y, v_y = self.qkv_y(y).view(y.shape[0], y.shape[1], 3, self.num_heads, -1).unbind(2) # (B, N, local_h, head_dim)
|
| 130 |
+
|
| 131 |
+
q_y = self.q_norm_y(q_y)
|
| 132 |
+
k_y = self.k_norm_y(k_y)
|
| 133 |
+
|
| 134 |
+
# Split qkv_x into q, k, v
|
| 135 |
+
q_x, k_x, v_x = self.qkv_x(x).view(x.shape[0], x.shape[1], 3, self.num_heads, -1).unbind(2) # (B, N, local_h, head_dim)
|
| 136 |
+
q_x = self.q_norm_x(q_x)
|
| 137 |
+
q_x = apply_rotary_emb_qk_real(q_x, rope_cos, rope_sin)
|
| 138 |
+
k_x = self.k_norm_x(k_x)
|
| 139 |
+
k_x = apply_rotary_emb_qk_real(k_x, rope_cos, rope_sin)
|
| 140 |
+
|
| 141 |
+
q = torch.cat([q_x, q_y[:, :crop_y]], dim=1).transpose(1, 2)
|
| 142 |
+
k = torch.cat([k_x, k_y[:, :crop_y]], dim=1).transpose(1, 2)
|
| 143 |
+
v = torch.cat([v_x, v_y[:, :crop_y]], dim=1).transpose(1, 2)
|
| 144 |
+
|
| 145 |
+
xy = optimized_attention(q,
|
| 146 |
+
k,
|
| 147 |
+
v, self.num_heads, skip_reshape=True, transformer_options=transformer_options)
|
| 148 |
+
|
| 149 |
+
x, y = torch.tensor_split(xy, (q_x.shape[1],), dim=1)
|
| 150 |
+
x = self.proj_x(x)
|
| 151 |
+
o = torch.zeros(y.shape[0], q_y.shape[1], y.shape[-1], device=y.device, dtype=y.dtype)
|
| 152 |
+
o[:, :y.shape[1]] = y
|
| 153 |
+
|
| 154 |
+
y = self.proj_y(o)
|
| 155 |
+
# print("ox", x)
|
| 156 |
+
# print("oy", y)
|
| 157 |
+
return x, y
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
class AsymmetricJointBlock(nn.Module):
|
| 161 |
+
def __init__(
|
| 162 |
+
self,
|
| 163 |
+
hidden_size_x: int,
|
| 164 |
+
hidden_size_y: int,
|
| 165 |
+
num_heads: int,
|
| 166 |
+
*,
|
| 167 |
+
mlp_ratio_x: float = 8.0, # Ratio of hidden size to d_model for MLP for visual tokens.
|
| 168 |
+
mlp_ratio_y: float = 4.0, # Ratio of hidden size to d_model for MLP for text tokens.
|
| 169 |
+
update_y: bool = True, # Whether to update text tokens in this block.
|
| 170 |
+
device: Optional[torch.device] = None,
|
| 171 |
+
dtype=None,
|
| 172 |
+
operations=None,
|
| 173 |
+
**block_kwargs,
|
| 174 |
+
):
|
| 175 |
+
super().__init__()
|
| 176 |
+
self.update_y = update_y
|
| 177 |
+
self.hidden_size_x = hidden_size_x
|
| 178 |
+
self.hidden_size_y = hidden_size_y
|
| 179 |
+
self.mod_x = operations.Linear(hidden_size_x, 4 * hidden_size_x, device=device, dtype=dtype)
|
| 180 |
+
if self.update_y:
|
| 181 |
+
self.mod_y = operations.Linear(hidden_size_x, 4 * hidden_size_y, device=device, dtype=dtype)
|
| 182 |
+
else:
|
| 183 |
+
self.mod_y = operations.Linear(hidden_size_x, hidden_size_y, device=device, dtype=dtype)
|
| 184 |
+
|
| 185 |
+
# Self-attention:
|
| 186 |
+
self.attn = AsymmetricAttention(
|
| 187 |
+
hidden_size_x,
|
| 188 |
+
hidden_size_y,
|
| 189 |
+
num_heads=num_heads,
|
| 190 |
+
update_y=update_y,
|
| 191 |
+
device=device,
|
| 192 |
+
dtype=dtype,
|
| 193 |
+
operations=operations,
|
| 194 |
+
**block_kwargs,
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
# MLP.
|
| 198 |
+
mlp_hidden_dim_x = int(hidden_size_x * mlp_ratio_x)
|
| 199 |
+
assert mlp_hidden_dim_x == int(1536 * 8)
|
| 200 |
+
self.mlp_x = FeedForward(
|
| 201 |
+
in_features=hidden_size_x,
|
| 202 |
+
hidden_size=mlp_hidden_dim_x,
|
| 203 |
+
multiple_of=256,
|
| 204 |
+
ffn_dim_multiplier=None,
|
| 205 |
+
device=device,
|
| 206 |
+
dtype=dtype,
|
| 207 |
+
operations=operations,
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
# MLP for text not needed in last block.
|
| 211 |
+
if self.update_y:
|
| 212 |
+
mlp_hidden_dim_y = int(hidden_size_y * mlp_ratio_y)
|
| 213 |
+
self.mlp_y = FeedForward(
|
| 214 |
+
in_features=hidden_size_y,
|
| 215 |
+
hidden_size=mlp_hidden_dim_y,
|
| 216 |
+
multiple_of=256,
|
| 217 |
+
ffn_dim_multiplier=None,
|
| 218 |
+
device=device,
|
| 219 |
+
dtype=dtype,
|
| 220 |
+
operations=operations,
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
def forward(
|
| 224 |
+
self,
|
| 225 |
+
x: torch.Tensor,
|
| 226 |
+
c: torch.Tensor,
|
| 227 |
+
y: torch.Tensor,
|
| 228 |
+
transformer_options={},
|
| 229 |
+
**attn_kwargs,
|
| 230 |
+
):
|
| 231 |
+
"""Forward pass of a block.
|
| 232 |
+
|
| 233 |
+
Args:
|
| 234 |
+
x: (B, N, dim) tensor of visual tokens
|
| 235 |
+
c: (B, dim) tensor of conditioned features
|
| 236 |
+
y: (B, L, dim) tensor of text tokens
|
| 237 |
+
num_frames: Number of frames in the video. N = num_frames * num_spatial_tokens
|
| 238 |
+
|
| 239 |
+
Returns:
|
| 240 |
+
x: (B, N, dim) tensor of visual tokens after block
|
| 241 |
+
y: (B, L, dim) tensor of text tokens after block
|
| 242 |
+
"""
|
| 243 |
+
N = x.size(1)
|
| 244 |
+
|
| 245 |
+
c = F.silu(c)
|
| 246 |
+
mod_x = self.mod_x(c)
|
| 247 |
+
scale_msa_x, gate_msa_x, scale_mlp_x, gate_mlp_x = mod_x.chunk(4, dim=1)
|
| 248 |
+
|
| 249 |
+
mod_y = self.mod_y(c)
|
| 250 |
+
if self.update_y:
|
| 251 |
+
scale_msa_y, gate_msa_y, scale_mlp_y, gate_mlp_y = mod_y.chunk(4, dim=1)
|
| 252 |
+
else:
|
| 253 |
+
scale_msa_y = mod_y
|
| 254 |
+
|
| 255 |
+
# Self-attention block.
|
| 256 |
+
x_attn, y_attn = self.attn(
|
| 257 |
+
x,
|
| 258 |
+
y,
|
| 259 |
+
scale_x=scale_msa_x,
|
| 260 |
+
scale_y=scale_msa_y,
|
| 261 |
+
transformer_options=transformer_options,
|
| 262 |
+
**attn_kwargs,
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
assert x_attn.size(1) == N
|
| 266 |
+
x = residual_tanh_gated_rmsnorm(x, x_attn, gate_msa_x)
|
| 267 |
+
if self.update_y:
|
| 268 |
+
y = residual_tanh_gated_rmsnorm(y, y_attn, gate_msa_y)
|
| 269 |
+
|
| 270 |
+
# MLP block.
|
| 271 |
+
x = self.ff_block_x(x, scale_mlp_x, gate_mlp_x)
|
| 272 |
+
if self.update_y:
|
| 273 |
+
y = self.ff_block_y(y, scale_mlp_y, gate_mlp_y)
|
| 274 |
+
|
| 275 |
+
return x, y
|
| 276 |
+
|
| 277 |
+
def ff_block_x(self, x, scale_x, gate_x):
|
| 278 |
+
x_mod = modulated_rmsnorm(x, scale_x)
|
| 279 |
+
x_res = self.mlp_x(x_mod)
|
| 280 |
+
x = residual_tanh_gated_rmsnorm(x, x_res, gate_x) # Sandwich norm
|
| 281 |
+
return x
|
| 282 |
+
|
| 283 |
+
def ff_block_y(self, y, scale_y, gate_y):
|
| 284 |
+
y_mod = modulated_rmsnorm(y, scale_y)
|
| 285 |
+
y_res = self.mlp_y(y_mod)
|
| 286 |
+
y = residual_tanh_gated_rmsnorm(y, y_res, gate_y) # Sandwich norm
|
| 287 |
+
return y
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class FinalLayer(nn.Module):
|
| 291 |
+
"""
|
| 292 |
+
The final layer of DiT.
|
| 293 |
+
"""
|
| 294 |
+
|
| 295 |
+
def __init__(
|
| 296 |
+
self,
|
| 297 |
+
hidden_size,
|
| 298 |
+
patch_size,
|
| 299 |
+
out_channels,
|
| 300 |
+
device: Optional[torch.device] = None,
|
| 301 |
+
dtype=None,
|
| 302 |
+
operations=None,
|
| 303 |
+
):
|
| 304 |
+
super().__init__()
|
| 305 |
+
self.norm_final = operations.LayerNorm(
|
| 306 |
+
hidden_size, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype
|
| 307 |
+
)
|
| 308 |
+
self.mod = operations.Linear(hidden_size, 2 * hidden_size, device=device, dtype=dtype)
|
| 309 |
+
self.linear = operations.Linear(
|
| 310 |
+
hidden_size, patch_size * patch_size * out_channels, device=device, dtype=dtype
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
def forward(self, x, c):
|
| 314 |
+
c = F.silu(c)
|
| 315 |
+
shift, scale = self.mod(c).chunk(2, dim=1)
|
| 316 |
+
x = modulate(self.norm_final(x), shift, scale)
|
| 317 |
+
x = self.linear(x)
|
| 318 |
+
return x
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
class AsymmDiTJoint(nn.Module):
|
| 322 |
+
"""
|
| 323 |
+
Diffusion model with a Transformer backbone.
|
| 324 |
+
|
| 325 |
+
Ingests text embeddings instead of a label.
|
| 326 |
+
"""
|
| 327 |
+
|
| 328 |
+
def __init__(
|
| 329 |
+
self,
|
| 330 |
+
*,
|
| 331 |
+
patch_size=2,
|
| 332 |
+
in_channels=4,
|
| 333 |
+
hidden_size_x=1152,
|
| 334 |
+
hidden_size_y=1152,
|
| 335 |
+
depth=48,
|
| 336 |
+
num_heads=16,
|
| 337 |
+
mlp_ratio_x=8.0,
|
| 338 |
+
mlp_ratio_y=4.0,
|
| 339 |
+
use_t5: bool = False,
|
| 340 |
+
t5_feat_dim: int = 4096,
|
| 341 |
+
t5_token_length: int = 256,
|
| 342 |
+
learn_sigma=True,
|
| 343 |
+
patch_embed_bias: bool = True,
|
| 344 |
+
timestep_mlp_bias: bool = True,
|
| 345 |
+
attend_to_padding: bool = False,
|
| 346 |
+
timestep_scale: Optional[float] = None,
|
| 347 |
+
use_extended_posenc: bool = False,
|
| 348 |
+
posenc_preserve_area: bool = False,
|
| 349 |
+
rope_theta: float = 10000.0,
|
| 350 |
+
image_model=None,
|
| 351 |
+
device: Optional[torch.device] = None,
|
| 352 |
+
dtype=None,
|
| 353 |
+
operations=None,
|
| 354 |
+
**block_kwargs,
|
| 355 |
+
):
|
| 356 |
+
super().__init__()
|
| 357 |
+
|
| 358 |
+
self.dtype = dtype
|
| 359 |
+
self.learn_sigma = learn_sigma
|
| 360 |
+
self.in_channels = in_channels
|
| 361 |
+
self.out_channels = in_channels * 2 if learn_sigma else in_channels
|
| 362 |
+
self.patch_size = patch_size
|
| 363 |
+
self.num_heads = num_heads
|
| 364 |
+
self.hidden_size_x = hidden_size_x
|
| 365 |
+
self.hidden_size_y = hidden_size_y
|
| 366 |
+
self.head_dim = (
|
| 367 |
+
hidden_size_x // num_heads
|
| 368 |
+
) # Head dimension and count is determined by visual.
|
| 369 |
+
self.attend_to_padding = attend_to_padding
|
| 370 |
+
self.use_extended_posenc = use_extended_posenc
|
| 371 |
+
self.posenc_preserve_area = posenc_preserve_area
|
| 372 |
+
self.use_t5 = use_t5
|
| 373 |
+
self.t5_token_length = t5_token_length
|
| 374 |
+
self.t5_feat_dim = t5_feat_dim
|
| 375 |
+
self.rope_theta = (
|
| 376 |
+
rope_theta # Scaling factor for frequency computation for temporal RoPE.
|
| 377 |
+
)
|
| 378 |
+
|
| 379 |
+
self.x_embedder = PatchEmbed(
|
| 380 |
+
patch_size=patch_size,
|
| 381 |
+
in_chans=in_channels,
|
| 382 |
+
embed_dim=hidden_size_x,
|
| 383 |
+
bias=patch_embed_bias,
|
| 384 |
+
dtype=dtype,
|
| 385 |
+
device=device,
|
| 386 |
+
operations=operations
|
| 387 |
+
)
|
| 388 |
+
# Conditionings
|
| 389 |
+
# Timestep
|
| 390 |
+
self.t_embedder = TimestepEmbedder(
|
| 391 |
+
hidden_size_x, bias=timestep_mlp_bias, timestep_scale=timestep_scale, dtype=dtype, device=device, operations=operations
|
| 392 |
+
)
|
| 393 |
+
|
| 394 |
+
if self.use_t5:
|
| 395 |
+
# Caption Pooling (T5)
|
| 396 |
+
self.t5_y_embedder = AttentionPool(
|
| 397 |
+
t5_feat_dim, num_heads=8, output_dim=hidden_size_x, dtype=dtype, device=device, operations=operations
|
| 398 |
+
)
|
| 399 |
+
|
| 400 |
+
# Dense Embedding Projection (T5)
|
| 401 |
+
self.t5_yproj = operations.Linear(
|
| 402 |
+
t5_feat_dim, hidden_size_y, bias=True, dtype=dtype, device=device
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
# Initialize pos_frequencies as an empty parameter.
|
| 406 |
+
self.pos_frequencies = nn.Parameter(
|
| 407 |
+
torch.empty(3, self.num_heads, self.head_dim // 2, dtype=dtype, device=device)
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
assert not self.attend_to_padding
|
| 411 |
+
|
| 412 |
+
# for depth 48:
|
| 413 |
+
# b = 0: AsymmetricJointBlock, update_y=True
|
| 414 |
+
# b = 1: AsymmetricJointBlock, update_y=True
|
| 415 |
+
# ...
|
| 416 |
+
# b = 46: AsymmetricJointBlock, update_y=True
|
| 417 |
+
# b = 47: AsymmetricJointBlock, update_y=False. No need to update text features.
|
| 418 |
+
blocks = []
|
| 419 |
+
for b in range(depth):
|
| 420 |
+
# Joint multi-modal block
|
| 421 |
+
update_y = b < depth - 1
|
| 422 |
+
block = AsymmetricJointBlock(
|
| 423 |
+
hidden_size_x,
|
| 424 |
+
hidden_size_y,
|
| 425 |
+
num_heads,
|
| 426 |
+
mlp_ratio_x=mlp_ratio_x,
|
| 427 |
+
mlp_ratio_y=mlp_ratio_y,
|
| 428 |
+
update_y=update_y,
|
| 429 |
+
attend_to_padding=attend_to_padding,
|
| 430 |
+
device=device,
|
| 431 |
+
dtype=dtype,
|
| 432 |
+
operations=operations,
|
| 433 |
+
**block_kwargs,
|
| 434 |
+
)
|
| 435 |
+
|
| 436 |
+
blocks.append(block)
|
| 437 |
+
self.blocks = nn.ModuleList(blocks)
|
| 438 |
+
|
| 439 |
+
self.final_layer = FinalLayer(
|
| 440 |
+
hidden_size_x, patch_size, self.out_channels, dtype=dtype, device=device, operations=operations
|
| 441 |
+
)
|
| 442 |
+
|
| 443 |
+
def embed_x(self, x: torch.Tensor) -> torch.Tensor:
|
| 444 |
+
"""
|
| 445 |
+
Args:
|
| 446 |
+
x: (B, C=12, T, H, W) tensor of visual tokens
|
| 447 |
+
|
| 448 |
+
Returns:
|
| 449 |
+
x: (B, C=3072, N) tensor of visual tokens with positional embedding.
|
| 450 |
+
"""
|
| 451 |
+
return self.x_embedder(x) # Convert BcTHW to BCN
|
| 452 |
+
|
| 453 |
+
def prepare(
|
| 454 |
+
self,
|
| 455 |
+
x: torch.Tensor,
|
| 456 |
+
sigma: torch.Tensor,
|
| 457 |
+
t5_feat: torch.Tensor,
|
| 458 |
+
t5_mask: torch.Tensor,
|
| 459 |
+
):
|
| 460 |
+
"""Prepare input and conditioning embeddings."""
|
| 461 |
+
# Visual patch embeddings with positional encoding.
|
| 462 |
+
T, H, W = x.shape[-3:]
|
| 463 |
+
pH, pW = H // self.patch_size, W // self.patch_size
|
| 464 |
+
x = self.embed_x(x) # (B, N, D), where N = T * H * W / patch_size ** 2
|
| 465 |
+
assert x.ndim == 3
|
| 466 |
+
|
| 467 |
+
pH, pW = H // self.patch_size, W // self.patch_size
|
| 468 |
+
N = T * pH * pW
|
| 469 |
+
assert x.size(1) == N
|
| 470 |
+
pos = create_position_matrix(
|
| 471 |
+
T, pH=pH, pW=pW, device=x.device, dtype=torch.float32
|
| 472 |
+
) # (N, 3)
|
| 473 |
+
rope_cos, rope_sin = compute_mixed_rotation(
|
| 474 |
+
freqs=comfy.ops.cast_to(self.pos_frequencies, dtype=x.dtype, device=x.device), pos=pos
|
| 475 |
+
) # Each are (N, num_heads, dim // 2)
|
| 476 |
+
|
| 477 |
+
c_t = self.t_embedder(1 - sigma, out_dtype=x.dtype) # (B, D)
|
| 478 |
+
|
| 479 |
+
t5_y_pool = self.t5_y_embedder(t5_feat, t5_mask) # (B, D)
|
| 480 |
+
|
| 481 |
+
c = c_t + t5_y_pool
|
| 482 |
+
|
| 483 |
+
y_feat = self.t5_yproj(t5_feat) # (B, L, t5_feat_dim) --> (B, L, D)
|
| 484 |
+
|
| 485 |
+
return x, c, y_feat, rope_cos, rope_sin
|
| 486 |
+
|
| 487 |
+
def forward(
|
| 488 |
+
self,
|
| 489 |
+
x: torch.Tensor,
|
| 490 |
+
timestep: torch.Tensor,
|
| 491 |
+
context: List[torch.Tensor],
|
| 492 |
+
attention_mask: List[torch.Tensor],
|
| 493 |
+
num_tokens=256,
|
| 494 |
+
packed_indices: Dict[str, torch.Tensor] = None,
|
| 495 |
+
rope_cos: torch.Tensor = None,
|
| 496 |
+
rope_sin: torch.Tensor = None,
|
| 497 |
+
control=None, transformer_options={}, **kwargs
|
| 498 |
+
):
|
| 499 |
+
patches_replace = transformer_options.get("patches_replace", {})
|
| 500 |
+
y_feat = context
|
| 501 |
+
y_mask = attention_mask
|
| 502 |
+
sigma = timestep
|
| 503 |
+
"""Forward pass of DiT.
|
| 504 |
+
|
| 505 |
+
Args:
|
| 506 |
+
x: (B, C, T, H, W) tensor of spatial inputs (images or latent representations of images)
|
| 507 |
+
sigma: (B,) tensor of noise standard deviations
|
| 508 |
+
y_feat: List((B, L, y_feat_dim) tensor of caption token features. For SDXL text encoders: L=77, y_feat_dim=2048)
|
| 509 |
+
y_mask: List((B, L) boolean tensor indicating which tokens are not padding)
|
| 510 |
+
packed_indices: Dict with keys for Flash Attention. Result of compute_packed_indices.
|
| 511 |
+
"""
|
| 512 |
+
B, _, T, H, W = x.shape
|
| 513 |
+
|
| 514 |
+
x, c, y_feat, rope_cos, rope_sin = self.prepare(
|
| 515 |
+
x, sigma, y_feat, y_mask
|
| 516 |
+
)
|
| 517 |
+
del y_mask
|
| 518 |
+
|
| 519 |
+
blocks_replace = patches_replace.get("dit", {})
|
| 520 |
+
for i, block in enumerate(self.blocks):
|
| 521 |
+
if ("double_block", i) in blocks_replace:
|
| 522 |
+
def block_wrap(args):
|
| 523 |
+
out = {}
|
| 524 |
+
out["img"], out["txt"] = block(
|
| 525 |
+
args["img"],
|
| 526 |
+
args["vec"],
|
| 527 |
+
args["txt"],
|
| 528 |
+
rope_cos=args["rope_cos"],
|
| 529 |
+
rope_sin=args["rope_sin"],
|
| 530 |
+
crop_y=args["num_tokens"],
|
| 531 |
+
transformer_options=args["transformer_options"]
|
| 532 |
+
)
|
| 533 |
+
return out
|
| 534 |
+
out = blocks_replace[("double_block", i)]({"img": x, "txt": y_feat, "vec": c, "rope_cos": rope_cos, "rope_sin": rope_sin, "num_tokens": num_tokens, "transformer_options": transformer_options}, {"original_block": block_wrap})
|
| 535 |
+
y_feat = out["txt"]
|
| 536 |
+
x = out["img"]
|
| 537 |
+
else:
|
| 538 |
+
x, y_feat = block(
|
| 539 |
+
x,
|
| 540 |
+
c,
|
| 541 |
+
y_feat,
|
| 542 |
+
rope_cos=rope_cos,
|
| 543 |
+
rope_sin=rope_sin,
|
| 544 |
+
crop_y=num_tokens,
|
| 545 |
+
transformer_options=transformer_options,
|
| 546 |
+
) # (B, M, D), (B, L, D)
|
| 547 |
+
del y_feat # Final layers don't use dense text features.
|
| 548 |
+
|
| 549 |
+
x = self.final_layer(x, c) # (B, M, patch_size ** 2 * out_channels)
|
| 550 |
+
x = rearrange(
|
| 551 |
+
x,
|
| 552 |
+
"B (T hp wp) (p1 p2 c) -> B c T (hp p1) (wp p2)",
|
| 553 |
+
T=T,
|
| 554 |
+
hp=H // self.patch_size,
|
| 555 |
+
wp=W // self.patch_size,
|
| 556 |
+
p1=self.patch_size,
|
| 557 |
+
p2=self.patch_size,
|
| 558 |
+
c=self.out_channels,
|
| 559 |
+
)
|
| 560 |
+
|
| 561 |
+
return -x
|
ComfyUI/comfy/ldm/genmo/joint_model/layers.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#original code from https://github.com/genmoai/models under apache 2.0 license
|
| 2 |
+
#adapted to ComfyUI
|
| 3 |
+
|
| 4 |
+
import collections.abc
|
| 5 |
+
import math
|
| 6 |
+
from itertools import repeat
|
| 7 |
+
from typing import Callable, Optional
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from einops import rearrange
|
| 13 |
+
import comfy.ldm.common_dit
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# From PyTorch internals
|
| 17 |
+
def _ntuple(n):
|
| 18 |
+
def parse(x):
|
| 19 |
+
if isinstance(x, collections.abc.Iterable) and not isinstance(x, str):
|
| 20 |
+
return tuple(x)
|
| 21 |
+
return tuple(repeat(x, n))
|
| 22 |
+
|
| 23 |
+
return parse
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
to_2tuple = _ntuple(2)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class TimestepEmbedder(nn.Module):
|
| 30 |
+
def __init__(
|
| 31 |
+
self,
|
| 32 |
+
hidden_size: int,
|
| 33 |
+
frequency_embedding_size: int = 256,
|
| 34 |
+
*,
|
| 35 |
+
bias: bool = True,
|
| 36 |
+
timestep_scale: Optional[float] = None,
|
| 37 |
+
dtype=None,
|
| 38 |
+
device=None,
|
| 39 |
+
operations=None,
|
| 40 |
+
):
|
| 41 |
+
super().__init__()
|
| 42 |
+
self.mlp = nn.Sequential(
|
| 43 |
+
operations.Linear(frequency_embedding_size, hidden_size, bias=bias, dtype=dtype, device=device),
|
| 44 |
+
nn.SiLU(),
|
| 45 |
+
operations.Linear(hidden_size, hidden_size, bias=bias, dtype=dtype, device=device),
|
| 46 |
+
)
|
| 47 |
+
self.frequency_embedding_size = frequency_embedding_size
|
| 48 |
+
self.timestep_scale = timestep_scale
|
| 49 |
+
|
| 50 |
+
@staticmethod
|
| 51 |
+
def timestep_embedding(t, dim, max_period=10000):
|
| 52 |
+
half = dim // 2
|
| 53 |
+
freqs = torch.arange(start=0, end=half, dtype=torch.float32, device=t.device)
|
| 54 |
+
freqs.mul_(-math.log(max_period) / half).exp_()
|
| 55 |
+
args = t[:, None].float() * freqs[None]
|
| 56 |
+
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 57 |
+
if dim % 2:
|
| 58 |
+
embedding = torch.cat(
|
| 59 |
+
[embedding, torch.zeros_like(embedding[:, :1])], dim=-1
|
| 60 |
+
)
|
| 61 |
+
return embedding
|
| 62 |
+
|
| 63 |
+
def forward(self, t, out_dtype):
|
| 64 |
+
if self.timestep_scale is not None:
|
| 65 |
+
t = t * self.timestep_scale
|
| 66 |
+
t_freq = self.timestep_embedding(t, self.frequency_embedding_size).to(dtype=out_dtype)
|
| 67 |
+
t_emb = self.mlp(t_freq)
|
| 68 |
+
return t_emb
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class FeedForward(nn.Module):
|
| 72 |
+
def __init__(
|
| 73 |
+
self,
|
| 74 |
+
in_features: int,
|
| 75 |
+
hidden_size: int,
|
| 76 |
+
multiple_of: int,
|
| 77 |
+
ffn_dim_multiplier: Optional[float],
|
| 78 |
+
device: Optional[torch.device] = None,
|
| 79 |
+
dtype=None,
|
| 80 |
+
operations=None,
|
| 81 |
+
):
|
| 82 |
+
super().__init__()
|
| 83 |
+
# keep parameter count and computation constant compared to standard FFN
|
| 84 |
+
hidden_size = int(2 * hidden_size / 3)
|
| 85 |
+
# custom dim factor multiplier
|
| 86 |
+
if ffn_dim_multiplier is not None:
|
| 87 |
+
hidden_size = int(ffn_dim_multiplier * hidden_size)
|
| 88 |
+
hidden_size = multiple_of * ((hidden_size + multiple_of - 1) // multiple_of)
|
| 89 |
+
|
| 90 |
+
self.hidden_dim = hidden_size
|
| 91 |
+
self.w1 = operations.Linear(in_features, 2 * hidden_size, bias=False, device=device, dtype=dtype)
|
| 92 |
+
self.w2 = operations.Linear(hidden_size, in_features, bias=False, device=device, dtype=dtype)
|
| 93 |
+
|
| 94 |
+
def forward(self, x):
|
| 95 |
+
x, gate = self.w1(x).chunk(2, dim=-1)
|
| 96 |
+
x = self.w2(F.silu(x) * gate)
|
| 97 |
+
return x
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
class PatchEmbed(nn.Module):
|
| 101 |
+
def __init__(
|
| 102 |
+
self,
|
| 103 |
+
patch_size: int = 16,
|
| 104 |
+
in_chans: int = 3,
|
| 105 |
+
embed_dim: int = 768,
|
| 106 |
+
norm_layer: Optional[Callable] = None,
|
| 107 |
+
flatten: bool = True,
|
| 108 |
+
bias: bool = True,
|
| 109 |
+
dynamic_img_pad: bool = False,
|
| 110 |
+
dtype=None,
|
| 111 |
+
device=None,
|
| 112 |
+
operations=None,
|
| 113 |
+
):
|
| 114 |
+
super().__init__()
|
| 115 |
+
self.patch_size = to_2tuple(patch_size)
|
| 116 |
+
self.flatten = flatten
|
| 117 |
+
self.dynamic_img_pad = dynamic_img_pad
|
| 118 |
+
|
| 119 |
+
self.proj = operations.Conv2d(
|
| 120 |
+
in_chans,
|
| 121 |
+
embed_dim,
|
| 122 |
+
kernel_size=patch_size,
|
| 123 |
+
stride=patch_size,
|
| 124 |
+
bias=bias,
|
| 125 |
+
device=device,
|
| 126 |
+
dtype=dtype,
|
| 127 |
+
)
|
| 128 |
+
assert norm_layer is None
|
| 129 |
+
self.norm = (
|
| 130 |
+
norm_layer(embed_dim, device=device) if norm_layer else nn.Identity()
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
def forward(self, x):
|
| 134 |
+
B, _C, T, H, W = x.shape
|
| 135 |
+
if not self.dynamic_img_pad:
|
| 136 |
+
assert H % self.patch_size[0] == 0, f"Input height ({H}) should be divisible by patch size ({self.patch_size[0]})."
|
| 137 |
+
assert W % self.patch_size[1] == 0, f"Input width ({W}) should be divisible by patch size ({self.patch_size[1]})."
|
| 138 |
+
else:
|
| 139 |
+
pad_h = (self.patch_size[0] - H % self.patch_size[0]) % self.patch_size[0]
|
| 140 |
+
pad_w = (self.patch_size[1] - W % self.patch_size[1]) % self.patch_size[1]
|
| 141 |
+
x = F.pad(x, (0, pad_w, 0, pad_h))
|
| 142 |
+
|
| 143 |
+
x = rearrange(x, "B C T H W -> (B T) C H W", B=B, T=T)
|
| 144 |
+
x = comfy.ldm.common_dit.pad_to_patch_size(x, self.patch_size, padding_mode='circular')
|
| 145 |
+
x = self.proj(x)
|
| 146 |
+
|
| 147 |
+
# Flatten temporal and spatial dimensions.
|
| 148 |
+
if not self.flatten:
|
| 149 |
+
raise NotImplementedError("Must flatten output.")
|
| 150 |
+
x = rearrange(x, "(B T) C H W -> B (T H W) C", B=B, T=T)
|
| 151 |
+
|
| 152 |
+
x = self.norm(x)
|
| 153 |
+
return x
|
ComfyUI/comfy/ldm/genmo/joint_model/rope_mixed.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#original code from https://github.com/genmoai/models under apache 2.0 license
|
| 2 |
+
|
| 3 |
+
# import functools
|
| 4 |
+
import math
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def centers(start: float, stop, num, dtype=None, device=None):
|
| 10 |
+
"""linspace through bin centers.
|
| 11 |
+
|
| 12 |
+
Args:
|
| 13 |
+
start (float): Start of the range.
|
| 14 |
+
stop (float): End of the range.
|
| 15 |
+
num (int): Number of points.
|
| 16 |
+
dtype (torch.dtype): Data type of the points.
|
| 17 |
+
device (torch.device): Device of the points.
|
| 18 |
+
|
| 19 |
+
Returns:
|
| 20 |
+
centers (Tensor): Centers of the bins. Shape: (num,).
|
| 21 |
+
"""
|
| 22 |
+
edges = torch.linspace(start, stop, num + 1, dtype=dtype, device=device)
|
| 23 |
+
return (edges[:-1] + edges[1:]) / 2
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# @functools.lru_cache(maxsize=1)
|
| 27 |
+
def create_position_matrix(
|
| 28 |
+
T: int,
|
| 29 |
+
pH: int,
|
| 30 |
+
pW: int,
|
| 31 |
+
device: torch.device,
|
| 32 |
+
dtype: torch.dtype,
|
| 33 |
+
*,
|
| 34 |
+
target_area: float = 36864,
|
| 35 |
+
):
|
| 36 |
+
"""
|
| 37 |
+
Args:
|
| 38 |
+
T: int - Temporal dimension
|
| 39 |
+
pH: int - Height dimension after patchify
|
| 40 |
+
pW: int - Width dimension after patchify
|
| 41 |
+
|
| 42 |
+
Returns:
|
| 43 |
+
pos: [T * pH * pW, 3] - position matrix
|
| 44 |
+
"""
|
| 45 |
+
# Create 1D tensors for each dimension
|
| 46 |
+
t = torch.arange(T, dtype=dtype)
|
| 47 |
+
|
| 48 |
+
# Positionally interpolate to area 36864.
|
| 49 |
+
# (3072x3072 frame with 16x16 patches = 192x192 latents).
|
| 50 |
+
# This automatically scales rope positions when the resolution changes.
|
| 51 |
+
# We use a large target area so the model is more sensitive
|
| 52 |
+
# to changes in the learned pos_frequencies matrix.
|
| 53 |
+
scale = math.sqrt(target_area / (pW * pH))
|
| 54 |
+
w = centers(-pW * scale / 2, pW * scale / 2, pW)
|
| 55 |
+
h = centers(-pH * scale / 2, pH * scale / 2, pH)
|
| 56 |
+
|
| 57 |
+
# Use meshgrid to create 3D grids
|
| 58 |
+
grid_t, grid_h, grid_w = torch.meshgrid(t, h, w, indexing="ij")
|
| 59 |
+
|
| 60 |
+
# Stack and reshape the grids.
|
| 61 |
+
pos = torch.stack([grid_t, grid_h, grid_w], dim=-1) # [T, pH, pW, 3]
|
| 62 |
+
pos = pos.view(-1, 3) # [T * pH * pW, 3]
|
| 63 |
+
pos = pos.to(dtype=dtype, device=device)
|
| 64 |
+
|
| 65 |
+
return pos
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def compute_mixed_rotation(
|
| 69 |
+
freqs: torch.Tensor,
|
| 70 |
+
pos: torch.Tensor,
|
| 71 |
+
):
|
| 72 |
+
"""
|
| 73 |
+
Project each 3-dim position into per-head, per-head-dim 1D frequencies.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
freqs: [3, num_heads, num_freqs] - learned rotation frequency (for t, row, col) for each head position
|
| 77 |
+
pos: [N, 3] - position of each token
|
| 78 |
+
num_heads: int
|
| 79 |
+
|
| 80 |
+
Returns:
|
| 81 |
+
freqs_cos: [N, num_heads, num_freqs] - cosine components
|
| 82 |
+
freqs_sin: [N, num_heads, num_freqs] - sine components
|
| 83 |
+
"""
|
| 84 |
+
assert freqs.ndim == 3
|
| 85 |
+
freqs_sum = torch.einsum("Nd,dhf->Nhf", pos.to(freqs), freqs)
|
| 86 |
+
freqs_cos = torch.cos(freqs_sum)
|
| 87 |
+
freqs_sin = torch.sin(freqs_sum)
|
| 88 |
+
return freqs_cos, freqs_sin
|
ComfyUI/comfy/ldm/genmo/joint_model/temporal_rope.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#original code from https://github.com/genmoai/models under apache 2.0 license
|
| 2 |
+
|
| 3 |
+
# Based on Llama3 Implementation.
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def apply_rotary_emb_qk_real(
|
| 8 |
+
xqk: torch.Tensor,
|
| 9 |
+
freqs_cos: torch.Tensor,
|
| 10 |
+
freqs_sin: torch.Tensor,
|
| 11 |
+
) -> torch.Tensor:
|
| 12 |
+
"""
|
| 13 |
+
Apply rotary embeddings to input tensors using the given frequency tensor without complex numbers.
|
| 14 |
+
|
| 15 |
+
Args:
|
| 16 |
+
xqk (torch.Tensor): Query and/or Key tensors to apply rotary embeddings. Shape: (B, S, *, num_heads, D)
|
| 17 |
+
Can be either just query or just key, or both stacked along some batch or * dim.
|
| 18 |
+
freqs_cos (torch.Tensor): Precomputed cosine frequency tensor.
|
| 19 |
+
freqs_sin (torch.Tensor): Precomputed sine frequency tensor.
|
| 20 |
+
|
| 21 |
+
Returns:
|
| 22 |
+
torch.Tensor: The input tensor with rotary embeddings applied.
|
| 23 |
+
"""
|
| 24 |
+
# Split the last dimension into even and odd parts
|
| 25 |
+
xqk_even = xqk[..., 0::2]
|
| 26 |
+
xqk_odd = xqk[..., 1::2]
|
| 27 |
+
|
| 28 |
+
# Apply rotation
|
| 29 |
+
cos_part = (xqk_even * freqs_cos - xqk_odd * freqs_sin).type_as(xqk)
|
| 30 |
+
sin_part = (xqk_even * freqs_sin + xqk_odd * freqs_cos).type_as(xqk)
|
| 31 |
+
|
| 32 |
+
# Interleave the results back into the original shape
|
| 33 |
+
out = torch.stack([cos_part, sin_part], dim=-1).flatten(-2)
|
| 34 |
+
return out
|
ComfyUI/comfy/ldm/genmo/joint_model/utils.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#original code from https://github.com/genmoai/models under apache 2.0 license
|
| 2 |
+
#adapted to ComfyUI
|
| 3 |
+
|
| 4 |
+
from typing import Optional
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def modulate(x, shift, scale):
|
| 12 |
+
return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def pool_tokens(x: torch.Tensor, mask: torch.Tensor, *, keepdim=False) -> torch.Tensor:
|
| 16 |
+
"""
|
| 17 |
+
Pool tokens in x using mask.
|
| 18 |
+
|
| 19 |
+
NOTE: We assume x does not require gradients.
|
| 20 |
+
|
| 21 |
+
Args:
|
| 22 |
+
x: (B, L, D) tensor of tokens.
|
| 23 |
+
mask: (B, L) boolean tensor indicating which tokens are not padding.
|
| 24 |
+
|
| 25 |
+
Returns:
|
| 26 |
+
pooled: (B, D) tensor of pooled tokens.
|
| 27 |
+
"""
|
| 28 |
+
assert x.size(1) == mask.size(1) # Expected mask to have same length as tokens.
|
| 29 |
+
assert x.size(0) == mask.size(0) # Expected mask to have same batch size as tokens.
|
| 30 |
+
mask = mask[:, :, None].to(dtype=x.dtype)
|
| 31 |
+
mask = mask / mask.sum(dim=1, keepdim=True).clamp(min=1)
|
| 32 |
+
pooled = (x * mask).sum(dim=1, keepdim=keepdim)
|
| 33 |
+
return pooled
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class AttentionPool(nn.Module):
|
| 37 |
+
def __init__(
|
| 38 |
+
self,
|
| 39 |
+
embed_dim: int,
|
| 40 |
+
num_heads: int,
|
| 41 |
+
output_dim: int = None,
|
| 42 |
+
device: Optional[torch.device] = None,
|
| 43 |
+
dtype=None,
|
| 44 |
+
operations=None,
|
| 45 |
+
):
|
| 46 |
+
"""
|
| 47 |
+
Args:
|
| 48 |
+
spatial_dim (int): Number of tokens in sequence length.
|
| 49 |
+
embed_dim (int): Dimensionality of input tokens.
|
| 50 |
+
num_heads (int): Number of attention heads.
|
| 51 |
+
output_dim (int): Dimensionality of output tokens. Defaults to embed_dim.
|
| 52 |
+
"""
|
| 53 |
+
super().__init__()
|
| 54 |
+
self.num_heads = num_heads
|
| 55 |
+
self.to_kv = operations.Linear(embed_dim, 2 * embed_dim, device=device, dtype=dtype)
|
| 56 |
+
self.to_q = operations.Linear(embed_dim, embed_dim, device=device, dtype=dtype)
|
| 57 |
+
self.to_out = operations.Linear(embed_dim, output_dim or embed_dim, device=device, dtype=dtype)
|
| 58 |
+
|
| 59 |
+
def forward(self, x, mask):
|
| 60 |
+
"""
|
| 61 |
+
Args:
|
| 62 |
+
x (torch.Tensor): (B, L, D) tensor of input tokens.
|
| 63 |
+
mask (torch.Tensor): (B, L) boolean tensor indicating which tokens are not padding.
|
| 64 |
+
|
| 65 |
+
NOTE: We assume x does not require gradients.
|
| 66 |
+
|
| 67 |
+
Returns:
|
| 68 |
+
x (torch.Tensor): (B, D) tensor of pooled tokens.
|
| 69 |
+
"""
|
| 70 |
+
D = x.size(2)
|
| 71 |
+
|
| 72 |
+
# Construct attention mask, shape: (B, 1, num_queries=1, num_keys=1+L).
|
| 73 |
+
attn_mask = mask[:, None, None, :].bool() # (B, 1, 1, L).
|
| 74 |
+
attn_mask = F.pad(attn_mask, (1, 0), value=True) # (B, 1, 1, 1+L).
|
| 75 |
+
|
| 76 |
+
# Average non-padding token features. These will be used as the query.
|
| 77 |
+
x_pool = pool_tokens(x, mask, keepdim=True) # (B, 1, D)
|
| 78 |
+
|
| 79 |
+
# Concat pooled features to input sequence.
|
| 80 |
+
x = torch.cat([x_pool, x], dim=1) # (B, L+1, D)
|
| 81 |
+
|
| 82 |
+
# Compute queries, keys, values. Only the mean token is used to create a query.
|
| 83 |
+
kv = self.to_kv(x) # (B, L+1, 2 * D)
|
| 84 |
+
q = self.to_q(x[:, 0]) # (B, D)
|
| 85 |
+
|
| 86 |
+
# Extract heads.
|
| 87 |
+
head_dim = D // self.num_heads
|
| 88 |
+
kv = kv.unflatten(2, (2, self.num_heads, head_dim)) # (B, 1+L, 2, H, head_dim)
|
| 89 |
+
kv = kv.transpose(1, 3) # (B, H, 2, 1+L, head_dim)
|
| 90 |
+
k, v = kv.unbind(2) # (B, H, 1+L, head_dim)
|
| 91 |
+
q = q.unflatten(1, (self.num_heads, head_dim)) # (B, H, head_dim)
|
| 92 |
+
q = q.unsqueeze(2) # (B, H, 1, head_dim)
|
| 93 |
+
|
| 94 |
+
# Compute attention.
|
| 95 |
+
x = F.scaled_dot_product_attention(
|
| 96 |
+
q, k, v, attn_mask=attn_mask, dropout_p=0.0
|
| 97 |
+
) # (B, H, 1, head_dim)
|
| 98 |
+
|
| 99 |
+
# Concatenate heads and run output.
|
| 100 |
+
x = x.squeeze(2).flatten(1, 2) # (B, D = H * head_dim)
|
| 101 |
+
x = self.to_out(x)
|
| 102 |
+
return x
|
ComfyUI/comfy/ldm/genmo/vae/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (29.2 kB). View file
|
|
|
ComfyUI/comfy/ldm/genmo/vae/model.py
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#original code from https://github.com/genmoai/models under apache 2.0 license
|
| 2 |
+
#adapted to ComfyUI
|
| 3 |
+
|
| 4 |
+
from typing import List, Optional, Tuple, Union
|
| 5 |
+
from functools import partial
|
| 6 |
+
import math
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
from einops import rearrange
|
| 12 |
+
|
| 13 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 14 |
+
|
| 15 |
+
import comfy.ops
|
| 16 |
+
ops = comfy.ops.disable_weight_init
|
| 17 |
+
|
| 18 |
+
# import mochi_preview.dit.joint_model.context_parallel as cp
|
| 19 |
+
# from mochi_preview.vae.cp_conv import cp_pass_frames, gather_all_frames
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def cast_tuple(t, length=1):
|
| 23 |
+
return t if isinstance(t, tuple) else ((t,) * length)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class GroupNormSpatial(ops.GroupNorm):
|
| 27 |
+
"""
|
| 28 |
+
GroupNorm applied per-frame.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
def forward(self, x: torch.Tensor, *, chunk_size: int = 8):
|
| 32 |
+
B, C, T, H, W = x.shape
|
| 33 |
+
x = rearrange(x, "B C T H W -> (B T) C H W")
|
| 34 |
+
# Run group norm in chunks.
|
| 35 |
+
output = torch.empty_like(x)
|
| 36 |
+
for b in range(0, B * T, chunk_size):
|
| 37 |
+
output[b : b + chunk_size] = super().forward(x[b : b + chunk_size])
|
| 38 |
+
return rearrange(output, "(B T) C H W -> B C T H W", B=B, T=T)
|
| 39 |
+
|
| 40 |
+
class PConv3d(ops.Conv3d):
|
| 41 |
+
def __init__(
|
| 42 |
+
self,
|
| 43 |
+
in_channels,
|
| 44 |
+
out_channels,
|
| 45 |
+
kernel_size: Union[int, Tuple[int, int, int]],
|
| 46 |
+
stride: Union[int, Tuple[int, int, int]],
|
| 47 |
+
causal: bool = True,
|
| 48 |
+
context_parallel: bool = True,
|
| 49 |
+
**kwargs,
|
| 50 |
+
):
|
| 51 |
+
self.causal = causal
|
| 52 |
+
self.context_parallel = context_parallel
|
| 53 |
+
kernel_size = cast_tuple(kernel_size, 3)
|
| 54 |
+
stride = cast_tuple(stride, 3)
|
| 55 |
+
height_pad = (kernel_size[1] - 1) // 2
|
| 56 |
+
width_pad = (kernel_size[2] - 1) // 2
|
| 57 |
+
|
| 58 |
+
super().__init__(
|
| 59 |
+
in_channels=in_channels,
|
| 60 |
+
out_channels=out_channels,
|
| 61 |
+
kernel_size=kernel_size,
|
| 62 |
+
stride=stride,
|
| 63 |
+
dilation=(1, 1, 1),
|
| 64 |
+
padding=(0, height_pad, width_pad),
|
| 65 |
+
**kwargs,
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
def forward(self, x: torch.Tensor):
|
| 69 |
+
# Compute padding amounts.
|
| 70 |
+
context_size = self.kernel_size[0] - 1
|
| 71 |
+
if self.causal:
|
| 72 |
+
pad_front = context_size
|
| 73 |
+
pad_back = 0
|
| 74 |
+
else:
|
| 75 |
+
pad_front = context_size // 2
|
| 76 |
+
pad_back = context_size - pad_front
|
| 77 |
+
|
| 78 |
+
# Apply padding.
|
| 79 |
+
assert self.padding_mode == "replicate" # DEBUG
|
| 80 |
+
mode = "constant" if self.padding_mode == "zeros" else self.padding_mode
|
| 81 |
+
x = F.pad(x, (0, 0, 0, 0, pad_front, pad_back), mode=mode)
|
| 82 |
+
return super().forward(x)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class Conv1x1(ops.Linear):
|
| 86 |
+
"""*1x1 Conv implemented with a linear layer."""
|
| 87 |
+
|
| 88 |
+
def __init__(self, in_features: int, out_features: int, *args, **kwargs):
|
| 89 |
+
super().__init__(in_features, out_features, *args, **kwargs)
|
| 90 |
+
|
| 91 |
+
def forward(self, x: torch.Tensor):
|
| 92 |
+
"""Forward pass.
|
| 93 |
+
|
| 94 |
+
Args:
|
| 95 |
+
x: Input tensor. Shape: [B, C, *] or [B, *, C].
|
| 96 |
+
|
| 97 |
+
Returns:
|
| 98 |
+
x: Output tensor. Shape: [B, C', *] or [B, *, C'].
|
| 99 |
+
"""
|
| 100 |
+
x = x.movedim(1, -1)
|
| 101 |
+
x = super().forward(x)
|
| 102 |
+
x = x.movedim(-1, 1)
|
| 103 |
+
return x
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class DepthToSpaceTime(nn.Module):
|
| 107 |
+
def __init__(
|
| 108 |
+
self,
|
| 109 |
+
temporal_expansion: int,
|
| 110 |
+
spatial_expansion: int,
|
| 111 |
+
):
|
| 112 |
+
super().__init__()
|
| 113 |
+
self.temporal_expansion = temporal_expansion
|
| 114 |
+
self.spatial_expansion = spatial_expansion
|
| 115 |
+
|
| 116 |
+
# When printed, this module should show the temporal and spatial expansion factors.
|
| 117 |
+
def extra_repr(self):
|
| 118 |
+
return f"texp={self.temporal_expansion}, sexp={self.spatial_expansion}"
|
| 119 |
+
|
| 120 |
+
def forward(self, x: torch.Tensor):
|
| 121 |
+
"""Forward pass.
|
| 122 |
+
|
| 123 |
+
Args:
|
| 124 |
+
x: Input tensor. Shape: [B, C, T, H, W].
|
| 125 |
+
|
| 126 |
+
Returns:
|
| 127 |
+
x: Rearranged tensor. Shape: [B, C/(st*s*s), T*st, H*s, W*s].
|
| 128 |
+
"""
|
| 129 |
+
x = rearrange(
|
| 130 |
+
x,
|
| 131 |
+
"B (C st sh sw) T H W -> B C (T st) (H sh) (W sw)",
|
| 132 |
+
st=self.temporal_expansion,
|
| 133 |
+
sh=self.spatial_expansion,
|
| 134 |
+
sw=self.spatial_expansion,
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
# cp_rank, _ = cp.get_cp_rank_size()
|
| 138 |
+
if self.temporal_expansion > 1: # and cp_rank == 0:
|
| 139 |
+
# Drop the first self.temporal_expansion - 1 frames.
|
| 140 |
+
# This is because we always want the 3x3x3 conv filter to only apply
|
| 141 |
+
# to the first frame, and the first frame doesn't need to be repeated.
|
| 142 |
+
assert all(x.shape)
|
| 143 |
+
x = x[:, :, self.temporal_expansion - 1 :]
|
| 144 |
+
assert all(x.shape)
|
| 145 |
+
|
| 146 |
+
return x
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def norm_fn(
|
| 150 |
+
in_channels: int,
|
| 151 |
+
affine: bool = True,
|
| 152 |
+
):
|
| 153 |
+
return GroupNormSpatial(affine=affine, num_groups=32, num_channels=in_channels)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class ResBlock(nn.Module):
|
| 157 |
+
"""Residual block that preserves the spatial dimensions."""
|
| 158 |
+
|
| 159 |
+
def __init__(
|
| 160 |
+
self,
|
| 161 |
+
channels: int,
|
| 162 |
+
*,
|
| 163 |
+
affine: bool = True,
|
| 164 |
+
attn_block: Optional[nn.Module] = None,
|
| 165 |
+
causal: bool = True,
|
| 166 |
+
prune_bottleneck: bool = False,
|
| 167 |
+
padding_mode: str,
|
| 168 |
+
bias: bool = True,
|
| 169 |
+
):
|
| 170 |
+
super().__init__()
|
| 171 |
+
self.channels = channels
|
| 172 |
+
|
| 173 |
+
assert causal
|
| 174 |
+
self.stack = nn.Sequential(
|
| 175 |
+
norm_fn(channels, affine=affine),
|
| 176 |
+
nn.SiLU(inplace=True),
|
| 177 |
+
PConv3d(
|
| 178 |
+
in_channels=channels,
|
| 179 |
+
out_channels=channels // 2 if prune_bottleneck else channels,
|
| 180 |
+
kernel_size=(3, 3, 3),
|
| 181 |
+
stride=(1, 1, 1),
|
| 182 |
+
padding_mode=padding_mode,
|
| 183 |
+
bias=bias,
|
| 184 |
+
causal=causal,
|
| 185 |
+
),
|
| 186 |
+
norm_fn(channels, affine=affine),
|
| 187 |
+
nn.SiLU(inplace=True),
|
| 188 |
+
PConv3d(
|
| 189 |
+
in_channels=channels // 2 if prune_bottleneck else channels,
|
| 190 |
+
out_channels=channels,
|
| 191 |
+
kernel_size=(3, 3, 3),
|
| 192 |
+
stride=(1, 1, 1),
|
| 193 |
+
padding_mode=padding_mode,
|
| 194 |
+
bias=bias,
|
| 195 |
+
causal=causal,
|
| 196 |
+
),
|
| 197 |
+
)
|
| 198 |
+
|
| 199 |
+
self.attn_block = attn_block if attn_block else nn.Identity()
|
| 200 |
+
|
| 201 |
+
def forward(self, x: torch.Tensor):
|
| 202 |
+
"""Forward pass.
|
| 203 |
+
|
| 204 |
+
Args:
|
| 205 |
+
x: Input tensor. Shape: [B, C, T, H, W].
|
| 206 |
+
"""
|
| 207 |
+
residual = x
|
| 208 |
+
x = self.stack(x)
|
| 209 |
+
x = x + residual
|
| 210 |
+
del residual
|
| 211 |
+
|
| 212 |
+
return self.attn_block(x)
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
class Attention(nn.Module):
|
| 216 |
+
def __init__(
|
| 217 |
+
self,
|
| 218 |
+
dim: int,
|
| 219 |
+
head_dim: int = 32,
|
| 220 |
+
qkv_bias: bool = False,
|
| 221 |
+
out_bias: bool = True,
|
| 222 |
+
qk_norm: bool = True,
|
| 223 |
+
) -> None:
|
| 224 |
+
super().__init__()
|
| 225 |
+
self.head_dim = head_dim
|
| 226 |
+
self.num_heads = dim // head_dim
|
| 227 |
+
self.qk_norm = qk_norm
|
| 228 |
+
|
| 229 |
+
self.qkv = nn.Linear(dim, 3 * dim, bias=qkv_bias)
|
| 230 |
+
self.out = nn.Linear(dim, dim, bias=out_bias)
|
| 231 |
+
|
| 232 |
+
def forward(
|
| 233 |
+
self,
|
| 234 |
+
x: torch.Tensor,
|
| 235 |
+
) -> torch.Tensor:
|
| 236 |
+
"""Compute temporal self-attention.
|
| 237 |
+
|
| 238 |
+
Args:
|
| 239 |
+
x: Input tensor. Shape: [B, C, T, H, W].
|
| 240 |
+
chunk_size: Chunk size for large tensors.
|
| 241 |
+
|
| 242 |
+
Returns:
|
| 243 |
+
x: Output tensor. Shape: [B, C, T, H, W].
|
| 244 |
+
"""
|
| 245 |
+
B, _, T, H, W = x.shape
|
| 246 |
+
|
| 247 |
+
if T == 1:
|
| 248 |
+
# No attention for single frame.
|
| 249 |
+
x = x.movedim(1, -1) # [B, C, T, H, W] -> [B, T, H, W, C]
|
| 250 |
+
qkv = self.qkv(x)
|
| 251 |
+
_, _, x = qkv.chunk(3, dim=-1) # Throw away queries and keys.
|
| 252 |
+
x = self.out(x)
|
| 253 |
+
return x.movedim(-1, 1) # [B, T, H, W, C] -> [B, C, T, H, W]
|
| 254 |
+
|
| 255 |
+
# 1D temporal attention.
|
| 256 |
+
x = rearrange(x, "B C t h w -> (B h w) t C")
|
| 257 |
+
qkv = self.qkv(x)
|
| 258 |
+
|
| 259 |
+
# Input: qkv with shape [B, t, 3 * num_heads * head_dim]
|
| 260 |
+
# Output: x with shape [B, num_heads, t, head_dim]
|
| 261 |
+
q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, self.head_dim).transpose(1, 3).unbind(2)
|
| 262 |
+
|
| 263 |
+
if self.qk_norm:
|
| 264 |
+
q = F.normalize(q, p=2, dim=-1)
|
| 265 |
+
k = F.normalize(k, p=2, dim=-1)
|
| 266 |
+
|
| 267 |
+
x = optimized_attention(q, k, v, self.num_heads, skip_reshape=True)
|
| 268 |
+
|
| 269 |
+
assert x.size(0) == q.size(0)
|
| 270 |
+
|
| 271 |
+
x = self.out(x)
|
| 272 |
+
x = rearrange(x, "(B h w) t C -> B C t h w", B=B, h=H, w=W)
|
| 273 |
+
return x
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
class AttentionBlock(nn.Module):
|
| 277 |
+
def __init__(
|
| 278 |
+
self,
|
| 279 |
+
dim: int,
|
| 280 |
+
**attn_kwargs,
|
| 281 |
+
) -> None:
|
| 282 |
+
super().__init__()
|
| 283 |
+
self.norm = norm_fn(dim)
|
| 284 |
+
self.attn = Attention(dim, **attn_kwargs)
|
| 285 |
+
|
| 286 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 287 |
+
return x + self.attn(self.norm(x))
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class CausalUpsampleBlock(nn.Module):
|
| 291 |
+
def __init__(
|
| 292 |
+
self,
|
| 293 |
+
in_channels: int,
|
| 294 |
+
out_channels: int,
|
| 295 |
+
num_res_blocks: int,
|
| 296 |
+
*,
|
| 297 |
+
temporal_expansion: int = 2,
|
| 298 |
+
spatial_expansion: int = 2,
|
| 299 |
+
**block_kwargs,
|
| 300 |
+
):
|
| 301 |
+
super().__init__()
|
| 302 |
+
|
| 303 |
+
blocks = []
|
| 304 |
+
for _ in range(num_res_blocks):
|
| 305 |
+
blocks.append(block_fn(in_channels, **block_kwargs))
|
| 306 |
+
self.blocks = nn.Sequential(*blocks)
|
| 307 |
+
|
| 308 |
+
self.temporal_expansion = temporal_expansion
|
| 309 |
+
self.spatial_expansion = spatial_expansion
|
| 310 |
+
|
| 311 |
+
# Change channels in the final convolution layer.
|
| 312 |
+
self.proj = Conv1x1(
|
| 313 |
+
in_channels,
|
| 314 |
+
out_channels * temporal_expansion * (spatial_expansion**2),
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
self.d2st = DepthToSpaceTime(
|
| 318 |
+
temporal_expansion=temporal_expansion, spatial_expansion=spatial_expansion
|
| 319 |
+
)
|
| 320 |
+
|
| 321 |
+
def forward(self, x):
|
| 322 |
+
x = self.blocks(x)
|
| 323 |
+
x = self.proj(x)
|
| 324 |
+
x = self.d2st(x)
|
| 325 |
+
return x
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def block_fn(channels, *, affine: bool = True, has_attention: bool = False, **block_kwargs):
|
| 329 |
+
attn_block = AttentionBlock(channels) if has_attention else None
|
| 330 |
+
return ResBlock(channels, affine=affine, attn_block=attn_block, **block_kwargs)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
class DownsampleBlock(nn.Module):
|
| 334 |
+
def __init__(
|
| 335 |
+
self,
|
| 336 |
+
in_channels: int,
|
| 337 |
+
out_channels: int,
|
| 338 |
+
num_res_blocks,
|
| 339 |
+
*,
|
| 340 |
+
temporal_reduction=2,
|
| 341 |
+
spatial_reduction=2,
|
| 342 |
+
**block_kwargs,
|
| 343 |
+
):
|
| 344 |
+
"""
|
| 345 |
+
Downsample block for the VAE encoder.
|
| 346 |
+
|
| 347 |
+
Args:
|
| 348 |
+
in_channels: Number of input channels.
|
| 349 |
+
out_channels: Number of output channels.
|
| 350 |
+
num_res_blocks: Number of residual blocks.
|
| 351 |
+
temporal_reduction: Temporal reduction factor.
|
| 352 |
+
spatial_reduction: Spatial reduction factor.
|
| 353 |
+
"""
|
| 354 |
+
super().__init__()
|
| 355 |
+
layers = []
|
| 356 |
+
|
| 357 |
+
# Change the channel count in the strided convolution.
|
| 358 |
+
# This lets the ResBlock have uniform channel count,
|
| 359 |
+
# as in ConvNeXt.
|
| 360 |
+
assert in_channels != out_channels
|
| 361 |
+
layers.append(
|
| 362 |
+
PConv3d(
|
| 363 |
+
in_channels=in_channels,
|
| 364 |
+
out_channels=out_channels,
|
| 365 |
+
kernel_size=(temporal_reduction, spatial_reduction, spatial_reduction),
|
| 366 |
+
stride=(temporal_reduction, spatial_reduction, spatial_reduction),
|
| 367 |
+
# First layer in each block always uses replicate padding
|
| 368 |
+
padding_mode="replicate",
|
| 369 |
+
bias=block_kwargs["bias"],
|
| 370 |
+
)
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
for _ in range(num_res_blocks):
|
| 374 |
+
layers.append(block_fn(out_channels, **block_kwargs))
|
| 375 |
+
|
| 376 |
+
self.layers = nn.Sequential(*layers)
|
| 377 |
+
|
| 378 |
+
def forward(self, x):
|
| 379 |
+
return self.layers(x)
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
def add_fourier_features(inputs: torch.Tensor, start=6, stop=8, step=1):
|
| 383 |
+
num_freqs = (stop - start) // step
|
| 384 |
+
assert inputs.ndim == 5
|
| 385 |
+
C = inputs.size(1)
|
| 386 |
+
|
| 387 |
+
# Create Base 2 Fourier features.
|
| 388 |
+
freqs = torch.arange(start, stop, step, dtype=inputs.dtype, device=inputs.device)
|
| 389 |
+
assert num_freqs == len(freqs)
|
| 390 |
+
w = torch.pow(2.0, freqs) * (2 * torch.pi) # [num_freqs]
|
| 391 |
+
C = inputs.shape[1]
|
| 392 |
+
w = w.repeat(C)[None, :, None, None, None] # [1, C * num_freqs, 1, 1, 1]
|
| 393 |
+
|
| 394 |
+
# Interleaved repeat of input channels to match w.
|
| 395 |
+
h = inputs.repeat_interleave(num_freqs, dim=1) # [B, C * num_freqs, T, H, W]
|
| 396 |
+
# Scale channels by frequency.
|
| 397 |
+
h = w * h
|
| 398 |
+
|
| 399 |
+
return torch.cat(
|
| 400 |
+
[
|
| 401 |
+
inputs,
|
| 402 |
+
torch.sin(h),
|
| 403 |
+
torch.cos(h),
|
| 404 |
+
],
|
| 405 |
+
dim=1,
|
| 406 |
+
)
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
class FourierFeatures(nn.Module):
|
| 410 |
+
def __init__(self, start: int = 6, stop: int = 8, step: int = 1):
|
| 411 |
+
super().__init__()
|
| 412 |
+
self.start = start
|
| 413 |
+
self.stop = stop
|
| 414 |
+
self.step = step
|
| 415 |
+
|
| 416 |
+
def forward(self, inputs):
|
| 417 |
+
"""Add Fourier features to inputs.
|
| 418 |
+
|
| 419 |
+
Args:
|
| 420 |
+
inputs: Input tensor. Shape: [B, C, T, H, W]
|
| 421 |
+
|
| 422 |
+
Returns:
|
| 423 |
+
h: Output tensor. Shape: [B, (1 + 2 * num_freqs) * C, T, H, W]
|
| 424 |
+
"""
|
| 425 |
+
return add_fourier_features(inputs, self.start, self.stop, self.step)
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
class Decoder(nn.Module):
|
| 429 |
+
def __init__(
|
| 430 |
+
self,
|
| 431 |
+
*,
|
| 432 |
+
out_channels: int = 3,
|
| 433 |
+
latent_dim: int,
|
| 434 |
+
base_channels: int,
|
| 435 |
+
channel_multipliers: List[int],
|
| 436 |
+
num_res_blocks: List[int],
|
| 437 |
+
temporal_expansions: Optional[List[int]] = None,
|
| 438 |
+
spatial_expansions: Optional[List[int]] = None,
|
| 439 |
+
has_attention: List[bool],
|
| 440 |
+
output_norm: bool = True,
|
| 441 |
+
nonlinearity: str = "silu",
|
| 442 |
+
output_nonlinearity: str = "silu",
|
| 443 |
+
causal: bool = True,
|
| 444 |
+
**block_kwargs,
|
| 445 |
+
):
|
| 446 |
+
super().__init__()
|
| 447 |
+
self.input_channels = latent_dim
|
| 448 |
+
self.base_channels = base_channels
|
| 449 |
+
self.channel_multipliers = channel_multipliers
|
| 450 |
+
self.num_res_blocks = num_res_blocks
|
| 451 |
+
self.output_nonlinearity = output_nonlinearity
|
| 452 |
+
assert nonlinearity == "silu"
|
| 453 |
+
assert causal
|
| 454 |
+
|
| 455 |
+
ch = [mult * base_channels for mult in channel_multipliers]
|
| 456 |
+
self.num_up_blocks = len(ch) - 1
|
| 457 |
+
assert len(num_res_blocks) == self.num_up_blocks + 2
|
| 458 |
+
|
| 459 |
+
blocks = []
|
| 460 |
+
|
| 461 |
+
first_block = [
|
| 462 |
+
ops.Conv3d(latent_dim, ch[-1], kernel_size=(1, 1, 1))
|
| 463 |
+
] # Input layer.
|
| 464 |
+
# First set of blocks preserve channel count.
|
| 465 |
+
for _ in range(num_res_blocks[-1]):
|
| 466 |
+
first_block.append(
|
| 467 |
+
block_fn(
|
| 468 |
+
ch[-1],
|
| 469 |
+
has_attention=has_attention[-1],
|
| 470 |
+
causal=causal,
|
| 471 |
+
**block_kwargs,
|
| 472 |
+
)
|
| 473 |
+
)
|
| 474 |
+
blocks.append(nn.Sequential(*first_block))
|
| 475 |
+
|
| 476 |
+
assert len(temporal_expansions) == len(spatial_expansions) == self.num_up_blocks
|
| 477 |
+
assert len(num_res_blocks) == len(has_attention) == self.num_up_blocks + 2
|
| 478 |
+
|
| 479 |
+
upsample_block_fn = CausalUpsampleBlock
|
| 480 |
+
|
| 481 |
+
for i in range(self.num_up_blocks):
|
| 482 |
+
block = upsample_block_fn(
|
| 483 |
+
ch[-i - 1],
|
| 484 |
+
ch[-i - 2],
|
| 485 |
+
num_res_blocks=num_res_blocks[-i - 2],
|
| 486 |
+
has_attention=has_attention[-i - 2],
|
| 487 |
+
temporal_expansion=temporal_expansions[-i - 1],
|
| 488 |
+
spatial_expansion=spatial_expansions[-i - 1],
|
| 489 |
+
causal=causal,
|
| 490 |
+
**block_kwargs,
|
| 491 |
+
)
|
| 492 |
+
blocks.append(block)
|
| 493 |
+
|
| 494 |
+
assert not output_norm
|
| 495 |
+
|
| 496 |
+
# Last block. Preserve channel count.
|
| 497 |
+
last_block = []
|
| 498 |
+
for _ in range(num_res_blocks[0]):
|
| 499 |
+
last_block.append(
|
| 500 |
+
block_fn(
|
| 501 |
+
ch[0], has_attention=has_attention[0], causal=causal, **block_kwargs
|
| 502 |
+
)
|
| 503 |
+
)
|
| 504 |
+
blocks.append(nn.Sequential(*last_block))
|
| 505 |
+
|
| 506 |
+
self.blocks = nn.ModuleList(blocks)
|
| 507 |
+
self.output_proj = Conv1x1(ch[0], out_channels)
|
| 508 |
+
|
| 509 |
+
def forward(self, x):
|
| 510 |
+
"""Forward pass.
|
| 511 |
+
|
| 512 |
+
Args:
|
| 513 |
+
x: Latent tensor. Shape: [B, input_channels, t, h, w]. Scaled [-1, 1].
|
| 514 |
+
|
| 515 |
+
Returns:
|
| 516 |
+
x: Reconstructed video tensor. Shape: [B, C, T, H, W]. Scaled to [-1, 1].
|
| 517 |
+
T + 1 = (t - 1) * 4.
|
| 518 |
+
H = h * 16, W = w * 16.
|
| 519 |
+
"""
|
| 520 |
+
for block in self.blocks:
|
| 521 |
+
x = block(x)
|
| 522 |
+
|
| 523 |
+
if self.output_nonlinearity == "silu":
|
| 524 |
+
x = F.silu(x, inplace=not self.training)
|
| 525 |
+
else:
|
| 526 |
+
assert (
|
| 527 |
+
not self.output_nonlinearity
|
| 528 |
+
) # StyleGAN3 omits the to-RGB nonlinearity.
|
| 529 |
+
|
| 530 |
+
return self.output_proj(x).contiguous()
|
| 531 |
+
|
| 532 |
+
class LatentDistribution:
|
| 533 |
+
def __init__(self, mean: torch.Tensor, logvar: torch.Tensor):
|
| 534 |
+
"""Initialize latent distribution.
|
| 535 |
+
|
| 536 |
+
Args:
|
| 537 |
+
mean: Mean of the distribution. Shape: [B, C, T, H, W].
|
| 538 |
+
logvar: Logarithm of variance of the distribution. Shape: [B, C, T, H, W].
|
| 539 |
+
"""
|
| 540 |
+
assert mean.shape == logvar.shape
|
| 541 |
+
self.mean = mean
|
| 542 |
+
self.logvar = logvar
|
| 543 |
+
|
| 544 |
+
def sample(self, temperature=1.0, generator: torch.Generator = None, noise=None):
|
| 545 |
+
if temperature == 0.0:
|
| 546 |
+
return self.mean
|
| 547 |
+
|
| 548 |
+
if noise is None:
|
| 549 |
+
noise = torch.randn(self.mean.shape, device=self.mean.device, dtype=self.mean.dtype, generator=generator)
|
| 550 |
+
else:
|
| 551 |
+
assert noise.device == self.mean.device
|
| 552 |
+
noise = noise.to(self.mean.dtype)
|
| 553 |
+
|
| 554 |
+
if temperature != 1.0:
|
| 555 |
+
raise NotImplementedError(f"Temperature {temperature} is not supported.")
|
| 556 |
+
|
| 557 |
+
# Just Gaussian sample with no scaling of variance.
|
| 558 |
+
return noise * torch.exp(self.logvar * 0.5) + self.mean
|
| 559 |
+
|
| 560 |
+
def mode(self):
|
| 561 |
+
return self.mean
|
| 562 |
+
|
| 563 |
+
class Encoder(nn.Module):
|
| 564 |
+
def __init__(
|
| 565 |
+
self,
|
| 566 |
+
*,
|
| 567 |
+
in_channels: int,
|
| 568 |
+
base_channels: int,
|
| 569 |
+
channel_multipliers: List[int],
|
| 570 |
+
num_res_blocks: List[int],
|
| 571 |
+
latent_dim: int,
|
| 572 |
+
temporal_reductions: List[int],
|
| 573 |
+
spatial_reductions: List[int],
|
| 574 |
+
prune_bottlenecks: List[bool],
|
| 575 |
+
has_attentions: List[bool],
|
| 576 |
+
affine: bool = True,
|
| 577 |
+
bias: bool = True,
|
| 578 |
+
input_is_conv_1x1: bool = False,
|
| 579 |
+
padding_mode: str,
|
| 580 |
+
):
|
| 581 |
+
super().__init__()
|
| 582 |
+
self.temporal_reductions = temporal_reductions
|
| 583 |
+
self.spatial_reductions = spatial_reductions
|
| 584 |
+
self.base_channels = base_channels
|
| 585 |
+
self.channel_multipliers = channel_multipliers
|
| 586 |
+
self.num_res_blocks = num_res_blocks
|
| 587 |
+
self.latent_dim = latent_dim
|
| 588 |
+
|
| 589 |
+
self.fourier_features = FourierFeatures()
|
| 590 |
+
ch = [mult * base_channels for mult in channel_multipliers]
|
| 591 |
+
num_down_blocks = len(ch) - 1
|
| 592 |
+
assert len(num_res_blocks) == num_down_blocks + 2
|
| 593 |
+
|
| 594 |
+
layers = (
|
| 595 |
+
[ops.Conv3d(in_channels, ch[0], kernel_size=(1, 1, 1), bias=True)]
|
| 596 |
+
if not input_is_conv_1x1
|
| 597 |
+
else [Conv1x1(in_channels, ch[0])]
|
| 598 |
+
)
|
| 599 |
+
|
| 600 |
+
assert len(prune_bottlenecks) == num_down_blocks + 2
|
| 601 |
+
assert len(has_attentions) == num_down_blocks + 2
|
| 602 |
+
block = partial(block_fn, padding_mode=padding_mode, affine=affine, bias=bias)
|
| 603 |
+
|
| 604 |
+
for _ in range(num_res_blocks[0]):
|
| 605 |
+
layers.append(block(ch[0], has_attention=has_attentions[0], prune_bottleneck=prune_bottlenecks[0]))
|
| 606 |
+
prune_bottlenecks = prune_bottlenecks[1:]
|
| 607 |
+
has_attentions = has_attentions[1:]
|
| 608 |
+
|
| 609 |
+
assert len(temporal_reductions) == len(spatial_reductions) == len(ch) - 1
|
| 610 |
+
for i in range(num_down_blocks):
|
| 611 |
+
layer = DownsampleBlock(
|
| 612 |
+
ch[i],
|
| 613 |
+
ch[i + 1],
|
| 614 |
+
num_res_blocks=num_res_blocks[i + 1],
|
| 615 |
+
temporal_reduction=temporal_reductions[i],
|
| 616 |
+
spatial_reduction=spatial_reductions[i],
|
| 617 |
+
prune_bottleneck=prune_bottlenecks[i],
|
| 618 |
+
has_attention=has_attentions[i],
|
| 619 |
+
affine=affine,
|
| 620 |
+
bias=bias,
|
| 621 |
+
padding_mode=padding_mode,
|
| 622 |
+
)
|
| 623 |
+
|
| 624 |
+
layers.append(layer)
|
| 625 |
+
|
| 626 |
+
# Additional blocks.
|
| 627 |
+
for _ in range(num_res_blocks[-1]):
|
| 628 |
+
layers.append(block(ch[-1], has_attention=has_attentions[-1], prune_bottleneck=prune_bottlenecks[-1]))
|
| 629 |
+
|
| 630 |
+
self.layers = nn.Sequential(*layers)
|
| 631 |
+
|
| 632 |
+
# Output layers.
|
| 633 |
+
self.output_norm = norm_fn(ch[-1])
|
| 634 |
+
self.output_proj = Conv1x1(ch[-1], 2 * latent_dim, bias=False)
|
| 635 |
+
|
| 636 |
+
@property
|
| 637 |
+
def temporal_downsample(self):
|
| 638 |
+
return math.prod(self.temporal_reductions)
|
| 639 |
+
|
| 640 |
+
@property
|
| 641 |
+
def spatial_downsample(self):
|
| 642 |
+
return math.prod(self.spatial_reductions)
|
| 643 |
+
|
| 644 |
+
def forward(self, x) -> LatentDistribution:
|
| 645 |
+
"""Forward pass.
|
| 646 |
+
|
| 647 |
+
Args:
|
| 648 |
+
x: Input video tensor. Shape: [B, C, T, H, W]. Scaled to [-1, 1]
|
| 649 |
+
|
| 650 |
+
Returns:
|
| 651 |
+
means: Latent tensor. Shape: [B, latent_dim, t, h, w]. Scaled [-1, 1].
|
| 652 |
+
h = H // 8, w = W // 8, t - 1 = (T - 1) // 6
|
| 653 |
+
logvar: Shape: [B, latent_dim, t, h, w].
|
| 654 |
+
"""
|
| 655 |
+
assert x.ndim == 5, f"Expected 5D input, got {x.shape}"
|
| 656 |
+
x = self.fourier_features(x)
|
| 657 |
+
|
| 658 |
+
x = self.layers(x)
|
| 659 |
+
|
| 660 |
+
x = self.output_norm(x)
|
| 661 |
+
x = F.silu(x, inplace=True)
|
| 662 |
+
x = self.output_proj(x)
|
| 663 |
+
|
| 664 |
+
means, logvar = torch.chunk(x, 2, dim=1)
|
| 665 |
+
|
| 666 |
+
assert means.ndim == 5
|
| 667 |
+
assert logvar.shape == means.shape
|
| 668 |
+
assert means.size(1) == self.latent_dim
|
| 669 |
+
|
| 670 |
+
return LatentDistribution(means, logvar)
|
| 671 |
+
|
| 672 |
+
|
| 673 |
+
class VideoVAE(nn.Module):
|
| 674 |
+
def __init__(self):
|
| 675 |
+
super().__init__()
|
| 676 |
+
self.encoder = Encoder(
|
| 677 |
+
in_channels=15,
|
| 678 |
+
base_channels=64,
|
| 679 |
+
channel_multipliers=[1, 2, 4, 6],
|
| 680 |
+
num_res_blocks=[3, 3, 4, 6, 3],
|
| 681 |
+
latent_dim=12,
|
| 682 |
+
temporal_reductions=[1, 2, 3],
|
| 683 |
+
spatial_reductions=[2, 2, 2],
|
| 684 |
+
prune_bottlenecks=[False, False, False, False, False],
|
| 685 |
+
has_attentions=[False, True, True, True, True],
|
| 686 |
+
affine=True,
|
| 687 |
+
bias=True,
|
| 688 |
+
input_is_conv_1x1=True,
|
| 689 |
+
padding_mode="replicate"
|
| 690 |
+
)
|
| 691 |
+
self.decoder = Decoder(
|
| 692 |
+
out_channels=3,
|
| 693 |
+
base_channels=128,
|
| 694 |
+
channel_multipliers=[1, 2, 4, 6],
|
| 695 |
+
temporal_expansions=[1, 2, 3],
|
| 696 |
+
spatial_expansions=[2, 2, 2],
|
| 697 |
+
num_res_blocks=[3, 3, 4, 6, 3],
|
| 698 |
+
latent_dim=12,
|
| 699 |
+
has_attention=[False, False, False, False, False],
|
| 700 |
+
padding_mode="replicate",
|
| 701 |
+
output_norm=False,
|
| 702 |
+
nonlinearity="silu",
|
| 703 |
+
output_nonlinearity="silu",
|
| 704 |
+
causal=True,
|
| 705 |
+
)
|
| 706 |
+
|
| 707 |
+
def encode(self, x):
|
| 708 |
+
return self.encoder(x).mode()
|
| 709 |
+
|
| 710 |
+
def decode(self, x):
|
| 711 |
+
return self.decoder(x)
|
ComfyUI/comfy/ldm/hidream/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (41.9 kB). View file
|
|
|
ComfyUI/comfy/ldm/hidream/model.py
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Tuple, List
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import einops
|
| 6 |
+
from einops import repeat
|
| 7 |
+
|
| 8 |
+
from comfy.ldm.lightricks.model import TimestepEmbedding, Timesteps
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
|
| 11 |
+
from comfy.ldm.flux.math import apply_rope, rope
|
| 12 |
+
from comfy.ldm.flux.layers import LastLayer
|
| 13 |
+
|
| 14 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 15 |
+
import comfy.model_management
|
| 16 |
+
import comfy.patcher_extension
|
| 17 |
+
import comfy.ldm.common_dit
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
# Copied from https://github.com/black-forest-labs/flux/blob/main/src/flux/modules/layers.py
|
| 21 |
+
class EmbedND(nn.Module):
|
| 22 |
+
def __init__(self, theta: int, axes_dim: List[int]):
|
| 23 |
+
super().__init__()
|
| 24 |
+
self.theta = theta
|
| 25 |
+
self.axes_dim = axes_dim
|
| 26 |
+
|
| 27 |
+
def forward(self, ids: torch.Tensor) -> torch.Tensor:
|
| 28 |
+
n_axes = ids.shape[-1]
|
| 29 |
+
emb = torch.cat(
|
| 30 |
+
[rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)],
|
| 31 |
+
dim=-3,
|
| 32 |
+
)
|
| 33 |
+
return emb.unsqueeze(2)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class PatchEmbed(nn.Module):
|
| 37 |
+
def __init__(
|
| 38 |
+
self,
|
| 39 |
+
patch_size=2,
|
| 40 |
+
in_channels=4,
|
| 41 |
+
out_channels=1024,
|
| 42 |
+
dtype=None, device=None, operations=None
|
| 43 |
+
):
|
| 44 |
+
super().__init__()
|
| 45 |
+
self.patch_size = patch_size
|
| 46 |
+
self.out_channels = out_channels
|
| 47 |
+
self.proj = operations.Linear(in_channels * patch_size * patch_size, out_channels, bias=True, dtype=dtype, device=device)
|
| 48 |
+
|
| 49 |
+
def forward(self, latent):
|
| 50 |
+
latent = self.proj(latent)
|
| 51 |
+
return latent
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class PooledEmbed(nn.Module):
|
| 55 |
+
def __init__(self, text_emb_dim, hidden_size, dtype=None, device=None, operations=None):
|
| 56 |
+
super().__init__()
|
| 57 |
+
self.pooled_embedder = TimestepEmbedding(in_channels=text_emb_dim, time_embed_dim=hidden_size, dtype=dtype, device=device, operations=operations)
|
| 58 |
+
|
| 59 |
+
def forward(self, pooled_embed):
|
| 60 |
+
return self.pooled_embedder(pooled_embed)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class TimestepEmbed(nn.Module):
|
| 64 |
+
def __init__(self, hidden_size, frequency_embedding_size=256, dtype=None, device=None, operations=None):
|
| 65 |
+
super().__init__()
|
| 66 |
+
self.time_proj = Timesteps(num_channels=frequency_embedding_size, flip_sin_to_cos=True, downscale_freq_shift=0)
|
| 67 |
+
self.timestep_embedder = TimestepEmbedding(in_channels=frequency_embedding_size, time_embed_dim=hidden_size, dtype=dtype, device=device, operations=operations)
|
| 68 |
+
|
| 69 |
+
def forward(self, timesteps, wdtype):
|
| 70 |
+
t_emb = self.time_proj(timesteps).to(dtype=wdtype)
|
| 71 |
+
t_emb = self.timestep_embedder(t_emb)
|
| 72 |
+
return t_emb
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def attention(query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, transformer_options={}):
|
| 76 |
+
return optimized_attention(query.view(query.shape[0], -1, query.shape[-1] * query.shape[-2]), key.view(key.shape[0], -1, key.shape[-1] * key.shape[-2]), value.view(value.shape[0], -1, value.shape[-1] * value.shape[-2]), query.shape[2], transformer_options=transformer_options)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class HiDreamAttnProcessor_flashattn:
|
| 80 |
+
"""Attention processor used typically in processing the SD3-like self-attention projections."""
|
| 81 |
+
|
| 82 |
+
def __call__(
|
| 83 |
+
self,
|
| 84 |
+
attn,
|
| 85 |
+
image_tokens: torch.FloatTensor,
|
| 86 |
+
image_tokens_masks: Optional[torch.FloatTensor] = None,
|
| 87 |
+
text_tokens: Optional[torch.FloatTensor] = None,
|
| 88 |
+
rope: torch.FloatTensor = None,
|
| 89 |
+
transformer_options={},
|
| 90 |
+
*args,
|
| 91 |
+
**kwargs,
|
| 92 |
+
) -> torch.FloatTensor:
|
| 93 |
+
dtype = image_tokens.dtype
|
| 94 |
+
batch_size = image_tokens.shape[0]
|
| 95 |
+
|
| 96 |
+
query_i = attn.q_rms_norm(attn.to_q(image_tokens)).to(dtype=dtype)
|
| 97 |
+
key_i = attn.k_rms_norm(attn.to_k(image_tokens)).to(dtype=dtype)
|
| 98 |
+
value_i = attn.to_v(image_tokens)
|
| 99 |
+
|
| 100 |
+
inner_dim = key_i.shape[-1]
|
| 101 |
+
head_dim = inner_dim // attn.heads
|
| 102 |
+
|
| 103 |
+
query_i = query_i.view(batch_size, -1, attn.heads, head_dim)
|
| 104 |
+
key_i = key_i.view(batch_size, -1, attn.heads, head_dim)
|
| 105 |
+
value_i = value_i.view(batch_size, -1, attn.heads, head_dim)
|
| 106 |
+
if image_tokens_masks is not None:
|
| 107 |
+
key_i = key_i * image_tokens_masks.view(batch_size, -1, 1, 1)
|
| 108 |
+
|
| 109 |
+
if not attn.single:
|
| 110 |
+
query_t = attn.q_rms_norm_t(attn.to_q_t(text_tokens)).to(dtype=dtype)
|
| 111 |
+
key_t = attn.k_rms_norm_t(attn.to_k_t(text_tokens)).to(dtype=dtype)
|
| 112 |
+
value_t = attn.to_v_t(text_tokens)
|
| 113 |
+
|
| 114 |
+
query_t = query_t.view(batch_size, -1, attn.heads, head_dim)
|
| 115 |
+
key_t = key_t.view(batch_size, -1, attn.heads, head_dim)
|
| 116 |
+
value_t = value_t.view(batch_size, -1, attn.heads, head_dim)
|
| 117 |
+
|
| 118 |
+
num_image_tokens = query_i.shape[1]
|
| 119 |
+
num_text_tokens = query_t.shape[1]
|
| 120 |
+
query = torch.cat([query_i, query_t], dim=1)
|
| 121 |
+
key = torch.cat([key_i, key_t], dim=1)
|
| 122 |
+
value = torch.cat([value_i, value_t], dim=1)
|
| 123 |
+
else:
|
| 124 |
+
query = query_i
|
| 125 |
+
key = key_i
|
| 126 |
+
value = value_i
|
| 127 |
+
|
| 128 |
+
if query.shape[-1] == rope.shape[-3] * 2:
|
| 129 |
+
query, key = apply_rope(query, key, rope)
|
| 130 |
+
else:
|
| 131 |
+
query_1, query_2 = query.chunk(2, dim=-1)
|
| 132 |
+
key_1, key_2 = key.chunk(2, dim=-1)
|
| 133 |
+
query_1, key_1 = apply_rope(query_1, key_1, rope)
|
| 134 |
+
query = torch.cat([query_1, query_2], dim=-1)
|
| 135 |
+
key = torch.cat([key_1, key_2], dim=-1)
|
| 136 |
+
|
| 137 |
+
hidden_states = attention(query, key, value, transformer_options=transformer_options)
|
| 138 |
+
|
| 139 |
+
if not attn.single:
|
| 140 |
+
hidden_states_i, hidden_states_t = torch.split(hidden_states, [num_image_tokens, num_text_tokens], dim=1)
|
| 141 |
+
hidden_states_i = attn.to_out(hidden_states_i)
|
| 142 |
+
hidden_states_t = attn.to_out_t(hidden_states_t)
|
| 143 |
+
return hidden_states_i, hidden_states_t
|
| 144 |
+
else:
|
| 145 |
+
hidden_states = attn.to_out(hidden_states)
|
| 146 |
+
return hidden_states
|
| 147 |
+
|
| 148 |
+
class HiDreamAttention(nn.Module):
|
| 149 |
+
def __init__(
|
| 150 |
+
self,
|
| 151 |
+
query_dim: int,
|
| 152 |
+
heads: int = 8,
|
| 153 |
+
dim_head: int = 64,
|
| 154 |
+
upcast_attention: bool = False,
|
| 155 |
+
upcast_softmax: bool = False,
|
| 156 |
+
scale_qk: bool = True,
|
| 157 |
+
eps: float = 1e-5,
|
| 158 |
+
processor = None,
|
| 159 |
+
out_dim: int = None,
|
| 160 |
+
single: bool = False,
|
| 161 |
+
dtype=None, device=None, operations=None
|
| 162 |
+
):
|
| 163 |
+
# super(Attention, self).__init__()
|
| 164 |
+
super().__init__()
|
| 165 |
+
self.inner_dim = out_dim if out_dim is not None else dim_head * heads
|
| 166 |
+
self.query_dim = query_dim
|
| 167 |
+
self.upcast_attention = upcast_attention
|
| 168 |
+
self.upcast_softmax = upcast_softmax
|
| 169 |
+
self.out_dim = out_dim if out_dim is not None else query_dim
|
| 170 |
+
|
| 171 |
+
self.scale_qk = scale_qk
|
| 172 |
+
self.scale = dim_head**-0.5 if self.scale_qk else 1.0
|
| 173 |
+
|
| 174 |
+
self.heads = out_dim // dim_head if out_dim is not None else heads
|
| 175 |
+
self.sliceable_head_dim = heads
|
| 176 |
+
self.single = single
|
| 177 |
+
|
| 178 |
+
linear_cls = operations.Linear
|
| 179 |
+
self.linear_cls = linear_cls
|
| 180 |
+
self.to_q = linear_cls(query_dim, self.inner_dim, dtype=dtype, device=device)
|
| 181 |
+
self.to_k = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
|
| 182 |
+
self.to_v = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
|
| 183 |
+
self.to_out = linear_cls(self.inner_dim, self.out_dim, dtype=dtype, device=device)
|
| 184 |
+
self.q_rms_norm = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
|
| 185 |
+
self.k_rms_norm = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
|
| 186 |
+
|
| 187 |
+
if not single:
|
| 188 |
+
self.to_q_t = linear_cls(query_dim, self.inner_dim, dtype=dtype, device=device)
|
| 189 |
+
self.to_k_t = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
|
| 190 |
+
self.to_v_t = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
|
| 191 |
+
self.to_out_t = linear_cls(self.inner_dim, self.out_dim, dtype=dtype, device=device)
|
| 192 |
+
self.q_rms_norm_t = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
|
| 193 |
+
self.k_rms_norm_t = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
|
| 194 |
+
|
| 195 |
+
self.processor = processor
|
| 196 |
+
|
| 197 |
+
def forward(
|
| 198 |
+
self,
|
| 199 |
+
norm_image_tokens: torch.FloatTensor,
|
| 200 |
+
image_tokens_masks: torch.FloatTensor = None,
|
| 201 |
+
norm_text_tokens: torch.FloatTensor = None,
|
| 202 |
+
rope: torch.FloatTensor = None,
|
| 203 |
+
transformer_options={},
|
| 204 |
+
) -> torch.Tensor:
|
| 205 |
+
return self.processor(
|
| 206 |
+
self,
|
| 207 |
+
image_tokens = norm_image_tokens,
|
| 208 |
+
image_tokens_masks = image_tokens_masks,
|
| 209 |
+
text_tokens = norm_text_tokens,
|
| 210 |
+
rope = rope,
|
| 211 |
+
transformer_options=transformer_options,
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
class FeedForwardSwiGLU(nn.Module):
|
| 216 |
+
def __init__(
|
| 217 |
+
self,
|
| 218 |
+
dim: int,
|
| 219 |
+
hidden_dim: int,
|
| 220 |
+
multiple_of: int = 256,
|
| 221 |
+
ffn_dim_multiplier: Optional[float] = None,
|
| 222 |
+
dtype=None, device=None, operations=None
|
| 223 |
+
):
|
| 224 |
+
super().__init__()
|
| 225 |
+
hidden_dim = int(2 * hidden_dim / 3)
|
| 226 |
+
# custom dim factor multiplier
|
| 227 |
+
if ffn_dim_multiplier is not None:
|
| 228 |
+
hidden_dim = int(ffn_dim_multiplier * hidden_dim)
|
| 229 |
+
hidden_dim = multiple_of * (
|
| 230 |
+
(hidden_dim + multiple_of - 1) // multiple_of
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
self.w1 = operations.Linear(dim, hidden_dim, bias=False, dtype=dtype, device=device)
|
| 234 |
+
self.w2 = operations.Linear(hidden_dim, dim, bias=False, dtype=dtype, device=device)
|
| 235 |
+
self.w3 = operations.Linear(dim, hidden_dim, bias=False, dtype=dtype, device=device)
|
| 236 |
+
|
| 237 |
+
def forward(self, x):
|
| 238 |
+
return self.w2(torch.nn.functional.silu(self.w1(x)) * self.w3(x))
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
# Modified from https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/model.py
|
| 242 |
+
class MoEGate(nn.Module):
|
| 243 |
+
def __init__(self, embed_dim, num_routed_experts=4, num_activated_experts=2, aux_loss_alpha=0.01, dtype=None, device=None, operations=None):
|
| 244 |
+
super().__init__()
|
| 245 |
+
self.top_k = num_activated_experts
|
| 246 |
+
self.n_routed_experts = num_routed_experts
|
| 247 |
+
|
| 248 |
+
self.scoring_func = 'softmax'
|
| 249 |
+
self.alpha = aux_loss_alpha
|
| 250 |
+
self.seq_aux = False
|
| 251 |
+
|
| 252 |
+
# topk selection algorithm
|
| 253 |
+
self.norm_topk_prob = False
|
| 254 |
+
self.gating_dim = embed_dim
|
| 255 |
+
self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim), dtype=dtype, device=device))
|
| 256 |
+
self.reset_parameters()
|
| 257 |
+
|
| 258 |
+
def reset_parameters(self) -> None:
|
| 259 |
+
pass
|
| 260 |
+
# import torch.nn.init as init
|
| 261 |
+
# init.kaiming_uniform_(self.weight, a=math.sqrt(5))
|
| 262 |
+
|
| 263 |
+
def forward(self, hidden_states):
|
| 264 |
+
bsz, seq_len, h = hidden_states.shape
|
| 265 |
+
|
| 266 |
+
### compute gating score
|
| 267 |
+
hidden_states = hidden_states.view(-1, h)
|
| 268 |
+
logits = F.linear(hidden_states, comfy.model_management.cast_to(self.weight, dtype=hidden_states.dtype, device=hidden_states.device), None)
|
| 269 |
+
if self.scoring_func == 'softmax':
|
| 270 |
+
scores = logits.softmax(dim=-1)
|
| 271 |
+
else:
|
| 272 |
+
raise NotImplementedError(f'insupportable scoring function for MoE gating: {self.scoring_func}')
|
| 273 |
+
|
| 274 |
+
### select top-k experts
|
| 275 |
+
topk_weight, topk_idx = torch.topk(scores, k=self.top_k, dim=-1, sorted=False)
|
| 276 |
+
|
| 277 |
+
### norm gate to sum 1
|
| 278 |
+
if self.top_k > 1 and self.norm_topk_prob:
|
| 279 |
+
denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20
|
| 280 |
+
topk_weight = topk_weight / denominator
|
| 281 |
+
|
| 282 |
+
aux_loss = None
|
| 283 |
+
return topk_idx, topk_weight, aux_loss
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
# Modified from https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/model.py
|
| 287 |
+
class MOEFeedForwardSwiGLU(nn.Module):
|
| 288 |
+
def __init__(
|
| 289 |
+
self,
|
| 290 |
+
dim: int,
|
| 291 |
+
hidden_dim: int,
|
| 292 |
+
num_routed_experts: int,
|
| 293 |
+
num_activated_experts: int,
|
| 294 |
+
dtype=None, device=None, operations=None
|
| 295 |
+
):
|
| 296 |
+
super().__init__()
|
| 297 |
+
self.shared_experts = FeedForwardSwiGLU(dim, hidden_dim // 2, dtype=dtype, device=device, operations=operations)
|
| 298 |
+
self.experts = nn.ModuleList([FeedForwardSwiGLU(dim, hidden_dim, dtype=dtype, device=device, operations=operations) for i in range(num_routed_experts)])
|
| 299 |
+
self.gate = MoEGate(
|
| 300 |
+
embed_dim = dim,
|
| 301 |
+
num_routed_experts = num_routed_experts,
|
| 302 |
+
num_activated_experts = num_activated_experts,
|
| 303 |
+
dtype=dtype, device=device, operations=operations
|
| 304 |
+
)
|
| 305 |
+
self.num_activated_experts = num_activated_experts
|
| 306 |
+
|
| 307 |
+
def forward(self, x):
|
| 308 |
+
wtype = x.dtype
|
| 309 |
+
identity = x
|
| 310 |
+
orig_shape = x.shape
|
| 311 |
+
topk_idx, topk_weight, aux_loss = self.gate(x)
|
| 312 |
+
x = x.view(-1, x.shape[-1])
|
| 313 |
+
flat_topk_idx = topk_idx.view(-1)
|
| 314 |
+
if True: # self.training: # TODO: check which branch performs faster
|
| 315 |
+
x = x.repeat_interleave(self.num_activated_experts, dim=0)
|
| 316 |
+
y = torch.empty_like(x, dtype=wtype)
|
| 317 |
+
for i, expert in enumerate(self.experts):
|
| 318 |
+
y[flat_topk_idx == i] = expert(x[flat_topk_idx == i]).to(dtype=wtype)
|
| 319 |
+
y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim=1)
|
| 320 |
+
y = y.view(*orig_shape).to(dtype=wtype)
|
| 321 |
+
#y = AddAuxiliaryLoss.apply(y, aux_loss)
|
| 322 |
+
else:
|
| 323 |
+
y = self.moe_infer(x, flat_topk_idx, topk_weight.view(-1, 1)).view(*orig_shape)
|
| 324 |
+
y = y + self.shared_experts(identity)
|
| 325 |
+
return y
|
| 326 |
+
|
| 327 |
+
@torch.no_grad()
|
| 328 |
+
def moe_infer(self, x, flat_expert_indices, flat_expert_weights):
|
| 329 |
+
expert_cache = torch.zeros_like(x)
|
| 330 |
+
idxs = flat_expert_indices.argsort()
|
| 331 |
+
tokens_per_expert = flat_expert_indices.bincount().cpu().numpy().cumsum(0)
|
| 332 |
+
token_idxs = idxs // self.num_activated_experts
|
| 333 |
+
for i, end_idx in enumerate(tokens_per_expert):
|
| 334 |
+
start_idx = 0 if i == 0 else tokens_per_expert[i-1]
|
| 335 |
+
if start_idx == end_idx:
|
| 336 |
+
continue
|
| 337 |
+
expert = self.experts[i]
|
| 338 |
+
exp_token_idx = token_idxs[start_idx:end_idx]
|
| 339 |
+
expert_tokens = x[exp_token_idx]
|
| 340 |
+
expert_out = expert(expert_tokens)
|
| 341 |
+
expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]])
|
| 342 |
+
|
| 343 |
+
# for fp16 and other dtype
|
| 344 |
+
expert_cache = expert_cache.to(expert_out.dtype)
|
| 345 |
+
expert_cache.scatter_reduce_(0, exp_token_idx.view(-1, 1).repeat(1, x.shape[-1]), expert_out, reduce='sum')
|
| 346 |
+
return expert_cache
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
class TextProjection(nn.Module):
|
| 350 |
+
def __init__(self, in_features, hidden_size, dtype=None, device=None, operations=None):
|
| 351 |
+
super().__init__()
|
| 352 |
+
self.linear = operations.Linear(in_features=in_features, out_features=hidden_size, bias=False, dtype=dtype, device=device)
|
| 353 |
+
|
| 354 |
+
def forward(self, caption):
|
| 355 |
+
hidden_states = self.linear(caption)
|
| 356 |
+
return hidden_states
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
class BlockType:
|
| 360 |
+
TransformerBlock = 1
|
| 361 |
+
SingleTransformerBlock = 2
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
class HiDreamImageSingleTransformerBlock(nn.Module):
|
| 365 |
+
def __init__(
|
| 366 |
+
self,
|
| 367 |
+
dim: int,
|
| 368 |
+
num_attention_heads: int,
|
| 369 |
+
attention_head_dim: int,
|
| 370 |
+
num_routed_experts: int = 4,
|
| 371 |
+
num_activated_experts: int = 2,
|
| 372 |
+
dtype=None, device=None, operations=None
|
| 373 |
+
):
|
| 374 |
+
super().__init__()
|
| 375 |
+
self.num_attention_heads = num_attention_heads
|
| 376 |
+
self.adaLN_modulation = nn.Sequential(
|
| 377 |
+
nn.SiLU(),
|
| 378 |
+
operations.Linear(dim, 6 * dim, bias=True, dtype=dtype, device=device)
|
| 379 |
+
)
|
| 380 |
+
|
| 381 |
+
# 1. Attention
|
| 382 |
+
self.norm1_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
|
| 383 |
+
self.attn1 = HiDreamAttention(
|
| 384 |
+
query_dim=dim,
|
| 385 |
+
heads=num_attention_heads,
|
| 386 |
+
dim_head=attention_head_dim,
|
| 387 |
+
processor = HiDreamAttnProcessor_flashattn(),
|
| 388 |
+
single = True,
|
| 389 |
+
dtype=dtype, device=device, operations=operations
|
| 390 |
+
)
|
| 391 |
+
|
| 392 |
+
# 3. Feed-forward
|
| 393 |
+
self.norm3_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
|
| 394 |
+
if num_routed_experts > 0:
|
| 395 |
+
self.ff_i = MOEFeedForwardSwiGLU(
|
| 396 |
+
dim = dim,
|
| 397 |
+
hidden_dim = 4 * dim,
|
| 398 |
+
num_routed_experts = num_routed_experts,
|
| 399 |
+
num_activated_experts = num_activated_experts,
|
| 400 |
+
dtype=dtype, device=device, operations=operations
|
| 401 |
+
)
|
| 402 |
+
else:
|
| 403 |
+
self.ff_i = FeedForwardSwiGLU(dim = dim, hidden_dim = 4 * dim, dtype=dtype, device=device, operations=operations)
|
| 404 |
+
|
| 405 |
+
def forward(
|
| 406 |
+
self,
|
| 407 |
+
image_tokens: torch.FloatTensor,
|
| 408 |
+
image_tokens_masks: Optional[torch.FloatTensor] = None,
|
| 409 |
+
text_tokens: Optional[torch.FloatTensor] = None,
|
| 410 |
+
adaln_input: Optional[torch.FloatTensor] = None,
|
| 411 |
+
rope: torch.FloatTensor = None,
|
| 412 |
+
transformer_options={},
|
| 413 |
+
) -> torch.FloatTensor:
|
| 414 |
+
wtype = image_tokens.dtype
|
| 415 |
+
shift_msa_i, scale_msa_i, gate_msa_i, shift_mlp_i, scale_mlp_i, gate_mlp_i = \
|
| 416 |
+
self.adaLN_modulation(adaln_input)[:,None].chunk(6, dim=-1)
|
| 417 |
+
|
| 418 |
+
# 1. MM-Attention
|
| 419 |
+
norm_image_tokens = self.norm1_i(image_tokens).to(dtype=wtype)
|
| 420 |
+
norm_image_tokens = norm_image_tokens * (1 + scale_msa_i) + shift_msa_i
|
| 421 |
+
attn_output_i = self.attn1(
|
| 422 |
+
norm_image_tokens,
|
| 423 |
+
image_tokens_masks,
|
| 424 |
+
rope = rope,
|
| 425 |
+
transformer_options=transformer_options,
|
| 426 |
+
)
|
| 427 |
+
image_tokens = gate_msa_i * attn_output_i + image_tokens
|
| 428 |
+
|
| 429 |
+
# 2. Feed-forward
|
| 430 |
+
norm_image_tokens = self.norm3_i(image_tokens).to(dtype=wtype)
|
| 431 |
+
norm_image_tokens = norm_image_tokens * (1 + scale_mlp_i) + shift_mlp_i
|
| 432 |
+
ff_output_i = gate_mlp_i * self.ff_i(norm_image_tokens.to(dtype=wtype))
|
| 433 |
+
image_tokens = ff_output_i + image_tokens
|
| 434 |
+
return image_tokens
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
class HiDreamImageTransformerBlock(nn.Module):
|
| 438 |
+
def __init__(
|
| 439 |
+
self,
|
| 440 |
+
dim: int,
|
| 441 |
+
num_attention_heads: int,
|
| 442 |
+
attention_head_dim: int,
|
| 443 |
+
num_routed_experts: int = 4,
|
| 444 |
+
num_activated_experts: int = 2,
|
| 445 |
+
dtype=None, device=None, operations=None
|
| 446 |
+
):
|
| 447 |
+
super().__init__()
|
| 448 |
+
self.num_attention_heads = num_attention_heads
|
| 449 |
+
self.adaLN_modulation = nn.Sequential(
|
| 450 |
+
nn.SiLU(),
|
| 451 |
+
operations.Linear(dim, 12 * dim, bias=True, dtype=dtype, device=device)
|
| 452 |
+
)
|
| 453 |
+
# nn.init.zeros_(self.adaLN_modulation[1].weight)
|
| 454 |
+
# nn.init.zeros_(self.adaLN_modulation[1].bias)
|
| 455 |
+
|
| 456 |
+
# 1. Attention
|
| 457 |
+
self.norm1_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
|
| 458 |
+
self.norm1_t = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
|
| 459 |
+
self.attn1 = HiDreamAttention(
|
| 460 |
+
query_dim=dim,
|
| 461 |
+
heads=num_attention_heads,
|
| 462 |
+
dim_head=attention_head_dim,
|
| 463 |
+
processor = HiDreamAttnProcessor_flashattn(),
|
| 464 |
+
single = False,
|
| 465 |
+
dtype=dtype, device=device, operations=operations
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
# 3. Feed-forward
|
| 469 |
+
self.norm3_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
|
| 470 |
+
if num_routed_experts > 0:
|
| 471 |
+
self.ff_i = MOEFeedForwardSwiGLU(
|
| 472 |
+
dim = dim,
|
| 473 |
+
hidden_dim = 4 * dim,
|
| 474 |
+
num_routed_experts = num_routed_experts,
|
| 475 |
+
num_activated_experts = num_activated_experts,
|
| 476 |
+
dtype=dtype, device=device, operations=operations
|
| 477 |
+
)
|
| 478 |
+
else:
|
| 479 |
+
self.ff_i = FeedForwardSwiGLU(dim = dim, hidden_dim = 4 * dim, dtype=dtype, device=device, operations=operations)
|
| 480 |
+
self.norm3_t = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False)
|
| 481 |
+
self.ff_t = FeedForwardSwiGLU(dim = dim, hidden_dim = 4 * dim, dtype=dtype, device=device, operations=operations)
|
| 482 |
+
|
| 483 |
+
def forward(
|
| 484 |
+
self,
|
| 485 |
+
image_tokens: torch.FloatTensor,
|
| 486 |
+
image_tokens_masks: Optional[torch.FloatTensor] = None,
|
| 487 |
+
text_tokens: Optional[torch.FloatTensor] = None,
|
| 488 |
+
adaln_input: Optional[torch.FloatTensor] = None,
|
| 489 |
+
rope: torch.FloatTensor = None,
|
| 490 |
+
transformer_options={},
|
| 491 |
+
) -> torch.FloatTensor:
|
| 492 |
+
wtype = image_tokens.dtype
|
| 493 |
+
shift_msa_i, scale_msa_i, gate_msa_i, shift_mlp_i, scale_mlp_i, gate_mlp_i, \
|
| 494 |
+
shift_msa_t, scale_msa_t, gate_msa_t, shift_mlp_t, scale_mlp_t, gate_mlp_t = \
|
| 495 |
+
self.adaLN_modulation(adaln_input)[:,None].chunk(12, dim=-1)
|
| 496 |
+
|
| 497 |
+
# 1. MM-Attention
|
| 498 |
+
norm_image_tokens = self.norm1_i(image_tokens).to(dtype=wtype)
|
| 499 |
+
norm_image_tokens = norm_image_tokens * (1 + scale_msa_i) + shift_msa_i
|
| 500 |
+
norm_text_tokens = self.norm1_t(text_tokens).to(dtype=wtype)
|
| 501 |
+
norm_text_tokens = norm_text_tokens * (1 + scale_msa_t) + shift_msa_t
|
| 502 |
+
|
| 503 |
+
attn_output_i, attn_output_t = self.attn1(
|
| 504 |
+
norm_image_tokens,
|
| 505 |
+
image_tokens_masks,
|
| 506 |
+
norm_text_tokens,
|
| 507 |
+
rope = rope,
|
| 508 |
+
transformer_options=transformer_options,
|
| 509 |
+
)
|
| 510 |
+
|
| 511 |
+
image_tokens = gate_msa_i * attn_output_i + image_tokens
|
| 512 |
+
text_tokens = gate_msa_t * attn_output_t + text_tokens
|
| 513 |
+
|
| 514 |
+
# 2. Feed-forward
|
| 515 |
+
norm_image_tokens = self.norm3_i(image_tokens).to(dtype=wtype)
|
| 516 |
+
norm_image_tokens = norm_image_tokens * (1 + scale_mlp_i) + shift_mlp_i
|
| 517 |
+
norm_text_tokens = self.norm3_t(text_tokens).to(dtype=wtype)
|
| 518 |
+
norm_text_tokens = norm_text_tokens * (1 + scale_mlp_t) + shift_mlp_t
|
| 519 |
+
|
| 520 |
+
ff_output_i = gate_mlp_i * self.ff_i(norm_image_tokens)
|
| 521 |
+
ff_output_t = gate_mlp_t * self.ff_t(norm_text_tokens)
|
| 522 |
+
image_tokens = ff_output_i + image_tokens
|
| 523 |
+
text_tokens = ff_output_t + text_tokens
|
| 524 |
+
return image_tokens, text_tokens
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
class HiDreamImageBlock(nn.Module):
|
| 528 |
+
def __init__(
|
| 529 |
+
self,
|
| 530 |
+
dim: int,
|
| 531 |
+
num_attention_heads: int,
|
| 532 |
+
attention_head_dim: int,
|
| 533 |
+
num_routed_experts: int = 4,
|
| 534 |
+
num_activated_experts: int = 2,
|
| 535 |
+
block_type: BlockType = BlockType.TransformerBlock,
|
| 536 |
+
dtype=None, device=None, operations=None
|
| 537 |
+
):
|
| 538 |
+
super().__init__()
|
| 539 |
+
block_classes = {
|
| 540 |
+
BlockType.TransformerBlock: HiDreamImageTransformerBlock,
|
| 541 |
+
BlockType.SingleTransformerBlock: HiDreamImageSingleTransformerBlock,
|
| 542 |
+
}
|
| 543 |
+
self.block = block_classes[block_type](
|
| 544 |
+
dim,
|
| 545 |
+
num_attention_heads,
|
| 546 |
+
attention_head_dim,
|
| 547 |
+
num_routed_experts,
|
| 548 |
+
num_activated_experts,
|
| 549 |
+
dtype=dtype, device=device, operations=operations
|
| 550 |
+
)
|
| 551 |
+
|
| 552 |
+
def forward(
|
| 553 |
+
self,
|
| 554 |
+
image_tokens: torch.FloatTensor,
|
| 555 |
+
image_tokens_masks: Optional[torch.FloatTensor] = None,
|
| 556 |
+
text_tokens: Optional[torch.FloatTensor] = None,
|
| 557 |
+
adaln_input: torch.FloatTensor = None,
|
| 558 |
+
rope: torch.FloatTensor = None,
|
| 559 |
+
transformer_options={},
|
| 560 |
+
) -> torch.FloatTensor:
|
| 561 |
+
return self.block(
|
| 562 |
+
image_tokens,
|
| 563 |
+
image_tokens_masks,
|
| 564 |
+
text_tokens,
|
| 565 |
+
adaln_input,
|
| 566 |
+
rope,
|
| 567 |
+
transformer_options=transformer_options,
|
| 568 |
+
)
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
class HiDreamImageTransformer2DModel(nn.Module):
|
| 572 |
+
def __init__(
|
| 573 |
+
self,
|
| 574 |
+
patch_size: Optional[int] = None,
|
| 575 |
+
in_channels: int = 64,
|
| 576 |
+
out_channels: Optional[int] = None,
|
| 577 |
+
num_layers: int = 16,
|
| 578 |
+
num_single_layers: int = 32,
|
| 579 |
+
attention_head_dim: int = 128,
|
| 580 |
+
num_attention_heads: int = 20,
|
| 581 |
+
caption_channels: List[int] = None,
|
| 582 |
+
text_emb_dim: int = 2048,
|
| 583 |
+
num_routed_experts: int = 4,
|
| 584 |
+
num_activated_experts: int = 2,
|
| 585 |
+
axes_dims_rope: Tuple[int, int] = (32, 32),
|
| 586 |
+
max_resolution: Tuple[int, int] = (128, 128),
|
| 587 |
+
llama_layers: List[int] = None,
|
| 588 |
+
image_model=None,
|
| 589 |
+
dtype=None, device=None, operations=None
|
| 590 |
+
):
|
| 591 |
+
self.patch_size = patch_size
|
| 592 |
+
self.num_attention_heads = num_attention_heads
|
| 593 |
+
self.attention_head_dim = attention_head_dim
|
| 594 |
+
self.num_layers = num_layers
|
| 595 |
+
self.num_single_layers = num_single_layers
|
| 596 |
+
|
| 597 |
+
self.gradient_checkpointing = False
|
| 598 |
+
|
| 599 |
+
super().__init__()
|
| 600 |
+
self.dtype = dtype
|
| 601 |
+
self.out_channels = out_channels or in_channels
|
| 602 |
+
self.inner_dim = self.num_attention_heads * self.attention_head_dim
|
| 603 |
+
self.llama_layers = llama_layers
|
| 604 |
+
|
| 605 |
+
self.t_embedder = TimestepEmbed(self.inner_dim, dtype=dtype, device=device, operations=operations)
|
| 606 |
+
self.p_embedder = PooledEmbed(text_emb_dim, self.inner_dim, dtype=dtype, device=device, operations=operations)
|
| 607 |
+
self.x_embedder = PatchEmbed(
|
| 608 |
+
patch_size = patch_size,
|
| 609 |
+
in_channels = in_channels,
|
| 610 |
+
out_channels = self.inner_dim,
|
| 611 |
+
dtype=dtype, device=device, operations=operations
|
| 612 |
+
)
|
| 613 |
+
self.pe_embedder = EmbedND(theta=10000, axes_dim=axes_dims_rope)
|
| 614 |
+
|
| 615 |
+
self.double_stream_blocks = nn.ModuleList(
|
| 616 |
+
[
|
| 617 |
+
HiDreamImageBlock(
|
| 618 |
+
dim = self.inner_dim,
|
| 619 |
+
num_attention_heads = self.num_attention_heads,
|
| 620 |
+
attention_head_dim = self.attention_head_dim,
|
| 621 |
+
num_routed_experts = num_routed_experts,
|
| 622 |
+
num_activated_experts = num_activated_experts,
|
| 623 |
+
block_type = BlockType.TransformerBlock,
|
| 624 |
+
dtype=dtype, device=device, operations=operations
|
| 625 |
+
)
|
| 626 |
+
for i in range(self.num_layers)
|
| 627 |
+
]
|
| 628 |
+
)
|
| 629 |
+
|
| 630 |
+
self.single_stream_blocks = nn.ModuleList(
|
| 631 |
+
[
|
| 632 |
+
HiDreamImageBlock(
|
| 633 |
+
dim = self.inner_dim,
|
| 634 |
+
num_attention_heads = self.num_attention_heads,
|
| 635 |
+
attention_head_dim = self.attention_head_dim,
|
| 636 |
+
num_routed_experts = num_routed_experts,
|
| 637 |
+
num_activated_experts = num_activated_experts,
|
| 638 |
+
block_type = BlockType.SingleTransformerBlock,
|
| 639 |
+
dtype=dtype, device=device, operations=operations
|
| 640 |
+
)
|
| 641 |
+
for i in range(self.num_single_layers)
|
| 642 |
+
]
|
| 643 |
+
)
|
| 644 |
+
|
| 645 |
+
self.final_layer = LastLayer(self.inner_dim, patch_size, self.out_channels, dtype=dtype, device=device, operations=operations)
|
| 646 |
+
|
| 647 |
+
caption_channels = [caption_channels[1], ] * (num_layers + num_single_layers) + [caption_channels[0], ]
|
| 648 |
+
caption_projection = []
|
| 649 |
+
for caption_channel in caption_channels:
|
| 650 |
+
caption_projection.append(TextProjection(in_features=caption_channel, hidden_size=self.inner_dim, dtype=dtype, device=device, operations=operations))
|
| 651 |
+
self.caption_projection = nn.ModuleList(caption_projection)
|
| 652 |
+
self.max_seq = max_resolution[0] * max_resolution[1] // (patch_size * patch_size)
|
| 653 |
+
|
| 654 |
+
def expand_timesteps(self, timesteps, batch_size, device):
|
| 655 |
+
if not torch.is_tensor(timesteps):
|
| 656 |
+
is_mps = device.type == "mps"
|
| 657 |
+
if isinstance(timesteps, float):
|
| 658 |
+
dtype = torch.float32 if is_mps else torch.float64
|
| 659 |
+
else:
|
| 660 |
+
dtype = torch.int32 if is_mps else torch.int64
|
| 661 |
+
timesteps = torch.tensor([timesteps], dtype=dtype, device=device)
|
| 662 |
+
elif len(timesteps.shape) == 0:
|
| 663 |
+
timesteps = timesteps[None].to(device)
|
| 664 |
+
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
| 665 |
+
timesteps = timesteps.expand(batch_size)
|
| 666 |
+
return timesteps
|
| 667 |
+
|
| 668 |
+
def unpatchify(self, x: torch.Tensor, img_sizes: List[Tuple[int, int]]) -> List[torch.Tensor]:
|
| 669 |
+
x_arr = []
|
| 670 |
+
for i, img_size in enumerate(img_sizes):
|
| 671 |
+
pH, pW = img_size
|
| 672 |
+
x_arr.append(
|
| 673 |
+
einops.rearrange(x[i, :pH*pW].reshape(1, pH, pW, -1), 'B H W (p1 p2 C) -> B C (H p1) (W p2)',
|
| 674 |
+
p1=self.patch_size, p2=self.patch_size)
|
| 675 |
+
)
|
| 676 |
+
x = torch.cat(x_arr, dim=0)
|
| 677 |
+
return x
|
| 678 |
+
|
| 679 |
+
def patchify(self, x, max_seq, img_sizes=None):
|
| 680 |
+
pz2 = self.patch_size * self.patch_size
|
| 681 |
+
if isinstance(x, torch.Tensor):
|
| 682 |
+
B = x.shape[0]
|
| 683 |
+
device = x.device
|
| 684 |
+
dtype = x.dtype
|
| 685 |
+
else:
|
| 686 |
+
B = len(x)
|
| 687 |
+
device = x[0].device
|
| 688 |
+
dtype = x[0].dtype
|
| 689 |
+
x_masks = torch.zeros((B, max_seq), dtype=dtype, device=device)
|
| 690 |
+
|
| 691 |
+
if img_sizes is not None:
|
| 692 |
+
for i, img_size in enumerate(img_sizes):
|
| 693 |
+
x_masks[i, 0:img_size[0] * img_size[1]] = 1
|
| 694 |
+
x = einops.rearrange(x, 'B C S p -> B S (p C)', p=pz2)
|
| 695 |
+
elif isinstance(x, torch.Tensor):
|
| 696 |
+
pH, pW = x.shape[-2] // self.patch_size, x.shape[-1] // self.patch_size
|
| 697 |
+
x = einops.rearrange(x, 'B C (H p1) (W p2) -> B (H W) (p1 p2 C)', p1=self.patch_size, p2=self.patch_size)
|
| 698 |
+
img_sizes = [[pH, pW]] * B
|
| 699 |
+
x_masks = None
|
| 700 |
+
else:
|
| 701 |
+
raise NotImplementedError
|
| 702 |
+
return x, x_masks, img_sizes
|
| 703 |
+
|
| 704 |
+
def forward(self,
|
| 705 |
+
x: torch.Tensor,
|
| 706 |
+
t: torch.Tensor,
|
| 707 |
+
y: Optional[torch.Tensor] = None,
|
| 708 |
+
context: Optional[torch.Tensor] = None,
|
| 709 |
+
encoder_hidden_states_llama3=None,
|
| 710 |
+
image_cond=None,
|
| 711 |
+
control = None,
|
| 712 |
+
transformer_options = {},
|
| 713 |
+
):
|
| 714 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 715 |
+
self._forward,
|
| 716 |
+
self,
|
| 717 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
|
| 718 |
+
).execute(x, t, y, context, encoder_hidden_states_llama3, image_cond, control, transformer_options)
|
| 719 |
+
|
| 720 |
+
def _forward(
|
| 721 |
+
self,
|
| 722 |
+
x: torch.Tensor,
|
| 723 |
+
t: torch.Tensor,
|
| 724 |
+
y: Optional[torch.Tensor] = None,
|
| 725 |
+
context: Optional[torch.Tensor] = None,
|
| 726 |
+
encoder_hidden_states_llama3=None,
|
| 727 |
+
image_cond=None,
|
| 728 |
+
control = None,
|
| 729 |
+
transformer_options = {},
|
| 730 |
+
) -> torch.Tensor:
|
| 731 |
+
bs, c, h, w = x.shape
|
| 732 |
+
if image_cond is not None:
|
| 733 |
+
x = torch.cat([x, image_cond], dim=-1)
|
| 734 |
+
hidden_states = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_size, self.patch_size))
|
| 735 |
+
timesteps = t
|
| 736 |
+
pooled_embeds = y
|
| 737 |
+
T5_encoder_hidden_states = context
|
| 738 |
+
|
| 739 |
+
img_sizes = None
|
| 740 |
+
|
| 741 |
+
# spatial forward
|
| 742 |
+
batch_size = hidden_states.shape[0]
|
| 743 |
+
hidden_states_type = hidden_states.dtype
|
| 744 |
+
|
| 745 |
+
# 0. time
|
| 746 |
+
timesteps = self.expand_timesteps(timesteps, batch_size, hidden_states.device)
|
| 747 |
+
timesteps = self.t_embedder(timesteps, hidden_states_type)
|
| 748 |
+
p_embedder = self.p_embedder(pooled_embeds)
|
| 749 |
+
adaln_input = timesteps + p_embedder
|
| 750 |
+
|
| 751 |
+
hidden_states, image_tokens_masks, img_sizes = self.patchify(hidden_states, self.max_seq, img_sizes)
|
| 752 |
+
if image_tokens_masks is None:
|
| 753 |
+
pH, pW = img_sizes[0]
|
| 754 |
+
img_ids = torch.zeros(pH, pW, 3, device=hidden_states.device)
|
| 755 |
+
img_ids[..., 1] = img_ids[..., 1] + torch.arange(pH, device=hidden_states.device)[:, None]
|
| 756 |
+
img_ids[..., 2] = img_ids[..., 2] + torch.arange(pW, device=hidden_states.device)[None, :]
|
| 757 |
+
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=batch_size)
|
| 758 |
+
hidden_states = self.x_embedder(hidden_states)
|
| 759 |
+
|
| 760 |
+
# T5_encoder_hidden_states = encoder_hidden_states[0]
|
| 761 |
+
encoder_hidden_states = encoder_hidden_states_llama3.movedim(1, 0)
|
| 762 |
+
encoder_hidden_states = [encoder_hidden_states[k] for k in self.llama_layers]
|
| 763 |
+
|
| 764 |
+
if self.caption_projection is not None:
|
| 765 |
+
new_encoder_hidden_states = []
|
| 766 |
+
for i, enc_hidden_state in enumerate(encoder_hidden_states):
|
| 767 |
+
enc_hidden_state = self.caption_projection[i](enc_hidden_state)
|
| 768 |
+
enc_hidden_state = enc_hidden_state.view(batch_size, -1, hidden_states.shape[-1])
|
| 769 |
+
new_encoder_hidden_states.append(enc_hidden_state)
|
| 770 |
+
encoder_hidden_states = new_encoder_hidden_states
|
| 771 |
+
T5_encoder_hidden_states = self.caption_projection[-1](T5_encoder_hidden_states)
|
| 772 |
+
T5_encoder_hidden_states = T5_encoder_hidden_states.view(batch_size, -1, hidden_states.shape[-1])
|
| 773 |
+
encoder_hidden_states.append(T5_encoder_hidden_states)
|
| 774 |
+
|
| 775 |
+
txt_ids = torch.zeros(
|
| 776 |
+
batch_size,
|
| 777 |
+
encoder_hidden_states[-1].shape[1] + encoder_hidden_states[-2].shape[1] + encoder_hidden_states[0].shape[1],
|
| 778 |
+
3,
|
| 779 |
+
device=img_ids.device, dtype=img_ids.dtype
|
| 780 |
+
)
|
| 781 |
+
ids = torch.cat((img_ids, txt_ids), dim=1)
|
| 782 |
+
rope = self.pe_embedder(ids)
|
| 783 |
+
|
| 784 |
+
# 2. Blocks
|
| 785 |
+
block_id = 0
|
| 786 |
+
initial_encoder_hidden_states = torch.cat([encoder_hidden_states[-1], encoder_hidden_states[-2]], dim=1)
|
| 787 |
+
initial_encoder_hidden_states_seq_len = initial_encoder_hidden_states.shape[1]
|
| 788 |
+
for bid, block in enumerate(self.double_stream_blocks):
|
| 789 |
+
cur_llama31_encoder_hidden_states = encoder_hidden_states[block_id]
|
| 790 |
+
cur_encoder_hidden_states = torch.cat([initial_encoder_hidden_states, cur_llama31_encoder_hidden_states], dim=1)
|
| 791 |
+
hidden_states, initial_encoder_hidden_states = block(
|
| 792 |
+
image_tokens = hidden_states,
|
| 793 |
+
image_tokens_masks = image_tokens_masks,
|
| 794 |
+
text_tokens = cur_encoder_hidden_states,
|
| 795 |
+
adaln_input = adaln_input,
|
| 796 |
+
rope = rope,
|
| 797 |
+
transformer_options=transformer_options,
|
| 798 |
+
)
|
| 799 |
+
initial_encoder_hidden_states = initial_encoder_hidden_states[:, :initial_encoder_hidden_states_seq_len]
|
| 800 |
+
block_id += 1
|
| 801 |
+
|
| 802 |
+
image_tokens_seq_len = hidden_states.shape[1]
|
| 803 |
+
hidden_states = torch.cat([hidden_states, initial_encoder_hidden_states], dim=1)
|
| 804 |
+
hidden_states_seq_len = hidden_states.shape[1]
|
| 805 |
+
if image_tokens_masks is not None:
|
| 806 |
+
encoder_attention_mask_ones = torch.ones(
|
| 807 |
+
(batch_size, initial_encoder_hidden_states.shape[1] + cur_llama31_encoder_hidden_states.shape[1]),
|
| 808 |
+
device=image_tokens_masks.device, dtype=image_tokens_masks.dtype
|
| 809 |
+
)
|
| 810 |
+
image_tokens_masks = torch.cat([image_tokens_masks, encoder_attention_mask_ones], dim=1)
|
| 811 |
+
|
| 812 |
+
for bid, block in enumerate(self.single_stream_blocks):
|
| 813 |
+
cur_llama31_encoder_hidden_states = encoder_hidden_states[block_id]
|
| 814 |
+
hidden_states = torch.cat([hidden_states, cur_llama31_encoder_hidden_states], dim=1)
|
| 815 |
+
hidden_states = block(
|
| 816 |
+
image_tokens=hidden_states,
|
| 817 |
+
image_tokens_masks=image_tokens_masks,
|
| 818 |
+
text_tokens=None,
|
| 819 |
+
adaln_input=adaln_input,
|
| 820 |
+
rope=rope,
|
| 821 |
+
transformer_options=transformer_options,
|
| 822 |
+
)
|
| 823 |
+
hidden_states = hidden_states[:, :hidden_states_seq_len]
|
| 824 |
+
block_id += 1
|
| 825 |
+
|
| 826 |
+
hidden_states = hidden_states[:, :image_tokens_seq_len, ...]
|
| 827 |
+
output = self.final_layer(hidden_states, adaln_input)
|
| 828 |
+
output = self.unpatchify(output, img_sizes)
|
| 829 |
+
return -output[:, :, :h, :w]
|
ComfyUI/comfy/ldm/hidream_o1/__pycache__/attention.cpython-312.pyc
ADDED
|
Binary file (2.42 kB). View file
|
|
|
ComfyUI/comfy/ldm/hidream_o1/__pycache__/conditioning.cpython-312.pyc
ADDED
|
Binary file (10.9 kB). View file
|
|
|
ComfyUI/comfy/ldm/hidream_o1/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (16.1 kB). View file
|
|
|
ComfyUI/comfy/ldm/hidream_o1/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (9.95 kB). View file
|
|
|
ComfyUI/comfy/ldm/hidream_o1/attention.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HiDream-O1 two-pass attention: tokens [0, ar_len) are causal, [ar_len, T)
|
| 2 |
+
attend full K/V. Splitting Q at the boundary avoids the (B, 1, T, T) additive
|
| 3 |
+
mask the general-purpose path would build (~500 MB at T~16K) and lets the
|
| 4 |
+
gen half hit the user's preferred backend via optimized_attention.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
|
| 9 |
+
import comfy.ops
|
| 10 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def make_two_pass_attention(ar_len: int, transformer_options=None):
|
| 14 |
+
"""Build a two-pass attention callable. AR pass uses SDPA-causal directly, gen pass routes through optimized_attention.
|
| 15 |
+
The AR pass goes through SDPA directand bypasses wrappers, it is only ~1% of T at typical edit sizes.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def two_pass_attention(q, k, v, heads, enable_gqa=False, **kwargs):
|
| 19 |
+
B, H, T, D = q.shape
|
| 20 |
+
|
| 21 |
+
if T < k.shape[2]: # KV-cache hot path: Q is shorter than K/V (cached AR prefix is in K/V only), all fresh Q positions are in the gen region, single full-attention call
|
| 22 |
+
out = optimized_attention(q, k, v, heads, mask=None, skip_reshape=True, skip_output_reshape=True, transformer_options=transformer_options, enable_gqa=enable_gqa)
|
| 23 |
+
elif ar_len >= T:
|
| 24 |
+
out = comfy.ops.scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=0.0, is_causal=True, enable_gqa=enable_gqa)
|
| 25 |
+
elif ar_len <= 0:
|
| 26 |
+
out = optimized_attention(q, k, v, heads, mask=None, skip_reshape=True, skip_output_reshape=True, transformer_options=transformer_options, enable_gqa=enable_gqa)
|
| 27 |
+
else:
|
| 28 |
+
out_ar = comfy.ops.scaled_dot_product_attention(
|
| 29 |
+
q[:, :, :ar_len], k[:, :, :ar_len], v[:, :, :ar_len],
|
| 30 |
+
attn_mask=None, dropout_p=0.0, is_causal=True, enable_gqa=enable_gqa,
|
| 31 |
+
)
|
| 32 |
+
out_gen = optimized_attention(
|
| 33 |
+
q[:, :, ar_len:], k, v, heads,
|
| 34 |
+
mask=None, skip_reshape=True, skip_output_reshape=True,
|
| 35 |
+
transformer_options=transformer_options, enable_gqa=enable_gqa,
|
| 36 |
+
)
|
| 37 |
+
out = torch.cat([out_ar, out_gen], dim=2)
|
| 38 |
+
|
| 39 |
+
return out.transpose(1, 2).reshape(B, T, H * D)
|
| 40 |
+
|
| 41 |
+
return two_pass_attention
|
ComfyUI/comfy/ldm/hidream_o1/conditioning.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HiDream-O1 conditioning prep — ref-image dual path + extra_conds assembly.
|
| 2 |
+
|
| 3 |
+
Each ref image goes through two paths: a 32x32 patchified stream concatenated
|
| 4 |
+
to the noised target, and a Qwen3-VL ViT path producing tokens that scatter
|
| 5 |
+
into input_ids at <|image_pad|> positions.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from typing import List
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
|
| 12 |
+
import comfy.utils
|
| 13 |
+
from comfy.text_encoders.qwen_vl import process_qwen2vl_images
|
| 14 |
+
|
| 15 |
+
from .utils import (PATCH_SIZE, calculate_dimensions, cond_image_size, ref_max_size, resize_tensor)
|
| 16 |
+
|
| 17 |
+
# Qwen3-VL ViT preprocessing constants (preprocessor_config.json).
|
| 18 |
+
VIT_PATCH = 16
|
| 19 |
+
VIT_MERGE = 2
|
| 20 |
+
VIT_IMAGE_MEAN = [0.5, 0.5, 0.5]
|
| 21 |
+
VIT_IMAGE_STD = [0.5, 0.5, 0.5]
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def prepare_ref_images(
|
| 25 |
+
ref_images: List[torch.Tensor],
|
| 26 |
+
target_h: int,
|
| 27 |
+
target_w: int,
|
| 28 |
+
device: torch.device,
|
| 29 |
+
dtype: torch.dtype,
|
| 30 |
+
):
|
| 31 |
+
"""Build the dual-path tensors for K reference images at (target_h, target_w).
|
| 32 |
+
|
| 33 |
+
Returns None for K=0, else a dict with ref_patches, ref_pixel_values,
|
| 34 |
+
ref_image_grid_thw, per_ref_vit_tokens, per_ref_patch_grids.
|
| 35 |
+
"""
|
| 36 |
+
K = len(ref_images)
|
| 37 |
+
if K == 0:
|
| 38 |
+
return None
|
| 39 |
+
max_size = ref_max_size(max(target_h, target_w), K)
|
| 40 |
+
cis = cond_image_size(K)
|
| 41 |
+
|
| 42 |
+
refs_t = [img[0].clamp(0, 1).permute(2, 0, 1).unsqueeze(0).contiguous().float() for img in ref_images]
|
| 43 |
+
refs_t = [resize_tensor(t, max_size, PATCH_SIZE) for t in refs_t]
|
| 44 |
+
|
| 45 |
+
# 32-patch path.
|
| 46 |
+
ref_patches_per = []
|
| 47 |
+
per_ref_patch_grids = []
|
| 48 |
+
for t in refs_t:
|
| 49 |
+
t_norm = (t.squeeze(0) - 0.5) / 0.5 # (3, H, W) in [-1, 1]
|
| 50 |
+
h_p, w_p = t_norm.shape[-2] // PATCH_SIZE, t_norm.shape[-1] // PATCH_SIZE
|
| 51 |
+
per_ref_patch_grids.append((h_p, w_p))
|
| 52 |
+
patches = (
|
| 53 |
+
t_norm.reshape(3, h_p, PATCH_SIZE, w_p, PATCH_SIZE)
|
| 54 |
+
.permute(1, 3, 0, 2, 4)
|
| 55 |
+
.reshape(h_p * w_p, 3 * PATCH_SIZE * PATCH_SIZE)
|
| 56 |
+
)
|
| 57 |
+
ref_patches_per.append(patches)
|
| 58 |
+
ref_patches = torch.cat(ref_patches_per, dim=0).unsqueeze(0).to(device=device, dtype=dtype)
|
| 59 |
+
|
| 60 |
+
# ViT path.
|
| 61 |
+
refs_vlm_t = []
|
| 62 |
+
for t in refs_t:
|
| 63 |
+
_, _, h, w = t.shape
|
| 64 |
+
cond_w, cond_h = calculate_dimensions(cis, w / h)
|
| 65 |
+
cond_w = max(cond_w, VIT_PATCH * VIT_MERGE)
|
| 66 |
+
cond_h = max(cond_h, VIT_PATCH * VIT_MERGE)
|
| 67 |
+
refs_vlm_t.append(comfy.utils.common_upscale(t, cond_w, cond_h, "lanczos", "disabled"))
|
| 68 |
+
|
| 69 |
+
pv_list, grid_list, per_ref_vit_tokens = [], [], []
|
| 70 |
+
for t_v in refs_vlm_t:
|
| 71 |
+
pv, grid_thw = process_qwen2vl_images(
|
| 72 |
+
t_v.permute(0, 2, 3, 1),
|
| 73 |
+
min_pixels=0, max_pixels=10**12,
|
| 74 |
+
patch_size=VIT_PATCH, merge_size=VIT_MERGE,
|
| 75 |
+
image_mean=VIT_IMAGE_MEAN, image_std=VIT_IMAGE_STD,
|
| 76 |
+
)
|
| 77 |
+
grid_thw = grid_thw[0]
|
| 78 |
+
pv_list.append(pv.to(device=device, dtype=dtype))
|
| 79 |
+
grid_list.append(grid_thw.to(device=device))
|
| 80 |
+
# Post-merge token count = number of <|image_pad|> tokens this image expands to in input_ids.
|
| 81 |
+
gh, gw = int(grid_thw[1].item()), int(grid_thw[2].item())
|
| 82 |
+
per_ref_vit_tokens.append((gh // VIT_MERGE) * (gw // VIT_MERGE))
|
| 83 |
+
|
| 84 |
+
return {
|
| 85 |
+
"ref_patches": ref_patches,
|
| 86 |
+
"ref_pixel_values": torch.cat(pv_list, dim=0),
|
| 87 |
+
"ref_image_grid_thw": torch.stack(grid_list, dim=0),
|
| 88 |
+
"per_ref_vit_tokens": per_ref_vit_tokens,
|
| 89 |
+
"per_ref_patch_grids": per_ref_patch_grids,
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def build_ref_input_ids(
|
| 94 |
+
text_input_ids: torch.Tensor,
|
| 95 |
+
per_ref_vit_tokens: List[int],
|
| 96 |
+
image_token_id: int,
|
| 97 |
+
vision_start_id: int,
|
| 98 |
+
vision_end_id: int,
|
| 99 |
+
):
|
| 100 |
+
"""Splice [vision_start, image_pad*N, vision_end] blocks into input_ids
|
| 101 |
+
after the [im_start, user, \\n] prefix (matches original chat template).
|
| 102 |
+
"""
|
| 103 |
+
ids = text_input_ids[0].tolist()
|
| 104 |
+
inserted = []
|
| 105 |
+
for n_pad in per_ref_vit_tokens:
|
| 106 |
+
inserted.extend([vision_start_id] + [image_token_id] * n_pad + [vision_end_id])
|
| 107 |
+
new_ids = ids[:3] + inserted + ids[3:] # 3 = len([im_start, user, \n])
|
| 108 |
+
return torch.tensor([new_ids], dtype=text_input_ids.dtype, device=text_input_ids.device)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def build_extra_conds(
|
| 112 |
+
text_input_ids: torch.Tensor,
|
| 113 |
+
noise: torch.Tensor,
|
| 114 |
+
ref_images: List[torch.Tensor] = None,
|
| 115 |
+
target_patch_size: int = 32,
|
| 116 |
+
):
|
| 117 |
+
"""Assemble all conditioning tensors for HiDreamO1Transformer.forward:
|
| 118 |
+
input_ids (with ref-vision tokens spliced in for the edit/IP path),
|
| 119 |
+
position_ids (MRoPE), token_types, vinput_mask, plus the ref
|
| 120 |
+
dual-path tensors when refs are provided.
|
| 121 |
+
"""
|
| 122 |
+
from .utils import get_rope_index_fix_point
|
| 123 |
+
from comfy.text_encoders.hidream_o1 import (
|
| 124 |
+
IMAGE_TOKEN_ID, VISION_START_ID, VISION_END_ID,
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
if text_input_ids.dim() == 1:
|
| 128 |
+
text_input_ids = text_input_ids.unsqueeze(0)
|
| 129 |
+
text_input_ids = text_input_ids.long().to(noise.device)
|
| 130 |
+
B = noise.shape[0]
|
| 131 |
+
if text_input_ids.shape[0] == 1 and B > 1:
|
| 132 |
+
text_input_ids = text_input_ids.expand(B, -1)
|
| 133 |
+
|
| 134 |
+
H, W = noise.shape[-2], noise.shape[-1]
|
| 135 |
+
h_p, w_p = H // target_patch_size, W // target_patch_size
|
| 136 |
+
image_len = h_p * w_p
|
| 137 |
+
image_grid_thw_tgt = torch.tensor(
|
| 138 |
+
[[1, h_p, w_p]], dtype=torch.long, device=text_input_ids.device,
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
out = {}
|
| 142 |
+
if ref_images:
|
| 143 |
+
ref = prepare_ref_images(ref_images, H, W, device=noise.device, dtype=noise.dtype)
|
| 144 |
+
text_input_ids = build_ref_input_ids(
|
| 145 |
+
text_input_ids, ref["per_ref_vit_tokens"],
|
| 146 |
+
IMAGE_TOKEN_ID, VISION_START_ID, VISION_END_ID,
|
| 147 |
+
)
|
| 148 |
+
new_txt_len = text_input_ids.shape[1]
|
| 149 |
+
|
| 150 |
+
# Each ref's patchified stream gets a [vision_start, image_pad*N-1]
|
| 151 |
+
# block in the position-id stream after the noised target.
|
| 152 |
+
ref_grid_lengths = [hp * wp for (hp, wp) in ref["per_ref_patch_grids"]]
|
| 153 |
+
tgt_vision = torch.full((1, image_len), IMAGE_TOKEN_ID,
|
| 154 |
+
dtype=text_input_ids.dtype, device=text_input_ids.device)
|
| 155 |
+
tgt_vision[:, 0] = VISION_START_ID
|
| 156 |
+
ref_vision_blocks = []
|
| 157 |
+
for rl in ref_grid_lengths:
|
| 158 |
+
blk = torch.full((1, rl), IMAGE_TOKEN_ID,
|
| 159 |
+
dtype=text_input_ids.dtype, device=text_input_ids.device)
|
| 160 |
+
blk[:, 0] = VISION_START_ID
|
| 161 |
+
ref_vision_blocks.append(blk)
|
| 162 |
+
ref_vision_cat = torch.cat([tgt_vision] + ref_vision_blocks, dim=1)
|
| 163 |
+
input_ids_pad = torch.cat([text_input_ids, ref_vision_cat], dim=-1)
|
| 164 |
+
total_ref_patches_len = sum(ref_grid_lengths)
|
| 165 |
+
total_len = new_txt_len + image_len + total_ref_patches_len
|
| 166 |
+
|
| 167 |
+
# K (ViT, post-merge) + 1 (target) + K (ref-patches) image grids.
|
| 168 |
+
K = len(ref_images)
|
| 169 |
+
igthw_cond = ref["ref_image_grid_thw"].clone()
|
| 170 |
+
igthw_cond[:, 1] //= 2
|
| 171 |
+
igthw_cond[:, 2] //= 2
|
| 172 |
+
image_grid_thw_ref = torch.tensor(
|
| 173 |
+
[[1, hp, wp] for (hp, wp) in ref["per_ref_patch_grids"]],
|
| 174 |
+
dtype=torch.long, device=text_input_ids.device,
|
| 175 |
+
)
|
| 176 |
+
igthw_all = torch.cat([
|
| 177 |
+
igthw_cond.to(text_input_ids.device),
|
| 178 |
+
image_grid_thw_tgt,
|
| 179 |
+
image_grid_thw_ref,
|
| 180 |
+
], dim=0)
|
| 181 |
+
position_ids, _ = get_rope_index_fix_point(
|
| 182 |
+
spatial_merge_size=1,
|
| 183 |
+
image_token_id=IMAGE_TOKEN_ID,
|
| 184 |
+
vision_start_token_id=VISION_START_ID,
|
| 185 |
+
input_ids=input_ids_pad, image_grid_thw=igthw_all,
|
| 186 |
+
attention_mask=None,
|
| 187 |
+
skip_vision_start_token=[0] * K + [1] + [1] * K,
|
| 188 |
+
fix_point=4096,
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
# tms + target_image + ref_patches are all gen.
|
| 192 |
+
tms_pos = new_txt_len - 1
|
| 193 |
+
ar_len = tms_pos
|
| 194 |
+
token_types = torch.zeros(B, total_len, dtype=torch.long, device=noise.device)
|
| 195 |
+
token_types[:, tms_pos:] = 1
|
| 196 |
+
vinput_mask = torch.zeros(B, total_len, dtype=torch.bool, device=noise.device)
|
| 197 |
+
vinput_mask[:, new_txt_len:] = True
|
| 198 |
+
|
| 199 |
+
# Leading batch dim sidesteps CONDRegular.process_cond's repeat_to_batch_size truncation
|
| 200 |
+
out["ref_pixel_values"] = ref["ref_pixel_values"].unsqueeze(0)
|
| 201 |
+
out["ref_image_grid_thw"] = ref["ref_image_grid_thw"].unsqueeze(0)
|
| 202 |
+
out["ref_patches"] = ref["ref_patches"]
|
| 203 |
+
else:
|
| 204 |
+
# T2I: text + noised target only, vision_start replaces the first image token
|
| 205 |
+
txt_len = text_input_ids.shape[1]
|
| 206 |
+
total_len = txt_len + image_len
|
| 207 |
+
vision_tokens = torch.full((B, image_len), IMAGE_TOKEN_ID,
|
| 208 |
+
dtype=text_input_ids.dtype, device=text_input_ids.device)
|
| 209 |
+
vision_tokens[:, 0] = VISION_START_ID
|
| 210 |
+
input_ids_pad = torch.cat([text_input_ids, vision_tokens], dim=-1)
|
| 211 |
+
position_ids, _ = get_rope_index_fix_point(
|
| 212 |
+
spatial_merge_size=1,
|
| 213 |
+
image_token_id=IMAGE_TOKEN_ID,
|
| 214 |
+
vision_start_token_id=VISION_START_ID,
|
| 215 |
+
input_ids=input_ids_pad, image_grid_thw=image_grid_thw_tgt,
|
| 216 |
+
attention_mask=None,
|
| 217 |
+
skip_vision_start_token=[1],
|
| 218 |
+
)
|
| 219 |
+
ar_len = txt_len - 1
|
| 220 |
+
token_types = torch.zeros(B, total_len, dtype=torch.long, device=noise.device)
|
| 221 |
+
token_types[:, ar_len:] = 1
|
| 222 |
+
vinput_mask = torch.zeros(B, total_len, dtype=torch.bool, device=noise.device)
|
| 223 |
+
vinput_mask[:, txt_len:] = True
|
| 224 |
+
|
| 225 |
+
out["input_ids"] = text_input_ids
|
| 226 |
+
out["position_ids"] = position_ids[:, 0].unsqueeze(0) # Collapse position_ids batch and add a leading dim so CONDRegular's batch-resize doesn't truncate the 3-axis MRoPE dim
|
| 227 |
+
out["token_types"] = token_types
|
| 228 |
+
out["vinput_mask"] = vinput_mask
|
| 229 |
+
out["ar_len"] = ar_len
|
| 230 |
+
return out
|
ComfyUI/comfy/ldm/hidream_o1/model.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HiDream-O1-Image transformer.
|
| 2 |
+
|
| 3 |
+
Pixel-space DiT built on Qwen3-VL: the vision tower (Qwen35VisionModel)
|
| 4 |
+
encodes ref images, the Qwen3-VL-8B decoder (Llama2_ with interleaved MRoPE)
|
| 5 |
+
processes a unified text+image sequence, and 32x32 patch embed/unembed
|
| 6 |
+
shims map raw RGB in and out of LLM hidden space. The Qwen3-VL deepstack
|
| 7 |
+
mergers go unused — their weights are dropped at load.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from dataclasses import dataclass, field
|
| 11 |
+
from typing import List, Optional
|
| 12 |
+
|
| 13 |
+
import einops
|
| 14 |
+
import torch
|
| 15 |
+
import torch.nn as nn
|
| 16 |
+
|
| 17 |
+
import comfy.patcher_extension
|
| 18 |
+
from comfy.ldm.modules.diffusionmodules.mmdit import TimestepEmbedder
|
| 19 |
+
from comfy.text_encoders.llama import Llama2_
|
| 20 |
+
from comfy.text_encoders.qwen35 import Qwen35VisionModel
|
| 21 |
+
|
| 22 |
+
from .attention import make_two_pass_attention
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
IMAGE_TOKEN_ID = 151655 # Qwen3-VL <|image_pad|>
|
| 26 |
+
TMS_TOKEN_ID = 151673 # HiDream-O1 <|tms_token|>
|
| 27 |
+
PATCH_SIZE = 32
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
@dataclass
|
| 31 |
+
class HiDreamO1TextConfig:
|
| 32 |
+
"""Qwen3-VL-8B text-decoder dims (matches public Qwen3-VL-8B-Instruct)."""
|
| 33 |
+
vocab_size: int = 151936
|
| 34 |
+
hidden_size: int = 4096
|
| 35 |
+
intermediate_size: int = 12288
|
| 36 |
+
num_hidden_layers: int = 36
|
| 37 |
+
num_attention_heads: int = 32
|
| 38 |
+
num_key_value_heads: int = 8
|
| 39 |
+
head_dim: int = 128
|
| 40 |
+
max_position_embeddings: int = 128000
|
| 41 |
+
rms_norm_eps: float = 1e-6
|
| 42 |
+
rope_theta: float = 5000000.0
|
| 43 |
+
rope_scale: Optional[float] = None
|
| 44 |
+
rope_dims: List[int] = field(default_factory=lambda: [24, 20, 20])
|
| 45 |
+
interleaved_mrope: bool = True
|
| 46 |
+
transformer_type: str = "llama"
|
| 47 |
+
rms_norm_add: bool = False
|
| 48 |
+
mlp_activation: str = "silu"
|
| 49 |
+
qkv_bias: bool = False
|
| 50 |
+
q_norm: str = "gemma3"
|
| 51 |
+
k_norm: str = "gemma3"
|
| 52 |
+
final_norm: bool = True
|
| 53 |
+
lm_head: bool = False
|
| 54 |
+
stop_tokens: List[int] = field(default_factory=lambda: [151643, 151645])
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
QWEN3VL_VISION_DEFAULTS = dict(
|
| 58 |
+
hidden_size=1152,
|
| 59 |
+
num_heads=16,
|
| 60 |
+
intermediate_size=4304,
|
| 61 |
+
depth=27,
|
| 62 |
+
patch_size=16,
|
| 63 |
+
temporal_patch_size=2,
|
| 64 |
+
in_channels=3,
|
| 65 |
+
spatial_merge_size=2,
|
| 66 |
+
num_position_embeddings=2304,
|
| 67 |
+
deepstack_visual_indexes=(8, 16, 24),
|
| 68 |
+
out_hidden_size=4096, # final merger projects directly into LLM hidden
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class BottleneckPatchEmbed(nn.Module):
|
| 73 |
+
# 3072 -> 1024 -> 4096 (raw 32x32 RGB patch -> bottleneck -> LLM hidden).
|
| 74 |
+
def __init__(self, patch_size=32, in_chans=3, pca_dim=1024, embed_dim=4096, bias=True, device=None, dtype=None, ops=None):
|
| 75 |
+
super().__init__()
|
| 76 |
+
self.proj1 = ops.Linear(patch_size * patch_size * in_chans, pca_dim, bias=False, device=device, dtype=dtype)
|
| 77 |
+
self.proj2 = ops.Linear(pca_dim, embed_dim, bias=bias, device=device, dtype=dtype)
|
| 78 |
+
|
| 79 |
+
def forward(self, x):
|
| 80 |
+
return self.proj2(self.proj1(x))
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class FinalLayer(nn.Module):
|
| 84 |
+
# 4096 -> 3072 (LLM hidden -> flat pixel patch).
|
| 85 |
+
def __init__(self, hidden_size, patch_size=32, out_channels=3, device=None, dtype=None, ops=None):
|
| 86 |
+
super().__init__()
|
| 87 |
+
self.linear = ops.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True, device=device, dtype=dtype)
|
| 88 |
+
|
| 89 |
+
def forward(self, x):
|
| 90 |
+
return self.linear(x)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class HiDreamO1Transformer(nn.Module):
|
| 94 |
+
"""HiDream-O1 unified pixel-level transformer."""
|
| 95 |
+
|
| 96 |
+
def __init__(self, image_model=None, dtype=None, device=None, operations=None,
|
| 97 |
+
text_config_overrides=None, vision_config_overrides=None, **kwargs):
|
| 98 |
+
super().__init__()
|
| 99 |
+
self.dtype = dtype
|
| 100 |
+
|
| 101 |
+
text_cfg = HiDreamO1TextConfig(**(text_config_overrides or {}))
|
| 102 |
+
vision_cfg = dict(QWEN3VL_VISION_DEFAULTS)
|
| 103 |
+
if vision_config_overrides:
|
| 104 |
+
vision_cfg.update(vision_config_overrides)
|
| 105 |
+
vision_cfg["out_hidden_size"] = text_cfg.hidden_size
|
| 106 |
+
|
| 107 |
+
self.text_config = text_cfg
|
| 108 |
+
self.vision_config = vision_cfg
|
| 109 |
+
self.hidden_size = text_cfg.hidden_size
|
| 110 |
+
self.patch_size = PATCH_SIZE
|
| 111 |
+
self.in_channels = 3
|
| 112 |
+
self.tms_token_id = TMS_TOKEN_ID
|
| 113 |
+
|
| 114 |
+
self.visual = Qwen35VisionModel(vision_cfg, device=device, dtype=dtype, ops=operations)
|
| 115 |
+
self.language_model = Llama2_(text_cfg, device=device, dtype=dtype, ops=operations)
|
| 116 |
+
self.t_embedder1 = TimestepEmbedder(
|
| 117 |
+
text_cfg.hidden_size, device=device, dtype=dtype, operations=operations,
|
| 118 |
+
)
|
| 119 |
+
self.x_embedder = BottleneckPatchEmbed(
|
| 120 |
+
patch_size=self.patch_size, in_chans=self.in_channels,
|
| 121 |
+
pca_dim=text_cfg.hidden_size // 4, embed_dim=text_cfg.hidden_size,
|
| 122 |
+
bias=True, device=device, dtype=dtype, ops=operations,
|
| 123 |
+
)
|
| 124 |
+
self.final_layer2 = FinalLayer(
|
| 125 |
+
text_cfg.hidden_size, patch_size=self.patch_size,
|
| 126 |
+
out_channels=self.in_channels, device=device, dtype=dtype, ops=operations,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
self._visual_cache = None
|
| 130 |
+
self._kv_cache_entries = []
|
| 131 |
+
|
| 132 |
+
def clear_kv_cache(self):
|
| 133 |
+
self._kv_cache_entries = []
|
| 134 |
+
self._visual_cache = None
|
| 135 |
+
|
| 136 |
+
def forward(self, x, timesteps, context=None, transformer_options={}, **kwargs):
|
| 137 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 138 |
+
self._forward,
|
| 139 |
+
self,
|
| 140 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
|
| 141 |
+
).execute(x, timesteps, context, transformer_options, **kwargs)
|
| 142 |
+
|
| 143 |
+
def _forward(self, x, timesteps, context=None, transformer_options={}, input_ids=None, attention_mask=None, position_ids=None,
|
| 144 |
+
vinput_mask=None, ar_len=None, ref_pixel_values=None, ref_image_grid_thw=None, ref_patches=None, **kwargs):
|
| 145 |
+
"""Returns flow-match velocity (x - x_pred) / sigma"""
|
| 146 |
+
|
| 147 |
+
if input_ids is None or position_ids is None:
|
| 148 |
+
raise ValueError("HiDreamO1Transformer requires input_ids and position_ids in conditioning")
|
| 149 |
+
|
| 150 |
+
B, _, H, W = x.shape
|
| 151 |
+
h_p, w_p = H // self.patch_size, W // self.patch_size
|
| 152 |
+
tgt_image_len = h_p * w_p
|
| 153 |
+
|
| 154 |
+
z = einops.rearrange(
|
| 155 |
+
x, 'B C (H p1) (W p2) -> B (H W) (C p1 p2)',
|
| 156 |
+
p1=self.patch_size, p2=self.patch_size,
|
| 157 |
+
)
|
| 158 |
+
vinputs = torch.cat([z, ref_patches.to(z.dtype)], dim=1) if ref_patches is not None else z
|
| 159 |
+
|
| 160 |
+
inputs_embeds = self.language_model.embed_tokens(input_ids).to(x.dtype)
|
| 161 |
+
|
| 162 |
+
if ref_pixel_values is not None and ref_image_grid_thw is not None:
|
| 163 |
+
# ViT output is constant across sampling steps within a generation
|
| 164 |
+
# identity-key by the input tensor so refs don't recompute every step.
|
| 165 |
+
cached = self._visual_cache
|
| 166 |
+
if cached is not None and cached[0] is ref_pixel_values:
|
| 167 |
+
image_embeds = cached[1]
|
| 168 |
+
else:
|
| 169 |
+
ref_pv = ref_pixel_values.to(inputs_embeds.device)
|
| 170 |
+
ref_grid = ref_image_grid_thw.to(inputs_embeds.device).long()
|
| 171 |
+
# extra_conds wraps with a leading batch dim; refs are model-level so [0] always recovers them.
|
| 172 |
+
if ref_pv.dim() == 3:
|
| 173 |
+
ref_pv = ref_pv[0]
|
| 174 |
+
if ref_grid.dim() == 3:
|
| 175 |
+
ref_grid = ref_grid[0]
|
| 176 |
+
image_embeds = self.visual(ref_pv, ref_grid).to(inputs_embeds.dtype)
|
| 177 |
+
self._visual_cache = (ref_pixel_values, image_embeds)
|
| 178 |
+
# image_pad positions identical across batch (input_ids shared cond/uncond).
|
| 179 |
+
image_idx = (input_ids[0] == IMAGE_TOKEN_ID).nonzero(as_tuple=True)[0]
|
| 180 |
+
if image_idx.shape[0] != image_embeds.shape[0]:
|
| 181 |
+
raise ValueError(
|
| 182 |
+
f"Image-token count {image_idx.shape[0]} != ViT output count "
|
| 183 |
+
f"{image_embeds.shape[0]}; check tokenizer/processor alignment."
|
| 184 |
+
)
|
| 185 |
+
inputs_embeds[:, image_idx] = image_embeds.unsqueeze(0).expand(B, -1, -1)
|
| 186 |
+
|
| 187 |
+
sigma = timesteps.float() / 1000.0
|
| 188 |
+
t_pixeldit = 1.0 - sigma
|
| 189 |
+
t_emb = self.t_embedder1(t_pixeldit * 1000, inputs_embeds.dtype)
|
| 190 |
+
tms_mask_3d = (input_ids == self.tms_token_id).unsqueeze(-1).expand_as(inputs_embeds)
|
| 191 |
+
inputs_embeds = torch.where(tms_mask_3d, t_emb.unsqueeze(1).expand_as(inputs_embeds), inputs_embeds)
|
| 192 |
+
|
| 193 |
+
vinputs_embedded = self.x_embedder(vinputs.to(inputs_embeds.dtype))
|
| 194 |
+
inputs_embeds = torch.cat([inputs_embeds, vinputs_embedded], dim=1)
|
| 195 |
+
|
| 196 |
+
# extra_conds stores position_ids as (1, 3, T); process_cond repeats dim 0 to B. Take row 0.
|
| 197 |
+
freqs_cis = self.language_model.compute_freqs_cis(position_ids[0].to(x.device), x.device)
|
| 198 |
+
freqs_cis = tuple(t.to(x.dtype) for t in freqs_cis)
|
| 199 |
+
|
| 200 |
+
two_pass_attn = make_two_pass_attention(ar_len, transformer_options=transformer_options)
|
| 201 |
+
patches_replace = transformer_options.get("patches_replace", {})
|
| 202 |
+
blocks_replace = patches_replace.get("dit", {})
|
| 203 |
+
transformer_options["total_blocks"] = len(self.language_model.layers)
|
| 204 |
+
transformer_options["block_type"] = "double"
|
| 205 |
+
|
| 206 |
+
# Cache prefix K/V across steps. Key includes input_ids (prompt), ref_id
|
| 207 |
+
# (refs scatter into inputs_embeds), and position_ids (RoPE baked into cached K).
|
| 208 |
+
can_cache = not blocks_replace and ar_len > 0
|
| 209 |
+
cache_len = ar_len if can_cache else 0
|
| 210 |
+
ref_id = id(ref_pixel_values) if ref_pixel_values is not None else None
|
| 211 |
+
pos_ids_key = position_ids[..., :cache_len] if can_cache else position_ids
|
| 212 |
+
cache_entries = self._kv_cache_entries
|
| 213 |
+
# Drop stale entries from a previous device (model was unloaded and reloaded).
|
| 214 |
+
if cache_entries and cache_entries[0]["input_ids"].device != input_ids.device:
|
| 215 |
+
cache_entries = []
|
| 216 |
+
self._kv_cache_entries = []
|
| 217 |
+
kv_cache = None
|
| 218 |
+
if can_cache:
|
| 219 |
+
for entry in cache_entries:
|
| 220 |
+
ck = entry["input_ids"]
|
| 221 |
+
ep = entry["position_ids"]
|
| 222 |
+
if (entry["cache_len"] == cache_len
|
| 223 |
+
and ck.shape == input_ids.shape and torch.equal(ck, input_ids)
|
| 224 |
+
and entry["ref_id"] == ref_id
|
| 225 |
+
and ep.shape == pos_ids_key.shape and torch.equal(ep, pos_ids_key)):
|
| 226 |
+
kv_cache = entry
|
| 227 |
+
break
|
| 228 |
+
|
| 229 |
+
if kv_cache is not None:
|
| 230 |
+
# Hot path: project Q/K/V only for fresh positions; past_key_value prepends cached AR K/V.
|
| 231 |
+
hidden_states = inputs_embeds[:, cache_len:]
|
| 232 |
+
sliced_freqs = tuple(t[..., cache_len:, :] for t in freqs_cis)
|
| 233 |
+
for i, layer in enumerate(self.language_model.layers):
|
| 234 |
+
transformer_options["block_index"] = i
|
| 235 |
+
K_i, V_i = kv_cache["kv"][i]
|
| 236 |
+
hidden_states, _ = layer(
|
| 237 |
+
x=hidden_states, attention_mask=None, freqs_cis=sliced_freqs, optimized_attention=two_pass_attn,
|
| 238 |
+
past_key_value=(K_i, V_i, cache_len),
|
| 239 |
+
)
|
| 240 |
+
else:
|
| 241 |
+
# Cold path: run full sequence; if cacheable, snapshot K/V at AR positions.
|
| 242 |
+
snapshots = [] if can_cache else None
|
| 243 |
+
past_kv_cold = () if can_cache else None
|
| 244 |
+
hidden_states = inputs_embeds
|
| 245 |
+
for i, layer in enumerate(self.language_model.layers):
|
| 246 |
+
transformer_options["block_index"] = i
|
| 247 |
+
if ("double_block", i) in blocks_replace:
|
| 248 |
+
def block_wrap(args, _layer=layer):
|
| 249 |
+
out = {}
|
| 250 |
+
out["x"], _ = _layer(
|
| 251 |
+
x=args["x"], attention_mask=args.get("attention_mask"),
|
| 252 |
+
freqs_cis=args["freqs_cis"], optimized_attention=args["optimized_attention"],
|
| 253 |
+
past_key_value=None,
|
| 254 |
+
)
|
| 255 |
+
return out
|
| 256 |
+
out = blocks_replace[("double_block", i)](
|
| 257 |
+
{"x": hidden_states, "attention_mask": None,
|
| 258 |
+
"freqs_cis": freqs_cis, "optimized_attention": two_pass_attn,
|
| 259 |
+
"transformer_options": transformer_options},
|
| 260 |
+
{"original_block": block_wrap},
|
| 261 |
+
)
|
| 262 |
+
hidden_states = out["x"]
|
| 263 |
+
else:
|
| 264 |
+
hidden_states, present_kv = layer(
|
| 265 |
+
x=hidden_states, attention_mask=None,
|
| 266 |
+
freqs_cis=freqs_cis, optimized_attention=two_pass_attn,
|
| 267 |
+
past_key_value=past_kv_cold,
|
| 268 |
+
)
|
| 269 |
+
if snapshots is not None:
|
| 270 |
+
K, V, _ = present_kv
|
| 271 |
+
snapshots.append((K[:, :, :cache_len].contiguous(),
|
| 272 |
+
V[:, :, :cache_len].contiguous()))
|
| 273 |
+
if snapshots is not None:
|
| 274 |
+
# Cap at 2 entries (cond + uncond). Multi-cond workflows LRU-evict.
|
| 275 |
+
new_entry = {
|
| 276 |
+
"input_ids": input_ids.clone(),
|
| 277 |
+
"cache_len": cache_len,
|
| 278 |
+
"kv": snapshots,
|
| 279 |
+
"ref_id": ref_id,
|
| 280 |
+
"position_ids": pos_ids_key.clone(),
|
| 281 |
+
}
|
| 282 |
+
self._kv_cache_entries = (cache_entries + [new_entry])[-2:]
|
| 283 |
+
|
| 284 |
+
if self.language_model.norm is not None:
|
| 285 |
+
hidden_states = self.language_model.norm(hidden_states)
|
| 286 |
+
|
| 287 |
+
# Slice target-image positions before the final projection so the Linear only runs on tgt_image_len tokens.
|
| 288 |
+
# In the hot path hidden_states starts at original position cache_len, so masks/indices shift by cache_len.
|
| 289 |
+
sliced_offset = cache_len if kv_cache is not None else 0
|
| 290 |
+
if vinput_mask is not None:
|
| 291 |
+
vmask = vinput_mask.to(x.device).bool()
|
| 292 |
+
if sliced_offset > 0:
|
| 293 |
+
vmask = vmask[:, sliced_offset:]
|
| 294 |
+
target_hidden = hidden_states[vmask].view(B, -1, hidden_states.shape[-1])[:, :tgt_image_len]
|
| 295 |
+
else:
|
| 296 |
+
txt_seq_len = input_ids.shape[1]
|
| 297 |
+
start = txt_seq_len - sliced_offset
|
| 298 |
+
target_hidden = hidden_states[:, start:start + tgt_image_len]
|
| 299 |
+
x_pred_tgt = self.final_layer2(target_hidden)
|
| 300 |
+
|
| 301 |
+
# fp32 final subtraction, bf16 here noticeably degrades samples.
|
| 302 |
+
x_pred_img = einops.rearrange(
|
| 303 |
+
x_pred_tgt, 'B (H W) (C p1 p2) -> B C (H p1) (W p2)',
|
| 304 |
+
H=h_p, W=w_p, p1=self.patch_size, p2=self.patch_size,
|
| 305 |
+
)
|
| 306 |
+
return (x.float() - x_pred_img.float()) / sigma.view(B, 1, 1, 1).clamp_min(1e-3)
|
ComfyUI/comfy/ldm/hidream_o1/utils.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HiDream-O1 input-prep helpers: image/resolution math and unified-sequence
|
| 2 |
+
RoPE position-id assembly. The fix_point offset in get_rope_index_fix_point
|
| 3 |
+
lets the target image and patchified ref images share spatial RoPE positions
|
| 4 |
+
despite living at different sequence indices — same 2D image plane.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import math
|
| 8 |
+
from typing import Optional
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
PATCH_SIZE = 32
|
| 14 |
+
CONDITION_IMAGE_SIZE = 384 # ViT-side base size for ref images
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def resize_tensor(img_t, image_size, patch_size=16):
|
| 18 |
+
"""img_t: (1, 3, H, W) float [0, 1]. Fit to image_size**2 area, patch-aligned, center-cropped."""
|
| 19 |
+
|
| 20 |
+
while min(img_t.shape[-2], img_t.shape[-1]) >= 2 * image_size: # Pre-halves with 2x2 box averaging while the image is still very large
|
| 21 |
+
img_t = torch.nn.functional.avg_pool2d(img_t, kernel_size=2, stride=2)
|
| 22 |
+
|
| 23 |
+
_, _, height, width = img_t.shape
|
| 24 |
+
m = patch_size
|
| 25 |
+
s_max = image_size * image_size
|
| 26 |
+
scale = math.sqrt(s_max / (width * height))
|
| 27 |
+
|
| 28 |
+
candidates = [
|
| 29 |
+
(round(width * scale) // m * m, round(height * scale) // m * m),
|
| 30 |
+
(round(width * scale) // m * m, math.floor(height * scale) // m * m),
|
| 31 |
+
(math.floor(width * scale) // m * m, round(height * scale) // m * m),
|
| 32 |
+
(math.floor(width * scale) // m * m, math.floor(height * scale) // m * m),
|
| 33 |
+
]
|
| 34 |
+
candidates = sorted(candidates, key=lambda x: x[0] * x[1], reverse=True)
|
| 35 |
+
new_size = candidates[-1]
|
| 36 |
+
for c in candidates:
|
| 37 |
+
if c[0] * c[1] <= s_max:
|
| 38 |
+
new_size = c
|
| 39 |
+
break
|
| 40 |
+
|
| 41 |
+
new_w, new_h = new_size
|
| 42 |
+
s1 = width / new_w
|
| 43 |
+
s2 = height / new_h
|
| 44 |
+
if s1 < s2:
|
| 45 |
+
resize_w, resize_h = new_w, round(height / s1)
|
| 46 |
+
else:
|
| 47 |
+
resize_w, resize_h = round(width / s2), new_h
|
| 48 |
+
img_t = torch.nn.functional.interpolate(img_t, size=(resize_h, resize_w), mode="bicubic")
|
| 49 |
+
top = (resize_h - new_h) // 2
|
| 50 |
+
left = (resize_w - new_w) // 2
|
| 51 |
+
return img_t[..., top:top + new_h, left:left + new_w]
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def calculate_dimensions(max_size, ratio):
|
| 55 |
+
"""(W, H) for an aspect ratio fitting in max_size**2 area, 32-aligned."""
|
| 56 |
+
width = math.sqrt(max_size * max_size * ratio)
|
| 57 |
+
height = width / ratio
|
| 58 |
+
width = int(width / 32) * 32
|
| 59 |
+
height = int(height / 32) * 32
|
| 60 |
+
return width, height
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def ref_max_size(target_max_dim, k):
|
| 64 |
+
"""K-dependent ref-image max dim before patchifying."""
|
| 65 |
+
if k == 1:
|
| 66 |
+
return target_max_dim
|
| 67 |
+
if k == 2:
|
| 68 |
+
return target_max_dim * 48 // 64
|
| 69 |
+
if k <= 4:
|
| 70 |
+
return target_max_dim // 2
|
| 71 |
+
if k <= 8:
|
| 72 |
+
return target_max_dim * 24 // 64
|
| 73 |
+
return target_max_dim // 4
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def cond_image_size(k):
|
| 77 |
+
"""K-dependent ViT-side image size."""
|
| 78 |
+
if k <= 4:
|
| 79 |
+
return CONDITION_IMAGE_SIZE
|
| 80 |
+
if k <= 8:
|
| 81 |
+
return CONDITION_IMAGE_SIZE * 48 // 64
|
| 82 |
+
return CONDITION_IMAGE_SIZE // 2
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def get_rope_index_fix_point(
|
| 86 |
+
spatial_merge_size: int,
|
| 87 |
+
image_token_id: int,
|
| 88 |
+
vision_start_token_id: int,
|
| 89 |
+
input_ids: Optional[torch.LongTensor] = None,
|
| 90 |
+
image_grid_thw: Optional[torch.LongTensor] = None,
|
| 91 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 92 |
+
skip_vision_start_token=None,
|
| 93 |
+
fix_point: int = 4096,
|
| 94 |
+
):
|
| 95 |
+
mrope_position_deltas = []
|
| 96 |
+
if input_ids is not None and image_grid_thw is not None:
|
| 97 |
+
total_input_ids = input_ids
|
| 98 |
+
if attention_mask is None:
|
| 99 |
+
attention_mask = torch.ones_like(total_input_ids)
|
| 100 |
+
position_ids = torch.ones(
|
| 101 |
+
3, input_ids.shape[0], input_ids.shape[1],
|
| 102 |
+
dtype=input_ids.dtype, device=input_ids.device,
|
| 103 |
+
)
|
| 104 |
+
attention_mask = attention_mask.to(total_input_ids.device)
|
| 105 |
+
for i, input_ids_b in enumerate(total_input_ids):
|
| 106 |
+
fp = fix_point
|
| 107 |
+
image_index = 0
|
| 108 |
+
input_ids_b = input_ids_b[attention_mask[i] == 1]
|
| 109 |
+
vision_start_indices = torch.argwhere(input_ids_b == vision_start_token_id).squeeze(1)
|
| 110 |
+
vision_tokens = input_ids_b[vision_start_indices + 1]
|
| 111 |
+
image_nums = (vision_tokens == image_token_id).sum()
|
| 112 |
+
input_tokens = input_ids_b.tolist()
|
| 113 |
+
llm_pos_ids_list = []
|
| 114 |
+
st = 0
|
| 115 |
+
remain_images = image_nums
|
| 116 |
+
for _ in range(image_nums):
|
| 117 |
+
if image_token_id in input_tokens and remain_images > 0:
|
| 118 |
+
ed = input_tokens.index(image_token_id, st)
|
| 119 |
+
else:
|
| 120 |
+
ed = len(input_tokens) + 1
|
| 121 |
+
t = image_grid_thw[image_index][0]
|
| 122 |
+
h = image_grid_thw[image_index][1]
|
| 123 |
+
w = image_grid_thw[image_index][2]
|
| 124 |
+
image_index += 1
|
| 125 |
+
remain_images -= 1
|
| 126 |
+
llm_grid_t = t.item()
|
| 127 |
+
llm_grid_h = h.item() // spatial_merge_size
|
| 128 |
+
llm_grid_w = w.item() // spatial_merge_size
|
| 129 |
+
text_len = ed - st
|
| 130 |
+
text_len -= skip_vision_start_token[image_index - 1]
|
| 131 |
+
text_len = max(0, text_len)
|
| 132 |
+
st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
|
| 133 |
+
llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
|
| 134 |
+
|
| 135 |
+
t_index = torch.arange(llm_grid_t).view(-1, 1).expand(-1, llm_grid_h * llm_grid_w).flatten()
|
| 136 |
+
h_index = torch.arange(llm_grid_h).view(1, -1, 1).expand(llm_grid_t, -1, llm_grid_w).flatten()
|
| 137 |
+
w_index = torch.arange(llm_grid_w).view(1, 1, -1).expand(llm_grid_t, llm_grid_h, -1).flatten()
|
| 138 |
+
|
| 139 |
+
if skip_vision_start_token[image_index - 1]:
|
| 140 |
+
if fp > 0:
|
| 141 |
+
fp = fp - st_idx
|
| 142 |
+
llm_pos_ids_list.append(torch.stack([t_index, h_index, w_index]) + fp + st_idx)
|
| 143 |
+
fp = 0
|
| 144 |
+
else:
|
| 145 |
+
llm_pos_ids_list.append(torch.stack([t_index, h_index, w_index]) + text_len + st_idx)
|
| 146 |
+
st = ed + llm_grid_t * llm_grid_h * llm_grid_w
|
| 147 |
+
|
| 148 |
+
if st < len(input_tokens):
|
| 149 |
+
st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
|
| 150 |
+
text_len = len(input_tokens) - st
|
| 151 |
+
llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
|
| 152 |
+
|
| 153 |
+
llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
|
| 154 |
+
position_ids[..., i, attention_mask[i] == 1] = llm_positions.to(position_ids.device)
|
| 155 |
+
mrope_position_deltas.append(llm_positions.max() + 1 - len(total_input_ids[i]))
|
| 156 |
+
mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1)
|
| 157 |
+
return position_ids, mrope_position_deltas
|
| 158 |
+
|
| 159 |
+
if attention_mask is not None:
|
| 160 |
+
position_ids = attention_mask.long().cumsum(-1) - 1
|
| 161 |
+
position_ids.masked_fill_(attention_mask == 0, 1)
|
| 162 |
+
position_ids = position_ids.unsqueeze(0).expand(3, -1, -1).to(attention_mask.device)
|
| 163 |
+
max_position_ids = position_ids.max(0, keepdim=False)[0].max(-1, keepdim=True)[0]
|
| 164 |
+
mrope_position_deltas = max_position_ids + 1 - attention_mask.shape[-1]
|
| 165 |
+
else:
|
| 166 |
+
position_ids = (
|
| 167 |
+
torch.arange(input_ids.shape[1], device=input_ids.device)
|
| 168 |
+
.view(1, 1, -1).expand(3, input_ids.shape[0], -1)
|
| 169 |
+
)
|
| 170 |
+
mrope_position_deltas = torch.zeros(
|
| 171 |
+
[input_ids.shape[0], 1], device=input_ids.device, dtype=input_ids.dtype,
|
| 172 |
+
)
|
| 173 |
+
return position_ids, mrope_position_deltas
|
ComfyUI/comfy/ldm/hunyuan3d/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (6.03 kB). View file
|
|
|
ComfyUI/comfy/ldm/hunyuan3d/__pycache__/vae.cpython-312.pyc
ADDED
|
Binary file (49.6 kB). View file
|
|
|
ComfyUI/comfy/ldm/hunyuan3d/model.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
from comfy.ldm.flux.layers import (
|
| 4 |
+
DoubleStreamBlock,
|
| 5 |
+
LastLayer,
|
| 6 |
+
MLPEmbedder,
|
| 7 |
+
SingleStreamBlock,
|
| 8 |
+
timestep_embedding,
|
| 9 |
+
)
|
| 10 |
+
import comfy.patcher_extension
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class Hunyuan3Dv2(nn.Module):
|
| 14 |
+
def __init__(
|
| 15 |
+
self,
|
| 16 |
+
in_channels=64,
|
| 17 |
+
context_in_dim=1536,
|
| 18 |
+
hidden_size=1024,
|
| 19 |
+
mlp_ratio=4.0,
|
| 20 |
+
num_heads=16,
|
| 21 |
+
depth=16,
|
| 22 |
+
depth_single_blocks=32,
|
| 23 |
+
qkv_bias=True,
|
| 24 |
+
guidance_embed=False,
|
| 25 |
+
image_model=None,
|
| 26 |
+
dtype=None,
|
| 27 |
+
device=None,
|
| 28 |
+
operations=None
|
| 29 |
+
):
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.dtype = dtype
|
| 32 |
+
|
| 33 |
+
if hidden_size % num_heads != 0:
|
| 34 |
+
raise ValueError(
|
| 35 |
+
f"Hidden size {hidden_size} must be divisible by num_heads {num_heads}"
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
self.max_period = 1000 # While reimplementing the model I noticed that they messed up. This 1000 value was meant to be the time_factor but they set the max_period instead
|
| 39 |
+
self.latent_in = operations.Linear(in_channels, hidden_size, bias=True, dtype=dtype, device=device)
|
| 40 |
+
self.time_in = MLPEmbedder(in_dim=256, hidden_dim=hidden_size, dtype=dtype, device=device, operations=operations)
|
| 41 |
+
self.guidance_in = (
|
| 42 |
+
MLPEmbedder(in_dim=256, hidden_dim=hidden_size, dtype=dtype, device=device, operations=operations) if guidance_embed else None
|
| 43 |
+
)
|
| 44 |
+
self.cond_in = operations.Linear(context_in_dim, hidden_size, dtype=dtype, device=device)
|
| 45 |
+
self.double_blocks = nn.ModuleList(
|
| 46 |
+
[
|
| 47 |
+
DoubleStreamBlock(
|
| 48 |
+
hidden_size,
|
| 49 |
+
num_heads,
|
| 50 |
+
mlp_ratio=mlp_ratio,
|
| 51 |
+
qkv_bias=qkv_bias,
|
| 52 |
+
dtype=dtype, device=device, operations=operations
|
| 53 |
+
)
|
| 54 |
+
for _ in range(depth)
|
| 55 |
+
]
|
| 56 |
+
)
|
| 57 |
+
self.single_blocks = nn.ModuleList(
|
| 58 |
+
[
|
| 59 |
+
SingleStreamBlock(
|
| 60 |
+
hidden_size,
|
| 61 |
+
num_heads,
|
| 62 |
+
mlp_ratio=mlp_ratio,
|
| 63 |
+
dtype=dtype, device=device, operations=operations
|
| 64 |
+
)
|
| 65 |
+
for _ in range(depth_single_blocks)
|
| 66 |
+
]
|
| 67 |
+
)
|
| 68 |
+
self.final_layer = LastLayer(hidden_size, 1, in_channels, dtype=dtype, device=device, operations=operations)
|
| 69 |
+
|
| 70 |
+
def forward(self, x, timestep, context, guidance=None, transformer_options={}, **kwargs):
|
| 71 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 72 |
+
self._forward,
|
| 73 |
+
self,
|
| 74 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
|
| 75 |
+
).execute(x, timestep, context, guidance, transformer_options, **kwargs)
|
| 76 |
+
|
| 77 |
+
def _forward(self, x, timestep, context, guidance=None, transformer_options={}, **kwargs):
|
| 78 |
+
x = x.movedim(-1, -2)
|
| 79 |
+
timestep = 1.0 - timestep
|
| 80 |
+
txt = context
|
| 81 |
+
img = self.latent_in(x)
|
| 82 |
+
|
| 83 |
+
vec = self.time_in(timestep_embedding(timestep, 256, self.max_period).to(dtype=img.dtype))
|
| 84 |
+
if self.guidance_in is not None:
|
| 85 |
+
if guidance is not None:
|
| 86 |
+
vec = vec + self.guidance_in(timestep_embedding(guidance, 256, self.max_period).to(img.dtype))
|
| 87 |
+
|
| 88 |
+
txt = self.cond_in(txt)
|
| 89 |
+
pe = None
|
| 90 |
+
attn_mask = None
|
| 91 |
+
|
| 92 |
+
patches_replace = transformer_options.get("patches_replace", {})
|
| 93 |
+
blocks_replace = patches_replace.get("dit", {})
|
| 94 |
+
for i, block in enumerate(self.double_blocks):
|
| 95 |
+
if ("double_block", i) in blocks_replace:
|
| 96 |
+
def block_wrap(args):
|
| 97 |
+
out = {}
|
| 98 |
+
out["img"], out["txt"] = block(img=args["img"],
|
| 99 |
+
txt=args["txt"],
|
| 100 |
+
vec=args["vec"],
|
| 101 |
+
pe=args["pe"],
|
| 102 |
+
attn_mask=args.get("attn_mask"),
|
| 103 |
+
transformer_options=args["transformer_options"])
|
| 104 |
+
return out
|
| 105 |
+
|
| 106 |
+
out = blocks_replace[("double_block", i)]({"img": img,
|
| 107 |
+
"txt": txt,
|
| 108 |
+
"vec": vec,
|
| 109 |
+
"pe": pe,
|
| 110 |
+
"attn_mask": attn_mask,
|
| 111 |
+
"transformer_options": transformer_options},
|
| 112 |
+
{"original_block": block_wrap})
|
| 113 |
+
txt = out["txt"]
|
| 114 |
+
img = out["img"]
|
| 115 |
+
else:
|
| 116 |
+
img, txt = block(img=img,
|
| 117 |
+
txt=txt,
|
| 118 |
+
vec=vec,
|
| 119 |
+
pe=pe,
|
| 120 |
+
attn_mask=attn_mask,
|
| 121 |
+
transformer_options=transformer_options)
|
| 122 |
+
|
| 123 |
+
img = torch.cat((txt, img), 1)
|
| 124 |
+
|
| 125 |
+
for i, block in enumerate(self.single_blocks):
|
| 126 |
+
if ("single_block", i) in blocks_replace:
|
| 127 |
+
def block_wrap(args):
|
| 128 |
+
out = {}
|
| 129 |
+
out["img"] = block(args["img"],
|
| 130 |
+
vec=args["vec"],
|
| 131 |
+
pe=args["pe"],
|
| 132 |
+
attn_mask=args.get("attn_mask"),
|
| 133 |
+
transformer_options=args["transformer_options"])
|
| 134 |
+
return out
|
| 135 |
+
|
| 136 |
+
out = blocks_replace[("single_block", i)]({"img": img,
|
| 137 |
+
"vec": vec,
|
| 138 |
+
"pe": pe,
|
| 139 |
+
"attn_mask": attn_mask,
|
| 140 |
+
"transformer_options": transformer_options},
|
| 141 |
+
{"original_block": block_wrap})
|
| 142 |
+
img = out["img"]
|
| 143 |
+
else:
|
| 144 |
+
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask, transformer_options=transformer_options)
|
| 145 |
+
|
| 146 |
+
img = img[:, txt.shape[1]:, ...]
|
| 147 |
+
img = self.final_layer(img, vec)
|
| 148 |
+
return img.movedim(-2, -1) * (-1.0)
|
ComfyUI/comfy/ldm/hunyuan3d/vae.py
ADDED
|
@@ -0,0 +1,988 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Original: https://github.com/Tencent/Hunyuan3D-2/blob/main/hy3dgen/shapegen/models/autoencoders/model.py
|
| 2 |
+
# Since the header on their VAE source file was a bit confusing we asked for permission to use this code from tencent under the GPL license used in ComfyUI.
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import torch.nn as nn
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
import numpy as np
|
| 8 |
+
import math
|
| 9 |
+
from tqdm import tqdm
|
| 10 |
+
|
| 11 |
+
from typing import Optional
|
| 12 |
+
|
| 13 |
+
import logging
|
| 14 |
+
|
| 15 |
+
import comfy.ops
|
| 16 |
+
ops = comfy.ops.disable_weight_init
|
| 17 |
+
|
| 18 |
+
def fps(src: torch.Tensor, batch: torch.Tensor, sampling_ratio: float, start_random: bool = True):
|
| 19 |
+
|
| 20 |
+
# manually create the pointer vector
|
| 21 |
+
assert src.size(0) == batch.numel()
|
| 22 |
+
|
| 23 |
+
batch_size = int(batch.max()) + 1
|
| 24 |
+
deg = src.new_zeros(batch_size, dtype = torch.long)
|
| 25 |
+
|
| 26 |
+
deg.scatter_add_(0, batch, torch.ones_like(batch))
|
| 27 |
+
|
| 28 |
+
ptr_vec = deg.new_zeros(batch_size + 1)
|
| 29 |
+
torch.cumsum(deg, 0, out=ptr_vec[1:])
|
| 30 |
+
|
| 31 |
+
#return fps_sampling(src, ptr_vec, ratio)
|
| 32 |
+
sampled_indicies = []
|
| 33 |
+
|
| 34 |
+
for b in range(batch_size):
|
| 35 |
+
# start and the end of each batch
|
| 36 |
+
start, end = ptr_vec[b].item(), ptr_vec[b + 1].item()
|
| 37 |
+
# points from the point cloud
|
| 38 |
+
points = src[start:end]
|
| 39 |
+
|
| 40 |
+
num_points = points.size(0)
|
| 41 |
+
num_samples = max(1, math.ceil(num_points * sampling_ratio))
|
| 42 |
+
|
| 43 |
+
selected = torch.zeros(num_samples, device = src.device, dtype = torch.long)
|
| 44 |
+
distances = torch.full((num_points,), float("inf"), device = src.device)
|
| 45 |
+
|
| 46 |
+
# select a random start point
|
| 47 |
+
if start_random:
|
| 48 |
+
farthest = torch.randint(0, num_points, (1,), device = src.device)
|
| 49 |
+
else:
|
| 50 |
+
farthest = torch.tensor([0], device = src.device, dtype = torch.long)
|
| 51 |
+
|
| 52 |
+
for i in range(num_samples):
|
| 53 |
+
selected[i] = farthest
|
| 54 |
+
centroid = points[farthest].squeeze(0)
|
| 55 |
+
dist = torch.norm(points - centroid, dim = 1) # compute euclidean distance
|
| 56 |
+
distances = torch.minimum(distances, dist)
|
| 57 |
+
farthest = torch.argmax(distances)
|
| 58 |
+
|
| 59 |
+
sampled_indicies.append(torch.arange(start, end)[selected])
|
| 60 |
+
|
| 61 |
+
return torch.cat(sampled_indicies, dim = 0)
|
| 62 |
+
class PointCrossAttention(nn.Module):
|
| 63 |
+
def __init__(self,
|
| 64 |
+
num_latents: int,
|
| 65 |
+
downsample_ratio: float,
|
| 66 |
+
pc_size: int,
|
| 67 |
+
pc_sharpedge_size: int,
|
| 68 |
+
point_feats: int,
|
| 69 |
+
width: int,
|
| 70 |
+
heads: int,
|
| 71 |
+
layers: int,
|
| 72 |
+
fourier_embedder,
|
| 73 |
+
normal_pe: bool = False,
|
| 74 |
+
qkv_bias: bool = False,
|
| 75 |
+
use_ln_post: bool = True,
|
| 76 |
+
qk_norm: bool = True):
|
| 77 |
+
|
| 78 |
+
super().__init__()
|
| 79 |
+
|
| 80 |
+
self.fourier_embedder = fourier_embedder
|
| 81 |
+
|
| 82 |
+
self.pc_size = pc_size
|
| 83 |
+
self.normal_pe = normal_pe
|
| 84 |
+
self.downsample_ratio = downsample_ratio
|
| 85 |
+
self.pc_sharpedge_size = pc_sharpedge_size
|
| 86 |
+
self.num_latents = num_latents
|
| 87 |
+
self.point_feats = point_feats
|
| 88 |
+
|
| 89 |
+
self.input_proj = nn.Linear(self.fourier_embedder.out_dim + point_feats, width)
|
| 90 |
+
|
| 91 |
+
self.cross_attn = ResidualCrossAttentionBlock(
|
| 92 |
+
width = width,
|
| 93 |
+
heads = heads,
|
| 94 |
+
qkv_bias = qkv_bias,
|
| 95 |
+
qk_norm = qk_norm
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
self.self_attn = None
|
| 99 |
+
if layers > 0:
|
| 100 |
+
self.self_attn = Transformer(
|
| 101 |
+
width = width,
|
| 102 |
+
heads = heads,
|
| 103 |
+
qkv_bias = qkv_bias,
|
| 104 |
+
qk_norm = qk_norm,
|
| 105 |
+
layers = layers
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
if use_ln_post:
|
| 109 |
+
self.ln_post = nn.LayerNorm(width)
|
| 110 |
+
else:
|
| 111 |
+
self.ln_post = None
|
| 112 |
+
|
| 113 |
+
def sample_points_and_latents(self, point_cloud: torch.Tensor, features: torch.Tensor):
|
| 114 |
+
|
| 115 |
+
"""
|
| 116 |
+
Subsample points randomly from the point cloud (input_pc)
|
| 117 |
+
Further sample the subsampled points to get query_pc
|
| 118 |
+
take the fourier embeddings for both input and query pc
|
| 119 |
+
|
| 120 |
+
Mental Note: FPS-sampled points (query_pc) act as latent tokens that attend to and learn from the broader context in input_pc.
|
| 121 |
+
Goal: get a smaller represenation (query_pc) to represent the entire scence structure by learning from a broader subset (input_pc).
|
| 122 |
+
More computationally efficient.
|
| 123 |
+
|
| 124 |
+
Features are additional information for each point in the cloud
|
| 125 |
+
"""
|
| 126 |
+
|
| 127 |
+
B, _, D = point_cloud.shape
|
| 128 |
+
|
| 129 |
+
num_latents = int(self.num_latents)
|
| 130 |
+
|
| 131 |
+
num_random_query = self.pc_size / (self.pc_size + self.pc_sharpedge_size) * num_latents
|
| 132 |
+
num_sharpedge_query = num_latents - num_random_query
|
| 133 |
+
|
| 134 |
+
# Split random and sharpedge surface points
|
| 135 |
+
random_pc, sharpedge_pc = torch.split(point_cloud, [self.pc_size, self.pc_sharpedge_size], dim=1)
|
| 136 |
+
|
| 137 |
+
# assert statements
|
| 138 |
+
assert random_pc.shape[1] <= self.pc_size, "Random surface points size must be less than or equal to pc_size"
|
| 139 |
+
assert sharpedge_pc.shape[1] <= self.pc_sharpedge_size, "Sharpedge surface points size must be less than or equal to pc_sharpedge_size"
|
| 140 |
+
|
| 141 |
+
input_random_pc_size = int(num_random_query * self.downsample_ratio)
|
| 142 |
+
random_query_pc, random_input_pc, random_idx_pc, random_idx_query = \
|
| 143 |
+
self.subsample(pc = random_pc, num_query = num_random_query, input_pc_size = input_random_pc_size)
|
| 144 |
+
|
| 145 |
+
input_sharpedge_pc_size = int(num_sharpedge_query * self.downsample_ratio)
|
| 146 |
+
|
| 147 |
+
if input_sharpedge_pc_size == 0:
|
| 148 |
+
sharpedge_input_pc = torch.zeros(B, 0, D, dtype = random_input_pc.dtype).to(point_cloud.device)
|
| 149 |
+
sharpedge_query_pc = torch.zeros(B, 0, D, dtype= random_query_pc.dtype).to(point_cloud.device)
|
| 150 |
+
|
| 151 |
+
else:
|
| 152 |
+
sharpedge_query_pc, sharpedge_input_pc, sharpedge_idx_pc, sharpedge_idx_query = \
|
| 153 |
+
self.subsample(pc = sharpedge_pc, num_query = num_sharpedge_query, input_pc_size = input_sharpedge_pc_size)
|
| 154 |
+
|
| 155 |
+
# concat the random and sharpedges
|
| 156 |
+
query_pc = torch.cat([random_query_pc, sharpedge_query_pc], dim = 1)
|
| 157 |
+
input_pc = torch.cat([random_input_pc, sharpedge_input_pc], dim = 1)
|
| 158 |
+
|
| 159 |
+
query = self.fourier_embedder(query_pc)
|
| 160 |
+
data = self.fourier_embedder(input_pc)
|
| 161 |
+
|
| 162 |
+
if self.point_feats > 0:
|
| 163 |
+
random_surface_features, sharpedge_surface_features = torch.split(features, [self.pc_size, self.pc_sharpedge_size], dim = 1)
|
| 164 |
+
|
| 165 |
+
input_random_surface_features, query_random_features = \
|
| 166 |
+
self.handle_features(features = random_surface_features, idx_pc = random_idx_pc, batch_size = B,
|
| 167 |
+
input_pc_size = input_random_pc_size, idx_query = random_idx_query)
|
| 168 |
+
|
| 169 |
+
if input_sharpedge_pc_size == 0:
|
| 170 |
+
input_sharpedge_surface_features = torch.zeros(B, 0, self.point_feats,
|
| 171 |
+
dtype = input_random_surface_features.dtype, device = point_cloud.device)
|
| 172 |
+
|
| 173 |
+
query_sharpedge_features = torch.zeros(B, 0, self.point_feats,
|
| 174 |
+
dtype = query_random_features.dtype, device = point_cloud.device)
|
| 175 |
+
else:
|
| 176 |
+
|
| 177 |
+
input_sharpedge_surface_features, query_sharpedge_features = \
|
| 178 |
+
self.handle_features(idx_pc = sharpedge_idx_pc, features = sharpedge_surface_features,
|
| 179 |
+
batch_size = B, idx_query = sharpedge_idx_query, input_pc_size = input_sharpedge_pc_size)
|
| 180 |
+
|
| 181 |
+
query_features = torch.cat([query_random_features, query_sharpedge_features], dim = 1)
|
| 182 |
+
input_features = torch.cat([input_random_surface_features, input_sharpedge_surface_features], dim = 1)
|
| 183 |
+
|
| 184 |
+
if self.normal_pe:
|
| 185 |
+
# apply the fourier embeddings on the first 3 dims (xyz)
|
| 186 |
+
input_features_pe = self.fourier_embedder(input_features[..., :3])
|
| 187 |
+
query_features_pe = self.fourier_embedder(query_features[..., :3])
|
| 188 |
+
# replace the first 3 dims with the new PE ones
|
| 189 |
+
input_features = torch.cat([input_features_pe, input_features[..., :3]], dim = -1)
|
| 190 |
+
query_features = torch.cat([query_features_pe, query_features[..., :3]], dim = -1)
|
| 191 |
+
|
| 192 |
+
# concat at the channels dim
|
| 193 |
+
query = torch.cat([query, query_features], dim = -1)
|
| 194 |
+
data = torch.cat([data, input_features], dim = -1)
|
| 195 |
+
|
| 196 |
+
# don't return pc_info to avoid unnecessary memory usuage
|
| 197 |
+
return query.view(B, -1, query.shape[-1]), data.view(B, -1, data.shape[-1])
|
| 198 |
+
|
| 199 |
+
def forward(self, point_cloud: torch.Tensor, features: torch.Tensor):
|
| 200 |
+
|
| 201 |
+
query, data = self.sample_points_and_latents(point_cloud = point_cloud, features = features)
|
| 202 |
+
|
| 203 |
+
# apply projections
|
| 204 |
+
query = self.input_proj(query)
|
| 205 |
+
data = self.input_proj(data)
|
| 206 |
+
|
| 207 |
+
# apply cross attention between query and data
|
| 208 |
+
latents = self.cross_attn(query, data)
|
| 209 |
+
|
| 210 |
+
if self.self_attn is not None:
|
| 211 |
+
latents = self.self_attn(latents)
|
| 212 |
+
|
| 213 |
+
if self.ln_post is not None:
|
| 214 |
+
latents = self.ln_post(latents)
|
| 215 |
+
|
| 216 |
+
return latents
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def subsample(self, pc, num_query, input_pc_size: int):
|
| 220 |
+
|
| 221 |
+
"""
|
| 222 |
+
num_query: number of points to keep after FPS
|
| 223 |
+
input_pc_size: number of points to select before FPS
|
| 224 |
+
"""
|
| 225 |
+
|
| 226 |
+
B, _, D = pc.shape
|
| 227 |
+
query_ratio = num_query / input_pc_size
|
| 228 |
+
|
| 229 |
+
# random subsampling of points inside the point cloud
|
| 230 |
+
idx_pc = torch.randperm(pc.shape[1], device = pc.device)[:input_pc_size]
|
| 231 |
+
input_pc = pc[:, idx_pc, :]
|
| 232 |
+
|
| 233 |
+
# flatten to allow applying fps across the whole batch
|
| 234 |
+
flattent_input_pc = input_pc.view(B * input_pc_size, D)
|
| 235 |
+
|
| 236 |
+
# construct a batch_down tensor to tell fps
|
| 237 |
+
# which points belong to which batch
|
| 238 |
+
N_down = int(flattent_input_pc.shape[0] / B)
|
| 239 |
+
batch_down = torch.arange(B).to(pc.device)
|
| 240 |
+
batch_down = torch.repeat_interleave(batch_down, N_down)
|
| 241 |
+
|
| 242 |
+
idx_query = fps(flattent_input_pc, batch_down, sampling_ratio = query_ratio)
|
| 243 |
+
query_pc = flattent_input_pc[idx_query].view(B, -1, D)
|
| 244 |
+
|
| 245 |
+
return query_pc, input_pc, idx_pc, idx_query
|
| 246 |
+
|
| 247 |
+
def handle_features(self, features, idx_pc, input_pc_size, batch_size: int, idx_query):
|
| 248 |
+
|
| 249 |
+
B = batch_size
|
| 250 |
+
|
| 251 |
+
input_surface_features = features[:, idx_pc, :]
|
| 252 |
+
flattent_input_features = input_surface_features.view(B * input_pc_size, -1)
|
| 253 |
+
query_features = flattent_input_features[idx_query].view(B, -1,
|
| 254 |
+
flattent_input_features.shape[-1])
|
| 255 |
+
|
| 256 |
+
return input_surface_features, query_features
|
| 257 |
+
|
| 258 |
+
def normalize_mesh(mesh, scale = 0.9999):
|
| 259 |
+
"""Normalize mesh to fit in [-scale, scale]. Translate mesh so its center is [0,0,0]"""
|
| 260 |
+
|
| 261 |
+
bbox = mesh.bounds
|
| 262 |
+
center = (bbox[1] + bbox[0]) / 2
|
| 263 |
+
|
| 264 |
+
max_extent = (bbox[1] - bbox[0]).max()
|
| 265 |
+
mesh.apply_translation(-center)
|
| 266 |
+
mesh.apply_scale((2 * scale) / max_extent)
|
| 267 |
+
|
| 268 |
+
return mesh
|
| 269 |
+
|
| 270 |
+
def sample_pointcloud(mesh, num = 200000):
|
| 271 |
+
""" Uniformly sample points from the surface of the mesh """
|
| 272 |
+
|
| 273 |
+
points, face_idx = mesh.sample(num, return_index = True)
|
| 274 |
+
normals = mesh.face_normals[face_idx]
|
| 275 |
+
return torch.from_numpy(points.astype(np.float32)), torch.from_numpy(normals.astype(np.float32))
|
| 276 |
+
|
| 277 |
+
def detect_sharp_edges(mesh, threshold=0.985):
|
| 278 |
+
"""Return edge indices (a, b) that lie on sharp boundaries of the mesh."""
|
| 279 |
+
|
| 280 |
+
V, F = mesh.vertices, mesh.faces
|
| 281 |
+
VN, FN = mesh.vertex_normals, mesh.face_normals
|
| 282 |
+
|
| 283 |
+
sharp_mask = np.ones(V.shape[0])
|
| 284 |
+
for i in range(3):
|
| 285 |
+
indices = F[:, i]
|
| 286 |
+
alignment = np.einsum('ij,ij->i', VN[indices], FN)
|
| 287 |
+
dot_stack = np.stack((sharp_mask[indices], alignment), axis=-1)
|
| 288 |
+
sharp_mask[indices] = np.min(dot_stack, axis=-1)
|
| 289 |
+
|
| 290 |
+
edge_a = np.concatenate([F[:, 0], F[:, 1], F[:, 2]])
|
| 291 |
+
edge_b = np.concatenate([F[:, 1], F[:, 2], F[:, 0]])
|
| 292 |
+
sharp_edges = (sharp_mask[edge_a] < threshold) & (sharp_mask[edge_b] < threshold)
|
| 293 |
+
|
| 294 |
+
return edge_a[sharp_edges], edge_b[sharp_edges]
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
def sharp_sample_pointcloud(mesh, num = 16384):
|
| 298 |
+
""" Sample points preferentially from sharp edges in the mesh. """
|
| 299 |
+
|
| 300 |
+
edge_a, edge_b = detect_sharp_edges(mesh)
|
| 301 |
+
V, VN = mesh.vertices, mesh.vertex_normals
|
| 302 |
+
|
| 303 |
+
va, vb = V[edge_a], V[edge_b]
|
| 304 |
+
na, nb = VN[edge_a], VN[edge_b]
|
| 305 |
+
|
| 306 |
+
edge_lengths = np.linalg.norm(vb - va, axis=-1)
|
| 307 |
+
weights = edge_lengths / edge_lengths.sum()
|
| 308 |
+
|
| 309 |
+
indices = np.searchsorted(np.cumsum(weights), np.random.rand(num))
|
| 310 |
+
t = np.random.rand(num, 1)
|
| 311 |
+
|
| 312 |
+
samples = t * va[indices] + (1 - t) * vb[indices]
|
| 313 |
+
normals = t * na[indices] + (1 - t) * nb[indices]
|
| 314 |
+
|
| 315 |
+
return samples.astype(np.float32), normals.astype(np.float32)
|
| 316 |
+
|
| 317 |
+
def load_surface_sharpedge(mesh, num_points=4096, num_sharp_points=4096, sharpedge_flag = True, device = "cuda"):
|
| 318 |
+
"""Load a surface with optional sharp-edge annotations from a trimesh mesh."""
|
| 319 |
+
|
| 320 |
+
import trimesh
|
| 321 |
+
|
| 322 |
+
try:
|
| 323 |
+
mesh_full = trimesh.util.concatenate(mesh.dump())
|
| 324 |
+
except Exception:
|
| 325 |
+
mesh_full = trimesh.util.concatenate(mesh)
|
| 326 |
+
|
| 327 |
+
mesh_full = normalize_mesh(mesh_full)
|
| 328 |
+
|
| 329 |
+
faces = mesh_full.faces
|
| 330 |
+
vertices = mesh_full.vertices
|
| 331 |
+
origin_face_count = faces.shape[0]
|
| 332 |
+
|
| 333 |
+
mesh_surface = trimesh.Trimesh(vertices=vertices, faces=faces[:origin_face_count])
|
| 334 |
+
mesh_fill = trimesh.Trimesh(vertices=vertices, faces=faces[origin_face_count:])
|
| 335 |
+
|
| 336 |
+
area_surface = mesh_surface.area
|
| 337 |
+
area_fill = mesh_fill.area
|
| 338 |
+
total_area = area_surface + area_fill
|
| 339 |
+
|
| 340 |
+
sample_num = 499712 // 2
|
| 341 |
+
fill_ratio = area_fill / total_area if total_area > 0 else 0
|
| 342 |
+
|
| 343 |
+
num_fill = int(sample_num * fill_ratio)
|
| 344 |
+
num_surface = sample_num - num_fill
|
| 345 |
+
|
| 346 |
+
surf_pts, surf_normals = sample_pointcloud(mesh_surface, num_surface)
|
| 347 |
+
fill_pts, fill_normals = (torch.zeros(0, 3), torch.zeros(0, 3)) if num_fill == 0 else sample_pointcloud(mesh_fill, num_fill)
|
| 348 |
+
|
| 349 |
+
sharp_pts, sharp_normals = sharp_sample_pointcloud(mesh_surface, sample_num)
|
| 350 |
+
|
| 351 |
+
def assemble_tensor(points, normals, label=None):
|
| 352 |
+
|
| 353 |
+
data = torch.cat([points, normals], dim=1).half().to(device)
|
| 354 |
+
|
| 355 |
+
if label is not None:
|
| 356 |
+
label_tensor = torch.full((data.shape[0], 1), float(label), dtype=torch.float16).to(device)
|
| 357 |
+
data = torch.cat([data, label_tensor], dim=1)
|
| 358 |
+
|
| 359 |
+
return data
|
| 360 |
+
|
| 361 |
+
surface = assemble_tensor(torch.cat([surf_pts.to(device), fill_pts.to(device)], dim=0),
|
| 362 |
+
torch.cat([surf_normals.to(device), fill_normals.to(device)], dim=0),
|
| 363 |
+
label = 0 if sharpedge_flag else None)
|
| 364 |
+
|
| 365 |
+
sharp_surface = assemble_tensor(torch.from_numpy(sharp_pts), torch.from_numpy(sharp_normals),
|
| 366 |
+
label = 1 if sharpedge_flag else None)
|
| 367 |
+
|
| 368 |
+
rng = np.random.default_rng()
|
| 369 |
+
|
| 370 |
+
surface = surface[rng.choice(surface.shape[0], num_points, replace = False)]
|
| 371 |
+
sharp_surface = sharp_surface[rng.choice(sharp_surface.shape[0], num_sharp_points, replace = False)]
|
| 372 |
+
|
| 373 |
+
full = torch.cat([surface, sharp_surface], dim = 0).unsqueeze(0)
|
| 374 |
+
|
| 375 |
+
return full
|
| 376 |
+
|
| 377 |
+
class SharpEdgeSurfaceLoader:
|
| 378 |
+
""" Load mesh surface and sharp edge samples. """
|
| 379 |
+
|
| 380 |
+
def __init__(self, num_uniform_points = 8192, num_sharp_points = 8192):
|
| 381 |
+
|
| 382 |
+
self.num_uniform_points = num_uniform_points
|
| 383 |
+
self.num_sharp_points = num_sharp_points
|
| 384 |
+
self.total_points = num_uniform_points + num_sharp_points
|
| 385 |
+
|
| 386 |
+
def __call__(self, mesh_input, device = "cuda"):
|
| 387 |
+
mesh = self._load_mesh(mesh_input)
|
| 388 |
+
return load_surface_sharpedge(mesh, self.num_uniform_points, self.num_sharp_points, device = device)
|
| 389 |
+
|
| 390 |
+
@staticmethod
|
| 391 |
+
def _load_mesh(mesh_input):
|
| 392 |
+
import trimesh
|
| 393 |
+
|
| 394 |
+
if isinstance(mesh_input, str):
|
| 395 |
+
mesh = trimesh.load(mesh_input, force="mesh", merge_primitives = True)
|
| 396 |
+
else:
|
| 397 |
+
mesh = mesh_input
|
| 398 |
+
|
| 399 |
+
if isinstance(mesh, trimesh.Scene):
|
| 400 |
+
combined = None
|
| 401 |
+
for obj in mesh.geometry.values():
|
| 402 |
+
combined = obj if combined is None else combined + obj
|
| 403 |
+
return combined
|
| 404 |
+
|
| 405 |
+
return mesh
|
| 406 |
+
|
| 407 |
+
class DiagonalGaussianDistribution:
|
| 408 |
+
def __init__(self, params: torch.Tensor, feature_dim: int = -1):
|
| 409 |
+
|
| 410 |
+
# divide quant channels (8) into mean and log variance
|
| 411 |
+
self.mean, self.logvar = torch.chunk(params, 2, dim = feature_dim)
|
| 412 |
+
|
| 413 |
+
self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
|
| 414 |
+
self.std = torch.exp(0.5 * self.logvar)
|
| 415 |
+
|
| 416 |
+
def sample(self):
|
| 417 |
+
|
| 418 |
+
eps = torch.randn_like(self.std)
|
| 419 |
+
z = self.mean + eps * self.std
|
| 420 |
+
|
| 421 |
+
return z
|
| 422 |
+
|
| 423 |
+
################################################
|
| 424 |
+
# Volume Decoder
|
| 425 |
+
################################################
|
| 426 |
+
|
| 427 |
+
class VanillaVolumeDecoder():
|
| 428 |
+
@torch.no_grad()
|
| 429 |
+
def __call__(self, latents: torch.Tensor, geo_decoder: callable, octree_resolution: int, bounds = 1.01,
|
| 430 |
+
num_chunks: int = 10_000, enable_pbar: bool = True, **kwargs):
|
| 431 |
+
|
| 432 |
+
if isinstance(bounds, float):
|
| 433 |
+
bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
|
| 434 |
+
|
| 435 |
+
bbox_min, bbox_max = torch.tensor(bounds[:3]), torch.tensor(bounds[3:])
|
| 436 |
+
|
| 437 |
+
x = torch.linspace(bbox_min[0], bbox_max[0], int(octree_resolution) + 1, dtype = torch.float32)
|
| 438 |
+
y = torch.linspace(bbox_min[1], bbox_max[1], int(octree_resolution) + 1, dtype = torch.float32)
|
| 439 |
+
z = torch.linspace(bbox_min[2], bbox_max[2], int(octree_resolution) + 1, dtype = torch.float32)
|
| 440 |
+
|
| 441 |
+
[xs, ys, zs] = torch.meshgrid(x, y, z, indexing = "ij")
|
| 442 |
+
xyz = torch.stack((xs, ys, zs), axis=-1).to(latents.device, dtype = latents.dtype).contiguous().reshape(-1, 3)
|
| 443 |
+
grid_size = [int(octree_resolution) + 1, int(octree_resolution) + 1, int(octree_resolution) + 1]
|
| 444 |
+
|
| 445 |
+
batch_logits = []
|
| 446 |
+
for start in tqdm(range(0, xyz.shape[0], num_chunks), desc="Volume Decoding",
|
| 447 |
+
disable=not enable_pbar):
|
| 448 |
+
|
| 449 |
+
chunk_queries = xyz[start: start + num_chunks, :]
|
| 450 |
+
chunk_queries = chunk_queries.unsqueeze(0).repeat(latents.shape[0], 1, 1)
|
| 451 |
+
logits = geo_decoder(queries = chunk_queries, latents = latents)
|
| 452 |
+
batch_logits.append(logits)
|
| 453 |
+
|
| 454 |
+
grid_logits = torch.cat(batch_logits, dim = 1)
|
| 455 |
+
grid_logits = grid_logits.view((latents.shape[0], *grid_size)).float()
|
| 456 |
+
|
| 457 |
+
return grid_logits
|
| 458 |
+
|
| 459 |
+
class FourierEmbedder(nn.Module):
|
| 460 |
+
"""The sin/cosine positional embedding. Given an input tensor `x` of shape [n_batch, ..., c_dim], it converts
|
| 461 |
+
each feature dimension of `x[..., i]` into:
|
| 462 |
+
[
|
| 463 |
+
sin(x[..., i]),
|
| 464 |
+
sin(f_1*x[..., i]),
|
| 465 |
+
sin(f_2*x[..., i]),
|
| 466 |
+
...
|
| 467 |
+
sin(f_N * x[..., i]),
|
| 468 |
+
cos(x[..., i]),
|
| 469 |
+
cos(f_1*x[..., i]),
|
| 470 |
+
cos(f_2*x[..., i]),
|
| 471 |
+
...
|
| 472 |
+
cos(f_N * x[..., i]),
|
| 473 |
+
x[..., i] # only present if include_input is True.
|
| 474 |
+
], here f_i is the frequency.
|
| 475 |
+
|
| 476 |
+
Denote the space is [0 / num_freqs, 1 / num_freqs, 2 / num_freqs, 3 / num_freqs, ..., (num_freqs - 1) / num_freqs].
|
| 477 |
+
If logspace is True, then the frequency f_i is [2^(0 / num_freqs), ..., 2^(i / num_freqs), ...];
|
| 478 |
+
Otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)].
|
| 479 |
+
|
| 480 |
+
Args:
|
| 481 |
+
num_freqs (int): the number of frequencies, default is 6;
|
| 482 |
+
logspace (bool): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
|
| 483 |
+
otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)];
|
| 484 |
+
input_dim (int): the input dimension, default is 3;
|
| 485 |
+
include_input (bool): include the input tensor or not, default is True.
|
| 486 |
+
|
| 487 |
+
Attributes:
|
| 488 |
+
frequencies (torch.Tensor): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
|
| 489 |
+
otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1);
|
| 490 |
+
|
| 491 |
+
out_dim (int): the embedding size, if include_input is True, it is input_dim * (num_freqs * 2 + 1),
|
| 492 |
+
otherwise, it is input_dim * num_freqs * 2.
|
| 493 |
+
|
| 494 |
+
"""
|
| 495 |
+
|
| 496 |
+
def __init__(self,
|
| 497 |
+
num_freqs: int = 6,
|
| 498 |
+
logspace: bool = True,
|
| 499 |
+
input_dim: int = 3,
|
| 500 |
+
include_input: bool = True,
|
| 501 |
+
include_pi: bool = True) -> None:
|
| 502 |
+
|
| 503 |
+
"""The initialization"""
|
| 504 |
+
|
| 505 |
+
super().__init__()
|
| 506 |
+
|
| 507 |
+
if logspace:
|
| 508 |
+
frequencies = 2.0 ** torch.arange(
|
| 509 |
+
num_freqs,
|
| 510 |
+
dtype=torch.float32
|
| 511 |
+
)
|
| 512 |
+
else:
|
| 513 |
+
frequencies = torch.linspace(
|
| 514 |
+
1.0,
|
| 515 |
+
2.0 ** (num_freqs - 1),
|
| 516 |
+
num_freqs,
|
| 517 |
+
dtype=torch.float32
|
| 518 |
+
)
|
| 519 |
+
|
| 520 |
+
if include_pi:
|
| 521 |
+
frequencies *= torch.pi
|
| 522 |
+
|
| 523 |
+
self.register_buffer("frequencies", frequencies, persistent=False)
|
| 524 |
+
self.include_input = include_input
|
| 525 |
+
self.num_freqs = num_freqs
|
| 526 |
+
|
| 527 |
+
self.out_dim = self.get_dims(input_dim)
|
| 528 |
+
|
| 529 |
+
def get_dims(self, input_dim):
|
| 530 |
+
temp = 1 if self.include_input or self.num_freqs == 0 else 0
|
| 531 |
+
out_dim = input_dim * (self.num_freqs * 2 + temp)
|
| 532 |
+
|
| 533 |
+
return out_dim
|
| 534 |
+
|
| 535 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 536 |
+
""" Forward process.
|
| 537 |
+
|
| 538 |
+
Args:
|
| 539 |
+
x: tensor of shape [..., dim]
|
| 540 |
+
|
| 541 |
+
Returns:
|
| 542 |
+
embedding: an embedding of `x` of shape [..., dim * (num_freqs * 2 + temp)]
|
| 543 |
+
where temp is 1 if include_input is True and 0 otherwise.
|
| 544 |
+
"""
|
| 545 |
+
|
| 546 |
+
if self.num_freqs > 0:
|
| 547 |
+
embed = (x[..., None].contiguous() * self.frequencies.to(device=x.device, dtype=x.dtype)).view(*x.shape[:-1], -1)
|
| 548 |
+
if self.include_input:
|
| 549 |
+
return torch.cat((x, embed.sin(), embed.cos()), dim=-1)
|
| 550 |
+
else:
|
| 551 |
+
return torch.cat((embed.sin(), embed.cos()), dim=-1)
|
| 552 |
+
else:
|
| 553 |
+
return x
|
| 554 |
+
|
| 555 |
+
class CrossAttentionProcessor:
|
| 556 |
+
def __call__(self, attn, q, k, v):
|
| 557 |
+
out = comfy.ops.scaled_dot_product_attention(q, k, v)
|
| 558 |
+
return out
|
| 559 |
+
|
| 560 |
+
class DropPath(nn.Module):
|
| 561 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
| 562 |
+
"""
|
| 563 |
+
|
| 564 |
+
def __init__(self, drop_prob: float = 0., scale_by_keep: bool = True):
|
| 565 |
+
super(DropPath, self).__init__()
|
| 566 |
+
self.drop_prob = drop_prob
|
| 567 |
+
self.scale_by_keep = scale_by_keep
|
| 568 |
+
|
| 569 |
+
def forward(self, x):
|
| 570 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
| 571 |
+
|
| 572 |
+
This is the same as the DropConnect impl I created for EfficientNet, etc networks, however,
|
| 573 |
+
the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
|
| 574 |
+
See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for
|
| 575 |
+
changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use
|
| 576 |
+
'survival rate' as the argument.
|
| 577 |
+
|
| 578 |
+
"""
|
| 579 |
+
if self.drop_prob == 0. or not self.training:
|
| 580 |
+
return x
|
| 581 |
+
keep_prob = 1 - self.drop_prob
|
| 582 |
+
shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
|
| 583 |
+
random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
|
| 584 |
+
if keep_prob > 0.0 and self.scale_by_keep:
|
| 585 |
+
random_tensor.div_(keep_prob)
|
| 586 |
+
return x * random_tensor
|
| 587 |
+
|
| 588 |
+
def extra_repr(self):
|
| 589 |
+
return f'drop_prob={round(self.drop_prob, 3):0.3f}'
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
class MLP(nn.Module):
|
| 593 |
+
def __init__(
|
| 594 |
+
self, *,
|
| 595 |
+
width: int,
|
| 596 |
+
expand_ratio: int = 4,
|
| 597 |
+
output_width: int = None,
|
| 598 |
+
drop_path_rate: float = 0.0
|
| 599 |
+
):
|
| 600 |
+
super().__init__()
|
| 601 |
+
self.width = width
|
| 602 |
+
self.c_fc = ops.Linear(width, width * expand_ratio)
|
| 603 |
+
self.c_proj = ops.Linear(width * expand_ratio, output_width if output_width is not None else width)
|
| 604 |
+
self.gelu = nn.GELU()
|
| 605 |
+
self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
|
| 606 |
+
|
| 607 |
+
def forward(self, x):
|
| 608 |
+
return self.drop_path(self.c_proj(self.gelu(self.c_fc(x))))
|
| 609 |
+
|
| 610 |
+
class QKVMultiheadCrossAttention(nn.Module):
|
| 611 |
+
def __init__(
|
| 612 |
+
self,
|
| 613 |
+
heads: int,
|
| 614 |
+
n_data = None,
|
| 615 |
+
width=None,
|
| 616 |
+
qk_norm=False,
|
| 617 |
+
norm_layer=ops.LayerNorm
|
| 618 |
+
):
|
| 619 |
+
super().__init__()
|
| 620 |
+
self.heads = heads
|
| 621 |
+
self.n_data = n_data
|
| 622 |
+
self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 623 |
+
self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 624 |
+
|
| 625 |
+
def forward(self, q, kv):
|
| 626 |
+
|
| 627 |
+
_, n_ctx, _ = q.shape
|
| 628 |
+
bs, n_data, width = kv.shape
|
| 629 |
+
|
| 630 |
+
attn_ch = width // self.heads // 2
|
| 631 |
+
q = q.view(bs, n_ctx, self.heads, -1)
|
| 632 |
+
|
| 633 |
+
kv = kv.view(bs, n_data, self.heads, -1)
|
| 634 |
+
k, v = torch.split(kv, attn_ch, dim=-1)
|
| 635 |
+
|
| 636 |
+
q = self.q_norm(q)
|
| 637 |
+
k = self.k_norm(k)
|
| 638 |
+
|
| 639 |
+
q, k, v = [t.permute(0, 2, 1, 3) for t in (q, k, v)]
|
| 640 |
+
out = F.scaled_dot_product_attention(q, k, v)
|
| 641 |
+
|
| 642 |
+
out = out.transpose(1, 2).reshape(bs, n_ctx, -1)
|
| 643 |
+
|
| 644 |
+
return out
|
| 645 |
+
|
| 646 |
+
class MultiheadCrossAttention(nn.Module):
|
| 647 |
+
def __init__(
|
| 648 |
+
self,
|
| 649 |
+
*,
|
| 650 |
+
width: int,
|
| 651 |
+
heads: int,
|
| 652 |
+
qkv_bias: bool = True,
|
| 653 |
+
data_width: Optional[int] = None,
|
| 654 |
+
norm_layer=ops.LayerNorm,
|
| 655 |
+
qk_norm: bool = False,
|
| 656 |
+
kv_cache: bool = False,
|
| 657 |
+
):
|
| 658 |
+
super().__init__()
|
| 659 |
+
self.width = width
|
| 660 |
+
self.heads = heads
|
| 661 |
+
self.data_width = width if data_width is None else data_width
|
| 662 |
+
self.c_q = ops.Linear(width, width, bias=qkv_bias)
|
| 663 |
+
self.c_kv = ops.Linear(self.data_width, width * 2, bias=qkv_bias)
|
| 664 |
+
self.c_proj = ops.Linear(width, width)
|
| 665 |
+
self.attention = QKVMultiheadCrossAttention(
|
| 666 |
+
heads=heads,
|
| 667 |
+
width=width,
|
| 668 |
+
norm_layer=norm_layer,
|
| 669 |
+
qk_norm=qk_norm
|
| 670 |
+
)
|
| 671 |
+
self.kv_cache = kv_cache
|
| 672 |
+
self.data = None
|
| 673 |
+
|
| 674 |
+
def forward(self, x, data):
|
| 675 |
+
x = self.c_q(x)
|
| 676 |
+
if self.kv_cache:
|
| 677 |
+
if self.data is None:
|
| 678 |
+
self.data = self.c_kv(data)
|
| 679 |
+
logging.info('Save kv cache,this should be called only once for one mesh')
|
| 680 |
+
data = self.data
|
| 681 |
+
else:
|
| 682 |
+
data = self.c_kv(data)
|
| 683 |
+
x = self.attention(x, data)
|
| 684 |
+
x = self.c_proj(x)
|
| 685 |
+
return x
|
| 686 |
+
|
| 687 |
+
class ResidualCrossAttentionBlock(nn.Module):
|
| 688 |
+
def __init__(
|
| 689 |
+
self,
|
| 690 |
+
*,
|
| 691 |
+
width: int,
|
| 692 |
+
heads: int,
|
| 693 |
+
mlp_expand_ratio: int = 4,
|
| 694 |
+
data_width: Optional[int] = None,
|
| 695 |
+
qkv_bias: bool = True,
|
| 696 |
+
norm_layer=ops.LayerNorm,
|
| 697 |
+
qk_norm: bool = False
|
| 698 |
+
):
|
| 699 |
+
super().__init__()
|
| 700 |
+
|
| 701 |
+
if data_width is None:
|
| 702 |
+
data_width = width
|
| 703 |
+
|
| 704 |
+
self.attn = MultiheadCrossAttention(
|
| 705 |
+
width=width,
|
| 706 |
+
heads=heads,
|
| 707 |
+
data_width=data_width,
|
| 708 |
+
qkv_bias=qkv_bias,
|
| 709 |
+
norm_layer=norm_layer,
|
| 710 |
+
qk_norm=qk_norm
|
| 711 |
+
)
|
| 712 |
+
self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 713 |
+
self.ln_2 = norm_layer(data_width, elementwise_affine=True, eps=1e-6)
|
| 714 |
+
self.ln_3 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 715 |
+
self.mlp = MLP(width=width, expand_ratio=mlp_expand_ratio)
|
| 716 |
+
|
| 717 |
+
def forward(self, x: torch.Tensor, data: torch.Tensor):
|
| 718 |
+
x = x + self.attn(self.ln_1(x), self.ln_2(data))
|
| 719 |
+
x = x + self.mlp(self.ln_3(x))
|
| 720 |
+
return x
|
| 721 |
+
|
| 722 |
+
|
| 723 |
+
class QKVMultiheadAttention(nn.Module):
|
| 724 |
+
def __init__(
|
| 725 |
+
self,
|
| 726 |
+
*,
|
| 727 |
+
heads: int,
|
| 728 |
+
width=None,
|
| 729 |
+
qk_norm=False,
|
| 730 |
+
norm_layer=ops.LayerNorm
|
| 731 |
+
):
|
| 732 |
+
super().__init__()
|
| 733 |
+
self.heads = heads
|
| 734 |
+
self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 735 |
+
self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
|
| 736 |
+
|
| 737 |
+
def forward(self, qkv):
|
| 738 |
+
bs, n_ctx, width = qkv.shape
|
| 739 |
+
attn_ch = width // self.heads // 3
|
| 740 |
+
qkv = qkv.view(bs, n_ctx, self.heads, -1)
|
| 741 |
+
q, k, v = torch.split(qkv, attn_ch, dim=-1)
|
| 742 |
+
|
| 743 |
+
q = self.q_norm(q)
|
| 744 |
+
k = self.k_norm(k)
|
| 745 |
+
|
| 746 |
+
q, k, v = [t.permute(0, 2, 1, 3) for t in (q, k, v)]
|
| 747 |
+
out = F.scaled_dot_product_attention(q, k, v).transpose(1, 2).reshape(bs, n_ctx, -1)
|
| 748 |
+
return out
|
| 749 |
+
|
| 750 |
+
|
| 751 |
+
class MultiheadAttention(nn.Module):
|
| 752 |
+
def __init__(
|
| 753 |
+
self,
|
| 754 |
+
*,
|
| 755 |
+
width: int,
|
| 756 |
+
heads: int,
|
| 757 |
+
qkv_bias: bool,
|
| 758 |
+
norm_layer=ops.LayerNorm,
|
| 759 |
+
qk_norm: bool = False,
|
| 760 |
+
drop_path_rate: float = 0.0
|
| 761 |
+
):
|
| 762 |
+
super().__init__()
|
| 763 |
+
|
| 764 |
+
self.c_qkv = ops.Linear(width, width * 3, bias=qkv_bias)
|
| 765 |
+
self.c_proj = ops.Linear(width, width)
|
| 766 |
+
self.attention = QKVMultiheadAttention(
|
| 767 |
+
heads=heads,
|
| 768 |
+
width=width,
|
| 769 |
+
norm_layer=norm_layer,
|
| 770 |
+
qk_norm=qk_norm
|
| 771 |
+
)
|
| 772 |
+
self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
|
| 773 |
+
|
| 774 |
+
def forward(self, x):
|
| 775 |
+
x = self.c_qkv(x)
|
| 776 |
+
x = self.attention(x)
|
| 777 |
+
x = self.drop_path(self.c_proj(x))
|
| 778 |
+
return x
|
| 779 |
+
|
| 780 |
+
|
| 781 |
+
class ResidualAttentionBlock(nn.Module):
|
| 782 |
+
def __init__(
|
| 783 |
+
self,
|
| 784 |
+
*,
|
| 785 |
+
width: int,
|
| 786 |
+
heads: int,
|
| 787 |
+
qkv_bias: bool = True,
|
| 788 |
+
norm_layer=ops.LayerNorm,
|
| 789 |
+
qk_norm: bool = False,
|
| 790 |
+
drop_path_rate: float = 0.0,
|
| 791 |
+
):
|
| 792 |
+
super().__init__()
|
| 793 |
+
self.attn = MultiheadAttention(
|
| 794 |
+
width=width,
|
| 795 |
+
heads=heads,
|
| 796 |
+
qkv_bias=qkv_bias,
|
| 797 |
+
norm_layer=norm_layer,
|
| 798 |
+
qk_norm=qk_norm,
|
| 799 |
+
drop_path_rate=drop_path_rate
|
| 800 |
+
)
|
| 801 |
+
self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 802 |
+
self.mlp = MLP(width=width, drop_path_rate=drop_path_rate)
|
| 803 |
+
self.ln_2 = norm_layer(width, elementwise_affine=True, eps=1e-6)
|
| 804 |
+
|
| 805 |
+
def forward(self, x: torch.Tensor):
|
| 806 |
+
x = x + self.attn(self.ln_1(x))
|
| 807 |
+
x = x + self.mlp(self.ln_2(x))
|
| 808 |
+
return x
|
| 809 |
+
|
| 810 |
+
|
| 811 |
+
class Transformer(nn.Module):
|
| 812 |
+
def __init__(
|
| 813 |
+
self,
|
| 814 |
+
*,
|
| 815 |
+
width: int,
|
| 816 |
+
layers: int,
|
| 817 |
+
heads: int,
|
| 818 |
+
qkv_bias: bool = True,
|
| 819 |
+
norm_layer=ops.LayerNorm,
|
| 820 |
+
qk_norm: bool = False,
|
| 821 |
+
drop_path_rate: float = 0.0
|
| 822 |
+
):
|
| 823 |
+
super().__init__()
|
| 824 |
+
self.width = width
|
| 825 |
+
self.layers = layers
|
| 826 |
+
self.resblocks = nn.ModuleList(
|
| 827 |
+
[
|
| 828 |
+
ResidualAttentionBlock(
|
| 829 |
+
width=width,
|
| 830 |
+
heads=heads,
|
| 831 |
+
qkv_bias=qkv_bias,
|
| 832 |
+
norm_layer=norm_layer,
|
| 833 |
+
qk_norm=qk_norm,
|
| 834 |
+
drop_path_rate=drop_path_rate
|
| 835 |
+
)
|
| 836 |
+
for _ in range(layers)
|
| 837 |
+
]
|
| 838 |
+
)
|
| 839 |
+
|
| 840 |
+
def forward(self, x: torch.Tensor):
|
| 841 |
+
for block in self.resblocks:
|
| 842 |
+
x = block(x)
|
| 843 |
+
return x
|
| 844 |
+
|
| 845 |
+
|
| 846 |
+
class CrossAttentionDecoder(nn.Module):
|
| 847 |
+
|
| 848 |
+
def __init__(
|
| 849 |
+
self,
|
| 850 |
+
*,
|
| 851 |
+
out_channels: int,
|
| 852 |
+
fourier_embedder: FourierEmbedder,
|
| 853 |
+
width: int,
|
| 854 |
+
heads: int,
|
| 855 |
+
mlp_expand_ratio: int = 4,
|
| 856 |
+
downsample_ratio: int = 1,
|
| 857 |
+
enable_ln_post: bool = True,
|
| 858 |
+
qkv_bias: bool = True,
|
| 859 |
+
qk_norm: bool = False,
|
| 860 |
+
label_type: str = "binary"
|
| 861 |
+
):
|
| 862 |
+
super().__init__()
|
| 863 |
+
|
| 864 |
+
self.enable_ln_post = enable_ln_post
|
| 865 |
+
self.fourier_embedder = fourier_embedder
|
| 866 |
+
self.downsample_ratio = downsample_ratio
|
| 867 |
+
self.query_proj = ops.Linear(self.fourier_embedder.out_dim, width)
|
| 868 |
+
if self.downsample_ratio != 1:
|
| 869 |
+
self.latents_proj = ops.Linear(width * downsample_ratio, width)
|
| 870 |
+
if not self.enable_ln_post:
|
| 871 |
+
qk_norm = False
|
| 872 |
+
self.cross_attn_decoder = ResidualCrossAttentionBlock(
|
| 873 |
+
width=width,
|
| 874 |
+
mlp_expand_ratio=mlp_expand_ratio,
|
| 875 |
+
heads=heads,
|
| 876 |
+
qkv_bias=qkv_bias,
|
| 877 |
+
qk_norm=qk_norm
|
| 878 |
+
)
|
| 879 |
+
|
| 880 |
+
if self.enable_ln_post:
|
| 881 |
+
self.ln_post = ops.LayerNorm(width)
|
| 882 |
+
self.output_proj = ops.Linear(width, out_channels)
|
| 883 |
+
self.label_type = label_type
|
| 884 |
+
self.count = 0
|
| 885 |
+
|
| 886 |
+
def forward(self, queries=None, query_embeddings=None, latents=None):
|
| 887 |
+
if query_embeddings is None:
|
| 888 |
+
query_embeddings = self.query_proj(self.fourier_embedder(queries).to(latents.dtype))
|
| 889 |
+
self.count += query_embeddings.shape[1]
|
| 890 |
+
if self.downsample_ratio != 1:
|
| 891 |
+
latents = self.latents_proj(latents)
|
| 892 |
+
x = self.cross_attn_decoder(query_embeddings, latents)
|
| 893 |
+
if self.enable_ln_post:
|
| 894 |
+
x = self.ln_post(x)
|
| 895 |
+
occ = self.output_proj(x)
|
| 896 |
+
return occ
|
| 897 |
+
|
| 898 |
+
|
| 899 |
+
class ShapeVAE(nn.Module):
|
| 900 |
+
def __init__(
|
| 901 |
+
self,
|
| 902 |
+
*,
|
| 903 |
+
num_latents: int = 4096,
|
| 904 |
+
embed_dim: int = 64,
|
| 905 |
+
width: int = 1024,
|
| 906 |
+
heads: int = 16,
|
| 907 |
+
num_decoder_layers: int = 16,
|
| 908 |
+
num_encoder_layers: int = 8,
|
| 909 |
+
pc_size: int = 81920,
|
| 910 |
+
pc_sharpedge_size: int = 0,
|
| 911 |
+
point_feats: int = 4,
|
| 912 |
+
downsample_ratio: int = 20,
|
| 913 |
+
geo_decoder_downsample_ratio: int = 1,
|
| 914 |
+
geo_decoder_mlp_expand_ratio: int = 4,
|
| 915 |
+
geo_decoder_ln_post: bool = True,
|
| 916 |
+
num_freqs: int = 8,
|
| 917 |
+
qkv_bias: bool = False,
|
| 918 |
+
qk_norm: bool = True,
|
| 919 |
+
drop_path_rate: float = 0.0,
|
| 920 |
+
include_pi: bool = False,
|
| 921 |
+
scale_factor: float = 1.0039506158752403,
|
| 922 |
+
label_type: str = "binary",
|
| 923 |
+
):
|
| 924 |
+
super().__init__()
|
| 925 |
+
self.geo_decoder_ln_post = geo_decoder_ln_post
|
| 926 |
+
|
| 927 |
+
self.fourier_embedder = FourierEmbedder(num_freqs=num_freqs, include_pi=include_pi)
|
| 928 |
+
|
| 929 |
+
self.encoder = PointCrossAttention(layers = num_encoder_layers,
|
| 930 |
+
num_latents = num_latents,
|
| 931 |
+
downsample_ratio = downsample_ratio,
|
| 932 |
+
heads = heads,
|
| 933 |
+
pc_size = pc_size,
|
| 934 |
+
width = width,
|
| 935 |
+
point_feats = point_feats,
|
| 936 |
+
fourier_embedder = self.fourier_embedder,
|
| 937 |
+
pc_sharpedge_size = pc_sharpedge_size)
|
| 938 |
+
|
| 939 |
+
self.post_kl = ops.Linear(embed_dim, width)
|
| 940 |
+
|
| 941 |
+
self.transformer = Transformer(
|
| 942 |
+
width=width,
|
| 943 |
+
layers=num_decoder_layers,
|
| 944 |
+
heads=heads,
|
| 945 |
+
qkv_bias=qkv_bias,
|
| 946 |
+
qk_norm=qk_norm,
|
| 947 |
+
drop_path_rate=drop_path_rate
|
| 948 |
+
)
|
| 949 |
+
|
| 950 |
+
self.geo_decoder = CrossAttentionDecoder(
|
| 951 |
+
fourier_embedder=self.fourier_embedder,
|
| 952 |
+
out_channels=1,
|
| 953 |
+
mlp_expand_ratio=geo_decoder_mlp_expand_ratio,
|
| 954 |
+
downsample_ratio=geo_decoder_downsample_ratio,
|
| 955 |
+
enable_ln_post=self.geo_decoder_ln_post,
|
| 956 |
+
width=width // geo_decoder_downsample_ratio,
|
| 957 |
+
heads=heads // geo_decoder_downsample_ratio,
|
| 958 |
+
qkv_bias=qkv_bias,
|
| 959 |
+
qk_norm=qk_norm,
|
| 960 |
+
label_type=label_type,
|
| 961 |
+
)
|
| 962 |
+
|
| 963 |
+
self.volume_decoder = VanillaVolumeDecoder()
|
| 964 |
+
self.scale_factor = scale_factor
|
| 965 |
+
|
| 966 |
+
def decode(self, latents, **kwargs):
|
| 967 |
+
latents = self.post_kl(latents.movedim(-2, -1))
|
| 968 |
+
latents = self.transformer(latents)
|
| 969 |
+
|
| 970 |
+
bounds = kwargs.get("bounds", 1.01)
|
| 971 |
+
num_chunks = kwargs.get("num_chunks", 8000)
|
| 972 |
+
octree_resolution = kwargs.get("octree_resolution", 256)
|
| 973 |
+
enable_pbar = kwargs.get("enable_pbar", True)
|
| 974 |
+
|
| 975 |
+
grid_logits = self.volume_decoder(latents, self.geo_decoder, bounds=bounds, num_chunks=num_chunks, octree_resolution=octree_resolution, enable_pbar=enable_pbar)
|
| 976 |
+
return grid_logits.movedim(-2, -1)
|
| 977 |
+
|
| 978 |
+
def encode(self, surface):
|
| 979 |
+
|
| 980 |
+
pc, feats = surface[:, :, :3], surface[:, :, 3:]
|
| 981 |
+
latents = self.encoder(pc, feats)
|
| 982 |
+
|
| 983 |
+
moments = self.pre_kl(latents)
|
| 984 |
+
posterior = DiagonalGaussianDistribution(moments, feature_dim = -1)
|
| 985 |
+
|
| 986 |
+
latents = posterior.sample()
|
| 987 |
+
|
| 988 |
+
return latents
|
ComfyUI/comfy/ldm/hunyuan3dv2_1/__pycache__/hunyuandit.cpython-312.pyc
ADDED
|
Binary file (30.5 kB). View file
|
|
|
ComfyUI/comfy/ldm/hunyuan3dv2_1/hunyuandit.py
ADDED
|
@@ -0,0 +1,668 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 6 |
+
import comfy.model_management
|
| 7 |
+
|
| 8 |
+
class GELU(nn.Module):
|
| 9 |
+
|
| 10 |
+
def __init__(self, dim_in: int, dim_out: int, operations, device, dtype):
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.proj = operations.Linear(dim_in, dim_out, device = device, dtype = dtype)
|
| 13 |
+
|
| 14 |
+
def gelu(self, gate: torch.Tensor) -> torch.Tensor:
|
| 15 |
+
|
| 16 |
+
if gate.device.type == "mps":
|
| 17 |
+
return F.gelu(gate.to(dtype = torch.float32)).to(dtype = gate.dtype)
|
| 18 |
+
|
| 19 |
+
return F.gelu(gate)
|
| 20 |
+
|
| 21 |
+
def forward(self, hidden_states):
|
| 22 |
+
|
| 23 |
+
hidden_states = self.proj(hidden_states)
|
| 24 |
+
hidden_states = self.gelu(hidden_states)
|
| 25 |
+
|
| 26 |
+
return hidden_states
|
| 27 |
+
|
| 28 |
+
class FeedForward(nn.Module):
|
| 29 |
+
|
| 30 |
+
def __init__(self, dim: int, dim_out = None, mult: int = 4,
|
| 31 |
+
dropout: float = 0.0, inner_dim = None, operations = None, device = None, dtype = None):
|
| 32 |
+
|
| 33 |
+
super().__init__()
|
| 34 |
+
if inner_dim is None:
|
| 35 |
+
inner_dim = int(dim * mult)
|
| 36 |
+
|
| 37 |
+
dim_out = dim_out if dim_out is not None else dim
|
| 38 |
+
|
| 39 |
+
act_fn = GELU(dim, inner_dim, operations = operations, device = device, dtype = dtype)
|
| 40 |
+
|
| 41 |
+
self.net = nn.ModuleList([])
|
| 42 |
+
self.net.append(act_fn)
|
| 43 |
+
|
| 44 |
+
self.net.append(nn.Dropout(dropout))
|
| 45 |
+
self.net.append(operations.Linear(inner_dim, dim_out, device = device, dtype = dtype))
|
| 46 |
+
|
| 47 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 48 |
+
for module in self.net:
|
| 49 |
+
hidden_states = module(hidden_states)
|
| 50 |
+
return hidden_states
|
| 51 |
+
|
| 52 |
+
class AddAuxLoss(torch.autograd.Function):
|
| 53 |
+
|
| 54 |
+
@staticmethod
|
| 55 |
+
def forward(ctx, x, loss):
|
| 56 |
+
# do nothing in forward (no computation)
|
| 57 |
+
ctx.requires_aux_loss = loss.requires_grad
|
| 58 |
+
ctx.dtype = loss.dtype
|
| 59 |
+
|
| 60 |
+
return x
|
| 61 |
+
|
| 62 |
+
@staticmethod
|
| 63 |
+
def backward(ctx, grad_output):
|
| 64 |
+
# add the aux loss gradients
|
| 65 |
+
grad_loss = None
|
| 66 |
+
# put the aux grad the same as the main grad loss
|
| 67 |
+
# aux grad contributes equally
|
| 68 |
+
if ctx.requires_aux_loss:
|
| 69 |
+
grad_loss = torch.ones(1, dtype = ctx.dtype, device = grad_output.device)
|
| 70 |
+
|
| 71 |
+
return grad_output, grad_loss
|
| 72 |
+
|
| 73 |
+
class MoEGate(nn.Module):
|
| 74 |
+
|
| 75 |
+
def __init__(self, embed_dim, num_experts=16, num_experts_per_tok=2, aux_loss_alpha=0.01, device = None, dtype = None):
|
| 76 |
+
|
| 77 |
+
super().__init__()
|
| 78 |
+
self.top_k = num_experts_per_tok
|
| 79 |
+
self.n_routed_experts = num_experts
|
| 80 |
+
|
| 81 |
+
self.alpha = aux_loss_alpha
|
| 82 |
+
|
| 83 |
+
self.gating_dim = embed_dim
|
| 84 |
+
self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim), device = device, dtype = dtype))
|
| 85 |
+
|
| 86 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 87 |
+
|
| 88 |
+
# flatten hidden states
|
| 89 |
+
hidden_states = hidden_states.view(-1, hidden_states.size(-1))
|
| 90 |
+
|
| 91 |
+
# get logits and pass it to softmax
|
| 92 |
+
logits = F.linear(hidden_states, comfy.model_management.cast_to(self.weight, dtype=hidden_states.dtype, device=hidden_states.device), bias = None)
|
| 93 |
+
scores = logits.softmax(dim = -1)
|
| 94 |
+
|
| 95 |
+
topk_weight, topk_idx = torch.topk(scores, k = self.top_k, dim = -1, sorted = False)
|
| 96 |
+
|
| 97 |
+
if self.training and self.alpha > 0.0:
|
| 98 |
+
scores_for_aux = scores
|
| 99 |
+
|
| 100 |
+
# used bincount instead of one hot encoding
|
| 101 |
+
counts = torch.bincount(topk_idx.view(-1), minlength = self.n_routed_experts).float()
|
| 102 |
+
ce = counts / topk_idx.numel() # normalized expert usage
|
| 103 |
+
|
| 104 |
+
# mean expert score
|
| 105 |
+
Pi = scores_for_aux.mean(0)
|
| 106 |
+
|
| 107 |
+
# expert balance loss
|
| 108 |
+
aux_loss = (Pi * ce * self.n_routed_experts).sum() * self.alpha
|
| 109 |
+
else:
|
| 110 |
+
aux_loss = None
|
| 111 |
+
|
| 112 |
+
return topk_idx, topk_weight, aux_loss
|
| 113 |
+
|
| 114 |
+
class MoEBlock(nn.Module):
|
| 115 |
+
def __init__(self, dim, num_experts: int = 6, moe_top_k: int = 2, dropout: float = 0.0,
|
| 116 |
+
ff_inner_dim: int = None, operations = None, device = None, dtype = None):
|
| 117 |
+
super().__init__()
|
| 118 |
+
|
| 119 |
+
self.moe_top_k = moe_top_k
|
| 120 |
+
self.num_experts = num_experts
|
| 121 |
+
|
| 122 |
+
self.experts = nn.ModuleList([
|
| 123 |
+
FeedForward(dim, dropout = dropout, inner_dim = ff_inner_dim, operations = operations, device = device, dtype = dtype)
|
| 124 |
+
for _ in range(num_experts)
|
| 125 |
+
])
|
| 126 |
+
|
| 127 |
+
self.gate = MoEGate(dim, num_experts = num_experts, num_experts_per_tok = moe_top_k, device = device, dtype = dtype)
|
| 128 |
+
self.shared_experts = FeedForward(dim, dropout = dropout, inner_dim = ff_inner_dim, operations = operations, device = device, dtype = dtype)
|
| 129 |
+
|
| 130 |
+
def forward(self, hidden_states) -> torch.Tensor:
|
| 131 |
+
|
| 132 |
+
identity = hidden_states
|
| 133 |
+
orig_shape = hidden_states.shape
|
| 134 |
+
topk_idx, topk_weight, aux_loss = self.gate(hidden_states)
|
| 135 |
+
|
| 136 |
+
hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
|
| 137 |
+
flat_topk_idx = topk_idx.view(-1)
|
| 138 |
+
|
| 139 |
+
if self.training:
|
| 140 |
+
|
| 141 |
+
hidden_states = hidden_states.repeat_interleave(self.moe_top_k, dim = 0)
|
| 142 |
+
y = torch.empty_like(hidden_states, dtype = hidden_states.dtype)
|
| 143 |
+
|
| 144 |
+
for i, expert in enumerate(self.experts):
|
| 145 |
+
tmp = expert(hidden_states[flat_topk_idx == i])
|
| 146 |
+
y[flat_topk_idx == i] = tmp.to(hidden_states.dtype)
|
| 147 |
+
|
| 148 |
+
y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim = 1)
|
| 149 |
+
y = y.view(*orig_shape)
|
| 150 |
+
|
| 151 |
+
y = AddAuxLoss.apply(y, aux_loss)
|
| 152 |
+
else:
|
| 153 |
+
y = self.moe_infer(hidden_states, flat_expert_indices = flat_topk_idx,flat_expert_weights = topk_weight.view(-1, 1)).view(*orig_shape)
|
| 154 |
+
|
| 155 |
+
y = y + self.shared_experts(identity)
|
| 156 |
+
|
| 157 |
+
return y
|
| 158 |
+
|
| 159 |
+
@torch.no_grad()
|
| 160 |
+
def moe_infer(self, x, flat_expert_indices, flat_expert_weights):
|
| 161 |
+
|
| 162 |
+
expert_cache = torch.zeros_like(x)
|
| 163 |
+
idxs = flat_expert_indices.argsort()
|
| 164 |
+
|
| 165 |
+
# no need for .numpy().cpu() here
|
| 166 |
+
tokens_per_expert = flat_expert_indices.bincount().cumsum(0)
|
| 167 |
+
token_idxs = idxs // self.moe_top_k
|
| 168 |
+
|
| 169 |
+
for i, end_idx in enumerate(tokens_per_expert):
|
| 170 |
+
|
| 171 |
+
start_idx = 0 if i == 0 else tokens_per_expert[i-1]
|
| 172 |
+
|
| 173 |
+
if start_idx == end_idx:
|
| 174 |
+
continue
|
| 175 |
+
|
| 176 |
+
expert = self.experts[i]
|
| 177 |
+
exp_token_idx = token_idxs[start_idx:end_idx]
|
| 178 |
+
|
| 179 |
+
expert_tokens = x[exp_token_idx]
|
| 180 |
+
expert_out = expert(expert_tokens)
|
| 181 |
+
|
| 182 |
+
expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]])
|
| 183 |
+
|
| 184 |
+
# use index_add_ with a 1-D index tensor directly avoids building a large [N, D] index map and extra memcopy required by scatter_reduce_
|
| 185 |
+
# + avoid dtype conversion
|
| 186 |
+
expert_cache.index_add_(0, exp_token_idx, expert_out)
|
| 187 |
+
|
| 188 |
+
return expert_cache
|
| 189 |
+
|
| 190 |
+
class Timesteps(nn.Module):
|
| 191 |
+
def __init__(self, num_channels: int, downscale_freq_shift: float = 0.0,
|
| 192 |
+
scale: float = 1.0, max_period: int = 10000):
|
| 193 |
+
super().__init__()
|
| 194 |
+
|
| 195 |
+
self.num_channels = num_channels
|
| 196 |
+
half_dim = num_channels // 2
|
| 197 |
+
|
| 198 |
+
# precompute the “inv_freq” vector once
|
| 199 |
+
exponent = -math.log(max_period) * torch.arange(
|
| 200 |
+
half_dim, dtype=torch.float32
|
| 201 |
+
) / (half_dim - downscale_freq_shift)
|
| 202 |
+
|
| 203 |
+
inv_freq = torch.exp(exponent)
|
| 204 |
+
|
| 205 |
+
# pad
|
| 206 |
+
if num_channels % 2 == 1:
|
| 207 |
+
# we’ll pad a zero at the end of the cos-half
|
| 208 |
+
inv_freq = torch.cat([inv_freq, inv_freq.new_zeros(1)])
|
| 209 |
+
|
| 210 |
+
# register to buffer so it moves with the device
|
| 211 |
+
self.register_buffer("inv_freq", inv_freq, persistent = False)
|
| 212 |
+
self.scale = scale
|
| 213 |
+
|
| 214 |
+
def forward(self, timesteps: torch.Tensor):
|
| 215 |
+
|
| 216 |
+
x = timesteps.float().unsqueeze(1) * self.inv_freq.to(timesteps.device).unsqueeze(0)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
# fused CUDA kernels for sin and cos
|
| 220 |
+
sin_emb = x.sin()
|
| 221 |
+
cos_emb = x.cos()
|
| 222 |
+
|
| 223 |
+
emb = torch.cat([sin_emb, cos_emb], dim = 1)
|
| 224 |
+
|
| 225 |
+
# scale factor
|
| 226 |
+
if self.scale != 1.0:
|
| 227 |
+
emb = emb * self.scale
|
| 228 |
+
|
| 229 |
+
# If we padded inv_freq for odd, emb is already wide enough; otherwise:
|
| 230 |
+
if emb.shape[1] > self.num_channels:
|
| 231 |
+
emb = emb[:, :self.num_channels]
|
| 232 |
+
|
| 233 |
+
return emb
|
| 234 |
+
|
| 235 |
+
class TimestepEmbedder(nn.Module):
|
| 236 |
+
def __init__(self, hidden_size, frequency_embedding_size = 256, cond_proj_dim = None, operations = None, device = None, dtype = None):
|
| 237 |
+
super().__init__()
|
| 238 |
+
|
| 239 |
+
self.mlp = nn.Sequential(
|
| 240 |
+
operations.Linear(hidden_size, frequency_embedding_size, bias=True, device = device, dtype = dtype),
|
| 241 |
+
nn.GELU(),
|
| 242 |
+
operations.Linear(frequency_embedding_size, hidden_size, bias=True, device = device, dtype = dtype),
|
| 243 |
+
)
|
| 244 |
+
self.frequency_embedding_size = frequency_embedding_size
|
| 245 |
+
|
| 246 |
+
if cond_proj_dim is not None:
|
| 247 |
+
self.cond_proj = operations.Linear(cond_proj_dim, frequency_embedding_size, bias=False, device = device, dtype = dtype)
|
| 248 |
+
|
| 249 |
+
self.time_embed = Timesteps(hidden_size)
|
| 250 |
+
|
| 251 |
+
def forward(self, timesteps, condition):
|
| 252 |
+
|
| 253 |
+
timestep_embed = self.time_embed(timesteps).type(self.mlp[0].weight.dtype)
|
| 254 |
+
|
| 255 |
+
if condition is not None:
|
| 256 |
+
cond_embed = self.cond_proj(condition)
|
| 257 |
+
timestep_embed = timestep_embed + cond_embed
|
| 258 |
+
|
| 259 |
+
time_conditioned = self.mlp(timestep_embed)
|
| 260 |
+
|
| 261 |
+
# for broadcasting with image tokens
|
| 262 |
+
return time_conditioned.unsqueeze(1)
|
| 263 |
+
|
| 264 |
+
class MLP(nn.Module):
|
| 265 |
+
def __init__(self, *, width: int, operations = None, device = None, dtype = None):
|
| 266 |
+
super().__init__()
|
| 267 |
+
self.width = width
|
| 268 |
+
self.fc1 = operations.Linear(width, width * 4, device = device, dtype = dtype)
|
| 269 |
+
self.fc2 = operations.Linear(width * 4, width, device = device, dtype = dtype)
|
| 270 |
+
self.gelu = nn.GELU()
|
| 271 |
+
|
| 272 |
+
def forward(self, x):
|
| 273 |
+
return self.fc2(self.gelu(self.fc1(x)))
|
| 274 |
+
|
| 275 |
+
class CrossAttention(nn.Module):
|
| 276 |
+
def __init__(
|
| 277 |
+
self,
|
| 278 |
+
qdim,
|
| 279 |
+
kdim,
|
| 280 |
+
num_heads,
|
| 281 |
+
qkv_bias=True,
|
| 282 |
+
qk_norm=False,
|
| 283 |
+
norm_layer=nn.LayerNorm,
|
| 284 |
+
use_fp16: bool = False,
|
| 285 |
+
operations = None,
|
| 286 |
+
dtype = None,
|
| 287 |
+
device = None,
|
| 288 |
+
**kwargs,
|
| 289 |
+
):
|
| 290 |
+
super().__init__()
|
| 291 |
+
self.qdim = qdim
|
| 292 |
+
self.kdim = kdim
|
| 293 |
+
|
| 294 |
+
self.num_heads = num_heads
|
| 295 |
+
self.head_dim = self.qdim // num_heads
|
| 296 |
+
|
| 297 |
+
self.scale = self.head_dim ** -0.5
|
| 298 |
+
|
| 299 |
+
self.to_q = operations.Linear(qdim, qdim, bias=qkv_bias, device = device, dtype = dtype)
|
| 300 |
+
self.to_k = operations.Linear(kdim, qdim, bias=qkv_bias, device = device, dtype = dtype)
|
| 301 |
+
self.to_v = operations.Linear(kdim, qdim, bias=qkv_bias, device = device, dtype = dtype)
|
| 302 |
+
|
| 303 |
+
if use_fp16:
|
| 304 |
+
eps = 1.0 / 65504
|
| 305 |
+
else:
|
| 306 |
+
eps = 1e-6
|
| 307 |
+
|
| 308 |
+
if norm_layer == nn.LayerNorm:
|
| 309 |
+
norm_layer = operations.LayerNorm
|
| 310 |
+
else:
|
| 311 |
+
norm_layer = operations.RMSNorm
|
| 312 |
+
|
| 313 |
+
self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
|
| 314 |
+
self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
|
| 315 |
+
self.out_proj = operations.Linear(qdim, qdim, bias=True, device = device, dtype = dtype)
|
| 316 |
+
|
| 317 |
+
def forward(self, x, y):
|
| 318 |
+
|
| 319 |
+
b, s1, _ = x.shape
|
| 320 |
+
_, s2, _ = y.shape
|
| 321 |
+
|
| 322 |
+
y = y.to(next(self.to_k.parameters()).dtype)
|
| 323 |
+
|
| 324 |
+
q = self.to_q(x)
|
| 325 |
+
k = self.to_k(y)
|
| 326 |
+
v = self.to_v(y)
|
| 327 |
+
|
| 328 |
+
kv = torch.cat((k, v), dim=-1)
|
| 329 |
+
split_size = kv.shape[-1] // self.num_heads // 2
|
| 330 |
+
|
| 331 |
+
kv = kv.view(b, -1, self.num_heads, split_size * 2)
|
| 332 |
+
k, v = torch.split(kv, split_size, dim=-1)
|
| 333 |
+
|
| 334 |
+
q = q.view(b, s1, self.num_heads, self.head_dim)
|
| 335 |
+
k = k.view(b, s2, self.num_heads, self.head_dim)
|
| 336 |
+
v = v.reshape(b, s2, self.num_heads * self.head_dim)
|
| 337 |
+
|
| 338 |
+
q = self.q_norm(q)
|
| 339 |
+
k = self.k_norm(k)
|
| 340 |
+
|
| 341 |
+
x = optimized_attention(
|
| 342 |
+
q.reshape(b, s1, self.num_heads * self.head_dim),
|
| 343 |
+
k.reshape(b, s2, self.num_heads * self.head_dim),
|
| 344 |
+
v,
|
| 345 |
+
heads=self.num_heads,
|
| 346 |
+
low_precision_attention=False,
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
out = self.out_proj(x)
|
| 350 |
+
|
| 351 |
+
return out
|
| 352 |
+
|
| 353 |
+
class Attention(nn.Module):
|
| 354 |
+
|
| 355 |
+
def __init__(
|
| 356 |
+
self,
|
| 357 |
+
dim,
|
| 358 |
+
num_heads,
|
| 359 |
+
qkv_bias = True,
|
| 360 |
+
qk_norm = False,
|
| 361 |
+
norm_layer = nn.LayerNorm,
|
| 362 |
+
use_fp16: bool = False,
|
| 363 |
+
operations = None,
|
| 364 |
+
device = None,
|
| 365 |
+
dtype = None
|
| 366 |
+
):
|
| 367 |
+
super().__init__()
|
| 368 |
+
self.dim = dim
|
| 369 |
+
self.num_heads = num_heads
|
| 370 |
+
self.head_dim = self.dim // num_heads
|
| 371 |
+
self.scale = self.head_dim ** -0.5
|
| 372 |
+
|
| 373 |
+
self.to_q = operations.Linear(dim, dim, bias = qkv_bias, device = device, dtype = dtype)
|
| 374 |
+
self.to_k = operations.Linear(dim, dim, bias = qkv_bias, device = device, dtype = dtype)
|
| 375 |
+
self.to_v = operations.Linear(dim, dim, bias = qkv_bias, device = device, dtype = dtype)
|
| 376 |
+
|
| 377 |
+
if use_fp16:
|
| 378 |
+
eps = 1.0 / 65504
|
| 379 |
+
else:
|
| 380 |
+
eps = 1e-6
|
| 381 |
+
|
| 382 |
+
if norm_layer == nn.LayerNorm:
|
| 383 |
+
norm_layer = operations.LayerNorm
|
| 384 |
+
else:
|
| 385 |
+
norm_layer = operations.RMSNorm
|
| 386 |
+
|
| 387 |
+
self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
|
| 388 |
+
self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
|
| 389 |
+
self.out_proj = operations.Linear(dim, dim, device = device, dtype = dtype)
|
| 390 |
+
|
| 391 |
+
def forward(self, x):
|
| 392 |
+
B, N, _ = x.shape
|
| 393 |
+
|
| 394 |
+
query = self.to_q(x)
|
| 395 |
+
key = self.to_k(x)
|
| 396 |
+
value = self.to_v(x)
|
| 397 |
+
|
| 398 |
+
qkv_combined = torch.cat((query, key, value), dim=-1)
|
| 399 |
+
split_size = qkv_combined.shape[-1] // self.num_heads // 3
|
| 400 |
+
|
| 401 |
+
qkv = qkv_combined.view(B, -1, self.num_heads, split_size * 3)
|
| 402 |
+
query, key, value = torch.split(qkv, split_size, dim=-1)
|
| 403 |
+
|
| 404 |
+
query = query.reshape(B, N, self.num_heads, self.head_dim)
|
| 405 |
+
key = key.reshape(B, N, self.num_heads, self.head_dim)
|
| 406 |
+
value = value.reshape(B, N, self.num_heads * self.head_dim)
|
| 407 |
+
|
| 408 |
+
query = self.q_norm(query)
|
| 409 |
+
key = self.k_norm(key)
|
| 410 |
+
|
| 411 |
+
x = optimized_attention(
|
| 412 |
+
query.reshape(B, N, self.num_heads * self.head_dim),
|
| 413 |
+
key.reshape(B, N, self.num_heads * self.head_dim),
|
| 414 |
+
value,
|
| 415 |
+
heads=self.num_heads,
|
| 416 |
+
low_precision_attention=False,
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
x = self.out_proj(x)
|
| 420 |
+
return x
|
| 421 |
+
|
| 422 |
+
class HunYuanDiTBlock(nn.Module):
|
| 423 |
+
def __init__(
|
| 424 |
+
self,
|
| 425 |
+
hidden_size,
|
| 426 |
+
c_emb_size,
|
| 427 |
+
num_heads,
|
| 428 |
+
text_states_dim=1024,
|
| 429 |
+
qk_norm=False,
|
| 430 |
+
norm_layer=nn.LayerNorm,
|
| 431 |
+
qk_norm_layer=True,
|
| 432 |
+
qkv_bias=True,
|
| 433 |
+
skip_connection=True,
|
| 434 |
+
timested_modulate=False,
|
| 435 |
+
use_moe: bool = False,
|
| 436 |
+
num_experts: int = 8,
|
| 437 |
+
moe_top_k: int = 2,
|
| 438 |
+
use_fp16: bool = False,
|
| 439 |
+
operations = None,
|
| 440 |
+
device = None, dtype = None
|
| 441 |
+
):
|
| 442 |
+
super().__init__()
|
| 443 |
+
|
| 444 |
+
# eps can't be 1e-6 in fp16 mode because of numerical stability issues
|
| 445 |
+
if use_fp16:
|
| 446 |
+
eps = 1.0 / 65504
|
| 447 |
+
else:
|
| 448 |
+
eps = 1e-6
|
| 449 |
+
|
| 450 |
+
self.norm1 = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
|
| 451 |
+
|
| 452 |
+
self.attn1 = Attention(hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, qk_norm=qk_norm,
|
| 453 |
+
norm_layer=qk_norm_layer, use_fp16 = use_fp16, device = device, dtype = dtype, operations = operations)
|
| 454 |
+
|
| 455 |
+
self.norm2 = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
|
| 456 |
+
|
| 457 |
+
self.timested_modulate = timested_modulate
|
| 458 |
+
if self.timested_modulate:
|
| 459 |
+
self.default_modulation = nn.Sequential(
|
| 460 |
+
nn.SiLU(),
|
| 461 |
+
operations.Linear(c_emb_size, hidden_size, bias=True, device = device, dtype = dtype)
|
| 462 |
+
)
|
| 463 |
+
|
| 464 |
+
self.attn2 = CrossAttention(hidden_size, text_states_dim, num_heads=num_heads, qkv_bias=qkv_bias,
|
| 465 |
+
qk_norm=qk_norm, norm_layer=qk_norm_layer, use_fp16 = use_fp16,
|
| 466 |
+
device = device, dtype = dtype, operations = operations)
|
| 467 |
+
|
| 468 |
+
self.norm3 = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
|
| 469 |
+
|
| 470 |
+
if skip_connection:
|
| 471 |
+
self.skip_norm = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
|
| 472 |
+
self.skip_linear = operations.Linear(2 * hidden_size, hidden_size, device = device, dtype = dtype)
|
| 473 |
+
else:
|
| 474 |
+
self.skip_linear = None
|
| 475 |
+
|
| 476 |
+
self.use_moe = use_moe
|
| 477 |
+
|
| 478 |
+
if self.use_moe:
|
| 479 |
+
self.moe = MoEBlock(
|
| 480 |
+
hidden_size,
|
| 481 |
+
num_experts = num_experts,
|
| 482 |
+
moe_top_k = moe_top_k,
|
| 483 |
+
dropout = 0.0,
|
| 484 |
+
ff_inner_dim = int(hidden_size * 4.0),
|
| 485 |
+
device = device, dtype = dtype,
|
| 486 |
+
operations = operations
|
| 487 |
+
)
|
| 488 |
+
else:
|
| 489 |
+
self.mlp = MLP(width=hidden_size, operations=operations, device = device, dtype = dtype)
|
| 490 |
+
|
| 491 |
+
def forward(self, hidden_states, conditioning=None, text_states=None, skip_tensor=None):
|
| 492 |
+
|
| 493 |
+
if self.skip_linear is not None:
|
| 494 |
+
combined = torch.cat([skip_tensor, hidden_states], dim=-1)
|
| 495 |
+
hidden_states = self.skip_linear(combined)
|
| 496 |
+
hidden_states = self.skip_norm(hidden_states)
|
| 497 |
+
|
| 498 |
+
# self attention
|
| 499 |
+
if self.timested_modulate:
|
| 500 |
+
modulation_shift = self.default_modulation(conditioning).unsqueeze(dim=1)
|
| 501 |
+
hidden_states = hidden_states + modulation_shift
|
| 502 |
+
|
| 503 |
+
self_attn_out = self.attn1(self.norm1(hidden_states))
|
| 504 |
+
hidden_states = hidden_states + self_attn_out
|
| 505 |
+
|
| 506 |
+
# cross attention
|
| 507 |
+
hidden_states = hidden_states + self.attn2(self.norm2(hidden_states), text_states)
|
| 508 |
+
|
| 509 |
+
# MLP Layer
|
| 510 |
+
mlp_input = self.norm3(hidden_states)
|
| 511 |
+
|
| 512 |
+
if self.use_moe:
|
| 513 |
+
hidden_states = hidden_states + self.moe(mlp_input)
|
| 514 |
+
else:
|
| 515 |
+
hidden_states = hidden_states + self.mlp(mlp_input)
|
| 516 |
+
|
| 517 |
+
return hidden_states
|
| 518 |
+
|
| 519 |
+
class FinalLayer(nn.Module):
|
| 520 |
+
|
| 521 |
+
def __init__(self, final_hidden_size, out_channels, operations, use_fp16: bool = False, device = None, dtype = None):
|
| 522 |
+
super().__init__()
|
| 523 |
+
|
| 524 |
+
if use_fp16:
|
| 525 |
+
eps = 1.0 / 65504
|
| 526 |
+
else:
|
| 527 |
+
eps = 1e-6
|
| 528 |
+
|
| 529 |
+
self.norm_final = operations.LayerNorm(final_hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
|
| 530 |
+
self.linear = operations.Linear(final_hidden_size, out_channels, bias = True, device = device, dtype = dtype)
|
| 531 |
+
|
| 532 |
+
def forward(self, x):
|
| 533 |
+
x = self.norm_final(x)
|
| 534 |
+
x = x[:, 1:]
|
| 535 |
+
x = self.linear(x)
|
| 536 |
+
return x
|
| 537 |
+
|
| 538 |
+
class HunYuanDiTPlain(nn.Module):
|
| 539 |
+
|
| 540 |
+
# init with the defaults values from https://huggingface.co/tencent/Hunyuan3D-2.1/blob/main/hunyuan3d-dit-v2-1/config.yaml
|
| 541 |
+
def __init__(
|
| 542 |
+
self,
|
| 543 |
+
in_channels: int = 64,
|
| 544 |
+
hidden_size: int = 2048,
|
| 545 |
+
context_dim: int = 1024,
|
| 546 |
+
depth: int = 21,
|
| 547 |
+
num_heads: int = 16,
|
| 548 |
+
qk_norm: bool = True,
|
| 549 |
+
qkv_bias: bool = False,
|
| 550 |
+
num_moe_layers: int = 6,
|
| 551 |
+
guidance_cond_proj_dim = 2048,
|
| 552 |
+
norm_type = 'layer',
|
| 553 |
+
num_experts: int = 8,
|
| 554 |
+
moe_top_k: int = 2,
|
| 555 |
+
use_fp16: bool = False,
|
| 556 |
+
dtype = None,
|
| 557 |
+
device = None,
|
| 558 |
+
operations = None,
|
| 559 |
+
**kwargs
|
| 560 |
+
):
|
| 561 |
+
|
| 562 |
+
self.dtype = dtype
|
| 563 |
+
|
| 564 |
+
super().__init__()
|
| 565 |
+
|
| 566 |
+
self.depth = depth
|
| 567 |
+
|
| 568 |
+
self.in_channels = in_channels
|
| 569 |
+
self.out_channels = in_channels
|
| 570 |
+
|
| 571 |
+
self.num_heads = num_heads
|
| 572 |
+
self.hidden_size = hidden_size
|
| 573 |
+
|
| 574 |
+
norm = operations.LayerNorm if norm_type == 'layer' else operations.RMSNorm
|
| 575 |
+
qk_norm = operations.RMSNorm
|
| 576 |
+
|
| 577 |
+
self.context_dim = context_dim
|
| 578 |
+
self.guidance_cond_proj_dim = guidance_cond_proj_dim
|
| 579 |
+
|
| 580 |
+
self.x_embedder = operations.Linear(in_channels, hidden_size, bias = True, device = device, dtype = dtype)
|
| 581 |
+
self.t_embedder = TimestepEmbedder(hidden_size, hidden_size * 4, cond_proj_dim = guidance_cond_proj_dim, device = device, dtype = dtype, operations = operations)
|
| 582 |
+
|
| 583 |
+
|
| 584 |
+
# HUnYuanDiT Blocks
|
| 585 |
+
self.blocks = nn.ModuleList([
|
| 586 |
+
HunYuanDiTBlock(hidden_size=hidden_size,
|
| 587 |
+
c_emb_size=hidden_size,
|
| 588 |
+
num_heads=num_heads,
|
| 589 |
+
text_states_dim=context_dim,
|
| 590 |
+
qk_norm=qk_norm,
|
| 591 |
+
norm_layer = norm,
|
| 592 |
+
qk_norm_layer = qk_norm,
|
| 593 |
+
skip_connection=layer > depth // 2,
|
| 594 |
+
qkv_bias=qkv_bias,
|
| 595 |
+
use_moe=True if depth - layer <= num_moe_layers else False,
|
| 596 |
+
num_experts=num_experts,
|
| 597 |
+
moe_top_k=moe_top_k,
|
| 598 |
+
use_fp16 = use_fp16,
|
| 599 |
+
device = device, dtype = dtype, operations = operations)
|
| 600 |
+
for layer in range(depth)
|
| 601 |
+
])
|
| 602 |
+
|
| 603 |
+
self.depth = depth
|
| 604 |
+
|
| 605 |
+
self.final_layer = FinalLayer(hidden_size, self.out_channels, use_fp16 = use_fp16, operations = operations, device = device, dtype = dtype)
|
| 606 |
+
|
| 607 |
+
def forward(self, x, t, context, transformer_options = {}, **kwargs):
|
| 608 |
+
|
| 609 |
+
x = x.movedim(-1, -2)
|
| 610 |
+
|
| 611 |
+
swap_cfg_halves = context.shape[0] >= 2
|
| 612 |
+
|
| 613 |
+
if swap_cfg_halves:
|
| 614 |
+
first_half, second_half = context.chunk(2, dim = 0)
|
| 615 |
+
context = torch.cat([second_half, first_half], dim = 0)
|
| 616 |
+
|
| 617 |
+
main_condition = context
|
| 618 |
+
|
| 619 |
+
t = 1.0 - t
|
| 620 |
+
|
| 621 |
+
time_embedded = self.t_embedder(t, condition = kwargs.get('guidance_cond'))
|
| 622 |
+
|
| 623 |
+
x = x.to(dtype = next(self.x_embedder.parameters()).dtype)
|
| 624 |
+
x_embedded = self.x_embedder(x)
|
| 625 |
+
|
| 626 |
+
combined = torch.cat([time_embedded, x_embedded], dim=1)
|
| 627 |
+
|
| 628 |
+
def block_wrap(args):
|
| 629 |
+
return block(
|
| 630 |
+
args["x"],
|
| 631 |
+
args["t"],
|
| 632 |
+
args["cond"],
|
| 633 |
+
skip_tensor=args.get("skip"),)
|
| 634 |
+
|
| 635 |
+
skip_stack = []
|
| 636 |
+
patches_replace = transformer_options.get("patches_replace", {})
|
| 637 |
+
blocks_replace = patches_replace.get("dit", {})
|
| 638 |
+
for idx, block in enumerate(self.blocks):
|
| 639 |
+
if idx <= self.depth // 2:
|
| 640 |
+
skip_input = None
|
| 641 |
+
else:
|
| 642 |
+
skip_input = skip_stack.pop()
|
| 643 |
+
|
| 644 |
+
if ("block", idx) in blocks_replace:
|
| 645 |
+
|
| 646 |
+
combined = blocks_replace[("block", idx)](
|
| 647 |
+
{
|
| 648 |
+
"x": combined,
|
| 649 |
+
"t": time_embedded,
|
| 650 |
+
"cond": main_condition,
|
| 651 |
+
"skip": skip_input,
|
| 652 |
+
},
|
| 653 |
+
{"original_block": block_wrap},
|
| 654 |
+
)
|
| 655 |
+
else:
|
| 656 |
+
combined = block(combined, time_embedded, main_condition, skip_tensor=skip_input)
|
| 657 |
+
|
| 658 |
+
if idx < self.depth // 2:
|
| 659 |
+
skip_stack.append(combined)
|
| 660 |
+
|
| 661 |
+
output = self.final_layer(combined)
|
| 662 |
+
output = output.movedim(-2, -1) * (-1.0)
|
| 663 |
+
|
| 664 |
+
if swap_cfg_halves:
|
| 665 |
+
first_half, second_half = output.chunk(2, dim = 0)
|
| 666 |
+
output = torch.cat([second_half, first_half], dim = 0)
|
| 667 |
+
|
| 668 |
+
return output
|
ComfyUI/comfy/ldm/hunyuan_video/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (29.5 kB). View file
|
|
|
ComfyUI/comfy/ldm/hunyuan_video/__pycache__/upsampler.cpython-312.pyc
ADDED
|
Binary file (7.67 kB). View file
|
|
|
ComfyUI/comfy/ldm/hunyuan_video/__pycache__/vae.cpython-312.pyc
ADDED
|
Binary file (9.91 kB). View file
|
|
|
ComfyUI/comfy/ldm/hunyuan_video/__pycache__/vae_refiner.cpython-312.pyc
ADDED
|
Binary file (17.6 kB). View file
|
|
|
ComfyUI/comfy/ldm/hunyuan_video/model.py
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Based on Flux code because of weird hunyuan video code license.
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import comfy.patcher_extension
|
| 5 |
+
import comfy.ldm.flux.layers
|
| 6 |
+
import comfy.ldm.modules.diffusionmodules.mmdit
|
| 7 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 8 |
+
|
| 9 |
+
from dataclasses import dataclass
|
| 10 |
+
from einops import repeat
|
| 11 |
+
|
| 12 |
+
from torch import Tensor, nn
|
| 13 |
+
|
| 14 |
+
from comfy.ldm.flux.layers import (
|
| 15 |
+
DoubleStreamBlock,
|
| 16 |
+
EmbedND,
|
| 17 |
+
LastLayer,
|
| 18 |
+
MLPEmbedder,
|
| 19 |
+
SingleStreamBlock,
|
| 20 |
+
timestep_embedding
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
import comfy.ldm.common_dit
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@dataclass
|
| 27 |
+
class HunyuanVideoParams:
|
| 28 |
+
in_channels: int
|
| 29 |
+
out_channels: int
|
| 30 |
+
vec_in_dim: int
|
| 31 |
+
context_in_dim: int
|
| 32 |
+
hidden_size: int
|
| 33 |
+
mlp_ratio: float
|
| 34 |
+
num_heads: int
|
| 35 |
+
depth: int
|
| 36 |
+
depth_single_blocks: int
|
| 37 |
+
axes_dim: list
|
| 38 |
+
theta: int
|
| 39 |
+
patch_size: list
|
| 40 |
+
qkv_bias: bool
|
| 41 |
+
guidance_embed: bool
|
| 42 |
+
byt5: bool
|
| 43 |
+
meanflow: bool
|
| 44 |
+
use_cond_type_embedding: bool
|
| 45 |
+
vision_in_dim: int
|
| 46 |
+
meanflow_sum: bool
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class SelfAttentionRef(nn.Module):
|
| 50 |
+
def __init__(self, dim: int, qkv_bias: bool = False, dtype=None, device=None, operations=None):
|
| 51 |
+
super().__init__()
|
| 52 |
+
self.qkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device)
|
| 53 |
+
self.proj = operations.Linear(dim, dim, dtype=dtype, device=device)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class TokenRefinerBlock(nn.Module):
|
| 57 |
+
def __init__(
|
| 58 |
+
self,
|
| 59 |
+
hidden_size,
|
| 60 |
+
heads,
|
| 61 |
+
dtype=None,
|
| 62 |
+
device=None,
|
| 63 |
+
operations=None
|
| 64 |
+
):
|
| 65 |
+
super().__init__()
|
| 66 |
+
self.heads = heads
|
| 67 |
+
mlp_hidden_dim = hidden_size * 4
|
| 68 |
+
|
| 69 |
+
self.adaLN_modulation = nn.Sequential(
|
| 70 |
+
nn.SiLU(),
|
| 71 |
+
operations.Linear(hidden_size, 2 * hidden_size, bias=True, dtype=dtype, device=device),
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
self.norm1 = operations.LayerNorm(hidden_size, elementwise_affine=True, eps=1e-6, dtype=dtype, device=device)
|
| 75 |
+
self.self_attn = SelfAttentionRef(hidden_size, True, dtype=dtype, device=device, operations=operations)
|
| 76 |
+
|
| 77 |
+
self.norm2 = operations.LayerNorm(hidden_size, elementwise_affine=True, eps=1e-6, dtype=dtype, device=device)
|
| 78 |
+
|
| 79 |
+
self.mlp = nn.Sequential(
|
| 80 |
+
operations.Linear(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype, device=device),
|
| 81 |
+
nn.SiLU(),
|
| 82 |
+
operations.Linear(mlp_hidden_dim, hidden_size, bias=True, dtype=dtype, device=device),
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
def forward(self, x, c, mask, transformer_options={}):
|
| 86 |
+
mod1, mod2 = self.adaLN_modulation(c).chunk(2, dim=1)
|
| 87 |
+
|
| 88 |
+
norm_x = self.norm1(x)
|
| 89 |
+
qkv = self.self_attn.qkv(norm_x)
|
| 90 |
+
q, k, v = qkv.reshape(qkv.shape[0], qkv.shape[1], 3, self.heads, -1).permute(2, 0, 3, 1, 4)
|
| 91 |
+
attn = optimized_attention(q, k, v, self.heads, mask=mask, skip_reshape=True, transformer_options=transformer_options)
|
| 92 |
+
|
| 93 |
+
x = x + self.self_attn.proj(attn) * mod1.unsqueeze(1)
|
| 94 |
+
x = x + self.mlp(self.norm2(x)) * mod2.unsqueeze(1)
|
| 95 |
+
return x
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class IndividualTokenRefiner(nn.Module):
|
| 99 |
+
def __init__(
|
| 100 |
+
self,
|
| 101 |
+
hidden_size,
|
| 102 |
+
heads,
|
| 103 |
+
num_blocks,
|
| 104 |
+
dtype=None,
|
| 105 |
+
device=None,
|
| 106 |
+
operations=None
|
| 107 |
+
):
|
| 108 |
+
super().__init__()
|
| 109 |
+
self.blocks = nn.ModuleList(
|
| 110 |
+
[
|
| 111 |
+
TokenRefinerBlock(
|
| 112 |
+
hidden_size=hidden_size,
|
| 113 |
+
heads=heads,
|
| 114 |
+
dtype=dtype,
|
| 115 |
+
device=device,
|
| 116 |
+
operations=operations
|
| 117 |
+
)
|
| 118 |
+
for _ in range(num_blocks)
|
| 119 |
+
]
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
def forward(self, x, c, mask, transformer_options={}):
|
| 123 |
+
m = None
|
| 124 |
+
if mask is not None:
|
| 125 |
+
m = mask.view(mask.shape[0], 1, 1, mask.shape[1]).repeat(1, 1, mask.shape[1], 1)
|
| 126 |
+
m = m + m.transpose(2, 3)
|
| 127 |
+
|
| 128 |
+
for block in self.blocks:
|
| 129 |
+
x = block(x, c, m, transformer_options=transformer_options)
|
| 130 |
+
return x
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
class TokenRefiner(nn.Module):
|
| 135 |
+
def __init__(
|
| 136 |
+
self,
|
| 137 |
+
text_dim,
|
| 138 |
+
hidden_size,
|
| 139 |
+
heads,
|
| 140 |
+
num_blocks,
|
| 141 |
+
dtype=None,
|
| 142 |
+
device=None,
|
| 143 |
+
operations=None
|
| 144 |
+
):
|
| 145 |
+
super().__init__()
|
| 146 |
+
|
| 147 |
+
self.input_embedder = operations.Linear(text_dim, hidden_size, bias=True, dtype=dtype, device=device)
|
| 148 |
+
self.t_embedder = MLPEmbedder(256, hidden_size, dtype=dtype, device=device, operations=operations)
|
| 149 |
+
self.c_embedder = MLPEmbedder(text_dim, hidden_size, dtype=dtype, device=device, operations=operations)
|
| 150 |
+
self.individual_token_refiner = IndividualTokenRefiner(hidden_size, heads, num_blocks, dtype=dtype, device=device, operations=operations)
|
| 151 |
+
|
| 152 |
+
def forward(
|
| 153 |
+
self,
|
| 154 |
+
x,
|
| 155 |
+
timesteps,
|
| 156 |
+
mask,
|
| 157 |
+
transformer_options={},
|
| 158 |
+
):
|
| 159 |
+
t = self.t_embedder(timestep_embedding(timesteps, 256, time_factor=1.0).to(x.dtype))
|
| 160 |
+
# m = mask.float().unsqueeze(-1)
|
| 161 |
+
# c = (x.float() * m).sum(dim=1) / m.sum(dim=1) #TODO: the following works when the x.shape is the same length as the tokens but might break otherwise
|
| 162 |
+
if x.dtype == torch.float16:
|
| 163 |
+
c = x.float().sum(dim=1) / x.shape[1]
|
| 164 |
+
else:
|
| 165 |
+
c = x.sum(dim=1) / x.shape[1]
|
| 166 |
+
|
| 167 |
+
c = t + self.c_embedder(c.to(x.dtype))
|
| 168 |
+
x = self.input_embedder(x)
|
| 169 |
+
x = self.individual_token_refiner(x, c, mask, transformer_options=transformer_options)
|
| 170 |
+
return x
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class ByT5Mapper(nn.Module):
|
| 174 |
+
def __init__(self, in_dim, out_dim, hidden_dim, out_dim1, use_res=False, dtype=None, device=None, operations=None):
|
| 175 |
+
super().__init__()
|
| 176 |
+
self.layernorm = operations.LayerNorm(in_dim, dtype=dtype, device=device)
|
| 177 |
+
self.fc1 = operations.Linear(in_dim, hidden_dim, dtype=dtype, device=device)
|
| 178 |
+
self.fc2 = operations.Linear(hidden_dim, out_dim, dtype=dtype, device=device)
|
| 179 |
+
self.fc3 = operations.Linear(out_dim, out_dim1, dtype=dtype, device=device)
|
| 180 |
+
self.use_res = use_res
|
| 181 |
+
self.act_fn = nn.GELU()
|
| 182 |
+
|
| 183 |
+
def forward(self, x):
|
| 184 |
+
if self.use_res:
|
| 185 |
+
res = x
|
| 186 |
+
x = self.layernorm(x)
|
| 187 |
+
x = self.fc1(x)
|
| 188 |
+
x = self.act_fn(x)
|
| 189 |
+
x = self.fc2(x)
|
| 190 |
+
x2 = self.act_fn(x)
|
| 191 |
+
x2 = self.fc3(x2)
|
| 192 |
+
if self.use_res:
|
| 193 |
+
x2 = x2 + res
|
| 194 |
+
return x2
|
| 195 |
+
|
| 196 |
+
class HunyuanVideo(nn.Module):
|
| 197 |
+
"""
|
| 198 |
+
Transformer model for flow matching on sequences.
|
| 199 |
+
"""
|
| 200 |
+
|
| 201 |
+
def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
|
| 202 |
+
super().__init__()
|
| 203 |
+
self.dtype = dtype
|
| 204 |
+
operation_settings = {"operations": operations, "device": device, "dtype": dtype}
|
| 205 |
+
|
| 206 |
+
params = HunyuanVideoParams(**kwargs)
|
| 207 |
+
self.params = params
|
| 208 |
+
self.patch_size = params.patch_size
|
| 209 |
+
self.in_channels = params.in_channels
|
| 210 |
+
self.out_channels = params.out_channels
|
| 211 |
+
self.use_cond_type_embedding = params.use_cond_type_embedding
|
| 212 |
+
self.vision_in_dim = params.vision_in_dim
|
| 213 |
+
if params.hidden_size % params.num_heads != 0:
|
| 214 |
+
raise ValueError(
|
| 215 |
+
f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
|
| 216 |
+
)
|
| 217 |
+
pe_dim = params.hidden_size // params.num_heads
|
| 218 |
+
if sum(params.axes_dim) != pe_dim:
|
| 219 |
+
raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
|
| 220 |
+
self.hidden_size = params.hidden_size
|
| 221 |
+
self.num_heads = params.num_heads
|
| 222 |
+
self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
|
| 223 |
+
|
| 224 |
+
self.img_in = comfy.ldm.modules.diffusionmodules.mmdit.PatchEmbed(None, self.patch_size, self.in_channels, self.hidden_size, conv3d=len(self.patch_size) == 3, dtype=dtype, device=device, operations=operations)
|
| 225 |
+
self.time_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size, dtype=dtype, device=device, operations=operations)
|
| 226 |
+
if params.vec_in_dim is not None:
|
| 227 |
+
self.vector_in = MLPEmbedder(params.vec_in_dim, self.hidden_size, dtype=dtype, device=device, operations=operations)
|
| 228 |
+
else:
|
| 229 |
+
self.vector_in = None
|
| 230 |
+
|
| 231 |
+
self.guidance_in = (
|
| 232 |
+
MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size, dtype=dtype, device=device, operations=operations) if params.guidance_embed else nn.Identity()
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
self.txt_in = TokenRefiner(params.context_in_dim, self.hidden_size, self.num_heads, 2, dtype=dtype, device=device, operations=operations)
|
| 236 |
+
|
| 237 |
+
self.double_blocks = nn.ModuleList(
|
| 238 |
+
[
|
| 239 |
+
DoubleStreamBlock(
|
| 240 |
+
self.hidden_size,
|
| 241 |
+
self.num_heads,
|
| 242 |
+
mlp_ratio=params.mlp_ratio,
|
| 243 |
+
qkv_bias=params.qkv_bias,
|
| 244 |
+
dtype=dtype, device=device, operations=operations
|
| 245 |
+
)
|
| 246 |
+
for _ in range(params.depth)
|
| 247 |
+
]
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
self.single_blocks = nn.ModuleList(
|
| 251 |
+
[
|
| 252 |
+
SingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, dtype=dtype, device=device, operations=operations)
|
| 253 |
+
for _ in range(params.depth_single_blocks)
|
| 254 |
+
]
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
if params.byt5:
|
| 258 |
+
self.byt5_in = ByT5Mapper(
|
| 259 |
+
in_dim=1472,
|
| 260 |
+
out_dim=2048,
|
| 261 |
+
hidden_dim=2048,
|
| 262 |
+
out_dim1=self.hidden_size,
|
| 263 |
+
use_res=False,
|
| 264 |
+
dtype=dtype, device=device, operations=operations
|
| 265 |
+
)
|
| 266 |
+
else:
|
| 267 |
+
self.byt5_in = None
|
| 268 |
+
|
| 269 |
+
if params.meanflow:
|
| 270 |
+
self.time_r_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size, dtype=dtype, device=device, operations=operations)
|
| 271 |
+
else:
|
| 272 |
+
self.time_r_in = None
|
| 273 |
+
|
| 274 |
+
if final_layer:
|
| 275 |
+
self.final_layer = LastLayer(self.hidden_size, self.patch_size[-1], self.out_channels, dtype=dtype, device=device, operations=operations)
|
| 276 |
+
|
| 277 |
+
# HunyuanVideo 1.5 specific modules
|
| 278 |
+
if self.vision_in_dim is not None:
|
| 279 |
+
from comfy.ldm.wan.model import MLPProj
|
| 280 |
+
self.vision_in = MLPProj(in_dim=self.vision_in_dim, out_dim=self.hidden_size, operation_settings=operation_settings)
|
| 281 |
+
else:
|
| 282 |
+
self.vision_in = None
|
| 283 |
+
if self.use_cond_type_embedding:
|
| 284 |
+
# 0: text_encoder feature 1: byt5 feature 2: vision_encoder feature
|
| 285 |
+
self.cond_type_embedding = nn.Embedding(3, self.hidden_size)
|
| 286 |
+
else:
|
| 287 |
+
self.cond_type_embedding = None
|
| 288 |
+
|
| 289 |
+
def forward_orig(
|
| 290 |
+
self,
|
| 291 |
+
img: Tensor,
|
| 292 |
+
img_ids: Tensor,
|
| 293 |
+
txt: Tensor,
|
| 294 |
+
txt_ids: Tensor,
|
| 295 |
+
txt_mask: Tensor,
|
| 296 |
+
timesteps: Tensor,
|
| 297 |
+
y: Tensor = None,
|
| 298 |
+
txt_byt5=None,
|
| 299 |
+
clip_fea=None,
|
| 300 |
+
guidance: Tensor = None,
|
| 301 |
+
guiding_frame_index=None,
|
| 302 |
+
ref_latent=None,
|
| 303 |
+
disable_time_r=False,
|
| 304 |
+
control=None,
|
| 305 |
+
transformer_options={},
|
| 306 |
+
) -> Tensor:
|
| 307 |
+
transformer_options = transformer_options.copy()
|
| 308 |
+
patches_replace = transformer_options.get("patches_replace", {})
|
| 309 |
+
|
| 310 |
+
initial_shape = list(img.shape)
|
| 311 |
+
# running on sequences img
|
| 312 |
+
img = self.img_in(img)
|
| 313 |
+
vec = self.time_in(timestep_embedding(timesteps, 256, time_factor=1.0).to(img.dtype))
|
| 314 |
+
|
| 315 |
+
if (self.time_r_in is not None) and (not disable_time_r):
|
| 316 |
+
w = torch.where(transformer_options['sigmas'][0] == transformer_options['sample_sigmas'])[0] # This most likely could be improved
|
| 317 |
+
if len(w) > 0:
|
| 318 |
+
timesteps_r = transformer_options['sample_sigmas'][w[0] + 1]
|
| 319 |
+
timesteps_r = timesteps_r.unsqueeze(0).to(device=timesteps.device, dtype=timesteps.dtype)
|
| 320 |
+
vec_r = self.time_r_in(timestep_embedding(timesteps_r, 256, time_factor=1000.0).to(img.dtype))
|
| 321 |
+
vec = (vec + vec_r) if self.params.meanflow_sum else (vec + vec_r) / 2
|
| 322 |
+
|
| 323 |
+
if ref_latent is not None:
|
| 324 |
+
ref_latent_ids = self.img_ids(ref_latent)
|
| 325 |
+
ref_latent = self.img_in(ref_latent)
|
| 326 |
+
img = torch.cat([ref_latent, img], dim=-2)
|
| 327 |
+
ref_latent_ids[..., 0] = -1
|
| 328 |
+
ref_latent_ids[..., 2] += (initial_shape[-1] // self.patch_size[-1])
|
| 329 |
+
img_ids = torch.cat([ref_latent_ids, img_ids], dim=-2)
|
| 330 |
+
|
| 331 |
+
if guiding_frame_index is not None:
|
| 332 |
+
token_replace_vec = self.time_in(timestep_embedding(guiding_frame_index, 256, time_factor=1.0))
|
| 333 |
+
if self.vector_in is not None:
|
| 334 |
+
vec_ = self.vector_in(y[:, :self.params.vec_in_dim])
|
| 335 |
+
vec = torch.cat([(vec_ + token_replace_vec).unsqueeze(1), (vec_ + vec).unsqueeze(1)], dim=1)
|
| 336 |
+
else:
|
| 337 |
+
vec = torch.cat([(token_replace_vec).unsqueeze(1), (vec).unsqueeze(1)], dim=1)
|
| 338 |
+
frame_tokens = (initial_shape[-1] // self.patch_size[-1]) * (initial_shape[-2] // self.patch_size[-2])
|
| 339 |
+
modulation_dims = [(0, frame_tokens, 0), (frame_tokens, None, 1)]
|
| 340 |
+
modulation_dims_txt = [(0, None, 1)]
|
| 341 |
+
else:
|
| 342 |
+
if self.vector_in is not None:
|
| 343 |
+
vec = vec + self.vector_in(y[:, :self.params.vec_in_dim])
|
| 344 |
+
modulation_dims = None
|
| 345 |
+
modulation_dims_txt = None
|
| 346 |
+
|
| 347 |
+
if self.params.guidance_embed:
|
| 348 |
+
if guidance is not None:
|
| 349 |
+
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
|
| 350 |
+
|
| 351 |
+
if txt_mask is not None and not torch.is_floating_point(txt_mask):
|
| 352 |
+
txt_mask = (txt_mask - 1).to(img.dtype) * torch.finfo(img.dtype).max
|
| 353 |
+
|
| 354 |
+
txt = self.txt_in(txt, timesteps, txt_mask, transformer_options=transformer_options)
|
| 355 |
+
|
| 356 |
+
if self.cond_type_embedding is not None:
|
| 357 |
+
self.cond_type_embedding.to(txt.device)
|
| 358 |
+
cond_emb = self.cond_type_embedding(torch.zeros_like(txt[:, :, 0], device=txt.device, dtype=torch.long))
|
| 359 |
+
txt = txt + cond_emb.to(txt.dtype)
|
| 360 |
+
|
| 361 |
+
if self.byt5_in is not None and txt_byt5 is not None:
|
| 362 |
+
txt_byt5 = self.byt5_in(txt_byt5)
|
| 363 |
+
if self.cond_type_embedding is not None:
|
| 364 |
+
cond_emb = self.cond_type_embedding(torch.ones_like(txt_byt5[:, :, 0], device=txt_byt5.device, dtype=torch.long))
|
| 365 |
+
txt_byt5 = txt_byt5 + cond_emb.to(txt_byt5.dtype)
|
| 366 |
+
txt = torch.cat((txt_byt5, txt), dim=1) # byt5 first for HunyuanVideo1.5
|
| 367 |
+
else:
|
| 368 |
+
txt = torch.cat((txt, txt_byt5), dim=1)
|
| 369 |
+
txt_byt5_ids = torch.zeros((txt_ids.shape[0], txt_byt5.shape[1], txt_ids.shape[-1]), device=txt_ids.device, dtype=txt_ids.dtype)
|
| 370 |
+
txt_ids = torch.cat((txt_ids, txt_byt5_ids), dim=1)
|
| 371 |
+
|
| 372 |
+
if clip_fea is not None:
|
| 373 |
+
txt_vision_states = self.vision_in(clip_fea)
|
| 374 |
+
if self.cond_type_embedding is not None:
|
| 375 |
+
cond_emb = self.cond_type_embedding(2 * torch.ones_like(txt_vision_states[:, :, 0], dtype=torch.long, device=txt_vision_states.device))
|
| 376 |
+
txt_vision_states = txt_vision_states + cond_emb
|
| 377 |
+
txt = torch.cat((txt_vision_states.to(txt.dtype), txt), dim=1)
|
| 378 |
+
extra_txt_ids = torch.zeros((txt_ids.shape[0], txt_vision_states.shape[1], txt_ids.shape[-1]), device=txt_ids.device, dtype=txt_ids.dtype)
|
| 379 |
+
txt_ids = torch.cat((txt_ids, extra_txt_ids), dim=1)
|
| 380 |
+
|
| 381 |
+
ids = torch.cat((txt_ids, img_ids), dim=1)
|
| 382 |
+
pe = self.pe_embedder(ids)
|
| 383 |
+
|
| 384 |
+
img_len = img.shape[1]
|
| 385 |
+
if txt_mask is not None:
|
| 386 |
+
attn_mask_len = img_len + txt.shape[1]
|
| 387 |
+
attn_mask = torch.zeros((1, 1, attn_mask_len), dtype=img.dtype, device=img.device)
|
| 388 |
+
attn_mask[:, 0, :txt.shape[1]] = txt_mask
|
| 389 |
+
else:
|
| 390 |
+
attn_mask = None
|
| 391 |
+
|
| 392 |
+
blocks_replace = patches_replace.get("dit", {})
|
| 393 |
+
transformer_options["total_blocks"] = len(self.double_blocks)
|
| 394 |
+
transformer_options["block_type"] = "double"
|
| 395 |
+
for i, block in enumerate(self.double_blocks):
|
| 396 |
+
transformer_options["block_index"] = i
|
| 397 |
+
if ("double_block", i) in blocks_replace:
|
| 398 |
+
def block_wrap(args):
|
| 399 |
+
out = {}
|
| 400 |
+
out["img"], out["txt"] = block(img=args["img"], txt=args["txt"], vec=args["vec"], pe=args["pe"], attn_mask=args["attention_mask"], modulation_dims_img=args["modulation_dims_img"], modulation_dims_txt=args["modulation_dims_txt"], transformer_options=args["transformer_options"])
|
| 401 |
+
return out
|
| 402 |
+
|
| 403 |
+
out = blocks_replace[("double_block", i)]({"img": img, "txt": txt, "vec": vec, "pe": pe, "attention_mask": attn_mask, 'modulation_dims_img': modulation_dims, 'modulation_dims_txt': modulation_dims_txt, 'transformer_options': transformer_options}, {"original_block": block_wrap})
|
| 404 |
+
txt = out["txt"]
|
| 405 |
+
img = out["img"]
|
| 406 |
+
else:
|
| 407 |
+
img, txt = block(img=img, txt=txt, vec=vec, pe=pe, attn_mask=attn_mask, modulation_dims_img=modulation_dims, modulation_dims_txt=modulation_dims_txt, transformer_options=transformer_options)
|
| 408 |
+
|
| 409 |
+
if control is not None: # Controlnet
|
| 410 |
+
control_i = control.get("input")
|
| 411 |
+
if i < len(control_i):
|
| 412 |
+
add = control_i[i]
|
| 413 |
+
if add is not None:
|
| 414 |
+
img += add
|
| 415 |
+
|
| 416 |
+
img = torch.cat((txt, img), 1)
|
| 417 |
+
|
| 418 |
+
transformer_options["total_blocks"] = len(self.single_blocks)
|
| 419 |
+
transformer_options["block_type"] = "single"
|
| 420 |
+
transformer_options["img_slice"] = [txt.shape[1], img.shape[1]]
|
| 421 |
+
for i, block in enumerate(self.single_blocks):
|
| 422 |
+
transformer_options["block_index"] = i
|
| 423 |
+
if ("single_block", i) in blocks_replace:
|
| 424 |
+
def block_wrap(args):
|
| 425 |
+
out = {}
|
| 426 |
+
out["img"] = block(args["img"], vec=args["vec"], pe=args["pe"], attn_mask=args["attention_mask"], modulation_dims=args["modulation_dims"], transformer_options=args["transformer_options"])
|
| 427 |
+
return out
|
| 428 |
+
|
| 429 |
+
out = blocks_replace[("single_block", i)]({"img": img, "vec": vec, "pe": pe, "attention_mask": attn_mask, 'modulation_dims': modulation_dims, 'transformer_options': transformer_options}, {"original_block": block_wrap})
|
| 430 |
+
img = out["img"]
|
| 431 |
+
else:
|
| 432 |
+
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask, modulation_dims=modulation_dims, transformer_options=transformer_options)
|
| 433 |
+
|
| 434 |
+
if control is not None: # Controlnet
|
| 435 |
+
control_o = control.get("output")
|
| 436 |
+
if i < len(control_o):
|
| 437 |
+
add = control_o[i]
|
| 438 |
+
if add is not None:
|
| 439 |
+
img[:, txt.shape[1]: img_len + txt.shape[1]] += add
|
| 440 |
+
|
| 441 |
+
img = img[:, txt.shape[1]: img_len + txt.shape[1]]
|
| 442 |
+
if ref_latent is not None:
|
| 443 |
+
img = img[:, ref_latent.shape[1]:]
|
| 444 |
+
|
| 445 |
+
img = self.final_layer(img, vec, modulation_dims=modulation_dims) # (N, T, patch_size ** 2 * out_channels)
|
| 446 |
+
|
| 447 |
+
shape = initial_shape[-len(self.patch_size):]
|
| 448 |
+
for i in range(len(shape)):
|
| 449 |
+
shape[i] = shape[i] // self.patch_size[i]
|
| 450 |
+
img = img.reshape([img.shape[0]] + shape + [self.out_channels] + self.patch_size)
|
| 451 |
+
if img.ndim == 8:
|
| 452 |
+
img = img.permute(0, 4, 1, 5, 2, 6, 3, 7)
|
| 453 |
+
img = img.reshape(initial_shape[0], self.out_channels, initial_shape[2], initial_shape[3], initial_shape[4])
|
| 454 |
+
else:
|
| 455 |
+
img = img.permute(0, 3, 1, 4, 2, 5)
|
| 456 |
+
img = img.reshape(initial_shape[0], self.out_channels, initial_shape[2], initial_shape[3])
|
| 457 |
+
return img
|
| 458 |
+
|
| 459 |
+
def img_ids(self, x):
|
| 460 |
+
bs, c, t, h, w = x.shape
|
| 461 |
+
patch_size = self.patch_size
|
| 462 |
+
t_len = ((t + (patch_size[0] // 2)) // patch_size[0])
|
| 463 |
+
h_len = ((h + (patch_size[1] // 2)) // patch_size[1])
|
| 464 |
+
w_len = ((w + (patch_size[2] // 2)) // patch_size[2])
|
| 465 |
+
img_ids = torch.zeros((t_len, h_len, w_len, 3), device=x.device, dtype=x.dtype)
|
| 466 |
+
img_ids[:, :, :, 0] = img_ids[:, :, :, 0] + torch.linspace(0, t_len - 1, steps=t_len, device=x.device, dtype=x.dtype).reshape(-1, 1, 1)
|
| 467 |
+
img_ids[:, :, :, 1] = img_ids[:, :, :, 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype).reshape(1, -1, 1)
|
| 468 |
+
img_ids[:, :, :, 2] = img_ids[:, :, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).reshape(1, 1, -1)
|
| 469 |
+
return repeat(img_ids, "t h w c -> b (t h w) c", b=bs)
|
| 470 |
+
|
| 471 |
+
def img_ids_2d(self, x):
|
| 472 |
+
bs, c, h, w = x.shape
|
| 473 |
+
patch_size = self.patch_size
|
| 474 |
+
h_len = ((h + (patch_size[0] // 2)) // patch_size[0])
|
| 475 |
+
w_len = ((w + (patch_size[1] // 2)) // patch_size[1])
|
| 476 |
+
img_ids = torch.zeros((h_len, w_len, 2), device=x.device, dtype=x.dtype)
|
| 477 |
+
img_ids[:, :, 0] = img_ids[:, :, 0] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype).unsqueeze(1)
|
| 478 |
+
img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).unsqueeze(0)
|
| 479 |
+
return repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
| 480 |
+
|
| 481 |
+
def forward(self, x, timestep, context, y=None, txt_byt5=None, clip_fea=None, guidance=None, attention_mask=None, guiding_frame_index=None, ref_latent=None, disable_time_r=False, control=None, transformer_options={}, **kwargs):
|
| 482 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 483 |
+
self._forward,
|
| 484 |
+
self,
|
| 485 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
|
| 486 |
+
).execute(x, timestep, context, y, txt_byt5, clip_fea, guidance, attention_mask, guiding_frame_index, ref_latent, disable_time_r, control, transformer_options, **kwargs)
|
| 487 |
+
|
| 488 |
+
def _forward(self, x, timestep, context, y=None, txt_byt5=None, clip_fea=None, guidance=None, attention_mask=None, guiding_frame_index=None, ref_latent=None, disable_time_r=False, control=None, transformer_options={}, **kwargs):
|
| 489 |
+
bs = x.shape[0]
|
| 490 |
+
if len(self.patch_size) == 3:
|
| 491 |
+
img_ids = self.img_ids(x)
|
| 492 |
+
txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
|
| 493 |
+
else:
|
| 494 |
+
img_ids = self.img_ids_2d(x)
|
| 495 |
+
txt_ids = torch.zeros((bs, context.shape[1], 2), device=x.device, dtype=x.dtype)
|
| 496 |
+
out = self.forward_orig(x, img_ids, context, txt_ids, attention_mask, timestep, y, txt_byt5, clip_fea, guidance, guiding_frame_index, ref_latent, disable_time_r=disable_time_r, control=control, transformer_options=transformer_options)
|
| 497 |
+
return out
|
ComfyUI/comfy/ldm/hunyuan_video/upsampler.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from comfy.ldm.modules.diffusionmodules.model import ResnetBlock, VideoConv3d
|
| 5 |
+
from comfy.ldm.hunyuan_video.vae_refiner import RMS_norm
|
| 6 |
+
import comfy.model_management
|
| 7 |
+
import comfy.model_patcher
|
| 8 |
+
|
| 9 |
+
class SRResidualCausalBlock3D(nn.Module):
|
| 10 |
+
def __init__(self, channels: int):
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.block = nn.Sequential(
|
| 13 |
+
VideoConv3d(channels, channels, kernel_size=3),
|
| 14 |
+
nn.SiLU(inplace=True),
|
| 15 |
+
VideoConv3d(channels, channels, kernel_size=3),
|
| 16 |
+
nn.SiLU(inplace=True),
|
| 17 |
+
VideoConv3d(channels, channels, kernel_size=3),
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 21 |
+
return x + self.block(x)
|
| 22 |
+
|
| 23 |
+
class SRModel3DV2(nn.Module):
|
| 24 |
+
def __init__(
|
| 25 |
+
self,
|
| 26 |
+
in_channels: int,
|
| 27 |
+
out_channels: int,
|
| 28 |
+
hidden_channels: int = 64,
|
| 29 |
+
num_blocks: int = 6,
|
| 30 |
+
global_residual: bool = False,
|
| 31 |
+
):
|
| 32 |
+
super().__init__()
|
| 33 |
+
self.in_conv = VideoConv3d(in_channels, hidden_channels, kernel_size=3)
|
| 34 |
+
self.blocks = nn.ModuleList([SRResidualCausalBlock3D(hidden_channels) for _ in range(num_blocks)])
|
| 35 |
+
self.out_conv = VideoConv3d(hidden_channels, out_channels, kernel_size=3)
|
| 36 |
+
self.global_residual = bool(global_residual)
|
| 37 |
+
|
| 38 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 39 |
+
residual = x
|
| 40 |
+
y = self.in_conv(x)
|
| 41 |
+
for blk in self.blocks:
|
| 42 |
+
y = blk(y)
|
| 43 |
+
y = self.out_conv(y)
|
| 44 |
+
if self.global_residual and (y.shape == residual.shape):
|
| 45 |
+
y = y + residual
|
| 46 |
+
return y
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class Upsampler(nn.Module):
|
| 50 |
+
def __init__(
|
| 51 |
+
self,
|
| 52 |
+
z_channels: int,
|
| 53 |
+
out_channels: int,
|
| 54 |
+
block_out_channels: tuple[int, ...],
|
| 55 |
+
num_res_blocks: int = 2,
|
| 56 |
+
):
|
| 57 |
+
super().__init__()
|
| 58 |
+
self.num_res_blocks = num_res_blocks
|
| 59 |
+
self.block_out_channels = block_out_channels
|
| 60 |
+
self.z_channels = z_channels
|
| 61 |
+
|
| 62 |
+
ch = block_out_channels[0]
|
| 63 |
+
self.conv_in = VideoConv3d(z_channels, ch, kernel_size=3)
|
| 64 |
+
|
| 65 |
+
self.up = nn.ModuleList()
|
| 66 |
+
|
| 67 |
+
for i, tgt in enumerate(block_out_channels):
|
| 68 |
+
stage = nn.Module()
|
| 69 |
+
stage.block = nn.ModuleList([ResnetBlock(in_channels=ch if j == 0 else tgt,
|
| 70 |
+
out_channels=tgt,
|
| 71 |
+
temb_channels=0,
|
| 72 |
+
conv_shortcut=False,
|
| 73 |
+
conv_op=VideoConv3d, norm_op=RMS_norm)
|
| 74 |
+
for j in range(num_res_blocks + 1)])
|
| 75 |
+
ch = tgt
|
| 76 |
+
self.up.append(stage)
|
| 77 |
+
|
| 78 |
+
self.norm_out = RMS_norm(ch)
|
| 79 |
+
self.conv_out = VideoConv3d(ch, out_channels, kernel_size=3)
|
| 80 |
+
|
| 81 |
+
def forward(self, z):
|
| 82 |
+
"""
|
| 83 |
+
Args:
|
| 84 |
+
z: (B, C, T, H, W)
|
| 85 |
+
target_shape: (H, W)
|
| 86 |
+
"""
|
| 87 |
+
# z to block_in
|
| 88 |
+
repeats = self.block_out_channels[0] // (self.z_channels)
|
| 89 |
+
x = self.conv_in(z) + z.repeat_interleave(repeats=repeats, dim=1)
|
| 90 |
+
|
| 91 |
+
# upsampling
|
| 92 |
+
for stage in self.up:
|
| 93 |
+
for blk in stage.block:
|
| 94 |
+
x = blk(x)
|
| 95 |
+
|
| 96 |
+
out = self.conv_out(F.silu(self.norm_out(x)))
|
| 97 |
+
return out
|
| 98 |
+
|
| 99 |
+
UPSAMPLERS = {
|
| 100 |
+
"720p": SRModel3DV2,
|
| 101 |
+
"1080p": Upsampler,
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
class HunyuanVideo15SRModel():
|
| 105 |
+
def __init__(self, model_type, config):
|
| 106 |
+
self.load_device = comfy.model_management.vae_device()
|
| 107 |
+
offload_device = comfy.model_management.vae_offload_device()
|
| 108 |
+
self.dtype = comfy.model_management.vae_dtype(self.load_device)
|
| 109 |
+
self.model_class = UPSAMPLERS.get(model_type)
|
| 110 |
+
self.model = self.model_class(**config).eval()
|
| 111 |
+
|
| 112 |
+
self.patcher = comfy.model_patcher.CoreModelPatcher(self.model, load_device=self.load_device, offload_device=offload_device)
|
| 113 |
+
|
| 114 |
+
def load_sd(self, sd):
|
| 115 |
+
return self.model.load_state_dict(sd, strict=True, assign=self.patcher.is_dynamic())
|
| 116 |
+
|
| 117 |
+
def get_sd(self):
|
| 118 |
+
return self.model.state_dict()
|
| 119 |
+
|
| 120 |
+
def resample_latent(self, latent):
|
| 121 |
+
comfy.model_management.load_model_gpu(self.patcher)
|
| 122 |
+
return self.model(latent.to(self.load_device))
|
ComfyUI/comfy/ldm/hunyuan_video/vae.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch.nn as nn
|
| 2 |
+
import torch.nn.functional as F
|
| 3 |
+
from comfy.ldm.modules.diffusionmodules.model import ResnetBlock, AttnBlock
|
| 4 |
+
import comfy.ops
|
| 5 |
+
ops = comfy.ops.disable_weight_init
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class PixelShuffle2D(nn.Module):
|
| 9 |
+
def __init__(self, in_dim, out_dim, op=ops.Conv2d):
|
| 10 |
+
super().__init__()
|
| 11 |
+
self.conv = op(in_dim, out_dim >> 2, 3, 1, 1)
|
| 12 |
+
self.ratio = (in_dim << 2) // out_dim
|
| 13 |
+
|
| 14 |
+
def forward(self, x):
|
| 15 |
+
b, c, h, w = x.shape
|
| 16 |
+
h2, w2 = h >> 1, w >> 1
|
| 17 |
+
y = self.conv(x).view(b, -1, h2, 2, w2, 2).permute(0, 3, 5, 1, 2, 4).reshape(b, -1, h2, w2)
|
| 18 |
+
r = x.view(b, c, h2, 2, w2, 2).permute(0, 3, 5, 1, 2, 4).reshape(b, c << 2, h2, w2)
|
| 19 |
+
return y + r.view(b, y.shape[1], self.ratio, h2, w2).mean(2)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class PixelUnshuffle2D(nn.Module):
|
| 23 |
+
def __init__(self, in_dim, out_dim, op=ops.Conv2d):
|
| 24 |
+
super().__init__()
|
| 25 |
+
self.conv = op(in_dim, out_dim << 2, 3, 1, 1)
|
| 26 |
+
self.scale = (out_dim << 2) // in_dim
|
| 27 |
+
|
| 28 |
+
def forward(self, x):
|
| 29 |
+
b, c, h, w = x.shape
|
| 30 |
+
h2, w2 = h << 1, w << 1
|
| 31 |
+
y = self.conv(x).view(b, 2, 2, -1, h, w).permute(0, 3, 4, 1, 5, 2).reshape(b, -1, h2, w2)
|
| 32 |
+
r = x.repeat_interleave(self.scale, 1).view(b, 2, 2, -1, h, w).permute(0, 3, 4, 1, 5, 2).reshape(b, -1, h2, w2)
|
| 33 |
+
return y + r
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class Encoder(nn.Module):
|
| 37 |
+
def __init__(self, in_channels, z_channels, block_out_channels, num_res_blocks,
|
| 38 |
+
ffactor_spatial, downsample_match_channel=True, **_):
|
| 39 |
+
super().__init__()
|
| 40 |
+
self.z_channels = z_channels
|
| 41 |
+
self.block_out_channels = block_out_channels
|
| 42 |
+
self.num_res_blocks = num_res_blocks
|
| 43 |
+
self.conv_in = ops.Conv2d(in_channels, block_out_channels[0], 3, 1, 1)
|
| 44 |
+
|
| 45 |
+
self.down = nn.ModuleList()
|
| 46 |
+
ch = block_out_channels[0]
|
| 47 |
+
depth = (ffactor_spatial >> 1).bit_length()
|
| 48 |
+
|
| 49 |
+
for i, tgt in enumerate(block_out_channels):
|
| 50 |
+
stage = nn.Module()
|
| 51 |
+
stage.block = nn.ModuleList([ResnetBlock(in_channels=ch if j == 0 else tgt,
|
| 52 |
+
out_channels=tgt,
|
| 53 |
+
temb_channels=0,
|
| 54 |
+
conv_op=ops.Conv2d)
|
| 55 |
+
for j in range(num_res_blocks)])
|
| 56 |
+
ch = tgt
|
| 57 |
+
if i < depth:
|
| 58 |
+
nxt = block_out_channels[i + 1] if i + 1 < len(block_out_channels) and downsample_match_channel else ch
|
| 59 |
+
stage.downsample = PixelShuffle2D(ch, nxt, ops.Conv2d)
|
| 60 |
+
ch = nxt
|
| 61 |
+
self.down.append(stage)
|
| 62 |
+
|
| 63 |
+
self.mid = nn.Module()
|
| 64 |
+
self.mid.block_1 = ResnetBlock(in_channels=ch, out_channels=ch, temb_channels=0, conv_op=ops.Conv2d)
|
| 65 |
+
self.mid.attn_1 = AttnBlock(ch, conv_op=ops.Conv2d)
|
| 66 |
+
self.mid.block_2 = ResnetBlock(in_channels=ch, out_channels=ch, temb_channels=0, conv_op=ops.Conv2d)
|
| 67 |
+
|
| 68 |
+
self.norm_out = ops.GroupNorm(32, ch, 1e-6, True)
|
| 69 |
+
self.conv_out = ops.Conv2d(ch, z_channels << 1, 3, 1, 1)
|
| 70 |
+
|
| 71 |
+
def forward(self, x):
|
| 72 |
+
x = self.conv_in(x)
|
| 73 |
+
|
| 74 |
+
for stage in self.down:
|
| 75 |
+
for blk in stage.block:
|
| 76 |
+
x = blk(x)
|
| 77 |
+
if hasattr(stage, 'downsample'):
|
| 78 |
+
x = stage.downsample(x)
|
| 79 |
+
|
| 80 |
+
x = self.mid.block_2(self.mid.attn_1(self.mid.block_1(x)))
|
| 81 |
+
|
| 82 |
+
b, c, h, w = x.shape
|
| 83 |
+
grp = c // (self.z_channels << 1)
|
| 84 |
+
skip = x.view(b, c // grp, grp, h, w).mean(2)
|
| 85 |
+
|
| 86 |
+
return self.conv_out(F.silu(self.norm_out(x))) + skip
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class Decoder(nn.Module):
|
| 90 |
+
def __init__(self, z_channels, out_channels, block_out_channels, num_res_blocks,
|
| 91 |
+
ffactor_spatial, upsample_match_channel=True, **_):
|
| 92 |
+
super().__init__()
|
| 93 |
+
block_out_channels = block_out_channels[::-1]
|
| 94 |
+
self.z_channels = z_channels
|
| 95 |
+
self.block_out_channels = block_out_channels
|
| 96 |
+
self.num_res_blocks = num_res_blocks
|
| 97 |
+
|
| 98 |
+
ch = block_out_channels[0]
|
| 99 |
+
self.conv_in = ops.Conv2d(z_channels, ch, 3, 1, 1)
|
| 100 |
+
|
| 101 |
+
self.mid = nn.Module()
|
| 102 |
+
self.mid.block_1 = ResnetBlock(in_channels=ch, out_channels=ch, temb_channels=0, conv_op=ops.Conv2d)
|
| 103 |
+
self.mid.attn_1 = AttnBlock(ch, conv_op=ops.Conv2d)
|
| 104 |
+
self.mid.block_2 = ResnetBlock(in_channels=ch, out_channels=ch, temb_channels=0, conv_op=ops.Conv2d)
|
| 105 |
+
|
| 106 |
+
self.up = nn.ModuleList()
|
| 107 |
+
depth = (ffactor_spatial >> 1).bit_length()
|
| 108 |
+
|
| 109 |
+
for i, tgt in enumerate(block_out_channels):
|
| 110 |
+
stage = nn.Module()
|
| 111 |
+
stage.block = nn.ModuleList([ResnetBlock(in_channels=ch if j == 0 else tgt,
|
| 112 |
+
out_channels=tgt,
|
| 113 |
+
temb_channels=0,
|
| 114 |
+
conv_op=ops.Conv2d)
|
| 115 |
+
for j in range(num_res_blocks + 1)])
|
| 116 |
+
ch = tgt
|
| 117 |
+
if i < depth:
|
| 118 |
+
nxt = block_out_channels[i + 1] if i + 1 < len(block_out_channels) and upsample_match_channel else ch
|
| 119 |
+
stage.upsample = PixelUnshuffle2D(ch, nxt, ops.Conv2d)
|
| 120 |
+
ch = nxt
|
| 121 |
+
self.up.append(stage)
|
| 122 |
+
|
| 123 |
+
self.norm_out = ops.GroupNorm(32, ch, 1e-6, True)
|
| 124 |
+
self.conv_out = ops.Conv2d(ch, out_channels, 3, 1, 1)
|
| 125 |
+
|
| 126 |
+
def forward(self, z):
|
| 127 |
+
x = self.conv_in(z) + z.repeat_interleave(self.block_out_channels[0] // self.z_channels, 1)
|
| 128 |
+
x = self.mid.block_2(self.mid.attn_1(self.mid.block_1(x)))
|
| 129 |
+
|
| 130 |
+
for stage in self.up:
|
| 131 |
+
for blk in stage.block:
|
| 132 |
+
x = blk(x)
|
| 133 |
+
if hasattr(stage, 'upsample'):
|
| 134 |
+
x = stage.upsample(x)
|
| 135 |
+
|
| 136 |
+
return self.conv_out(F.silu(self.norm_out(x)))
|
ComfyUI/comfy/ldm/hunyuan_video/vae_refiner.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from comfy.ldm.modules.diffusionmodules.model import ResnetBlock, AttnBlock, CarriedConv3d, Normalize, conv_carry_causal_3d, torch_cat_if_needed
|
| 5 |
+
import comfy.ops
|
| 6 |
+
import comfy.ldm.models.autoencoder
|
| 7 |
+
import comfy.model_management
|
| 8 |
+
ops = comfy.ops.disable_weight_init
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class RMS_norm(nn.Module):
|
| 12 |
+
def __init__(self, dim):
|
| 13 |
+
super().__init__()
|
| 14 |
+
shape = (dim, 1, 1, 1)
|
| 15 |
+
self.scale = dim**0.5
|
| 16 |
+
self.gamma = nn.Parameter(torch.empty(shape))
|
| 17 |
+
|
| 18 |
+
def forward(self, x):
|
| 19 |
+
return F.normalize(x, dim=1) * self.scale * comfy.model_management.cast_to(self.gamma, dtype=x.dtype, device=x.device)
|
| 20 |
+
|
| 21 |
+
class DnSmpl(nn.Module):
|
| 22 |
+
def __init__(self, ic, oc, tds, refiner_vae, op):
|
| 23 |
+
super().__init__()
|
| 24 |
+
fct = 2 * 2 * 2 if tds else 1 * 2 * 2
|
| 25 |
+
assert oc % fct == 0
|
| 26 |
+
self.conv = op(ic, oc // fct, kernel_size=3, stride=1, padding=1)
|
| 27 |
+
self.refiner_vae = refiner_vae
|
| 28 |
+
|
| 29 |
+
self.tds = tds
|
| 30 |
+
self.gs = fct * ic // oc
|
| 31 |
+
|
| 32 |
+
def forward(self, x, conv_carry_in=None, conv_carry_out=None):
|
| 33 |
+
r1 = 2 if self.tds else 1
|
| 34 |
+
h = conv_carry_causal_3d([x], self.conv, conv_carry_in, conv_carry_out)
|
| 35 |
+
|
| 36 |
+
if self.tds and self.refiner_vae and conv_carry_in is None:
|
| 37 |
+
|
| 38 |
+
hf = h[:, :, :1, :, :]
|
| 39 |
+
b, c, f, ht, wd = hf.shape
|
| 40 |
+
hf = hf.reshape(b, c, f, ht // 2, 2, wd // 2, 2)
|
| 41 |
+
hf = hf.permute(0, 4, 6, 1, 2, 3, 5)
|
| 42 |
+
hf = hf.reshape(b, 2 * 2 * c, f, ht // 2, wd // 2)
|
| 43 |
+
hf = torch.cat([hf, hf], dim=1)
|
| 44 |
+
|
| 45 |
+
h = h[:, :, 1:, :, :]
|
| 46 |
+
|
| 47 |
+
xf = x[:, :, :1, :, :]
|
| 48 |
+
b, ci, f, ht, wd = xf.shape
|
| 49 |
+
xf = xf.reshape(b, ci, f, ht // 2, 2, wd // 2, 2)
|
| 50 |
+
xf = xf.permute(0, 4, 6, 1, 2, 3, 5)
|
| 51 |
+
xf = xf.reshape(b, 2 * 2 * ci, f, ht // 2, wd // 2)
|
| 52 |
+
B, C, T, H, W = xf.shape
|
| 53 |
+
xf = xf.view(B, hf.shape[1], self.gs // 2, T, H, W).mean(dim=2)
|
| 54 |
+
|
| 55 |
+
x = x[:, :, 1:, :, :]
|
| 56 |
+
|
| 57 |
+
if h.shape[2] == 0:
|
| 58 |
+
return hf + xf
|
| 59 |
+
|
| 60 |
+
b, c, frms, ht, wd = h.shape
|
| 61 |
+
nf = frms // r1
|
| 62 |
+
h = h.reshape(b, c, nf, r1, ht // 2, 2, wd // 2, 2)
|
| 63 |
+
h = h.permute(0, 3, 5, 7, 1, 2, 4, 6)
|
| 64 |
+
h = h.reshape(b, r1 * 2 * 2 * c, nf, ht // 2, wd // 2)
|
| 65 |
+
|
| 66 |
+
b, ci, frms, ht, wd = x.shape
|
| 67 |
+
nf = frms // r1
|
| 68 |
+
x = x.reshape(b, ci, nf, r1, ht // 2, 2, wd // 2, 2)
|
| 69 |
+
x = x.permute(0, 3, 5, 7, 1, 2, 4, 6)
|
| 70 |
+
x = x.reshape(b, r1 * 2 * 2 * ci, nf, ht // 2, wd // 2)
|
| 71 |
+
B, C, T, H, W = x.shape
|
| 72 |
+
x = x.view(B, h.shape[1], self.gs, T, H, W).mean(dim=2)
|
| 73 |
+
|
| 74 |
+
if self.tds and self.refiner_vae and conv_carry_in is None:
|
| 75 |
+
h = torch.cat([hf, h], dim=2)
|
| 76 |
+
x = torch.cat([xf, x], dim=2)
|
| 77 |
+
|
| 78 |
+
return h + x
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class UpSmpl(nn.Module):
|
| 82 |
+
def __init__(self, ic, oc, tus, refiner_vae, op):
|
| 83 |
+
super().__init__()
|
| 84 |
+
fct = 2 * 2 * 2 if tus else 1 * 2 * 2
|
| 85 |
+
self.conv = op(ic, oc * fct, kernel_size=3, stride=1, padding=1)
|
| 86 |
+
self.refiner_vae = refiner_vae
|
| 87 |
+
|
| 88 |
+
self.tus = tus
|
| 89 |
+
self.rp = fct * oc // ic
|
| 90 |
+
|
| 91 |
+
def forward(self, x, conv_carry_in=None, conv_carry_out=None):
|
| 92 |
+
r1 = 2 if self.tus else 1
|
| 93 |
+
h = conv_carry_causal_3d([x], self.conv, conv_carry_in, conv_carry_out)
|
| 94 |
+
|
| 95 |
+
if self.tus and self.refiner_vae and conv_carry_in is None:
|
| 96 |
+
hf = h[:, :, :1, :, :]
|
| 97 |
+
b, c, f, ht, wd = hf.shape
|
| 98 |
+
nc = c // (2 * 2)
|
| 99 |
+
hf = hf.reshape(b, 2, 2, nc, f, ht, wd)
|
| 100 |
+
hf = hf.permute(0, 3, 4, 5, 1, 6, 2)
|
| 101 |
+
hf = hf.reshape(b, nc, f, ht * 2, wd * 2)
|
| 102 |
+
hf = hf[:, : hf.shape[1] // 2]
|
| 103 |
+
|
| 104 |
+
h = h[:, :, 1:, :, :]
|
| 105 |
+
|
| 106 |
+
xf = x[:, :, :1, :, :]
|
| 107 |
+
b, ci, f, ht, wd = xf.shape
|
| 108 |
+
xf = xf.repeat_interleave(repeats=self.rp // 2, dim=1)
|
| 109 |
+
b, c, f, ht, wd = xf.shape
|
| 110 |
+
nc = c // (2 * 2)
|
| 111 |
+
xf = xf.reshape(b, 2, 2, nc, f, ht, wd)
|
| 112 |
+
xf = xf.permute(0, 3, 4, 5, 1, 6, 2)
|
| 113 |
+
xf = xf.reshape(b, nc, f, ht * 2, wd * 2)
|
| 114 |
+
|
| 115 |
+
x = x[:, :, 1:, :, :]
|
| 116 |
+
|
| 117 |
+
b, c, frms, ht, wd = h.shape
|
| 118 |
+
nc = c // (r1 * 2 * 2)
|
| 119 |
+
h = h.reshape(b, r1, 2, 2, nc, frms, ht, wd)
|
| 120 |
+
h = h.permute(0, 4, 5, 1, 6, 2, 7, 3)
|
| 121 |
+
h = h.reshape(b, nc, frms * r1, ht * 2, wd * 2)
|
| 122 |
+
|
| 123 |
+
x = x.repeat_interleave(repeats=self.rp, dim=1)
|
| 124 |
+
b, c, frms, ht, wd = x.shape
|
| 125 |
+
nc = c // (r1 * 2 * 2)
|
| 126 |
+
x = x.reshape(b, r1, 2, 2, nc, frms, ht, wd)
|
| 127 |
+
x = x.permute(0, 4, 5, 1, 6, 2, 7, 3)
|
| 128 |
+
x = x.reshape(b, nc, frms * r1, ht * 2, wd * 2)
|
| 129 |
+
|
| 130 |
+
if self.tus and self.refiner_vae and conv_carry_in is None:
|
| 131 |
+
h = torch.cat([hf, h], dim=2)
|
| 132 |
+
x = torch.cat([xf, x], dim=2)
|
| 133 |
+
|
| 134 |
+
return h + x
|
| 135 |
+
|
| 136 |
+
class Encoder(nn.Module):
|
| 137 |
+
def __init__(self, in_channels, z_channels, block_out_channels, num_res_blocks,
|
| 138 |
+
ffactor_spatial, ffactor_temporal, downsample_match_channel=True, refiner_vae=True, **_):
|
| 139 |
+
super().__init__()
|
| 140 |
+
self.z_channels = z_channels
|
| 141 |
+
self.block_out_channels = block_out_channels
|
| 142 |
+
self.num_res_blocks = num_res_blocks
|
| 143 |
+
self.ffactor_temporal = ffactor_temporal
|
| 144 |
+
|
| 145 |
+
self.refiner_vae = refiner_vae
|
| 146 |
+
if self.refiner_vae:
|
| 147 |
+
conv_op = CarriedConv3d
|
| 148 |
+
norm_op = RMS_norm
|
| 149 |
+
else:
|
| 150 |
+
conv_op = ops.Conv3d
|
| 151 |
+
norm_op = Normalize
|
| 152 |
+
|
| 153 |
+
self.conv_in = conv_op(in_channels, block_out_channels[0], 3, 1, 1)
|
| 154 |
+
|
| 155 |
+
self.down = nn.ModuleList()
|
| 156 |
+
ch = block_out_channels[0]
|
| 157 |
+
depth = (ffactor_spatial >> 1).bit_length()
|
| 158 |
+
depth_temporal = ((ffactor_spatial // self.ffactor_temporal) >> 1).bit_length()
|
| 159 |
+
|
| 160 |
+
for i, tgt in enumerate(block_out_channels):
|
| 161 |
+
stage = nn.Module()
|
| 162 |
+
stage.block = nn.ModuleList([ResnetBlock(in_channels=ch if j == 0 else tgt,
|
| 163 |
+
out_channels=tgt,
|
| 164 |
+
temb_channels=0,
|
| 165 |
+
conv_op=conv_op, norm_op=norm_op)
|
| 166 |
+
for j in range(num_res_blocks)])
|
| 167 |
+
ch = tgt
|
| 168 |
+
if i < depth:
|
| 169 |
+
nxt = block_out_channels[i + 1] if i + 1 < len(block_out_channels) and downsample_match_channel else ch
|
| 170 |
+
stage.downsample = DnSmpl(ch, nxt, tds=i >= depth_temporal, refiner_vae=self.refiner_vae, op=conv_op)
|
| 171 |
+
ch = nxt
|
| 172 |
+
self.down.append(stage)
|
| 173 |
+
|
| 174 |
+
self.mid = nn.Module()
|
| 175 |
+
self.mid.block_1 = ResnetBlock(in_channels=ch, out_channels=ch, conv_op=conv_op, norm_op=norm_op)
|
| 176 |
+
self.mid.attn_1 = AttnBlock(ch, conv_op=ops.Conv3d, norm_op=norm_op)
|
| 177 |
+
self.mid.block_2 = ResnetBlock(in_channels=ch, out_channels=ch, conv_op=conv_op, norm_op=norm_op)
|
| 178 |
+
|
| 179 |
+
self.norm_out = norm_op(ch)
|
| 180 |
+
self.conv_out = conv_op(ch, z_channels << 1, 3, 1, 1)
|
| 181 |
+
|
| 182 |
+
self.regul = comfy.ldm.models.autoencoder.DiagonalGaussianRegularizer()
|
| 183 |
+
|
| 184 |
+
def forward(self, x):
|
| 185 |
+
if not self.refiner_vae and x.shape[2] == 1:
|
| 186 |
+
x = x.expand(-1, -1, self.ffactor_temporal, -1, -1)
|
| 187 |
+
|
| 188 |
+
if self.refiner_vae:
|
| 189 |
+
xl = [x[:, :, :1, :, :]]
|
| 190 |
+
if x.shape[2] > self.ffactor_temporal:
|
| 191 |
+
xl += torch.split(x[:, :, 1: 1 + ((x.shape[2] - 1) // self.ffactor_temporal) * self.ffactor_temporal, :, :], self.ffactor_temporal * 2, dim=2)
|
| 192 |
+
x = xl
|
| 193 |
+
else:
|
| 194 |
+
x = [x]
|
| 195 |
+
out = []
|
| 196 |
+
|
| 197 |
+
conv_carry_in = None
|
| 198 |
+
|
| 199 |
+
for i, x1 in enumerate(x):
|
| 200 |
+
conv_carry_out = []
|
| 201 |
+
if i == len(x) - 1:
|
| 202 |
+
conv_carry_out = None
|
| 203 |
+
|
| 204 |
+
x1 = [ x1 ]
|
| 205 |
+
x1 = conv_carry_causal_3d(x1, self.conv_in, conv_carry_in, conv_carry_out)
|
| 206 |
+
|
| 207 |
+
for stage in self.down:
|
| 208 |
+
for blk in stage.block:
|
| 209 |
+
x1 = blk(x1, None, conv_carry_in, conv_carry_out)
|
| 210 |
+
if hasattr(stage, 'downsample'):
|
| 211 |
+
x1 = stage.downsample(x1, conv_carry_in, conv_carry_out)
|
| 212 |
+
|
| 213 |
+
out.append(x1)
|
| 214 |
+
conv_carry_in = conv_carry_out
|
| 215 |
+
|
| 216 |
+
out = torch_cat_if_needed(out, dim=2)
|
| 217 |
+
|
| 218 |
+
x = self.mid.block_2(self.mid.attn_1(self.mid.block_1(out)))
|
| 219 |
+
del out
|
| 220 |
+
|
| 221 |
+
b, c, t, h, w = x.shape
|
| 222 |
+
grp = c // (self.z_channels << 1)
|
| 223 |
+
skip = x.view(b, c // grp, grp, t, h, w).mean(2)
|
| 224 |
+
|
| 225 |
+
out = conv_carry_causal_3d([F.silu(self.norm_out(x))], self.conv_out) + skip
|
| 226 |
+
|
| 227 |
+
if self.refiner_vae:
|
| 228 |
+
out = self.regul(out)[0]
|
| 229 |
+
|
| 230 |
+
return out
|
| 231 |
+
|
| 232 |
+
class Decoder(nn.Module):
|
| 233 |
+
def __init__(self, z_channels, out_channels, block_out_channels, num_res_blocks,
|
| 234 |
+
ffactor_spatial, ffactor_temporal, upsample_match_channel=True, refiner_vae=True, **_):
|
| 235 |
+
super().__init__()
|
| 236 |
+
block_out_channels = block_out_channels[::-1]
|
| 237 |
+
self.z_channels = z_channels
|
| 238 |
+
self.block_out_channels = block_out_channels
|
| 239 |
+
self.num_res_blocks = num_res_blocks
|
| 240 |
+
|
| 241 |
+
self.refiner_vae = refiner_vae
|
| 242 |
+
if self.refiner_vae:
|
| 243 |
+
conv_op = CarriedConv3d
|
| 244 |
+
norm_op = RMS_norm
|
| 245 |
+
else:
|
| 246 |
+
conv_op = ops.Conv3d
|
| 247 |
+
norm_op = Normalize
|
| 248 |
+
|
| 249 |
+
ch = block_out_channels[0]
|
| 250 |
+
self.conv_in = conv_op(z_channels, ch, kernel_size=3, stride=1, padding=1)
|
| 251 |
+
|
| 252 |
+
self.mid = nn.Module()
|
| 253 |
+
self.mid.block_1 = ResnetBlock(in_channels=ch, out_channels=ch, conv_op=conv_op, norm_op=norm_op)
|
| 254 |
+
self.mid.attn_1 = AttnBlock(ch, conv_op=ops.Conv3d, norm_op=norm_op)
|
| 255 |
+
self.mid.block_2 = ResnetBlock(in_channels=ch, out_channels=ch, conv_op=conv_op, norm_op=norm_op)
|
| 256 |
+
|
| 257 |
+
self.up = nn.ModuleList()
|
| 258 |
+
depth = (ffactor_spatial >> 1).bit_length()
|
| 259 |
+
depth_temporal = (ffactor_temporal >> 1).bit_length()
|
| 260 |
+
|
| 261 |
+
for i, tgt in enumerate(block_out_channels):
|
| 262 |
+
stage = nn.Module()
|
| 263 |
+
stage.block = nn.ModuleList([ResnetBlock(in_channels=ch if j == 0 else tgt,
|
| 264 |
+
out_channels=tgt,
|
| 265 |
+
temb_channels=0,
|
| 266 |
+
conv_op=conv_op, norm_op=norm_op)
|
| 267 |
+
for j in range(num_res_blocks + 1)])
|
| 268 |
+
ch = tgt
|
| 269 |
+
if i < depth:
|
| 270 |
+
nxt = block_out_channels[i + 1] if i + 1 < len(block_out_channels) and upsample_match_channel else ch
|
| 271 |
+
stage.upsample = UpSmpl(ch, nxt, tus=i < depth_temporal, refiner_vae=self.refiner_vae, op=conv_op)
|
| 272 |
+
ch = nxt
|
| 273 |
+
self.up.append(stage)
|
| 274 |
+
|
| 275 |
+
self.norm_out = norm_op(ch)
|
| 276 |
+
self.conv_out = conv_op(ch, out_channels, 3, stride=1, padding=1)
|
| 277 |
+
|
| 278 |
+
def forward(self, z):
|
| 279 |
+
x = conv_carry_causal_3d([z], self.conv_in) + z.repeat_interleave(self.block_out_channels[0] // self.z_channels, 1)
|
| 280 |
+
x = self.mid.block_2(self.mid.attn_1(self.mid.block_1(x)))
|
| 281 |
+
|
| 282 |
+
if self.refiner_vae:
|
| 283 |
+
x = torch.split(x, 2, dim=2)
|
| 284 |
+
else:
|
| 285 |
+
x = [ x ]
|
| 286 |
+
out = []
|
| 287 |
+
|
| 288 |
+
conv_carry_in = None
|
| 289 |
+
|
| 290 |
+
for i, x1 in enumerate(x):
|
| 291 |
+
conv_carry_out = []
|
| 292 |
+
if i == len(x) - 1:
|
| 293 |
+
conv_carry_out = None
|
| 294 |
+
for stage in self.up:
|
| 295 |
+
for blk in stage.block:
|
| 296 |
+
x1 = blk(x1, None, conv_carry_in, conv_carry_out)
|
| 297 |
+
if hasattr(stage, 'upsample'):
|
| 298 |
+
x1 = stage.upsample(x1, conv_carry_in, conv_carry_out)
|
| 299 |
+
|
| 300 |
+
x1 = [ F.silu(self.norm_out(x1)) ]
|
| 301 |
+
x1 = conv_carry_causal_3d(x1, self.conv_out, conv_carry_in, conv_carry_out)
|
| 302 |
+
out.append(x1)
|
| 303 |
+
conv_carry_in = conv_carry_out
|
| 304 |
+
del x
|
| 305 |
+
|
| 306 |
+
out = torch_cat_if_needed(out, dim=2)
|
| 307 |
+
|
| 308 |
+
if not self.refiner_vae:
|
| 309 |
+
if z.shape[-3] == 1:
|
| 310 |
+
out = out[:, :, -1:]
|
| 311 |
+
|
| 312 |
+
return out
|
| 313 |
+
|
ComfyUI/comfy/ldm/hydit/__pycache__/attn_layers.cpython-312.pyc
ADDED
|
Binary file (14 kB). View file
|
|
|
ComfyUI/comfy/ldm/hydit/__pycache__/controlnet.cpython-312.pyc
ADDED
|
Binary file (9.49 kB). View file
|
|
|
ComfyUI/comfy/ldm/hydit/__pycache__/models.cpython-312.pyc
ADDED
|
Binary file (18.5 kB). View file
|
|
|
ComfyUI/comfy/ldm/hydit/__pycache__/poolers.cpython-312.pyc
ADDED
|
Binary file (3.54 kB). View file
|
|
|
ComfyUI/comfy/ldm/hydit/__pycache__/posemb_layers.cpython-312.pyc
ADDED
|
Binary file (9.8 kB). View file
|
|
|
ComfyUI/comfy/ldm/hydit/attn_layers.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from typing import Tuple, Union, Optional
|
| 4 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def reshape_for_broadcast(freqs_cis: Union[torch.Tensor, Tuple[torch.Tensor]], x: torch.Tensor, head_first=False):
|
| 8 |
+
"""
|
| 9 |
+
Reshape frequency tensor for broadcasting it with another tensor.
|
| 10 |
+
|
| 11 |
+
This function reshapes the frequency tensor to have the same shape as the target tensor 'x'
|
| 12 |
+
for the purpose of broadcasting the frequency tensor during element-wise operations.
|
| 13 |
+
|
| 14 |
+
Args:
|
| 15 |
+
freqs_cis (Union[torch.Tensor, Tuple[torch.Tensor]]): Frequency tensor to be reshaped.
|
| 16 |
+
x (torch.Tensor): Target tensor for broadcasting compatibility.
|
| 17 |
+
head_first (bool): head dimension first (except batch dim) or not.
|
| 18 |
+
|
| 19 |
+
Returns:
|
| 20 |
+
torch.Tensor: Reshaped frequency tensor.
|
| 21 |
+
|
| 22 |
+
Raises:
|
| 23 |
+
AssertionError: If the frequency tensor doesn't match the expected shape.
|
| 24 |
+
AssertionError: If the target tensor 'x' doesn't have the expected number of dimensions.
|
| 25 |
+
"""
|
| 26 |
+
ndim = x.ndim
|
| 27 |
+
assert 0 <= 1 < ndim
|
| 28 |
+
|
| 29 |
+
if isinstance(freqs_cis, tuple):
|
| 30 |
+
# freqs_cis: (cos, sin) in real space
|
| 31 |
+
if head_first:
|
| 32 |
+
assert freqs_cis[0].shape == (x.shape[-2], x.shape[-1]), f'freqs_cis shape {freqs_cis[0].shape} does not match x shape {x.shape}'
|
| 33 |
+
shape = [d if i == ndim - 2 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
| 34 |
+
else:
|
| 35 |
+
assert freqs_cis[0].shape == (x.shape[1], x.shape[-1]), f'freqs_cis shape {freqs_cis[0].shape} does not match x shape {x.shape}'
|
| 36 |
+
shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
| 37 |
+
return freqs_cis[0].view(*shape), freqs_cis[1].view(*shape)
|
| 38 |
+
else:
|
| 39 |
+
# freqs_cis: values in complex space
|
| 40 |
+
if head_first:
|
| 41 |
+
assert freqs_cis.shape == (x.shape[-2], x.shape[-1]), f'freqs_cis shape {freqs_cis.shape} does not match x shape {x.shape}'
|
| 42 |
+
shape = [d if i == ndim - 2 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
| 43 |
+
else:
|
| 44 |
+
assert freqs_cis.shape == (x.shape[1], x.shape[-1]), f'freqs_cis shape {freqs_cis.shape} does not match x shape {x.shape}'
|
| 45 |
+
shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
| 46 |
+
return freqs_cis.view(*shape)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def rotate_half(x):
|
| 50 |
+
x_real, x_imag = x.reshape(*x.shape[:-1], -1, 2).unbind(-1) # [B, S, H, D//2]
|
| 51 |
+
return torch.stack([-x_imag, x_real], dim=-1).flatten(3)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def apply_rotary_emb(
|
| 55 |
+
xq: torch.Tensor,
|
| 56 |
+
xk: Optional[torch.Tensor],
|
| 57 |
+
freqs_cis: Union[torch.Tensor, Tuple[torch.Tensor]],
|
| 58 |
+
head_first: bool = False,
|
| 59 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 60 |
+
"""
|
| 61 |
+
Apply rotary embeddings to input tensors using the given frequency tensor.
|
| 62 |
+
|
| 63 |
+
This function applies rotary embeddings to the given query 'xq' and key 'xk' tensors using the provided
|
| 64 |
+
frequency tensor 'freqs_cis'. The input tensors are reshaped as complex numbers, and the frequency tensor
|
| 65 |
+
is reshaped for broadcasting compatibility. The resulting tensors contain rotary embeddings and are
|
| 66 |
+
returned as real tensors.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
xq (torch.Tensor): Query tensor to apply rotary embeddings. [B, S, H, D]
|
| 70 |
+
xk (torch.Tensor): Key tensor to apply rotary embeddings. [B, S, H, D]
|
| 71 |
+
freqs_cis (Union[torch.Tensor, Tuple[torch.Tensor]]): Precomputed frequency tensor for complex exponentials.
|
| 72 |
+
head_first (bool): head dimension first (except batch dim) or not.
|
| 73 |
+
|
| 74 |
+
Returns:
|
| 75 |
+
Tuple[torch.Tensor, torch.Tensor]: Tuple of modified query tensor and key tensor with rotary embeddings.
|
| 76 |
+
|
| 77 |
+
"""
|
| 78 |
+
xk_out = None
|
| 79 |
+
if isinstance(freqs_cis, tuple):
|
| 80 |
+
cos, sin = reshape_for_broadcast(freqs_cis, xq, head_first) # [S, D]
|
| 81 |
+
xq_out = (xq * cos + rotate_half(xq) * sin)
|
| 82 |
+
if xk is not None:
|
| 83 |
+
xk_out = (xk * cos + rotate_half(xk) * sin)
|
| 84 |
+
else:
|
| 85 |
+
xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2)) # [B, S, H, D//2]
|
| 86 |
+
freqs_cis = reshape_for_broadcast(freqs_cis, xq_, head_first).to(xq.device) # [S, D//2] --> [1, S, 1, D//2]
|
| 87 |
+
xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(3).type_as(xq)
|
| 88 |
+
if xk is not None:
|
| 89 |
+
xk_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2)) # [B, S, H, D//2]
|
| 90 |
+
xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(3).type_as(xk)
|
| 91 |
+
|
| 92 |
+
return xq_out, xk_out
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
class CrossAttention(nn.Module):
|
| 97 |
+
"""
|
| 98 |
+
Use QK Normalization.
|
| 99 |
+
"""
|
| 100 |
+
def __init__(self,
|
| 101 |
+
qdim,
|
| 102 |
+
kdim,
|
| 103 |
+
num_heads,
|
| 104 |
+
qkv_bias=True,
|
| 105 |
+
qk_norm=False,
|
| 106 |
+
attn_drop=0.0,
|
| 107 |
+
proj_drop=0.0,
|
| 108 |
+
attn_precision=None,
|
| 109 |
+
device=None,
|
| 110 |
+
dtype=None,
|
| 111 |
+
operations=None,
|
| 112 |
+
):
|
| 113 |
+
factory_kwargs = {'device': device, 'dtype': dtype}
|
| 114 |
+
super().__init__()
|
| 115 |
+
self.attn_precision = attn_precision
|
| 116 |
+
self.qdim = qdim
|
| 117 |
+
self.kdim = kdim
|
| 118 |
+
self.num_heads = num_heads
|
| 119 |
+
assert self.qdim % num_heads == 0, "self.qdim must be divisible by num_heads"
|
| 120 |
+
self.head_dim = self.qdim // num_heads
|
| 121 |
+
assert self.head_dim % 8 == 0 and self.head_dim <= 128, "Only support head_dim <= 128 and divisible by 8"
|
| 122 |
+
self.scale = self.head_dim ** -0.5
|
| 123 |
+
|
| 124 |
+
self.q_proj = operations.Linear(qdim, qdim, bias=qkv_bias, **factory_kwargs)
|
| 125 |
+
self.kv_proj = operations.Linear(kdim, 2 * qdim, bias=qkv_bias, **factory_kwargs)
|
| 126 |
+
|
| 127 |
+
# TODO: eps should be 1 / 65530 if using fp16
|
| 128 |
+
self.q_norm = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1e-6, dtype=dtype, device=device) if qk_norm else nn.Identity()
|
| 129 |
+
self.k_norm = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1e-6, dtype=dtype, device=device) if qk_norm else nn.Identity()
|
| 130 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 131 |
+
self.out_proj = operations.Linear(qdim, qdim, bias=qkv_bias, **factory_kwargs)
|
| 132 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 133 |
+
|
| 134 |
+
def forward(self, x, y, freqs_cis_img=None):
|
| 135 |
+
"""
|
| 136 |
+
Parameters
|
| 137 |
+
----------
|
| 138 |
+
x: torch.Tensor
|
| 139 |
+
(batch, seqlen1, hidden_dim) (where hidden_dim = num heads * head dim)
|
| 140 |
+
y: torch.Tensor
|
| 141 |
+
(batch, seqlen2, hidden_dim2)
|
| 142 |
+
freqs_cis_img: torch.Tensor
|
| 143 |
+
(batch, hidden_dim // 2), RoPE for image
|
| 144 |
+
"""
|
| 145 |
+
b, s1, c = x.shape # [b, s1, D]
|
| 146 |
+
_, s2, c = y.shape # [b, s2, 1024]
|
| 147 |
+
|
| 148 |
+
q = self.q_proj(x).view(b, s1, self.num_heads, self.head_dim) # [b, s1, h, d]
|
| 149 |
+
kv = self.kv_proj(y).view(b, s2, 2, self.num_heads, self.head_dim) # [b, s2, 2, h, d]
|
| 150 |
+
k, v = kv.unbind(dim=2) # [b, s, h, d]
|
| 151 |
+
q = self.q_norm(q)
|
| 152 |
+
k = self.k_norm(k)
|
| 153 |
+
|
| 154 |
+
# Apply RoPE if needed
|
| 155 |
+
if freqs_cis_img is not None:
|
| 156 |
+
qq, _ = apply_rotary_emb(q, None, freqs_cis_img)
|
| 157 |
+
assert qq.shape == q.shape, f'qq: {qq.shape}, q: {q.shape}'
|
| 158 |
+
q = qq
|
| 159 |
+
|
| 160 |
+
q = q.transpose(-2, -3).contiguous() # q -> B, L1, H, C - B, H, L1, C
|
| 161 |
+
k = k.transpose(-2, -3).contiguous() # k -> B, L2, H, C - B, H, C, L2
|
| 162 |
+
v = v.transpose(-2, -3).contiguous()
|
| 163 |
+
|
| 164 |
+
context = optimized_attention(q, k, v, self.num_heads, skip_reshape=True, attn_precision=self.attn_precision)
|
| 165 |
+
|
| 166 |
+
out = self.out_proj(context) # context.reshape - B, L1, -1
|
| 167 |
+
out = self.proj_drop(out)
|
| 168 |
+
|
| 169 |
+
out_tuple = (out,)
|
| 170 |
+
|
| 171 |
+
return out_tuple
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
class Attention(nn.Module):
|
| 175 |
+
"""
|
| 176 |
+
We rename some layer names to align with flash attention
|
| 177 |
+
"""
|
| 178 |
+
def __init__(self, dim, num_heads, qkv_bias=True, qk_norm=False, attn_drop=0., proj_drop=0., attn_precision=None, dtype=None, device=None, operations=None):
|
| 179 |
+
super().__init__()
|
| 180 |
+
self.attn_precision = attn_precision
|
| 181 |
+
self.dim = dim
|
| 182 |
+
self.num_heads = num_heads
|
| 183 |
+
assert self.dim % num_heads == 0, 'dim should be divisible by num_heads'
|
| 184 |
+
self.head_dim = self.dim // num_heads
|
| 185 |
+
# This assertion is aligned with flash attention
|
| 186 |
+
assert self.head_dim % 8 == 0 and self.head_dim <= 128, "Only support head_dim <= 128 and divisible by 8"
|
| 187 |
+
self.scale = self.head_dim ** -0.5
|
| 188 |
+
|
| 189 |
+
# qkv --> Wqkv
|
| 190 |
+
self.Wqkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device)
|
| 191 |
+
# TODO: eps should be 1 / 65530 if using fp16
|
| 192 |
+
self.q_norm = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1e-6, dtype=dtype, device=device) if qk_norm else nn.Identity()
|
| 193 |
+
self.k_norm = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1e-6, dtype=dtype, device=device) if qk_norm else nn.Identity()
|
| 194 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 195 |
+
self.out_proj = operations.Linear(dim, dim, dtype=dtype, device=device)
|
| 196 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 197 |
+
|
| 198 |
+
def forward(self, x, freqs_cis_img=None):
|
| 199 |
+
B, N, C = x.shape
|
| 200 |
+
qkv = self.Wqkv(x).reshape(B, N, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4) # [3, b, h, s, d]
|
| 201 |
+
q, k, v = qkv.unbind(0) # [b, h, s, d]
|
| 202 |
+
q = self.q_norm(q) # [b, h, s, d]
|
| 203 |
+
k = self.k_norm(k) # [b, h, s, d]
|
| 204 |
+
|
| 205 |
+
# Apply RoPE if needed
|
| 206 |
+
if freqs_cis_img is not None:
|
| 207 |
+
qq, kk = apply_rotary_emb(q, k, freqs_cis_img, head_first=True)
|
| 208 |
+
assert qq.shape == q.shape and kk.shape == k.shape, \
|
| 209 |
+
f'qq: {qq.shape}, q: {q.shape}, kk: {kk.shape}, k: {k.shape}'
|
| 210 |
+
q, k = qq, kk
|
| 211 |
+
|
| 212 |
+
x = optimized_attention(q, k, v, self.num_heads, skip_reshape=True, attn_precision=self.attn_precision)
|
| 213 |
+
x = self.out_proj(x)
|
| 214 |
+
x = self.proj_drop(x)
|
| 215 |
+
|
| 216 |
+
out_tuple = (x,)
|
| 217 |
+
|
| 218 |
+
return out_tuple
|
ComfyUI/comfy/ldm/hydit/controlnet.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
from comfy.ldm.modules.diffusionmodules.mmdit import (
|
| 7 |
+
TimestepEmbedder,
|
| 8 |
+
PatchEmbed,
|
| 9 |
+
)
|
| 10 |
+
from .poolers import AttentionPool
|
| 11 |
+
|
| 12 |
+
import comfy.latent_formats
|
| 13 |
+
from .models import HunYuanDiTBlock, calc_rope
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class HunYuanControlNet(nn.Module):
|
| 18 |
+
"""
|
| 19 |
+
HunYuanDiT: Diffusion model with a Transformer backbone.
|
| 20 |
+
|
| 21 |
+
Inherit ModelMixin and ConfigMixin to be compatible with the sampler StableDiffusionPipeline of diffusers.
|
| 22 |
+
|
| 23 |
+
Inherit PeftAdapterMixin to be compatible with the PEFT training pipeline.
|
| 24 |
+
|
| 25 |
+
Parameters
|
| 26 |
+
----------
|
| 27 |
+
args: argparse.Namespace
|
| 28 |
+
The arguments parsed by argparse.
|
| 29 |
+
input_size: tuple
|
| 30 |
+
The size of the input image.
|
| 31 |
+
patch_size: int
|
| 32 |
+
The size of the patch.
|
| 33 |
+
in_channels: int
|
| 34 |
+
The number of input channels.
|
| 35 |
+
hidden_size: int
|
| 36 |
+
The hidden size of the transformer backbone.
|
| 37 |
+
depth: int
|
| 38 |
+
The number of transformer blocks.
|
| 39 |
+
num_heads: int
|
| 40 |
+
The number of attention heads.
|
| 41 |
+
mlp_ratio: float
|
| 42 |
+
The ratio of the hidden size of the MLP in the transformer block.
|
| 43 |
+
log_fn: callable
|
| 44 |
+
The logging function.
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
def __init__(
|
| 48 |
+
self,
|
| 49 |
+
input_size: tuple = 128,
|
| 50 |
+
patch_size: int = 2,
|
| 51 |
+
in_channels: int = 4,
|
| 52 |
+
hidden_size: int = 1408,
|
| 53 |
+
depth: int = 40,
|
| 54 |
+
num_heads: int = 16,
|
| 55 |
+
mlp_ratio: float = 4.3637,
|
| 56 |
+
text_states_dim=1024,
|
| 57 |
+
text_states_dim_t5=2048,
|
| 58 |
+
text_len=77,
|
| 59 |
+
text_len_t5=256,
|
| 60 |
+
qk_norm=True, # See http://arxiv.org/abs/2302.05442 for details.
|
| 61 |
+
size_cond=False,
|
| 62 |
+
use_style_cond=False,
|
| 63 |
+
learn_sigma=True,
|
| 64 |
+
norm="layer",
|
| 65 |
+
log_fn: callable = print,
|
| 66 |
+
attn_precision=None,
|
| 67 |
+
dtype=None,
|
| 68 |
+
device=None,
|
| 69 |
+
operations=None,
|
| 70 |
+
**kwargs,
|
| 71 |
+
):
|
| 72 |
+
super().__init__()
|
| 73 |
+
self.log_fn = log_fn
|
| 74 |
+
self.depth = depth
|
| 75 |
+
self.learn_sigma = learn_sigma
|
| 76 |
+
self.in_channels = in_channels
|
| 77 |
+
self.out_channels = in_channels * 2 if learn_sigma else in_channels
|
| 78 |
+
self.patch_size = patch_size
|
| 79 |
+
self.num_heads = num_heads
|
| 80 |
+
self.hidden_size = hidden_size
|
| 81 |
+
self.text_states_dim = text_states_dim
|
| 82 |
+
self.text_states_dim_t5 = text_states_dim_t5
|
| 83 |
+
self.text_len = text_len
|
| 84 |
+
self.text_len_t5 = text_len_t5
|
| 85 |
+
self.size_cond = size_cond
|
| 86 |
+
self.use_style_cond = use_style_cond
|
| 87 |
+
self.norm = norm
|
| 88 |
+
self.dtype = dtype
|
| 89 |
+
self.latent_format = comfy.latent_formats.SDXL
|
| 90 |
+
|
| 91 |
+
self.mlp_t5 = nn.Sequential(
|
| 92 |
+
nn.Linear(
|
| 93 |
+
self.text_states_dim_t5,
|
| 94 |
+
self.text_states_dim_t5 * 4,
|
| 95 |
+
bias=True,
|
| 96 |
+
dtype=dtype,
|
| 97 |
+
device=device,
|
| 98 |
+
),
|
| 99 |
+
nn.SiLU(),
|
| 100 |
+
nn.Linear(
|
| 101 |
+
self.text_states_dim_t5 * 4,
|
| 102 |
+
self.text_states_dim,
|
| 103 |
+
bias=True,
|
| 104 |
+
dtype=dtype,
|
| 105 |
+
device=device,
|
| 106 |
+
),
|
| 107 |
+
)
|
| 108 |
+
# learnable replace
|
| 109 |
+
self.text_embedding_padding = nn.Parameter(
|
| 110 |
+
torch.randn(
|
| 111 |
+
self.text_len + self.text_len_t5,
|
| 112 |
+
self.text_states_dim,
|
| 113 |
+
dtype=dtype,
|
| 114 |
+
device=device,
|
| 115 |
+
)
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
# Attention pooling
|
| 119 |
+
pooler_out_dim = 1024
|
| 120 |
+
self.pooler = AttentionPool(
|
| 121 |
+
self.text_len_t5,
|
| 122 |
+
self.text_states_dim_t5,
|
| 123 |
+
num_heads=8,
|
| 124 |
+
output_dim=pooler_out_dim,
|
| 125 |
+
dtype=dtype,
|
| 126 |
+
device=device,
|
| 127 |
+
operations=operations,
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
# Dimension of the extra input vectors
|
| 131 |
+
self.extra_in_dim = pooler_out_dim
|
| 132 |
+
|
| 133 |
+
if self.size_cond:
|
| 134 |
+
# Image size and crop size conditions
|
| 135 |
+
self.extra_in_dim += 6 * 256
|
| 136 |
+
|
| 137 |
+
if self.use_style_cond:
|
| 138 |
+
# Here we use a default learned embedder layer for future extension.
|
| 139 |
+
self.style_embedder = nn.Embedding(
|
| 140 |
+
1, hidden_size, dtype=dtype, device=device
|
| 141 |
+
)
|
| 142 |
+
self.extra_in_dim += hidden_size
|
| 143 |
+
|
| 144 |
+
# Text embedding for `add`
|
| 145 |
+
self.x_embedder = PatchEmbed(
|
| 146 |
+
input_size,
|
| 147 |
+
patch_size,
|
| 148 |
+
in_channels,
|
| 149 |
+
hidden_size,
|
| 150 |
+
dtype=dtype,
|
| 151 |
+
device=device,
|
| 152 |
+
operations=operations,
|
| 153 |
+
)
|
| 154 |
+
self.t_embedder = TimestepEmbedder(
|
| 155 |
+
hidden_size, dtype=dtype, device=device, operations=operations
|
| 156 |
+
)
|
| 157 |
+
self.extra_embedder = nn.Sequential(
|
| 158 |
+
operations.Linear(
|
| 159 |
+
self.extra_in_dim, hidden_size * 4, dtype=dtype, device=device
|
| 160 |
+
),
|
| 161 |
+
nn.SiLU(),
|
| 162 |
+
operations.Linear(
|
| 163 |
+
hidden_size * 4, hidden_size, bias=True, dtype=dtype, device=device
|
| 164 |
+
),
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
# HUnYuanDiT Blocks
|
| 168 |
+
self.blocks = nn.ModuleList(
|
| 169 |
+
[
|
| 170 |
+
HunYuanDiTBlock(
|
| 171 |
+
hidden_size=hidden_size,
|
| 172 |
+
c_emb_size=hidden_size,
|
| 173 |
+
num_heads=num_heads,
|
| 174 |
+
mlp_ratio=mlp_ratio,
|
| 175 |
+
text_states_dim=self.text_states_dim,
|
| 176 |
+
qk_norm=qk_norm,
|
| 177 |
+
norm_type=self.norm,
|
| 178 |
+
skip=False,
|
| 179 |
+
attn_precision=attn_precision,
|
| 180 |
+
dtype=dtype,
|
| 181 |
+
device=device,
|
| 182 |
+
operations=operations,
|
| 183 |
+
)
|
| 184 |
+
for _ in range(19)
|
| 185 |
+
]
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
# Input zero linear for the first block
|
| 189 |
+
self.before_proj = operations.Linear(self.hidden_size, self.hidden_size, dtype=dtype, device=device)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
# Output zero linear for the every block
|
| 193 |
+
self.after_proj_list = nn.ModuleList(
|
| 194 |
+
[
|
| 195 |
+
|
| 196 |
+
operations.Linear(
|
| 197 |
+
self.hidden_size, self.hidden_size, dtype=dtype, device=device
|
| 198 |
+
)
|
| 199 |
+
for _ in range(len(self.blocks))
|
| 200 |
+
]
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
def forward(
|
| 204 |
+
self,
|
| 205 |
+
x,
|
| 206 |
+
hint,
|
| 207 |
+
timesteps,
|
| 208 |
+
context,#encoder_hidden_states=None,
|
| 209 |
+
text_embedding_mask=None,
|
| 210 |
+
encoder_hidden_states_t5=None,
|
| 211 |
+
text_embedding_mask_t5=None,
|
| 212 |
+
image_meta_size=None,
|
| 213 |
+
style=None,
|
| 214 |
+
return_dict=False,
|
| 215 |
+
**kwarg,
|
| 216 |
+
):
|
| 217 |
+
"""
|
| 218 |
+
Forward pass of the encoder.
|
| 219 |
+
|
| 220 |
+
Parameters
|
| 221 |
+
----------
|
| 222 |
+
x: torch.Tensor
|
| 223 |
+
(B, D, H, W)
|
| 224 |
+
t: torch.Tensor
|
| 225 |
+
(B)
|
| 226 |
+
encoder_hidden_states: torch.Tensor
|
| 227 |
+
CLIP text embedding, (B, L_clip, D)
|
| 228 |
+
text_embedding_mask: torch.Tensor
|
| 229 |
+
CLIP text embedding mask, (B, L_clip)
|
| 230 |
+
encoder_hidden_states_t5: torch.Tensor
|
| 231 |
+
T5 text embedding, (B, L_t5, D)
|
| 232 |
+
text_embedding_mask_t5: torch.Tensor
|
| 233 |
+
T5 text embedding mask, (B, L_t5)
|
| 234 |
+
image_meta_size: torch.Tensor
|
| 235 |
+
(B, 6)
|
| 236 |
+
style: torch.Tensor
|
| 237 |
+
(B)
|
| 238 |
+
cos_cis_img: torch.Tensor
|
| 239 |
+
sin_cis_img: torch.Tensor
|
| 240 |
+
return_dict: bool
|
| 241 |
+
Whether to return a dictionary.
|
| 242 |
+
"""
|
| 243 |
+
condition = hint
|
| 244 |
+
if condition.shape[0] == 1:
|
| 245 |
+
condition = torch.repeat_interleave(condition, x.shape[0], dim=0)
|
| 246 |
+
|
| 247 |
+
text_states = context # 2,77,1024
|
| 248 |
+
text_states_t5 = encoder_hidden_states_t5 # 2,256,2048
|
| 249 |
+
text_states_mask = text_embedding_mask.bool() # 2,77
|
| 250 |
+
text_states_t5_mask = text_embedding_mask_t5.bool() # 2,256
|
| 251 |
+
b_t5, l_t5, c_t5 = text_states_t5.shape
|
| 252 |
+
text_states_t5 = self.mlp_t5(text_states_t5.view(-1, c_t5)).view(b_t5, l_t5, -1)
|
| 253 |
+
|
| 254 |
+
padding = comfy.ops.cast_to_input(self.text_embedding_padding, text_states)
|
| 255 |
+
|
| 256 |
+
text_states[:, -self.text_len :] = torch.where(
|
| 257 |
+
text_states_mask[:, -self.text_len :].unsqueeze(2),
|
| 258 |
+
text_states[:, -self.text_len :],
|
| 259 |
+
padding[: self.text_len],
|
| 260 |
+
)
|
| 261 |
+
text_states_t5[:, -self.text_len_t5 :] = torch.where(
|
| 262 |
+
text_states_t5_mask[:, -self.text_len_t5 :].unsqueeze(2),
|
| 263 |
+
text_states_t5[:, -self.text_len_t5 :],
|
| 264 |
+
padding[self.text_len :],
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
text_states = torch.cat([text_states, text_states_t5], dim=1) # 2,205,1024
|
| 268 |
+
|
| 269 |
+
# _, _, oh, ow = x.shape
|
| 270 |
+
# th, tw = oh // self.patch_size, ow // self.patch_size
|
| 271 |
+
|
| 272 |
+
# Get image RoPE embedding according to `reso`lution.
|
| 273 |
+
freqs_cis_img = calc_rope(
|
| 274 |
+
x, self.patch_size, self.hidden_size // self.num_heads
|
| 275 |
+
) # (cos_cis_img, sin_cis_img)
|
| 276 |
+
|
| 277 |
+
# ========================= Build time and image embedding =========================
|
| 278 |
+
t = self.t_embedder(timesteps, dtype=self.dtype)
|
| 279 |
+
x = self.x_embedder(x)
|
| 280 |
+
|
| 281 |
+
# ========================= Concatenate all extra vectors =========================
|
| 282 |
+
# Build text tokens with pooling
|
| 283 |
+
extra_vec = self.pooler(encoder_hidden_states_t5)
|
| 284 |
+
|
| 285 |
+
# Build image meta size tokens if applicable
|
| 286 |
+
# if image_meta_size is not None:
|
| 287 |
+
# image_meta_size = timestep_embedding(image_meta_size.view(-1), 256) # [B * 6, 256]
|
| 288 |
+
# if image_meta_size.dtype != self.dtype:
|
| 289 |
+
# image_meta_size = image_meta_size.half()
|
| 290 |
+
# image_meta_size = image_meta_size.view(-1, 6 * 256)
|
| 291 |
+
# extra_vec = torch.cat([extra_vec, image_meta_size], dim=1) # [B, D + 6 * 256]
|
| 292 |
+
|
| 293 |
+
# Build style tokens
|
| 294 |
+
if style is not None:
|
| 295 |
+
style_embedding = self.style_embedder(style)
|
| 296 |
+
extra_vec = torch.cat([extra_vec, style_embedding], dim=1)
|
| 297 |
+
|
| 298 |
+
# Concatenate all extra vectors
|
| 299 |
+
c = t + self.extra_embedder(extra_vec) # [B, D]
|
| 300 |
+
|
| 301 |
+
# ========================= Deal with Condition =========================
|
| 302 |
+
condition = self.x_embedder(condition)
|
| 303 |
+
|
| 304 |
+
# ========================= Forward pass through HunYuanDiT blocks =========================
|
| 305 |
+
controls = []
|
| 306 |
+
x = x + self.before_proj(condition) # add condition
|
| 307 |
+
for layer, block in enumerate(self.blocks):
|
| 308 |
+
x = block(x, c, text_states, freqs_cis_img)
|
| 309 |
+
controls.append(self.after_proj_list[layer](x)) # zero linear for output
|
| 310 |
+
|
| 311 |
+
return {"output": controls}
|