Upload folder using huggingface_hub (part 2)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +1 -0
- ComfyUI/comfy/ldm/cascade/controlnet.py +92 -0
- ComfyUI/comfy/ldm/cascade/stage_a.py +250 -0
- ComfyUI/comfy/ldm/cascade/stage_b.py +256 -0
- ComfyUI/comfy/ldm/cascade/stage_c.py +273 -0
- ComfyUI/comfy/ldm/cascade/stage_c_coder.py +98 -0
- ComfyUI/comfy/ldm/chroma/layers.py +62 -0
- ComfyUI/comfy/ldm/chroma/model.py +295 -0
- ComfyUI/comfy/ldm/chroma_radiance/layers.py +204 -0
- ComfyUI/comfy/ldm/chroma_radiance/model.py +343 -0
- ComfyUI/comfy/ldm/cogvideo/__init__.py +0 -0
- ComfyUI/comfy/ldm/cogvideo/model.py +573 -0
- ComfyUI/comfy/ldm/cogvideo/vae.py +566 -0
- ComfyUI/comfy/ldm/colormap.py +25 -0
- ComfyUI/comfy/ldm/common_dit.py +16 -0
- ComfyUI/comfy/ldm/cosmos/blocks.py +805 -0
- ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/layers3d.py +1041 -0
- ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/patching.py +377 -0
- ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/utils.py +113 -0
- ComfyUI/comfy/ldm/cosmos/model.py +552 -0
- ComfyUI/comfy/ldm/cosmos/position_embedding.py +207 -0
- ComfyUI/comfy/ldm/cosmos/predict2.py +932 -0
- ComfyUI/comfy/ldm/cosmos/vae.py +131 -0
- ComfyUI/comfy/ldm/depth_anything_3/camera.py +177 -0
- ComfyUI/comfy/ldm/depth_anything_3/dpt.py +489 -0
- ComfyUI/comfy/ldm/depth_anything_3/model.py +236 -0
- ComfyUI/comfy/ldm/depth_anything_3/preprocess.py +128 -0
- ComfyUI/comfy/ldm/depth_anything_3/ray_pose.py +272 -0
- ComfyUI/comfy/ldm/depth_anything_3/reference_view_selector.py +87 -0
- ComfyUI/comfy/ldm/depth_anything_3/transform.py +160 -0
- ComfyUI/comfy/ldm/ernie/model.py +307 -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/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/model.py +711 -0
- ComfyUI/comfy/ldm/hidream/model.py +829 -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/model.py +148 -0
- ComfyUI/comfy/ldm/hunyuan3d/vae.py +988 -0
- ComfyUI/comfy/ldm/hunyuan3dv2_1/hunyuandit.py +668 -0
.gitattributes
CHANGED
|
@@ -6,3 +6,4 @@ unstableRevolution_V3Fp16.safetensors filter=lfs diff=lfs merge=lfs -text
|
|
| 6 |
ae.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 7 |
qwen_3_4b.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 8 |
pornmaster_proSDXLV8.safetensors filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 6 |
ae.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 7 |
qwen_3_4b.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 8 |
pornmaster_proSDXLV8.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
ComfyUI/comfy/text_encoders/t5_pile_tokenizer/tokenizer.model filter=lfs diff=lfs merge=lfs -text
|
ComfyUI/comfy/ldm/cascade/controlnet.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file is part of ComfyUI.
|
| 3 |
+
Copyright (C) 2024 Stability AI
|
| 4 |
+
|
| 5 |
+
This program is free software: you can redistribute it and/or modify
|
| 6 |
+
it under the terms of the GNU General Public License as published by
|
| 7 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 8 |
+
(at your option) any later version.
|
| 9 |
+
|
| 10 |
+
This program is distributed in the hope that it will be useful,
|
| 11 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
+
GNU General Public License for more details.
|
| 14 |
+
|
| 15 |
+
You should have received a copy of the GNU General Public License
|
| 16 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import torchvision
|
| 20 |
+
from torch import nn
|
| 21 |
+
from .common import LayerNorm2d_op
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class CNetResBlock(nn.Module):
|
| 25 |
+
def __init__(self, c, dtype=None, device=None, operations=None):
|
| 26 |
+
super().__init__()
|
| 27 |
+
self.blocks = nn.Sequential(
|
| 28 |
+
LayerNorm2d_op(operations)(c, dtype=dtype, device=device),
|
| 29 |
+
nn.GELU(),
|
| 30 |
+
operations.Conv2d(c, c, kernel_size=3, padding=1),
|
| 31 |
+
LayerNorm2d_op(operations)(c, dtype=dtype, device=device),
|
| 32 |
+
nn.GELU(),
|
| 33 |
+
operations.Conv2d(c, c, kernel_size=3, padding=1),
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
def forward(self, x):
|
| 37 |
+
return x + self.blocks(x)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class ControlNet(nn.Module):
|
| 41 |
+
def __init__(self, c_in=3, c_proj=2048, proj_blocks=None, bottleneck_mode=None, dtype=None, device=None, operations=nn):
|
| 42 |
+
super().__init__()
|
| 43 |
+
if bottleneck_mode is None:
|
| 44 |
+
bottleneck_mode = 'effnet'
|
| 45 |
+
self.proj_blocks = proj_blocks
|
| 46 |
+
if bottleneck_mode == 'effnet':
|
| 47 |
+
embd_channels = 1280
|
| 48 |
+
self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
|
| 49 |
+
if c_in != 3:
|
| 50 |
+
in_weights = self.backbone[0][0].weight.data
|
| 51 |
+
self.backbone[0][0] = operations.Conv2d(c_in, 24, kernel_size=3, stride=2, bias=False, dtype=dtype, device=device)
|
| 52 |
+
if c_in > 3:
|
| 53 |
+
# nn.init.constant_(self.backbone[0][0].weight, 0)
|
| 54 |
+
self.backbone[0][0].weight.data[:, :3] = in_weights[:, :3].clone()
|
| 55 |
+
else:
|
| 56 |
+
self.backbone[0][0].weight.data = in_weights[:, :c_in].clone()
|
| 57 |
+
elif bottleneck_mode == 'simple':
|
| 58 |
+
embd_channels = c_in
|
| 59 |
+
self.backbone = nn.Sequential(
|
| 60 |
+
operations.Conv2d(embd_channels, embd_channels * 4, kernel_size=3, padding=1, dtype=dtype, device=device),
|
| 61 |
+
nn.LeakyReLU(0.2, inplace=True),
|
| 62 |
+
operations.Conv2d(embd_channels * 4, embd_channels, kernel_size=3, padding=1, dtype=dtype, device=device),
|
| 63 |
+
)
|
| 64 |
+
elif bottleneck_mode == 'large':
|
| 65 |
+
self.backbone = nn.Sequential(
|
| 66 |
+
operations.Conv2d(c_in, 4096 * 4, kernel_size=1, dtype=dtype, device=device),
|
| 67 |
+
nn.LeakyReLU(0.2, inplace=True),
|
| 68 |
+
operations.Conv2d(4096 * 4, 1024, kernel_size=1, dtype=dtype, device=device),
|
| 69 |
+
*[CNetResBlock(1024, dtype=dtype, device=device, operations=operations) for _ in range(8)],
|
| 70 |
+
operations.Conv2d(1024, 1280, kernel_size=1, dtype=dtype, device=device),
|
| 71 |
+
)
|
| 72 |
+
embd_channels = 1280
|
| 73 |
+
else:
|
| 74 |
+
raise ValueError(f'Unknown bottleneck mode: {bottleneck_mode}')
|
| 75 |
+
self.projections = nn.ModuleList()
|
| 76 |
+
for _ in range(len(proj_blocks)):
|
| 77 |
+
self.projections.append(nn.Sequential(
|
| 78 |
+
operations.Conv2d(embd_channels, embd_channels, kernel_size=1, bias=False, dtype=dtype, device=device),
|
| 79 |
+
nn.LeakyReLU(0.2, inplace=True),
|
| 80 |
+
operations.Conv2d(embd_channels, c_proj, kernel_size=1, bias=False, dtype=dtype, device=device),
|
| 81 |
+
))
|
| 82 |
+
# nn.init.constant_(self.projections[-1][-1].weight, 0) # zero output projection
|
| 83 |
+
self.xl = False
|
| 84 |
+
self.input_channels = c_in
|
| 85 |
+
self.unshuffle_amount = 8
|
| 86 |
+
|
| 87 |
+
def forward(self, x):
|
| 88 |
+
x = self.backbone(x)
|
| 89 |
+
proj_outputs = [None for _ in range(max(self.proj_blocks) + 1)]
|
| 90 |
+
for i, idx in enumerate(self.proj_blocks):
|
| 91 |
+
proj_outputs[idx] = self.projections[i](x)
|
| 92 |
+
return {"input": proj_outputs[::-1]}
|
ComfyUI/comfy/ldm/cascade/stage_a.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file is part of ComfyUI.
|
| 3 |
+
Copyright (C) 2024 Stability AI
|
| 4 |
+
|
| 5 |
+
This program is free software: you can redistribute it and/or modify
|
| 6 |
+
it under the terms of the GNU General Public License as published by
|
| 7 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 8 |
+
(at your option) any later version.
|
| 9 |
+
|
| 10 |
+
This program is distributed in the hope that it will be useful,
|
| 11 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
+
GNU General Public License for more details.
|
| 14 |
+
|
| 15 |
+
You should have received a copy of the GNU General Public License
|
| 16 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
from torch import nn
|
| 21 |
+
from torch.autograd import Function
|
| 22 |
+
import comfy.ops
|
| 23 |
+
|
| 24 |
+
ops = comfy.ops.disable_weight_init
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class vector_quantize(Function):
|
| 28 |
+
@staticmethod
|
| 29 |
+
def forward(ctx, x, codebook):
|
| 30 |
+
with torch.no_grad():
|
| 31 |
+
codebook_sqr = torch.sum(codebook ** 2, dim=1)
|
| 32 |
+
x_sqr = torch.sum(x ** 2, dim=1, keepdim=True)
|
| 33 |
+
|
| 34 |
+
dist = torch.addmm(codebook_sqr + x_sqr, x, codebook.t(), alpha=-2.0, beta=1.0)
|
| 35 |
+
_, indices = dist.min(dim=1)
|
| 36 |
+
|
| 37 |
+
ctx.save_for_backward(indices, codebook)
|
| 38 |
+
ctx.mark_non_differentiable(indices)
|
| 39 |
+
|
| 40 |
+
nn = torch.index_select(codebook, 0, indices)
|
| 41 |
+
return nn, indices
|
| 42 |
+
|
| 43 |
+
@staticmethod
|
| 44 |
+
def backward(ctx, grad_output, grad_indices):
|
| 45 |
+
grad_inputs, grad_codebook = None, None
|
| 46 |
+
|
| 47 |
+
if ctx.needs_input_grad[0]:
|
| 48 |
+
grad_inputs = grad_output.clone()
|
| 49 |
+
if ctx.needs_input_grad[1]:
|
| 50 |
+
# Gradient wrt. the codebook
|
| 51 |
+
indices, codebook = ctx.saved_tensors
|
| 52 |
+
|
| 53 |
+
grad_codebook = torch.zeros_like(codebook)
|
| 54 |
+
grad_codebook.index_add_(0, indices, grad_output)
|
| 55 |
+
|
| 56 |
+
return (grad_inputs, grad_codebook)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class VectorQuantize(nn.Module):
|
| 60 |
+
def __init__(self, embedding_size, k, ema_decay=0.99, ema_loss=False):
|
| 61 |
+
"""
|
| 62 |
+
Takes an input of variable size (as long as the last dimension matches the embedding size).
|
| 63 |
+
Returns one tensor containing the nearest neigbour embeddings to each of the inputs,
|
| 64 |
+
with the same size as the input, vq and commitment components for the loss as a touple
|
| 65 |
+
in the second output and the indices of the quantized vectors in the third:
|
| 66 |
+
quantized, (vq_loss, commit_loss), indices
|
| 67 |
+
"""
|
| 68 |
+
super(VectorQuantize, self).__init__()
|
| 69 |
+
|
| 70 |
+
self.codebook = nn.Embedding(k, embedding_size)
|
| 71 |
+
self.codebook.weight.data.uniform_(-1./k, 1./k)
|
| 72 |
+
self.vq = vector_quantize.apply
|
| 73 |
+
|
| 74 |
+
self.ema_decay = ema_decay
|
| 75 |
+
self.ema_loss = ema_loss
|
| 76 |
+
if ema_loss:
|
| 77 |
+
self.register_buffer('ema_element_count', torch.ones(k))
|
| 78 |
+
self.register_buffer('ema_weight_sum', torch.zeros_like(self.codebook.weight))
|
| 79 |
+
|
| 80 |
+
def _laplace_smoothing(self, x, epsilon):
|
| 81 |
+
n = torch.sum(x)
|
| 82 |
+
return ((x + epsilon) / (n + x.size(0) * epsilon) * n)
|
| 83 |
+
|
| 84 |
+
def _updateEMA(self, z_e_x, indices):
|
| 85 |
+
mask = nn.functional.one_hot(indices, self.ema_element_count.size(0)).float()
|
| 86 |
+
elem_count = mask.sum(dim=0)
|
| 87 |
+
weight_sum = torch.mm(mask.t(), z_e_x)
|
| 88 |
+
|
| 89 |
+
self.ema_element_count = (self.ema_decay * self.ema_element_count) + ((1-self.ema_decay) * elem_count)
|
| 90 |
+
self.ema_element_count = self._laplace_smoothing(self.ema_element_count, 1e-5)
|
| 91 |
+
self.ema_weight_sum = (self.ema_decay * self.ema_weight_sum) + ((1-self.ema_decay) * weight_sum)
|
| 92 |
+
|
| 93 |
+
self.codebook.weight.data = self.ema_weight_sum / self.ema_element_count.unsqueeze(-1)
|
| 94 |
+
|
| 95 |
+
def idx2vq(self, idx, dim=-1):
|
| 96 |
+
q_idx = self.codebook(idx)
|
| 97 |
+
if dim != -1:
|
| 98 |
+
q_idx = q_idx.movedim(-1, dim)
|
| 99 |
+
return q_idx
|
| 100 |
+
|
| 101 |
+
def forward(self, x, get_losses=True, dim=-1):
|
| 102 |
+
if dim != -1:
|
| 103 |
+
x = x.movedim(dim, -1)
|
| 104 |
+
z_e_x = x.contiguous().view(-1, x.size(-1)) if len(x.shape) > 2 else x
|
| 105 |
+
z_q_x, indices = self.vq(z_e_x, self.codebook.weight.detach())
|
| 106 |
+
vq_loss, commit_loss = None, None
|
| 107 |
+
if self.ema_loss and self.training:
|
| 108 |
+
self._updateEMA(z_e_x.detach(), indices.detach())
|
| 109 |
+
# pick the graded embeddings after updating the codebook in order to have a more accurate commitment loss
|
| 110 |
+
z_q_x_grd = torch.index_select(self.codebook.weight, dim=0, index=indices)
|
| 111 |
+
if get_losses:
|
| 112 |
+
vq_loss = (z_q_x_grd - z_e_x.detach()).pow(2).mean()
|
| 113 |
+
commit_loss = (z_e_x - z_q_x_grd.detach()).pow(2).mean()
|
| 114 |
+
|
| 115 |
+
z_q_x = z_q_x.view(x.shape)
|
| 116 |
+
if dim != -1:
|
| 117 |
+
z_q_x = z_q_x.movedim(-1, dim)
|
| 118 |
+
return z_q_x, (vq_loss, commit_loss), indices.view(x.shape[:-1])
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
class ResBlock(nn.Module):
|
| 122 |
+
def __init__(self, c, c_hidden):
|
| 123 |
+
super().__init__()
|
| 124 |
+
# depthwise/attention
|
| 125 |
+
self.norm1 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
|
| 126 |
+
self.depthwise = nn.Sequential(
|
| 127 |
+
nn.ReplicationPad2d(1),
|
| 128 |
+
ops.Conv2d(c, c, kernel_size=3, groups=c)
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
# channelwise
|
| 132 |
+
self.norm2 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
|
| 133 |
+
self.channelwise = nn.Sequential(
|
| 134 |
+
ops.Linear(c, c_hidden),
|
| 135 |
+
nn.GELU(),
|
| 136 |
+
ops.Linear(c_hidden, c),
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
self.gammas = nn.Parameter(torch.zeros(6), requires_grad=False)
|
| 140 |
+
|
| 141 |
+
def _norm(self, x, norm):
|
| 142 |
+
return norm(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
|
| 143 |
+
|
| 144 |
+
def forward(self, x):
|
| 145 |
+
mods = self.gammas
|
| 146 |
+
|
| 147 |
+
x_temp = self._norm(x, self.norm1) * (1 + mods[0]) + mods[1]
|
| 148 |
+
try:
|
| 149 |
+
x = x + self.depthwise(x_temp) * mods[2]
|
| 150 |
+
except: #operation not implemented for bf16
|
| 151 |
+
x_temp = self.depthwise[0](x_temp.float()).to(x.dtype)
|
| 152 |
+
x = x + self.depthwise[1](x_temp) * mods[2]
|
| 153 |
+
|
| 154 |
+
x_temp = self._norm(x, self.norm2) * (1 + mods[3]) + mods[4]
|
| 155 |
+
x = x + self.channelwise(x_temp.permute(0, 2, 3, 1)).permute(0, 3, 1, 2) * mods[5]
|
| 156 |
+
|
| 157 |
+
return x
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
class StageA(nn.Module):
|
| 161 |
+
def __init__(self, levels=2, bottleneck_blocks=12, c_hidden=384, c_latent=4, codebook_size=8192):
|
| 162 |
+
super().__init__()
|
| 163 |
+
self.c_latent = c_latent
|
| 164 |
+
c_levels = [c_hidden // (2 ** i) for i in reversed(range(levels))]
|
| 165 |
+
|
| 166 |
+
# Encoder blocks
|
| 167 |
+
self.in_block = nn.Sequential(
|
| 168 |
+
nn.PixelUnshuffle(2),
|
| 169 |
+
ops.Conv2d(3 * 4, c_levels[0], kernel_size=1)
|
| 170 |
+
)
|
| 171 |
+
down_blocks = []
|
| 172 |
+
for i in range(levels):
|
| 173 |
+
if i > 0:
|
| 174 |
+
down_blocks.append(ops.Conv2d(c_levels[i - 1], c_levels[i], kernel_size=4, stride=2, padding=1))
|
| 175 |
+
block = ResBlock(c_levels[i], c_levels[i] * 4)
|
| 176 |
+
down_blocks.append(block)
|
| 177 |
+
down_blocks.append(nn.Sequential(
|
| 178 |
+
ops.Conv2d(c_levels[-1], c_latent, kernel_size=1, bias=False),
|
| 179 |
+
nn.BatchNorm2d(c_latent), # then normalize them to have mean 0 and std 1
|
| 180 |
+
))
|
| 181 |
+
self.down_blocks = nn.Sequential(*down_blocks)
|
| 182 |
+
self.down_blocks[0]
|
| 183 |
+
|
| 184 |
+
self.codebook_size = codebook_size
|
| 185 |
+
self.vquantizer = VectorQuantize(c_latent, k=codebook_size)
|
| 186 |
+
|
| 187 |
+
# Decoder blocks
|
| 188 |
+
up_blocks = [nn.Sequential(
|
| 189 |
+
ops.Conv2d(c_latent, c_levels[-1], kernel_size=1)
|
| 190 |
+
)]
|
| 191 |
+
for i in range(levels):
|
| 192 |
+
for j in range(bottleneck_blocks if i == 0 else 1):
|
| 193 |
+
block = ResBlock(c_levels[levels - 1 - i], c_levels[levels - 1 - i] * 4)
|
| 194 |
+
up_blocks.append(block)
|
| 195 |
+
if i < levels - 1:
|
| 196 |
+
up_blocks.append(
|
| 197 |
+
ops.ConvTranspose2d(c_levels[levels - 1 - i], c_levels[levels - 2 - i], kernel_size=4, stride=2,
|
| 198 |
+
padding=1))
|
| 199 |
+
self.up_blocks = nn.Sequential(*up_blocks)
|
| 200 |
+
self.out_block = nn.Sequential(
|
| 201 |
+
ops.Conv2d(c_levels[0], 3 * 4, kernel_size=1),
|
| 202 |
+
nn.PixelShuffle(2),
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
def encode(self, x, quantize=False):
|
| 206 |
+
x = self.in_block(x)
|
| 207 |
+
x = self.down_blocks(x)
|
| 208 |
+
if quantize:
|
| 209 |
+
qe, (vq_loss, commit_loss), indices = self.vquantizer.forward(x, dim=1)
|
| 210 |
+
return qe, x, indices, vq_loss + commit_loss * 0.25
|
| 211 |
+
else:
|
| 212 |
+
return x
|
| 213 |
+
|
| 214 |
+
def decode(self, x):
|
| 215 |
+
x = self.up_blocks(x)
|
| 216 |
+
x = self.out_block(x)
|
| 217 |
+
return x
|
| 218 |
+
|
| 219 |
+
def forward(self, x, quantize=False):
|
| 220 |
+
qe, x, _, vq_loss = self.encode(x, quantize)
|
| 221 |
+
x = self.decode(qe)
|
| 222 |
+
return x, vq_loss
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
class Discriminator(nn.Module):
|
| 226 |
+
def __init__(self, c_in=3, c_cond=0, c_hidden=512, depth=6):
|
| 227 |
+
super().__init__()
|
| 228 |
+
d = max(depth - 3, 3)
|
| 229 |
+
layers = [
|
| 230 |
+
nn.utils.spectral_norm(ops.Conv2d(c_in, c_hidden // (2 ** d), kernel_size=3, stride=2, padding=1)),
|
| 231 |
+
nn.LeakyReLU(0.2),
|
| 232 |
+
]
|
| 233 |
+
for i in range(depth - 1):
|
| 234 |
+
c_in = c_hidden // (2 ** max((d - i), 0))
|
| 235 |
+
c_out = c_hidden // (2 ** max((d - 1 - i), 0))
|
| 236 |
+
layers.append(nn.utils.spectral_norm(ops.Conv2d(c_in, c_out, kernel_size=3, stride=2, padding=1)))
|
| 237 |
+
layers.append(nn.InstanceNorm2d(c_out))
|
| 238 |
+
layers.append(nn.LeakyReLU(0.2))
|
| 239 |
+
self.encoder = nn.Sequential(*layers)
|
| 240 |
+
self.shuffle = ops.Conv2d((c_hidden + c_cond) if c_cond > 0 else c_hidden, 1, kernel_size=1)
|
| 241 |
+
self.logits = nn.Sigmoid()
|
| 242 |
+
|
| 243 |
+
def forward(self, x, cond=None):
|
| 244 |
+
x = self.encoder(x)
|
| 245 |
+
if cond is not None:
|
| 246 |
+
cond = cond.view(cond.size(0), cond.size(1), 1, 1, ).expand(-1, -1, x.size(-2), x.size(-1))
|
| 247 |
+
x = torch.cat([x, cond], dim=1)
|
| 248 |
+
x = self.shuffle(x)
|
| 249 |
+
x = self.logits(x)
|
| 250 |
+
return x
|
ComfyUI/comfy/ldm/cascade/stage_b.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file is part of ComfyUI.
|
| 3 |
+
Copyright (C) 2024 Stability AI
|
| 4 |
+
|
| 5 |
+
This program is free software: you can redistribute it and/or modify
|
| 6 |
+
it under the terms of the GNU General Public License as published by
|
| 7 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 8 |
+
(at your option) any later version.
|
| 9 |
+
|
| 10 |
+
This program is distributed in the hope that it will be useful,
|
| 11 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
+
GNU General Public License for more details.
|
| 14 |
+
|
| 15 |
+
You should have received a copy of the GNU General Public License
|
| 16 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import math
|
| 20 |
+
import torch
|
| 21 |
+
from torch import nn
|
| 22 |
+
from .common import AttnBlock, LayerNorm2d_op, ResBlock, FeedForwardBlock, TimestepBlock
|
| 23 |
+
|
| 24 |
+
class StageB(nn.Module):
|
| 25 |
+
def __init__(self, c_in=4, c_out=4, c_r=64, patch_size=2, c_cond=1280, c_hidden=[320, 640, 1280, 1280],
|
| 26 |
+
nhead=[-1, -1, 20, 20], blocks=[[2, 6, 28, 6], [6, 28, 6, 2]],
|
| 27 |
+
block_repeat=[[1, 1, 1, 1], [3, 3, 2, 2]], level_config=['CT', 'CT', 'CTA', 'CTA'], c_clip=1280,
|
| 28 |
+
c_clip_seq=4, c_effnet=16, c_pixels=3, kernel_size=3, dropout=[0, 0, 0.0, 0.0], self_attn=True,
|
| 29 |
+
t_conds=['sca'], stable_cascade_stage=None, dtype=None, device=None, operations=None):
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.dtype = dtype
|
| 32 |
+
self.c_r = c_r
|
| 33 |
+
self.t_conds = t_conds
|
| 34 |
+
self.c_clip_seq = c_clip_seq
|
| 35 |
+
if not isinstance(dropout, list):
|
| 36 |
+
dropout = [dropout] * len(c_hidden)
|
| 37 |
+
if not isinstance(self_attn, list):
|
| 38 |
+
self_attn = [self_attn] * len(c_hidden)
|
| 39 |
+
|
| 40 |
+
# CONDITIONING
|
| 41 |
+
self.effnet_mapper = nn.Sequential(
|
| 42 |
+
operations.Conv2d(c_effnet, c_hidden[0] * 4, kernel_size=1, dtype=dtype, device=device),
|
| 43 |
+
nn.GELU(),
|
| 44 |
+
operations.Conv2d(c_hidden[0] * 4, c_hidden[0], kernel_size=1, dtype=dtype, device=device),
|
| 45 |
+
LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 46 |
+
)
|
| 47 |
+
self.pixels_mapper = nn.Sequential(
|
| 48 |
+
operations.Conv2d(c_pixels, c_hidden[0] * 4, kernel_size=1, dtype=dtype, device=device),
|
| 49 |
+
nn.GELU(),
|
| 50 |
+
operations.Conv2d(c_hidden[0] * 4, c_hidden[0], kernel_size=1, dtype=dtype, device=device),
|
| 51 |
+
LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 52 |
+
)
|
| 53 |
+
self.clip_mapper = operations.Linear(c_clip, c_cond * c_clip_seq, dtype=dtype, device=device)
|
| 54 |
+
self.clip_norm = operations.LayerNorm(c_cond, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 55 |
+
|
| 56 |
+
self.embedding = nn.Sequential(
|
| 57 |
+
nn.PixelUnshuffle(patch_size),
|
| 58 |
+
operations.Conv2d(c_in * (patch_size ** 2), c_hidden[0], kernel_size=1, dtype=dtype, device=device),
|
| 59 |
+
LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
def get_block(block_type, c_hidden, nhead, c_skip=0, dropout=0, self_attn=True):
|
| 63 |
+
if block_type == 'C':
|
| 64 |
+
return ResBlock(c_hidden, c_skip, kernel_size=kernel_size, dropout=dropout, dtype=dtype, device=device, operations=operations)
|
| 65 |
+
elif block_type == 'A':
|
| 66 |
+
return AttnBlock(c_hidden, c_cond, nhead, self_attn=self_attn, dropout=dropout, dtype=dtype, device=device, operations=operations)
|
| 67 |
+
elif block_type == 'F':
|
| 68 |
+
return FeedForwardBlock(c_hidden, dropout=dropout, dtype=dtype, device=device, operations=operations)
|
| 69 |
+
elif block_type == 'T':
|
| 70 |
+
return TimestepBlock(c_hidden, c_r, conds=t_conds, dtype=dtype, device=device, operations=operations)
|
| 71 |
+
else:
|
| 72 |
+
raise Exception(f'Block type {block_type} not supported')
|
| 73 |
+
|
| 74 |
+
# BLOCKS
|
| 75 |
+
# -- down blocks
|
| 76 |
+
self.down_blocks = nn.ModuleList()
|
| 77 |
+
self.down_downscalers = nn.ModuleList()
|
| 78 |
+
self.down_repeat_mappers = nn.ModuleList()
|
| 79 |
+
for i in range(len(c_hidden)):
|
| 80 |
+
if i > 0:
|
| 81 |
+
self.down_downscalers.append(nn.Sequential(
|
| 82 |
+
LayerNorm2d_op(operations)(c_hidden[i - 1], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
|
| 83 |
+
operations.Conv2d(c_hidden[i - 1], c_hidden[i], kernel_size=2, stride=2, dtype=dtype, device=device),
|
| 84 |
+
))
|
| 85 |
+
else:
|
| 86 |
+
self.down_downscalers.append(nn.Identity())
|
| 87 |
+
down_block = nn.ModuleList()
|
| 88 |
+
for _ in range(blocks[0][i]):
|
| 89 |
+
for block_type in level_config[i]:
|
| 90 |
+
block = get_block(block_type, c_hidden[i], nhead[i], dropout=dropout[i], self_attn=self_attn[i])
|
| 91 |
+
down_block.append(block)
|
| 92 |
+
self.down_blocks.append(down_block)
|
| 93 |
+
if block_repeat is not None:
|
| 94 |
+
block_repeat_mappers = nn.ModuleList()
|
| 95 |
+
for _ in range(block_repeat[0][i] - 1):
|
| 96 |
+
block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
|
| 97 |
+
self.down_repeat_mappers.append(block_repeat_mappers)
|
| 98 |
+
|
| 99 |
+
# -- up blocks
|
| 100 |
+
self.up_blocks = nn.ModuleList()
|
| 101 |
+
self.up_upscalers = nn.ModuleList()
|
| 102 |
+
self.up_repeat_mappers = nn.ModuleList()
|
| 103 |
+
for i in reversed(range(len(c_hidden))):
|
| 104 |
+
if i > 0:
|
| 105 |
+
self.up_upscalers.append(nn.Sequential(
|
| 106 |
+
LayerNorm2d_op(operations)(c_hidden[i], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
|
| 107 |
+
operations.ConvTranspose2d(c_hidden[i], c_hidden[i - 1], kernel_size=2, stride=2, dtype=dtype, device=device),
|
| 108 |
+
))
|
| 109 |
+
else:
|
| 110 |
+
self.up_upscalers.append(nn.Identity())
|
| 111 |
+
up_block = nn.ModuleList()
|
| 112 |
+
for j in range(blocks[1][::-1][i]):
|
| 113 |
+
for k, block_type in enumerate(level_config[i]):
|
| 114 |
+
c_skip = c_hidden[i] if i < len(c_hidden) - 1 and j == k == 0 else 0
|
| 115 |
+
block = get_block(block_type, c_hidden[i], nhead[i], c_skip=c_skip, dropout=dropout[i],
|
| 116 |
+
self_attn=self_attn[i])
|
| 117 |
+
up_block.append(block)
|
| 118 |
+
self.up_blocks.append(up_block)
|
| 119 |
+
if block_repeat is not None:
|
| 120 |
+
block_repeat_mappers = nn.ModuleList()
|
| 121 |
+
for _ in range(block_repeat[1][::-1][i] - 1):
|
| 122 |
+
block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
|
| 123 |
+
self.up_repeat_mappers.append(block_repeat_mappers)
|
| 124 |
+
|
| 125 |
+
# OUTPUT
|
| 126 |
+
self.clf = nn.Sequential(
|
| 127 |
+
LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
|
| 128 |
+
operations.Conv2d(c_hidden[0], c_out * (patch_size ** 2), kernel_size=1, dtype=dtype, device=device),
|
| 129 |
+
nn.PixelShuffle(patch_size),
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
# --- WEIGHT INIT ---
|
| 133 |
+
# self.apply(self._init_weights) # General init
|
| 134 |
+
# nn.init.normal_(self.clip_mapper.weight, std=0.02) # conditionings
|
| 135 |
+
# nn.init.normal_(self.effnet_mapper[0].weight, std=0.02) # conditionings
|
| 136 |
+
# nn.init.normal_(self.effnet_mapper[2].weight, std=0.02) # conditionings
|
| 137 |
+
# nn.init.normal_(self.pixels_mapper[0].weight, std=0.02) # conditionings
|
| 138 |
+
# nn.init.normal_(self.pixels_mapper[2].weight, std=0.02) # conditionings
|
| 139 |
+
# torch.nn.init.xavier_uniform_(self.embedding[1].weight, 0.02) # inputs
|
| 140 |
+
# nn.init.constant_(self.clf[1].weight, 0) # outputs
|
| 141 |
+
#
|
| 142 |
+
# # blocks
|
| 143 |
+
# for level_block in self.down_blocks + self.up_blocks:
|
| 144 |
+
# for block in level_block:
|
| 145 |
+
# if isinstance(block, ResBlock) or isinstance(block, FeedForwardBlock):
|
| 146 |
+
# block.channelwise[-1].weight.data *= np.sqrt(1 / sum(blocks[0]))
|
| 147 |
+
# elif isinstance(block, TimestepBlock):
|
| 148 |
+
# for layer in block.modules():
|
| 149 |
+
# if isinstance(layer, nn.Linear):
|
| 150 |
+
# nn.init.constant_(layer.weight, 0)
|
| 151 |
+
#
|
| 152 |
+
# def _init_weights(self, m):
|
| 153 |
+
# if isinstance(m, (nn.Conv2d, nn.Linear)):
|
| 154 |
+
# torch.nn.init.xavier_uniform_(m.weight)
|
| 155 |
+
# if m.bias is not None:
|
| 156 |
+
# nn.init.constant_(m.bias, 0)
|
| 157 |
+
|
| 158 |
+
def gen_r_embedding(self, r, max_positions=10000):
|
| 159 |
+
r = r * max_positions
|
| 160 |
+
half_dim = self.c_r // 2
|
| 161 |
+
emb = math.log(max_positions) / (half_dim - 1)
|
| 162 |
+
emb = torch.arange(half_dim, device=r.device).float().mul(-emb).exp()
|
| 163 |
+
emb = r[:, None] * emb[None, :]
|
| 164 |
+
emb = torch.cat([emb.sin(), emb.cos()], dim=1)
|
| 165 |
+
if self.c_r % 2 == 1: # zero pad
|
| 166 |
+
emb = nn.functional.pad(emb, (0, 1), mode='constant')
|
| 167 |
+
return emb
|
| 168 |
+
|
| 169 |
+
def gen_c_embeddings(self, clip):
|
| 170 |
+
if len(clip.shape) == 2:
|
| 171 |
+
clip = clip.unsqueeze(1)
|
| 172 |
+
clip = self.clip_mapper(clip).view(clip.size(0), clip.size(1) * self.c_clip_seq, -1)
|
| 173 |
+
clip = self.clip_norm(clip)
|
| 174 |
+
return clip
|
| 175 |
+
|
| 176 |
+
def _down_encode(self, x, r_embed, clip, transformer_options={}):
|
| 177 |
+
level_outputs = []
|
| 178 |
+
block_group = zip(self.down_blocks, self.down_downscalers, self.down_repeat_mappers)
|
| 179 |
+
for down_block, downscaler, repmap in block_group:
|
| 180 |
+
x = downscaler(x)
|
| 181 |
+
for i in range(len(repmap) + 1):
|
| 182 |
+
for block in down_block:
|
| 183 |
+
if isinstance(block, ResBlock) or (
|
| 184 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 185 |
+
ResBlock)):
|
| 186 |
+
x = block(x)
|
| 187 |
+
elif isinstance(block, AttnBlock) or (
|
| 188 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 189 |
+
AttnBlock)):
|
| 190 |
+
x = block(x, clip, transformer_options=transformer_options)
|
| 191 |
+
elif isinstance(block, TimestepBlock) or (
|
| 192 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 193 |
+
TimestepBlock)):
|
| 194 |
+
x = block(x, r_embed)
|
| 195 |
+
else:
|
| 196 |
+
x = block(x)
|
| 197 |
+
if i < len(repmap):
|
| 198 |
+
x = repmap[i](x)
|
| 199 |
+
level_outputs.insert(0, x)
|
| 200 |
+
return level_outputs
|
| 201 |
+
|
| 202 |
+
def _up_decode(self, level_outputs, r_embed, clip, transformer_options={}):
|
| 203 |
+
x = level_outputs[0]
|
| 204 |
+
block_group = zip(self.up_blocks, self.up_upscalers, self.up_repeat_mappers)
|
| 205 |
+
for i, (up_block, upscaler, repmap) in enumerate(block_group):
|
| 206 |
+
for j in range(len(repmap) + 1):
|
| 207 |
+
for k, block in enumerate(up_block):
|
| 208 |
+
if isinstance(block, ResBlock) or (
|
| 209 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 210 |
+
ResBlock)):
|
| 211 |
+
skip = level_outputs[i] if k == 0 and i > 0 else None
|
| 212 |
+
if skip is not None and (x.size(-1) != skip.size(-1) or x.size(-2) != skip.size(-2)):
|
| 213 |
+
x = torch.nn.functional.interpolate(x, skip.shape[-2:], mode='bilinear',
|
| 214 |
+
align_corners=True)
|
| 215 |
+
x = block(x, skip)
|
| 216 |
+
elif isinstance(block, AttnBlock) or (
|
| 217 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 218 |
+
AttnBlock)):
|
| 219 |
+
x = block(x, clip, transformer_options=transformer_options)
|
| 220 |
+
elif isinstance(block, TimestepBlock) or (
|
| 221 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 222 |
+
TimestepBlock)):
|
| 223 |
+
x = block(x, r_embed)
|
| 224 |
+
else:
|
| 225 |
+
x = block(x)
|
| 226 |
+
if j < len(repmap):
|
| 227 |
+
x = repmap[j](x)
|
| 228 |
+
x = upscaler(x)
|
| 229 |
+
return x
|
| 230 |
+
|
| 231 |
+
def forward(self, x, r, effnet, clip, pixels=None, transformer_options={}, **kwargs):
|
| 232 |
+
if pixels is None:
|
| 233 |
+
pixels = x.new_zeros(x.size(0), 3, 8, 8)
|
| 234 |
+
|
| 235 |
+
# Process the conditioning embeddings
|
| 236 |
+
r_embed = self.gen_r_embedding(r).to(dtype=x.dtype)
|
| 237 |
+
for c in self.t_conds:
|
| 238 |
+
t_cond = kwargs.get(c, torch.zeros_like(r))
|
| 239 |
+
r_embed = torch.cat([r_embed, self.gen_r_embedding(t_cond).to(dtype=x.dtype)], dim=1)
|
| 240 |
+
clip = self.gen_c_embeddings(clip)
|
| 241 |
+
|
| 242 |
+
# Model Blocks
|
| 243 |
+
x = self.embedding(x)
|
| 244 |
+
x = x + self.effnet_mapper(
|
| 245 |
+
nn.functional.interpolate(effnet, size=x.shape[-2:], mode='bilinear', align_corners=True))
|
| 246 |
+
x = x + nn.functional.interpolate(self.pixels_mapper(pixels), size=x.shape[-2:], mode='bilinear',
|
| 247 |
+
align_corners=True)
|
| 248 |
+
level_outputs = self._down_encode(x, r_embed, clip, transformer_options=transformer_options)
|
| 249 |
+
x = self._up_decode(level_outputs, r_embed, clip, transformer_options=transformer_options)
|
| 250 |
+
return self.clf(x)
|
| 251 |
+
|
| 252 |
+
def update_weights_ema(self, src_model, beta=0.999):
|
| 253 |
+
for self_params, src_params in zip(self.parameters(), src_model.parameters()):
|
| 254 |
+
self_params.data = self_params.data * beta + src_params.data.clone().to(self_params.device) * (1 - beta)
|
| 255 |
+
for self_buffers, src_buffers in zip(self.buffers(), src_model.buffers()):
|
| 256 |
+
self_buffers.data = self_buffers.data * beta + src_buffers.data.clone().to(self_buffers.device) * (1 - beta)
|
ComfyUI/comfy/ldm/cascade/stage_c.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file is part of ComfyUI.
|
| 3 |
+
Copyright (C) 2024 Stability AI
|
| 4 |
+
|
| 5 |
+
This program is free software: you can redistribute it and/or modify
|
| 6 |
+
it under the terms of the GNU General Public License as published by
|
| 7 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 8 |
+
(at your option) any later version.
|
| 9 |
+
|
| 10 |
+
This program is distributed in the hope that it will be useful,
|
| 11 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
+
GNU General Public License for more details.
|
| 14 |
+
|
| 15 |
+
You should have received a copy of the GNU General Public License
|
| 16 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
from torch import nn
|
| 21 |
+
import math
|
| 22 |
+
from .common import AttnBlock, LayerNorm2d_op, ResBlock, FeedForwardBlock, TimestepBlock
|
| 23 |
+
# from .controlnet import ControlNetDeliverer
|
| 24 |
+
|
| 25 |
+
class UpDownBlock2d(nn.Module):
|
| 26 |
+
def __init__(self, c_in, c_out, mode, enabled=True, dtype=None, device=None, operations=None):
|
| 27 |
+
super().__init__()
|
| 28 |
+
assert mode in ['up', 'down']
|
| 29 |
+
interpolation = nn.Upsample(scale_factor=2 if mode == 'up' else 0.5, mode='bilinear',
|
| 30 |
+
align_corners=True) if enabled else nn.Identity()
|
| 31 |
+
mapping = operations.Conv2d(c_in, c_out, kernel_size=1, dtype=dtype, device=device)
|
| 32 |
+
self.blocks = nn.ModuleList([interpolation, mapping] if mode == 'up' else [mapping, interpolation])
|
| 33 |
+
|
| 34 |
+
def forward(self, x):
|
| 35 |
+
for block in self.blocks:
|
| 36 |
+
x = block(x)
|
| 37 |
+
return x
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class StageC(nn.Module):
|
| 41 |
+
def __init__(self, c_in=16, c_out=16, c_r=64, patch_size=1, c_cond=2048, c_hidden=[2048, 2048], nhead=[32, 32],
|
| 42 |
+
blocks=[[8, 24], [24, 8]], block_repeat=[[1, 1], [1, 1]], level_config=['CTA', 'CTA'],
|
| 43 |
+
c_clip_text=1280, c_clip_text_pooled=1280, c_clip_img=768, c_clip_seq=4, kernel_size=3,
|
| 44 |
+
dropout=[0.0, 0.0], self_attn=True, t_conds=['sca', 'crp'], switch_level=[False], stable_cascade_stage=None,
|
| 45 |
+
dtype=None, device=None, operations=None):
|
| 46 |
+
super().__init__()
|
| 47 |
+
self.dtype = dtype
|
| 48 |
+
self.c_r = c_r
|
| 49 |
+
self.t_conds = t_conds
|
| 50 |
+
self.c_clip_seq = c_clip_seq
|
| 51 |
+
if not isinstance(dropout, list):
|
| 52 |
+
dropout = [dropout] * len(c_hidden)
|
| 53 |
+
if not isinstance(self_attn, list):
|
| 54 |
+
self_attn = [self_attn] * len(c_hidden)
|
| 55 |
+
|
| 56 |
+
# CONDITIONING
|
| 57 |
+
self.clip_txt_mapper = operations.Linear(c_clip_text, c_cond, dtype=dtype, device=device)
|
| 58 |
+
self.clip_txt_pooled_mapper = operations.Linear(c_clip_text_pooled, c_cond * c_clip_seq, dtype=dtype, device=device)
|
| 59 |
+
self.clip_img_mapper = operations.Linear(c_clip_img, c_cond * c_clip_seq, dtype=dtype, device=device)
|
| 60 |
+
self.clip_norm = operations.LayerNorm(c_cond, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 61 |
+
|
| 62 |
+
self.embedding = nn.Sequential(
|
| 63 |
+
nn.PixelUnshuffle(patch_size),
|
| 64 |
+
operations.Conv2d(c_in * (patch_size ** 2), c_hidden[0], kernel_size=1, dtype=dtype, device=device),
|
| 65 |
+
LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6)
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
def get_block(block_type, c_hidden, nhead, c_skip=0, dropout=0, self_attn=True):
|
| 69 |
+
if block_type == 'C':
|
| 70 |
+
return ResBlock(c_hidden, c_skip, kernel_size=kernel_size, dropout=dropout, dtype=dtype, device=device, operations=operations)
|
| 71 |
+
elif block_type == 'A':
|
| 72 |
+
return AttnBlock(c_hidden, c_cond, nhead, self_attn=self_attn, dropout=dropout, dtype=dtype, device=device, operations=operations)
|
| 73 |
+
elif block_type == 'F':
|
| 74 |
+
return FeedForwardBlock(c_hidden, dropout=dropout, dtype=dtype, device=device, operations=operations)
|
| 75 |
+
elif block_type == 'T':
|
| 76 |
+
return TimestepBlock(c_hidden, c_r, conds=t_conds, dtype=dtype, device=device, operations=operations)
|
| 77 |
+
else:
|
| 78 |
+
raise Exception(f'Block type {block_type} not supported')
|
| 79 |
+
|
| 80 |
+
# BLOCKS
|
| 81 |
+
# -- down blocks
|
| 82 |
+
self.down_blocks = nn.ModuleList()
|
| 83 |
+
self.down_downscalers = nn.ModuleList()
|
| 84 |
+
self.down_repeat_mappers = nn.ModuleList()
|
| 85 |
+
for i in range(len(c_hidden)):
|
| 86 |
+
if i > 0:
|
| 87 |
+
self.down_downscalers.append(nn.Sequential(
|
| 88 |
+
LayerNorm2d_op(operations)(c_hidden[i - 1], elementwise_affine=False, eps=1e-6),
|
| 89 |
+
UpDownBlock2d(c_hidden[i - 1], c_hidden[i], mode='down', enabled=switch_level[i - 1], dtype=dtype, device=device, operations=operations)
|
| 90 |
+
))
|
| 91 |
+
else:
|
| 92 |
+
self.down_downscalers.append(nn.Identity())
|
| 93 |
+
down_block = nn.ModuleList()
|
| 94 |
+
for _ in range(blocks[0][i]):
|
| 95 |
+
for block_type in level_config[i]:
|
| 96 |
+
block = get_block(block_type, c_hidden[i], nhead[i], dropout=dropout[i], self_attn=self_attn[i])
|
| 97 |
+
down_block.append(block)
|
| 98 |
+
self.down_blocks.append(down_block)
|
| 99 |
+
if block_repeat is not None:
|
| 100 |
+
block_repeat_mappers = nn.ModuleList()
|
| 101 |
+
for _ in range(block_repeat[0][i] - 1):
|
| 102 |
+
block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
|
| 103 |
+
self.down_repeat_mappers.append(block_repeat_mappers)
|
| 104 |
+
|
| 105 |
+
# -- up blocks
|
| 106 |
+
self.up_blocks = nn.ModuleList()
|
| 107 |
+
self.up_upscalers = nn.ModuleList()
|
| 108 |
+
self.up_repeat_mappers = nn.ModuleList()
|
| 109 |
+
for i in reversed(range(len(c_hidden))):
|
| 110 |
+
if i > 0:
|
| 111 |
+
self.up_upscalers.append(nn.Sequential(
|
| 112 |
+
LayerNorm2d_op(operations)(c_hidden[i], elementwise_affine=False, eps=1e-6),
|
| 113 |
+
UpDownBlock2d(c_hidden[i], c_hidden[i - 1], mode='up', enabled=switch_level[i - 1], dtype=dtype, device=device, operations=operations)
|
| 114 |
+
))
|
| 115 |
+
else:
|
| 116 |
+
self.up_upscalers.append(nn.Identity())
|
| 117 |
+
up_block = nn.ModuleList()
|
| 118 |
+
for j in range(blocks[1][::-1][i]):
|
| 119 |
+
for k, block_type in enumerate(level_config[i]):
|
| 120 |
+
c_skip = c_hidden[i] if i < len(c_hidden) - 1 and j == k == 0 else 0
|
| 121 |
+
block = get_block(block_type, c_hidden[i], nhead[i], c_skip=c_skip, dropout=dropout[i],
|
| 122 |
+
self_attn=self_attn[i])
|
| 123 |
+
up_block.append(block)
|
| 124 |
+
self.up_blocks.append(up_block)
|
| 125 |
+
if block_repeat is not None:
|
| 126 |
+
block_repeat_mappers = nn.ModuleList()
|
| 127 |
+
for _ in range(block_repeat[1][::-1][i] - 1):
|
| 128 |
+
block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
|
| 129 |
+
self.up_repeat_mappers.append(block_repeat_mappers)
|
| 130 |
+
|
| 131 |
+
# OUTPUT
|
| 132 |
+
self.clf = nn.Sequential(
|
| 133 |
+
LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
|
| 134 |
+
operations.Conv2d(c_hidden[0], c_out * (patch_size ** 2), kernel_size=1, dtype=dtype, device=device),
|
| 135 |
+
nn.PixelShuffle(patch_size),
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
# --- WEIGHT INIT ---
|
| 139 |
+
# self.apply(self._init_weights) # General init
|
| 140 |
+
# nn.init.normal_(self.clip_txt_mapper.weight, std=0.02) # conditionings
|
| 141 |
+
# nn.init.normal_(self.clip_txt_pooled_mapper.weight, std=0.02) # conditionings
|
| 142 |
+
# nn.init.normal_(self.clip_img_mapper.weight, std=0.02) # conditionings
|
| 143 |
+
# torch.nn.init.xavier_uniform_(self.embedding[1].weight, 0.02) # inputs
|
| 144 |
+
# nn.init.constant_(self.clf[1].weight, 0) # outputs
|
| 145 |
+
#
|
| 146 |
+
# # blocks
|
| 147 |
+
# for level_block in self.down_blocks + self.up_blocks:
|
| 148 |
+
# for block in level_block:
|
| 149 |
+
# if isinstance(block, ResBlock) or isinstance(block, FeedForwardBlock):
|
| 150 |
+
# block.channelwise[-1].weight.data *= np.sqrt(1 / sum(blocks[0]))
|
| 151 |
+
# elif isinstance(block, TimestepBlock):
|
| 152 |
+
# for layer in block.modules():
|
| 153 |
+
# if isinstance(layer, nn.Linear):
|
| 154 |
+
# nn.init.constant_(layer.weight, 0)
|
| 155 |
+
#
|
| 156 |
+
# def _init_weights(self, m):
|
| 157 |
+
# if isinstance(m, (nn.Conv2d, nn.Linear)):
|
| 158 |
+
# torch.nn.init.xavier_uniform_(m.weight)
|
| 159 |
+
# if m.bias is not None:
|
| 160 |
+
# nn.init.constant_(m.bias, 0)
|
| 161 |
+
|
| 162 |
+
def gen_r_embedding(self, r, max_positions=10000):
|
| 163 |
+
r = r * max_positions
|
| 164 |
+
half_dim = self.c_r // 2
|
| 165 |
+
emb = math.log(max_positions) / (half_dim - 1)
|
| 166 |
+
emb = torch.arange(half_dim, device=r.device).float().mul(-emb).exp()
|
| 167 |
+
emb = r[:, None] * emb[None, :]
|
| 168 |
+
emb = torch.cat([emb.sin(), emb.cos()], dim=1)
|
| 169 |
+
if self.c_r % 2 == 1: # zero pad
|
| 170 |
+
emb = nn.functional.pad(emb, (0, 1), mode='constant')
|
| 171 |
+
return emb
|
| 172 |
+
|
| 173 |
+
def gen_c_embeddings(self, clip_txt, clip_txt_pooled, clip_img):
|
| 174 |
+
clip_txt = self.clip_txt_mapper(clip_txt)
|
| 175 |
+
if len(clip_txt_pooled.shape) == 2:
|
| 176 |
+
clip_txt_pooled = clip_txt_pooled.unsqueeze(1)
|
| 177 |
+
if len(clip_img.shape) == 2:
|
| 178 |
+
clip_img = clip_img.unsqueeze(1)
|
| 179 |
+
clip_txt_pool = self.clip_txt_pooled_mapper(clip_txt_pooled).view(clip_txt_pooled.size(0), clip_txt_pooled.size(1) * self.c_clip_seq, -1)
|
| 180 |
+
clip_img = self.clip_img_mapper(clip_img).view(clip_img.size(0), clip_img.size(1) * self.c_clip_seq, -1)
|
| 181 |
+
clip = torch.cat([clip_txt, clip_txt_pool, clip_img], dim=1)
|
| 182 |
+
clip = self.clip_norm(clip)
|
| 183 |
+
return clip
|
| 184 |
+
|
| 185 |
+
def _down_encode(self, x, r_embed, clip, cnet=None, transformer_options={}):
|
| 186 |
+
level_outputs = []
|
| 187 |
+
block_group = zip(self.down_blocks, self.down_downscalers, self.down_repeat_mappers)
|
| 188 |
+
for down_block, downscaler, repmap in block_group:
|
| 189 |
+
x = downscaler(x)
|
| 190 |
+
for i in range(len(repmap) + 1):
|
| 191 |
+
for block in down_block:
|
| 192 |
+
if isinstance(block, ResBlock) or (
|
| 193 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 194 |
+
ResBlock)):
|
| 195 |
+
if cnet is not None:
|
| 196 |
+
next_cnet = cnet.pop()
|
| 197 |
+
if next_cnet is not None:
|
| 198 |
+
x = x + nn.functional.interpolate(next_cnet, size=x.shape[-2:], mode='bilinear',
|
| 199 |
+
align_corners=True).to(x.dtype)
|
| 200 |
+
x = block(x)
|
| 201 |
+
elif isinstance(block, AttnBlock) or (
|
| 202 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 203 |
+
AttnBlock)):
|
| 204 |
+
x = block(x, clip, transformer_options=transformer_options)
|
| 205 |
+
elif isinstance(block, TimestepBlock) or (
|
| 206 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 207 |
+
TimestepBlock)):
|
| 208 |
+
x = block(x, r_embed)
|
| 209 |
+
else:
|
| 210 |
+
x = block(x)
|
| 211 |
+
if i < len(repmap):
|
| 212 |
+
x = repmap[i](x)
|
| 213 |
+
level_outputs.insert(0, x)
|
| 214 |
+
return level_outputs
|
| 215 |
+
|
| 216 |
+
def _up_decode(self, level_outputs, r_embed, clip, cnet=None, transformer_options={}):
|
| 217 |
+
x = level_outputs[0]
|
| 218 |
+
block_group = zip(self.up_blocks, self.up_upscalers, self.up_repeat_mappers)
|
| 219 |
+
for i, (up_block, upscaler, repmap) in enumerate(block_group):
|
| 220 |
+
for j in range(len(repmap) + 1):
|
| 221 |
+
for k, block in enumerate(up_block):
|
| 222 |
+
if isinstance(block, ResBlock) or (
|
| 223 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 224 |
+
ResBlock)):
|
| 225 |
+
skip = level_outputs[i] if k == 0 and i > 0 else None
|
| 226 |
+
if skip is not None and (x.size(-1) != skip.size(-1) or x.size(-2) != skip.size(-2)):
|
| 227 |
+
x = torch.nn.functional.interpolate(x, skip.shape[-2:], mode='bilinear',
|
| 228 |
+
align_corners=True)
|
| 229 |
+
if cnet is not None:
|
| 230 |
+
next_cnet = cnet.pop()
|
| 231 |
+
if next_cnet is not None:
|
| 232 |
+
x = x + nn.functional.interpolate(next_cnet, size=x.shape[-2:], mode='bilinear',
|
| 233 |
+
align_corners=True).to(x.dtype)
|
| 234 |
+
x = block(x, skip)
|
| 235 |
+
elif isinstance(block, AttnBlock) or (
|
| 236 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 237 |
+
AttnBlock)):
|
| 238 |
+
x = block(x, clip, transformer_options=transformer_options)
|
| 239 |
+
elif isinstance(block, TimestepBlock) or (
|
| 240 |
+
hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
|
| 241 |
+
TimestepBlock)):
|
| 242 |
+
x = block(x, r_embed)
|
| 243 |
+
else:
|
| 244 |
+
x = block(x)
|
| 245 |
+
if j < len(repmap):
|
| 246 |
+
x = repmap[j](x)
|
| 247 |
+
x = upscaler(x)
|
| 248 |
+
return x
|
| 249 |
+
|
| 250 |
+
def forward(self, x, r, clip_text, clip_text_pooled, clip_img, control=None, transformer_options={}, **kwargs):
|
| 251 |
+
# Process the conditioning embeddings
|
| 252 |
+
r_embed = self.gen_r_embedding(r).to(dtype=x.dtype)
|
| 253 |
+
for c in self.t_conds:
|
| 254 |
+
t_cond = kwargs.get(c, torch.zeros_like(r))
|
| 255 |
+
r_embed = torch.cat([r_embed, self.gen_r_embedding(t_cond).to(dtype=x.dtype)], dim=1)
|
| 256 |
+
clip = self.gen_c_embeddings(clip_text, clip_text_pooled, clip_img)
|
| 257 |
+
|
| 258 |
+
if control is not None:
|
| 259 |
+
cnet = control.get("input")
|
| 260 |
+
else:
|
| 261 |
+
cnet = None
|
| 262 |
+
|
| 263 |
+
# Model Blocks
|
| 264 |
+
x = self.embedding(x)
|
| 265 |
+
level_outputs = self._down_encode(x, r_embed, clip, cnet, transformer_options=transformer_options)
|
| 266 |
+
x = self._up_decode(level_outputs, r_embed, clip, cnet, transformer_options=transformer_options)
|
| 267 |
+
return self.clf(x)
|
| 268 |
+
|
| 269 |
+
def update_weights_ema(self, src_model, beta=0.999):
|
| 270 |
+
for self_params, src_params in zip(self.parameters(), src_model.parameters()):
|
| 271 |
+
self_params.data = self_params.data * beta + src_params.data.clone().to(self_params.device) * (1 - beta)
|
| 272 |
+
for self_buffers, src_buffers in zip(self.buffers(), src_model.buffers()):
|
| 273 |
+
self_buffers.data = self_buffers.data * beta + src_buffers.data.clone().to(self_buffers.device) * (1 - beta)
|
ComfyUI/comfy/ldm/cascade/stage_c_coder.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file is part of ComfyUI.
|
| 3 |
+
Copyright (C) 2024 Stability AI
|
| 4 |
+
|
| 5 |
+
This program is free software: you can redistribute it and/or modify
|
| 6 |
+
it under the terms of the GNU General Public License as published by
|
| 7 |
+
the Free Software Foundation, either version 3 of the License, or
|
| 8 |
+
(at your option) any later version.
|
| 9 |
+
|
| 10 |
+
This program is distributed in the hope that it will be useful,
|
| 11 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 12 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 13 |
+
GNU General Public License for more details.
|
| 14 |
+
|
| 15 |
+
You should have received a copy of the GNU General Public License
|
| 16 |
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
| 17 |
+
"""
|
| 18 |
+
import torch
|
| 19 |
+
import torchvision
|
| 20 |
+
from torch import nn
|
| 21 |
+
|
| 22 |
+
import comfy.ops
|
| 23 |
+
|
| 24 |
+
ops = comfy.ops.disable_weight_init
|
| 25 |
+
|
| 26 |
+
# EfficientNet
|
| 27 |
+
class EfficientNetEncoder(nn.Module):
|
| 28 |
+
def __init__(self, c_latent=16):
|
| 29 |
+
super().__init__()
|
| 30 |
+
self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
|
| 31 |
+
self.mapper = nn.Sequential(
|
| 32 |
+
ops.Conv2d(1280, c_latent, kernel_size=1, bias=False),
|
| 33 |
+
nn.BatchNorm2d(c_latent, affine=False), # then normalize them to have mean 0 and std 1
|
| 34 |
+
)
|
| 35 |
+
self.mean = nn.Parameter(torch.tensor([0.485, 0.456, 0.406]))
|
| 36 |
+
self.std = nn.Parameter(torch.tensor([0.229, 0.224, 0.225]))
|
| 37 |
+
|
| 38 |
+
def forward(self, x):
|
| 39 |
+
x = x * 0.5 + 0.5
|
| 40 |
+
x = (x - self.mean.view([3,1,1]).to(device=x.device, dtype=x.dtype)) / self.std.view([3,1,1]).to(device=x.device, dtype=x.dtype)
|
| 41 |
+
o = self.mapper(self.backbone(x))
|
| 42 |
+
return o
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# Fast Decoder for Stage C latents. E.g. 16 x 24 x 24 -> 3 x 192 x 192
|
| 46 |
+
class Previewer(nn.Module):
|
| 47 |
+
def __init__(self, c_in=16, c_hidden=512, c_out=3):
|
| 48 |
+
super().__init__()
|
| 49 |
+
self.blocks = nn.Sequential(
|
| 50 |
+
ops.Conv2d(c_in, c_hidden, kernel_size=1), # 16 channels to 512 channels
|
| 51 |
+
nn.GELU(),
|
| 52 |
+
nn.BatchNorm2d(c_hidden),
|
| 53 |
+
|
| 54 |
+
ops.Conv2d(c_hidden, c_hidden, kernel_size=3, padding=1),
|
| 55 |
+
nn.GELU(),
|
| 56 |
+
nn.BatchNorm2d(c_hidden),
|
| 57 |
+
|
| 58 |
+
ops.ConvTranspose2d(c_hidden, c_hidden // 2, kernel_size=2, stride=2), # 16 -> 32
|
| 59 |
+
nn.GELU(),
|
| 60 |
+
nn.BatchNorm2d(c_hidden // 2),
|
| 61 |
+
|
| 62 |
+
ops.Conv2d(c_hidden // 2, c_hidden // 2, kernel_size=3, padding=1),
|
| 63 |
+
nn.GELU(),
|
| 64 |
+
nn.BatchNorm2d(c_hidden // 2),
|
| 65 |
+
|
| 66 |
+
ops.ConvTranspose2d(c_hidden // 2, c_hidden // 4, kernel_size=2, stride=2), # 32 -> 64
|
| 67 |
+
nn.GELU(),
|
| 68 |
+
nn.BatchNorm2d(c_hidden // 4),
|
| 69 |
+
|
| 70 |
+
ops.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
|
| 71 |
+
nn.GELU(),
|
| 72 |
+
nn.BatchNorm2d(c_hidden // 4),
|
| 73 |
+
|
| 74 |
+
ops.ConvTranspose2d(c_hidden // 4, c_hidden // 4, kernel_size=2, stride=2), # 64 -> 128
|
| 75 |
+
nn.GELU(),
|
| 76 |
+
nn.BatchNorm2d(c_hidden // 4),
|
| 77 |
+
|
| 78 |
+
ops.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
|
| 79 |
+
nn.GELU(),
|
| 80 |
+
nn.BatchNorm2d(c_hidden // 4),
|
| 81 |
+
|
| 82 |
+
ops.Conv2d(c_hidden // 4, c_out, kernel_size=1),
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
def forward(self, x):
|
| 86 |
+
return (self.blocks(x) - 0.5) * 2.0
|
| 87 |
+
|
| 88 |
+
class StageC_coder(nn.Module):
|
| 89 |
+
def __init__(self):
|
| 90 |
+
super().__init__()
|
| 91 |
+
self.previewer = Previewer()
|
| 92 |
+
self.encoder = EfficientNetEncoder()
|
| 93 |
+
|
| 94 |
+
def encode(self, x):
|
| 95 |
+
return self.encoder(x)
|
| 96 |
+
|
| 97 |
+
def decode(self, x):
|
| 98 |
+
return self.previewer(x)
|
ComfyUI/comfy/ldm/chroma/layers.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import Tensor, nn
|
| 3 |
+
|
| 4 |
+
from comfy.ldm.flux.layers import (
|
| 5 |
+
MLPEmbedder,
|
| 6 |
+
ModulationOut,
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
# TODO: remove this in a few months
|
| 10 |
+
SingleStreamBlock = None
|
| 11 |
+
DoubleStreamBlock = None
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class ChromaModulationOut(ModulationOut):
|
| 15 |
+
@classmethod
|
| 16 |
+
def from_offset(cls, tensor: torch.Tensor, offset: int = 0) -> ModulationOut:
|
| 17 |
+
return cls(
|
| 18 |
+
shift=tensor[:, offset : offset + 1, :],
|
| 19 |
+
scale=tensor[:, offset + 1 : offset + 2, :],
|
| 20 |
+
gate=tensor[:, offset + 2 : offset + 3, :],
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class Approximator(nn.Module):
|
| 27 |
+
def __init__(self, in_dim: int, out_dim: int, hidden_dim: int, n_layers = 5, dtype=None, device=None, operations=None):
|
| 28 |
+
super().__init__()
|
| 29 |
+
self.in_proj = operations.Linear(in_dim, hidden_dim, bias=True, dtype=dtype, device=device)
|
| 30 |
+
self.layers = nn.ModuleList([MLPEmbedder(hidden_dim, hidden_dim, dtype=dtype, device=device, operations=operations) for x in range( n_layers)])
|
| 31 |
+
self.norms = nn.ModuleList([operations.RMSNorm(hidden_dim, dtype=dtype, device=device) for x in range( n_layers)])
|
| 32 |
+
self.out_proj = operations.Linear(hidden_dim, out_dim, dtype=dtype, device=device)
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def device(self):
|
| 36 |
+
# Get the device of the module (assumes all parameters are on the same device)
|
| 37 |
+
return next(self.parameters()).device
|
| 38 |
+
|
| 39 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 40 |
+
x = self.in_proj(x)
|
| 41 |
+
|
| 42 |
+
for layer, norms in zip(self.layers, self.norms):
|
| 43 |
+
x = x + layer(norms(x))
|
| 44 |
+
|
| 45 |
+
x = self.out_proj(x)
|
| 46 |
+
|
| 47 |
+
return x
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class LastLayer(nn.Module):
|
| 51 |
+
def __init__(self, hidden_size: int, patch_size: int, out_channels: int, dtype=None, device=None, operations=None):
|
| 52 |
+
super().__init__()
|
| 53 |
+
self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
|
| 54 |
+
self.linear = operations.Linear(hidden_size, out_channels, bias=True, dtype=dtype, device=device)
|
| 55 |
+
|
| 56 |
+
def forward(self, x: Tensor, vec: Tensor) -> Tensor:
|
| 57 |
+
shift, scale = vec
|
| 58 |
+
shift = shift.squeeze(1)
|
| 59 |
+
scale = scale.squeeze(1)
|
| 60 |
+
x = torch.addcmul(shift[:, None, :], 1 + scale[:, None, :], self.norm_final(x))
|
| 61 |
+
x = self.linear(x)
|
| 62 |
+
return x
|
ComfyUI/comfy/ldm/chroma/model.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.patcher_extension
|
| 9 |
+
import comfy.ldm.common_dit
|
| 10 |
+
|
| 11 |
+
from comfy.ldm.flux.layers import (
|
| 12 |
+
EmbedND,
|
| 13 |
+
timestep_embedding,
|
| 14 |
+
DoubleStreamBlock,
|
| 15 |
+
SingleStreamBlock,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
from .layers import (
|
| 19 |
+
LastLayer,
|
| 20 |
+
Approximator,
|
| 21 |
+
ChromaModulationOut,
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@dataclass
|
| 26 |
+
class ChromaParams:
|
| 27 |
+
in_channels: int
|
| 28 |
+
out_channels: int
|
| 29 |
+
context_in_dim: int
|
| 30 |
+
hidden_size: int
|
| 31 |
+
mlp_ratio: float
|
| 32 |
+
num_heads: int
|
| 33 |
+
depth: int
|
| 34 |
+
depth_single_blocks: int
|
| 35 |
+
axes_dim: list
|
| 36 |
+
theta: int
|
| 37 |
+
patch_size: int
|
| 38 |
+
qkv_bias: bool
|
| 39 |
+
in_dim: int
|
| 40 |
+
out_dim: int
|
| 41 |
+
hidden_dim: int
|
| 42 |
+
n_layers: int
|
| 43 |
+
txt_ids_dims: list
|
| 44 |
+
vec_in_dim: int
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class Chroma(nn.Module):
|
| 49 |
+
"""
|
| 50 |
+
Transformer model for flow matching on sequences.
|
| 51 |
+
"""
|
| 52 |
+
|
| 53 |
+
def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
|
| 54 |
+
super().__init__()
|
| 55 |
+
self.dtype = dtype
|
| 56 |
+
params = ChromaParams(**kwargs)
|
| 57 |
+
self.params = params
|
| 58 |
+
self.patch_size = params.patch_size
|
| 59 |
+
self.in_channels = params.in_channels
|
| 60 |
+
self.out_channels = params.out_channels
|
| 61 |
+
if params.hidden_size % params.num_heads != 0:
|
| 62 |
+
raise ValueError(
|
| 63 |
+
f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
|
| 64 |
+
)
|
| 65 |
+
pe_dim = params.hidden_size // params.num_heads
|
| 66 |
+
if sum(params.axes_dim) != pe_dim:
|
| 67 |
+
raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
|
| 68 |
+
self.hidden_size = params.hidden_size
|
| 69 |
+
self.num_heads = params.num_heads
|
| 70 |
+
self.in_dim = params.in_dim
|
| 71 |
+
self.out_dim = params.out_dim
|
| 72 |
+
self.hidden_dim = params.hidden_dim
|
| 73 |
+
self.n_layers = params.n_layers
|
| 74 |
+
self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
|
| 75 |
+
self.img_in = operations.Linear(self.in_channels, self.hidden_size, bias=True, dtype=dtype, device=device)
|
| 76 |
+
self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, dtype=dtype, device=device)
|
| 77 |
+
# set as nn identity for now, will overwrite it later.
|
| 78 |
+
self.distilled_guidance_layer = Approximator(
|
| 79 |
+
in_dim=self.in_dim,
|
| 80 |
+
hidden_dim=self.hidden_dim,
|
| 81 |
+
out_dim=self.out_dim,
|
| 82 |
+
n_layers=self.n_layers,
|
| 83 |
+
dtype=dtype, device=device, operations=operations
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
self.double_blocks = nn.ModuleList(
|
| 88 |
+
[
|
| 89 |
+
DoubleStreamBlock(
|
| 90 |
+
self.hidden_size,
|
| 91 |
+
self.num_heads,
|
| 92 |
+
mlp_ratio=params.mlp_ratio,
|
| 93 |
+
qkv_bias=params.qkv_bias,
|
| 94 |
+
modulation=False,
|
| 95 |
+
dtype=dtype, device=device, operations=operations
|
| 96 |
+
)
|
| 97 |
+
for _ in range(params.depth)
|
| 98 |
+
]
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
self.single_blocks = nn.ModuleList(
|
| 102 |
+
[
|
| 103 |
+
SingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, modulation=False, dtype=dtype, device=device, operations=operations)
|
| 104 |
+
for _ in range(params.depth_single_blocks)
|
| 105 |
+
]
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
if final_layer:
|
| 109 |
+
self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, dtype=dtype, device=device, operations=operations)
|
| 110 |
+
|
| 111 |
+
self.skip_mmdit = []
|
| 112 |
+
self.skip_dit = []
|
| 113 |
+
self.lite = False
|
| 114 |
+
|
| 115 |
+
def get_modulations(self, tensor: torch.Tensor, block_type: str, *, idx: int = 0):
|
| 116 |
+
# This function slices up the modulations tensor which has the following layout:
|
| 117 |
+
# single : num_single_blocks * 3 elements
|
| 118 |
+
# double_img : num_double_blocks * 6 elements
|
| 119 |
+
# double_txt : num_double_blocks * 6 elements
|
| 120 |
+
# final : 2 elements
|
| 121 |
+
if block_type == "final":
|
| 122 |
+
return (tensor[:, -2:-1, :], tensor[:, -1:, :])
|
| 123 |
+
single_block_count = self.params.depth_single_blocks
|
| 124 |
+
double_block_count = self.params.depth
|
| 125 |
+
offset = 3 * idx
|
| 126 |
+
if block_type == "single":
|
| 127 |
+
return ChromaModulationOut.from_offset(tensor, offset)
|
| 128 |
+
# Double block modulations are 6 elements so we double 3 * idx.
|
| 129 |
+
offset *= 2
|
| 130 |
+
if block_type in {"double_img", "double_txt"}:
|
| 131 |
+
# Advance past the single block modulations.
|
| 132 |
+
offset += 3 * single_block_count
|
| 133 |
+
if block_type == "double_txt":
|
| 134 |
+
# Advance past the double block img modulations.
|
| 135 |
+
offset += 6 * double_block_count
|
| 136 |
+
return (
|
| 137 |
+
ChromaModulationOut.from_offset(tensor, offset),
|
| 138 |
+
ChromaModulationOut.from_offset(tensor, offset + 3),
|
| 139 |
+
)
|
| 140 |
+
raise ValueError("Bad block_type")
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def forward_orig(
|
| 144 |
+
self,
|
| 145 |
+
img: Tensor,
|
| 146 |
+
img_ids: Tensor,
|
| 147 |
+
txt: Tensor,
|
| 148 |
+
txt_ids: Tensor,
|
| 149 |
+
timesteps: Tensor,
|
| 150 |
+
guidance: Tensor = None,
|
| 151 |
+
control = None,
|
| 152 |
+
transformer_options={},
|
| 153 |
+
attn_mask: Tensor = None,
|
| 154 |
+
) -> Tensor:
|
| 155 |
+
transformer_options = transformer_options.copy()
|
| 156 |
+
patches_replace = transformer_options.get("patches_replace", {})
|
| 157 |
+
|
| 158 |
+
# running on sequences img
|
| 159 |
+
img = self.img_in(img)
|
| 160 |
+
|
| 161 |
+
# distilled vector guidance
|
| 162 |
+
mod_index_length = 344
|
| 163 |
+
distill_timestep = timestep_embedding(timesteps.detach().clone(), 16).to(img.device, img.dtype)
|
| 164 |
+
# guidance = guidance *
|
| 165 |
+
distil_guidance = timestep_embedding(guidance.detach().clone(), 16).to(img.device, img.dtype)
|
| 166 |
+
|
| 167 |
+
# get all modulation index
|
| 168 |
+
modulation_index = timestep_embedding(torch.arange(mod_index_length, device=img.device), 32).to(img.device, img.dtype)
|
| 169 |
+
# we need to broadcast the modulation index here so each batch has all of the index
|
| 170 |
+
modulation_index = modulation_index.unsqueeze(0).repeat(img.shape[0], 1, 1).to(img.device, img.dtype)
|
| 171 |
+
# and we need to broadcast timestep and guidance along too
|
| 172 |
+
timestep_guidance = torch.cat([distill_timestep, distil_guidance], dim=1).unsqueeze(1).repeat(1, mod_index_length, 1).to(img.dtype).to(img.device, img.dtype)
|
| 173 |
+
# then and only then we could concatenate it together
|
| 174 |
+
input_vec = torch.cat([timestep_guidance, modulation_index], dim=-1).to(img.device, img.dtype)
|
| 175 |
+
|
| 176 |
+
mod_vectors = self.distilled_guidance_layer(input_vec)
|
| 177 |
+
|
| 178 |
+
txt = self.txt_in(txt)
|
| 179 |
+
|
| 180 |
+
ids = torch.cat((txt_ids, img_ids), dim=1)
|
| 181 |
+
pe = self.pe_embedder(ids)
|
| 182 |
+
|
| 183 |
+
blocks_replace = patches_replace.get("dit", {})
|
| 184 |
+
transformer_options["total_blocks"] = len(self.double_blocks)
|
| 185 |
+
transformer_options["block_type"] = "double"
|
| 186 |
+
for i, block in enumerate(self.double_blocks):
|
| 187 |
+
transformer_options["block_index"] = i
|
| 188 |
+
if i not in self.skip_mmdit:
|
| 189 |
+
double_mod = (
|
| 190 |
+
self.get_modulations(mod_vectors, "double_img", idx=i),
|
| 191 |
+
self.get_modulations(mod_vectors, "double_txt", idx=i),
|
| 192 |
+
)
|
| 193 |
+
if ("double_block", i) in blocks_replace:
|
| 194 |
+
def block_wrap(args):
|
| 195 |
+
out = {}
|
| 196 |
+
out["img"], out["txt"] = block(img=args["img"],
|
| 197 |
+
txt=args["txt"],
|
| 198 |
+
vec=args["vec"],
|
| 199 |
+
pe=args["pe"],
|
| 200 |
+
attn_mask=args.get("attn_mask"),
|
| 201 |
+
transformer_options=args.get("transformer_options"))
|
| 202 |
+
return out
|
| 203 |
+
|
| 204 |
+
out = blocks_replace[("double_block", i)]({"img": img,
|
| 205 |
+
"txt": txt,
|
| 206 |
+
"vec": double_mod,
|
| 207 |
+
"pe": pe,
|
| 208 |
+
"attn_mask": attn_mask,
|
| 209 |
+
"transformer_options": transformer_options},
|
| 210 |
+
{"original_block": block_wrap})
|
| 211 |
+
txt = out["txt"]
|
| 212 |
+
img = out["img"]
|
| 213 |
+
else:
|
| 214 |
+
img, txt = block(img=img,
|
| 215 |
+
txt=txt,
|
| 216 |
+
vec=double_mod,
|
| 217 |
+
pe=pe,
|
| 218 |
+
attn_mask=attn_mask,
|
| 219 |
+
transformer_options=transformer_options)
|
| 220 |
+
|
| 221 |
+
if control is not None: # Controlnet
|
| 222 |
+
control_i = control.get("input")
|
| 223 |
+
if i < len(control_i):
|
| 224 |
+
add = control_i[i]
|
| 225 |
+
if add is not None:
|
| 226 |
+
img += add
|
| 227 |
+
|
| 228 |
+
img = torch.cat((txt, img), 1)
|
| 229 |
+
|
| 230 |
+
transformer_options["total_blocks"] = len(self.single_blocks)
|
| 231 |
+
transformer_options["block_type"] = "single"
|
| 232 |
+
transformer_options["img_slice"] = [txt.shape[1], img.shape[1]]
|
| 233 |
+
for i, block in enumerate(self.single_blocks):
|
| 234 |
+
transformer_options["block_index"] = i
|
| 235 |
+
if i not in self.skip_dit:
|
| 236 |
+
single_mod = self.get_modulations(mod_vectors, "single", idx=i)
|
| 237 |
+
if ("single_block", i) in blocks_replace:
|
| 238 |
+
def block_wrap(args):
|
| 239 |
+
out = {}
|
| 240 |
+
out["img"] = block(args["img"],
|
| 241 |
+
vec=args["vec"],
|
| 242 |
+
pe=args["pe"],
|
| 243 |
+
attn_mask=args.get("attn_mask"),
|
| 244 |
+
transformer_options=args.get("transformer_options"))
|
| 245 |
+
return out
|
| 246 |
+
|
| 247 |
+
out = blocks_replace[("single_block", i)]({"img": img,
|
| 248 |
+
"vec": single_mod,
|
| 249 |
+
"pe": pe,
|
| 250 |
+
"attn_mask": attn_mask,
|
| 251 |
+
"transformer_options": transformer_options},
|
| 252 |
+
{"original_block": block_wrap})
|
| 253 |
+
img = out["img"]
|
| 254 |
+
else:
|
| 255 |
+
img = block(img, vec=single_mod, pe=pe, attn_mask=attn_mask, transformer_options=transformer_options)
|
| 256 |
+
|
| 257 |
+
if control is not None: # Controlnet
|
| 258 |
+
control_o = control.get("output")
|
| 259 |
+
if i < len(control_o):
|
| 260 |
+
add = control_o[i]
|
| 261 |
+
if add is not None:
|
| 262 |
+
img[:, txt.shape[1] :, ...] += add
|
| 263 |
+
|
| 264 |
+
img = img[:, txt.shape[1] :, ...]
|
| 265 |
+
if hasattr(self, "final_layer"):
|
| 266 |
+
final_mod = self.get_modulations(mod_vectors, "final")
|
| 267 |
+
img = self.final_layer(img, vec=final_mod) # (N, T, patch_size ** 2 * out_channels)
|
| 268 |
+
return img
|
| 269 |
+
|
| 270 |
+
def forward(self, x, timestep, context, guidance, control=None, transformer_options={}, **kwargs):
|
| 271 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 272 |
+
self._forward,
|
| 273 |
+
self,
|
| 274 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
|
| 275 |
+
).execute(x, timestep, context, guidance, control, transformer_options, **kwargs)
|
| 276 |
+
|
| 277 |
+
def _forward(self, x, timestep, context, guidance, control=None, transformer_options={}, **kwargs):
|
| 278 |
+
bs, c, h, w = x.shape
|
| 279 |
+
x = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_size, self.patch_size))
|
| 280 |
+
|
| 281 |
+
img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=self.patch_size, pw=self.patch_size)
|
| 282 |
+
|
| 283 |
+
if img.ndim != 3 or context.ndim != 3:
|
| 284 |
+
raise ValueError("Input img and txt tensors must have 3 dimensions.")
|
| 285 |
+
|
| 286 |
+
h_len = ((h + (self.patch_size // 2)) // self.patch_size)
|
| 287 |
+
w_len = ((w + (self.patch_size // 2)) // self.patch_size)
|
| 288 |
+
img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype)
|
| 289 |
+
img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype).unsqueeze(1)
|
| 290 |
+
img_ids[:, :, 2] = img_ids[:, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).unsqueeze(0)
|
| 291 |
+
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
| 292 |
+
|
| 293 |
+
txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
|
| 294 |
+
out = self.forward_orig(img, img_ids, context, txt_ids, timestep, guidance, control, transformer_options, attn_mask=kwargs.get("attention_mask", None))
|
| 295 |
+
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,:w]
|
ComfyUI/comfy/ldm/chroma_radiance/layers.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Adapted from https://github.com/lodestone-rock/flow
|
| 2 |
+
from functools import lru_cache
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from torch import nn
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class NerfEmbedder(nn.Module):
|
| 9 |
+
"""
|
| 10 |
+
An embedder module that combines input features with a 2D positional
|
| 11 |
+
encoding that mimics the Discrete Cosine Transform (DCT).
|
| 12 |
+
|
| 13 |
+
This module takes an input tensor of shape (B, P^2, C), where P is the
|
| 14 |
+
patch size, and enriches it with positional information before projecting
|
| 15 |
+
it to a new hidden size.
|
| 16 |
+
"""
|
| 17 |
+
def __init__(
|
| 18 |
+
self,
|
| 19 |
+
in_channels: int,
|
| 20 |
+
hidden_size_input: int,
|
| 21 |
+
max_freqs: int,
|
| 22 |
+
dtype=None,
|
| 23 |
+
device=None,
|
| 24 |
+
operations=None,
|
| 25 |
+
):
|
| 26 |
+
"""
|
| 27 |
+
Initializes the NerfEmbedder.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
in_channels (int): The number of channels in the input tensor.
|
| 31 |
+
hidden_size_input (int): The desired dimension of the output embedding.
|
| 32 |
+
max_freqs (int): The number of frequency components to use for both
|
| 33 |
+
the x and y dimensions of the positional encoding.
|
| 34 |
+
The total number of positional features will be max_freqs^2.
|
| 35 |
+
"""
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.dtype = dtype
|
| 38 |
+
self.max_freqs = max_freqs
|
| 39 |
+
self.hidden_size_input = hidden_size_input
|
| 40 |
+
|
| 41 |
+
# A linear layer to project the concatenated input features and
|
| 42 |
+
# positional encodings to the final output dimension.
|
| 43 |
+
self.embedder = nn.Sequential(
|
| 44 |
+
operations.Linear(in_channels + max_freqs**2, hidden_size_input, dtype=dtype, device=device)
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
@lru_cache(maxsize=4)
|
| 48 |
+
def fetch_pos(self, patch_size: int, device: torch.device, dtype: torch.dtype) -> torch.Tensor:
|
| 49 |
+
"""
|
| 50 |
+
Generates and caches 2D DCT-like positional embeddings for a given patch size.
|
| 51 |
+
|
| 52 |
+
The LRU cache is a performance optimization that avoids recomputing the
|
| 53 |
+
same positional grid on every forward pass.
|
| 54 |
+
|
| 55 |
+
Args:
|
| 56 |
+
patch_size (int): The side length of the square input patch.
|
| 57 |
+
device: The torch device to create the tensors on.
|
| 58 |
+
dtype: The torch dtype for the tensors.
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
A tensor of shape (1, patch_size^2, max_freqs^2) containing the
|
| 62 |
+
positional embeddings.
|
| 63 |
+
"""
|
| 64 |
+
# Create normalized 1D coordinate grids from 0 to 1.
|
| 65 |
+
pos_x = torch.linspace(0, 1, patch_size, device=device, dtype=dtype)
|
| 66 |
+
pos_y = torch.linspace(0, 1, patch_size, device=device, dtype=dtype)
|
| 67 |
+
|
| 68 |
+
# Create a 2D meshgrid of coordinates.
|
| 69 |
+
pos_y, pos_x = torch.meshgrid(pos_y, pos_x, indexing="ij")
|
| 70 |
+
|
| 71 |
+
# Reshape positions to be broadcastable with frequencies.
|
| 72 |
+
# Shape becomes (patch_size^2, 1, 1).
|
| 73 |
+
pos_x = pos_x.reshape(-1, 1, 1)
|
| 74 |
+
pos_y = pos_y.reshape(-1, 1, 1)
|
| 75 |
+
|
| 76 |
+
# Create a 1D tensor of frequency values from 0 to max_freqs-1.
|
| 77 |
+
freqs = torch.linspace(0, self.max_freqs - 1, self.max_freqs, dtype=dtype, device=device)
|
| 78 |
+
|
| 79 |
+
# Reshape frequencies to be broadcastable for creating 2D basis functions.
|
| 80 |
+
# freqs_x shape: (1, max_freqs, 1)
|
| 81 |
+
# freqs_y shape: (1, 1, max_freqs)
|
| 82 |
+
freqs_x = freqs[None, :, None]
|
| 83 |
+
freqs_y = freqs[None, None, :]
|
| 84 |
+
|
| 85 |
+
# A custom weighting coefficient, not part of standard DCT.
|
| 86 |
+
# This seems to down-weight the contribution of higher-frequency interactions.
|
| 87 |
+
coeffs = (1 + freqs_x * freqs_y) ** -1
|
| 88 |
+
|
| 89 |
+
# Calculate the 1D cosine basis functions for x and y coordinates.
|
| 90 |
+
# This is the core of the DCT formulation.
|
| 91 |
+
dct_x = torch.cos(pos_x * freqs_x * torch.pi)
|
| 92 |
+
dct_y = torch.cos(pos_y * freqs_y * torch.pi)
|
| 93 |
+
|
| 94 |
+
# Combine the 1D basis functions to create 2D basis functions by element-wise
|
| 95 |
+
# multiplication, and apply the custom coefficients. Broadcasting handles the
|
| 96 |
+
# combination of all (pos_x, freqs_x) with all (pos_y, freqs_y).
|
| 97 |
+
# The result is flattened into a feature vector for each position.
|
| 98 |
+
dct = (dct_x * dct_y * coeffs).view(1, -1, self.max_freqs ** 2)
|
| 99 |
+
|
| 100 |
+
return dct
|
| 101 |
+
|
| 102 |
+
def forward(self, inputs: torch.Tensor) -> torch.Tensor:
|
| 103 |
+
"""
|
| 104 |
+
Forward pass for the embedder.
|
| 105 |
+
|
| 106 |
+
Args:
|
| 107 |
+
inputs (Tensor): The input tensor of shape (B, P^2, C).
|
| 108 |
+
|
| 109 |
+
Returns:
|
| 110 |
+
Tensor: The output tensor of shape (B, P^2, hidden_size_input).
|
| 111 |
+
"""
|
| 112 |
+
# Get the batch size, number of pixels, and number of channels.
|
| 113 |
+
B, P2, C = inputs.shape
|
| 114 |
+
|
| 115 |
+
# Infer the patch side length from the number of pixels (P^2).
|
| 116 |
+
patch_size = int(P2 ** 0.5)
|
| 117 |
+
|
| 118 |
+
input_dtype = inputs.dtype
|
| 119 |
+
inputs = inputs.to(dtype=self.dtype)
|
| 120 |
+
|
| 121 |
+
# Fetch the pre-computed or cached positional embeddings.
|
| 122 |
+
dct = self.fetch_pos(patch_size, inputs.device, self.dtype)
|
| 123 |
+
|
| 124 |
+
# Repeat the positional embeddings for each item in the batch.
|
| 125 |
+
dct = dct.repeat(B, 1, 1)
|
| 126 |
+
|
| 127 |
+
# Concatenate the original input features with the positional embeddings
|
| 128 |
+
# along the feature dimension.
|
| 129 |
+
inputs = torch.cat((inputs, dct), dim=-1)
|
| 130 |
+
|
| 131 |
+
# Project the combined tensor to the target hidden size.
|
| 132 |
+
return self.embedder(inputs).to(dtype=input_dtype)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
class NerfGLUBlock(nn.Module):
|
| 136 |
+
"""
|
| 137 |
+
A NerfBlock using a Gated Linear Unit (GLU) like MLP.
|
| 138 |
+
"""
|
| 139 |
+
def __init__(self, hidden_size_s: int, hidden_size_x: int, mlp_ratio, dtype=None, device=None, operations=None):
|
| 140 |
+
super().__init__()
|
| 141 |
+
# The total number of parameters for the MLP is increased to accommodate
|
| 142 |
+
# the gate, value, and output projection matrices.
|
| 143 |
+
# We now need to generate parameters for 3 matrices.
|
| 144 |
+
total_params = 3 * hidden_size_x**2 * mlp_ratio
|
| 145 |
+
self.param_generator = operations.Linear(hidden_size_s, total_params, dtype=dtype, device=device)
|
| 146 |
+
self.norm = operations.RMSNorm(hidden_size_x, dtype=dtype, device=device)
|
| 147 |
+
self.mlp_ratio = mlp_ratio
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def forward(self, x: torch.Tensor, s: torch.Tensor) -> torch.Tensor:
|
| 151 |
+
batch_size, num_x, hidden_size_x = x.shape
|
| 152 |
+
mlp_params = self.param_generator(s)
|
| 153 |
+
|
| 154 |
+
# Split the generated parameters into three parts for the gate, value, and output projection.
|
| 155 |
+
fc1_gate_params, fc1_value_params, fc2_params = mlp_params.chunk(3, dim=-1)
|
| 156 |
+
|
| 157 |
+
# Reshape the parameters into matrices for batch matrix multiplication.
|
| 158 |
+
fc1_gate = fc1_gate_params.view(batch_size, hidden_size_x, hidden_size_x * self.mlp_ratio)
|
| 159 |
+
fc1_value = fc1_value_params.view(batch_size, hidden_size_x, hidden_size_x * self.mlp_ratio)
|
| 160 |
+
fc2 = fc2_params.view(batch_size, hidden_size_x * self.mlp_ratio, hidden_size_x)
|
| 161 |
+
|
| 162 |
+
# Normalize the generated weight matrices as in the original implementation.
|
| 163 |
+
fc1_gate = torch.nn.functional.normalize(fc1_gate, dim=-2)
|
| 164 |
+
fc1_value = torch.nn.functional.normalize(fc1_value, dim=-2)
|
| 165 |
+
fc2 = torch.nn.functional.normalize(fc2, dim=-2)
|
| 166 |
+
|
| 167 |
+
res_x = x
|
| 168 |
+
x = self.norm(x)
|
| 169 |
+
|
| 170 |
+
# Apply the final output projection.
|
| 171 |
+
x = torch.bmm(torch.nn.functional.silu(torch.bmm(x, fc1_gate)) * torch.bmm(x, fc1_value), fc2)
|
| 172 |
+
|
| 173 |
+
return x + res_x
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
class NerfFinalLayer(nn.Module):
|
| 177 |
+
def __init__(self, hidden_size, out_channels, dtype=None, device=None, operations=None):
|
| 178 |
+
super().__init__()
|
| 179 |
+
self.norm = operations.RMSNorm(hidden_size, dtype=dtype, device=device)
|
| 180 |
+
self.linear = operations.Linear(hidden_size, out_channels, dtype=dtype, device=device)
|
| 181 |
+
|
| 182 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 183 |
+
# RMSNorm normalizes over the last dimension, but our channel dim (C) is at dim=1.
|
| 184 |
+
# So we temporarily move the channel dimension to the end for the norm operation.
|
| 185 |
+
return self.linear(self.norm(x.movedim(1, -1))).movedim(-1, 1)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class NerfFinalLayerConv(nn.Module):
|
| 189 |
+
def __init__(self, hidden_size: int, out_channels: int, dtype=None, device=None, operations=None):
|
| 190 |
+
super().__init__()
|
| 191 |
+
self.norm = operations.RMSNorm(hidden_size, dtype=dtype, device=device)
|
| 192 |
+
self.conv = operations.Conv2d(
|
| 193 |
+
in_channels=hidden_size,
|
| 194 |
+
out_channels=out_channels,
|
| 195 |
+
kernel_size=3,
|
| 196 |
+
padding=1,
|
| 197 |
+
dtype=dtype,
|
| 198 |
+
device=device,
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 202 |
+
# RMSNorm normalizes over the last dimension, but our channel dim (C) is at dim=1.
|
| 203 |
+
# So we temporarily move the channel dimension to the end for the norm operation.
|
| 204 |
+
return self.conv(self.norm(x.movedim(1, -1)).movedim(-1, 1))
|
ComfyUI/comfy/ldm/chroma_radiance/model.py
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Credits:
|
| 2 |
+
# Original Flux code can be found on: https://github.com/black-forest-labs/flux
|
| 3 |
+
# Chroma Radiance adaption referenced from https://github.com/lodestone-rock/flow
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from typing import Optional
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from torch import Tensor, nn
|
| 10 |
+
from einops import repeat
|
| 11 |
+
import comfy.ldm.common_dit
|
| 12 |
+
|
| 13 |
+
from comfy.ldm.flux.layers import EmbedND, DoubleStreamBlock, SingleStreamBlock
|
| 14 |
+
|
| 15 |
+
from comfy.ldm.chroma.model import Chroma, ChromaParams
|
| 16 |
+
from comfy.ldm.chroma.layers import (
|
| 17 |
+
Approximator,
|
| 18 |
+
)
|
| 19 |
+
from .layers import (
|
| 20 |
+
NerfEmbedder,
|
| 21 |
+
NerfGLUBlock,
|
| 22 |
+
NerfFinalLayer,
|
| 23 |
+
NerfFinalLayerConv,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@dataclass
|
| 28 |
+
class ChromaRadianceParams(ChromaParams):
|
| 29 |
+
patch_size: int
|
| 30 |
+
nerf_hidden_size: int
|
| 31 |
+
nerf_mlp_ratio: int
|
| 32 |
+
nerf_depth: int
|
| 33 |
+
nerf_max_freqs: int
|
| 34 |
+
# Setting nerf_tile_size to 0 disables tiling.
|
| 35 |
+
nerf_tile_size: int
|
| 36 |
+
# Currently one of linear (legacy) or conv.
|
| 37 |
+
nerf_final_head_type: str
|
| 38 |
+
# None means use the same dtype as the model.
|
| 39 |
+
nerf_embedder_dtype: Optional[torch.dtype]
|
| 40 |
+
use_x0: bool
|
| 41 |
+
# Use sequential txt_ids instead of zeros
|
| 42 |
+
use_sequential_txt_ids: bool
|
| 43 |
+
|
| 44 |
+
class ChromaRadiance(Chroma):
|
| 45 |
+
"""
|
| 46 |
+
Transformer model for flow matching on sequences.
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
|
| 50 |
+
if operations is None:
|
| 51 |
+
raise RuntimeError("Attempt to create ChromaRadiance object without setting operations")
|
| 52 |
+
nn.Module.__init__(self)
|
| 53 |
+
self.dtype = dtype
|
| 54 |
+
params = ChromaRadianceParams(**kwargs)
|
| 55 |
+
self.params = params
|
| 56 |
+
self.patch_size = params.patch_size
|
| 57 |
+
self.in_channels = params.in_channels
|
| 58 |
+
self.out_channels = params.out_channels
|
| 59 |
+
if params.hidden_size % params.num_heads != 0:
|
| 60 |
+
raise ValueError(
|
| 61 |
+
f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
|
| 62 |
+
)
|
| 63 |
+
pe_dim = params.hidden_size // params.num_heads
|
| 64 |
+
if sum(params.axes_dim) != pe_dim:
|
| 65 |
+
raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
|
| 66 |
+
self.hidden_size = params.hidden_size
|
| 67 |
+
self.num_heads = params.num_heads
|
| 68 |
+
self.in_dim = params.in_dim
|
| 69 |
+
self.out_dim = params.out_dim
|
| 70 |
+
self.hidden_dim = params.hidden_dim
|
| 71 |
+
self.n_layers = params.n_layers
|
| 72 |
+
self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
|
| 73 |
+
self.img_in_patch = operations.Conv2d(
|
| 74 |
+
params.in_channels,
|
| 75 |
+
params.hidden_size,
|
| 76 |
+
kernel_size=params.patch_size,
|
| 77 |
+
stride=params.patch_size,
|
| 78 |
+
bias=True,
|
| 79 |
+
dtype=dtype,
|
| 80 |
+
device=device,
|
| 81 |
+
)
|
| 82 |
+
self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, dtype=dtype, device=device)
|
| 83 |
+
# set as nn identity for now, will overwrite it later.
|
| 84 |
+
self.distilled_guidance_layer = Approximator(
|
| 85 |
+
in_dim=self.in_dim,
|
| 86 |
+
hidden_dim=self.hidden_dim,
|
| 87 |
+
out_dim=self.out_dim,
|
| 88 |
+
n_layers=self.n_layers,
|
| 89 |
+
dtype=dtype, device=device, operations=operations
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
self.double_blocks = nn.ModuleList(
|
| 93 |
+
[
|
| 94 |
+
DoubleStreamBlock(
|
| 95 |
+
self.hidden_size,
|
| 96 |
+
self.num_heads,
|
| 97 |
+
mlp_ratio=params.mlp_ratio,
|
| 98 |
+
qkv_bias=params.qkv_bias,
|
| 99 |
+
modulation=False,
|
| 100 |
+
dtype=dtype, device=device, operations=operations
|
| 101 |
+
)
|
| 102 |
+
for _ in range(params.depth)
|
| 103 |
+
]
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
self.single_blocks = nn.ModuleList(
|
| 107 |
+
[
|
| 108 |
+
SingleStreamBlock(
|
| 109 |
+
self.hidden_size,
|
| 110 |
+
self.num_heads,
|
| 111 |
+
mlp_ratio=params.mlp_ratio,
|
| 112 |
+
modulation=False,
|
| 113 |
+
dtype=dtype, device=device, operations=operations,
|
| 114 |
+
)
|
| 115 |
+
for _ in range(params.depth_single_blocks)
|
| 116 |
+
]
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
# pixel channel concat with DCT
|
| 120 |
+
self.nerf_image_embedder = NerfEmbedder(
|
| 121 |
+
in_channels=params.in_channels,
|
| 122 |
+
hidden_size_input=params.nerf_hidden_size,
|
| 123 |
+
max_freqs=params.nerf_max_freqs,
|
| 124 |
+
dtype=params.nerf_embedder_dtype or dtype,
|
| 125 |
+
device=device,
|
| 126 |
+
operations=operations,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
self.nerf_blocks = nn.ModuleList([
|
| 130 |
+
NerfGLUBlock(
|
| 131 |
+
hidden_size_s=params.hidden_size,
|
| 132 |
+
hidden_size_x=params.nerf_hidden_size,
|
| 133 |
+
mlp_ratio=params.nerf_mlp_ratio,
|
| 134 |
+
dtype=dtype,
|
| 135 |
+
device=device,
|
| 136 |
+
operations=operations,
|
| 137 |
+
) for _ in range(params.nerf_depth)
|
| 138 |
+
])
|
| 139 |
+
|
| 140 |
+
if params.nerf_final_head_type == "linear":
|
| 141 |
+
self.nerf_final_layer = NerfFinalLayer(
|
| 142 |
+
params.nerf_hidden_size,
|
| 143 |
+
out_channels=params.in_channels,
|
| 144 |
+
dtype=dtype,
|
| 145 |
+
device=device,
|
| 146 |
+
operations=operations,
|
| 147 |
+
)
|
| 148 |
+
elif params.nerf_final_head_type == "conv":
|
| 149 |
+
self.nerf_final_layer_conv = NerfFinalLayerConv(
|
| 150 |
+
params.nerf_hidden_size,
|
| 151 |
+
out_channels=params.in_channels,
|
| 152 |
+
dtype=dtype,
|
| 153 |
+
device=device,
|
| 154 |
+
operations=operations,
|
| 155 |
+
)
|
| 156 |
+
else:
|
| 157 |
+
errstr = f"Unsupported nerf_final_head_type {params.nerf_final_head_type}"
|
| 158 |
+
raise ValueError(errstr)
|
| 159 |
+
|
| 160 |
+
self.skip_mmdit = []
|
| 161 |
+
self.skip_dit = []
|
| 162 |
+
self.lite = False
|
| 163 |
+
|
| 164 |
+
if params.use_x0:
|
| 165 |
+
self.register_buffer("__x0__", torch.tensor([]))
|
| 166 |
+
|
| 167 |
+
if params.use_sequential_txt_ids:
|
| 168 |
+
self.register_buffer("__sequential__", torch.tensor([]))
|
| 169 |
+
|
| 170 |
+
@property
|
| 171 |
+
def _nerf_final_layer(self) -> nn.Module:
|
| 172 |
+
if self.params.nerf_final_head_type == "linear":
|
| 173 |
+
return self.nerf_final_layer
|
| 174 |
+
if self.params.nerf_final_head_type == "conv":
|
| 175 |
+
return self.nerf_final_layer_conv
|
| 176 |
+
# Impossible to get here as we raise an error on unexpected types on initialization.
|
| 177 |
+
raise NotImplementedError
|
| 178 |
+
|
| 179 |
+
def img_in(self, img: Tensor) -> Tensor:
|
| 180 |
+
img = self.img_in_patch(img) # -> [B, Hidden, H/P, W/P]
|
| 181 |
+
# flatten into a sequence for the transformer.
|
| 182 |
+
return img.flatten(2).transpose(1, 2) # -> [B, NumPatches, Hidden]
|
| 183 |
+
|
| 184 |
+
def forward_nerf(
|
| 185 |
+
self,
|
| 186 |
+
img_orig: Tensor,
|
| 187 |
+
img_out: Tensor,
|
| 188 |
+
params: ChromaRadianceParams,
|
| 189 |
+
) -> Tensor:
|
| 190 |
+
B, C, H, W = img_orig.shape
|
| 191 |
+
num_patches = img_out.shape[1]
|
| 192 |
+
patch_size = params.patch_size
|
| 193 |
+
|
| 194 |
+
# Store the raw pixel values of each patch for the NeRF head later.
|
| 195 |
+
# unfold creates patches: [B, C * P * P, NumPatches]
|
| 196 |
+
nerf_pixels = nn.functional.unfold(img_orig, kernel_size=patch_size, stride=patch_size)
|
| 197 |
+
nerf_pixels = nerf_pixels.transpose(1, 2) # -> [B, NumPatches, C * P * P]
|
| 198 |
+
|
| 199 |
+
# Reshape for per-patch processing
|
| 200 |
+
nerf_hidden = img_out.reshape(B * num_patches, params.hidden_size)
|
| 201 |
+
nerf_pixels = nerf_pixels.reshape(B * num_patches, C, patch_size**2).transpose(1, 2)
|
| 202 |
+
|
| 203 |
+
if params.nerf_tile_size > 0 and num_patches > params.nerf_tile_size:
|
| 204 |
+
# Enable tiling if nerf_tile_size isn't 0 and we actually have more patches than
|
| 205 |
+
# the tile size.
|
| 206 |
+
img_dct = self.forward_tiled_nerf(nerf_hidden, nerf_pixels, B, C, num_patches, patch_size, params)
|
| 207 |
+
else:
|
| 208 |
+
# Get DCT-encoded pixel embeddings [pixel-dct]
|
| 209 |
+
img_dct = self.nerf_image_embedder(nerf_pixels)
|
| 210 |
+
|
| 211 |
+
# Pass through the dynamic MLP blocks (the NeRF)
|
| 212 |
+
for block in self.nerf_blocks:
|
| 213 |
+
img_dct = block(img_dct, nerf_hidden)
|
| 214 |
+
|
| 215 |
+
# Reassemble the patches into the final image.
|
| 216 |
+
img_dct = img_dct.transpose(1, 2) # -> [B*NumPatches, C, P*P]
|
| 217 |
+
# Reshape to combine with batch dimension for fold
|
| 218 |
+
img_dct = img_dct.reshape(B, num_patches, -1) # -> [B, NumPatches, C*P*P]
|
| 219 |
+
img_dct = img_dct.transpose(1, 2) # -> [B, C*P*P, NumPatches]
|
| 220 |
+
img_dct = nn.functional.fold(
|
| 221 |
+
img_dct,
|
| 222 |
+
output_size=(H, W),
|
| 223 |
+
kernel_size=patch_size,
|
| 224 |
+
stride=patch_size,
|
| 225 |
+
)
|
| 226 |
+
return self._nerf_final_layer(img_dct)
|
| 227 |
+
|
| 228 |
+
def forward_tiled_nerf(
|
| 229 |
+
self,
|
| 230 |
+
nerf_hidden: Tensor,
|
| 231 |
+
nerf_pixels: Tensor,
|
| 232 |
+
batch: int,
|
| 233 |
+
channels: int,
|
| 234 |
+
num_patches: int,
|
| 235 |
+
patch_size: int,
|
| 236 |
+
params: ChromaRadianceParams,
|
| 237 |
+
) -> Tensor:
|
| 238 |
+
"""
|
| 239 |
+
Processes the NeRF head in tiles to save memory.
|
| 240 |
+
nerf_hidden has shape [B, L, D]
|
| 241 |
+
nerf_pixels has shape [B, L, C * P * P]
|
| 242 |
+
"""
|
| 243 |
+
tile_size = params.nerf_tile_size
|
| 244 |
+
output_tiles = []
|
| 245 |
+
# Iterate over the patches in tiles. The dimension L (num_patches) is at index 1.
|
| 246 |
+
for i in range(0, num_patches, tile_size):
|
| 247 |
+
end = min(i + tile_size, num_patches)
|
| 248 |
+
|
| 249 |
+
# Slice the current tile from the input tensors
|
| 250 |
+
nerf_hidden_tile = nerf_hidden[i * batch:end * batch]
|
| 251 |
+
nerf_pixels_tile = nerf_pixels[i * batch:end * batch]
|
| 252 |
+
|
| 253 |
+
# get DCT-encoded pixel embeddings [pixel-dct]
|
| 254 |
+
img_dct_tile = self.nerf_image_embedder(nerf_pixels_tile)
|
| 255 |
+
|
| 256 |
+
# pass through the dynamic MLP blocks (the NeRF)
|
| 257 |
+
for block in self.nerf_blocks:
|
| 258 |
+
img_dct_tile = block(img_dct_tile, nerf_hidden_tile)
|
| 259 |
+
|
| 260 |
+
output_tiles.append(img_dct_tile)
|
| 261 |
+
|
| 262 |
+
# Concatenate the processed tiles along the patch dimension
|
| 263 |
+
return torch.cat(output_tiles, dim=0)
|
| 264 |
+
|
| 265 |
+
def radiance_get_override_params(self, overrides: dict) -> ChromaRadianceParams:
|
| 266 |
+
params = self.params
|
| 267 |
+
if not overrides:
|
| 268 |
+
return params
|
| 269 |
+
params_dict = {k: getattr(params, k) for k in params.__dataclass_fields__}
|
| 270 |
+
nullable_keys = frozenset(("nerf_embedder_dtype",))
|
| 271 |
+
bad_keys = tuple(k for k in overrides if k not in params_dict)
|
| 272 |
+
if bad_keys:
|
| 273 |
+
e = f"Unknown key(s) in transformer_options chroma_radiance_options: {', '.join(bad_keys)}"
|
| 274 |
+
raise ValueError(e)
|
| 275 |
+
bad_keys = tuple(
|
| 276 |
+
k
|
| 277 |
+
for k, v in overrides.items()
|
| 278 |
+
if not isinstance(v, type(getattr(params, k))) and (v is not None or k not in nullable_keys)
|
| 279 |
+
)
|
| 280 |
+
if bad_keys:
|
| 281 |
+
e = f"Invalid value(s) in transformer_options chroma_radiance_options: {', '.join(bad_keys)}"
|
| 282 |
+
raise ValueError(e)
|
| 283 |
+
# At this point it's all valid keys and values so we can merge with the existing params.
|
| 284 |
+
params_dict |= overrides
|
| 285 |
+
return params.__class__(**params_dict)
|
| 286 |
+
|
| 287 |
+
def _apply_x0_residual(self, predicted, noisy, timesteps):
|
| 288 |
+
|
| 289 |
+
# non zero during training to prevent 0 div
|
| 290 |
+
eps = 0.0
|
| 291 |
+
return (noisy - predicted) / (timesteps.view(-1,1,1,1) + eps)
|
| 292 |
+
|
| 293 |
+
def _forward(
|
| 294 |
+
self,
|
| 295 |
+
x: Tensor,
|
| 296 |
+
timestep: Tensor,
|
| 297 |
+
context: Tensor,
|
| 298 |
+
guidance: Optional[Tensor],
|
| 299 |
+
control: Optional[dict]=None,
|
| 300 |
+
transformer_options: dict={},
|
| 301 |
+
**kwargs: dict,
|
| 302 |
+
) -> Tensor:
|
| 303 |
+
bs, c, h, w = x.shape
|
| 304 |
+
img = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_size, self.patch_size))
|
| 305 |
+
|
| 306 |
+
if img.ndim != 4:
|
| 307 |
+
raise ValueError("Input img tensor must be in [B, C, H, W] format.")
|
| 308 |
+
if context.ndim != 3:
|
| 309 |
+
raise ValueError("Input txt tensors must have 3 dimensions.")
|
| 310 |
+
|
| 311 |
+
params = self.radiance_get_override_params(transformer_options.get("chroma_radiance_options", {}))
|
| 312 |
+
|
| 313 |
+
h_len = (img.shape[-2] // self.patch_size)
|
| 314 |
+
w_len = (img.shape[-1] // self.patch_size)
|
| 315 |
+
|
| 316 |
+
img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype)
|
| 317 |
+
img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype).unsqueeze(1)
|
| 318 |
+
img_ids[:, :, 2] = img_ids[:, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).unsqueeze(0)
|
| 319 |
+
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
| 320 |
+
txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
|
| 321 |
+
# Radiance after 2026-05-22 uses sequential txt_ids instead of zeros
|
| 322 |
+
if params.use_sequential_txt_ids:
|
| 323 |
+
txt_ids[:, :, 0] = torch.arange(context.shape[1], device=x.device, dtype=x.dtype).unsqueeze(0).expand(bs, -1)
|
| 324 |
+
|
| 325 |
+
img_out = self.forward_orig(
|
| 326 |
+
img,
|
| 327 |
+
img_ids,
|
| 328 |
+
context,
|
| 329 |
+
txt_ids,
|
| 330 |
+
timestep,
|
| 331 |
+
guidance,
|
| 332 |
+
control,
|
| 333 |
+
transformer_options,
|
| 334 |
+
attn_mask=kwargs.get("attention_mask", None),
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
out = self.forward_nerf(img, img_out, params)[:, :, :h, :w]
|
| 338 |
+
|
| 339 |
+
# If x0 variant → v-pred, just return this instead
|
| 340 |
+
if hasattr(self, "__x0__"):
|
| 341 |
+
out = self._apply_x0_residual(out, img, timestep)
|
| 342 |
+
return out
|
| 343 |
+
|
ComfyUI/comfy/ldm/cogvideo/__init__.py
ADDED
|
File without changes
|
ComfyUI/comfy/ldm/cogvideo/model.py
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CogVideoX 3D Transformer - ported to ComfyUI native ops
|
| 2 |
+
# Architecture reference: diffusers CogVideoXTransformer3DModel
|
| 3 |
+
# Style reference: comfy/ldm/wan/model.py
|
| 4 |
+
|
| 5 |
+
import math
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
|
| 10 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 11 |
+
import comfy.patcher_extension
|
| 12 |
+
import comfy.ldm.common_dit
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def _get_1d_rotary_pos_embed(dim, pos, theta=10000.0):
|
| 16 |
+
"""Returns (cos, sin) each with shape [seq_len, dim].
|
| 17 |
+
|
| 18 |
+
Frequencies are computed at dim//2 resolution then repeat_interleaved
|
| 19 |
+
to full dim, matching CogVideoX's interleaved (real, imag) pair format.
|
| 20 |
+
"""
|
| 21 |
+
freqs = 1.0 / (theta ** (torch.arange(0, dim, 2, dtype=torch.float32, device=pos.device) / dim))
|
| 22 |
+
angles = torch.outer(pos.float(), freqs.float())
|
| 23 |
+
cos = angles.cos().repeat_interleave(2, dim=-1).float()
|
| 24 |
+
sin = angles.sin().repeat_interleave(2, dim=-1).float()
|
| 25 |
+
return (cos, sin)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def apply_rotary_emb(x, freqs_cos_sin):
|
| 29 |
+
"""Apply CogVideoX rotary embedding to query or key tensor.
|
| 30 |
+
|
| 31 |
+
x: [B, heads, seq_len, head_dim]
|
| 32 |
+
freqs_cos_sin: (cos, sin) each [seq_len, head_dim//2]
|
| 33 |
+
|
| 34 |
+
Uses interleaved pair rotation (same as diffusers CogVideoX/Flux).
|
| 35 |
+
head_dim is reshaped to (-1, 2) pairs, rotated, then flattened back.
|
| 36 |
+
"""
|
| 37 |
+
cos, sin = freqs_cos_sin
|
| 38 |
+
cos = cos[None, None, :, :].to(x.device)
|
| 39 |
+
sin = sin[None, None, :, :].to(x.device)
|
| 40 |
+
|
| 41 |
+
# Interleaved pairs: [B, H, S, D] -> [B, H, S, D//2, 2] -> (real, imag)
|
| 42 |
+
x_real, x_imag = x.reshape(*x.shape[:-1], -1, 2).unbind(-1)
|
| 43 |
+
x_rotated = torch.stack([-x_imag, x_real], dim=-1).flatten(3)
|
| 44 |
+
|
| 45 |
+
return (x.float() * cos + x_rotated.float() * sin).to(x.dtype)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def get_timestep_embedding(timesteps, dim, flip_sin_to_cos=True, downscale_freq_shift=0, scale=1, max_period=10000):
|
| 49 |
+
half = dim // 2
|
| 50 |
+
freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=timesteps.device) / half)
|
| 51 |
+
args = timesteps[:, None].float() * freqs[None] * scale
|
| 52 |
+
embedding = torch.cat([torch.sin(args), torch.cos(args)], dim=-1)
|
| 53 |
+
if flip_sin_to_cos:
|
| 54 |
+
embedding = torch.cat([embedding[:, half:], embedding[:, :half]], dim=-1)
|
| 55 |
+
if dim % 2:
|
| 56 |
+
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
| 57 |
+
return embedding
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def get_3d_sincos_pos_embed(embed_dim, spatial_size, temporal_size, spatial_interpolation_scale=1.0, temporal_interpolation_scale=1.0, device=None):
|
| 61 |
+
if isinstance(spatial_size, int):
|
| 62 |
+
spatial_size = (spatial_size, spatial_size)
|
| 63 |
+
|
| 64 |
+
grid_w = torch.arange(spatial_size[0], dtype=torch.float32, device=device) / spatial_interpolation_scale
|
| 65 |
+
grid_h = torch.arange(spatial_size[1], dtype=torch.float32, device=device) / spatial_interpolation_scale
|
| 66 |
+
grid_t = torch.arange(temporal_size, dtype=torch.float32, device=device) / temporal_interpolation_scale
|
| 67 |
+
|
| 68 |
+
grid_t, grid_h, grid_w = torch.meshgrid(grid_t, grid_h, grid_w, indexing="ij")
|
| 69 |
+
|
| 70 |
+
embed_dim_spatial = 2 * (embed_dim // 3)
|
| 71 |
+
embed_dim_temporal = embed_dim // 3
|
| 72 |
+
|
| 73 |
+
pos_embed_spatial = _get_2d_sincos_pos_embed(embed_dim_spatial, grid_h, grid_w, device=device)
|
| 74 |
+
pos_embed_temporal = _get_1d_sincos_pos_embed(embed_dim_temporal, grid_t[:, 0, 0], device=device)
|
| 75 |
+
|
| 76 |
+
T, H, W = grid_t.shape
|
| 77 |
+
pos_embed_temporal = pos_embed_temporal.unsqueeze(1).unsqueeze(1).expand(-1, H, W, -1)
|
| 78 |
+
pos_embed = torch.cat([pos_embed_temporal, pos_embed_spatial], dim=-1)
|
| 79 |
+
|
| 80 |
+
return pos_embed
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def _get_2d_sincos_pos_embed(embed_dim, grid_h, grid_w, device=None):
|
| 84 |
+
T, H, W = grid_h.shape
|
| 85 |
+
half_dim = embed_dim // 2
|
| 86 |
+
pos_h = _get_1d_sincos_pos_embed(half_dim, grid_h.reshape(-1), device=device).reshape(T, H, W, half_dim)
|
| 87 |
+
pos_w = _get_1d_sincos_pos_embed(half_dim, grid_w.reshape(-1), device=device).reshape(T, H, W, half_dim)
|
| 88 |
+
return torch.cat([pos_h, pos_w], dim=-1)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _get_1d_sincos_pos_embed(embed_dim, pos, device=None):
|
| 92 |
+
half = embed_dim // 2
|
| 93 |
+
freqs = torch.exp(-math.log(10000.0) * torch.arange(start=0, end=half, dtype=torch.float32, device=device) / half)
|
| 94 |
+
args = pos.float().reshape(-1)[:, None] * freqs[None]
|
| 95 |
+
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
| 96 |
+
if embed_dim % 2:
|
| 97 |
+
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
| 98 |
+
return embedding
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class CogVideoXPatchEmbed(nn.Module):
|
| 103 |
+
def __init__(self, patch_size=2, patch_size_t=None, in_channels=16, dim=1920,
|
| 104 |
+
text_dim=4096, bias=True, sample_width=90, sample_height=60,
|
| 105 |
+
sample_frames=49, temporal_compression_ratio=4,
|
| 106 |
+
max_text_seq_length=226, spatial_interpolation_scale=1.875,
|
| 107 |
+
temporal_interpolation_scale=1.0, use_positional_embeddings=True,
|
| 108 |
+
use_learned_positional_embeddings=True,
|
| 109 |
+
device=None, dtype=None, operations=None):
|
| 110 |
+
super().__init__()
|
| 111 |
+
self.patch_size = patch_size
|
| 112 |
+
self.patch_size_t = patch_size_t
|
| 113 |
+
self.dim = dim
|
| 114 |
+
self.sample_height = sample_height
|
| 115 |
+
self.sample_width = sample_width
|
| 116 |
+
self.sample_frames = sample_frames
|
| 117 |
+
self.temporal_compression_ratio = temporal_compression_ratio
|
| 118 |
+
self.max_text_seq_length = max_text_seq_length
|
| 119 |
+
self.spatial_interpolation_scale = spatial_interpolation_scale
|
| 120 |
+
self.temporal_interpolation_scale = temporal_interpolation_scale
|
| 121 |
+
self.use_positional_embeddings = use_positional_embeddings
|
| 122 |
+
self.use_learned_positional_embeddings = use_learned_positional_embeddings
|
| 123 |
+
|
| 124 |
+
if patch_size_t is None:
|
| 125 |
+
self.proj = operations.Conv2d(in_channels, dim, kernel_size=patch_size, stride=patch_size, bias=bias, device=device, dtype=dtype)
|
| 126 |
+
else:
|
| 127 |
+
self.proj = operations.Linear(in_channels * patch_size * patch_size * patch_size_t, dim, device=device, dtype=dtype)
|
| 128 |
+
|
| 129 |
+
self.text_proj = operations.Linear(text_dim, dim, device=device, dtype=dtype)
|
| 130 |
+
|
| 131 |
+
if use_positional_embeddings or use_learned_positional_embeddings:
|
| 132 |
+
persistent = use_learned_positional_embeddings
|
| 133 |
+
pos_embedding = self._get_positional_embeddings(sample_height, sample_width, sample_frames)
|
| 134 |
+
self.register_buffer("pos_embedding", pos_embedding, persistent=persistent)
|
| 135 |
+
|
| 136 |
+
def _get_positional_embeddings(self, sample_height, sample_width, sample_frames, device=None):
|
| 137 |
+
post_patch_height = sample_height // self.patch_size
|
| 138 |
+
post_patch_width = sample_width // self.patch_size
|
| 139 |
+
post_time_compression_frames = (sample_frames - 1) // self.temporal_compression_ratio + 1
|
| 140 |
+
if self.patch_size_t is not None:
|
| 141 |
+
post_time_compression_frames = post_time_compression_frames // self.patch_size_t
|
| 142 |
+
num_patches = post_patch_height * post_patch_width * post_time_compression_frames
|
| 143 |
+
|
| 144 |
+
pos_embedding = get_3d_sincos_pos_embed(
|
| 145 |
+
self.dim,
|
| 146 |
+
(post_patch_width, post_patch_height),
|
| 147 |
+
post_time_compression_frames,
|
| 148 |
+
self.spatial_interpolation_scale,
|
| 149 |
+
self.temporal_interpolation_scale,
|
| 150 |
+
device=device,
|
| 151 |
+
)
|
| 152 |
+
pos_embedding = pos_embedding.reshape(-1, self.dim)
|
| 153 |
+
joint_pos_embedding = pos_embedding.new_zeros(
|
| 154 |
+
1, self.max_text_seq_length + num_patches, self.dim, requires_grad=False
|
| 155 |
+
)
|
| 156 |
+
joint_pos_embedding.data[:, self.max_text_seq_length:].copy_(pos_embedding)
|
| 157 |
+
return joint_pos_embedding
|
| 158 |
+
|
| 159 |
+
def forward(self, text_embeds, image_embeds):
|
| 160 |
+
input_dtype = text_embeds.dtype
|
| 161 |
+
text_embeds = self.text_proj(text_embeds.to(self.text_proj.weight.dtype)).to(input_dtype)
|
| 162 |
+
batch_size, num_frames, channels, height, width = image_embeds.shape
|
| 163 |
+
|
| 164 |
+
proj_dtype = self.proj.weight.dtype
|
| 165 |
+
if self.patch_size_t is None:
|
| 166 |
+
image_embeds = image_embeds.reshape(-1, channels, height, width)
|
| 167 |
+
image_embeds = self.proj(image_embeds.to(proj_dtype)).to(input_dtype)
|
| 168 |
+
image_embeds = image_embeds.view(batch_size, num_frames, *image_embeds.shape[1:])
|
| 169 |
+
image_embeds = image_embeds.flatten(3).transpose(2, 3)
|
| 170 |
+
image_embeds = image_embeds.flatten(1, 2)
|
| 171 |
+
else:
|
| 172 |
+
p = self.patch_size
|
| 173 |
+
p_t = self.patch_size_t
|
| 174 |
+
image_embeds = image_embeds.permute(0, 1, 3, 4, 2)
|
| 175 |
+
image_embeds = image_embeds.reshape(
|
| 176 |
+
batch_size, num_frames // p_t, p_t, height // p, p, width // p, p, channels
|
| 177 |
+
)
|
| 178 |
+
image_embeds = image_embeds.permute(0, 1, 3, 5, 7, 2, 4, 6).flatten(4, 7).flatten(1, 3)
|
| 179 |
+
image_embeds = self.proj(image_embeds.to(proj_dtype)).to(input_dtype)
|
| 180 |
+
|
| 181 |
+
embeds = torch.cat([text_embeds, image_embeds], dim=1).contiguous()
|
| 182 |
+
|
| 183 |
+
if self.use_positional_embeddings or self.use_learned_positional_embeddings:
|
| 184 |
+
text_seq_length = text_embeds.shape[1]
|
| 185 |
+
num_image_patches = image_embeds.shape[1]
|
| 186 |
+
|
| 187 |
+
if self.use_learned_positional_embeddings:
|
| 188 |
+
image_pos = self.pos_embedding[
|
| 189 |
+
:, self.max_text_seq_length:self.max_text_seq_length + num_image_patches
|
| 190 |
+
].to(device=embeds.device, dtype=embeds.dtype)
|
| 191 |
+
else:
|
| 192 |
+
image_pos = get_3d_sincos_pos_embed(
|
| 193 |
+
self.dim,
|
| 194 |
+
(width // self.patch_size, height // self.patch_size),
|
| 195 |
+
num_image_patches // ((height // self.patch_size) * (width // self.patch_size)),
|
| 196 |
+
self.spatial_interpolation_scale,
|
| 197 |
+
self.temporal_interpolation_scale,
|
| 198 |
+
device=embeds.device,
|
| 199 |
+
).reshape(1, num_image_patches, self.dim).to(dtype=embeds.dtype)
|
| 200 |
+
|
| 201 |
+
# Build joint: zeros for text + sincos for image
|
| 202 |
+
joint_pos = torch.zeros(1, text_seq_length + num_image_patches, self.dim, device=embeds.device, dtype=embeds.dtype)
|
| 203 |
+
joint_pos[:, text_seq_length:] = image_pos
|
| 204 |
+
embeds = embeds + joint_pos
|
| 205 |
+
|
| 206 |
+
return embeds
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
class CogVideoXLayerNormZero(nn.Module):
|
| 210 |
+
def __init__(self, time_dim, dim, elementwise_affine=True, eps=1e-5, bias=True,
|
| 211 |
+
device=None, dtype=None, operations=None):
|
| 212 |
+
super().__init__()
|
| 213 |
+
self.silu = nn.SiLU()
|
| 214 |
+
self.linear = operations.Linear(time_dim, 6 * dim, bias=bias, device=device, dtype=dtype)
|
| 215 |
+
self.norm = operations.LayerNorm(dim, eps=eps, elementwise_affine=elementwise_affine, device=device, dtype=dtype)
|
| 216 |
+
|
| 217 |
+
def forward(self, hidden_states, encoder_hidden_states, temb):
|
| 218 |
+
shift, scale, gate, enc_shift, enc_scale, enc_gate = self.linear(self.silu(temb)).chunk(6, dim=1)
|
| 219 |
+
hidden_states = self.norm(hidden_states) * (1 + scale)[:, None, :] + shift[:, None, :]
|
| 220 |
+
encoder_hidden_states = self.norm(encoder_hidden_states) * (1 + enc_scale)[:, None, :] + enc_shift[:, None, :]
|
| 221 |
+
return hidden_states, encoder_hidden_states, gate[:, None, :], enc_gate[:, None, :]
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
class CogVideoXAdaLayerNorm(nn.Module):
|
| 225 |
+
def __init__(self, time_dim, dim, elementwise_affine=True, eps=1e-5,
|
| 226 |
+
device=None, dtype=None, operations=None):
|
| 227 |
+
super().__init__()
|
| 228 |
+
self.silu = nn.SiLU()
|
| 229 |
+
self.linear = operations.Linear(time_dim, 2 * dim, device=device, dtype=dtype)
|
| 230 |
+
self.norm = operations.LayerNorm(dim, eps=eps, elementwise_affine=elementwise_affine, device=device, dtype=dtype)
|
| 231 |
+
|
| 232 |
+
def forward(self, x, temb):
|
| 233 |
+
temb = self.linear(self.silu(temb))
|
| 234 |
+
shift, scale = temb.chunk(2, dim=1)
|
| 235 |
+
x = self.norm(x) * (1 + scale)[:, None, :] + shift[:, None, :]
|
| 236 |
+
return x
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
class CogVideoXBlock(nn.Module):
|
| 240 |
+
def __init__(self, dim, num_heads, head_dim, time_dim,
|
| 241 |
+
eps=1e-5, ff_inner_dim=None, ff_bias=True,
|
| 242 |
+
device=None, dtype=None, operations=None):
|
| 243 |
+
super().__init__()
|
| 244 |
+
self.dim = dim
|
| 245 |
+
self.num_heads = num_heads
|
| 246 |
+
self.head_dim = head_dim
|
| 247 |
+
|
| 248 |
+
self.norm1 = CogVideoXLayerNormZero(time_dim, dim, eps=eps, device=device, dtype=dtype, operations=operations)
|
| 249 |
+
|
| 250 |
+
# Self-attention (joint text + latent)
|
| 251 |
+
self.q = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
|
| 252 |
+
self.k = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
|
| 253 |
+
self.v = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
|
| 254 |
+
self.norm_q = operations.LayerNorm(head_dim, eps=1e-6, elementwise_affine=True, device=device, dtype=dtype)
|
| 255 |
+
self.norm_k = operations.LayerNorm(head_dim, eps=1e-6, elementwise_affine=True, device=device, dtype=dtype)
|
| 256 |
+
self.attn_out = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
|
| 257 |
+
|
| 258 |
+
self.norm2 = CogVideoXLayerNormZero(time_dim, dim, eps=eps, device=device, dtype=dtype, operations=operations)
|
| 259 |
+
|
| 260 |
+
# Feed-forward (GELU approximate)
|
| 261 |
+
inner_dim = ff_inner_dim or dim * 4
|
| 262 |
+
self.ff_proj = operations.Linear(dim, inner_dim, bias=ff_bias, device=device, dtype=dtype)
|
| 263 |
+
self.ff_out = operations.Linear(inner_dim, dim, bias=ff_bias, device=device, dtype=dtype)
|
| 264 |
+
|
| 265 |
+
def forward(self, hidden_states, encoder_hidden_states, temb, image_rotary_emb=None, transformer_options=None):
|
| 266 |
+
if transformer_options is None:
|
| 267 |
+
transformer_options = {}
|
| 268 |
+
text_seq_length = encoder_hidden_states.size(1)
|
| 269 |
+
|
| 270 |
+
# Norm & modulate
|
| 271 |
+
norm_hidden, norm_encoder, gate_msa, enc_gate_msa = self.norm1(hidden_states, encoder_hidden_states, temb)
|
| 272 |
+
|
| 273 |
+
# Joint self-attention
|
| 274 |
+
qkv_input = torch.cat([norm_encoder, norm_hidden], dim=1)
|
| 275 |
+
b, s, _ = qkv_input.shape
|
| 276 |
+
n, d = self.num_heads, self.head_dim
|
| 277 |
+
|
| 278 |
+
q = self.q(qkv_input).view(b, s, n, d)
|
| 279 |
+
k = self.k(qkv_input).view(b, s, n, d)
|
| 280 |
+
v = self.v(qkv_input)
|
| 281 |
+
|
| 282 |
+
q = self.norm_q(q).view(b, s, n, d)
|
| 283 |
+
k = self.norm_k(k).view(b, s, n, d)
|
| 284 |
+
|
| 285 |
+
# Apply rotary embeddings to image tokens only (diffusers format: [B, heads, seq, head_dim])
|
| 286 |
+
if image_rotary_emb is not None:
|
| 287 |
+
q_img = q[:, text_seq_length:].transpose(1, 2) # [B, heads, img_seq, head_dim]
|
| 288 |
+
k_img = k[:, text_seq_length:].transpose(1, 2)
|
| 289 |
+
q_img = apply_rotary_emb(q_img, image_rotary_emb)
|
| 290 |
+
k_img = apply_rotary_emb(k_img, image_rotary_emb)
|
| 291 |
+
q = torch.cat([q[:, :text_seq_length], q_img.transpose(1, 2)], dim=1)
|
| 292 |
+
k = torch.cat([k[:, :text_seq_length], k_img.transpose(1, 2)], dim=1)
|
| 293 |
+
|
| 294 |
+
attn_out = optimized_attention(
|
| 295 |
+
q.reshape(b, s, n * d),
|
| 296 |
+
k.reshape(b, s, n * d),
|
| 297 |
+
v,
|
| 298 |
+
heads=self.num_heads,
|
| 299 |
+
transformer_options=transformer_options,
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
attn_out = self.attn_out(attn_out)
|
| 303 |
+
|
| 304 |
+
attn_encoder, attn_hidden = attn_out.split([text_seq_length, s - text_seq_length], dim=1)
|
| 305 |
+
|
| 306 |
+
hidden_states = hidden_states + gate_msa * attn_hidden
|
| 307 |
+
encoder_hidden_states = encoder_hidden_states + enc_gate_msa * attn_encoder
|
| 308 |
+
|
| 309 |
+
# Norm & modulate for FF
|
| 310 |
+
norm_hidden, norm_encoder, gate_ff, enc_gate_ff = self.norm2(hidden_states, encoder_hidden_states, temb)
|
| 311 |
+
|
| 312 |
+
# Feed-forward (GELU on concatenated text + latent)
|
| 313 |
+
ff_input = torch.cat([norm_encoder, norm_hidden], dim=1)
|
| 314 |
+
ff_output = self.ff_out(F.gelu(self.ff_proj(ff_input), approximate="tanh"))
|
| 315 |
+
|
| 316 |
+
hidden_states = hidden_states + gate_ff * ff_output[:, text_seq_length:]
|
| 317 |
+
encoder_hidden_states = encoder_hidden_states + enc_gate_ff * ff_output[:, :text_seq_length]
|
| 318 |
+
|
| 319 |
+
return hidden_states, encoder_hidden_states
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
class CogVideoXTransformer3DModel(nn.Module):
|
| 323 |
+
def __init__(self,
|
| 324 |
+
num_attention_heads=30,
|
| 325 |
+
attention_head_dim=64,
|
| 326 |
+
in_channels=16,
|
| 327 |
+
out_channels=16,
|
| 328 |
+
flip_sin_to_cos=True,
|
| 329 |
+
freq_shift=0,
|
| 330 |
+
time_embed_dim=512,
|
| 331 |
+
ofs_embed_dim=None,
|
| 332 |
+
text_embed_dim=4096,
|
| 333 |
+
num_layers=30,
|
| 334 |
+
dropout=0.0,
|
| 335 |
+
attention_bias=True,
|
| 336 |
+
sample_width=90,
|
| 337 |
+
sample_height=60,
|
| 338 |
+
sample_frames=49,
|
| 339 |
+
patch_size=2,
|
| 340 |
+
patch_size_t=None,
|
| 341 |
+
temporal_compression_ratio=4,
|
| 342 |
+
max_text_seq_length=226,
|
| 343 |
+
spatial_interpolation_scale=1.875,
|
| 344 |
+
temporal_interpolation_scale=1.0,
|
| 345 |
+
use_rotary_positional_embeddings=False,
|
| 346 |
+
use_learned_positional_embeddings=False,
|
| 347 |
+
patch_bias=True,
|
| 348 |
+
image_model=None,
|
| 349 |
+
device=None,
|
| 350 |
+
dtype=None,
|
| 351 |
+
operations=None,
|
| 352 |
+
):
|
| 353 |
+
super().__init__()
|
| 354 |
+
self.dtype = dtype
|
| 355 |
+
dim = num_attention_heads * attention_head_dim
|
| 356 |
+
self.dim = dim
|
| 357 |
+
self.num_attention_heads = num_attention_heads
|
| 358 |
+
self.attention_head_dim = attention_head_dim
|
| 359 |
+
self.in_channels = in_channels
|
| 360 |
+
self.out_channels = out_channels
|
| 361 |
+
self.patch_size = patch_size
|
| 362 |
+
self.patch_size_t = patch_size_t
|
| 363 |
+
self.max_text_seq_length = max_text_seq_length
|
| 364 |
+
self.use_rotary_positional_embeddings = use_rotary_positional_embeddings
|
| 365 |
+
|
| 366 |
+
# 1. Patch embedding
|
| 367 |
+
self.patch_embed = CogVideoXPatchEmbed(
|
| 368 |
+
patch_size=patch_size,
|
| 369 |
+
patch_size_t=patch_size_t,
|
| 370 |
+
in_channels=in_channels,
|
| 371 |
+
dim=dim,
|
| 372 |
+
text_dim=text_embed_dim,
|
| 373 |
+
bias=patch_bias,
|
| 374 |
+
sample_width=sample_width,
|
| 375 |
+
sample_height=sample_height,
|
| 376 |
+
sample_frames=sample_frames,
|
| 377 |
+
temporal_compression_ratio=temporal_compression_ratio,
|
| 378 |
+
max_text_seq_length=max_text_seq_length,
|
| 379 |
+
spatial_interpolation_scale=spatial_interpolation_scale,
|
| 380 |
+
temporal_interpolation_scale=temporal_interpolation_scale,
|
| 381 |
+
use_positional_embeddings=not use_rotary_positional_embeddings,
|
| 382 |
+
use_learned_positional_embeddings=use_learned_positional_embeddings,
|
| 383 |
+
device=device, dtype=torch.float32, operations=operations,
|
| 384 |
+
)
|
| 385 |
+
|
| 386 |
+
# 2. Time embedding
|
| 387 |
+
self.time_proj_dim = dim
|
| 388 |
+
self.time_proj_flip = flip_sin_to_cos
|
| 389 |
+
self.time_proj_shift = freq_shift
|
| 390 |
+
self.time_embedding_linear_1 = operations.Linear(dim, time_embed_dim, device=device, dtype=dtype)
|
| 391 |
+
self.time_embedding_act = nn.SiLU()
|
| 392 |
+
self.time_embedding_linear_2 = operations.Linear(time_embed_dim, time_embed_dim, device=device, dtype=dtype)
|
| 393 |
+
|
| 394 |
+
# Optional OFS embedding (CogVideoX 1.5 I2V)
|
| 395 |
+
self.ofs_proj_dim = ofs_embed_dim
|
| 396 |
+
if ofs_embed_dim:
|
| 397 |
+
self.ofs_embedding_linear_1 = operations.Linear(ofs_embed_dim, ofs_embed_dim, device=device, dtype=dtype)
|
| 398 |
+
self.ofs_embedding_act = nn.SiLU()
|
| 399 |
+
self.ofs_embedding_linear_2 = operations.Linear(ofs_embed_dim, ofs_embed_dim, device=device, dtype=dtype)
|
| 400 |
+
else:
|
| 401 |
+
self.ofs_embedding_linear_1 = None
|
| 402 |
+
|
| 403 |
+
# 3. Transformer blocks
|
| 404 |
+
self.blocks = nn.ModuleList([
|
| 405 |
+
CogVideoXBlock(
|
| 406 |
+
dim=dim,
|
| 407 |
+
num_heads=num_attention_heads,
|
| 408 |
+
head_dim=attention_head_dim,
|
| 409 |
+
time_dim=time_embed_dim,
|
| 410 |
+
eps=1e-5,
|
| 411 |
+
device=device, dtype=dtype, operations=operations,
|
| 412 |
+
)
|
| 413 |
+
for _ in range(num_layers)
|
| 414 |
+
])
|
| 415 |
+
|
| 416 |
+
self.norm_final = operations.LayerNorm(dim, eps=1e-5, elementwise_affine=True, device=device, dtype=dtype)
|
| 417 |
+
|
| 418 |
+
# 4. Output
|
| 419 |
+
self.norm_out = CogVideoXAdaLayerNorm(
|
| 420 |
+
time_dim=time_embed_dim, dim=dim, eps=1e-5,
|
| 421 |
+
device=device, dtype=dtype, operations=operations,
|
| 422 |
+
)
|
| 423 |
+
|
| 424 |
+
if patch_size_t is None:
|
| 425 |
+
output_dim = patch_size * patch_size * out_channels
|
| 426 |
+
else:
|
| 427 |
+
output_dim = patch_size * patch_size * patch_size_t * out_channels
|
| 428 |
+
|
| 429 |
+
self.proj_out = operations.Linear(dim, output_dim, device=device, dtype=dtype)
|
| 430 |
+
|
| 431 |
+
self.spatial_interpolation_scale = spatial_interpolation_scale
|
| 432 |
+
self.temporal_interpolation_scale = temporal_interpolation_scale
|
| 433 |
+
self.temporal_compression_ratio = temporal_compression_ratio
|
| 434 |
+
|
| 435 |
+
def forward(self, x, timestep, context, ofs=None, transformer_options=None, **kwargs):
|
| 436 |
+
if transformer_options is None:
|
| 437 |
+
transformer_options = {}
|
| 438 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 439 |
+
self._forward,
|
| 440 |
+
self,
|
| 441 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
|
| 442 |
+
).execute(x, timestep, context, ofs, transformer_options, **kwargs)
|
| 443 |
+
|
| 444 |
+
def _forward(self, x, timestep, context, ofs=None, transformer_options=None, **kwargs):
|
| 445 |
+
if transformer_options is None:
|
| 446 |
+
transformer_options = {}
|
| 447 |
+
# ComfyUI passes [B, C, T, H, W]
|
| 448 |
+
batch_size, channels, t, h, w = x.shape
|
| 449 |
+
|
| 450 |
+
# Pad to patch size (temporal + spatial), same pattern as WAN
|
| 451 |
+
p_t = self.patch_size_t if self.patch_size_t is not None else 1
|
| 452 |
+
x = comfy.ldm.common_dit.pad_to_patch_size(x, (p_t, self.patch_size, self.patch_size))
|
| 453 |
+
|
| 454 |
+
# CogVideoX expects [B, T, C, H, W]
|
| 455 |
+
x = x.permute(0, 2, 1, 3, 4)
|
| 456 |
+
batch_size, num_frames, channels, height, width = x.shape
|
| 457 |
+
|
| 458 |
+
# Time embedding
|
| 459 |
+
t_emb = get_timestep_embedding(timestep, self.time_proj_dim, self.time_proj_flip, self.time_proj_shift)
|
| 460 |
+
t_emb = t_emb.to(dtype=x.dtype)
|
| 461 |
+
emb = self.time_embedding_linear_2(self.time_embedding_act(self.time_embedding_linear_1(t_emb)))
|
| 462 |
+
|
| 463 |
+
if self.ofs_embedding_linear_1 is not None and ofs is not None:
|
| 464 |
+
ofs_emb = get_timestep_embedding(ofs, self.ofs_proj_dim, self.time_proj_flip, self.time_proj_shift)
|
| 465 |
+
ofs_emb = ofs_emb.to(dtype=x.dtype)
|
| 466 |
+
ofs_emb = self.ofs_embedding_linear_2(self.ofs_embedding_act(self.ofs_embedding_linear_1(ofs_emb)))
|
| 467 |
+
emb = emb + ofs_emb
|
| 468 |
+
|
| 469 |
+
# Patch embedding
|
| 470 |
+
hidden_states = self.patch_embed(context, x)
|
| 471 |
+
|
| 472 |
+
text_seq_length = context.shape[1]
|
| 473 |
+
encoder_hidden_states = hidden_states[:, :text_seq_length]
|
| 474 |
+
hidden_states = hidden_states[:, text_seq_length:]
|
| 475 |
+
|
| 476 |
+
# Rotary embeddings (if used)
|
| 477 |
+
image_rotary_emb = None
|
| 478 |
+
if self.use_rotary_positional_embeddings:
|
| 479 |
+
post_patch_height = height // self.patch_size
|
| 480 |
+
post_patch_width = width // self.patch_size
|
| 481 |
+
if self.patch_size_t is None:
|
| 482 |
+
post_time = num_frames
|
| 483 |
+
else:
|
| 484 |
+
post_time = num_frames // self.patch_size_t
|
| 485 |
+
image_rotary_emb = self._get_rotary_emb(post_patch_height, post_patch_width, post_time, device=x.device)
|
| 486 |
+
|
| 487 |
+
# Transformer blocks
|
| 488 |
+
for i, block in enumerate(self.blocks):
|
| 489 |
+
hidden_states, encoder_hidden_states = block(
|
| 490 |
+
hidden_states=hidden_states,
|
| 491 |
+
encoder_hidden_states=encoder_hidden_states,
|
| 492 |
+
temb=emb,
|
| 493 |
+
image_rotary_emb=image_rotary_emb,
|
| 494 |
+
transformer_options=transformer_options,
|
| 495 |
+
)
|
| 496 |
+
|
| 497 |
+
hidden_states = self.norm_final(hidden_states)
|
| 498 |
+
|
| 499 |
+
# Output projection
|
| 500 |
+
hidden_states = self.norm_out(hidden_states, temb=emb)
|
| 501 |
+
hidden_states = self.proj_out(hidden_states)
|
| 502 |
+
|
| 503 |
+
# Unpatchify
|
| 504 |
+
p = self.patch_size
|
| 505 |
+
p_t = self.patch_size_t
|
| 506 |
+
|
| 507 |
+
if p_t is None:
|
| 508 |
+
output = hidden_states.reshape(batch_size, num_frames, height // p, width // p, -1, p, p)
|
| 509 |
+
output = output.permute(0, 1, 4, 2, 5, 3, 6).flatten(5, 6).flatten(3, 4)
|
| 510 |
+
else:
|
| 511 |
+
output = hidden_states.reshape(
|
| 512 |
+
batch_size, (num_frames + p_t - 1) // p_t, height // p, width // p, -1, p_t, p, p
|
| 513 |
+
)
|
| 514 |
+
output = output.permute(0, 1, 5, 4, 2, 6, 3, 7).flatten(6, 7).flatten(4, 5).flatten(1, 2)
|
| 515 |
+
|
| 516 |
+
# Back to ComfyUI format [B, C, T, H, W] and crop padding
|
| 517 |
+
output = output.permute(0, 2, 1, 3, 4)[:, :, :t, :h, :w]
|
| 518 |
+
return output
|
| 519 |
+
|
| 520 |
+
def _get_rotary_emb(self, h, w, t, device):
|
| 521 |
+
"""Compute CogVideoX 3D rotary positional embeddings.
|
| 522 |
+
|
| 523 |
+
For CogVideoX 1.5 (patch_size_t != None): uses "slice" mode — grid positions
|
| 524 |
+
are integer arange computed at max_size, then sliced to actual size.
|
| 525 |
+
For CogVideoX 1.0 (patch_size_t == None): uses "linspace" mode with crop coords
|
| 526 |
+
scaled by spatial_interpolation_scale.
|
| 527 |
+
"""
|
| 528 |
+
d = self.attention_head_dim
|
| 529 |
+
dim_t = d // 4
|
| 530 |
+
dim_h = d // 8 * 3
|
| 531 |
+
dim_w = d // 8 * 3
|
| 532 |
+
|
| 533 |
+
if self.patch_size_t is not None:
|
| 534 |
+
# CogVideoX 1.5: "slice" mode — positions are simple integer indices
|
| 535 |
+
# Compute at max(sample_size, actual_size) then slice to actual
|
| 536 |
+
base_h = self.patch_embed.sample_height // self.patch_size
|
| 537 |
+
base_w = self.patch_embed.sample_width // self.patch_size
|
| 538 |
+
max_h = max(base_h, h)
|
| 539 |
+
max_w = max(base_w, w)
|
| 540 |
+
|
| 541 |
+
grid_h = torch.arange(max_h, device=device, dtype=torch.float32)
|
| 542 |
+
grid_w = torch.arange(max_w, device=device, dtype=torch.float32)
|
| 543 |
+
grid_t = torch.arange(t, device=device, dtype=torch.float32)
|
| 544 |
+
else:
|
| 545 |
+
# CogVideoX 1.0: "linspace" mode with interpolation scale
|
| 546 |
+
grid_h = torch.linspace(0, h - 1, h, device=device, dtype=torch.float32) * self.spatial_interpolation_scale
|
| 547 |
+
grid_w = torch.linspace(0, w - 1, w, device=device, dtype=torch.float32) * self.spatial_interpolation_scale
|
| 548 |
+
grid_t = torch.arange(t, device=device, dtype=torch.float32)
|
| 549 |
+
|
| 550 |
+
freqs_t = _get_1d_rotary_pos_embed(dim_t, grid_t)
|
| 551 |
+
freqs_h = _get_1d_rotary_pos_embed(dim_h, grid_h)
|
| 552 |
+
freqs_w = _get_1d_rotary_pos_embed(dim_w, grid_w)
|
| 553 |
+
|
| 554 |
+
t_cos, t_sin = freqs_t
|
| 555 |
+
h_cos, h_sin = freqs_h
|
| 556 |
+
w_cos, w_sin = freqs_w
|
| 557 |
+
|
| 558 |
+
# Slice to actual size (for "slice" mode where grids may be larger)
|
| 559 |
+
t_cos, t_sin = t_cos[:t], t_sin[:t]
|
| 560 |
+
h_cos, h_sin = h_cos[:h], h_sin[:h]
|
| 561 |
+
w_cos, w_sin = w_cos[:w], w_sin[:w]
|
| 562 |
+
|
| 563 |
+
# Broadcast and concatenate into [T*H*W, head_dim]
|
| 564 |
+
t_cos = t_cos[:, None, None, :].expand(-1, h, w, -1)
|
| 565 |
+
t_sin = t_sin[:, None, None, :].expand(-1, h, w, -1)
|
| 566 |
+
h_cos = h_cos[None, :, None, :].expand(t, -1, w, -1)
|
| 567 |
+
h_sin = h_sin[None, :, None, :].expand(t, -1, w, -1)
|
| 568 |
+
w_cos = w_cos[None, None, :, :].expand(t, h, -1, -1)
|
| 569 |
+
w_sin = w_sin[None, None, :, :].expand(t, h, -1, -1)
|
| 570 |
+
|
| 571 |
+
cos = torch.cat([t_cos, h_cos, w_cos], dim=-1).reshape(t * h * w, -1)
|
| 572 |
+
sin = torch.cat([t_sin, h_sin, w_sin], dim=-1).reshape(t * h * w, -1)
|
| 573 |
+
return (cos, sin)
|
ComfyUI/comfy/ldm/cogvideo/vae.py
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CogVideoX VAE - ported to ComfyUI native ops
|
| 2 |
+
# Architecture reference: diffusers AutoencoderKLCogVideoX
|
| 3 |
+
# Style reference: comfy/ldm/wan/vae.py
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
|
| 11 |
+
import comfy.ops
|
| 12 |
+
ops = comfy.ops.disable_weight_init
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class CausalConv3d(nn.Module):
|
| 16 |
+
"""Causal 3D convolution with temporal padding.
|
| 17 |
+
|
| 18 |
+
Uses comfy.ops.Conv3d with autopad='causal_zero' fast path: when input has
|
| 19 |
+
a single temporal frame and no cache, the 3D conv weight is sliced to act
|
| 20 |
+
as a 2D conv, avoiding computation on zero-padded temporal dimensions.
|
| 21 |
+
"""
|
| 22 |
+
def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1, pad_mode="constant"):
|
| 23 |
+
super().__init__()
|
| 24 |
+
if isinstance(kernel_size, int):
|
| 25 |
+
kernel_size = (kernel_size,) * 3
|
| 26 |
+
|
| 27 |
+
time_kernel, height_kernel, width_kernel = kernel_size
|
| 28 |
+
self.time_kernel_size = time_kernel
|
| 29 |
+
self.pad_mode = pad_mode
|
| 30 |
+
|
| 31 |
+
height_pad = (height_kernel - 1) // 2
|
| 32 |
+
width_pad = (width_kernel - 1) // 2
|
| 33 |
+
self.time_causal_padding = (width_pad, width_pad, height_pad, height_pad, time_kernel - 1, 0)
|
| 34 |
+
|
| 35 |
+
stride = stride if isinstance(stride, tuple) else (stride, 1, 1)
|
| 36 |
+
dilation = (dilation, 1, 1)
|
| 37 |
+
self.conv = ops.Conv3d(
|
| 38 |
+
in_channels, out_channels, kernel_size,
|
| 39 |
+
stride=stride, dilation=dilation,
|
| 40 |
+
padding=(0, height_pad, width_pad),
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
def forward(self, x, conv_cache=None):
|
| 44 |
+
if self.pad_mode == "replicate":
|
| 45 |
+
x = F.pad(x, self.time_causal_padding, mode="replicate")
|
| 46 |
+
conv_cache = None
|
| 47 |
+
else:
|
| 48 |
+
kernel_t = self.time_kernel_size
|
| 49 |
+
if kernel_t > 1:
|
| 50 |
+
if conv_cache is None and x.shape[2] == 1:
|
| 51 |
+
# Fast path: single frame, no cache. All temporal padding
|
| 52 |
+
# frames are copies of the input (replicate-style), so the
|
| 53 |
+
# 3D conv reduces to a 2D conv with summed temporal kernel.
|
| 54 |
+
w = comfy.ops.cast_to_input(self.conv.weight, x)
|
| 55 |
+
b = comfy.ops.cast_to_input(self.conv.bias, x) if self.conv.bias is not None else None
|
| 56 |
+
w2d = w.sum(dim=2, keepdim=True)
|
| 57 |
+
out = F.conv3d(x, w2d, b,
|
| 58 |
+
self.conv.stride, self.conv.padding,
|
| 59 |
+
self.conv.dilation, self.conv.groups)
|
| 60 |
+
return out, None
|
| 61 |
+
cached = [conv_cache] if conv_cache is not None else [x[:, :, :1]] * (kernel_t - 1)
|
| 62 |
+
x = torch.cat(cached + [x], dim=2)
|
| 63 |
+
conv_cache = x[:, :, -self.time_kernel_size + 1:].clone() if self.time_kernel_size > 1 else None
|
| 64 |
+
|
| 65 |
+
out = self.conv(x)
|
| 66 |
+
return out, conv_cache
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def _interpolate_zq(zq, target_size):
|
| 70 |
+
"""Interpolate latent z to target (T, H, W), matching CogVideoX's first-frame-special handling."""
|
| 71 |
+
t = target_size[0]
|
| 72 |
+
if t > 1 and t % 2 == 1:
|
| 73 |
+
z_first = F.interpolate(zq[:, :, :1], size=(1, target_size[1], target_size[2]))
|
| 74 |
+
z_rest = F.interpolate(zq[:, :, 1:], size=(t - 1, target_size[1], target_size[2]))
|
| 75 |
+
return torch.cat([z_first, z_rest], dim=2)
|
| 76 |
+
return F.interpolate(zq, size=target_size)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class SpatialNorm3D(nn.Module):
|
| 80 |
+
"""Spatially conditioned normalization."""
|
| 81 |
+
def __init__(self, f_channels, zq_channels, groups=32):
|
| 82 |
+
super().__init__()
|
| 83 |
+
self.norm_layer = ops.GroupNorm(num_channels=f_channels, num_groups=groups, eps=1e-6, affine=True)
|
| 84 |
+
self.conv_y = CausalConv3d(zq_channels, f_channels, kernel_size=1, stride=1)
|
| 85 |
+
self.conv_b = CausalConv3d(zq_channels, f_channels, kernel_size=1, stride=1)
|
| 86 |
+
|
| 87 |
+
def forward(self, f, zq, conv_cache=None):
|
| 88 |
+
new_cache = {}
|
| 89 |
+
conv_cache = conv_cache or {}
|
| 90 |
+
|
| 91 |
+
if zq.shape[-3:] != f.shape[-3:]:
|
| 92 |
+
zq = _interpolate_zq(zq, f.shape[-3:])
|
| 93 |
+
|
| 94 |
+
conv_y, new_cache["conv_y"] = self.conv_y(zq, conv_cache=conv_cache.get("conv_y"))
|
| 95 |
+
conv_b, new_cache["conv_b"] = self.conv_b(zq, conv_cache=conv_cache.get("conv_b"))
|
| 96 |
+
|
| 97 |
+
return self.norm_layer(f) * conv_y + conv_b, new_cache
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
class ResnetBlock3D(nn.Module):
|
| 101 |
+
"""3D ResNet block with optional spatial norm."""
|
| 102 |
+
def __init__(self, in_channels, out_channels=None, temb_channels=512, groups=32,
|
| 103 |
+
eps=1e-6, act_fn="silu", spatial_norm_dim=None, pad_mode="first"):
|
| 104 |
+
super().__init__()
|
| 105 |
+
out_channels = out_channels or in_channels
|
| 106 |
+
self.in_channels = in_channels
|
| 107 |
+
self.out_channels = out_channels
|
| 108 |
+
self.spatial_norm_dim = spatial_norm_dim
|
| 109 |
+
|
| 110 |
+
if act_fn == "silu":
|
| 111 |
+
self.nonlinearity = nn.SiLU()
|
| 112 |
+
elif act_fn == "swish":
|
| 113 |
+
self.nonlinearity = nn.SiLU()
|
| 114 |
+
else:
|
| 115 |
+
self.nonlinearity = nn.SiLU()
|
| 116 |
+
|
| 117 |
+
if spatial_norm_dim is None:
|
| 118 |
+
self.norm1 = ops.GroupNorm(num_channels=in_channels, num_groups=groups, eps=eps)
|
| 119 |
+
self.norm2 = ops.GroupNorm(num_channels=out_channels, num_groups=groups, eps=eps)
|
| 120 |
+
else:
|
| 121 |
+
self.norm1 = SpatialNorm3D(in_channels, spatial_norm_dim, groups=groups)
|
| 122 |
+
self.norm2 = SpatialNorm3D(out_channels, spatial_norm_dim, groups=groups)
|
| 123 |
+
|
| 124 |
+
self.conv1 = CausalConv3d(in_channels, out_channels, kernel_size=3, pad_mode=pad_mode)
|
| 125 |
+
|
| 126 |
+
if temb_channels > 0:
|
| 127 |
+
self.temb_proj = ops.Linear(temb_channels, out_channels)
|
| 128 |
+
|
| 129 |
+
self.conv2 = CausalConv3d(out_channels, out_channels, kernel_size=3, pad_mode=pad_mode)
|
| 130 |
+
|
| 131 |
+
if in_channels != out_channels:
|
| 132 |
+
self.conv_shortcut = ops.Conv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
|
| 133 |
+
else:
|
| 134 |
+
self.conv_shortcut = None
|
| 135 |
+
|
| 136 |
+
def forward(self, x, temb=None, zq=None, conv_cache=None):
|
| 137 |
+
new_cache = {}
|
| 138 |
+
conv_cache = conv_cache or {}
|
| 139 |
+
residual = x
|
| 140 |
+
|
| 141 |
+
if zq is not None:
|
| 142 |
+
x, new_cache["norm1"] = self.norm1(x, zq, conv_cache=conv_cache.get("norm1"))
|
| 143 |
+
else:
|
| 144 |
+
x = self.norm1(x)
|
| 145 |
+
|
| 146 |
+
x = self.nonlinearity(x)
|
| 147 |
+
x, new_cache["conv1"] = self.conv1(x, conv_cache=conv_cache.get("conv1"))
|
| 148 |
+
|
| 149 |
+
if temb is not None and hasattr(self, "temb_proj"):
|
| 150 |
+
x = x + self.temb_proj(self.nonlinearity(temb))[:, :, None, None, None]
|
| 151 |
+
|
| 152 |
+
if zq is not None:
|
| 153 |
+
x, new_cache["norm2"] = self.norm2(x, zq, conv_cache=conv_cache.get("norm2"))
|
| 154 |
+
else:
|
| 155 |
+
x = self.norm2(x)
|
| 156 |
+
|
| 157 |
+
x = self.nonlinearity(x)
|
| 158 |
+
x, new_cache["conv2"] = self.conv2(x, conv_cache=conv_cache.get("conv2"))
|
| 159 |
+
|
| 160 |
+
if self.conv_shortcut is not None:
|
| 161 |
+
residual = self.conv_shortcut(residual)
|
| 162 |
+
|
| 163 |
+
return x + residual, new_cache
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class Downsample3D(nn.Module):
|
| 167 |
+
"""3D downsampling with optional temporal compression."""
|
| 168 |
+
def __init__(self, in_channels, out_channels, kernel_size=3, stride=2, padding=0, compress_time=False):
|
| 169 |
+
super().__init__()
|
| 170 |
+
self.conv = ops.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
|
| 171 |
+
self.compress_time = compress_time
|
| 172 |
+
|
| 173 |
+
def forward(self, x):
|
| 174 |
+
if self.compress_time:
|
| 175 |
+
b, c, t, h, w = x.shape
|
| 176 |
+
x = x.permute(0, 3, 4, 1, 2).reshape(b * h * w, c, t)
|
| 177 |
+
if t % 2 == 1:
|
| 178 |
+
x_first, x_rest = x[..., 0], x[..., 1:]
|
| 179 |
+
if x_rest.shape[-1] > 0:
|
| 180 |
+
x_rest = F.avg_pool1d(x_rest, kernel_size=2, stride=2)
|
| 181 |
+
x = torch.cat([x_first[..., None], x_rest], dim=-1)
|
| 182 |
+
x = x.reshape(b, h, w, c, x.shape[-1]).permute(0, 3, 4, 1, 2)
|
| 183 |
+
else:
|
| 184 |
+
x = F.avg_pool1d(x, kernel_size=2, stride=2)
|
| 185 |
+
x = x.reshape(b, h, w, c, x.shape[-1]).permute(0, 3, 4, 1, 2)
|
| 186 |
+
|
| 187 |
+
pad = (0, 1, 0, 1)
|
| 188 |
+
x = F.pad(x, pad, mode="constant", value=0)
|
| 189 |
+
b, c, t, h, w = x.shape
|
| 190 |
+
x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
|
| 191 |
+
x = self.conv(x)
|
| 192 |
+
x = x.reshape(b, t, x.shape[1], x.shape[2], x.shape[3]).permute(0, 2, 1, 3, 4)
|
| 193 |
+
return x
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
class Upsample3D(nn.Module):
|
| 197 |
+
"""3D upsampling with optional temporal decompression."""
|
| 198 |
+
def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, compress_time=False):
|
| 199 |
+
super().__init__()
|
| 200 |
+
self.conv = ops.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
|
| 201 |
+
self.compress_time = compress_time
|
| 202 |
+
|
| 203 |
+
def forward(self, x):
|
| 204 |
+
if self.compress_time:
|
| 205 |
+
if x.shape[2] > 1 and x.shape[2] % 2 == 1:
|
| 206 |
+
x_first, x_rest = x[:, :, 0], x[:, :, 1:]
|
| 207 |
+
x_first = F.interpolate(x_first, scale_factor=2.0)
|
| 208 |
+
x_rest = F.interpolate(x_rest, scale_factor=2.0)
|
| 209 |
+
x = torch.cat([x_first[:, :, None, :, :], x_rest], dim=2)
|
| 210 |
+
elif x.shape[2] > 1:
|
| 211 |
+
x = F.interpolate(x, scale_factor=2.0)
|
| 212 |
+
else:
|
| 213 |
+
x = x.squeeze(2)
|
| 214 |
+
x = F.interpolate(x, scale_factor=2.0)
|
| 215 |
+
x = x[:, :, None, :, :]
|
| 216 |
+
else:
|
| 217 |
+
b, c, t, h, w = x.shape
|
| 218 |
+
x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
|
| 219 |
+
x = F.interpolate(x, scale_factor=2.0)
|
| 220 |
+
x = x.reshape(b, t, c, *x.shape[2:]).permute(0, 2, 1, 3, 4)
|
| 221 |
+
|
| 222 |
+
b, c, t, h, w = x.shape
|
| 223 |
+
x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
|
| 224 |
+
x = self.conv(x)
|
| 225 |
+
x = x.reshape(b, t, *x.shape[1:]).permute(0, 2, 1, 3, 4)
|
| 226 |
+
return x
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
class DownBlock3D(nn.Module):
|
| 230 |
+
def __init__(self, in_channels, out_channels, temb_channels=0, num_layers=1,
|
| 231 |
+
eps=1e-6, act_fn="silu", groups=32, add_downsample=True,
|
| 232 |
+
compress_time=False, pad_mode="first"):
|
| 233 |
+
super().__init__()
|
| 234 |
+
self.resnets = nn.ModuleList([
|
| 235 |
+
ResnetBlock3D(
|
| 236 |
+
in_channels=in_channels if i == 0 else out_channels,
|
| 237 |
+
out_channels=out_channels,
|
| 238 |
+
temb_channels=temb_channels,
|
| 239 |
+
groups=groups, eps=eps, act_fn=act_fn, pad_mode=pad_mode,
|
| 240 |
+
)
|
| 241 |
+
for i in range(num_layers)
|
| 242 |
+
])
|
| 243 |
+
self.downsamplers = nn.ModuleList([Downsample3D(out_channels, out_channels, compress_time=compress_time)]) if add_downsample else None
|
| 244 |
+
|
| 245 |
+
def forward(self, x, temb=None, zq=None, conv_cache=None):
|
| 246 |
+
new_cache = {}
|
| 247 |
+
conv_cache = conv_cache or {}
|
| 248 |
+
for i, resnet in enumerate(self.resnets):
|
| 249 |
+
x, new_cache[f"resnet_{i}"] = resnet(x, temb, zq, conv_cache=conv_cache.get(f"resnet_{i}"))
|
| 250 |
+
if self.downsamplers is not None:
|
| 251 |
+
for ds in self.downsamplers:
|
| 252 |
+
x = ds(x)
|
| 253 |
+
return x, new_cache
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
class MidBlock3D(nn.Module):
|
| 257 |
+
def __init__(self, in_channels, temb_channels=0, num_layers=1,
|
| 258 |
+
eps=1e-6, act_fn="silu", groups=32, spatial_norm_dim=None, pad_mode="first"):
|
| 259 |
+
super().__init__()
|
| 260 |
+
self.resnets = nn.ModuleList([
|
| 261 |
+
ResnetBlock3D(
|
| 262 |
+
in_channels=in_channels, out_channels=in_channels,
|
| 263 |
+
temb_channels=temb_channels, groups=groups, eps=eps,
|
| 264 |
+
act_fn=act_fn, spatial_norm_dim=spatial_norm_dim, pad_mode=pad_mode,
|
| 265 |
+
)
|
| 266 |
+
for _ in range(num_layers)
|
| 267 |
+
])
|
| 268 |
+
|
| 269 |
+
def forward(self, x, temb=None, zq=None, conv_cache=None):
|
| 270 |
+
new_cache = {}
|
| 271 |
+
conv_cache = conv_cache or {}
|
| 272 |
+
for i, resnet in enumerate(self.resnets):
|
| 273 |
+
x, new_cache[f"resnet_{i}"] = resnet(x, temb, zq, conv_cache=conv_cache.get(f"resnet_{i}"))
|
| 274 |
+
return x, new_cache
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
class UpBlock3D(nn.Module):
|
| 278 |
+
def __init__(self, in_channels, out_channels, temb_channels=0, num_layers=1,
|
| 279 |
+
eps=1e-6, act_fn="silu", groups=32, spatial_norm_dim=16,
|
| 280 |
+
add_upsample=True, compress_time=False, pad_mode="first"):
|
| 281 |
+
super().__init__()
|
| 282 |
+
self.resnets = nn.ModuleList([
|
| 283 |
+
ResnetBlock3D(
|
| 284 |
+
in_channels=in_channels if i == 0 else out_channels,
|
| 285 |
+
out_channels=out_channels,
|
| 286 |
+
temb_channels=temb_channels, groups=groups, eps=eps,
|
| 287 |
+
act_fn=act_fn, spatial_norm_dim=spatial_norm_dim, pad_mode=pad_mode,
|
| 288 |
+
)
|
| 289 |
+
for i in range(num_layers)
|
| 290 |
+
])
|
| 291 |
+
self.upsamplers = nn.ModuleList([Upsample3D(out_channels, out_channels, compress_time=compress_time)]) if add_upsample else None
|
| 292 |
+
|
| 293 |
+
def forward(self, x, temb=None, zq=None, conv_cache=None):
|
| 294 |
+
new_cache = {}
|
| 295 |
+
conv_cache = conv_cache or {}
|
| 296 |
+
for i, resnet in enumerate(self.resnets):
|
| 297 |
+
x, new_cache[f"resnet_{i}"] = resnet(x, temb, zq, conv_cache=conv_cache.get(f"resnet_{i}"))
|
| 298 |
+
if self.upsamplers is not None:
|
| 299 |
+
for us in self.upsamplers:
|
| 300 |
+
x = us(x)
|
| 301 |
+
return x, new_cache
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
class Encoder3D(nn.Module):
|
| 305 |
+
def __init__(self, in_channels=3, out_channels=16,
|
| 306 |
+
block_out_channels=(128, 256, 256, 512),
|
| 307 |
+
layers_per_block=3, act_fn="silu",
|
| 308 |
+
eps=1e-6, groups=32, pad_mode="first",
|
| 309 |
+
temporal_compression_ratio=4):
|
| 310 |
+
super().__init__()
|
| 311 |
+
temporal_compress_level = int(np.log2(temporal_compression_ratio))
|
| 312 |
+
|
| 313 |
+
self.conv_in = CausalConv3d(in_channels, block_out_channels[0], kernel_size=3, pad_mode=pad_mode)
|
| 314 |
+
|
| 315 |
+
self.down_blocks = nn.ModuleList()
|
| 316 |
+
output_channel = block_out_channels[0]
|
| 317 |
+
for i in range(len(block_out_channels)):
|
| 318 |
+
input_channel = output_channel
|
| 319 |
+
output_channel = block_out_channels[i]
|
| 320 |
+
is_final = i == len(block_out_channels) - 1
|
| 321 |
+
compress_time = i < temporal_compress_level
|
| 322 |
+
|
| 323 |
+
self.down_blocks.append(DownBlock3D(
|
| 324 |
+
in_channels=input_channel, out_channels=output_channel,
|
| 325 |
+
temb_channels=0, num_layers=layers_per_block,
|
| 326 |
+
eps=eps, act_fn=act_fn, groups=groups,
|
| 327 |
+
add_downsample=not is_final, compress_time=compress_time,
|
| 328 |
+
))
|
| 329 |
+
|
| 330 |
+
self.mid_block = MidBlock3D(
|
| 331 |
+
in_channels=block_out_channels[-1], temb_channels=0,
|
| 332 |
+
num_layers=2, eps=eps, act_fn=act_fn, groups=groups, pad_mode=pad_mode,
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
self.norm_out = ops.GroupNorm(groups, block_out_channels[-1], eps=1e-6)
|
| 336 |
+
self.conv_act = nn.SiLU()
|
| 337 |
+
self.conv_out = CausalConv3d(block_out_channels[-1], 2 * out_channels, kernel_size=3, pad_mode=pad_mode)
|
| 338 |
+
|
| 339 |
+
def forward(self, x, conv_cache=None):
|
| 340 |
+
new_cache = {}
|
| 341 |
+
conv_cache = conv_cache or {}
|
| 342 |
+
|
| 343 |
+
x, new_cache["conv_in"] = self.conv_in(x, conv_cache=conv_cache.get("conv_in"))
|
| 344 |
+
|
| 345 |
+
for i, block in enumerate(self.down_blocks):
|
| 346 |
+
key = f"down_block_{i}"
|
| 347 |
+
x, new_cache[key] = block(x, None, None, conv_cache.get(key))
|
| 348 |
+
|
| 349 |
+
x, new_cache["mid_block"] = self.mid_block(x, None, None, conv_cache=conv_cache.get("mid_block"))
|
| 350 |
+
|
| 351 |
+
x = self.norm_out(x)
|
| 352 |
+
x = self.conv_act(x)
|
| 353 |
+
x, new_cache["conv_out"] = self.conv_out(x, conv_cache=conv_cache.get("conv_out"))
|
| 354 |
+
|
| 355 |
+
return x, new_cache
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
class Decoder3D(nn.Module):
|
| 359 |
+
def __init__(self, in_channels=16, out_channels=3,
|
| 360 |
+
block_out_channels=(128, 256, 256, 512),
|
| 361 |
+
layers_per_block=3, act_fn="silu",
|
| 362 |
+
eps=1e-6, groups=32, pad_mode="first",
|
| 363 |
+
temporal_compression_ratio=4):
|
| 364 |
+
super().__init__()
|
| 365 |
+
reversed_channels = list(reversed(block_out_channels))
|
| 366 |
+
temporal_compress_level = int(np.log2(temporal_compression_ratio))
|
| 367 |
+
|
| 368 |
+
self.conv_in = CausalConv3d(in_channels, reversed_channels[0], kernel_size=3, pad_mode=pad_mode)
|
| 369 |
+
|
| 370 |
+
self.mid_block = MidBlock3D(
|
| 371 |
+
in_channels=reversed_channels[0], temb_channels=0,
|
| 372 |
+
num_layers=2, eps=eps, act_fn=act_fn, groups=groups,
|
| 373 |
+
spatial_norm_dim=in_channels, pad_mode=pad_mode,
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
+
self.up_blocks = nn.ModuleList()
|
| 377 |
+
output_channel = reversed_channels[0]
|
| 378 |
+
for i in range(len(block_out_channels)):
|
| 379 |
+
prev_channel = output_channel
|
| 380 |
+
output_channel = reversed_channels[i]
|
| 381 |
+
is_final = i == len(block_out_channels) - 1
|
| 382 |
+
compress_time = i < temporal_compress_level
|
| 383 |
+
|
| 384 |
+
self.up_blocks.append(UpBlock3D(
|
| 385 |
+
in_channels=prev_channel, out_channels=output_channel,
|
| 386 |
+
temb_channels=0, num_layers=layers_per_block + 1,
|
| 387 |
+
eps=eps, act_fn=act_fn, groups=groups,
|
| 388 |
+
spatial_norm_dim=in_channels,
|
| 389 |
+
add_upsample=not is_final, compress_time=compress_time,
|
| 390 |
+
))
|
| 391 |
+
|
| 392 |
+
self.norm_out = SpatialNorm3D(reversed_channels[-1], in_channels, groups=groups)
|
| 393 |
+
self.conv_act = nn.SiLU()
|
| 394 |
+
self.conv_out = CausalConv3d(reversed_channels[-1], out_channels, kernel_size=3, pad_mode=pad_mode)
|
| 395 |
+
|
| 396 |
+
def forward(self, sample, conv_cache=None):
|
| 397 |
+
new_cache = {}
|
| 398 |
+
conv_cache = conv_cache or {}
|
| 399 |
+
|
| 400 |
+
x, new_cache["conv_in"] = self.conv_in(sample, conv_cache=conv_cache.get("conv_in"))
|
| 401 |
+
|
| 402 |
+
x, new_cache["mid_block"] = self.mid_block(x, None, sample, conv_cache=conv_cache.get("mid_block"))
|
| 403 |
+
|
| 404 |
+
for i, block in enumerate(self.up_blocks):
|
| 405 |
+
key = f"up_block_{i}"
|
| 406 |
+
x, new_cache[key] = block(x, None, sample, conv_cache=conv_cache.get(key))
|
| 407 |
+
|
| 408 |
+
x, new_cache["norm_out"] = self.norm_out(x, sample, conv_cache=conv_cache.get("norm_out"))
|
| 409 |
+
x = self.conv_act(x)
|
| 410 |
+
x, new_cache["conv_out"] = self.conv_out(x, conv_cache=conv_cache.get("conv_out"))
|
| 411 |
+
|
| 412 |
+
return x, new_cache
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
class AutoencoderKLCogVideoX(nn.Module):
|
| 417 |
+
"""CogVideoX VAE. Spatial tiling/slicing handled by ComfyUI's VAE wrapper.
|
| 418 |
+
|
| 419 |
+
Uses rolling temporal decode: conv_in + mid_block + temporal up_blocks run
|
| 420 |
+
on the full (low-res) tensor, then the expensive spatial-only up_blocks +
|
| 421 |
+
norm_out + conv_out are processed in small temporal chunks with conv_cache
|
| 422 |
+
carrying causal state between chunks. This keeps peak VRAM proportional to
|
| 423 |
+
chunk_size rather than total frame count.
|
| 424 |
+
"""
|
| 425 |
+
|
| 426 |
+
def __init__(self,
|
| 427 |
+
in_channels=3, out_channels=3,
|
| 428 |
+
block_out_channels=(128, 256, 256, 512),
|
| 429 |
+
latent_channels=16, layers_per_block=3,
|
| 430 |
+
act_fn="silu", eps=1e-6, groups=32,
|
| 431 |
+
temporal_compression_ratio=4,
|
| 432 |
+
):
|
| 433 |
+
super().__init__()
|
| 434 |
+
self.latent_channels = latent_channels
|
| 435 |
+
self.temporal_compression_ratio = temporal_compression_ratio
|
| 436 |
+
|
| 437 |
+
self.encoder = Encoder3D(
|
| 438 |
+
in_channels=in_channels, out_channels=latent_channels,
|
| 439 |
+
block_out_channels=block_out_channels, layers_per_block=layers_per_block,
|
| 440 |
+
act_fn=act_fn, eps=eps, groups=groups,
|
| 441 |
+
temporal_compression_ratio=temporal_compression_ratio,
|
| 442 |
+
)
|
| 443 |
+
self.decoder = Decoder3D(
|
| 444 |
+
in_channels=latent_channels, out_channels=out_channels,
|
| 445 |
+
block_out_channels=block_out_channels, layers_per_block=layers_per_block,
|
| 446 |
+
act_fn=act_fn, eps=eps, groups=groups,
|
| 447 |
+
temporal_compression_ratio=temporal_compression_ratio,
|
| 448 |
+
)
|
| 449 |
+
|
| 450 |
+
self.num_latent_frames_batch_size = 2
|
| 451 |
+
self.num_sample_frames_batch_size = 8
|
| 452 |
+
|
| 453 |
+
def encode(self, x):
|
| 454 |
+
t = x.shape[2]
|
| 455 |
+
frame_batch = self.num_sample_frames_batch_size
|
| 456 |
+
remainder = t % frame_batch
|
| 457 |
+
conv_cache = None
|
| 458 |
+
enc = []
|
| 459 |
+
|
| 460 |
+
# Process remainder frames first so only the first chunk can have an
|
| 461 |
+
# odd temporal dimension — where Downsample3D's first-frame-special
|
| 462 |
+
# handling in temporal compression is actually correct.
|
| 463 |
+
if remainder > 0:
|
| 464 |
+
chunk, conv_cache = self.encoder(x[:, :, :remainder], conv_cache=conv_cache)
|
| 465 |
+
enc.append(chunk.to(x.device))
|
| 466 |
+
|
| 467 |
+
for start in range(remainder, t, frame_batch):
|
| 468 |
+
chunk, conv_cache = self.encoder(x[:, :, start:start + frame_batch], conv_cache=conv_cache)
|
| 469 |
+
enc.append(chunk.to(x.device))
|
| 470 |
+
|
| 471 |
+
enc = torch.cat(enc, dim=2)
|
| 472 |
+
mean, _ = enc.chunk(2, dim=1)
|
| 473 |
+
return mean
|
| 474 |
+
|
| 475 |
+
def decode(self, z):
|
| 476 |
+
return self._decode_rolling(z)
|
| 477 |
+
|
| 478 |
+
def _decode_batched(self, z):
|
| 479 |
+
"""Original batched decode - processes 2 latent frames through full decoder."""
|
| 480 |
+
t = z.shape[2]
|
| 481 |
+
frame_batch = self.num_latent_frames_batch_size
|
| 482 |
+
num_batches = max(t // frame_batch, 1)
|
| 483 |
+
conv_cache = None
|
| 484 |
+
dec = []
|
| 485 |
+
for i in range(num_batches):
|
| 486 |
+
remaining = t % frame_batch
|
| 487 |
+
start = frame_batch * i + (0 if i == 0 else remaining)
|
| 488 |
+
end = frame_batch * (i + 1) + remaining
|
| 489 |
+
chunk, conv_cache = self.decoder(z[:, :, start:end], conv_cache=conv_cache)
|
| 490 |
+
dec.append(chunk.cpu())
|
| 491 |
+
return torch.cat(dec, dim=2).to(z.device)
|
| 492 |
+
|
| 493 |
+
def _decode_rolling(self, z):
|
| 494 |
+
"""Rolling decode - processes low-res layers on full tensor, then rolls
|
| 495 |
+
through expensive high-res layers in temporal chunks."""
|
| 496 |
+
decoder = self.decoder
|
| 497 |
+
device = z.device
|
| 498 |
+
|
| 499 |
+
# Determine which up_blocks have temporal upsample vs spatial-only.
|
| 500 |
+
# Temporal up_blocks are cheap (low res), spatial-only are expensive.
|
| 501 |
+
temporal_compress_level = int(np.log2(self.temporal_compression_ratio))
|
| 502 |
+
split_at = temporal_compress_level # first N up_blocks do temporal upsample
|
| 503 |
+
|
| 504 |
+
# Phase 1: conv_in + mid_block + temporal up_blocks on full tensor (low/medium res)
|
| 505 |
+
x, _ = decoder.conv_in(z)
|
| 506 |
+
x, _ = decoder.mid_block(x, None, z)
|
| 507 |
+
|
| 508 |
+
for i in range(split_at):
|
| 509 |
+
x, _ = decoder.up_blocks[i](x, None, z)
|
| 510 |
+
|
| 511 |
+
# Phase 2: remaining spatial-only up_blocks + norm_out + conv_out in temporal chunks
|
| 512 |
+
remaining_blocks = list(range(split_at, len(decoder.up_blocks)))
|
| 513 |
+
chunk_size = 4 # pixel frames per chunk through high-res layers
|
| 514 |
+
t_expanded = x.shape[2]
|
| 515 |
+
|
| 516 |
+
if t_expanded <= chunk_size or len(remaining_blocks) == 0:
|
| 517 |
+
# Small enough to process in one go
|
| 518 |
+
for i in remaining_blocks:
|
| 519 |
+
x, _ = decoder.up_blocks[i](x, None, z)
|
| 520 |
+
x, _ = decoder.norm_out(x, z)
|
| 521 |
+
x = decoder.conv_act(x)
|
| 522 |
+
x, _ = decoder.conv_out(x)
|
| 523 |
+
return x
|
| 524 |
+
|
| 525 |
+
# Expand z temporally once to match Phase 2's time dimension.
|
| 526 |
+
# z stays at latent spatial resolution so this is small (~16 MB vs ~1.3 GB
|
| 527 |
+
# for the old approach of pre-interpolating to every pixel resolution).
|
| 528 |
+
z_time_expanded = _interpolate_zq(z, (t_expanded, z.shape[3], z.shape[4]))
|
| 529 |
+
|
| 530 |
+
# Process in temporal chunks, interpolating spatially per-chunk to avoid
|
| 531 |
+
# allocating full [B, C, t_expanded, H, W] tensors at each resolution.
|
| 532 |
+
dec_out = []
|
| 533 |
+
conv_caches = {}
|
| 534 |
+
|
| 535 |
+
for chunk_start in range(0, t_expanded, chunk_size):
|
| 536 |
+
chunk_end = min(chunk_start + chunk_size, t_expanded)
|
| 537 |
+
x_chunk = x[:, :, chunk_start:chunk_end]
|
| 538 |
+
z_t_chunk = z_time_expanded[:, :, chunk_start:chunk_end]
|
| 539 |
+
z_spatial_cache = {}
|
| 540 |
+
|
| 541 |
+
for i in remaining_blocks:
|
| 542 |
+
block = decoder.up_blocks[i]
|
| 543 |
+
cache_key = f"up_block_{i}"
|
| 544 |
+
hw_key = (x_chunk.shape[3], x_chunk.shape[4])
|
| 545 |
+
if hw_key not in z_spatial_cache:
|
| 546 |
+
if z_t_chunk.shape[3] == hw_key[0] and z_t_chunk.shape[4] == hw_key[1]:
|
| 547 |
+
z_spatial_cache[hw_key] = z_t_chunk
|
| 548 |
+
else:
|
| 549 |
+
z_spatial_cache[hw_key] = F.interpolate(z_t_chunk, size=(z_t_chunk.shape[2], hw_key[0], hw_key[1]))
|
| 550 |
+
x_chunk, new_cache = block(x_chunk, None, z_spatial_cache[hw_key], conv_cache=conv_caches.get(cache_key))
|
| 551 |
+
conv_caches[cache_key] = new_cache
|
| 552 |
+
|
| 553 |
+
hw_key = (x_chunk.shape[3], x_chunk.shape[4])
|
| 554 |
+
if hw_key not in z_spatial_cache:
|
| 555 |
+
z_spatial_cache[hw_key] = F.interpolate(z_t_chunk, size=(z_t_chunk.shape[2], hw_key[0], hw_key[1]))
|
| 556 |
+
x_chunk, new_cache = decoder.norm_out(x_chunk, z_spatial_cache[hw_key], conv_cache=conv_caches.get("norm_out"))
|
| 557 |
+
conv_caches["norm_out"] = new_cache
|
| 558 |
+
x_chunk = decoder.conv_act(x_chunk)
|
| 559 |
+
x_chunk, new_cache = decoder.conv_out(x_chunk, conv_cache=conv_caches.get("conv_out"))
|
| 560 |
+
conv_caches["conv_out"] = new_cache
|
| 561 |
+
|
| 562 |
+
dec_out.append(x_chunk.cpu())
|
| 563 |
+
del z_spatial_cache
|
| 564 |
+
|
| 565 |
+
del x, z_time_expanded
|
| 566 |
+
return torch.cat(dec_out, dim=2).to(device)
|
ComfyUI/comfy/ldm/colormap.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Colormap utilities for depth and geometry visualisation."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def turbo(x: torch.Tensor) -> torch.Tensor:
|
| 9 |
+
"""Anton Mikhailov polynomial approximation of the Turbo colormap.
|
| 10 |
+
|
| 11 |
+
Args:
|
| 12 |
+
x: Float tensor with values in [0, 1].
|
| 13 |
+
|
| 14 |
+
Returns:
|
| 15 |
+
RGB tensor of the same shape as ``x`` with a trailing size-3 dimension.
|
| 16 |
+
"""
|
| 17 |
+
x = x.clamp(0.0, 1.0)
|
| 18 |
+
x2 = x * x
|
| 19 |
+
x3 = x2 * x
|
| 20 |
+
x4 = x2 * x2
|
| 21 |
+
x5 = x4 * x
|
| 22 |
+
r = 0.13572138 + 4.61539260*x - 42.66032258*x2 + 132.13108234*x3 - 152.94239396*x4 + 59.28637943*x5
|
| 23 |
+
g = 0.09140261 + 2.19418839*x + 4.84296658*x2 - 14.18503333*x3 + 4.27729857*x4 + 2.82956604*x5
|
| 24 |
+
b = 0.10667330 + 12.64194608*x - 60.58204836*x2 + 110.36276771*x3 - 89.90310912*x4 + 27.34824973*x5
|
| 25 |
+
return torch.stack([r, g, b], dim=-1).clamp(0.0, 1.0)
|
ComfyUI/comfy/ldm/common_dit.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import comfy.rmsnorm
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def pad_to_patch_size(img, patch_size=(2, 2), padding_mode="circular"):
|
| 6 |
+
if padding_mode == "circular" and (torch.jit.is_tracing() or torch.jit.is_scripting()):
|
| 7 |
+
padding_mode = "reflect"
|
| 8 |
+
|
| 9 |
+
pad = ()
|
| 10 |
+
for i in range(img.ndim - 2):
|
| 11 |
+
pad = (0, (patch_size[i] - img.shape[i + 2] % patch_size[i]) % patch_size[i]) + pad
|
| 12 |
+
|
| 13 |
+
return torch.nn.functional.pad(img, pad, mode=padding_mode)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
rms_norm = comfy.rmsnorm.rms_norm
|
ComfyUI/comfy/ldm/cosmos/blocks.py
ADDED
|
@@ -0,0 +1,805 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import math
|
| 17 |
+
from typing import Optional
|
| 18 |
+
import logging
|
| 19 |
+
|
| 20 |
+
import numpy as np
|
| 21 |
+
import torch
|
| 22 |
+
from einops import rearrange, repeat
|
| 23 |
+
from einops.layers.torch import Rearrange
|
| 24 |
+
from torch import nn
|
| 25 |
+
|
| 26 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def get_normalization(name: str, channels: int, weight_args={}, operations=None):
|
| 30 |
+
if name == "I":
|
| 31 |
+
return nn.Identity()
|
| 32 |
+
elif name == "R":
|
| 33 |
+
return operations.RMSNorm(channels, elementwise_affine=True, eps=1e-6, **weight_args)
|
| 34 |
+
else:
|
| 35 |
+
raise ValueError(f"Normalization {name} not found")
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class BaseAttentionOp(nn.Module):
|
| 39 |
+
def __init__(self):
|
| 40 |
+
super().__init__()
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class Attention(nn.Module):
|
| 44 |
+
"""
|
| 45 |
+
Generalized attention impl.
|
| 46 |
+
|
| 47 |
+
Allowing for both self-attention and cross-attention configurations depending on whether a `context_dim` is provided.
|
| 48 |
+
If `context_dim` is None, self-attention is assumed.
|
| 49 |
+
|
| 50 |
+
Parameters:
|
| 51 |
+
query_dim (int): Dimension of each query vector.
|
| 52 |
+
context_dim (int, optional): Dimension of each context vector. If None, self-attention is assumed.
|
| 53 |
+
heads (int, optional): Number of attention heads. Defaults to 8.
|
| 54 |
+
dim_head (int, optional): Dimension of each head. Defaults to 64.
|
| 55 |
+
dropout (float, optional): Dropout rate applied to the output of the attention block. Defaults to 0.0.
|
| 56 |
+
attn_op (BaseAttentionOp, optional): Custom attention operation to be used instead of the default.
|
| 57 |
+
qkv_bias (bool, optional): If True, adds a learnable bias to query, key, and value projections. Defaults to False.
|
| 58 |
+
out_bias (bool, optional): If True, adds a learnable bias to the output projection. Defaults to False.
|
| 59 |
+
qkv_norm (str, optional): A string representing normalization strategies for query, key, and value projections.
|
| 60 |
+
Defaults to "SSI".
|
| 61 |
+
qkv_norm_mode (str, optional): A string representing normalization mode for query, key, and value projections.
|
| 62 |
+
Defaults to 'per_head'. Only support 'per_head'.
|
| 63 |
+
|
| 64 |
+
Examples:
|
| 65 |
+
>>> attn = Attention(query_dim=128, context_dim=256, heads=4, dim_head=32, dropout=0.1)
|
| 66 |
+
>>> query = torch.randn(10, 128) # Batch size of 10
|
| 67 |
+
>>> context = torch.randn(10, 256) # Batch size of 10
|
| 68 |
+
>>> output = attn(query, context) # Perform the attention operation
|
| 69 |
+
|
| 70 |
+
Note:
|
| 71 |
+
https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
def __init__(
|
| 75 |
+
self,
|
| 76 |
+
query_dim: int,
|
| 77 |
+
context_dim=None,
|
| 78 |
+
heads=8,
|
| 79 |
+
dim_head=64,
|
| 80 |
+
dropout=0.0,
|
| 81 |
+
attn_op: Optional[BaseAttentionOp] = None,
|
| 82 |
+
qkv_bias: bool = False,
|
| 83 |
+
out_bias: bool = False,
|
| 84 |
+
qkv_norm: str = "SSI",
|
| 85 |
+
qkv_norm_mode: str = "per_head",
|
| 86 |
+
backend: str = "transformer_engine",
|
| 87 |
+
qkv_format: str = "bshd",
|
| 88 |
+
weight_args={},
|
| 89 |
+
operations=None,
|
| 90 |
+
) -> None:
|
| 91 |
+
super().__init__()
|
| 92 |
+
|
| 93 |
+
self.is_selfattn = context_dim is None # self attention
|
| 94 |
+
|
| 95 |
+
inner_dim = dim_head * heads
|
| 96 |
+
context_dim = query_dim if context_dim is None else context_dim
|
| 97 |
+
|
| 98 |
+
self.heads = heads
|
| 99 |
+
self.dim_head = dim_head
|
| 100 |
+
self.qkv_norm_mode = qkv_norm_mode
|
| 101 |
+
self.qkv_format = qkv_format
|
| 102 |
+
|
| 103 |
+
if self.qkv_norm_mode == "per_head":
|
| 104 |
+
norm_dim = dim_head
|
| 105 |
+
else:
|
| 106 |
+
raise ValueError(f"Normalization mode {self.qkv_norm_mode} not found, only support 'per_head'")
|
| 107 |
+
|
| 108 |
+
self.backend = backend
|
| 109 |
+
|
| 110 |
+
self.to_q = nn.Sequential(
|
| 111 |
+
operations.Linear(query_dim, inner_dim, bias=qkv_bias, **weight_args),
|
| 112 |
+
get_normalization(qkv_norm[0], norm_dim, weight_args=weight_args, operations=operations),
|
| 113 |
+
)
|
| 114 |
+
self.to_k = nn.Sequential(
|
| 115 |
+
operations.Linear(context_dim, inner_dim, bias=qkv_bias, **weight_args),
|
| 116 |
+
get_normalization(qkv_norm[1], norm_dim, weight_args=weight_args, operations=operations),
|
| 117 |
+
)
|
| 118 |
+
self.to_v = nn.Sequential(
|
| 119 |
+
operations.Linear(context_dim, inner_dim, bias=qkv_bias, **weight_args),
|
| 120 |
+
get_normalization(qkv_norm[2], norm_dim, weight_args=weight_args, operations=operations),
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
self.to_out = nn.Sequential(
|
| 124 |
+
operations.Linear(inner_dim, query_dim, bias=out_bias, **weight_args),
|
| 125 |
+
nn.Dropout(dropout),
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
def cal_qkv(
|
| 129 |
+
self, x, context=None, mask=None, rope_emb=None, **kwargs
|
| 130 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 131 |
+
del kwargs
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
"""
|
| 135 |
+
self.to_q, self.to_k, self.to_v are nn.Sequential with projection + normalization layers.
|
| 136 |
+
Before 07/24/2024, these modules normalize across all heads.
|
| 137 |
+
After 07/24/2024, to support tensor parallelism and follow the common practice in the community,
|
| 138 |
+
we support to normalize per head.
|
| 139 |
+
To keep the checkpoint copatibility with the previous code,
|
| 140 |
+
we keep the nn.Sequential but call the projection and the normalization layers separately.
|
| 141 |
+
We use a flag `self.qkv_norm_mode` to control the normalization behavior.
|
| 142 |
+
The default value of `self.qkv_norm_mode` is "per_head", which means we normalize per head.
|
| 143 |
+
"""
|
| 144 |
+
if self.qkv_norm_mode == "per_head":
|
| 145 |
+
q = self.to_q[0](x)
|
| 146 |
+
context = x if context is None else context
|
| 147 |
+
k = self.to_k[0](context)
|
| 148 |
+
v = self.to_v[0](context)
|
| 149 |
+
q, k, v = map(
|
| 150 |
+
lambda t: rearrange(t, "s b (n c) -> b n s c", n=self.heads, c=self.dim_head),
|
| 151 |
+
(q, k, v),
|
| 152 |
+
)
|
| 153 |
+
else:
|
| 154 |
+
raise ValueError(f"Normalization mode {self.qkv_norm_mode} not found, only support 'per_head'")
|
| 155 |
+
|
| 156 |
+
q = self.to_q[1](q)
|
| 157 |
+
k = self.to_k[1](k)
|
| 158 |
+
v = self.to_v[1](v)
|
| 159 |
+
if self.is_selfattn and rope_emb is not None: # only apply to self-attention!
|
| 160 |
+
# apply_rotary_pos_emb inlined
|
| 161 |
+
q_shape = q.shape
|
| 162 |
+
q = q.reshape(*q.shape[:-1], 2, -1).movedim(-2, -1).unsqueeze(-2)
|
| 163 |
+
q = rope_emb[..., 0] * q[..., 0] + rope_emb[..., 1] * q[..., 1]
|
| 164 |
+
q = q.movedim(-1, -2).reshape(*q_shape).to(x.dtype)
|
| 165 |
+
|
| 166 |
+
# apply_rotary_pos_emb inlined
|
| 167 |
+
k_shape = k.shape
|
| 168 |
+
k = k.reshape(*k.shape[:-1], 2, -1).movedim(-2, -1).unsqueeze(-2)
|
| 169 |
+
k = rope_emb[..., 0] * k[..., 0] + rope_emb[..., 1] * k[..., 1]
|
| 170 |
+
k = k.movedim(-1, -2).reshape(*k_shape).to(x.dtype)
|
| 171 |
+
return q, k, v
|
| 172 |
+
|
| 173 |
+
def forward(
|
| 174 |
+
self,
|
| 175 |
+
x,
|
| 176 |
+
context=None,
|
| 177 |
+
mask=None,
|
| 178 |
+
rope_emb=None,
|
| 179 |
+
transformer_options={},
|
| 180 |
+
**kwargs,
|
| 181 |
+
):
|
| 182 |
+
"""
|
| 183 |
+
Args:
|
| 184 |
+
x (Tensor): The query tensor of shape [B, Mq, K]
|
| 185 |
+
context (Optional[Tensor]): The key tensor of shape [B, Mk, K] or use x as context [self attention] if None
|
| 186 |
+
"""
|
| 187 |
+
q, k, v = self.cal_qkv(x, context, mask, rope_emb=rope_emb, **kwargs)
|
| 188 |
+
out = optimized_attention(q, k, v, self.heads, skip_reshape=True, mask=mask, skip_output_reshape=True, transformer_options=transformer_options)
|
| 189 |
+
del q, k, v
|
| 190 |
+
out = rearrange(out, " b n s c -> s b (n c)")
|
| 191 |
+
return self.to_out(out)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
class FeedForward(nn.Module):
|
| 195 |
+
"""
|
| 196 |
+
Transformer FFN with optional gating
|
| 197 |
+
|
| 198 |
+
Parameters:
|
| 199 |
+
d_model (int): Dimensionality of input features.
|
| 200 |
+
d_ff (int): Dimensionality of the hidden layer.
|
| 201 |
+
dropout (float, optional): Dropout rate applied after the activation function. Defaults to 0.1.
|
| 202 |
+
activation (callable, optional): The activation function applied after the first linear layer.
|
| 203 |
+
Defaults to nn.ReLU().
|
| 204 |
+
is_gated (bool, optional): If set to True, incorporates gating mechanism to the feed-forward layer.
|
| 205 |
+
Defaults to False.
|
| 206 |
+
bias (bool, optional): If set to True, adds a bias to the linear layers. Defaults to True.
|
| 207 |
+
|
| 208 |
+
Example:
|
| 209 |
+
>>> ff = FeedForward(d_model=512, d_ff=2048)
|
| 210 |
+
>>> x = torch.randn(64, 10, 512) # Example input tensor
|
| 211 |
+
>>> output = ff(x)
|
| 212 |
+
>>> print(output.shape) # Expected shape: (64, 10, 512)
|
| 213 |
+
"""
|
| 214 |
+
|
| 215 |
+
def __init__(
|
| 216 |
+
self,
|
| 217 |
+
d_model: int,
|
| 218 |
+
d_ff: int,
|
| 219 |
+
dropout: float = 0.1,
|
| 220 |
+
activation=nn.ReLU(),
|
| 221 |
+
is_gated: bool = False,
|
| 222 |
+
bias: bool = False,
|
| 223 |
+
weight_args={},
|
| 224 |
+
operations=None,
|
| 225 |
+
) -> None:
|
| 226 |
+
super().__init__()
|
| 227 |
+
|
| 228 |
+
self.layer1 = operations.Linear(d_model, d_ff, bias=bias, **weight_args)
|
| 229 |
+
self.layer2 = operations.Linear(d_ff, d_model, bias=bias, **weight_args)
|
| 230 |
+
|
| 231 |
+
self.dropout = nn.Dropout(dropout)
|
| 232 |
+
self.activation = activation
|
| 233 |
+
self.is_gated = is_gated
|
| 234 |
+
if is_gated:
|
| 235 |
+
self.linear_gate = operations.Linear(d_model, d_ff, bias=False, **weight_args)
|
| 236 |
+
|
| 237 |
+
def forward(self, x: torch.Tensor):
|
| 238 |
+
g = self.activation(self.layer1(x))
|
| 239 |
+
if self.is_gated:
|
| 240 |
+
x = g * self.linear_gate(x)
|
| 241 |
+
else:
|
| 242 |
+
x = g
|
| 243 |
+
assert self.dropout.p == 0.0, "we skip dropout"
|
| 244 |
+
return self.layer2(x)
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
class GPT2FeedForward(FeedForward):
|
| 248 |
+
def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1, bias: bool = False, weight_args={}, operations=None):
|
| 249 |
+
super().__init__(
|
| 250 |
+
d_model=d_model,
|
| 251 |
+
d_ff=d_ff,
|
| 252 |
+
dropout=dropout,
|
| 253 |
+
activation=nn.GELU(),
|
| 254 |
+
is_gated=False,
|
| 255 |
+
bias=bias,
|
| 256 |
+
weight_args=weight_args,
|
| 257 |
+
operations=operations,
|
| 258 |
+
)
|
| 259 |
+
|
| 260 |
+
def forward(self, x: torch.Tensor):
|
| 261 |
+
assert self.dropout.p == 0.0, "we skip dropout"
|
| 262 |
+
|
| 263 |
+
x = self.layer1(x)
|
| 264 |
+
x = self.activation(x)
|
| 265 |
+
x = self.layer2(x)
|
| 266 |
+
|
| 267 |
+
return x
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def modulate(x, shift, scale):
|
| 271 |
+
return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
class Timesteps(nn.Module):
|
| 275 |
+
def __init__(self, num_channels):
|
| 276 |
+
super().__init__()
|
| 277 |
+
self.num_channels = num_channels
|
| 278 |
+
|
| 279 |
+
def forward(self, timesteps):
|
| 280 |
+
half_dim = self.num_channels // 2
|
| 281 |
+
exponent = -math.log(10000) * torch.arange(half_dim, dtype=torch.float32, device=timesteps.device)
|
| 282 |
+
exponent = exponent / (half_dim - 0.0)
|
| 283 |
+
|
| 284 |
+
emb = torch.exp(exponent)
|
| 285 |
+
emb = timesteps[:, None].float() * emb[None, :]
|
| 286 |
+
|
| 287 |
+
sin_emb = torch.sin(emb)
|
| 288 |
+
cos_emb = torch.cos(emb)
|
| 289 |
+
emb = torch.cat([cos_emb, sin_emb], dim=-1)
|
| 290 |
+
|
| 291 |
+
return emb
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
class TimestepEmbedding(nn.Module):
|
| 295 |
+
def __init__(self, in_features: int, out_features: int, use_adaln_lora: bool = False, weight_args={}, operations=None):
|
| 296 |
+
super().__init__()
|
| 297 |
+
logging.debug(
|
| 298 |
+
f"Using AdaLN LoRA Flag: {use_adaln_lora}. We enable bias if no AdaLN LoRA for backward compatibility."
|
| 299 |
+
)
|
| 300 |
+
self.linear_1 = operations.Linear(in_features, out_features, bias=not use_adaln_lora, **weight_args)
|
| 301 |
+
self.activation = nn.SiLU()
|
| 302 |
+
self.use_adaln_lora = use_adaln_lora
|
| 303 |
+
if use_adaln_lora:
|
| 304 |
+
self.linear_2 = operations.Linear(out_features, 3 * out_features, bias=False, **weight_args)
|
| 305 |
+
else:
|
| 306 |
+
self.linear_2 = operations.Linear(out_features, out_features, bias=True, **weight_args)
|
| 307 |
+
|
| 308 |
+
def forward(self, sample: torch.Tensor) -> torch.Tensor:
|
| 309 |
+
emb = self.linear_1(sample)
|
| 310 |
+
emb = self.activation(emb)
|
| 311 |
+
emb = self.linear_2(emb)
|
| 312 |
+
|
| 313 |
+
if self.use_adaln_lora:
|
| 314 |
+
adaln_lora_B_3D = emb
|
| 315 |
+
emb_B_D = sample
|
| 316 |
+
else:
|
| 317 |
+
emb_B_D = emb
|
| 318 |
+
adaln_lora_B_3D = None
|
| 319 |
+
|
| 320 |
+
return emb_B_D, adaln_lora_B_3D
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
class FourierFeatures(nn.Module):
|
| 324 |
+
"""
|
| 325 |
+
Implements a layer that generates Fourier features from input tensors, based on randomly sampled
|
| 326 |
+
frequencies and phases. This can help in learning high-frequency functions in low-dimensional problems.
|
| 327 |
+
|
| 328 |
+
[B] -> [B, D]
|
| 329 |
+
|
| 330 |
+
Parameters:
|
| 331 |
+
num_channels (int): The number of Fourier features to generate.
|
| 332 |
+
bandwidth (float, optional): The scaling factor for the frequency of the Fourier features. Defaults to 1.
|
| 333 |
+
normalize (bool, optional): If set to True, the outputs are scaled by sqrt(2), usually to normalize
|
| 334 |
+
the variance of the features. Defaults to False.
|
| 335 |
+
|
| 336 |
+
Example:
|
| 337 |
+
>>> layer = FourierFeatures(num_channels=256, bandwidth=0.5, normalize=True)
|
| 338 |
+
>>> x = torch.randn(10, 256) # Example input tensor
|
| 339 |
+
>>> output = layer(x)
|
| 340 |
+
>>> print(output.shape) # Expected shape: (10, 256)
|
| 341 |
+
"""
|
| 342 |
+
|
| 343 |
+
def __init__(self, num_channels, bandwidth=1, normalize=False):
|
| 344 |
+
super().__init__()
|
| 345 |
+
self.register_buffer("freqs", 2 * np.pi * bandwidth * torch.randn(num_channels), persistent=True)
|
| 346 |
+
self.register_buffer("phases", 2 * np.pi * torch.rand(num_channels), persistent=True)
|
| 347 |
+
self.gain = np.sqrt(2) if normalize else 1
|
| 348 |
+
|
| 349 |
+
def forward(self, x, gain: float = 1.0):
|
| 350 |
+
"""
|
| 351 |
+
Apply the Fourier feature transformation to the input tensor.
|
| 352 |
+
|
| 353 |
+
Args:
|
| 354 |
+
x (torch.Tensor): The input tensor.
|
| 355 |
+
gain (float, optional): An additional gain factor applied during the forward pass. Defaults to 1.
|
| 356 |
+
|
| 357 |
+
Returns:
|
| 358 |
+
torch.Tensor: The transformed tensor, with Fourier features applied.
|
| 359 |
+
"""
|
| 360 |
+
in_dtype = x.dtype
|
| 361 |
+
x = x.to(torch.float32).ger(self.freqs.to(torch.float32)).add(self.phases.to(torch.float32))
|
| 362 |
+
x = x.cos().mul(self.gain * gain).to(in_dtype)
|
| 363 |
+
return x
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
class PatchEmbed(nn.Module):
|
| 367 |
+
"""
|
| 368 |
+
PatchEmbed is a module for embedding patches from an input tensor by applying either 3D or 2D convolutional layers,
|
| 369 |
+
depending on the . This module can process inputs with temporal (video) and spatial (image) dimensions,
|
| 370 |
+
making it suitable for video and image processing tasks. It supports dividing the input into patches
|
| 371 |
+
and embedding each patch into a vector of size `out_channels`.
|
| 372 |
+
|
| 373 |
+
Parameters:
|
| 374 |
+
- spatial_patch_size (int): The size of each spatial patch.
|
| 375 |
+
- temporal_patch_size (int): The size of each temporal patch.
|
| 376 |
+
- in_channels (int): Number of input channels. Default: 3.
|
| 377 |
+
- out_channels (int): The dimension of the embedding vector for each patch. Default: 768.
|
| 378 |
+
- bias (bool): If True, adds a learnable bias to the output of the convolutional layers. Default: True.
|
| 379 |
+
"""
|
| 380 |
+
|
| 381 |
+
def __init__(
|
| 382 |
+
self,
|
| 383 |
+
spatial_patch_size,
|
| 384 |
+
temporal_patch_size,
|
| 385 |
+
in_channels=3,
|
| 386 |
+
out_channels=768,
|
| 387 |
+
bias=True,
|
| 388 |
+
weight_args={},
|
| 389 |
+
operations=None,
|
| 390 |
+
):
|
| 391 |
+
super().__init__()
|
| 392 |
+
self.spatial_patch_size = spatial_patch_size
|
| 393 |
+
self.temporal_patch_size = temporal_patch_size
|
| 394 |
+
|
| 395 |
+
self.proj = nn.Sequential(
|
| 396 |
+
Rearrange(
|
| 397 |
+
"b c (t r) (h m) (w n) -> b t h w (c r m n)",
|
| 398 |
+
r=temporal_patch_size,
|
| 399 |
+
m=spatial_patch_size,
|
| 400 |
+
n=spatial_patch_size,
|
| 401 |
+
),
|
| 402 |
+
operations.Linear(
|
| 403 |
+
in_channels * spatial_patch_size * spatial_patch_size * temporal_patch_size, out_channels, bias=bias, **weight_args
|
| 404 |
+
),
|
| 405 |
+
)
|
| 406 |
+
self.out = nn.Identity()
|
| 407 |
+
|
| 408 |
+
def forward(self, x):
|
| 409 |
+
"""
|
| 410 |
+
Forward pass of the PatchEmbed module.
|
| 411 |
+
|
| 412 |
+
Parameters:
|
| 413 |
+
- x (torch.Tensor): The input tensor of shape (B, C, T, H, W) where
|
| 414 |
+
B is the batch size,
|
| 415 |
+
C is the number of channels,
|
| 416 |
+
T is the temporal dimension,
|
| 417 |
+
H is the height, and
|
| 418 |
+
W is the width of the input.
|
| 419 |
+
|
| 420 |
+
Returns:
|
| 421 |
+
- torch.Tensor: The embedded patches as a tensor, with shape b t h w c.
|
| 422 |
+
"""
|
| 423 |
+
assert x.dim() == 5
|
| 424 |
+
_, _, T, H, W = x.shape
|
| 425 |
+
assert H % self.spatial_patch_size == 0 and W % self.spatial_patch_size == 0
|
| 426 |
+
assert T % self.temporal_patch_size == 0
|
| 427 |
+
x = self.proj(x)
|
| 428 |
+
return self.out(x)
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
class FinalLayer(nn.Module):
|
| 432 |
+
"""
|
| 433 |
+
The final layer of video DiT.
|
| 434 |
+
"""
|
| 435 |
+
|
| 436 |
+
def __init__(
|
| 437 |
+
self,
|
| 438 |
+
hidden_size,
|
| 439 |
+
spatial_patch_size,
|
| 440 |
+
temporal_patch_size,
|
| 441 |
+
out_channels,
|
| 442 |
+
use_adaln_lora: bool = False,
|
| 443 |
+
adaln_lora_dim: int = 256,
|
| 444 |
+
weight_args={},
|
| 445 |
+
operations=None,
|
| 446 |
+
):
|
| 447 |
+
super().__init__()
|
| 448 |
+
self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, **weight_args)
|
| 449 |
+
self.linear = operations.Linear(
|
| 450 |
+
hidden_size, spatial_patch_size * spatial_patch_size * temporal_patch_size * out_channels, bias=False, **weight_args
|
| 451 |
+
)
|
| 452 |
+
self.hidden_size = hidden_size
|
| 453 |
+
self.n_adaln_chunks = 2
|
| 454 |
+
self.use_adaln_lora = use_adaln_lora
|
| 455 |
+
if use_adaln_lora:
|
| 456 |
+
self.adaLN_modulation = nn.Sequential(
|
| 457 |
+
nn.SiLU(),
|
| 458 |
+
operations.Linear(hidden_size, adaln_lora_dim, bias=False, **weight_args),
|
| 459 |
+
operations.Linear(adaln_lora_dim, self.n_adaln_chunks * hidden_size, bias=False, **weight_args),
|
| 460 |
+
)
|
| 461 |
+
else:
|
| 462 |
+
self.adaLN_modulation = nn.Sequential(
|
| 463 |
+
nn.SiLU(), operations.Linear(hidden_size, self.n_adaln_chunks * hidden_size, bias=False, **weight_args)
|
| 464 |
+
)
|
| 465 |
+
|
| 466 |
+
def forward(
|
| 467 |
+
self,
|
| 468 |
+
x_BT_HW_D,
|
| 469 |
+
emb_B_D,
|
| 470 |
+
adaln_lora_B_3D: Optional[torch.Tensor] = None,
|
| 471 |
+
):
|
| 472 |
+
if self.use_adaln_lora:
|
| 473 |
+
assert adaln_lora_B_3D is not None
|
| 474 |
+
shift_B_D, scale_B_D = (self.adaLN_modulation(emb_B_D) + adaln_lora_B_3D[:, : 2 * self.hidden_size]).chunk(
|
| 475 |
+
2, dim=1
|
| 476 |
+
)
|
| 477 |
+
else:
|
| 478 |
+
shift_B_D, scale_B_D = self.adaLN_modulation(emb_B_D).chunk(2, dim=1)
|
| 479 |
+
|
| 480 |
+
B = emb_B_D.shape[0]
|
| 481 |
+
T = x_BT_HW_D.shape[0] // B
|
| 482 |
+
shift_BT_D, scale_BT_D = repeat(shift_B_D, "b d -> (b t) d", t=T), repeat(scale_B_D, "b d -> (b t) d", t=T)
|
| 483 |
+
x_BT_HW_D = modulate(self.norm_final(x_BT_HW_D), shift_BT_D, scale_BT_D)
|
| 484 |
+
|
| 485 |
+
x_BT_HW_D = self.linear(x_BT_HW_D)
|
| 486 |
+
return x_BT_HW_D
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
class VideoAttn(nn.Module):
|
| 490 |
+
"""
|
| 491 |
+
Implements video attention with optional cross-attention capabilities.
|
| 492 |
+
|
| 493 |
+
This module processes video features while maintaining their spatio-temporal structure. It can perform
|
| 494 |
+
self-attention within the video features or cross-attention with external context features.
|
| 495 |
+
|
| 496 |
+
Parameters:
|
| 497 |
+
x_dim (int): Dimension of input feature vectors
|
| 498 |
+
context_dim (Optional[int]): Dimension of context features for cross-attention. None for self-attention
|
| 499 |
+
num_heads (int): Number of attention heads
|
| 500 |
+
bias (bool): Whether to include bias in attention projections. Default: False
|
| 501 |
+
qkv_norm_mode (str): Normalization mode for query/key/value projections. Must be "per_head". Default: "per_head"
|
| 502 |
+
x_format (str): Format of input tensor. Must be "BTHWD". Default: "BTHWD"
|
| 503 |
+
|
| 504 |
+
Input shape:
|
| 505 |
+
- x: (T, H, W, B, D) video features
|
| 506 |
+
- context (optional): (M, B, D) context features for cross-attention
|
| 507 |
+
where:
|
| 508 |
+
T: temporal dimension
|
| 509 |
+
H: height
|
| 510 |
+
W: width
|
| 511 |
+
B: batch size
|
| 512 |
+
D: feature dimension
|
| 513 |
+
M: context sequence length
|
| 514 |
+
"""
|
| 515 |
+
|
| 516 |
+
def __init__(
|
| 517 |
+
self,
|
| 518 |
+
x_dim: int,
|
| 519 |
+
context_dim: Optional[int],
|
| 520 |
+
num_heads: int,
|
| 521 |
+
bias: bool = False,
|
| 522 |
+
qkv_norm_mode: str = "per_head",
|
| 523 |
+
x_format: str = "BTHWD",
|
| 524 |
+
weight_args={},
|
| 525 |
+
operations=None,
|
| 526 |
+
) -> None:
|
| 527 |
+
super().__init__()
|
| 528 |
+
self.x_format = x_format
|
| 529 |
+
|
| 530 |
+
self.attn = Attention(
|
| 531 |
+
x_dim,
|
| 532 |
+
context_dim,
|
| 533 |
+
num_heads,
|
| 534 |
+
x_dim // num_heads,
|
| 535 |
+
qkv_bias=bias,
|
| 536 |
+
qkv_norm="RRI",
|
| 537 |
+
out_bias=bias,
|
| 538 |
+
qkv_norm_mode=qkv_norm_mode,
|
| 539 |
+
qkv_format="sbhd",
|
| 540 |
+
weight_args=weight_args,
|
| 541 |
+
operations=operations,
|
| 542 |
+
)
|
| 543 |
+
|
| 544 |
+
def forward(
|
| 545 |
+
self,
|
| 546 |
+
x: torch.Tensor,
|
| 547 |
+
context: Optional[torch.Tensor] = None,
|
| 548 |
+
crossattn_mask: Optional[torch.Tensor] = None,
|
| 549 |
+
rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
|
| 550 |
+
transformer_options: Optional[dict] = {},
|
| 551 |
+
) -> torch.Tensor:
|
| 552 |
+
"""
|
| 553 |
+
Forward pass for video attention.
|
| 554 |
+
|
| 555 |
+
Args:
|
| 556 |
+
x (Tensor): Input tensor of shape (B, T, H, W, D) or (T, H, W, B, D) representing batches of video data.
|
| 557 |
+
context (Tensor): Context tensor of shape (B, M, D) or (M, B, D),
|
| 558 |
+
where M is the sequence length of the context.
|
| 559 |
+
crossattn_mask (Optional[Tensor]): An optional mask for cross-attention mechanisms.
|
| 560 |
+
rope_emb_L_1_1_D (Optional[Tensor]):
|
| 561 |
+
Rotary positional embedding tensor of shape (L, 1, 1, D). L == THW for current video training.
|
| 562 |
+
|
| 563 |
+
Returns:
|
| 564 |
+
Tensor: The output tensor with applied attention, maintaining the input shape.
|
| 565 |
+
"""
|
| 566 |
+
|
| 567 |
+
x_T_H_W_B_D = x
|
| 568 |
+
context_M_B_D = context
|
| 569 |
+
T, H, W, B, D = x_T_H_W_B_D.shape
|
| 570 |
+
x_THW_B_D = rearrange(x_T_H_W_B_D, "t h w b d -> (t h w) b d")
|
| 571 |
+
x_THW_B_D = self.attn(
|
| 572 |
+
x_THW_B_D,
|
| 573 |
+
context_M_B_D,
|
| 574 |
+
crossattn_mask,
|
| 575 |
+
rope_emb=rope_emb_L_1_1_D,
|
| 576 |
+
transformer_options=transformer_options,
|
| 577 |
+
)
|
| 578 |
+
x_T_H_W_B_D = rearrange(x_THW_B_D, "(t h w) b d -> t h w b d", h=H, w=W)
|
| 579 |
+
return x_T_H_W_B_D
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
def adaln_norm_state(norm_state, x, scale, shift):
|
| 583 |
+
normalized = norm_state(x)
|
| 584 |
+
return normalized * (1 + scale) + shift
|
| 585 |
+
|
| 586 |
+
|
| 587 |
+
class DITBuildingBlock(nn.Module):
|
| 588 |
+
"""
|
| 589 |
+
A building block for the DiT (Diffusion Transformer) architecture that supports different types of
|
| 590 |
+
attention and MLP operations with adaptive layer normalization.
|
| 591 |
+
|
| 592 |
+
Parameters:
|
| 593 |
+
block_type (str): Type of block - one of:
|
| 594 |
+
- "cross_attn"/"ca": Cross-attention
|
| 595 |
+
- "full_attn"/"fa": Full self-attention
|
| 596 |
+
- "mlp"/"ff": MLP/feedforward block
|
| 597 |
+
x_dim (int): Dimension of input features
|
| 598 |
+
context_dim (Optional[int]): Dimension of context features for cross-attention
|
| 599 |
+
num_heads (int): Number of attention heads
|
| 600 |
+
mlp_ratio (float): MLP hidden dimension multiplier. Default: 4.0
|
| 601 |
+
bias (bool): Whether to use bias in layers. Default: False
|
| 602 |
+
mlp_dropout (float): Dropout rate for MLP. Default: 0.0
|
| 603 |
+
qkv_norm_mode (str): QKV normalization mode. Default: "per_head"
|
| 604 |
+
x_format (str): Input tensor format. Default: "BTHWD"
|
| 605 |
+
use_adaln_lora (bool): Whether to use AdaLN-LoRA. Default: False
|
| 606 |
+
adaln_lora_dim (int): Dimension for AdaLN-LoRA. Default: 256
|
| 607 |
+
"""
|
| 608 |
+
|
| 609 |
+
def __init__(
|
| 610 |
+
self,
|
| 611 |
+
block_type: str,
|
| 612 |
+
x_dim: int,
|
| 613 |
+
context_dim: Optional[int],
|
| 614 |
+
num_heads: int,
|
| 615 |
+
mlp_ratio: float = 4.0,
|
| 616 |
+
bias: bool = False,
|
| 617 |
+
mlp_dropout: float = 0.0,
|
| 618 |
+
qkv_norm_mode: str = "per_head",
|
| 619 |
+
x_format: str = "BTHWD",
|
| 620 |
+
use_adaln_lora: bool = False,
|
| 621 |
+
adaln_lora_dim: int = 256,
|
| 622 |
+
weight_args={},
|
| 623 |
+
operations=None
|
| 624 |
+
) -> None:
|
| 625 |
+
block_type = block_type.lower()
|
| 626 |
+
|
| 627 |
+
super().__init__()
|
| 628 |
+
self.x_format = x_format
|
| 629 |
+
if block_type in ["cross_attn", "ca"]:
|
| 630 |
+
self.block = VideoAttn(
|
| 631 |
+
x_dim,
|
| 632 |
+
context_dim,
|
| 633 |
+
num_heads,
|
| 634 |
+
bias=bias,
|
| 635 |
+
qkv_norm_mode=qkv_norm_mode,
|
| 636 |
+
x_format=self.x_format,
|
| 637 |
+
weight_args=weight_args,
|
| 638 |
+
operations=operations,
|
| 639 |
+
)
|
| 640 |
+
elif block_type in ["full_attn", "fa"]:
|
| 641 |
+
self.block = VideoAttn(
|
| 642 |
+
x_dim, None, num_heads, bias=bias, qkv_norm_mode=qkv_norm_mode, x_format=self.x_format, weight_args=weight_args, operations=operations
|
| 643 |
+
)
|
| 644 |
+
elif block_type in ["mlp", "ff"]:
|
| 645 |
+
self.block = GPT2FeedForward(x_dim, int(x_dim * mlp_ratio), dropout=mlp_dropout, bias=bias, weight_args=weight_args, operations=operations)
|
| 646 |
+
else:
|
| 647 |
+
raise ValueError(f"Unknown block type: {block_type}")
|
| 648 |
+
|
| 649 |
+
self.block_type = block_type
|
| 650 |
+
self.use_adaln_lora = use_adaln_lora
|
| 651 |
+
|
| 652 |
+
self.norm_state = nn.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6)
|
| 653 |
+
self.n_adaln_chunks = 3
|
| 654 |
+
if use_adaln_lora:
|
| 655 |
+
self.adaLN_modulation = nn.Sequential(
|
| 656 |
+
nn.SiLU(),
|
| 657 |
+
operations.Linear(x_dim, adaln_lora_dim, bias=False, **weight_args),
|
| 658 |
+
operations.Linear(adaln_lora_dim, self.n_adaln_chunks * x_dim, bias=False, **weight_args),
|
| 659 |
+
)
|
| 660 |
+
else:
|
| 661 |
+
self.adaLN_modulation = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, self.n_adaln_chunks * x_dim, bias=False, **weight_args))
|
| 662 |
+
|
| 663 |
+
def forward(
|
| 664 |
+
self,
|
| 665 |
+
x: torch.Tensor,
|
| 666 |
+
emb_B_D: torch.Tensor,
|
| 667 |
+
crossattn_emb: torch.Tensor,
|
| 668 |
+
crossattn_mask: Optional[torch.Tensor] = None,
|
| 669 |
+
rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
|
| 670 |
+
adaln_lora_B_3D: Optional[torch.Tensor] = None,
|
| 671 |
+
transformer_options: Optional[dict] = {},
|
| 672 |
+
) -> torch.Tensor:
|
| 673 |
+
"""
|
| 674 |
+
Forward pass for dynamically configured blocks with adaptive normalization.
|
| 675 |
+
|
| 676 |
+
Args:
|
| 677 |
+
x (Tensor): Input tensor of shape (B, T, H, W, D) or (T, H, W, B, D).
|
| 678 |
+
emb_B_D (Tensor): Embedding tensor for adaptive layer normalization modulation.
|
| 679 |
+
crossattn_emb (Tensor): Tensor for cross-attention blocks.
|
| 680 |
+
crossattn_mask (Optional[Tensor]): Optional mask for cross-attention.
|
| 681 |
+
rope_emb_L_1_1_D (Optional[Tensor]):
|
| 682 |
+
Rotary positional embedding tensor of shape (L, 1, 1, D). L == THW for current video training.
|
| 683 |
+
|
| 684 |
+
Returns:
|
| 685 |
+
Tensor: The output tensor after processing through the configured block and adaptive normalization.
|
| 686 |
+
"""
|
| 687 |
+
if self.use_adaln_lora:
|
| 688 |
+
shift_B_D, scale_B_D, gate_B_D = (self.adaLN_modulation(emb_B_D) + adaln_lora_B_3D).chunk(
|
| 689 |
+
self.n_adaln_chunks, dim=1
|
| 690 |
+
)
|
| 691 |
+
else:
|
| 692 |
+
shift_B_D, scale_B_D, gate_B_D = self.adaLN_modulation(emb_B_D).chunk(self.n_adaln_chunks, dim=1)
|
| 693 |
+
|
| 694 |
+
shift_1_1_1_B_D, scale_1_1_1_B_D, gate_1_1_1_B_D = (
|
| 695 |
+
shift_B_D.unsqueeze(0).unsqueeze(0).unsqueeze(0),
|
| 696 |
+
scale_B_D.unsqueeze(0).unsqueeze(0).unsqueeze(0),
|
| 697 |
+
gate_B_D.unsqueeze(0).unsqueeze(0).unsqueeze(0),
|
| 698 |
+
)
|
| 699 |
+
|
| 700 |
+
if self.block_type in ["mlp", "ff"]:
|
| 701 |
+
x = x + gate_1_1_1_B_D * self.block(
|
| 702 |
+
adaln_norm_state(self.norm_state, x, scale_1_1_1_B_D, shift_1_1_1_B_D),
|
| 703 |
+
)
|
| 704 |
+
elif self.block_type in ["full_attn", "fa"]:
|
| 705 |
+
x = x + gate_1_1_1_B_D * self.block(
|
| 706 |
+
adaln_norm_state(self.norm_state, x, scale_1_1_1_B_D, shift_1_1_1_B_D),
|
| 707 |
+
context=None,
|
| 708 |
+
rope_emb_L_1_1_D=rope_emb_L_1_1_D,
|
| 709 |
+
transformer_options=transformer_options,
|
| 710 |
+
)
|
| 711 |
+
elif self.block_type in ["cross_attn", "ca"]:
|
| 712 |
+
x = x + gate_1_1_1_B_D * self.block(
|
| 713 |
+
adaln_norm_state(self.norm_state, x, scale_1_1_1_B_D, shift_1_1_1_B_D),
|
| 714 |
+
context=crossattn_emb,
|
| 715 |
+
crossattn_mask=crossattn_mask,
|
| 716 |
+
rope_emb_L_1_1_D=rope_emb_L_1_1_D,
|
| 717 |
+
transformer_options=transformer_options,
|
| 718 |
+
)
|
| 719 |
+
else:
|
| 720 |
+
raise ValueError(f"Unknown block type: {self.block_type}")
|
| 721 |
+
|
| 722 |
+
return x
|
| 723 |
+
|
| 724 |
+
|
| 725 |
+
class GeneralDITTransformerBlock(nn.Module):
|
| 726 |
+
"""
|
| 727 |
+
A wrapper module that manages a sequence of DITBuildingBlocks to form a complete transformer layer.
|
| 728 |
+
Each block in the sequence is specified by a block configuration string.
|
| 729 |
+
|
| 730 |
+
Parameters:
|
| 731 |
+
x_dim (int): Dimension of input features
|
| 732 |
+
context_dim (int): Dimension of context features for cross-attention blocks
|
| 733 |
+
num_heads (int): Number of attention heads
|
| 734 |
+
block_config (str): String specifying block sequence (e.g. "ca-fa-mlp" for cross-attention,
|
| 735 |
+
full-attention, then MLP)
|
| 736 |
+
mlp_ratio (float): MLP hidden dimension multiplier. Default: 4.0
|
| 737 |
+
x_format (str): Input tensor format. Default: "BTHWD"
|
| 738 |
+
use_adaln_lora (bool): Whether to use AdaLN-LoRA. Default: False
|
| 739 |
+
adaln_lora_dim (int): Dimension for AdaLN-LoRA. Default: 256
|
| 740 |
+
|
| 741 |
+
The block_config string uses "-" to separate block types:
|
| 742 |
+
- "ca"/"cross_attn": Cross-attention block
|
| 743 |
+
- "fa"/"full_attn": Full self-attention block
|
| 744 |
+
- "mlp"/"ff": MLP/feedforward block
|
| 745 |
+
|
| 746 |
+
Example:
|
| 747 |
+
block_config = "ca-fa-mlp" creates a sequence of:
|
| 748 |
+
1. Cross-attention block
|
| 749 |
+
2. Full self-attention block
|
| 750 |
+
3. MLP block
|
| 751 |
+
"""
|
| 752 |
+
|
| 753 |
+
def __init__(
|
| 754 |
+
self,
|
| 755 |
+
x_dim: int,
|
| 756 |
+
context_dim: int,
|
| 757 |
+
num_heads: int,
|
| 758 |
+
block_config: str,
|
| 759 |
+
mlp_ratio: float = 4.0,
|
| 760 |
+
x_format: str = "BTHWD",
|
| 761 |
+
use_adaln_lora: bool = False,
|
| 762 |
+
adaln_lora_dim: int = 256,
|
| 763 |
+
weight_args={},
|
| 764 |
+
operations=None
|
| 765 |
+
):
|
| 766 |
+
super().__init__()
|
| 767 |
+
self.blocks = nn.ModuleList()
|
| 768 |
+
self.x_format = x_format
|
| 769 |
+
for block_type in block_config.split("-"):
|
| 770 |
+
self.blocks.append(
|
| 771 |
+
DITBuildingBlock(
|
| 772 |
+
block_type,
|
| 773 |
+
x_dim,
|
| 774 |
+
context_dim,
|
| 775 |
+
num_heads,
|
| 776 |
+
mlp_ratio,
|
| 777 |
+
x_format=self.x_format,
|
| 778 |
+
use_adaln_lora=use_adaln_lora,
|
| 779 |
+
adaln_lora_dim=adaln_lora_dim,
|
| 780 |
+
weight_args=weight_args,
|
| 781 |
+
operations=operations,
|
| 782 |
+
)
|
| 783 |
+
)
|
| 784 |
+
|
| 785 |
+
def forward(
|
| 786 |
+
self,
|
| 787 |
+
x: torch.Tensor,
|
| 788 |
+
emb_B_D: torch.Tensor,
|
| 789 |
+
crossattn_emb: torch.Tensor,
|
| 790 |
+
crossattn_mask: Optional[torch.Tensor] = None,
|
| 791 |
+
rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
|
| 792 |
+
adaln_lora_B_3D: Optional[torch.Tensor] = None,
|
| 793 |
+
transformer_options: Optional[dict] = {},
|
| 794 |
+
) -> torch.Tensor:
|
| 795 |
+
for block in self.blocks:
|
| 796 |
+
x = block(
|
| 797 |
+
x,
|
| 798 |
+
emb_B_D,
|
| 799 |
+
crossattn_emb,
|
| 800 |
+
crossattn_mask,
|
| 801 |
+
rope_emb_L_1_1_D=rope_emb_L_1_1_D,
|
| 802 |
+
adaln_lora_B_3D=adaln_lora_B_3D,
|
| 803 |
+
transformer_options=transformer_options,
|
| 804 |
+
)
|
| 805 |
+
return x
|
ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/layers3d.py
ADDED
|
@@ -0,0 +1,1041 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""The model definition for 3D layers
|
| 16 |
+
|
| 17 |
+
Adapted from: https://github.com/lucidrains/magvit2-pytorch/blob/
|
| 18 |
+
9f49074179c912736e617d61b32be367eb5f993a/magvit2_pytorch/magvit2_pytorch.py#L889
|
| 19 |
+
|
| 20 |
+
[MIT License Copyright (c) 2023 Phil Wang]
|
| 21 |
+
https://github.com/lucidrains/magvit2-pytorch/blob/
|
| 22 |
+
9f49074179c912736e617d61b32be367eb5f993a/LICENSE
|
| 23 |
+
"""
|
| 24 |
+
import math
|
| 25 |
+
from typing import Tuple, Union
|
| 26 |
+
|
| 27 |
+
import numpy as np
|
| 28 |
+
import torch
|
| 29 |
+
import torch.nn as nn
|
| 30 |
+
import torch.nn.functional as F
|
| 31 |
+
import logging
|
| 32 |
+
|
| 33 |
+
from comfy.ldm.modules.diffusionmodules.model import vae_attention
|
| 34 |
+
|
| 35 |
+
from .patching import (
|
| 36 |
+
Patcher,
|
| 37 |
+
Patcher3D,
|
| 38 |
+
UnPatcher,
|
| 39 |
+
UnPatcher3D,
|
| 40 |
+
)
|
| 41 |
+
from .utils import (
|
| 42 |
+
CausalNormalize,
|
| 43 |
+
batch2space,
|
| 44 |
+
batch2time,
|
| 45 |
+
cast_tuple,
|
| 46 |
+
is_odd,
|
| 47 |
+
nonlinearity,
|
| 48 |
+
replication_pad,
|
| 49 |
+
space2batch,
|
| 50 |
+
time2batch,
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
import comfy.ops
|
| 54 |
+
ops = comfy.ops.disable_weight_init
|
| 55 |
+
|
| 56 |
+
_LEGACY_NUM_GROUPS = 32
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class CausalConv3d(nn.Module):
|
| 60 |
+
def __init__(
|
| 61 |
+
self,
|
| 62 |
+
chan_in: int = 1,
|
| 63 |
+
chan_out: int = 1,
|
| 64 |
+
kernel_size: Union[int, Tuple[int, int, int]] = 3,
|
| 65 |
+
pad_mode: str = "constant",
|
| 66 |
+
**kwargs,
|
| 67 |
+
):
|
| 68 |
+
super().__init__()
|
| 69 |
+
kernel_size = cast_tuple(kernel_size, 3)
|
| 70 |
+
|
| 71 |
+
time_kernel_size, height_kernel_size, width_kernel_size = kernel_size
|
| 72 |
+
|
| 73 |
+
assert is_odd(height_kernel_size) and is_odd(width_kernel_size)
|
| 74 |
+
|
| 75 |
+
dilation = kwargs.pop("dilation", 1)
|
| 76 |
+
stride = kwargs.pop("stride", 1)
|
| 77 |
+
time_stride = kwargs.pop("time_stride", 1)
|
| 78 |
+
time_dilation = kwargs.pop("time_dilation", 1)
|
| 79 |
+
padding = kwargs.pop("padding", 1)
|
| 80 |
+
|
| 81 |
+
self.pad_mode = pad_mode
|
| 82 |
+
time_pad = time_dilation * (time_kernel_size - 1) + (1 - time_stride)
|
| 83 |
+
self.time_pad = time_pad
|
| 84 |
+
|
| 85 |
+
self.spatial_pad = (padding, padding, padding, padding)
|
| 86 |
+
|
| 87 |
+
stride = (time_stride, stride, stride)
|
| 88 |
+
dilation = (time_dilation, dilation, dilation)
|
| 89 |
+
self.conv3d = ops.Conv3d(
|
| 90 |
+
chan_in,
|
| 91 |
+
chan_out,
|
| 92 |
+
kernel_size,
|
| 93 |
+
stride=stride,
|
| 94 |
+
dilation=dilation,
|
| 95 |
+
**kwargs,
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
def _replication_pad(self, x: torch.Tensor) -> torch.Tensor:
|
| 99 |
+
x_prev = x[:, :, :1, ...].repeat(1, 1, self.time_pad, 1, 1)
|
| 100 |
+
x = torch.cat([x_prev, x], dim=2)
|
| 101 |
+
padding = self.spatial_pad + (0, 0)
|
| 102 |
+
return F.pad(x, padding, mode=self.pad_mode, value=0.0)
|
| 103 |
+
|
| 104 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 105 |
+
x = self._replication_pad(x)
|
| 106 |
+
return self.conv3d(x)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
class CausalUpsample3d(nn.Module):
|
| 110 |
+
def __init__(self, in_channels: int) -> None:
|
| 111 |
+
super().__init__()
|
| 112 |
+
self.conv = CausalConv3d(
|
| 113 |
+
in_channels, in_channels, kernel_size=3, stride=1, padding=1
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 117 |
+
x = x.repeat_interleave(2, dim=3).repeat_interleave(2, dim=4)
|
| 118 |
+
time_factor = 1.0 + 1.0 * (x.shape[2] > 1)
|
| 119 |
+
if isinstance(time_factor, torch.Tensor):
|
| 120 |
+
time_factor = time_factor.item()
|
| 121 |
+
x = x.repeat_interleave(int(time_factor), dim=2)
|
| 122 |
+
# TODO(freda): Check if this causes temporal inconsistency.
|
| 123 |
+
# Shoule reverse the order of the following two ops,
|
| 124 |
+
# better perf and better temporal smoothness.
|
| 125 |
+
x = self.conv(x)
|
| 126 |
+
return x[..., int(time_factor - 1) :, :, :]
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class CausalDownsample3d(nn.Module):
|
| 130 |
+
def __init__(self, in_channels: int) -> None:
|
| 131 |
+
super().__init__()
|
| 132 |
+
self.conv = CausalConv3d(
|
| 133 |
+
in_channels,
|
| 134 |
+
in_channels,
|
| 135 |
+
kernel_size=3,
|
| 136 |
+
stride=2,
|
| 137 |
+
time_stride=2,
|
| 138 |
+
padding=0,
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 142 |
+
pad = (0, 1, 0, 1, 0, 0)
|
| 143 |
+
x = F.pad(x, pad, mode="constant", value=0)
|
| 144 |
+
x = replication_pad(x)
|
| 145 |
+
x = self.conv(x)
|
| 146 |
+
return x
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
class CausalHybridUpsample3d(nn.Module):
|
| 150 |
+
def __init__(
|
| 151 |
+
self,
|
| 152 |
+
in_channels: int,
|
| 153 |
+
spatial_up: bool = True,
|
| 154 |
+
temporal_up: bool = True,
|
| 155 |
+
**kwargs,
|
| 156 |
+
) -> None:
|
| 157 |
+
super().__init__()
|
| 158 |
+
self.spatial_up = spatial_up
|
| 159 |
+
self.temporal_up = temporal_up
|
| 160 |
+
if not self.spatial_up and not self.temporal_up:
|
| 161 |
+
return
|
| 162 |
+
|
| 163 |
+
self.conv1 = CausalConv3d(
|
| 164 |
+
in_channels,
|
| 165 |
+
in_channels,
|
| 166 |
+
kernel_size=(3, 1, 1),
|
| 167 |
+
stride=1,
|
| 168 |
+
time_stride=1,
|
| 169 |
+
padding=0,
|
| 170 |
+
)
|
| 171 |
+
self.conv2 = CausalConv3d(
|
| 172 |
+
in_channels,
|
| 173 |
+
in_channels,
|
| 174 |
+
kernel_size=(1, 3, 3),
|
| 175 |
+
stride=1,
|
| 176 |
+
time_stride=1,
|
| 177 |
+
padding=1,
|
| 178 |
+
)
|
| 179 |
+
self.conv3 = CausalConv3d(
|
| 180 |
+
in_channels,
|
| 181 |
+
in_channels,
|
| 182 |
+
kernel_size=1,
|
| 183 |
+
stride=1,
|
| 184 |
+
time_stride=1,
|
| 185 |
+
padding=0,
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 189 |
+
if not self.spatial_up and not self.temporal_up:
|
| 190 |
+
return x
|
| 191 |
+
|
| 192 |
+
# hybrid upsample temporally.
|
| 193 |
+
if self.temporal_up:
|
| 194 |
+
time_factor = 1.0 + 1.0 * (x.shape[2] > 1)
|
| 195 |
+
if isinstance(time_factor, torch.Tensor):
|
| 196 |
+
time_factor = time_factor.item()
|
| 197 |
+
x = x.repeat_interleave(int(time_factor), dim=2)
|
| 198 |
+
x = x[..., int(time_factor - 1) :, :, :]
|
| 199 |
+
x = self.conv1(x) + x
|
| 200 |
+
|
| 201 |
+
# hybrid upsample spatially.
|
| 202 |
+
if self.spatial_up:
|
| 203 |
+
x = x.repeat_interleave(2, dim=3).repeat_interleave(2, dim=4)
|
| 204 |
+
x = self.conv2(x) + x
|
| 205 |
+
|
| 206 |
+
# final 1x1x1 conv.
|
| 207 |
+
x = self.conv3(x)
|
| 208 |
+
return x
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
class CausalHybridDownsample3d(nn.Module):
|
| 212 |
+
def __init__(
|
| 213 |
+
self,
|
| 214 |
+
in_channels: int,
|
| 215 |
+
spatial_down: bool = True,
|
| 216 |
+
temporal_down: bool = True,
|
| 217 |
+
**kwargs,
|
| 218 |
+
) -> None:
|
| 219 |
+
super().__init__()
|
| 220 |
+
self.spatial_down = spatial_down
|
| 221 |
+
self.temporal_down = temporal_down
|
| 222 |
+
if not self.spatial_down and not self.temporal_down:
|
| 223 |
+
return
|
| 224 |
+
|
| 225 |
+
self.conv1 = CausalConv3d(
|
| 226 |
+
in_channels,
|
| 227 |
+
in_channels,
|
| 228 |
+
kernel_size=(1, 3, 3),
|
| 229 |
+
stride=2,
|
| 230 |
+
time_stride=1,
|
| 231 |
+
padding=0,
|
| 232 |
+
)
|
| 233 |
+
self.conv2 = CausalConv3d(
|
| 234 |
+
in_channels,
|
| 235 |
+
in_channels,
|
| 236 |
+
kernel_size=(3, 1, 1),
|
| 237 |
+
stride=1,
|
| 238 |
+
time_stride=2,
|
| 239 |
+
padding=0,
|
| 240 |
+
)
|
| 241 |
+
self.conv3 = CausalConv3d(
|
| 242 |
+
in_channels,
|
| 243 |
+
in_channels,
|
| 244 |
+
kernel_size=1,
|
| 245 |
+
stride=1,
|
| 246 |
+
time_stride=1,
|
| 247 |
+
padding=0,
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 252 |
+
if not self.spatial_down and not self.temporal_down:
|
| 253 |
+
return x
|
| 254 |
+
|
| 255 |
+
# hybrid downsample spatially.
|
| 256 |
+
if self.spatial_down:
|
| 257 |
+
pad = (0, 1, 0, 1, 0, 0)
|
| 258 |
+
x = F.pad(x, pad, mode="constant", value=0)
|
| 259 |
+
x1 = self.conv1(x)
|
| 260 |
+
x2 = F.avg_pool3d(x, kernel_size=(1, 2, 2), stride=(1, 2, 2))
|
| 261 |
+
x = x1 + x2
|
| 262 |
+
|
| 263 |
+
# hybrid downsample temporally.
|
| 264 |
+
if self.temporal_down:
|
| 265 |
+
x = replication_pad(x)
|
| 266 |
+
x1 = self.conv2(x)
|
| 267 |
+
x2 = F.avg_pool3d(x, kernel_size=(2, 1, 1), stride=(2, 1, 1))
|
| 268 |
+
x = x1 + x2
|
| 269 |
+
|
| 270 |
+
# final 1x1x1 conv.
|
| 271 |
+
x = self.conv3(x)
|
| 272 |
+
return x
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
class CausalResnetBlock3d(nn.Module):
|
| 276 |
+
def __init__(
|
| 277 |
+
self,
|
| 278 |
+
*,
|
| 279 |
+
in_channels: int,
|
| 280 |
+
out_channels: int = None,
|
| 281 |
+
dropout: float,
|
| 282 |
+
num_groups: int,
|
| 283 |
+
) -> None:
|
| 284 |
+
super().__init__()
|
| 285 |
+
self.in_channels = in_channels
|
| 286 |
+
out_channels = in_channels if out_channels is None else out_channels
|
| 287 |
+
|
| 288 |
+
self.norm1 = CausalNormalize(in_channels, num_groups=num_groups)
|
| 289 |
+
self.conv1 = CausalConv3d(
|
| 290 |
+
in_channels, out_channels, kernel_size=3, stride=1, padding=1
|
| 291 |
+
)
|
| 292 |
+
self.norm2 = CausalNormalize(out_channels, num_groups=num_groups)
|
| 293 |
+
self.dropout = torch.nn.Dropout(dropout)
|
| 294 |
+
self.conv2 = CausalConv3d(
|
| 295 |
+
out_channels, out_channels, kernel_size=3, stride=1, padding=1
|
| 296 |
+
)
|
| 297 |
+
self.nin_shortcut = (
|
| 298 |
+
CausalConv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
|
| 299 |
+
if in_channels != out_channels
|
| 300 |
+
else nn.Identity()
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 304 |
+
h = x
|
| 305 |
+
h = self.norm1(h)
|
| 306 |
+
h = nonlinearity(h)
|
| 307 |
+
h = self.conv1(h)
|
| 308 |
+
|
| 309 |
+
h = self.norm2(h)
|
| 310 |
+
h = nonlinearity(h)
|
| 311 |
+
h = self.dropout(h)
|
| 312 |
+
h = self.conv2(h)
|
| 313 |
+
x = self.nin_shortcut(x)
|
| 314 |
+
|
| 315 |
+
return x + h
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
class CausalResnetBlockFactorized3d(nn.Module):
|
| 319 |
+
def __init__(
|
| 320 |
+
self,
|
| 321 |
+
*,
|
| 322 |
+
in_channels: int,
|
| 323 |
+
out_channels: int = None,
|
| 324 |
+
dropout: float,
|
| 325 |
+
num_groups: int,
|
| 326 |
+
) -> None:
|
| 327 |
+
super().__init__()
|
| 328 |
+
self.in_channels = in_channels
|
| 329 |
+
out_channels = in_channels if out_channels is None else out_channels
|
| 330 |
+
|
| 331 |
+
self.norm1 = CausalNormalize(in_channels, num_groups=1)
|
| 332 |
+
self.conv1 = nn.Sequential(
|
| 333 |
+
CausalConv3d(
|
| 334 |
+
in_channels,
|
| 335 |
+
out_channels,
|
| 336 |
+
kernel_size=(1, 3, 3),
|
| 337 |
+
stride=1,
|
| 338 |
+
padding=1,
|
| 339 |
+
),
|
| 340 |
+
CausalConv3d(
|
| 341 |
+
out_channels,
|
| 342 |
+
out_channels,
|
| 343 |
+
kernel_size=(3, 1, 1),
|
| 344 |
+
stride=1,
|
| 345 |
+
padding=0,
|
| 346 |
+
),
|
| 347 |
+
)
|
| 348 |
+
self.norm2 = CausalNormalize(out_channels, num_groups=num_groups)
|
| 349 |
+
self.dropout = torch.nn.Dropout(dropout)
|
| 350 |
+
self.conv2 = nn.Sequential(
|
| 351 |
+
CausalConv3d(
|
| 352 |
+
out_channels,
|
| 353 |
+
out_channels,
|
| 354 |
+
kernel_size=(1, 3, 3),
|
| 355 |
+
stride=1,
|
| 356 |
+
padding=1,
|
| 357 |
+
),
|
| 358 |
+
CausalConv3d(
|
| 359 |
+
out_channels,
|
| 360 |
+
out_channels,
|
| 361 |
+
kernel_size=(3, 1, 1),
|
| 362 |
+
stride=1,
|
| 363 |
+
padding=0,
|
| 364 |
+
),
|
| 365 |
+
)
|
| 366 |
+
self.nin_shortcut = (
|
| 367 |
+
CausalConv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
|
| 368 |
+
if in_channels != out_channels
|
| 369 |
+
else nn.Identity()
|
| 370 |
+
)
|
| 371 |
+
|
| 372 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 373 |
+
h = x
|
| 374 |
+
h = self.norm1(h)
|
| 375 |
+
h = nonlinearity(h)
|
| 376 |
+
h = self.conv1(h)
|
| 377 |
+
|
| 378 |
+
h = self.norm2(h)
|
| 379 |
+
h = nonlinearity(h)
|
| 380 |
+
h = self.dropout(h)
|
| 381 |
+
h = self.conv2(h)
|
| 382 |
+
x = self.nin_shortcut(x)
|
| 383 |
+
|
| 384 |
+
return x + h
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
class CausalAttnBlock(nn.Module):
|
| 388 |
+
def __init__(self, in_channels: int, num_groups: int) -> None:
|
| 389 |
+
super().__init__()
|
| 390 |
+
|
| 391 |
+
self.norm = CausalNormalize(in_channels, num_groups=num_groups)
|
| 392 |
+
self.q = CausalConv3d(
|
| 393 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 394 |
+
)
|
| 395 |
+
self.k = CausalConv3d(
|
| 396 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 397 |
+
)
|
| 398 |
+
self.v = CausalConv3d(
|
| 399 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 400 |
+
)
|
| 401 |
+
self.proj_out = CausalConv3d(
|
| 402 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
self.optimized_attention = vae_attention()
|
| 406 |
+
|
| 407 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 408 |
+
h_ = x
|
| 409 |
+
h_ = self.norm(h_)
|
| 410 |
+
q = self.q(h_)
|
| 411 |
+
k = self.k(h_)
|
| 412 |
+
v = self.v(h_)
|
| 413 |
+
|
| 414 |
+
# compute attention
|
| 415 |
+
q, batch_size = time2batch(q)
|
| 416 |
+
k, batch_size = time2batch(k)
|
| 417 |
+
v, batch_size = time2batch(v)
|
| 418 |
+
|
| 419 |
+
b, c, h, w = q.shape
|
| 420 |
+
h_ = self.optimized_attention(q, k, v)
|
| 421 |
+
|
| 422 |
+
h_ = batch2time(h_, batch_size)
|
| 423 |
+
h_ = self.proj_out(h_)
|
| 424 |
+
return x + h_
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
class CausalTemporalAttnBlock(nn.Module):
|
| 428 |
+
def __init__(self, in_channels: int, num_groups: int) -> None:
|
| 429 |
+
super().__init__()
|
| 430 |
+
|
| 431 |
+
self.norm = CausalNormalize(in_channels, num_groups=num_groups)
|
| 432 |
+
self.q = CausalConv3d(
|
| 433 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 434 |
+
)
|
| 435 |
+
self.k = CausalConv3d(
|
| 436 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 437 |
+
)
|
| 438 |
+
self.v = CausalConv3d(
|
| 439 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 440 |
+
)
|
| 441 |
+
self.proj_out = CausalConv3d(
|
| 442 |
+
in_channels, in_channels, kernel_size=1, stride=1, padding=0
|
| 443 |
+
)
|
| 444 |
+
|
| 445 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 446 |
+
h_ = x
|
| 447 |
+
h_ = self.norm(h_)
|
| 448 |
+
q = self.q(h_)
|
| 449 |
+
k = self.k(h_)
|
| 450 |
+
v = self.v(h_)
|
| 451 |
+
|
| 452 |
+
# compute attention
|
| 453 |
+
q, batch_size, height = space2batch(q)
|
| 454 |
+
k, _, _ = space2batch(k)
|
| 455 |
+
v, _, _ = space2batch(v)
|
| 456 |
+
|
| 457 |
+
bhw, c, t = q.shape
|
| 458 |
+
q = q.permute(0, 2, 1) # (bhw, t, c)
|
| 459 |
+
k = k.permute(0, 2, 1) # (bhw, t, c)
|
| 460 |
+
v = v.permute(0, 2, 1) # (bhw, t, c)
|
| 461 |
+
|
| 462 |
+
w_ = torch.bmm(q, k.permute(0, 2, 1)) # (bhw, t, t)
|
| 463 |
+
w_ = w_ * (int(c) ** (-0.5))
|
| 464 |
+
|
| 465 |
+
# Apply causal mask
|
| 466 |
+
mask = torch.tril(torch.ones_like(w_))
|
| 467 |
+
w_ = w_.masked_fill(mask == 0, float("-inf"))
|
| 468 |
+
w_ = F.softmax(w_, dim=2)
|
| 469 |
+
|
| 470 |
+
# attend to values
|
| 471 |
+
h_ = torch.bmm(w_, v) # (bhw, t, c)
|
| 472 |
+
h_ = h_.permute(0, 2, 1).reshape(bhw, c, t) # (bhw, c, t)
|
| 473 |
+
|
| 474 |
+
h_ = batch2space(h_, batch_size, height)
|
| 475 |
+
h_ = self.proj_out(h_)
|
| 476 |
+
return x + h_
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
class EncoderBase(nn.Module):
|
| 480 |
+
def __init__(
|
| 481 |
+
self,
|
| 482 |
+
in_channels: int,
|
| 483 |
+
channels: int,
|
| 484 |
+
channels_mult: list[int],
|
| 485 |
+
num_res_blocks: int,
|
| 486 |
+
attn_resolutions: list[int],
|
| 487 |
+
dropout: float,
|
| 488 |
+
resolution: int,
|
| 489 |
+
z_channels: int,
|
| 490 |
+
**ignore_kwargs,
|
| 491 |
+
) -> None:
|
| 492 |
+
super().__init__()
|
| 493 |
+
self.num_resolutions = len(channels_mult)
|
| 494 |
+
self.num_res_blocks = num_res_blocks
|
| 495 |
+
|
| 496 |
+
# Patcher.
|
| 497 |
+
patch_size = ignore_kwargs.get("patch_size", 1)
|
| 498 |
+
self.patcher = Patcher(
|
| 499 |
+
patch_size, ignore_kwargs.get("patch_method", "rearrange")
|
| 500 |
+
)
|
| 501 |
+
in_channels = in_channels * patch_size * patch_size
|
| 502 |
+
|
| 503 |
+
# downsampling
|
| 504 |
+
self.conv_in = CausalConv3d(
|
| 505 |
+
in_channels, channels, kernel_size=3, stride=1, padding=1
|
| 506 |
+
)
|
| 507 |
+
|
| 508 |
+
# num of groups for GroupNorm, num_groups=1 for LayerNorm.
|
| 509 |
+
num_groups = ignore_kwargs.get("num_groups", _LEGACY_NUM_GROUPS)
|
| 510 |
+
curr_res = resolution // patch_size
|
| 511 |
+
in_ch_mult = (1,) + tuple(channels_mult)
|
| 512 |
+
self.in_ch_mult = in_ch_mult
|
| 513 |
+
self.down = nn.ModuleList()
|
| 514 |
+
for i_level in range(self.num_resolutions):
|
| 515 |
+
block = nn.ModuleList()
|
| 516 |
+
attn = nn.ModuleList()
|
| 517 |
+
block_in = channels * in_ch_mult[i_level]
|
| 518 |
+
block_out = channels * channels_mult[i_level]
|
| 519 |
+
for _ in range(self.num_res_blocks):
|
| 520 |
+
block.append(
|
| 521 |
+
CausalResnetBlock3d(
|
| 522 |
+
in_channels=block_in,
|
| 523 |
+
out_channels=block_out,
|
| 524 |
+
dropout=dropout,
|
| 525 |
+
num_groups=num_groups,
|
| 526 |
+
)
|
| 527 |
+
)
|
| 528 |
+
block_in = block_out
|
| 529 |
+
if curr_res in attn_resolutions:
|
| 530 |
+
attn.append(CausalAttnBlock(block_in, num_groups=num_groups))
|
| 531 |
+
down = nn.Module()
|
| 532 |
+
down.block = block
|
| 533 |
+
down.attn = attn
|
| 534 |
+
if i_level != self.num_resolutions - 1:
|
| 535 |
+
down.downsample = CausalDownsample3d(block_in)
|
| 536 |
+
curr_res = curr_res // 2
|
| 537 |
+
self.down.append(down)
|
| 538 |
+
|
| 539 |
+
# middle
|
| 540 |
+
self.mid = nn.Module()
|
| 541 |
+
self.mid.block_1 = CausalResnetBlock3d(
|
| 542 |
+
in_channels=block_in,
|
| 543 |
+
out_channels=block_in,
|
| 544 |
+
dropout=dropout,
|
| 545 |
+
num_groups=num_groups,
|
| 546 |
+
)
|
| 547 |
+
self.mid.attn_1 = CausalAttnBlock(block_in, num_groups=num_groups)
|
| 548 |
+
self.mid.block_2 = CausalResnetBlock3d(
|
| 549 |
+
in_channels=block_in,
|
| 550 |
+
out_channels=block_in,
|
| 551 |
+
dropout=dropout,
|
| 552 |
+
num_groups=num_groups,
|
| 553 |
+
)
|
| 554 |
+
|
| 555 |
+
# end
|
| 556 |
+
self.norm_out = CausalNormalize(block_in, num_groups=num_groups)
|
| 557 |
+
self.conv_out = CausalConv3d(
|
| 558 |
+
block_in, z_channels, kernel_size=3, stride=1, padding=1
|
| 559 |
+
)
|
| 560 |
+
|
| 561 |
+
def patcher3d(self, x: torch.Tensor) -> torch.Tensor:
|
| 562 |
+
x, batch_size = time2batch(x)
|
| 563 |
+
x = self.patcher(x)
|
| 564 |
+
x = batch2time(x, batch_size)
|
| 565 |
+
return x
|
| 566 |
+
|
| 567 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 568 |
+
x = self.patcher3d(x)
|
| 569 |
+
|
| 570 |
+
# downsampling
|
| 571 |
+
hs = [self.conv_in(x)]
|
| 572 |
+
for i_level in range(self.num_resolutions):
|
| 573 |
+
for i_block in range(self.num_res_blocks):
|
| 574 |
+
h = self.down[i_level].block[i_block](hs[-1])
|
| 575 |
+
if len(self.down[i_level].attn) > 0:
|
| 576 |
+
h = self.down[i_level].attn[i_block](h)
|
| 577 |
+
hs.append(h)
|
| 578 |
+
if i_level != self.num_resolutions - 1:
|
| 579 |
+
hs.append(self.down[i_level].downsample(hs[-1]))
|
| 580 |
+
else:
|
| 581 |
+
# temporal downsample (last level)
|
| 582 |
+
time_factor = 1 + 1 * (hs[-1].shape[2] > 1)
|
| 583 |
+
if isinstance(time_factor, torch.Tensor):
|
| 584 |
+
time_factor = time_factor.item()
|
| 585 |
+
hs[-1] = replication_pad(hs[-1])
|
| 586 |
+
hs.append(
|
| 587 |
+
F.avg_pool3d(
|
| 588 |
+
hs[-1],
|
| 589 |
+
kernel_size=[time_factor, 1, 1],
|
| 590 |
+
stride=[2, 1, 1],
|
| 591 |
+
)
|
| 592 |
+
)
|
| 593 |
+
|
| 594 |
+
# middle
|
| 595 |
+
h = hs[-1]
|
| 596 |
+
h = self.mid.block_1(h)
|
| 597 |
+
h = self.mid.attn_1(h)
|
| 598 |
+
h = self.mid.block_2(h)
|
| 599 |
+
|
| 600 |
+
# end
|
| 601 |
+
h = self.norm_out(h)
|
| 602 |
+
h = nonlinearity(h)
|
| 603 |
+
h = self.conv_out(h)
|
| 604 |
+
return h
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
class DecoderBase(nn.Module):
|
| 608 |
+
def __init__(
|
| 609 |
+
self,
|
| 610 |
+
out_channels: int,
|
| 611 |
+
channels: int,
|
| 612 |
+
channels_mult: list[int],
|
| 613 |
+
num_res_blocks: int,
|
| 614 |
+
attn_resolutions: list[int],
|
| 615 |
+
dropout: float,
|
| 616 |
+
resolution: int,
|
| 617 |
+
z_channels: int,
|
| 618 |
+
**ignore_kwargs,
|
| 619 |
+
):
|
| 620 |
+
super().__init__()
|
| 621 |
+
self.num_resolutions = len(channels_mult)
|
| 622 |
+
self.num_res_blocks = num_res_blocks
|
| 623 |
+
|
| 624 |
+
# UnPatcher.
|
| 625 |
+
patch_size = ignore_kwargs.get("patch_size", 1)
|
| 626 |
+
self.unpatcher = UnPatcher(
|
| 627 |
+
patch_size, ignore_kwargs.get("patch_method", "rearrange")
|
| 628 |
+
)
|
| 629 |
+
out_ch = out_channels * patch_size * patch_size
|
| 630 |
+
|
| 631 |
+
block_in = channels * channels_mult[self.num_resolutions - 1]
|
| 632 |
+
curr_res = (resolution // patch_size) // 2 ** (self.num_resolutions - 1)
|
| 633 |
+
self.z_shape = (1, z_channels, curr_res, curr_res)
|
| 634 |
+
logging.debug(
|
| 635 |
+
"Working with z of shape {} = {} dimensions.".format(
|
| 636 |
+
self.z_shape, np.prod(self.z_shape)
|
| 637 |
+
)
|
| 638 |
+
)
|
| 639 |
+
|
| 640 |
+
# z to block_in
|
| 641 |
+
self.conv_in = CausalConv3d(
|
| 642 |
+
z_channels, block_in, kernel_size=3, stride=1, padding=1
|
| 643 |
+
)
|
| 644 |
+
|
| 645 |
+
# num of groups for GroupNorm, num_groups=1 for LayerNorm.
|
| 646 |
+
num_groups = ignore_kwargs.get("num_groups", _LEGACY_NUM_GROUPS)
|
| 647 |
+
|
| 648 |
+
# middle
|
| 649 |
+
self.mid = nn.Module()
|
| 650 |
+
self.mid.block_1 = CausalResnetBlock3d(
|
| 651 |
+
in_channels=block_in,
|
| 652 |
+
out_channels=block_in,
|
| 653 |
+
dropout=dropout,
|
| 654 |
+
num_groups=num_groups,
|
| 655 |
+
)
|
| 656 |
+
self.mid.attn_1 = CausalAttnBlock(block_in, num_groups=num_groups)
|
| 657 |
+
self.mid.block_2 = CausalResnetBlock3d(
|
| 658 |
+
in_channels=block_in,
|
| 659 |
+
out_channels=block_in,
|
| 660 |
+
dropout=dropout,
|
| 661 |
+
num_groups=num_groups,
|
| 662 |
+
)
|
| 663 |
+
|
| 664 |
+
# upsampling
|
| 665 |
+
self.up = nn.ModuleList()
|
| 666 |
+
for i_level in reversed(range(self.num_resolutions)):
|
| 667 |
+
block = nn.ModuleList()
|
| 668 |
+
attn = nn.ModuleList()
|
| 669 |
+
block_out = channels * channels_mult[i_level]
|
| 670 |
+
for _ in range(self.num_res_blocks + 1):
|
| 671 |
+
block.append(
|
| 672 |
+
CausalResnetBlock3d(
|
| 673 |
+
in_channels=block_in,
|
| 674 |
+
out_channels=block_out,
|
| 675 |
+
dropout=dropout,
|
| 676 |
+
num_groups=num_groups,
|
| 677 |
+
)
|
| 678 |
+
)
|
| 679 |
+
block_in = block_out
|
| 680 |
+
if curr_res in attn_resolutions:
|
| 681 |
+
attn.append(CausalAttnBlock(block_in, num_groups=num_groups))
|
| 682 |
+
up = nn.Module()
|
| 683 |
+
up.block = block
|
| 684 |
+
up.attn = attn
|
| 685 |
+
if i_level != 0:
|
| 686 |
+
up.upsample = CausalUpsample3d(block_in)
|
| 687 |
+
curr_res = curr_res * 2
|
| 688 |
+
self.up.insert(0, up) # prepend to get consistent order
|
| 689 |
+
|
| 690 |
+
# end
|
| 691 |
+
self.norm_out = CausalNormalize(block_in, num_groups=num_groups)
|
| 692 |
+
self.conv_out = CausalConv3d(
|
| 693 |
+
block_in, out_ch, kernel_size=3, stride=1, padding=1
|
| 694 |
+
)
|
| 695 |
+
|
| 696 |
+
def unpatcher3d(self, x: torch.Tensor) -> torch.Tensor:
|
| 697 |
+
x, batch_size = time2batch(x)
|
| 698 |
+
x = self.unpatcher(x)
|
| 699 |
+
x = batch2time(x, batch_size)
|
| 700 |
+
|
| 701 |
+
return x
|
| 702 |
+
|
| 703 |
+
def forward(self, z):
|
| 704 |
+
h = self.conv_in(z)
|
| 705 |
+
|
| 706 |
+
# middle block.
|
| 707 |
+
h = self.mid.block_1(h)
|
| 708 |
+
h = self.mid.attn_1(h)
|
| 709 |
+
h = self.mid.block_2(h)
|
| 710 |
+
|
| 711 |
+
# decoder blocks.
|
| 712 |
+
for i_level in reversed(range(self.num_resolutions)):
|
| 713 |
+
for i_block in range(self.num_res_blocks + 1):
|
| 714 |
+
h = self.up[i_level].block[i_block](h)
|
| 715 |
+
if len(self.up[i_level].attn) > 0:
|
| 716 |
+
h = self.up[i_level].attn[i_block](h)
|
| 717 |
+
if i_level != 0:
|
| 718 |
+
h = self.up[i_level].upsample(h)
|
| 719 |
+
else:
|
| 720 |
+
# temporal upsample (last level)
|
| 721 |
+
time_factor = 1.0 + 1.0 * (h.shape[2] > 1)
|
| 722 |
+
if isinstance(time_factor, torch.Tensor):
|
| 723 |
+
time_factor = time_factor.item()
|
| 724 |
+
h = h.repeat_interleave(int(time_factor), dim=2)
|
| 725 |
+
h = h[..., int(time_factor - 1) :, :, :]
|
| 726 |
+
|
| 727 |
+
h = self.norm_out(h)
|
| 728 |
+
h = nonlinearity(h)
|
| 729 |
+
h = self.conv_out(h)
|
| 730 |
+
h = self.unpatcher3d(h)
|
| 731 |
+
return h
|
| 732 |
+
|
| 733 |
+
|
| 734 |
+
class EncoderFactorized(nn.Module):
|
| 735 |
+
def __init__(
|
| 736 |
+
self,
|
| 737 |
+
in_channels: int,
|
| 738 |
+
channels: int,
|
| 739 |
+
channels_mult: list[int],
|
| 740 |
+
num_res_blocks: int,
|
| 741 |
+
attn_resolutions: list[int],
|
| 742 |
+
dropout: float,
|
| 743 |
+
resolution: int,
|
| 744 |
+
z_channels: int,
|
| 745 |
+
spatial_compression: int = 8,
|
| 746 |
+
temporal_compression: int = 8,
|
| 747 |
+
**ignore_kwargs,
|
| 748 |
+
) -> None:
|
| 749 |
+
super().__init__()
|
| 750 |
+
self.num_resolutions = len(channels_mult)
|
| 751 |
+
self.num_res_blocks = num_res_blocks
|
| 752 |
+
|
| 753 |
+
# Patcher.
|
| 754 |
+
patch_size = ignore_kwargs.get("patch_size", 1)
|
| 755 |
+
self.patcher3d = Patcher3D(
|
| 756 |
+
patch_size, ignore_kwargs.get("patch_method", "haar")
|
| 757 |
+
)
|
| 758 |
+
in_channels = in_channels * patch_size * patch_size * patch_size
|
| 759 |
+
|
| 760 |
+
# calculate the number of downsample operations
|
| 761 |
+
self.num_spatial_downs = int(math.log2(spatial_compression)) - int(
|
| 762 |
+
math.log2(patch_size)
|
| 763 |
+
)
|
| 764 |
+
assert (
|
| 765 |
+
self.num_spatial_downs <= self.num_resolutions
|
| 766 |
+
), f"Spatially downsample {self.num_resolutions} times at most"
|
| 767 |
+
|
| 768 |
+
self.num_temporal_downs = int(math.log2(temporal_compression)) - int(
|
| 769 |
+
math.log2(patch_size)
|
| 770 |
+
)
|
| 771 |
+
assert (
|
| 772 |
+
self.num_temporal_downs <= self.num_resolutions
|
| 773 |
+
), f"Temporally downsample {self.num_resolutions} times at most"
|
| 774 |
+
|
| 775 |
+
# downsampling
|
| 776 |
+
self.conv_in = nn.Sequential(
|
| 777 |
+
CausalConv3d(
|
| 778 |
+
in_channels,
|
| 779 |
+
channels,
|
| 780 |
+
kernel_size=(1, 3, 3),
|
| 781 |
+
stride=1,
|
| 782 |
+
padding=1,
|
| 783 |
+
),
|
| 784 |
+
CausalConv3d(
|
| 785 |
+
channels, channels, kernel_size=(3, 1, 1), stride=1, padding=0
|
| 786 |
+
),
|
| 787 |
+
)
|
| 788 |
+
|
| 789 |
+
curr_res = resolution // patch_size
|
| 790 |
+
in_ch_mult = (1,) + tuple(channels_mult)
|
| 791 |
+
self.in_ch_mult = in_ch_mult
|
| 792 |
+
self.down = nn.ModuleList()
|
| 793 |
+
for i_level in range(self.num_resolutions):
|
| 794 |
+
block = nn.ModuleList()
|
| 795 |
+
attn = nn.ModuleList()
|
| 796 |
+
block_in = channels * in_ch_mult[i_level]
|
| 797 |
+
block_out = channels * channels_mult[i_level]
|
| 798 |
+
for _ in range(self.num_res_blocks):
|
| 799 |
+
block.append(
|
| 800 |
+
CausalResnetBlockFactorized3d(
|
| 801 |
+
in_channels=block_in,
|
| 802 |
+
out_channels=block_out,
|
| 803 |
+
dropout=dropout,
|
| 804 |
+
num_groups=1,
|
| 805 |
+
)
|
| 806 |
+
)
|
| 807 |
+
block_in = block_out
|
| 808 |
+
if curr_res in attn_resolutions:
|
| 809 |
+
attn.append(
|
| 810 |
+
nn.Sequential(
|
| 811 |
+
CausalAttnBlock(block_in, num_groups=1),
|
| 812 |
+
CausalTemporalAttnBlock(block_in, num_groups=1),
|
| 813 |
+
)
|
| 814 |
+
)
|
| 815 |
+
down = nn.Module()
|
| 816 |
+
down.block = block
|
| 817 |
+
down.attn = attn
|
| 818 |
+
if i_level != self.num_resolutions - 1:
|
| 819 |
+
spatial_down = i_level < self.num_spatial_downs
|
| 820 |
+
temporal_down = i_level < self.num_temporal_downs
|
| 821 |
+
down.downsample = CausalHybridDownsample3d(
|
| 822 |
+
block_in,
|
| 823 |
+
spatial_down=spatial_down,
|
| 824 |
+
temporal_down=temporal_down,
|
| 825 |
+
)
|
| 826 |
+
curr_res = curr_res // 2
|
| 827 |
+
self.down.append(down)
|
| 828 |
+
|
| 829 |
+
# middle
|
| 830 |
+
self.mid = nn.Module()
|
| 831 |
+
self.mid.block_1 = CausalResnetBlockFactorized3d(
|
| 832 |
+
in_channels=block_in,
|
| 833 |
+
out_channels=block_in,
|
| 834 |
+
dropout=dropout,
|
| 835 |
+
num_groups=1,
|
| 836 |
+
)
|
| 837 |
+
self.mid.attn_1 = nn.Sequential(
|
| 838 |
+
CausalAttnBlock(block_in, num_groups=1),
|
| 839 |
+
CausalTemporalAttnBlock(block_in, num_groups=1),
|
| 840 |
+
)
|
| 841 |
+
self.mid.block_2 = CausalResnetBlockFactorized3d(
|
| 842 |
+
in_channels=block_in,
|
| 843 |
+
out_channels=block_in,
|
| 844 |
+
dropout=dropout,
|
| 845 |
+
num_groups=1,
|
| 846 |
+
)
|
| 847 |
+
|
| 848 |
+
# end
|
| 849 |
+
self.norm_out = CausalNormalize(block_in, num_groups=1)
|
| 850 |
+
self.conv_out = nn.Sequential(
|
| 851 |
+
CausalConv3d(
|
| 852 |
+
block_in, z_channels, kernel_size=(1, 3, 3), stride=1, padding=1
|
| 853 |
+
),
|
| 854 |
+
CausalConv3d(
|
| 855 |
+
z_channels,
|
| 856 |
+
z_channels,
|
| 857 |
+
kernel_size=(3, 1, 1),
|
| 858 |
+
stride=1,
|
| 859 |
+
padding=0,
|
| 860 |
+
),
|
| 861 |
+
)
|
| 862 |
+
|
| 863 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 864 |
+
x = self.patcher3d(x)
|
| 865 |
+
|
| 866 |
+
# downsampling
|
| 867 |
+
h = self.conv_in(x)
|
| 868 |
+
for i_level in range(self.num_resolutions):
|
| 869 |
+
for i_block in range(self.num_res_blocks):
|
| 870 |
+
h = self.down[i_level].block[i_block](h)
|
| 871 |
+
if len(self.down[i_level].attn) > 0:
|
| 872 |
+
h = self.down[i_level].attn[i_block](h)
|
| 873 |
+
if i_level != self.num_resolutions - 1:
|
| 874 |
+
h = self.down[i_level].downsample(h)
|
| 875 |
+
|
| 876 |
+
# middle
|
| 877 |
+
h = self.mid.block_1(h)
|
| 878 |
+
h = self.mid.attn_1(h)
|
| 879 |
+
h = self.mid.block_2(h)
|
| 880 |
+
|
| 881 |
+
# end
|
| 882 |
+
h = self.norm_out(h)
|
| 883 |
+
h = nonlinearity(h)
|
| 884 |
+
h = self.conv_out(h)
|
| 885 |
+
return h
|
| 886 |
+
|
| 887 |
+
|
| 888 |
+
class DecoderFactorized(nn.Module):
|
| 889 |
+
def __init__(
|
| 890 |
+
self,
|
| 891 |
+
out_channels: int,
|
| 892 |
+
channels: int,
|
| 893 |
+
channels_mult: list[int],
|
| 894 |
+
num_res_blocks: int,
|
| 895 |
+
attn_resolutions: list[int],
|
| 896 |
+
dropout: float,
|
| 897 |
+
resolution: int,
|
| 898 |
+
z_channels: int,
|
| 899 |
+
spatial_compression: int = 8,
|
| 900 |
+
temporal_compression: int = 8,
|
| 901 |
+
**ignore_kwargs,
|
| 902 |
+
):
|
| 903 |
+
super().__init__()
|
| 904 |
+
self.num_resolutions = len(channels_mult)
|
| 905 |
+
self.num_res_blocks = num_res_blocks
|
| 906 |
+
|
| 907 |
+
# UnPatcher.
|
| 908 |
+
patch_size = ignore_kwargs.get("patch_size", 1)
|
| 909 |
+
self.unpatcher3d = UnPatcher3D(
|
| 910 |
+
patch_size, ignore_kwargs.get("patch_method", "haar")
|
| 911 |
+
)
|
| 912 |
+
out_ch = out_channels * patch_size * patch_size * patch_size
|
| 913 |
+
|
| 914 |
+
# calculate the number of upsample operations
|
| 915 |
+
self.num_spatial_ups = int(math.log2(spatial_compression)) - int(
|
| 916 |
+
math.log2(patch_size)
|
| 917 |
+
)
|
| 918 |
+
assert (
|
| 919 |
+
self.num_spatial_ups <= self.num_resolutions
|
| 920 |
+
), f"Spatially upsample {self.num_resolutions} times at most"
|
| 921 |
+
self.num_temporal_ups = int(math.log2(temporal_compression)) - int(
|
| 922 |
+
math.log2(patch_size)
|
| 923 |
+
)
|
| 924 |
+
assert (
|
| 925 |
+
self.num_temporal_ups <= self.num_resolutions
|
| 926 |
+
), f"Temporally upsample {self.num_resolutions} times at most"
|
| 927 |
+
|
| 928 |
+
block_in = channels * channels_mult[self.num_resolutions - 1]
|
| 929 |
+
curr_res = (resolution // patch_size) // 2 ** (self.num_resolutions - 1)
|
| 930 |
+
self.z_shape = (1, z_channels, curr_res, curr_res)
|
| 931 |
+
logging.debug(
|
| 932 |
+
"Working with z of shape {} = {} dimensions.".format(
|
| 933 |
+
self.z_shape, np.prod(self.z_shape)
|
| 934 |
+
)
|
| 935 |
+
)
|
| 936 |
+
|
| 937 |
+
# z to block_in
|
| 938 |
+
self.conv_in = nn.Sequential(
|
| 939 |
+
CausalConv3d(
|
| 940 |
+
z_channels, block_in, kernel_size=(1, 3, 3), stride=1, padding=1
|
| 941 |
+
),
|
| 942 |
+
CausalConv3d(
|
| 943 |
+
block_in, block_in, kernel_size=(3, 1, 1), stride=1, padding=0
|
| 944 |
+
),
|
| 945 |
+
)
|
| 946 |
+
|
| 947 |
+
# middle
|
| 948 |
+
self.mid = nn.Module()
|
| 949 |
+
self.mid.block_1 = CausalResnetBlockFactorized3d(
|
| 950 |
+
in_channels=block_in,
|
| 951 |
+
out_channels=block_in,
|
| 952 |
+
dropout=dropout,
|
| 953 |
+
num_groups=1,
|
| 954 |
+
)
|
| 955 |
+
self.mid.attn_1 = nn.Sequential(
|
| 956 |
+
CausalAttnBlock(block_in, num_groups=1),
|
| 957 |
+
CausalTemporalAttnBlock(block_in, num_groups=1),
|
| 958 |
+
)
|
| 959 |
+
self.mid.block_2 = CausalResnetBlockFactorized3d(
|
| 960 |
+
in_channels=block_in,
|
| 961 |
+
out_channels=block_in,
|
| 962 |
+
dropout=dropout,
|
| 963 |
+
num_groups=1,
|
| 964 |
+
)
|
| 965 |
+
|
| 966 |
+
legacy_mode = ignore_kwargs.get("legacy_mode", False)
|
| 967 |
+
# upsampling
|
| 968 |
+
self.up = nn.ModuleList()
|
| 969 |
+
for i_level in reversed(range(self.num_resolutions)):
|
| 970 |
+
block = nn.ModuleList()
|
| 971 |
+
attn = nn.ModuleList()
|
| 972 |
+
block_out = channels * channels_mult[i_level]
|
| 973 |
+
for _ in range(self.num_res_blocks + 1):
|
| 974 |
+
block.append(
|
| 975 |
+
CausalResnetBlockFactorized3d(
|
| 976 |
+
in_channels=block_in,
|
| 977 |
+
out_channels=block_out,
|
| 978 |
+
dropout=dropout,
|
| 979 |
+
num_groups=1,
|
| 980 |
+
)
|
| 981 |
+
)
|
| 982 |
+
block_in = block_out
|
| 983 |
+
if curr_res in attn_resolutions:
|
| 984 |
+
attn.append(
|
| 985 |
+
nn.Sequential(
|
| 986 |
+
CausalAttnBlock(block_in, num_groups=1),
|
| 987 |
+
CausalTemporalAttnBlock(block_in, num_groups=1),
|
| 988 |
+
)
|
| 989 |
+
)
|
| 990 |
+
up = nn.Module()
|
| 991 |
+
up.block = block
|
| 992 |
+
up.attn = attn
|
| 993 |
+
if i_level != 0:
|
| 994 |
+
# The layer index for temporal/spatial downsampling performed
|
| 995 |
+
# in the encoder should correspond to the layer index in
|
| 996 |
+
# reverse order where upsampling is performed in the decoder.
|
| 997 |
+
# If you've a pre-trained model, you can simply finetune.
|
| 998 |
+
i_level_reverse = self.num_resolutions - i_level - 1
|
| 999 |
+
if legacy_mode:
|
| 1000 |
+
temporal_up = i_level_reverse < self.num_temporal_ups
|
| 1001 |
+
else:
|
| 1002 |
+
temporal_up = 0 < i_level_reverse < self.num_temporal_ups + 1
|
| 1003 |
+
spatial_up = temporal_up or (
|
| 1004 |
+
i_level_reverse < self.num_spatial_ups
|
| 1005 |
+
and self.num_spatial_ups > self.num_temporal_ups
|
| 1006 |
+
)
|
| 1007 |
+
up.upsample = CausalHybridUpsample3d(
|
| 1008 |
+
block_in, spatial_up=spatial_up, temporal_up=temporal_up
|
| 1009 |
+
)
|
| 1010 |
+
curr_res = curr_res * 2
|
| 1011 |
+
self.up.insert(0, up) # prepend to get consistent order
|
| 1012 |
+
|
| 1013 |
+
# end
|
| 1014 |
+
self.norm_out = CausalNormalize(block_in, num_groups=1)
|
| 1015 |
+
self.conv_out = nn.Sequential(
|
| 1016 |
+
CausalConv3d(block_in, out_ch, kernel_size=(1, 3, 3), stride=1, padding=1),
|
| 1017 |
+
CausalConv3d(out_ch, out_ch, kernel_size=(3, 1, 1), stride=1, padding=0),
|
| 1018 |
+
)
|
| 1019 |
+
|
| 1020 |
+
def forward(self, z):
|
| 1021 |
+
h = self.conv_in(z)
|
| 1022 |
+
|
| 1023 |
+
# middle block.
|
| 1024 |
+
h = self.mid.block_1(h)
|
| 1025 |
+
h = self.mid.attn_1(h)
|
| 1026 |
+
h = self.mid.block_2(h)
|
| 1027 |
+
|
| 1028 |
+
# decoder blocks.
|
| 1029 |
+
for i_level in reversed(range(self.num_resolutions)):
|
| 1030 |
+
for i_block in range(self.num_res_blocks + 1):
|
| 1031 |
+
h = self.up[i_level].block[i_block](h)
|
| 1032 |
+
if len(self.up[i_level].attn) > 0:
|
| 1033 |
+
h = self.up[i_level].attn[i_block](h)
|
| 1034 |
+
if i_level != 0:
|
| 1035 |
+
h = self.up[i_level].upsample(h)
|
| 1036 |
+
|
| 1037 |
+
h = self.norm_out(h)
|
| 1038 |
+
h = nonlinearity(h)
|
| 1039 |
+
h = self.conv_out(h)
|
| 1040 |
+
h = self.unpatcher3d(h)
|
| 1041 |
+
return h
|
ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/patching.py
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""The patcher and unpatcher implementation for 2D and 3D data.
|
| 16 |
+
|
| 17 |
+
The idea of Haar wavelet is to compute LL, LH, HL, HH component as two 1D convolutions.
|
| 18 |
+
One on the rows and one on the columns.
|
| 19 |
+
For example, in 1D signal, we have [a, b], then the low-freq compoenent is [a + b] / 2 and high-freq is [a - b] / 2.
|
| 20 |
+
We can use a 1D convolution with kernel [1, 1] and stride 2 to represent the L component.
|
| 21 |
+
For H component, we can use a 1D convolution with kernel [1, -1] and stride 2.
|
| 22 |
+
Although in principle, we typically only do additional Haar wavelet over the LL component. But here we do it for all
|
| 23 |
+
as we need to support downsampling for more than 2x.
|
| 24 |
+
For example, 4x downsampling can be done by 2x Haar and additional 2x Haar, and the shape would be.
|
| 25 |
+
[3, 256, 256] -> [12, 128, 128] -> [48, 64, 64]
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
import torch
|
| 29 |
+
import torch.nn.functional as F
|
| 30 |
+
from einops import rearrange
|
| 31 |
+
|
| 32 |
+
_WAVELETS = {
|
| 33 |
+
"haar": torch.tensor([0.7071067811865476, 0.7071067811865476]),
|
| 34 |
+
"rearrange": torch.tensor([1.0, 1.0]),
|
| 35 |
+
}
|
| 36 |
+
_PERSISTENT = False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class Patcher(torch.nn.Module):
|
| 40 |
+
"""A module to convert image tensors into patches using torch operations.
|
| 41 |
+
|
| 42 |
+
The main difference from `class Patching` is that this module implements
|
| 43 |
+
all operations using torch, rather than python or numpy, for efficiency purpose.
|
| 44 |
+
|
| 45 |
+
It's bit-wise identical to the Patching module outputs, with the added
|
| 46 |
+
benefit of being torch.jit scriptable.
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
def __init__(self, patch_size=1, patch_method="haar"):
|
| 50 |
+
super().__init__()
|
| 51 |
+
self.patch_size = patch_size
|
| 52 |
+
self.patch_method = patch_method
|
| 53 |
+
self.register_buffer(
|
| 54 |
+
"wavelets", _WAVELETS[patch_method], persistent=_PERSISTENT
|
| 55 |
+
)
|
| 56 |
+
self.range = range(int(torch.log2(torch.tensor(self.patch_size)).item()))
|
| 57 |
+
self.register_buffer(
|
| 58 |
+
"_arange",
|
| 59 |
+
torch.arange(_WAVELETS[patch_method].shape[0]),
|
| 60 |
+
persistent=_PERSISTENT,
|
| 61 |
+
)
|
| 62 |
+
for param in self.parameters():
|
| 63 |
+
param.requires_grad = False
|
| 64 |
+
|
| 65 |
+
def forward(self, x):
|
| 66 |
+
if self.patch_method == "haar":
|
| 67 |
+
return self._haar(x)
|
| 68 |
+
elif self.patch_method == "rearrange":
|
| 69 |
+
return self._arrange(x)
|
| 70 |
+
else:
|
| 71 |
+
raise ValueError("Unknown patch method: " + self.patch_method)
|
| 72 |
+
|
| 73 |
+
def _dwt(self, x, mode="reflect", rescale=False):
|
| 74 |
+
dtype = x.dtype
|
| 75 |
+
h = self.wavelets.to(device=x.device)
|
| 76 |
+
|
| 77 |
+
n = h.shape[0]
|
| 78 |
+
g = x.shape[1]
|
| 79 |
+
hl = h.flip(0).reshape(1, 1, -1).repeat(g, 1, 1)
|
| 80 |
+
hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
|
| 81 |
+
hh = hh.to(dtype=dtype)
|
| 82 |
+
hl = hl.to(dtype=dtype)
|
| 83 |
+
|
| 84 |
+
x = F.pad(x, pad=(n - 2, n - 1, n - 2, n - 1), mode=mode).to(dtype)
|
| 85 |
+
xl = F.conv2d(x, hl.unsqueeze(2), groups=g, stride=(1, 2))
|
| 86 |
+
xh = F.conv2d(x, hh.unsqueeze(2), groups=g, stride=(1, 2))
|
| 87 |
+
xll = F.conv2d(xl, hl.unsqueeze(3), groups=g, stride=(2, 1))
|
| 88 |
+
xlh = F.conv2d(xl, hh.unsqueeze(3), groups=g, stride=(2, 1))
|
| 89 |
+
xhl = F.conv2d(xh, hl.unsqueeze(3), groups=g, stride=(2, 1))
|
| 90 |
+
xhh = F.conv2d(xh, hh.unsqueeze(3), groups=g, stride=(2, 1))
|
| 91 |
+
|
| 92 |
+
out = torch.cat([xll, xlh, xhl, xhh], dim=1)
|
| 93 |
+
if rescale:
|
| 94 |
+
out = out / 2
|
| 95 |
+
return out
|
| 96 |
+
|
| 97 |
+
def _haar(self, x):
|
| 98 |
+
for _ in self.range:
|
| 99 |
+
x = self._dwt(x, rescale=True)
|
| 100 |
+
return x
|
| 101 |
+
|
| 102 |
+
def _arrange(self, x):
|
| 103 |
+
x = rearrange(
|
| 104 |
+
x,
|
| 105 |
+
"b c (h p1) (w p2) -> b (c p1 p2) h w",
|
| 106 |
+
p1=self.patch_size,
|
| 107 |
+
p2=self.patch_size,
|
| 108 |
+
).contiguous()
|
| 109 |
+
return x
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
class Patcher3D(Patcher):
|
| 113 |
+
"""A 3D discrete wavelet transform for video data, expects 5D tensor, i.e. a batch of videos."""
|
| 114 |
+
|
| 115 |
+
def __init__(self, patch_size=1, patch_method="haar"):
|
| 116 |
+
super().__init__(patch_method=patch_method, patch_size=patch_size)
|
| 117 |
+
self.register_buffer(
|
| 118 |
+
"patch_size_buffer",
|
| 119 |
+
patch_size * torch.ones([1], dtype=torch.int32),
|
| 120 |
+
persistent=_PERSISTENT,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
def _dwt(self, x, wavelet, mode="reflect", rescale=False):
|
| 124 |
+
dtype = x.dtype
|
| 125 |
+
h = self.wavelets.to(device=x.device)
|
| 126 |
+
|
| 127 |
+
n = h.shape[0]
|
| 128 |
+
g = x.shape[1]
|
| 129 |
+
hl = h.flip(0).reshape(1, 1, -1).repeat(g, 1, 1)
|
| 130 |
+
hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
|
| 131 |
+
hh = hh.to(dtype=dtype)
|
| 132 |
+
hl = hl.to(dtype=dtype)
|
| 133 |
+
|
| 134 |
+
# Handles temporal axis.
|
| 135 |
+
x = F.pad(
|
| 136 |
+
x, pad=(max(0, n - 2), n - 1, n - 2, n - 1, n - 2, n - 1), mode=mode
|
| 137 |
+
).to(dtype)
|
| 138 |
+
xl = F.conv3d(x, hl.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1))
|
| 139 |
+
xh = F.conv3d(x, hh.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1))
|
| 140 |
+
|
| 141 |
+
# Handles spatial axes.
|
| 142 |
+
xll = F.conv3d(xl, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
|
| 143 |
+
xlh = F.conv3d(xl, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
|
| 144 |
+
xhl = F.conv3d(xh, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
|
| 145 |
+
xhh = F.conv3d(xh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
|
| 146 |
+
|
| 147 |
+
xlll = F.conv3d(xll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 148 |
+
xllh = F.conv3d(xll, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 149 |
+
xlhl = F.conv3d(xlh, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 150 |
+
xlhh = F.conv3d(xlh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 151 |
+
xhll = F.conv3d(xhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 152 |
+
xhlh = F.conv3d(xhl, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 153 |
+
xhhl = F.conv3d(xhh, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 154 |
+
xhhh = F.conv3d(xhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
|
| 155 |
+
|
| 156 |
+
out = torch.cat([xlll, xllh, xlhl, xlhh, xhll, xhlh, xhhl, xhhh], dim=1)
|
| 157 |
+
if rescale:
|
| 158 |
+
out = out / (2 * torch.sqrt(torch.tensor(2.0)))
|
| 159 |
+
return out
|
| 160 |
+
|
| 161 |
+
def _haar(self, x):
|
| 162 |
+
xi, xv = torch.split(x, [1, x.shape[2] - 1], dim=2)
|
| 163 |
+
x = torch.cat([xi.repeat_interleave(self.patch_size, dim=2), xv], dim=2)
|
| 164 |
+
for _ in self.range:
|
| 165 |
+
x = self._dwt(x, "haar", rescale=True)
|
| 166 |
+
return x
|
| 167 |
+
|
| 168 |
+
def _arrange(self, x):
|
| 169 |
+
xi, xv = torch.split(x, [1, x.shape[2] - 1], dim=2)
|
| 170 |
+
x = torch.cat([xi.repeat_interleave(self.patch_size, dim=2), xv], dim=2)
|
| 171 |
+
x = rearrange(
|
| 172 |
+
x,
|
| 173 |
+
"b c (t p1) (h p2) (w p3) -> b (c p1 p2 p3) t h w",
|
| 174 |
+
p1=self.patch_size,
|
| 175 |
+
p2=self.patch_size,
|
| 176 |
+
p3=self.patch_size,
|
| 177 |
+
).contiguous()
|
| 178 |
+
return x
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
class UnPatcher(torch.nn.Module):
|
| 182 |
+
"""A module to convert patches into image tensorsusing torch operations.
|
| 183 |
+
|
| 184 |
+
The main difference from `class Unpatching` is that this module implements
|
| 185 |
+
all operations using torch, rather than python or numpy, for efficiency purpose.
|
| 186 |
+
|
| 187 |
+
It's bit-wise identical to the Unpatching module outputs, with the added
|
| 188 |
+
benefit of being torch.jit scriptable.
|
| 189 |
+
"""
|
| 190 |
+
|
| 191 |
+
def __init__(self, patch_size=1, patch_method="haar"):
|
| 192 |
+
super().__init__()
|
| 193 |
+
self.patch_size = patch_size
|
| 194 |
+
self.patch_method = patch_method
|
| 195 |
+
self.register_buffer(
|
| 196 |
+
"wavelets", _WAVELETS[patch_method], persistent=_PERSISTENT
|
| 197 |
+
)
|
| 198 |
+
self.range = range(int(torch.log2(torch.tensor(self.patch_size)).item()))
|
| 199 |
+
self.register_buffer(
|
| 200 |
+
"_arange",
|
| 201 |
+
torch.arange(_WAVELETS[patch_method].shape[0]),
|
| 202 |
+
persistent=_PERSISTENT,
|
| 203 |
+
)
|
| 204 |
+
for param in self.parameters():
|
| 205 |
+
param.requires_grad = False
|
| 206 |
+
|
| 207 |
+
def forward(self, x):
|
| 208 |
+
if self.patch_method == "haar":
|
| 209 |
+
return self._ihaar(x)
|
| 210 |
+
elif self.patch_method == "rearrange":
|
| 211 |
+
return self._iarrange(x)
|
| 212 |
+
else:
|
| 213 |
+
raise ValueError("Unknown patch method: " + self.patch_method)
|
| 214 |
+
|
| 215 |
+
def _idwt(self, x, wavelet="haar", mode="reflect", rescale=False):
|
| 216 |
+
dtype = x.dtype
|
| 217 |
+
h = self.wavelets.to(device=x.device)
|
| 218 |
+
n = h.shape[0]
|
| 219 |
+
|
| 220 |
+
g = x.shape[1] // 4
|
| 221 |
+
hl = h.flip([0]).reshape(1, 1, -1).repeat([g, 1, 1])
|
| 222 |
+
hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
|
| 223 |
+
hh = hh.to(dtype=dtype)
|
| 224 |
+
hl = hl.to(dtype=dtype)
|
| 225 |
+
|
| 226 |
+
xll, xlh, xhl, xhh = torch.chunk(x.to(dtype), 4, dim=1)
|
| 227 |
+
|
| 228 |
+
# Inverse transform.
|
| 229 |
+
yl = torch.nn.functional.conv_transpose2d(
|
| 230 |
+
xll, hl.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
|
| 231 |
+
)
|
| 232 |
+
yl += torch.nn.functional.conv_transpose2d(
|
| 233 |
+
xlh, hh.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
|
| 234 |
+
)
|
| 235 |
+
yh = torch.nn.functional.conv_transpose2d(
|
| 236 |
+
xhl, hl.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
|
| 237 |
+
)
|
| 238 |
+
yh += torch.nn.functional.conv_transpose2d(
|
| 239 |
+
xhh, hh.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
|
| 240 |
+
)
|
| 241 |
+
y = torch.nn.functional.conv_transpose2d(
|
| 242 |
+
yl, hl.unsqueeze(2), groups=g, stride=(1, 2), padding=(0, n - 2)
|
| 243 |
+
)
|
| 244 |
+
y += torch.nn.functional.conv_transpose2d(
|
| 245 |
+
yh, hh.unsqueeze(2), groups=g, stride=(1, 2), padding=(0, n - 2)
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
if rescale:
|
| 249 |
+
y = y * 2
|
| 250 |
+
return y
|
| 251 |
+
|
| 252 |
+
def _ihaar(self, x):
|
| 253 |
+
for _ in self.range:
|
| 254 |
+
x = self._idwt(x, "haar", rescale=True)
|
| 255 |
+
return x
|
| 256 |
+
|
| 257 |
+
def _iarrange(self, x):
|
| 258 |
+
x = rearrange(
|
| 259 |
+
x,
|
| 260 |
+
"b (c p1 p2) h w -> b c (h p1) (w p2)",
|
| 261 |
+
p1=self.patch_size,
|
| 262 |
+
p2=self.patch_size,
|
| 263 |
+
)
|
| 264 |
+
return x
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
class UnPatcher3D(UnPatcher):
|
| 268 |
+
"""A 3D inverse discrete wavelet transform for video wavelet decompositions."""
|
| 269 |
+
|
| 270 |
+
def __init__(self, patch_size=1, patch_method="haar"):
|
| 271 |
+
super().__init__(patch_method=patch_method, patch_size=patch_size)
|
| 272 |
+
|
| 273 |
+
def _idwt(self, x, wavelet="haar", mode="reflect", rescale=False):
|
| 274 |
+
dtype = x.dtype
|
| 275 |
+
h = self.wavelets.to(device=x.device)
|
| 276 |
+
|
| 277 |
+
g = x.shape[1] // 8 # split into 8 spatio-temporal filtered tesnors.
|
| 278 |
+
hl = h.flip([0]).reshape(1, 1, -1).repeat([g, 1, 1])
|
| 279 |
+
hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
|
| 280 |
+
hl = hl.to(dtype=dtype)
|
| 281 |
+
hh = hh.to(dtype=dtype)
|
| 282 |
+
|
| 283 |
+
xlll, xllh, xlhl, xlhh, xhll, xhlh, xhhl, xhhh = torch.chunk(x, 8, dim=1)
|
| 284 |
+
del x
|
| 285 |
+
|
| 286 |
+
# Height height transposed convolutions.
|
| 287 |
+
xll = F.conv_transpose3d(
|
| 288 |
+
xlll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 289 |
+
)
|
| 290 |
+
del xlll
|
| 291 |
+
|
| 292 |
+
xll += F.conv_transpose3d(
|
| 293 |
+
xllh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 294 |
+
)
|
| 295 |
+
del xllh
|
| 296 |
+
|
| 297 |
+
xlh = F.conv_transpose3d(
|
| 298 |
+
xlhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 299 |
+
)
|
| 300 |
+
del xlhl
|
| 301 |
+
|
| 302 |
+
xlh += F.conv_transpose3d(
|
| 303 |
+
xlhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 304 |
+
)
|
| 305 |
+
del xlhh
|
| 306 |
+
|
| 307 |
+
xhl = F.conv_transpose3d(
|
| 308 |
+
xhll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 309 |
+
)
|
| 310 |
+
del xhll
|
| 311 |
+
|
| 312 |
+
xhl += F.conv_transpose3d(
|
| 313 |
+
xhlh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 314 |
+
)
|
| 315 |
+
del xhlh
|
| 316 |
+
|
| 317 |
+
xhh = F.conv_transpose3d(
|
| 318 |
+
xhhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 319 |
+
)
|
| 320 |
+
del xhhl
|
| 321 |
+
|
| 322 |
+
xhh += F.conv_transpose3d(
|
| 323 |
+
xhhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
|
| 324 |
+
)
|
| 325 |
+
del xhhh
|
| 326 |
+
|
| 327 |
+
# Handles width transposed convolutions.
|
| 328 |
+
xl = F.conv_transpose3d(
|
| 329 |
+
xll, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
|
| 330 |
+
)
|
| 331 |
+
del xll
|
| 332 |
+
|
| 333 |
+
xl += F.conv_transpose3d(
|
| 334 |
+
xlh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
|
| 335 |
+
)
|
| 336 |
+
del xlh
|
| 337 |
+
|
| 338 |
+
xh = F.conv_transpose3d(
|
| 339 |
+
xhl, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
|
| 340 |
+
)
|
| 341 |
+
del xhl
|
| 342 |
+
|
| 343 |
+
xh += F.conv_transpose3d(
|
| 344 |
+
xhh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
|
| 345 |
+
)
|
| 346 |
+
del xhh
|
| 347 |
+
|
| 348 |
+
# Handles time axis transposed convolutions.
|
| 349 |
+
x = F.conv_transpose3d(
|
| 350 |
+
xl, hl.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)
|
| 351 |
+
)
|
| 352 |
+
del xl
|
| 353 |
+
|
| 354 |
+
x += F.conv_transpose3d(
|
| 355 |
+
xh, hh.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)
|
| 356 |
+
)
|
| 357 |
+
|
| 358 |
+
if rescale:
|
| 359 |
+
x = x * (2 * torch.sqrt(torch.tensor(2.0)))
|
| 360 |
+
return x
|
| 361 |
+
|
| 362 |
+
def _ihaar(self, x):
|
| 363 |
+
for _ in self.range:
|
| 364 |
+
x = self._idwt(x, "haar", rescale=True)
|
| 365 |
+
x = x[:, :, self.patch_size - 1 :, ...]
|
| 366 |
+
return x
|
| 367 |
+
|
| 368 |
+
def _iarrange(self, x):
|
| 369 |
+
x = rearrange(
|
| 370 |
+
x,
|
| 371 |
+
"b (c p1 p2 p3) t h w -> b c (t p1) (h p2) (w p3)",
|
| 372 |
+
p1=self.patch_size,
|
| 373 |
+
p2=self.patch_size,
|
| 374 |
+
p3=self.patch_size,
|
| 375 |
+
)
|
| 376 |
+
x = x[:, :, self.patch_size - 1 :, ...]
|
| 377 |
+
return x
|
ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/utils.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""Shared utilities for the networks module."""
|
| 16 |
+
|
| 17 |
+
from typing import Any
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
from einops import rearrange
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
import comfy.ops
|
| 24 |
+
ops = comfy.ops.disable_weight_init
|
| 25 |
+
|
| 26 |
+
def time2batch(x: torch.Tensor) -> tuple[torch.Tensor, int]:
|
| 27 |
+
batch_size = x.shape[0]
|
| 28 |
+
return rearrange(x, "b c t h w -> (b t) c h w"), batch_size
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def batch2time(x: torch.Tensor, batch_size: int) -> torch.Tensor:
|
| 32 |
+
return rearrange(x, "(b t) c h w -> b c t h w", b=batch_size)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def space2batch(x: torch.Tensor) -> tuple[torch.Tensor, int]:
|
| 36 |
+
batch_size, height = x.shape[0], x.shape[-2]
|
| 37 |
+
return rearrange(x, "b c t h w -> (b h w) c t"), batch_size, height
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def batch2space(x: torch.Tensor, batch_size: int, height: int) -> torch.Tensor:
|
| 41 |
+
return rearrange(x, "(b h w) c t -> b c t h w", b=batch_size, h=height)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def cast_tuple(t: Any, length: int = 1) -> Any:
|
| 45 |
+
return t if isinstance(t, tuple) else ((t,) * length)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def replication_pad(x):
|
| 49 |
+
return torch.cat([x[:, :, :1, ...], x], dim=2)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def divisible_by(num: int, den: int) -> bool:
|
| 53 |
+
return (num % den) == 0
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def is_odd(n: int) -> bool:
|
| 57 |
+
return not divisible_by(n, 2)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def nonlinearity(x):
|
| 61 |
+
# x * sigmoid(x)
|
| 62 |
+
return torch.nn.functional.silu(x)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def Normalize(in_channels, num_groups=32):
|
| 66 |
+
return ops.GroupNorm(
|
| 67 |
+
num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class CausalNormalize(torch.nn.Module):
|
| 72 |
+
def __init__(self, in_channels, num_groups=1):
|
| 73 |
+
super().__init__()
|
| 74 |
+
self.norm = ops.GroupNorm(
|
| 75 |
+
num_groups=num_groups,
|
| 76 |
+
num_channels=in_channels,
|
| 77 |
+
eps=1e-6,
|
| 78 |
+
affine=True,
|
| 79 |
+
)
|
| 80 |
+
self.num_groups = num_groups
|
| 81 |
+
|
| 82 |
+
def forward(self, x):
|
| 83 |
+
# if num_groups !=1, we apply a spatio-temporal groupnorm for backward compatibility purpose.
|
| 84 |
+
# All new models should use num_groups=1, otherwise causality is not guaranteed.
|
| 85 |
+
if self.num_groups == 1:
|
| 86 |
+
x, batch_size = time2batch(x)
|
| 87 |
+
return batch2time(self.norm(x), batch_size)
|
| 88 |
+
return self.norm(x)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def exists(v):
|
| 92 |
+
return v is not None
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def default(*args):
|
| 96 |
+
for arg in args:
|
| 97 |
+
if exists(arg):
|
| 98 |
+
return arg
|
| 99 |
+
return None
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def round_ste(z: torch.Tensor) -> torch.Tensor:
|
| 103 |
+
"""Round with straight through gradients."""
|
| 104 |
+
zhat = z.round()
|
| 105 |
+
return z + (zhat - z).detach()
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def log(t, eps=1e-5):
|
| 109 |
+
return t.clamp(min=eps).log()
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def entropy(prob):
|
| 113 |
+
return (-prob * log(prob)).sum(dim=-1)
|
ComfyUI/comfy/ldm/cosmos/model.py
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
"""
|
| 17 |
+
A general implementation of adaln-modulated VIT-like~(DiT) transformer for video processing.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from typing import Optional, Tuple
|
| 21 |
+
|
| 22 |
+
import torch
|
| 23 |
+
from einops import rearrange
|
| 24 |
+
from torch import nn
|
| 25 |
+
from torchvision import transforms
|
| 26 |
+
|
| 27 |
+
from enum import Enum
|
| 28 |
+
import logging
|
| 29 |
+
|
| 30 |
+
import comfy.patcher_extension
|
| 31 |
+
|
| 32 |
+
from .blocks import (
|
| 33 |
+
FinalLayer,
|
| 34 |
+
GeneralDITTransformerBlock,
|
| 35 |
+
PatchEmbed,
|
| 36 |
+
TimestepEmbedding,
|
| 37 |
+
Timesteps,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
from .position_embedding import LearnablePosEmbAxis, VideoRopePosition3DEmb
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class DataType(Enum):
|
| 44 |
+
IMAGE = "image"
|
| 45 |
+
VIDEO = "video"
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class GeneralDIT(nn.Module):
|
| 49 |
+
"""
|
| 50 |
+
A general implementation of adaln-modulated VIT-like~(DiT) transformer for video processing.
|
| 51 |
+
|
| 52 |
+
Args:
|
| 53 |
+
max_img_h (int): Maximum height of the input images.
|
| 54 |
+
max_img_w (int): Maximum width of the input images.
|
| 55 |
+
max_frames (int): Maximum number of frames in the video sequence.
|
| 56 |
+
in_channels (int): Number of input channels (e.g., RGB channels for color images).
|
| 57 |
+
out_channels (int): Number of output channels.
|
| 58 |
+
patch_spatial (tuple): Spatial resolution of patches for input processing.
|
| 59 |
+
patch_temporal (int): Temporal resolution of patches for input processing.
|
| 60 |
+
concat_padding_mask (bool): If True, includes a mask channel in the input to handle padding.
|
| 61 |
+
block_config (str): Configuration of the transformer block. See Notes for supported block types.
|
| 62 |
+
model_channels (int): Base number of channels used throughout the model.
|
| 63 |
+
num_blocks (int): Number of transformer blocks.
|
| 64 |
+
num_heads (int): Number of heads in the multi-head attention layers.
|
| 65 |
+
mlp_ratio (float): Expansion ratio for MLP blocks.
|
| 66 |
+
block_x_format (str): Format of input tensor for transformer blocks ('BTHWD' or 'THWBD').
|
| 67 |
+
crossattn_emb_channels (int): Number of embedding channels for cross-attention.
|
| 68 |
+
use_cross_attn_mask (bool): Whether to use mask in cross-attention.
|
| 69 |
+
pos_emb_cls (str): Type of positional embeddings.
|
| 70 |
+
pos_emb_learnable (bool): Whether positional embeddings are learnable.
|
| 71 |
+
pos_emb_interpolation (str): Method for interpolating positional embeddings.
|
| 72 |
+
affline_emb_norm (bool): Whether to normalize affine embeddings.
|
| 73 |
+
use_adaln_lora (bool): Whether to use AdaLN-LoRA.
|
| 74 |
+
adaln_lora_dim (int): Dimension for AdaLN-LoRA.
|
| 75 |
+
rope_h_extrapolation_ratio (float): Height extrapolation ratio for RoPE.
|
| 76 |
+
rope_w_extrapolation_ratio (float): Width extrapolation ratio for RoPE.
|
| 77 |
+
rope_t_extrapolation_ratio (float): Temporal extrapolation ratio for RoPE.
|
| 78 |
+
extra_per_block_abs_pos_emb (bool): Whether to use extra per-block absolute positional embeddings.
|
| 79 |
+
extra_per_block_abs_pos_emb_type (str): Type of extra per-block positional embeddings.
|
| 80 |
+
extra_h_extrapolation_ratio (float): Height extrapolation ratio for extra embeddings.
|
| 81 |
+
extra_w_extrapolation_ratio (float): Width extrapolation ratio for extra embeddings.
|
| 82 |
+
extra_t_extrapolation_ratio (float): Temporal extrapolation ratio for extra embeddings.
|
| 83 |
+
|
| 84 |
+
Notes:
|
| 85 |
+
Supported block types in block_config:
|
| 86 |
+
* cross_attn, ca: Cross attention
|
| 87 |
+
* full_attn: Full attention on all flattened tokens
|
| 88 |
+
* mlp, ff: Feed forward block
|
| 89 |
+
"""
|
| 90 |
+
|
| 91 |
+
def __init__(
|
| 92 |
+
self,
|
| 93 |
+
max_img_h: int,
|
| 94 |
+
max_img_w: int,
|
| 95 |
+
max_frames: int,
|
| 96 |
+
in_channels: int,
|
| 97 |
+
out_channels: int,
|
| 98 |
+
patch_spatial: tuple,
|
| 99 |
+
patch_temporal: int,
|
| 100 |
+
concat_padding_mask: bool = True,
|
| 101 |
+
# attention settings
|
| 102 |
+
block_config: str = "FA-CA-MLP",
|
| 103 |
+
model_channels: int = 768,
|
| 104 |
+
num_blocks: int = 10,
|
| 105 |
+
num_heads: int = 16,
|
| 106 |
+
mlp_ratio: float = 4.0,
|
| 107 |
+
block_x_format: str = "BTHWD",
|
| 108 |
+
# cross attention settings
|
| 109 |
+
crossattn_emb_channels: int = 1024,
|
| 110 |
+
use_cross_attn_mask: bool = False,
|
| 111 |
+
# positional embedding settings
|
| 112 |
+
pos_emb_cls: str = "sincos",
|
| 113 |
+
pos_emb_learnable: bool = False,
|
| 114 |
+
pos_emb_interpolation: str = "crop",
|
| 115 |
+
affline_emb_norm: bool = False, # whether or not to normalize the affine embedding
|
| 116 |
+
use_adaln_lora: bool = False,
|
| 117 |
+
adaln_lora_dim: int = 256,
|
| 118 |
+
rope_h_extrapolation_ratio: float = 1.0,
|
| 119 |
+
rope_w_extrapolation_ratio: float = 1.0,
|
| 120 |
+
rope_t_extrapolation_ratio: float = 1.0,
|
| 121 |
+
extra_per_block_abs_pos_emb: bool = False,
|
| 122 |
+
extra_per_block_abs_pos_emb_type: str = "sincos",
|
| 123 |
+
extra_h_extrapolation_ratio: float = 1.0,
|
| 124 |
+
extra_w_extrapolation_ratio: float = 1.0,
|
| 125 |
+
extra_t_extrapolation_ratio: float = 1.0,
|
| 126 |
+
image_model=None,
|
| 127 |
+
device=None,
|
| 128 |
+
dtype=None,
|
| 129 |
+
operations=None,
|
| 130 |
+
) -> None:
|
| 131 |
+
super().__init__()
|
| 132 |
+
self.max_img_h = max_img_h
|
| 133 |
+
self.max_img_w = max_img_w
|
| 134 |
+
self.max_frames = max_frames
|
| 135 |
+
self.in_channels = in_channels
|
| 136 |
+
self.out_channels = out_channels
|
| 137 |
+
self.patch_spatial = patch_spatial
|
| 138 |
+
self.patch_temporal = patch_temporal
|
| 139 |
+
self.num_heads = num_heads
|
| 140 |
+
self.num_blocks = num_blocks
|
| 141 |
+
self.model_channels = model_channels
|
| 142 |
+
self.use_cross_attn_mask = use_cross_attn_mask
|
| 143 |
+
self.concat_padding_mask = concat_padding_mask
|
| 144 |
+
# positional embedding settings
|
| 145 |
+
self.pos_emb_cls = pos_emb_cls
|
| 146 |
+
self.pos_emb_learnable = pos_emb_learnable
|
| 147 |
+
self.pos_emb_interpolation = pos_emb_interpolation
|
| 148 |
+
self.affline_emb_norm = affline_emb_norm
|
| 149 |
+
self.rope_h_extrapolation_ratio = rope_h_extrapolation_ratio
|
| 150 |
+
self.rope_w_extrapolation_ratio = rope_w_extrapolation_ratio
|
| 151 |
+
self.rope_t_extrapolation_ratio = rope_t_extrapolation_ratio
|
| 152 |
+
self.extra_per_block_abs_pos_emb = extra_per_block_abs_pos_emb
|
| 153 |
+
self.extra_per_block_abs_pos_emb_type = extra_per_block_abs_pos_emb_type.lower()
|
| 154 |
+
self.extra_h_extrapolation_ratio = extra_h_extrapolation_ratio
|
| 155 |
+
self.extra_w_extrapolation_ratio = extra_w_extrapolation_ratio
|
| 156 |
+
self.extra_t_extrapolation_ratio = extra_t_extrapolation_ratio
|
| 157 |
+
self.dtype = dtype
|
| 158 |
+
weight_args = {"device": device, "dtype": dtype}
|
| 159 |
+
|
| 160 |
+
in_channels = in_channels + 1 if concat_padding_mask else in_channels
|
| 161 |
+
self.x_embedder = PatchEmbed(
|
| 162 |
+
spatial_patch_size=patch_spatial,
|
| 163 |
+
temporal_patch_size=patch_temporal,
|
| 164 |
+
in_channels=in_channels,
|
| 165 |
+
out_channels=model_channels,
|
| 166 |
+
bias=False,
|
| 167 |
+
weight_args=weight_args,
|
| 168 |
+
operations=operations,
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
self.build_pos_embed(device=device, dtype=dtype)
|
| 172 |
+
self.block_x_format = block_x_format
|
| 173 |
+
self.use_adaln_lora = use_adaln_lora
|
| 174 |
+
self.adaln_lora_dim = adaln_lora_dim
|
| 175 |
+
self.t_embedder = nn.ModuleList(
|
| 176 |
+
[Timesteps(model_channels),
|
| 177 |
+
TimestepEmbedding(model_channels, model_channels, use_adaln_lora=use_adaln_lora, weight_args=weight_args, operations=operations),]
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
self.blocks = nn.ModuleDict()
|
| 181 |
+
|
| 182 |
+
for idx in range(num_blocks):
|
| 183 |
+
self.blocks[f"block{idx}"] = GeneralDITTransformerBlock(
|
| 184 |
+
x_dim=model_channels,
|
| 185 |
+
context_dim=crossattn_emb_channels,
|
| 186 |
+
num_heads=num_heads,
|
| 187 |
+
block_config=block_config,
|
| 188 |
+
mlp_ratio=mlp_ratio,
|
| 189 |
+
x_format=self.block_x_format,
|
| 190 |
+
use_adaln_lora=use_adaln_lora,
|
| 191 |
+
adaln_lora_dim=adaln_lora_dim,
|
| 192 |
+
weight_args=weight_args,
|
| 193 |
+
operations=operations,
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
if self.affline_emb_norm:
|
| 197 |
+
logging.debug("Building affine embedding normalization layer")
|
| 198 |
+
self.affline_norm = operations.RMSNorm(model_channels, elementwise_affine=True, eps=1e-6, device=device, dtype=dtype)
|
| 199 |
+
else:
|
| 200 |
+
self.affline_norm = nn.Identity()
|
| 201 |
+
|
| 202 |
+
self.final_layer = FinalLayer(
|
| 203 |
+
hidden_size=self.model_channels,
|
| 204 |
+
spatial_patch_size=self.patch_spatial,
|
| 205 |
+
temporal_patch_size=self.patch_temporal,
|
| 206 |
+
out_channels=self.out_channels,
|
| 207 |
+
use_adaln_lora=self.use_adaln_lora,
|
| 208 |
+
adaln_lora_dim=self.adaln_lora_dim,
|
| 209 |
+
weight_args=weight_args,
|
| 210 |
+
operations=operations,
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
def build_pos_embed(self, device=None, dtype=None):
|
| 214 |
+
if self.pos_emb_cls == "rope3d":
|
| 215 |
+
cls_type = VideoRopePosition3DEmb
|
| 216 |
+
else:
|
| 217 |
+
raise ValueError(f"Unknown pos_emb_cls {self.pos_emb_cls}")
|
| 218 |
+
|
| 219 |
+
logging.debug(f"Building positional embedding with {self.pos_emb_cls} class, impl {cls_type}")
|
| 220 |
+
kwargs = dict(
|
| 221 |
+
model_channels=self.model_channels,
|
| 222 |
+
len_h=self.max_img_h // self.patch_spatial,
|
| 223 |
+
len_w=self.max_img_w // self.patch_spatial,
|
| 224 |
+
len_t=self.max_frames // self.patch_temporal,
|
| 225 |
+
is_learnable=self.pos_emb_learnable,
|
| 226 |
+
interpolation=self.pos_emb_interpolation,
|
| 227 |
+
head_dim=self.model_channels // self.num_heads,
|
| 228 |
+
h_extrapolation_ratio=self.rope_h_extrapolation_ratio,
|
| 229 |
+
w_extrapolation_ratio=self.rope_w_extrapolation_ratio,
|
| 230 |
+
t_extrapolation_ratio=self.rope_t_extrapolation_ratio,
|
| 231 |
+
device=device,
|
| 232 |
+
)
|
| 233 |
+
self.pos_embedder = cls_type(
|
| 234 |
+
**kwargs,
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
if self.extra_per_block_abs_pos_emb:
|
| 238 |
+
assert self.extra_per_block_abs_pos_emb_type in [
|
| 239 |
+
"learnable",
|
| 240 |
+
], f"Unknown extra_per_block_abs_pos_emb_type {self.extra_per_block_abs_pos_emb_type}"
|
| 241 |
+
kwargs["h_extrapolation_ratio"] = self.extra_h_extrapolation_ratio
|
| 242 |
+
kwargs["w_extrapolation_ratio"] = self.extra_w_extrapolation_ratio
|
| 243 |
+
kwargs["t_extrapolation_ratio"] = self.extra_t_extrapolation_ratio
|
| 244 |
+
kwargs["device"] = device
|
| 245 |
+
kwargs["dtype"] = dtype
|
| 246 |
+
self.extra_pos_embedder = LearnablePosEmbAxis(
|
| 247 |
+
**kwargs,
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
def prepare_embedded_sequence(
|
| 251 |
+
self,
|
| 252 |
+
x_B_C_T_H_W: torch.Tensor,
|
| 253 |
+
fps: Optional[torch.Tensor] = None,
|
| 254 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 255 |
+
latent_condition: Optional[torch.Tensor] = None,
|
| 256 |
+
latent_condition_sigma: Optional[torch.Tensor] = None,
|
| 257 |
+
) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 258 |
+
"""
|
| 259 |
+
Prepares an embedded sequence tensor by applying positional embeddings and handling padding masks.
|
| 260 |
+
|
| 261 |
+
Args:
|
| 262 |
+
x_B_C_T_H_W (torch.Tensor): video
|
| 263 |
+
fps (Optional[torch.Tensor]): Frames per second tensor to be used for positional embedding when required.
|
| 264 |
+
If None, a default value (`self.base_fps`) will be used.
|
| 265 |
+
padding_mask (Optional[torch.Tensor]): current it is not used
|
| 266 |
+
|
| 267 |
+
Returns:
|
| 268 |
+
Tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 269 |
+
- A tensor of shape (B, T, H, W, D) with the embedded sequence.
|
| 270 |
+
- An optional positional embedding tensor, returned only if the positional embedding class
|
| 271 |
+
(`self.pos_emb_cls`) includes 'rope'. Otherwise, None.
|
| 272 |
+
|
| 273 |
+
Notes:
|
| 274 |
+
- If `self.concat_padding_mask` is True, a padding mask channel is concatenated to the input tensor.
|
| 275 |
+
- The method of applying positional embeddings depends on the value of `self.pos_emb_cls`.
|
| 276 |
+
- If 'rope' is in `self.pos_emb_cls` (case insensitive), the positional embeddings are generated using
|
| 277 |
+
the `self.pos_embedder` with the shape [T, H, W].
|
| 278 |
+
- If "fps_aware" is in `self.pos_emb_cls`, the positional embeddings are generated using the
|
| 279 |
+
`self.pos_embedder` with the fps tensor.
|
| 280 |
+
- Otherwise, the positional embeddings are generated without considering fps.
|
| 281 |
+
"""
|
| 282 |
+
if self.concat_padding_mask:
|
| 283 |
+
if padding_mask is not None:
|
| 284 |
+
padding_mask = transforms.functional.resize(
|
| 285 |
+
padding_mask, list(x_B_C_T_H_W.shape[-2:]), interpolation=transforms.InterpolationMode.NEAREST
|
| 286 |
+
)
|
| 287 |
+
else:
|
| 288 |
+
padding_mask = torch.zeros((x_B_C_T_H_W.shape[0], 1, x_B_C_T_H_W.shape[-2], x_B_C_T_H_W.shape[-1]), dtype=x_B_C_T_H_W.dtype, device=x_B_C_T_H_W.device)
|
| 289 |
+
|
| 290 |
+
x_B_C_T_H_W = torch.cat(
|
| 291 |
+
[x_B_C_T_H_W, padding_mask.unsqueeze(1).repeat(1, 1, x_B_C_T_H_W.shape[2], 1, 1)], dim=1
|
| 292 |
+
)
|
| 293 |
+
x_B_T_H_W_D = self.x_embedder(x_B_C_T_H_W)
|
| 294 |
+
|
| 295 |
+
if self.extra_per_block_abs_pos_emb:
|
| 296 |
+
extra_pos_emb = self.extra_pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device, dtype=x_B_C_T_H_W.dtype)
|
| 297 |
+
else:
|
| 298 |
+
extra_pos_emb = None
|
| 299 |
+
|
| 300 |
+
if "rope" in self.pos_emb_cls.lower():
|
| 301 |
+
return x_B_T_H_W_D, self.pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device), extra_pos_emb
|
| 302 |
+
|
| 303 |
+
if "fps_aware" in self.pos_emb_cls:
|
| 304 |
+
x_B_T_H_W_D = x_B_T_H_W_D + self.pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device) # [B, T, H, W, D]
|
| 305 |
+
else:
|
| 306 |
+
x_B_T_H_W_D = x_B_T_H_W_D + self.pos_embedder(x_B_T_H_W_D, device=x_B_C_T_H_W.device) # [B, T, H, W, D]
|
| 307 |
+
|
| 308 |
+
return x_B_T_H_W_D, None, extra_pos_emb
|
| 309 |
+
|
| 310 |
+
def decoder_head(
|
| 311 |
+
self,
|
| 312 |
+
x_B_T_H_W_D: torch.Tensor,
|
| 313 |
+
emb_B_D: torch.Tensor,
|
| 314 |
+
crossattn_emb: torch.Tensor,
|
| 315 |
+
origin_shape: Tuple[int, int, int, int, int], # [B, C, T, H, W]
|
| 316 |
+
crossattn_mask: Optional[torch.Tensor] = None,
|
| 317 |
+
adaln_lora_B_3D: Optional[torch.Tensor] = None,
|
| 318 |
+
) -> torch.Tensor:
|
| 319 |
+
del crossattn_emb, crossattn_mask
|
| 320 |
+
B, C, T_before_patchify, H_before_patchify, W_before_patchify = origin_shape
|
| 321 |
+
x_BT_HW_D = rearrange(x_B_T_H_W_D, "B T H W D -> (B T) (H W) D")
|
| 322 |
+
x_BT_HW_D = self.final_layer(x_BT_HW_D, emb_B_D, adaln_lora_B_3D=adaln_lora_B_3D)
|
| 323 |
+
# This is to ensure x_BT_HW_D has the correct shape because
|
| 324 |
+
# when we merge T, H, W into one dimension, x_BT_HW_D has shape (B * T * H * W, 1*1, D).
|
| 325 |
+
x_BT_HW_D = x_BT_HW_D.view(
|
| 326 |
+
B * T_before_patchify // self.patch_temporal,
|
| 327 |
+
H_before_patchify // self.patch_spatial * W_before_patchify // self.patch_spatial,
|
| 328 |
+
-1,
|
| 329 |
+
)
|
| 330 |
+
x_B_D_T_H_W = rearrange(
|
| 331 |
+
x_BT_HW_D,
|
| 332 |
+
"(B T) (H W) (p1 p2 t C) -> B C (T t) (H p1) (W p2)",
|
| 333 |
+
p1=self.patch_spatial,
|
| 334 |
+
p2=self.patch_spatial,
|
| 335 |
+
H=H_before_patchify // self.patch_spatial,
|
| 336 |
+
W=W_before_patchify // self.patch_spatial,
|
| 337 |
+
t=self.patch_temporal,
|
| 338 |
+
B=B,
|
| 339 |
+
)
|
| 340 |
+
return x_B_D_T_H_W
|
| 341 |
+
|
| 342 |
+
def forward_before_blocks(
|
| 343 |
+
self,
|
| 344 |
+
x: torch.Tensor,
|
| 345 |
+
timesteps: torch.Tensor,
|
| 346 |
+
crossattn_emb: torch.Tensor,
|
| 347 |
+
crossattn_mask: Optional[torch.Tensor] = None,
|
| 348 |
+
fps: Optional[torch.Tensor] = None,
|
| 349 |
+
image_size: Optional[torch.Tensor] = None,
|
| 350 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 351 |
+
scalar_feature: Optional[torch.Tensor] = None,
|
| 352 |
+
data_type: Optional[DataType] = DataType.VIDEO,
|
| 353 |
+
latent_condition: Optional[torch.Tensor] = None,
|
| 354 |
+
latent_condition_sigma: Optional[torch.Tensor] = None,
|
| 355 |
+
**kwargs,
|
| 356 |
+
) -> torch.Tensor:
|
| 357 |
+
"""
|
| 358 |
+
Args:
|
| 359 |
+
x: (B, C, T, H, W) tensor of spatial-temp inputs
|
| 360 |
+
timesteps: (B, ) tensor of timesteps
|
| 361 |
+
crossattn_emb: (B, N, D) tensor of cross-attention embeddings
|
| 362 |
+
crossattn_mask: (B, N) tensor of cross-attention masks
|
| 363 |
+
"""
|
| 364 |
+
del kwargs
|
| 365 |
+
assert isinstance(
|
| 366 |
+
data_type, DataType
|
| 367 |
+
), f"Expected DataType, got {type(data_type)}. We need discuss this flag later."
|
| 368 |
+
original_shape = x.shape
|
| 369 |
+
x_B_T_H_W_D, rope_emb_L_1_1_D, extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = self.prepare_embedded_sequence(
|
| 370 |
+
x,
|
| 371 |
+
fps=fps,
|
| 372 |
+
padding_mask=padding_mask,
|
| 373 |
+
latent_condition=latent_condition,
|
| 374 |
+
latent_condition_sigma=latent_condition_sigma,
|
| 375 |
+
)
|
| 376 |
+
# logging affline scale information
|
| 377 |
+
affline_scale_log_info = {}
|
| 378 |
+
|
| 379 |
+
timesteps_B_D, adaln_lora_B_3D = self.t_embedder[1](self.t_embedder[0](timesteps.flatten()).to(x.dtype))
|
| 380 |
+
affline_emb_B_D = timesteps_B_D
|
| 381 |
+
affline_scale_log_info["timesteps_B_D"] = timesteps_B_D.detach()
|
| 382 |
+
|
| 383 |
+
if scalar_feature is not None:
|
| 384 |
+
raise NotImplementedError("Scalar feature is not implemented yet.")
|
| 385 |
+
|
| 386 |
+
affline_scale_log_info["affline_emb_B_D"] = affline_emb_B_D.detach()
|
| 387 |
+
affline_emb_B_D = self.affline_norm(affline_emb_B_D)
|
| 388 |
+
|
| 389 |
+
if self.use_cross_attn_mask:
|
| 390 |
+
if crossattn_mask is not None and not torch.is_floating_point(crossattn_mask):
|
| 391 |
+
crossattn_mask = (crossattn_mask - 1).to(x.dtype) * torch.finfo(x.dtype).max
|
| 392 |
+
crossattn_mask = crossattn_mask[:, None, None, :] # .to(dtype=torch.bool) # [B, 1, 1, length]
|
| 393 |
+
else:
|
| 394 |
+
crossattn_mask = None
|
| 395 |
+
|
| 396 |
+
if self.blocks["block0"].x_format == "THWBD":
|
| 397 |
+
x = rearrange(x_B_T_H_W_D, "B T H W D -> T H W B D")
|
| 398 |
+
if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
|
| 399 |
+
extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = rearrange(
|
| 400 |
+
extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D, "B T H W D -> T H W B D"
|
| 401 |
+
)
|
| 402 |
+
crossattn_emb = rearrange(crossattn_emb, "B M D -> M B D")
|
| 403 |
+
|
| 404 |
+
if crossattn_mask:
|
| 405 |
+
crossattn_mask = rearrange(crossattn_mask, "B M -> M B")
|
| 406 |
+
|
| 407 |
+
elif self.blocks["block0"].x_format == "BTHWD":
|
| 408 |
+
x = x_B_T_H_W_D
|
| 409 |
+
else:
|
| 410 |
+
raise ValueError(f"Unknown x_format {self.blocks[0].x_format}")
|
| 411 |
+
output = {
|
| 412 |
+
"x": x,
|
| 413 |
+
"affline_emb_B_D": affline_emb_B_D,
|
| 414 |
+
"crossattn_emb": crossattn_emb,
|
| 415 |
+
"crossattn_mask": crossattn_mask,
|
| 416 |
+
"rope_emb_L_1_1_D": rope_emb_L_1_1_D,
|
| 417 |
+
"adaln_lora_B_3D": adaln_lora_B_3D,
|
| 418 |
+
"original_shape": original_shape,
|
| 419 |
+
"extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D": extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D,
|
| 420 |
+
}
|
| 421 |
+
return output
|
| 422 |
+
|
| 423 |
+
def forward(
|
| 424 |
+
self,
|
| 425 |
+
x: torch.Tensor,
|
| 426 |
+
timesteps: torch.Tensor,
|
| 427 |
+
context: torch.Tensor,
|
| 428 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 429 |
+
# crossattn_emb: torch.Tensor,
|
| 430 |
+
# crossattn_mask: Optional[torch.Tensor] = None,
|
| 431 |
+
fps: Optional[torch.Tensor] = None,
|
| 432 |
+
image_size: Optional[torch.Tensor] = None,
|
| 433 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 434 |
+
scalar_feature: Optional[torch.Tensor] = None,
|
| 435 |
+
data_type: Optional[DataType] = DataType.VIDEO,
|
| 436 |
+
latent_condition: Optional[torch.Tensor] = None,
|
| 437 |
+
latent_condition_sigma: Optional[torch.Tensor] = None,
|
| 438 |
+
condition_video_augment_sigma: Optional[torch.Tensor] = None,
|
| 439 |
+
**kwargs,
|
| 440 |
+
):
|
| 441 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 442 |
+
self._forward,
|
| 443 |
+
self,
|
| 444 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, kwargs.get("transformer_options", {}))
|
| 445 |
+
).execute(x,
|
| 446 |
+
timesteps,
|
| 447 |
+
context,
|
| 448 |
+
attention_mask,
|
| 449 |
+
fps,
|
| 450 |
+
image_size,
|
| 451 |
+
padding_mask,
|
| 452 |
+
scalar_feature,
|
| 453 |
+
data_type,
|
| 454 |
+
latent_condition,
|
| 455 |
+
latent_condition_sigma,
|
| 456 |
+
condition_video_augment_sigma,
|
| 457 |
+
**kwargs)
|
| 458 |
+
|
| 459 |
+
def _forward(
|
| 460 |
+
self,
|
| 461 |
+
x: torch.Tensor,
|
| 462 |
+
timesteps: torch.Tensor,
|
| 463 |
+
context: torch.Tensor,
|
| 464 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 465 |
+
# crossattn_emb: torch.Tensor,
|
| 466 |
+
# crossattn_mask: Optional[torch.Tensor] = None,
|
| 467 |
+
fps: Optional[torch.Tensor] = None,
|
| 468 |
+
image_size: Optional[torch.Tensor] = None,
|
| 469 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 470 |
+
scalar_feature: Optional[torch.Tensor] = None,
|
| 471 |
+
data_type: Optional[DataType] = DataType.VIDEO,
|
| 472 |
+
latent_condition: Optional[torch.Tensor] = None,
|
| 473 |
+
latent_condition_sigma: Optional[torch.Tensor] = None,
|
| 474 |
+
condition_video_augment_sigma: Optional[torch.Tensor] = None,
|
| 475 |
+
**kwargs,
|
| 476 |
+
):
|
| 477 |
+
"""
|
| 478 |
+
Args:
|
| 479 |
+
x: (B, C, T, H, W) tensor of spatial-temp inputs
|
| 480 |
+
timesteps: (B, ) tensor of timesteps
|
| 481 |
+
crossattn_emb: (B, N, D) tensor of cross-attention embeddings
|
| 482 |
+
crossattn_mask: (B, N) tensor of cross-attention masks
|
| 483 |
+
condition_video_augment_sigma: (B,) used in lvg(long video generation), we add noise with this sigma to
|
| 484 |
+
augment condition input, the lvg model will condition on the condition_video_augment_sigma value;
|
| 485 |
+
we need forward_before_blocks pass to the forward_before_blocks function.
|
| 486 |
+
"""
|
| 487 |
+
|
| 488 |
+
crossattn_emb = context
|
| 489 |
+
crossattn_mask = attention_mask
|
| 490 |
+
|
| 491 |
+
inputs = self.forward_before_blocks(
|
| 492 |
+
x=x,
|
| 493 |
+
timesteps=timesteps,
|
| 494 |
+
crossattn_emb=crossattn_emb,
|
| 495 |
+
crossattn_mask=crossattn_mask,
|
| 496 |
+
fps=fps,
|
| 497 |
+
image_size=image_size,
|
| 498 |
+
padding_mask=padding_mask,
|
| 499 |
+
scalar_feature=scalar_feature,
|
| 500 |
+
data_type=data_type,
|
| 501 |
+
latent_condition=latent_condition,
|
| 502 |
+
latent_condition_sigma=latent_condition_sigma,
|
| 503 |
+
condition_video_augment_sigma=condition_video_augment_sigma,
|
| 504 |
+
**kwargs,
|
| 505 |
+
)
|
| 506 |
+
x, affline_emb_B_D, crossattn_emb, crossattn_mask, rope_emb_L_1_1_D, adaln_lora_B_3D, original_shape = (
|
| 507 |
+
inputs["x"],
|
| 508 |
+
inputs["affline_emb_B_D"],
|
| 509 |
+
inputs["crossattn_emb"],
|
| 510 |
+
inputs["crossattn_mask"],
|
| 511 |
+
inputs["rope_emb_L_1_1_D"],
|
| 512 |
+
inputs["adaln_lora_B_3D"],
|
| 513 |
+
inputs["original_shape"],
|
| 514 |
+
)
|
| 515 |
+
extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = inputs["extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D"].to(x.dtype)
|
| 516 |
+
del inputs
|
| 517 |
+
|
| 518 |
+
if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
|
| 519 |
+
assert (
|
| 520 |
+
x.shape == extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape
|
| 521 |
+
), f"{x.shape} != {extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape} {original_shape}"
|
| 522 |
+
|
| 523 |
+
transformer_options = kwargs.get("transformer_options", {})
|
| 524 |
+
for _, block in self.blocks.items():
|
| 525 |
+
assert (
|
| 526 |
+
self.blocks["block0"].x_format == block.x_format
|
| 527 |
+
), f"First block has x_format {self.blocks[0].x_format}, got {block.x_format}"
|
| 528 |
+
|
| 529 |
+
if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
|
| 530 |
+
x += extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D
|
| 531 |
+
x = block(
|
| 532 |
+
x,
|
| 533 |
+
affline_emb_B_D,
|
| 534 |
+
crossattn_emb,
|
| 535 |
+
crossattn_mask,
|
| 536 |
+
rope_emb_L_1_1_D=rope_emb_L_1_1_D,
|
| 537 |
+
adaln_lora_B_3D=adaln_lora_B_3D,
|
| 538 |
+
transformer_options=transformer_options,
|
| 539 |
+
)
|
| 540 |
+
|
| 541 |
+
x_B_T_H_W_D = rearrange(x, "T H W B D -> B T H W D")
|
| 542 |
+
|
| 543 |
+
x_B_D_T_H_W = self.decoder_head(
|
| 544 |
+
x_B_T_H_W_D=x_B_T_H_W_D,
|
| 545 |
+
emb_B_D=affline_emb_B_D,
|
| 546 |
+
crossattn_emb=None,
|
| 547 |
+
origin_shape=original_shape,
|
| 548 |
+
crossattn_mask=None,
|
| 549 |
+
adaln_lora_B_3D=adaln_lora_B_3D,
|
| 550 |
+
)
|
| 551 |
+
|
| 552 |
+
return x_B_D_T_H_W
|
ComfyUI/comfy/ldm/cosmos/position_embedding.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
from typing import List, Optional
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
from einops import rearrange, repeat
|
| 20 |
+
from torch import nn
|
| 21 |
+
import math
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def normalize(x: torch.Tensor, dim: Optional[List[int]] = None, eps: float = 0) -> torch.Tensor:
|
| 25 |
+
"""
|
| 26 |
+
Normalizes the input tensor along specified dimensions such that the average square norm of elements is adjusted.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
x (torch.Tensor): The input tensor to normalize.
|
| 30 |
+
dim (list, optional): The dimensions over which to normalize. If None, normalizes over all dimensions except the first.
|
| 31 |
+
eps (float, optional): A small constant to ensure numerical stability during division.
|
| 32 |
+
|
| 33 |
+
Returns:
|
| 34 |
+
torch.Tensor: The normalized tensor.
|
| 35 |
+
"""
|
| 36 |
+
if dim is None:
|
| 37 |
+
dim = list(range(1, x.ndim))
|
| 38 |
+
norm = torch.linalg.vector_norm(x, dim=dim, keepdim=True, dtype=torch.float32)
|
| 39 |
+
norm = torch.add(eps, norm, alpha=math.sqrt(norm.numel() / x.numel()))
|
| 40 |
+
return x / norm.to(x.dtype)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class VideoPositionEmb(nn.Module):
|
| 44 |
+
def forward(self, x_B_T_H_W_C: torch.Tensor, fps=Optional[torch.Tensor], device=None, dtype=None) -> torch.Tensor:
|
| 45 |
+
"""
|
| 46 |
+
It delegates the embedding generation to generate_embeddings function.
|
| 47 |
+
"""
|
| 48 |
+
B_T_H_W_C = x_B_T_H_W_C.shape
|
| 49 |
+
embeddings = self.generate_embeddings(B_T_H_W_C, fps=fps, device=device, dtype=dtype)
|
| 50 |
+
|
| 51 |
+
return embeddings
|
| 52 |
+
|
| 53 |
+
def generate_embeddings(self, B_T_H_W_C: torch.Size, fps=Optional[torch.Tensor], device=None):
|
| 54 |
+
raise NotImplementedError
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class VideoRopePosition3DEmb(VideoPositionEmb):
|
| 58 |
+
def __init__(
|
| 59 |
+
self,
|
| 60 |
+
*, # enforce keyword arguments
|
| 61 |
+
head_dim: int,
|
| 62 |
+
len_h: int,
|
| 63 |
+
len_w: int,
|
| 64 |
+
len_t: int,
|
| 65 |
+
base_fps: int = 24,
|
| 66 |
+
h_extrapolation_ratio: float = 1.0,
|
| 67 |
+
w_extrapolation_ratio: float = 1.0,
|
| 68 |
+
t_extrapolation_ratio: float = 1.0,
|
| 69 |
+
enable_fps_modulation: bool = True,
|
| 70 |
+
device=None,
|
| 71 |
+
**kwargs, # used for compatibility with other positional embeddings; unused in this class
|
| 72 |
+
):
|
| 73 |
+
del kwargs
|
| 74 |
+
super().__init__()
|
| 75 |
+
self.base_fps = base_fps
|
| 76 |
+
self.max_h = len_h
|
| 77 |
+
self.max_w = len_w
|
| 78 |
+
self.enable_fps_modulation = enable_fps_modulation
|
| 79 |
+
|
| 80 |
+
dim = head_dim
|
| 81 |
+
dim_h = dim // 6 * 2
|
| 82 |
+
dim_w = dim_h
|
| 83 |
+
dim_t = dim - 2 * dim_h
|
| 84 |
+
assert dim == dim_h + dim_w + dim_t, f"bad dim: {dim} != {dim_h} + {dim_w} + {dim_t}"
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"dim_spatial_range",
|
| 87 |
+
torch.arange(0, dim_h, 2, device=device)[: (dim_h // 2)].float() / dim_h,
|
| 88 |
+
persistent=False,
|
| 89 |
+
)
|
| 90 |
+
self.register_buffer(
|
| 91 |
+
"dim_temporal_range",
|
| 92 |
+
torch.arange(0, dim_t, 2, device=device)[: (dim_t // 2)].float() / dim_t,
|
| 93 |
+
persistent=False,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
self.h_ntk_factor = h_extrapolation_ratio ** (dim_h / (dim_h - 2))
|
| 97 |
+
self.w_ntk_factor = w_extrapolation_ratio ** (dim_w / (dim_w - 2))
|
| 98 |
+
self.t_ntk_factor = t_extrapolation_ratio ** (dim_t / (dim_t - 2))
|
| 99 |
+
|
| 100 |
+
def generate_embeddings(
|
| 101 |
+
self,
|
| 102 |
+
B_T_H_W_C: torch.Size,
|
| 103 |
+
fps: Optional[torch.Tensor] = None,
|
| 104 |
+
h_ntk_factor: Optional[float] = None,
|
| 105 |
+
w_ntk_factor: Optional[float] = None,
|
| 106 |
+
t_ntk_factor: Optional[float] = None,
|
| 107 |
+
device=None,
|
| 108 |
+
dtype=None,
|
| 109 |
+
):
|
| 110 |
+
"""
|
| 111 |
+
Generate embeddings for the given input size.
|
| 112 |
+
|
| 113 |
+
Args:
|
| 114 |
+
B_T_H_W_C (torch.Size): Input tensor size (Batch, Time, Height, Width, Channels).
|
| 115 |
+
fps (Optional[torch.Tensor], optional): Frames per second. Defaults to None.
|
| 116 |
+
h_ntk_factor (Optional[float], optional): Height NTK factor. If None, uses self.h_ntk_factor.
|
| 117 |
+
w_ntk_factor (Optional[float], optional): Width NTK factor. If None, uses self.w_ntk_factor.
|
| 118 |
+
t_ntk_factor (Optional[float], optional): Time NTK factor. If None, uses self.t_ntk_factor.
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
Not specified in the original code snippet.
|
| 122 |
+
"""
|
| 123 |
+
h_ntk_factor = h_ntk_factor if h_ntk_factor is not None else self.h_ntk_factor
|
| 124 |
+
w_ntk_factor = w_ntk_factor if w_ntk_factor is not None else self.w_ntk_factor
|
| 125 |
+
t_ntk_factor = t_ntk_factor if t_ntk_factor is not None else self.t_ntk_factor
|
| 126 |
+
|
| 127 |
+
h_theta = 10000.0 * h_ntk_factor
|
| 128 |
+
w_theta = 10000.0 * w_ntk_factor
|
| 129 |
+
t_theta = 10000.0 * t_ntk_factor
|
| 130 |
+
|
| 131 |
+
h_spatial_freqs = 1.0 / (h_theta**self.dim_spatial_range.to(device=device))
|
| 132 |
+
w_spatial_freqs = 1.0 / (w_theta**self.dim_spatial_range.to(device=device))
|
| 133 |
+
temporal_freqs = 1.0 / (t_theta**self.dim_temporal_range.to(device=device))
|
| 134 |
+
|
| 135 |
+
B, T, H, W, _ = B_T_H_W_C
|
| 136 |
+
seq = torch.arange(max(H, W, T), dtype=torch.float, device=device)
|
| 137 |
+
uniform_fps = (fps is None) or isinstance(fps, (int, float)) or (fps.min() == fps.max())
|
| 138 |
+
assert (
|
| 139 |
+
uniform_fps or B == 1 or T == 1
|
| 140 |
+
), "For video batch, batch size should be 1 for non-uniform fps. For image batch, T should be 1"
|
| 141 |
+
half_emb_h = torch.outer(seq[:H].to(device=device), h_spatial_freqs)
|
| 142 |
+
half_emb_w = torch.outer(seq[:W].to(device=device), w_spatial_freqs)
|
| 143 |
+
|
| 144 |
+
# apply sequence scaling in temporal dimension
|
| 145 |
+
if fps is None or self.enable_fps_modulation is False: # image case
|
| 146 |
+
half_emb_t = torch.outer(seq[:T].to(device=device), temporal_freqs)
|
| 147 |
+
else:
|
| 148 |
+
half_emb_t = torch.outer(seq[:T].to(device=device) / fps * self.base_fps, temporal_freqs)
|
| 149 |
+
|
| 150 |
+
half_emb_h = torch.stack([torch.cos(half_emb_h), -torch.sin(half_emb_h), torch.sin(half_emb_h), torch.cos(half_emb_h)], dim=-1)
|
| 151 |
+
half_emb_w = torch.stack([torch.cos(half_emb_w), -torch.sin(half_emb_w), torch.sin(half_emb_w), torch.cos(half_emb_w)], dim=-1)
|
| 152 |
+
half_emb_t = torch.stack([torch.cos(half_emb_t), -torch.sin(half_emb_t), torch.sin(half_emb_t), torch.cos(half_emb_t)], dim=-1)
|
| 153 |
+
|
| 154 |
+
em_T_H_W_D = torch.cat(
|
| 155 |
+
[
|
| 156 |
+
repeat(half_emb_t, "t d x -> t h w d x", h=H, w=W),
|
| 157 |
+
repeat(half_emb_h, "h d x -> t h w d x", t=T, w=W),
|
| 158 |
+
repeat(half_emb_w, "w d x -> t h w d x", t=T, h=H),
|
| 159 |
+
]
|
| 160 |
+
, dim=-2,
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
return rearrange(em_T_H_W_D, "t h w d (i j) -> (t h w) d i j", i=2, j=2).float()
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class LearnablePosEmbAxis(VideoPositionEmb):
|
| 167 |
+
def __init__(
|
| 168 |
+
self,
|
| 169 |
+
*, # enforce keyword arguments
|
| 170 |
+
interpolation: str,
|
| 171 |
+
model_channels: int,
|
| 172 |
+
len_h: int,
|
| 173 |
+
len_w: int,
|
| 174 |
+
len_t: int,
|
| 175 |
+
device=None,
|
| 176 |
+
dtype=None,
|
| 177 |
+
**kwargs,
|
| 178 |
+
):
|
| 179 |
+
"""
|
| 180 |
+
Args:
|
| 181 |
+
interpolation (str): we curretly only support "crop", ideally when we need extrapolation capacity, we should adjust frequency or other more advanced methods. they are not implemented yet.
|
| 182 |
+
"""
|
| 183 |
+
del kwargs # unused
|
| 184 |
+
super().__init__()
|
| 185 |
+
self.interpolation = interpolation
|
| 186 |
+
assert self.interpolation in ["crop"], f"Unknown interpolation method {self.interpolation}"
|
| 187 |
+
|
| 188 |
+
self.pos_emb_h = nn.Parameter(torch.empty(len_h, model_channels, device=device, dtype=dtype))
|
| 189 |
+
self.pos_emb_w = nn.Parameter(torch.empty(len_w, model_channels, device=device, dtype=dtype))
|
| 190 |
+
self.pos_emb_t = nn.Parameter(torch.empty(len_t, model_channels, device=device, dtype=dtype))
|
| 191 |
+
|
| 192 |
+
def generate_embeddings(self, B_T_H_W_C: torch.Size, fps=Optional[torch.Tensor], device=None, dtype=None) -> torch.Tensor:
|
| 193 |
+
B, T, H, W, _ = B_T_H_W_C
|
| 194 |
+
if self.interpolation == "crop":
|
| 195 |
+
emb_h_H = self.pos_emb_h[:H].to(device=device, dtype=dtype)
|
| 196 |
+
emb_w_W = self.pos_emb_w[:W].to(device=device, dtype=dtype)
|
| 197 |
+
emb_t_T = self.pos_emb_t[:T].to(device=device, dtype=dtype)
|
| 198 |
+
emb = (
|
| 199 |
+
repeat(emb_t_T, "t d-> b t h w d", b=B, h=H, w=W)
|
| 200 |
+
+ repeat(emb_h_H, "h d-> b t h w d", b=B, t=T, w=W)
|
| 201 |
+
+ repeat(emb_w_W, "w d-> b t h w d", b=B, t=T, h=H)
|
| 202 |
+
)
|
| 203 |
+
assert list(emb.shape)[:4] == [B, T, H, W], f"bad shape: {list(emb.shape)[:4]} != {B, T, H, W}"
|
| 204 |
+
else:
|
| 205 |
+
raise ValueError(f"Unknown interpolation method {self.interpolation}")
|
| 206 |
+
|
| 207 |
+
return normalize(emb, dim=-1, eps=1e-6)
|
ComfyUI/comfy/ldm/cosmos/predict2.py
ADDED
|
@@ -0,0 +1,932 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# original code from: https://github.com/nvidia-cosmos/cosmos-predict2
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
from torch import nn
|
| 5 |
+
from einops import rearrange
|
| 6 |
+
from einops.layers.torch import Rearrange
|
| 7 |
+
import logging
|
| 8 |
+
from typing import Callable, Optional, Tuple
|
| 9 |
+
import math
|
| 10 |
+
|
| 11 |
+
from .position_embedding import VideoRopePosition3DEmb, LearnablePosEmbAxis
|
| 12 |
+
from torchvision import transforms
|
| 13 |
+
|
| 14 |
+
import comfy.patcher_extension
|
| 15 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 16 |
+
import comfy.ldm.common_dit
|
| 17 |
+
import comfy.ops
|
| 18 |
+
import comfy.quant_ops
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# ---------------------- Feed Forward Network -----------------------
|
| 22 |
+
class GPT2FeedForward(nn.Module):
|
| 23 |
+
def __init__(self, d_model: int, d_ff: int, device=None, dtype=None, operations=None) -> None:
|
| 24 |
+
super().__init__()
|
| 25 |
+
self.activation = nn.GELU()
|
| 26 |
+
self.layer1 = operations.Linear(d_model, d_ff, bias=False, device=device, dtype=dtype)
|
| 27 |
+
self.layer2 = operations.Linear(d_ff, d_model, bias=False, device=device, dtype=dtype)
|
| 28 |
+
|
| 29 |
+
self._layer_id = None
|
| 30 |
+
self._dim = d_model
|
| 31 |
+
self._hidden_dim = d_ff
|
| 32 |
+
|
| 33 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 34 |
+
x = self.layer1(x)
|
| 35 |
+
|
| 36 |
+
x = self.activation(x)
|
| 37 |
+
x = self.layer2(x)
|
| 38 |
+
return x
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def torch_attention_op(q_B_S_H_D: torch.Tensor, k_B_S_H_D: torch.Tensor, v_B_S_H_D: torch.Tensor, transformer_options: Optional[dict] = {}) -> torch.Tensor:
|
| 42 |
+
"""Computes multi-head attention using PyTorch's native implementation.
|
| 43 |
+
|
| 44 |
+
This function provides a PyTorch backend alternative to Transformer Engine's attention operation.
|
| 45 |
+
It rearranges the input tensors to match PyTorch's expected format, computes scaled dot-product
|
| 46 |
+
attention, and rearranges the output back to the original format.
|
| 47 |
+
|
| 48 |
+
The input tensor names use the following dimension conventions:
|
| 49 |
+
|
| 50 |
+
- B: batch size
|
| 51 |
+
- S: sequence length
|
| 52 |
+
- H: number of attention heads
|
| 53 |
+
- D: head dimension
|
| 54 |
+
|
| 55 |
+
Args:
|
| 56 |
+
q_B_S_H_D: Query tensor with shape (batch, seq_len, n_heads, head_dim)
|
| 57 |
+
k_B_S_H_D: Key tensor with shape (batch, seq_len, n_heads, head_dim)
|
| 58 |
+
v_B_S_H_D: Value tensor with shape (batch, seq_len, n_heads, head_dim)
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
Attention output tensor with shape (batch, seq_len, n_heads * head_dim)
|
| 62 |
+
"""
|
| 63 |
+
in_q_shape = q_B_S_H_D.shape
|
| 64 |
+
in_k_shape = k_B_S_H_D.shape
|
| 65 |
+
q_B_H_S_D = rearrange(q_B_S_H_D, "b ... h k -> b h ... k").view(in_q_shape[0], in_q_shape[-2], -1, in_q_shape[-1])
|
| 66 |
+
k_B_H_S_D = rearrange(k_B_S_H_D, "b ... h v -> b h ... v").view(in_k_shape[0], in_k_shape[-2], -1, in_k_shape[-1])
|
| 67 |
+
v_B_H_S_D = rearrange(v_B_S_H_D, "b ... h v -> b h ... v").view(in_k_shape[0], in_k_shape[-2], -1, in_k_shape[-1])
|
| 68 |
+
return optimized_attention(q_B_H_S_D, k_B_H_S_D, v_B_H_S_D, in_q_shape[-2], skip_reshape=True, transformer_options=transformer_options)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Attention(nn.Module):
|
| 72 |
+
"""
|
| 73 |
+
A flexible attention module supporting both self-attention and cross-attention mechanisms.
|
| 74 |
+
|
| 75 |
+
This module implements a multi-head attention layer that can operate in either self-attention
|
| 76 |
+
or cross-attention mode. The mode is determined by whether a context dimension is provided.
|
| 77 |
+
The implementation uses scaled dot-product attention and supports optional bias terms and
|
| 78 |
+
dropout regularization.
|
| 79 |
+
|
| 80 |
+
Args:
|
| 81 |
+
query_dim (int): The dimensionality of the query vectors.
|
| 82 |
+
context_dim (int, optional): The dimensionality of the context (key/value) vectors.
|
| 83 |
+
If None, the module operates in self-attention mode using query_dim. Default: None
|
| 84 |
+
n_heads (int, optional): Number of attention heads for multi-head attention. Default: 8
|
| 85 |
+
head_dim (int, optional): The dimension of each attention head. Default: 64
|
| 86 |
+
dropout (float, optional): Dropout probability applied to the output. Default: 0.0
|
| 87 |
+
qkv_format (str, optional): Format specification for QKV tensors. Default: "bshd"
|
| 88 |
+
backend (str, optional): Backend to use for the attention operation. Default: "transformer_engine"
|
| 89 |
+
|
| 90 |
+
Examples:
|
| 91 |
+
>>> # Self-attention with 512 dimensions and 8 heads
|
| 92 |
+
>>> self_attn = Attention(query_dim=512)
|
| 93 |
+
>>> x = torch.randn(32, 16, 512) # (batch_size, seq_len, dim)
|
| 94 |
+
>>> out = self_attn(x) # (32, 16, 512)
|
| 95 |
+
|
| 96 |
+
>>> # Cross-attention
|
| 97 |
+
>>> cross_attn = Attention(query_dim=512, context_dim=256)
|
| 98 |
+
>>> query = torch.randn(32, 16, 512)
|
| 99 |
+
>>> context = torch.randn(32, 8, 256)
|
| 100 |
+
>>> out = cross_attn(query, context) # (32, 16, 512)
|
| 101 |
+
"""
|
| 102 |
+
|
| 103 |
+
def __init__(
|
| 104 |
+
self,
|
| 105 |
+
query_dim: int,
|
| 106 |
+
context_dim: Optional[int] = None,
|
| 107 |
+
n_heads: int = 8,
|
| 108 |
+
head_dim: int = 64,
|
| 109 |
+
dropout: float = 0.0,
|
| 110 |
+
device=None,
|
| 111 |
+
dtype=None,
|
| 112 |
+
operations=None,
|
| 113 |
+
) -> None:
|
| 114 |
+
super().__init__()
|
| 115 |
+
logging.debug(
|
| 116 |
+
f"Setting up {self.__class__.__name__}. Query dim is {query_dim}, context_dim is {context_dim} and using "
|
| 117 |
+
f"{n_heads} heads with a dimension of {head_dim}."
|
| 118 |
+
)
|
| 119 |
+
self.is_selfattn = context_dim is None # self attention
|
| 120 |
+
|
| 121 |
+
context_dim = query_dim if context_dim is None else context_dim
|
| 122 |
+
inner_dim = head_dim * n_heads
|
| 123 |
+
|
| 124 |
+
self.n_heads = n_heads
|
| 125 |
+
self.head_dim = head_dim
|
| 126 |
+
self.query_dim = query_dim
|
| 127 |
+
self.context_dim = context_dim
|
| 128 |
+
|
| 129 |
+
self.q_proj = operations.Linear(query_dim, inner_dim, bias=False, device=device, dtype=dtype)
|
| 130 |
+
self.q_norm = operations.RMSNorm(self.head_dim, eps=1e-6, device=device, dtype=dtype)
|
| 131 |
+
|
| 132 |
+
self.k_proj = operations.Linear(context_dim, inner_dim, bias=False, device=device, dtype=dtype)
|
| 133 |
+
self.k_norm = operations.RMSNorm(self.head_dim, eps=1e-6, device=device, dtype=dtype)
|
| 134 |
+
|
| 135 |
+
self.v_proj = operations.Linear(context_dim, inner_dim, bias=False, device=device, dtype=dtype)
|
| 136 |
+
self.v_norm = nn.Identity()
|
| 137 |
+
|
| 138 |
+
self.output_proj = operations.Linear(inner_dim, query_dim, bias=False, device=device, dtype=dtype)
|
| 139 |
+
self.output_dropout = nn.Dropout(dropout) if dropout > 1e-4 else nn.Identity()
|
| 140 |
+
|
| 141 |
+
self.attn_op = torch_attention_op
|
| 142 |
+
|
| 143 |
+
self._query_dim = query_dim
|
| 144 |
+
self._context_dim = context_dim
|
| 145 |
+
self._inner_dim = inner_dim
|
| 146 |
+
|
| 147 |
+
def compute_qkv(
|
| 148 |
+
self,
|
| 149 |
+
x: torch.Tensor,
|
| 150 |
+
context: Optional[torch.Tensor] = None,
|
| 151 |
+
rope_emb: Optional[torch.Tensor] = None,
|
| 152 |
+
transformer_options: Optional[dict] = {},
|
| 153 |
+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 154 |
+
context = x if context is None else context
|
| 155 |
+
q_input = x
|
| 156 |
+
k_input = context
|
| 157 |
+
v_input = context
|
| 158 |
+
|
| 159 |
+
transformer_patches = transformer_options.get("patches", {})
|
| 160 |
+
patch_name = "attn1_patch" if self.is_selfattn else "attn2_patch"
|
| 161 |
+
if patch_name in transformer_patches:
|
| 162 |
+
extra_options = transformer_options.copy()
|
| 163 |
+
extra_options["n_heads"] = self.n_heads
|
| 164 |
+
extra_options["dim_head"] = self.head_dim
|
| 165 |
+
for patch in transformer_patches[patch_name]:
|
| 166 |
+
out = patch(q_input, k_input, v_input, pe=rope_emb, attn_mask=None, extra_options=extra_options)
|
| 167 |
+
q_input = out.get("q", q_input)
|
| 168 |
+
k_input = out.get("k", k_input)
|
| 169 |
+
v_input = out.get("v", v_input)
|
| 170 |
+
rope_emb = out.get("pe", rope_emb)
|
| 171 |
+
|
| 172 |
+
q = self.q_proj(q_input)
|
| 173 |
+
k = self.k_proj(k_input)
|
| 174 |
+
v = self.v_proj(v_input)
|
| 175 |
+
q, k, v = map(
|
| 176 |
+
lambda t: rearrange(t, "b ... (h d) -> b ... h d", h=self.n_heads, d=self.head_dim),
|
| 177 |
+
(q, k, v),
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
def apply_norm_and_rotary_pos_emb(
|
| 181 |
+
q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, rope_emb: Optional[torch.Tensor]
|
| 182 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 183 |
+
v = self.v_norm(v)
|
| 184 |
+
if self.is_selfattn and rope_emb is not None: # only apply to self-attention!
|
| 185 |
+
q_scale, _, q_offload_stream = comfy.ops.cast_bias_weight(self.q_norm, q, offloadable=True)
|
| 186 |
+
k_scale, _, k_offload_stream = comfy.ops.cast_bias_weight(self.k_norm, k, offloadable=True)
|
| 187 |
+
q, k = comfy.quant_ops.ck.rms_rope_split_half(q, k, rope_emb, q_scale, k_scale, self.q_norm.eps)
|
| 188 |
+
comfy.ops.uncast_bias_weight(self.q_norm, q_scale, None, q_offload_stream)
|
| 189 |
+
comfy.ops.uncast_bias_weight(self.k_norm, k_scale, None, k_offload_stream)
|
| 190 |
+
else:
|
| 191 |
+
q = self.q_norm(q)
|
| 192 |
+
k = self.k_norm(k)
|
| 193 |
+
return q, k, v
|
| 194 |
+
|
| 195 |
+
q, k, v = apply_norm_and_rotary_pos_emb(q, k, v, rope_emb)
|
| 196 |
+
|
| 197 |
+
return q, k, v
|
| 198 |
+
|
| 199 |
+
def compute_attention(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, transformer_options: Optional[dict] = {}) -> torch.Tensor:
|
| 200 |
+
result = self.attn_op(q, k, v, transformer_options=transformer_options) # [B, S, H, D]
|
| 201 |
+
return self.output_dropout(self.output_proj(result))
|
| 202 |
+
|
| 203 |
+
def forward(
|
| 204 |
+
self,
|
| 205 |
+
x: torch.Tensor,
|
| 206 |
+
context: Optional[torch.Tensor] = None,
|
| 207 |
+
rope_emb: Optional[torch.Tensor] = None,
|
| 208 |
+
transformer_options: Optional[dict] = {},
|
| 209 |
+
) -> torch.Tensor:
|
| 210 |
+
"""
|
| 211 |
+
Args:
|
| 212 |
+
x (Tensor): The query tensor of shape [B, Mq, K]
|
| 213 |
+
context (Optional[Tensor]): The key tensor of shape [B, Mk, K] or use x as context [self attention] if None
|
| 214 |
+
"""
|
| 215 |
+
q, k, v = self.compute_qkv(x, context, rope_emb=rope_emb, transformer_options=transformer_options)
|
| 216 |
+
return self.compute_attention(q, k, v, transformer_options=transformer_options)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
class Timesteps(nn.Module):
|
| 220 |
+
def __init__(self, num_channels: int):
|
| 221 |
+
super().__init__()
|
| 222 |
+
self.num_channels = num_channels
|
| 223 |
+
|
| 224 |
+
def forward(self, timesteps_B_T: torch.Tensor) -> torch.Tensor:
|
| 225 |
+
assert timesteps_B_T.ndim == 2, f"Expected 2D input, got {timesteps_B_T.ndim}"
|
| 226 |
+
timesteps = timesteps_B_T.flatten().float()
|
| 227 |
+
half_dim = self.num_channels // 2
|
| 228 |
+
exponent = -math.log(10000) * torch.arange(half_dim, dtype=torch.float32, device=timesteps.device)
|
| 229 |
+
exponent = exponent / (half_dim - 0.0)
|
| 230 |
+
|
| 231 |
+
emb = torch.exp(exponent)
|
| 232 |
+
emb = timesteps[:, None].float() * emb[None, :]
|
| 233 |
+
|
| 234 |
+
sin_emb = torch.sin(emb)
|
| 235 |
+
cos_emb = torch.cos(emb)
|
| 236 |
+
emb = torch.cat([cos_emb, sin_emb], dim=-1)
|
| 237 |
+
|
| 238 |
+
return rearrange(emb, "(b t) d -> b t d", b=timesteps_B_T.shape[0], t=timesteps_B_T.shape[1])
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
class TimestepEmbedding(nn.Module):
|
| 242 |
+
def __init__(self, in_features: int, out_features: int, use_adaln_lora: bool = False, device=None, dtype=None, operations=None):
|
| 243 |
+
super().__init__()
|
| 244 |
+
logging.debug(
|
| 245 |
+
f"Using AdaLN LoRA Flag: {use_adaln_lora}. We enable bias if no AdaLN LoRA for backward compatibility."
|
| 246 |
+
)
|
| 247 |
+
self.in_dim = in_features
|
| 248 |
+
self.out_dim = out_features
|
| 249 |
+
self.linear_1 = operations.Linear(in_features, out_features, bias=not use_adaln_lora, device=device, dtype=dtype)
|
| 250 |
+
self.activation = nn.SiLU()
|
| 251 |
+
self.use_adaln_lora = use_adaln_lora
|
| 252 |
+
if use_adaln_lora:
|
| 253 |
+
self.linear_2 = operations.Linear(out_features, 3 * out_features, bias=False, device=device, dtype=dtype)
|
| 254 |
+
else:
|
| 255 |
+
self.linear_2 = operations.Linear(out_features, out_features, bias=False, device=device, dtype=dtype)
|
| 256 |
+
|
| 257 |
+
def forward(self, sample: torch.Tensor) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 258 |
+
emb = self.linear_1(sample)
|
| 259 |
+
emb = self.activation(emb)
|
| 260 |
+
emb = self.linear_2(emb)
|
| 261 |
+
|
| 262 |
+
if self.use_adaln_lora:
|
| 263 |
+
adaln_lora_B_T_3D = emb
|
| 264 |
+
emb_B_T_D = sample
|
| 265 |
+
else:
|
| 266 |
+
adaln_lora_B_T_3D = None
|
| 267 |
+
emb_B_T_D = emb
|
| 268 |
+
|
| 269 |
+
return emb_B_T_D, adaln_lora_B_T_3D
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
class PatchEmbed(nn.Module):
|
| 273 |
+
"""
|
| 274 |
+
PatchEmbed is a module for embedding patches from an input tensor by applying either 3D or 2D convolutional layers,
|
| 275 |
+
depending on the . This module can process inputs with temporal (video) and spatial (image) dimensions,
|
| 276 |
+
making it suitable for video and image processing tasks. It supports dividing the input into patches
|
| 277 |
+
and embedding each patch into a vector of size `out_channels`.
|
| 278 |
+
|
| 279 |
+
Parameters:
|
| 280 |
+
- spatial_patch_size (int): The size of each spatial patch.
|
| 281 |
+
- temporal_patch_size (int): The size of each temporal patch.
|
| 282 |
+
- in_channels (int): Number of input channels. Default: 3.
|
| 283 |
+
- out_channels (int): The dimension of the embedding vector for each patch. Default: 768.
|
| 284 |
+
- bias (bool): If True, adds a learnable bias to the output of the convolutional layers. Default: True.
|
| 285 |
+
"""
|
| 286 |
+
|
| 287 |
+
def __init__(
|
| 288 |
+
self,
|
| 289 |
+
spatial_patch_size: int,
|
| 290 |
+
temporal_patch_size: int,
|
| 291 |
+
in_channels: int = 3,
|
| 292 |
+
out_channels: int = 768,
|
| 293 |
+
device=None, dtype=None, operations=None
|
| 294 |
+
):
|
| 295 |
+
super().__init__()
|
| 296 |
+
self.spatial_patch_size = spatial_patch_size
|
| 297 |
+
self.temporal_patch_size = temporal_patch_size
|
| 298 |
+
|
| 299 |
+
self.proj = nn.Sequential(
|
| 300 |
+
Rearrange(
|
| 301 |
+
"b c (t r) (h m) (w n) -> b t h w (c r m n)",
|
| 302 |
+
r=temporal_patch_size,
|
| 303 |
+
m=spatial_patch_size,
|
| 304 |
+
n=spatial_patch_size,
|
| 305 |
+
),
|
| 306 |
+
operations.Linear(
|
| 307 |
+
in_channels * spatial_patch_size * spatial_patch_size * temporal_patch_size, out_channels, bias=False, device=device, dtype=dtype
|
| 308 |
+
),
|
| 309 |
+
)
|
| 310 |
+
self.dim = in_channels * spatial_patch_size * spatial_patch_size * temporal_patch_size
|
| 311 |
+
|
| 312 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 313 |
+
"""
|
| 314 |
+
Forward pass of the PatchEmbed module.
|
| 315 |
+
|
| 316 |
+
Parameters:
|
| 317 |
+
- x (torch.Tensor): The input tensor of shape (B, C, T, H, W) where
|
| 318 |
+
B is the batch size,
|
| 319 |
+
C is the number of channels,
|
| 320 |
+
T is the temporal dimension,
|
| 321 |
+
H is the height, and
|
| 322 |
+
W is the width of the input.
|
| 323 |
+
|
| 324 |
+
Returns:
|
| 325 |
+
- torch.Tensor: The embedded patches as a tensor, with shape b t h w c.
|
| 326 |
+
"""
|
| 327 |
+
assert x.dim() == 5
|
| 328 |
+
_, _, T, H, W = x.shape
|
| 329 |
+
assert (
|
| 330 |
+
H % self.spatial_patch_size == 0 and W % self.spatial_patch_size == 0
|
| 331 |
+
), f"H,W {(H, W)} should be divisible by spatial_patch_size {self.spatial_patch_size}"
|
| 332 |
+
assert T % self.temporal_patch_size == 0
|
| 333 |
+
x = self.proj(x)
|
| 334 |
+
return x
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
class FinalLayer(nn.Module):
|
| 338 |
+
"""
|
| 339 |
+
The final layer of video DiT.
|
| 340 |
+
"""
|
| 341 |
+
|
| 342 |
+
def __init__(
|
| 343 |
+
self,
|
| 344 |
+
hidden_size: int,
|
| 345 |
+
spatial_patch_size: int,
|
| 346 |
+
temporal_patch_size: int,
|
| 347 |
+
out_channels: int,
|
| 348 |
+
use_adaln_lora: bool = False,
|
| 349 |
+
adaln_lora_dim: int = 256,
|
| 350 |
+
device=None, dtype=None, operations=None
|
| 351 |
+
):
|
| 352 |
+
super().__init__()
|
| 353 |
+
self.layer_norm = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
| 354 |
+
self.linear = operations.Linear(
|
| 355 |
+
hidden_size, spatial_patch_size * spatial_patch_size * temporal_patch_size * out_channels, bias=False, device=device, dtype=dtype
|
| 356 |
+
)
|
| 357 |
+
self.hidden_size = hidden_size
|
| 358 |
+
self.n_adaln_chunks = 2
|
| 359 |
+
self.use_adaln_lora = use_adaln_lora
|
| 360 |
+
self.adaln_lora_dim = adaln_lora_dim
|
| 361 |
+
if use_adaln_lora:
|
| 362 |
+
self.adaln_modulation = nn.Sequential(
|
| 363 |
+
nn.SiLU(),
|
| 364 |
+
operations.Linear(hidden_size, adaln_lora_dim, bias=False, device=device, dtype=dtype),
|
| 365 |
+
operations.Linear(adaln_lora_dim, self.n_adaln_chunks * hidden_size, bias=False, device=device, dtype=dtype),
|
| 366 |
+
)
|
| 367 |
+
else:
|
| 368 |
+
self.adaln_modulation = nn.Sequential(
|
| 369 |
+
nn.SiLU(), operations.Linear(hidden_size, self.n_adaln_chunks * hidden_size, bias=False, device=device, dtype=dtype)
|
| 370 |
+
)
|
| 371 |
+
|
| 372 |
+
def forward(
|
| 373 |
+
self,
|
| 374 |
+
x_B_T_H_W_D: torch.Tensor,
|
| 375 |
+
emb_B_T_D: torch.Tensor,
|
| 376 |
+
adaln_lora_B_T_3D: Optional[torch.Tensor] = None,
|
| 377 |
+
):
|
| 378 |
+
if self.use_adaln_lora:
|
| 379 |
+
assert adaln_lora_B_T_3D is not None
|
| 380 |
+
shift_B_T_D, scale_B_T_D = (
|
| 381 |
+
self.adaln_modulation(emb_B_T_D) + adaln_lora_B_T_3D[:, :, : 2 * self.hidden_size]
|
| 382 |
+
).chunk(2, dim=-1)
|
| 383 |
+
else:
|
| 384 |
+
shift_B_T_D, scale_B_T_D = self.adaln_modulation(emb_B_T_D).chunk(2, dim=-1)
|
| 385 |
+
|
| 386 |
+
shift_B_T_1_1_D, scale_B_T_1_1_D = rearrange(shift_B_T_D, "b t d -> b t 1 1 d"), rearrange(
|
| 387 |
+
scale_B_T_D, "b t d -> b t 1 1 d"
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
+
def _fn(
|
| 391 |
+
_x_B_T_H_W_D: torch.Tensor,
|
| 392 |
+
_norm_layer: nn.Module,
|
| 393 |
+
_scale_B_T_1_1_D: torch.Tensor,
|
| 394 |
+
_shift_B_T_1_1_D: torch.Tensor,
|
| 395 |
+
) -> torch.Tensor:
|
| 396 |
+
return _norm_layer(_x_B_T_H_W_D) * (1 + _scale_B_T_1_1_D) + _shift_B_T_1_1_D
|
| 397 |
+
|
| 398 |
+
x_B_T_H_W_D = _fn(x_B_T_H_W_D, self.layer_norm, scale_B_T_1_1_D, shift_B_T_1_1_D)
|
| 399 |
+
x_B_T_H_W_O = self.linear(x_B_T_H_W_D)
|
| 400 |
+
return x_B_T_H_W_O
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
class Block(nn.Module):
|
| 404 |
+
"""
|
| 405 |
+
A transformer block that combines self-attention, cross-attention and MLP layers with AdaLN modulation.
|
| 406 |
+
Each component (self-attention, cross-attention, MLP) has its own layer normalization and AdaLN modulation.
|
| 407 |
+
|
| 408 |
+
Parameters:
|
| 409 |
+
x_dim (int): Dimension of input features
|
| 410 |
+
context_dim (int): Dimension of context features for cross-attention
|
| 411 |
+
num_heads (int): Number of attention heads
|
| 412 |
+
mlp_ratio (float): Multiplier for MLP hidden dimension. Default: 4.0
|
| 413 |
+
use_adaln_lora (bool): Whether to use AdaLN-LoRA modulation. Default: False
|
| 414 |
+
adaln_lora_dim (int): Hidden dimension for AdaLN-LoRA layers. Default: 256
|
| 415 |
+
|
| 416 |
+
The block applies the following sequence:
|
| 417 |
+
1. Self-attention with AdaLN modulation
|
| 418 |
+
2. Cross-attention with AdaLN modulation
|
| 419 |
+
3. MLP with AdaLN modulation
|
| 420 |
+
|
| 421 |
+
Each component uses skip connections and layer normalization.
|
| 422 |
+
"""
|
| 423 |
+
|
| 424 |
+
def __init__(
|
| 425 |
+
self,
|
| 426 |
+
x_dim: int,
|
| 427 |
+
context_dim: int,
|
| 428 |
+
num_heads: int,
|
| 429 |
+
mlp_ratio: float = 4.0,
|
| 430 |
+
use_adaln_lora: bool = False,
|
| 431 |
+
adaln_lora_dim: int = 256,
|
| 432 |
+
device=None,
|
| 433 |
+
dtype=None,
|
| 434 |
+
operations=None,
|
| 435 |
+
):
|
| 436 |
+
super().__init__()
|
| 437 |
+
self.x_dim = x_dim
|
| 438 |
+
self.layer_norm_self_attn = operations.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype)
|
| 439 |
+
self.self_attn = Attention(x_dim, None, num_heads, x_dim // num_heads, device=device, dtype=dtype, operations=operations)
|
| 440 |
+
|
| 441 |
+
self.layer_norm_cross_attn = operations.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype)
|
| 442 |
+
self.cross_attn = Attention(
|
| 443 |
+
x_dim, context_dim, num_heads, x_dim // num_heads, device=device, dtype=dtype, operations=operations
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
self.layer_norm_mlp = operations.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype)
|
| 447 |
+
self.mlp = GPT2FeedForward(x_dim, int(x_dim * mlp_ratio), device=device, dtype=dtype, operations=operations)
|
| 448 |
+
|
| 449 |
+
self.use_adaln_lora = use_adaln_lora
|
| 450 |
+
if self.use_adaln_lora:
|
| 451 |
+
self.adaln_modulation_self_attn = nn.Sequential(
|
| 452 |
+
nn.SiLU(),
|
| 453 |
+
operations.Linear(x_dim, adaln_lora_dim, bias=False, device=device, dtype=dtype),
|
| 454 |
+
operations.Linear(adaln_lora_dim, 3 * x_dim, bias=False, device=device, dtype=dtype),
|
| 455 |
+
)
|
| 456 |
+
self.adaln_modulation_cross_attn = nn.Sequential(
|
| 457 |
+
nn.SiLU(),
|
| 458 |
+
operations.Linear(x_dim, adaln_lora_dim, bias=False, device=device, dtype=dtype),
|
| 459 |
+
operations.Linear(adaln_lora_dim, 3 * x_dim, bias=False, device=device, dtype=dtype),
|
| 460 |
+
)
|
| 461 |
+
self.adaln_modulation_mlp = nn.Sequential(
|
| 462 |
+
nn.SiLU(),
|
| 463 |
+
operations.Linear(x_dim, adaln_lora_dim, bias=False, device=device, dtype=dtype),
|
| 464 |
+
operations.Linear(adaln_lora_dim, 3 * x_dim, bias=False, device=device, dtype=dtype),
|
| 465 |
+
)
|
| 466 |
+
else:
|
| 467 |
+
self.adaln_modulation_self_attn = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, 3 * x_dim, bias=False, device=device, dtype=dtype))
|
| 468 |
+
self.adaln_modulation_cross_attn = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, 3 * x_dim, bias=False, device=device, dtype=dtype))
|
| 469 |
+
self.adaln_modulation_mlp = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, 3 * x_dim, bias=False, device=device, dtype=dtype))
|
| 470 |
+
|
| 471 |
+
def forward(
|
| 472 |
+
self,
|
| 473 |
+
x_B_T_H_W_D: torch.Tensor,
|
| 474 |
+
emb_B_T_D: torch.Tensor,
|
| 475 |
+
crossattn_emb: torch.Tensor,
|
| 476 |
+
rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
|
| 477 |
+
adaln_lora_B_T_3D: Optional[torch.Tensor] = None,
|
| 478 |
+
extra_per_block_pos_emb: Optional[torch.Tensor] = None,
|
| 479 |
+
transformer_options: Optional[dict] = {},
|
| 480 |
+
) -> torch.Tensor:
|
| 481 |
+
residual_dtype = x_B_T_H_W_D.dtype
|
| 482 |
+
compute_dtype = emb_B_T_D.dtype
|
| 483 |
+
if extra_per_block_pos_emb is not None:
|
| 484 |
+
x_B_T_H_W_D = x_B_T_H_W_D + extra_per_block_pos_emb
|
| 485 |
+
|
| 486 |
+
if self.use_adaln_lora:
|
| 487 |
+
shift_self_attn_B_T_D, scale_self_attn_B_T_D, gate_self_attn_B_T_D = (
|
| 488 |
+
self.adaln_modulation_self_attn(emb_B_T_D) + adaln_lora_B_T_3D
|
| 489 |
+
).chunk(3, dim=-1)
|
| 490 |
+
shift_cross_attn_B_T_D, scale_cross_attn_B_T_D, gate_cross_attn_B_T_D = (
|
| 491 |
+
self.adaln_modulation_cross_attn(emb_B_T_D) + adaln_lora_B_T_3D
|
| 492 |
+
).chunk(3, dim=-1)
|
| 493 |
+
shift_mlp_B_T_D, scale_mlp_B_T_D, gate_mlp_B_T_D = (
|
| 494 |
+
self.adaln_modulation_mlp(emb_B_T_D) + adaln_lora_B_T_3D
|
| 495 |
+
).chunk(3, dim=-1)
|
| 496 |
+
else:
|
| 497 |
+
shift_self_attn_B_T_D, scale_self_attn_B_T_D, gate_self_attn_B_T_D = self.adaln_modulation_self_attn(
|
| 498 |
+
emb_B_T_D
|
| 499 |
+
).chunk(3, dim=-1)
|
| 500 |
+
shift_cross_attn_B_T_D, scale_cross_attn_B_T_D, gate_cross_attn_B_T_D = self.adaln_modulation_cross_attn(
|
| 501 |
+
emb_B_T_D
|
| 502 |
+
).chunk(3, dim=-1)
|
| 503 |
+
shift_mlp_B_T_D, scale_mlp_B_T_D, gate_mlp_B_T_D = self.adaln_modulation_mlp(emb_B_T_D).chunk(3, dim=-1)
|
| 504 |
+
|
| 505 |
+
# Reshape tensors from (B, T, D) to (B, T, 1, 1, D) for broadcasting
|
| 506 |
+
shift_self_attn_B_T_1_1_D = rearrange(shift_self_attn_B_T_D, "b t d -> b t 1 1 d")
|
| 507 |
+
scale_self_attn_B_T_1_1_D = rearrange(scale_self_attn_B_T_D, "b t d -> b t 1 1 d")
|
| 508 |
+
gate_self_attn_B_T_1_1_D = rearrange(gate_self_attn_B_T_D, "b t d -> b t 1 1 d")
|
| 509 |
+
|
| 510 |
+
shift_cross_attn_B_T_1_1_D = rearrange(shift_cross_attn_B_T_D, "b t d -> b t 1 1 d")
|
| 511 |
+
scale_cross_attn_B_T_1_1_D = rearrange(scale_cross_attn_B_T_D, "b t d -> b t 1 1 d")
|
| 512 |
+
gate_cross_attn_B_T_1_1_D = rearrange(gate_cross_attn_B_T_D, "b t d -> b t 1 1 d")
|
| 513 |
+
|
| 514 |
+
shift_mlp_B_T_1_1_D = rearrange(shift_mlp_B_T_D, "b t d -> b t 1 1 d")
|
| 515 |
+
scale_mlp_B_T_1_1_D = rearrange(scale_mlp_B_T_D, "b t d -> b t 1 1 d")
|
| 516 |
+
gate_mlp_B_T_1_1_D = rearrange(gate_mlp_B_T_D, "b t d -> b t 1 1 d")
|
| 517 |
+
|
| 518 |
+
B, T, H, W, D = x_B_T_H_W_D.shape
|
| 519 |
+
|
| 520 |
+
def _fn(_x_B_T_H_W_D, _norm_layer, _scale_B_T_1_1_D, _shift_B_T_1_1_D):
|
| 521 |
+
return _norm_layer(_x_B_T_H_W_D) * (1 + _scale_B_T_1_1_D) + _shift_B_T_1_1_D
|
| 522 |
+
|
| 523 |
+
normalized_x_B_T_H_W_D = _fn(
|
| 524 |
+
x_B_T_H_W_D,
|
| 525 |
+
self.layer_norm_self_attn,
|
| 526 |
+
scale_self_attn_B_T_1_1_D,
|
| 527 |
+
shift_self_attn_B_T_1_1_D,
|
| 528 |
+
)
|
| 529 |
+
result_B_T_H_W_D = rearrange(
|
| 530 |
+
self.self_attn(
|
| 531 |
+
# normalized_x_B_T_HW_D,
|
| 532 |
+
rearrange(normalized_x_B_T_H_W_D.to(compute_dtype), "b t h w d -> b (t h w) d"),
|
| 533 |
+
None,
|
| 534 |
+
rope_emb=rope_emb_L_1_1_D,
|
| 535 |
+
transformer_options=transformer_options,
|
| 536 |
+
),
|
| 537 |
+
"b (t h w) d -> b t h w d",
|
| 538 |
+
t=T,
|
| 539 |
+
h=H,
|
| 540 |
+
w=W,
|
| 541 |
+
)
|
| 542 |
+
x_B_T_H_W_D = torch.addcmul(x_B_T_H_W_D, gate_self_attn_B_T_1_1_D.to(residual_dtype), result_B_T_H_W_D.to(residual_dtype))
|
| 543 |
+
|
| 544 |
+
def _x_fn(
|
| 545 |
+
_x_B_T_H_W_D: torch.Tensor,
|
| 546 |
+
layer_norm_cross_attn: Callable,
|
| 547 |
+
_scale_cross_attn_B_T_1_1_D: torch.Tensor,
|
| 548 |
+
_shift_cross_attn_B_T_1_1_D: torch.Tensor,
|
| 549 |
+
transformer_options: Optional[dict] = {},
|
| 550 |
+
) -> torch.Tensor:
|
| 551 |
+
_normalized_x_B_T_H_W_D = _fn(
|
| 552 |
+
_x_B_T_H_W_D, layer_norm_cross_attn, _scale_cross_attn_B_T_1_1_D, _shift_cross_attn_B_T_1_1_D
|
| 553 |
+
)
|
| 554 |
+
_result_B_T_H_W_D = rearrange(
|
| 555 |
+
self.cross_attn(
|
| 556 |
+
rearrange(_normalized_x_B_T_H_W_D.to(compute_dtype), "b t h w d -> b (t h w) d"),
|
| 557 |
+
crossattn_emb,
|
| 558 |
+
rope_emb=rope_emb_L_1_1_D,
|
| 559 |
+
transformer_options=transformer_options,
|
| 560 |
+
),
|
| 561 |
+
"b (t h w) d -> b t h w d",
|
| 562 |
+
t=T,
|
| 563 |
+
h=H,
|
| 564 |
+
w=W,
|
| 565 |
+
)
|
| 566 |
+
return _result_B_T_H_W_D
|
| 567 |
+
|
| 568 |
+
result_B_T_H_W_D = _x_fn(
|
| 569 |
+
x_B_T_H_W_D,
|
| 570 |
+
self.layer_norm_cross_attn,
|
| 571 |
+
scale_cross_attn_B_T_1_1_D,
|
| 572 |
+
shift_cross_attn_B_T_1_1_D,
|
| 573 |
+
transformer_options=transformer_options,
|
| 574 |
+
)
|
| 575 |
+
x_B_T_H_W_D = torch.addcmul(x_B_T_H_W_D, gate_cross_attn_B_T_1_1_D.to(residual_dtype), result_B_T_H_W_D.to(residual_dtype))
|
| 576 |
+
|
| 577 |
+
normalized_x_B_T_H_W_D = _fn(
|
| 578 |
+
x_B_T_H_W_D,
|
| 579 |
+
self.layer_norm_mlp,
|
| 580 |
+
scale_mlp_B_T_1_1_D,
|
| 581 |
+
shift_mlp_B_T_1_1_D,
|
| 582 |
+
).to(compute_dtype)
|
| 583 |
+
patches = transformer_options.get("patches", {})
|
| 584 |
+
if "mlp_patch" in patches:
|
| 585 |
+
args = {"x": normalized_x_B_T_H_W_D, "transformer_options": transformer_options}
|
| 586 |
+
for patch in patches["mlp_patch"]:
|
| 587 |
+
args = patch(args)
|
| 588 |
+
normalized_x_B_T_H_W_D = args["x"]
|
| 589 |
+
result_B_T_H_W_D = self.mlp(normalized_x_B_T_H_W_D)
|
| 590 |
+
x_B_T_H_W_D = torch.addcmul(x_B_T_H_W_D, gate_mlp_B_T_1_1_D.to(residual_dtype), result_B_T_H_W_D.to(residual_dtype))
|
| 591 |
+
return x_B_T_H_W_D
|
| 592 |
+
|
| 593 |
+
|
| 594 |
+
class MiniTrainDIT(nn.Module):
|
| 595 |
+
"""
|
| 596 |
+
A clean impl of DIT that can load and reproduce the training results of the original DIT model in~(cosmos 1)
|
| 597 |
+
A general implementation of adaln-modulated VIT-like~(DiT) transformer for video processing.
|
| 598 |
+
|
| 599 |
+
Args:
|
| 600 |
+
max_img_h (int): Maximum height of the input images.
|
| 601 |
+
max_img_w (int): Maximum width of the input images.
|
| 602 |
+
max_frames (int): Maximum number of frames in the video sequence.
|
| 603 |
+
in_channels (int): Number of input channels (e.g., RGB channels for color images).
|
| 604 |
+
out_channels (int): Number of output channels.
|
| 605 |
+
patch_spatial (tuple): Spatial resolution of patches for input processing.
|
| 606 |
+
patch_temporal (int): Temporal resolution of patches for input processing.
|
| 607 |
+
concat_padding_mask (bool): If True, includes a mask channel in the input to handle padding.
|
| 608 |
+
model_channels (int): Base number of channels used throughout the model.
|
| 609 |
+
num_blocks (int): Number of transformer blocks.
|
| 610 |
+
num_heads (int): Number of heads in the multi-head attention layers.
|
| 611 |
+
mlp_ratio (float): Expansion ratio for MLP blocks.
|
| 612 |
+
crossattn_emb_channels (int): Number of embedding channels for cross-attention.
|
| 613 |
+
pos_emb_cls (str): Type of positional embeddings.
|
| 614 |
+
pos_emb_learnable (bool): Whether positional embeddings are learnable.
|
| 615 |
+
pos_emb_interpolation (str): Method for interpolating positional embeddings.
|
| 616 |
+
min_fps (int): Minimum frames per second.
|
| 617 |
+
max_fps (int): Maximum frames per second.
|
| 618 |
+
use_adaln_lora (bool): Whether to use AdaLN-LoRA.
|
| 619 |
+
adaln_lora_dim (int): Dimension for AdaLN-LoRA.
|
| 620 |
+
rope_h_extrapolation_ratio (float): Height extrapolation ratio for RoPE.
|
| 621 |
+
rope_w_extrapolation_ratio (float): Width extrapolation ratio for RoPE.
|
| 622 |
+
rope_t_extrapolation_ratio (float): Temporal extrapolation ratio for RoPE.
|
| 623 |
+
extra_per_block_abs_pos_emb (bool): Whether to use extra per-block absolute positional embeddings.
|
| 624 |
+
extra_h_extrapolation_ratio (float): Height extrapolation ratio for extra embeddings.
|
| 625 |
+
extra_w_extrapolation_ratio (float): Width extrapolation ratio for extra embeddings.
|
| 626 |
+
extra_t_extrapolation_ratio (float): Temporal extrapolation ratio for extra embeddings.
|
| 627 |
+
"""
|
| 628 |
+
|
| 629 |
+
def __init__(
|
| 630 |
+
self,
|
| 631 |
+
max_img_h: int,
|
| 632 |
+
max_img_w: int,
|
| 633 |
+
max_frames: int,
|
| 634 |
+
in_channels: int,
|
| 635 |
+
out_channels: int,
|
| 636 |
+
patch_spatial: int, # tuple,
|
| 637 |
+
patch_temporal: int,
|
| 638 |
+
concat_padding_mask: bool = True,
|
| 639 |
+
# attention settings
|
| 640 |
+
model_channels: int = 768,
|
| 641 |
+
num_blocks: int = 10,
|
| 642 |
+
num_heads: int = 16,
|
| 643 |
+
mlp_ratio: float = 4.0,
|
| 644 |
+
# cross attention settings
|
| 645 |
+
crossattn_emb_channels: int = 1024,
|
| 646 |
+
# positional embedding settings
|
| 647 |
+
pos_emb_cls: str = "sincos",
|
| 648 |
+
pos_emb_learnable: bool = False,
|
| 649 |
+
pos_emb_interpolation: str = "crop",
|
| 650 |
+
min_fps: int = 1,
|
| 651 |
+
max_fps: int = 30,
|
| 652 |
+
use_adaln_lora: bool = False,
|
| 653 |
+
adaln_lora_dim: int = 256,
|
| 654 |
+
rope_h_extrapolation_ratio: float = 1.0,
|
| 655 |
+
rope_w_extrapolation_ratio: float = 1.0,
|
| 656 |
+
rope_t_extrapolation_ratio: float = 1.0,
|
| 657 |
+
extra_per_block_abs_pos_emb: bool = False,
|
| 658 |
+
extra_h_extrapolation_ratio: float = 1.0,
|
| 659 |
+
extra_w_extrapolation_ratio: float = 1.0,
|
| 660 |
+
extra_t_extrapolation_ratio: float = 1.0,
|
| 661 |
+
rope_enable_fps_modulation: bool = True,
|
| 662 |
+
image_model=None,
|
| 663 |
+
device=None,
|
| 664 |
+
dtype=None,
|
| 665 |
+
operations=None,
|
| 666 |
+
) -> None:
|
| 667 |
+
super().__init__()
|
| 668 |
+
self.dtype = dtype
|
| 669 |
+
self.max_img_h = max_img_h
|
| 670 |
+
self.max_img_w = max_img_w
|
| 671 |
+
self.max_frames = max_frames
|
| 672 |
+
self.in_channels = in_channels
|
| 673 |
+
self.out_channels = out_channels
|
| 674 |
+
self.patch_spatial = patch_spatial
|
| 675 |
+
self.patch_temporal = patch_temporal
|
| 676 |
+
self.num_heads = num_heads
|
| 677 |
+
self.num_blocks = num_blocks
|
| 678 |
+
self.model_channels = model_channels
|
| 679 |
+
self.concat_padding_mask = concat_padding_mask
|
| 680 |
+
# positional embedding settings
|
| 681 |
+
self.pos_emb_cls = pos_emb_cls
|
| 682 |
+
self.pos_emb_learnable = pos_emb_learnable
|
| 683 |
+
self.pos_emb_interpolation = pos_emb_interpolation
|
| 684 |
+
self.min_fps = min_fps
|
| 685 |
+
self.max_fps = max_fps
|
| 686 |
+
self.rope_h_extrapolation_ratio = rope_h_extrapolation_ratio
|
| 687 |
+
self.rope_w_extrapolation_ratio = rope_w_extrapolation_ratio
|
| 688 |
+
self.rope_t_extrapolation_ratio = rope_t_extrapolation_ratio
|
| 689 |
+
self.extra_per_block_abs_pos_emb = extra_per_block_abs_pos_emb
|
| 690 |
+
self.extra_h_extrapolation_ratio = extra_h_extrapolation_ratio
|
| 691 |
+
self.extra_w_extrapolation_ratio = extra_w_extrapolation_ratio
|
| 692 |
+
self.extra_t_extrapolation_ratio = extra_t_extrapolation_ratio
|
| 693 |
+
self.rope_enable_fps_modulation = rope_enable_fps_modulation
|
| 694 |
+
|
| 695 |
+
self.build_pos_embed(device=device, dtype=dtype)
|
| 696 |
+
self.use_adaln_lora = use_adaln_lora
|
| 697 |
+
self.adaln_lora_dim = adaln_lora_dim
|
| 698 |
+
self.t_embedder = nn.Sequential(
|
| 699 |
+
Timesteps(model_channels),
|
| 700 |
+
TimestepEmbedding(model_channels, model_channels, use_adaln_lora=use_adaln_lora, device=device, dtype=dtype, operations=operations,),
|
| 701 |
+
)
|
| 702 |
+
|
| 703 |
+
in_channels = in_channels + 1 if concat_padding_mask else in_channels
|
| 704 |
+
self.x_embedder = PatchEmbed(
|
| 705 |
+
spatial_patch_size=patch_spatial,
|
| 706 |
+
temporal_patch_size=patch_temporal,
|
| 707 |
+
in_channels=in_channels,
|
| 708 |
+
out_channels=model_channels,
|
| 709 |
+
device=device, dtype=dtype, operations=operations,
|
| 710 |
+
)
|
| 711 |
+
|
| 712 |
+
self.blocks = nn.ModuleList(
|
| 713 |
+
[
|
| 714 |
+
Block(
|
| 715 |
+
x_dim=model_channels,
|
| 716 |
+
context_dim=crossattn_emb_channels,
|
| 717 |
+
num_heads=num_heads,
|
| 718 |
+
mlp_ratio=mlp_ratio,
|
| 719 |
+
use_adaln_lora=use_adaln_lora,
|
| 720 |
+
adaln_lora_dim=adaln_lora_dim,
|
| 721 |
+
device=device, dtype=dtype, operations=operations,
|
| 722 |
+
)
|
| 723 |
+
for _ in range(num_blocks)
|
| 724 |
+
]
|
| 725 |
+
)
|
| 726 |
+
|
| 727 |
+
self.final_layer = FinalLayer(
|
| 728 |
+
hidden_size=self.model_channels,
|
| 729 |
+
spatial_patch_size=self.patch_spatial,
|
| 730 |
+
temporal_patch_size=self.patch_temporal,
|
| 731 |
+
out_channels=self.out_channels,
|
| 732 |
+
use_adaln_lora=self.use_adaln_lora,
|
| 733 |
+
adaln_lora_dim=self.adaln_lora_dim,
|
| 734 |
+
device=device, dtype=dtype, operations=operations,
|
| 735 |
+
)
|
| 736 |
+
|
| 737 |
+
self.t_embedding_norm = operations.RMSNorm(model_channels, eps=1e-6, device=device, dtype=dtype)
|
| 738 |
+
|
| 739 |
+
def build_pos_embed(self, device=None, dtype=None) -> None:
|
| 740 |
+
if self.pos_emb_cls == "rope3d":
|
| 741 |
+
cls_type = VideoRopePosition3DEmb
|
| 742 |
+
else:
|
| 743 |
+
raise ValueError(f"Unknown pos_emb_cls {self.pos_emb_cls}")
|
| 744 |
+
|
| 745 |
+
logging.debug(f"Building positional embedding with {self.pos_emb_cls} class, impl {cls_type}")
|
| 746 |
+
kwargs = dict(
|
| 747 |
+
model_channels=self.model_channels,
|
| 748 |
+
len_h=self.max_img_h // self.patch_spatial,
|
| 749 |
+
len_w=self.max_img_w // self.patch_spatial,
|
| 750 |
+
len_t=self.max_frames // self.patch_temporal,
|
| 751 |
+
max_fps=self.max_fps,
|
| 752 |
+
min_fps=self.min_fps,
|
| 753 |
+
is_learnable=self.pos_emb_learnable,
|
| 754 |
+
interpolation=self.pos_emb_interpolation,
|
| 755 |
+
head_dim=self.model_channels // self.num_heads,
|
| 756 |
+
h_extrapolation_ratio=self.rope_h_extrapolation_ratio,
|
| 757 |
+
w_extrapolation_ratio=self.rope_w_extrapolation_ratio,
|
| 758 |
+
t_extrapolation_ratio=self.rope_t_extrapolation_ratio,
|
| 759 |
+
enable_fps_modulation=self.rope_enable_fps_modulation,
|
| 760 |
+
device=device,
|
| 761 |
+
)
|
| 762 |
+
self.pos_embedder = cls_type(
|
| 763 |
+
**kwargs, # type: ignore
|
| 764 |
+
)
|
| 765 |
+
|
| 766 |
+
if self.extra_per_block_abs_pos_emb:
|
| 767 |
+
kwargs["h_extrapolation_ratio"] = self.extra_h_extrapolation_ratio
|
| 768 |
+
kwargs["w_extrapolation_ratio"] = self.extra_w_extrapolation_ratio
|
| 769 |
+
kwargs["t_extrapolation_ratio"] = self.extra_t_extrapolation_ratio
|
| 770 |
+
kwargs["device"] = device
|
| 771 |
+
kwargs["dtype"] = dtype
|
| 772 |
+
self.extra_pos_embedder = LearnablePosEmbAxis(
|
| 773 |
+
**kwargs, # type: ignore
|
| 774 |
+
)
|
| 775 |
+
|
| 776 |
+
def prepare_embedded_sequence(
|
| 777 |
+
self,
|
| 778 |
+
x_B_C_T_H_W: torch.Tensor,
|
| 779 |
+
fps: Optional[torch.Tensor] = None,
|
| 780 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 781 |
+
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
|
| 782 |
+
"""
|
| 783 |
+
Prepares an embedded sequence tensor by applying positional embeddings and handling padding masks.
|
| 784 |
+
|
| 785 |
+
Args:
|
| 786 |
+
x_B_C_T_H_W (torch.Tensor): video
|
| 787 |
+
fps (Optional[torch.Tensor]): Frames per second tensor to be used for positional embedding when required.
|
| 788 |
+
If None, a default value (`self.base_fps`) will be used.
|
| 789 |
+
padding_mask (Optional[torch.Tensor]): current it is not used
|
| 790 |
+
|
| 791 |
+
Returns:
|
| 792 |
+
Tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 793 |
+
- A tensor of shape (B, T, H, W, D) with the embedded sequence.
|
| 794 |
+
- An optional positional embedding tensor, returned only if the positional embedding class
|
| 795 |
+
(`self.pos_emb_cls`) includes 'rope'. Otherwise, None.
|
| 796 |
+
|
| 797 |
+
Notes:
|
| 798 |
+
- If `self.concat_padding_mask` is True, a padding mask channel is concatenated to the input tensor.
|
| 799 |
+
- The method of applying positional embeddings depends on the value of `self.pos_emb_cls`.
|
| 800 |
+
- If 'rope' is in `self.pos_emb_cls` (case insensitive), the positional embeddings are generated using
|
| 801 |
+
the `self.pos_embedder` with the shape [T, H, W].
|
| 802 |
+
- If "fps_aware" is in `self.pos_emb_cls`, the positional embeddings are generated using the
|
| 803 |
+
`self.pos_embedder` with the fps tensor.
|
| 804 |
+
- Otherwise, the positional embeddings are generated without considering fps.
|
| 805 |
+
"""
|
| 806 |
+
if self.concat_padding_mask:
|
| 807 |
+
if padding_mask is None:
|
| 808 |
+
padding_mask = torch.zeros(x_B_C_T_H_W.shape[0], 1, x_B_C_T_H_W.shape[3], x_B_C_T_H_W.shape[4], dtype=x_B_C_T_H_W.dtype, device=x_B_C_T_H_W.device)
|
| 809 |
+
else:
|
| 810 |
+
padding_mask = transforms.functional.resize(
|
| 811 |
+
padding_mask, list(x_B_C_T_H_W.shape[-2:]), interpolation=transforms.InterpolationMode.NEAREST
|
| 812 |
+
)
|
| 813 |
+
x_B_C_T_H_W = torch.cat(
|
| 814 |
+
[x_B_C_T_H_W, padding_mask.unsqueeze(1).repeat(1, 1, x_B_C_T_H_W.shape[2], 1, 1)], dim=1
|
| 815 |
+
)
|
| 816 |
+
x_B_T_H_W_D = self.x_embedder(x_B_C_T_H_W)
|
| 817 |
+
|
| 818 |
+
if self.extra_per_block_abs_pos_emb:
|
| 819 |
+
extra_pos_emb = self.extra_pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device, dtype=x_B_C_T_H_W.dtype)
|
| 820 |
+
else:
|
| 821 |
+
extra_pos_emb = None
|
| 822 |
+
|
| 823 |
+
if "rope" in self.pos_emb_cls.lower():
|
| 824 |
+
return x_B_T_H_W_D, self.pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device), extra_pos_emb
|
| 825 |
+
x_B_T_H_W_D = x_B_T_H_W_D + self.pos_embedder(x_B_T_H_W_D, device=x_B_C_T_H_W.device) # [B, T, H, W, D]
|
| 826 |
+
|
| 827 |
+
return x_B_T_H_W_D, None, extra_pos_emb
|
| 828 |
+
|
| 829 |
+
def unpatchify(self, x_B_T_H_W_M: torch.Tensor) -> torch.Tensor:
|
| 830 |
+
x_B_C_Tt_Hp_Wp = rearrange(
|
| 831 |
+
x_B_T_H_W_M,
|
| 832 |
+
"B T H W (p1 p2 t C) -> B C (T t) (H p1) (W p2)",
|
| 833 |
+
p1=self.patch_spatial,
|
| 834 |
+
p2=self.patch_spatial,
|
| 835 |
+
t=self.patch_temporal,
|
| 836 |
+
)
|
| 837 |
+
return x_B_C_Tt_Hp_Wp
|
| 838 |
+
|
| 839 |
+
def forward(self,
|
| 840 |
+
x: torch.Tensor,
|
| 841 |
+
timesteps: torch.Tensor,
|
| 842 |
+
context: torch.Tensor,
|
| 843 |
+
fps: Optional[torch.Tensor] = None,
|
| 844 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 845 |
+
**kwargs,
|
| 846 |
+
):
|
| 847 |
+
return comfy.patcher_extension.WrapperExecutor.new_class_executor(
|
| 848 |
+
self._forward,
|
| 849 |
+
self,
|
| 850 |
+
comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, kwargs.get("transformer_options", {}))
|
| 851 |
+
).execute(x, timesteps, context, fps, padding_mask, **kwargs)
|
| 852 |
+
|
| 853 |
+
def _forward(
|
| 854 |
+
self,
|
| 855 |
+
x: torch.Tensor,
|
| 856 |
+
timesteps: torch.Tensor,
|
| 857 |
+
context: torch.Tensor,
|
| 858 |
+
fps: Optional[torch.Tensor] = None,
|
| 859 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 860 |
+
**kwargs,
|
| 861 |
+
):
|
| 862 |
+
orig_shape = list(x.shape)
|
| 863 |
+
x = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_temporal, self.patch_spatial, self.patch_spatial))
|
| 864 |
+
x_B_C_T_H_W = x
|
| 865 |
+
timesteps_B_T = timesteps
|
| 866 |
+
crossattn_emb = context
|
| 867 |
+
"""
|
| 868 |
+
Args:
|
| 869 |
+
x: (B, C, T, H, W) tensor of spatial-temp inputs
|
| 870 |
+
timesteps: (B, ) tensor of timesteps
|
| 871 |
+
crossattn_emb: (B, N, D) tensor of cross-attention embeddings
|
| 872 |
+
"""
|
| 873 |
+
x_B_T_H_W_D, rope_emb_L_1_1_D, extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = self.prepare_embedded_sequence(
|
| 874 |
+
x_B_C_T_H_W,
|
| 875 |
+
fps=fps,
|
| 876 |
+
padding_mask=padding_mask,
|
| 877 |
+
)
|
| 878 |
+
|
| 879 |
+
if timesteps_B_T.ndim == 1:
|
| 880 |
+
timesteps_B_T = timesteps_B_T.unsqueeze(1)
|
| 881 |
+
t_embedding_B_T_D, adaln_lora_B_T_3D = self.t_embedder[1](self.t_embedder[0](timesteps_B_T).to(x_B_T_H_W_D.dtype))
|
| 882 |
+
t_embedding_B_T_D = self.t_embedding_norm(t_embedding_B_T_D)
|
| 883 |
+
|
| 884 |
+
# for logging purpose
|
| 885 |
+
affline_scale_log_info = {}
|
| 886 |
+
affline_scale_log_info["t_embedding_B_T_D"] = t_embedding_B_T_D.detach()
|
| 887 |
+
self.affline_scale_log_info = affline_scale_log_info
|
| 888 |
+
self.affline_emb = t_embedding_B_T_D
|
| 889 |
+
self.crossattn_emb = crossattn_emb
|
| 890 |
+
|
| 891 |
+
if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
|
| 892 |
+
assert (
|
| 893 |
+
x_B_T_H_W_D.shape == extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape
|
| 894 |
+
), f"{x_B_T_H_W_D.shape} != {extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape}"
|
| 895 |
+
|
| 896 |
+
transformer_options = kwargs.get("transformer_options", {})
|
| 897 |
+
patches = transformer_options.get("patches", {})
|
| 898 |
+
if "post_input" in patches:
|
| 899 |
+
transformer_options = transformer_options.copy()
|
| 900 |
+
transformer_options["model_patch_data"] = {}
|
| 901 |
+
|
| 902 |
+
if "post_input" in patches:
|
| 903 |
+
for patch in patches["post_input"]:
|
| 904 |
+
out = patch({"img": x_B_T_H_W_D, "x": x_B_C_T_H_W, "transformer_options": transformer_options})
|
| 905 |
+
x_B_T_H_W_D = out["img"]
|
| 906 |
+
|
| 907 |
+
block_kwargs = {
|
| 908 |
+
"rope_emb_L_1_1_D": rope_emb_L_1_1_D.unsqueeze(1).unsqueeze(0),
|
| 909 |
+
"adaln_lora_B_T_3D": adaln_lora_B_T_3D,
|
| 910 |
+
"extra_per_block_pos_emb": extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D,
|
| 911 |
+
"transformer_options": transformer_options,
|
| 912 |
+
}
|
| 913 |
+
|
| 914 |
+
# The residual stream for this model has large values. To make fp16 compute_dtype work, we keep the residual stream
|
| 915 |
+
# in fp32, but run attention and MLP modules in fp16.
|
| 916 |
+
# An alternate method that clamps fp16 values "works" in the sense that it makes coherent images, but there is noticeable
|
| 917 |
+
# quality degradation and visual artifacts.
|
| 918 |
+
if x_B_T_H_W_D.dtype == torch.float16:
|
| 919 |
+
x_B_T_H_W_D = x_B_T_H_W_D.float()
|
| 920 |
+
|
| 921 |
+
for block_index, block in enumerate(self.blocks):
|
| 922 |
+
transformer_options["block_index"] = block_index
|
| 923 |
+
x_B_T_H_W_D = block(
|
| 924 |
+
x_B_T_H_W_D,
|
| 925 |
+
t_embedding_B_T_D,
|
| 926 |
+
crossattn_emb,
|
| 927 |
+
**block_kwargs,
|
| 928 |
+
)
|
| 929 |
+
|
| 930 |
+
x_B_T_H_W_O = self.final_layer(x_B_T_H_W_D.to(crossattn_emb.dtype), t_embedding_B_T_D, adaln_lora_B_T_3D=adaln_lora_B_T_3D)
|
| 931 |
+
x_B_C_Tt_Hp_Wp = self.unpatchify(x_B_T_H_W_O)[:, :, :orig_shape[-3], :orig_shape[-2], :orig_shape[-1]]
|
| 932 |
+
return x_B_C_Tt_Hp_Wp
|
ComfyUI/comfy/ldm/cosmos/vae.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""The causal continuous video tokenizer with VAE or AE formulation for 3D data.."""
|
| 16 |
+
|
| 17 |
+
import logging
|
| 18 |
+
import torch
|
| 19 |
+
from torch import nn
|
| 20 |
+
from enum import Enum
|
| 21 |
+
import math
|
| 22 |
+
|
| 23 |
+
from .cosmos_tokenizer.layers3d import (
|
| 24 |
+
EncoderFactorized,
|
| 25 |
+
DecoderFactorized,
|
| 26 |
+
CausalConv3d,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class IdentityDistribution(torch.nn.Module):
|
| 31 |
+
def __init__(self):
|
| 32 |
+
super().__init__()
|
| 33 |
+
|
| 34 |
+
def forward(self, parameters):
|
| 35 |
+
return parameters, (torch.tensor([0.0]), torch.tensor([0.0]))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class GaussianDistribution(torch.nn.Module):
|
| 39 |
+
def __init__(self, min_logvar: float = -30.0, max_logvar: float = 20.0):
|
| 40 |
+
super().__init__()
|
| 41 |
+
self.min_logvar = min_logvar
|
| 42 |
+
self.max_logvar = max_logvar
|
| 43 |
+
|
| 44 |
+
def sample(self, mean, logvar):
|
| 45 |
+
std = torch.exp(0.5 * logvar)
|
| 46 |
+
return mean + std * torch.randn_like(mean)
|
| 47 |
+
|
| 48 |
+
def forward(self, parameters):
|
| 49 |
+
mean, logvar = torch.chunk(parameters, 2, dim=1)
|
| 50 |
+
logvar = torch.clamp(logvar, self.min_logvar, self.max_logvar)
|
| 51 |
+
return self.sample(mean, logvar), (mean, logvar)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class ContinuousFormulation(Enum):
|
| 55 |
+
VAE = GaussianDistribution
|
| 56 |
+
AE = IdentityDistribution
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class CausalContinuousVideoTokenizer(nn.Module):
|
| 60 |
+
def __init__(
|
| 61 |
+
self, z_channels: int, z_factor: int, latent_channels: int, **kwargs
|
| 62 |
+
) -> None:
|
| 63 |
+
super().__init__()
|
| 64 |
+
self.name = kwargs.get("name", "CausalContinuousVideoTokenizer")
|
| 65 |
+
self.latent_channels = latent_channels
|
| 66 |
+
self.sigma_data = 0.5
|
| 67 |
+
|
| 68 |
+
# encoder_name = kwargs.get("encoder", Encoder3DType.BASE.name)
|
| 69 |
+
self.encoder = EncoderFactorized(
|
| 70 |
+
z_channels=z_factor * z_channels, **kwargs
|
| 71 |
+
)
|
| 72 |
+
if kwargs.get("temporal_compression", 4) == 4:
|
| 73 |
+
kwargs["channels_mult"] = [2, 4]
|
| 74 |
+
# decoder_name = kwargs.get("decoder", Decoder3DType.BASE.name)
|
| 75 |
+
self.decoder = DecoderFactorized(
|
| 76 |
+
z_channels=z_channels, **kwargs
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
self.quant_conv = CausalConv3d(
|
| 80 |
+
z_factor * z_channels,
|
| 81 |
+
z_factor * latent_channels,
|
| 82 |
+
kernel_size=1,
|
| 83 |
+
padding=0,
|
| 84 |
+
)
|
| 85 |
+
self.post_quant_conv = CausalConv3d(
|
| 86 |
+
latent_channels, z_channels, kernel_size=1, padding=0
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
# formulation_name = kwargs.get("formulation", ContinuousFormulation.AE.name)
|
| 90 |
+
self.distribution = IdentityDistribution() # ContinuousFormulation[formulation_name].value()
|
| 91 |
+
|
| 92 |
+
num_parameters = sum(param.numel() for param in self.parameters())
|
| 93 |
+
logging.debug(f"model={self.name}, num_parameters={num_parameters:,}")
|
| 94 |
+
logging.debug(
|
| 95 |
+
f"z_channels={z_channels}, latent_channels={self.latent_channels}."
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
latent_temporal_chunk = 16
|
| 99 |
+
self.latent_mean = nn.Parameter(torch.zeros([self.latent_channels * latent_temporal_chunk], dtype=torch.float32))
|
| 100 |
+
self.latent_std = nn.Parameter(torch.ones([self.latent_channels * latent_temporal_chunk], dtype=torch.float32))
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def encode(self, x):
|
| 104 |
+
h = self.encoder(x)
|
| 105 |
+
moments = self.quant_conv(h)
|
| 106 |
+
z, posteriors = self.distribution(moments)
|
| 107 |
+
latent_ch = z.shape[1]
|
| 108 |
+
latent_t = z.shape[2]
|
| 109 |
+
in_dtype = z.dtype
|
| 110 |
+
mean = self.latent_mean.view(latent_ch, -1)
|
| 111 |
+
std = self.latent_std.view(latent_ch, -1)
|
| 112 |
+
|
| 113 |
+
mean = mean.repeat(1, math.ceil(latent_t / mean.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
|
| 114 |
+
std = std.repeat(1, math.ceil(latent_t / std.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
|
| 115 |
+
return ((z - mean) / std) * self.sigma_data
|
| 116 |
+
|
| 117 |
+
def decode(self, z):
|
| 118 |
+
in_dtype = z.dtype
|
| 119 |
+
latent_ch = z.shape[1]
|
| 120 |
+
latent_t = z.shape[2]
|
| 121 |
+
mean = self.latent_mean.view(latent_ch, -1)
|
| 122 |
+
std = self.latent_std.view(latent_ch, -1)
|
| 123 |
+
|
| 124 |
+
mean = mean.repeat(1, math.ceil(latent_t / mean.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
|
| 125 |
+
std = std.repeat(1, math.ceil(latent_t / std.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
|
| 126 |
+
|
| 127 |
+
z = z / self.sigma_data
|
| 128 |
+
z = z * std + mean
|
| 129 |
+
z = self.post_quant_conv(z)
|
| 130 |
+
return self.decoder(z)
|
| 131 |
+
|
ComfyUI/comfy/ldm/depth_anything_3/camera.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Camera-token encoder and decoder for Depth Anything 3."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
import torch.nn.functional as F
|
| 8 |
+
|
| 9 |
+
from comfy.ldm.modules.attention import optimized_attention_for_device
|
| 10 |
+
from .transform import affine_inverse, extri_intri_to_pose_encoding
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
# -----------------------------------------------------------------------
|
| 14 |
+
# Building blocks (mirror depth_anything_3.model.utils.{attention,block})
|
| 15 |
+
# -----------------------------------------------------------------------
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class _Mlp(nn.Module):
|
| 19 |
+
"""Standard 2-layer MLP with GELU. Matches upstream ``utils.attention.Mlp``."""
|
| 20 |
+
|
| 21 |
+
def __init__(self, in_features, hidden_features=None, out_features=None, *, device=None, dtype=None, operations=None):
|
| 22 |
+
super().__init__()
|
| 23 |
+
out_features = out_features or in_features
|
| 24 |
+
hidden_features = hidden_features or in_features
|
| 25 |
+
self.fc1 = operations.Linear(in_features, hidden_features, bias=True, device=device, dtype=dtype)
|
| 26 |
+
self.fc2 = operations.Linear(hidden_features, out_features, bias=True, device=device, dtype=dtype)
|
| 27 |
+
|
| 28 |
+
def forward(self, x):
|
| 29 |
+
return self.fc2(F.gelu(self.fc1(x)))
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class _LayerScale(nn.Module):
|
| 33 |
+
"""Per-channel learnable scaling. Matches upstream LayerScale."""
|
| 34 |
+
|
| 35 |
+
def __init__(self, dim, *, device=None, dtype=None):
|
| 36 |
+
super().__init__()
|
| 37 |
+
self.gamma = nn.Parameter(torch.empty(dim, device=device, dtype=dtype))
|
| 38 |
+
|
| 39 |
+
def forward(self, x):
|
| 40 |
+
return x * self.gamma.to(dtype=x.dtype, device=x.device)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class _Attention(nn.Module):
|
| 44 |
+
""" Self-attention with fused QKV projection. Mirrors upstream utils.attention.Attention;
|
| 45 |
+
Layout matches the HF safetensors (attn.qkv.{weight,bias} and attn.proj.{weight,bias})."""
|
| 46 |
+
|
| 47 |
+
def __init__(self, dim, num_heads, *, device=None, dtype=None, operations=None):
|
| 48 |
+
super().__init__()
|
| 49 |
+
assert dim % num_heads == 0
|
| 50 |
+
self.num_heads = num_heads
|
| 51 |
+
self.head_dim = dim // num_heads
|
| 52 |
+
self.qkv = operations.Linear(dim, dim * 3, bias=True, device=device, dtype=dtype)
|
| 53 |
+
self.proj = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
|
| 54 |
+
|
| 55 |
+
def forward(self, x):
|
| 56 |
+
B, N, C = x.shape
|
| 57 |
+
qkv = self.qkv(x).reshape(B, N, 3, C)
|
| 58 |
+
q, k, v = qkv.unbind(2) # each (B, N, C)
|
| 59 |
+
attn_fn = optimized_attention_for_device(x.device, small_input=True)
|
| 60 |
+
out = attn_fn(q, k, v, heads=self.num_heads)
|
| 61 |
+
return self.proj(out)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class _Block(nn.Module):
|
| 65 |
+
"""Pre-norm transformer block with LayerScale. Used by :class:CameraEnc. Layout follows upstream utils.block.Block."""
|
| 66 |
+
|
| 67 |
+
def __init__(self, dim, num_heads, mlp_ratio=4, init_values=0.01, *, device=None, dtype=None, operations=None):
|
| 68 |
+
super().__init__()
|
| 69 |
+
self.norm1 = operations.LayerNorm(dim, device=device, dtype=dtype)
|
| 70 |
+
self.attn = _Attention(dim, num_heads, device=device, dtype=dtype, operations=operations)
|
| 71 |
+
self.ls1 = _LayerScale(dim, device=device, dtype=dtype) if init_values else nn.Identity()
|
| 72 |
+
self.norm2 = operations.LayerNorm(dim, device=device, dtype=dtype)
|
| 73 |
+
self.mlp = _Mlp(in_features=dim, hidden_features=int(dim * mlp_ratio), device=device, dtype=dtype, operations=operations)
|
| 74 |
+
self.ls2 = _LayerScale(dim, device=device, dtype=dtype) if init_values else nn.Identity()
|
| 75 |
+
|
| 76 |
+
def forward(self, x):
|
| 77 |
+
x = x + self.ls1(self.attn(self.norm1(x)))
|
| 78 |
+
x = x + self.ls2(self.mlp(self.norm2(x)))
|
| 79 |
+
return x
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class CameraEnc(nn.Module):
|
| 83 |
+
"""Encode per-view (extrinsics, intrinsics) into a camera token.
|
| 84 |
+
|
| 85 |
+
Maps a 9-D pose-encoding vector through a small MLP up to the backbone's
|
| 86 |
+
``embed_dim``, then runs ``trunk_depth`` transformer blocks. The output
|
| 87 |
+
has shape ``(B, S, embed_dim)`` and is injected at block ``alt_start``
|
| 88 |
+
of the DINOv2 backbone in place of the cls token.
|
| 89 |
+
|
| 90 |
+
Parameters mirror the upstream ``cam_enc.py`` so HF weights load directly.
|
| 91 |
+
"""
|
| 92 |
+
|
| 93 |
+
def __init__(
|
| 94 |
+
self,
|
| 95 |
+
dim_out: int = 1024,
|
| 96 |
+
dim_in: int = 9,
|
| 97 |
+
trunk_depth: int = 4,
|
| 98 |
+
target_dim: int = 9,
|
| 99 |
+
num_heads: int = 16,
|
| 100 |
+
mlp_ratio: int = 4,
|
| 101 |
+
init_values: float = 0.01,
|
| 102 |
+
*,
|
| 103 |
+
device=None, dtype=None, operations=None,
|
| 104 |
+
**_kwargs,
|
| 105 |
+
):
|
| 106 |
+
super().__init__()
|
| 107 |
+
self.target_dim = target_dim
|
| 108 |
+
self.trunk_depth = trunk_depth
|
| 109 |
+
self.trunk = nn.Sequential(*[
|
| 110 |
+
_Block(dim_out, num_heads=num_heads, mlp_ratio=mlp_ratio,
|
| 111 |
+
init_values=init_values,
|
| 112 |
+
device=device, dtype=dtype, operations=operations)
|
| 113 |
+
for _ in range(trunk_depth)
|
| 114 |
+
])
|
| 115 |
+
self.token_norm = operations.LayerNorm(dim_out, device=device, dtype=dtype)
|
| 116 |
+
self.trunk_norm = operations.LayerNorm(dim_out, device=device, dtype=dtype)
|
| 117 |
+
self.pose_branch = _Mlp(
|
| 118 |
+
in_features=dim_in,
|
| 119 |
+
hidden_features=dim_out // 2,
|
| 120 |
+
out_features=dim_out,
|
| 121 |
+
device=device, dtype=dtype, operations=operations,
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
def forward(self, extrinsics: torch.Tensor, intrinsics: torch.Tensor,
|
| 125 |
+
image_size_hw) -> torch.Tensor:
|
| 126 |
+
"""Encode camera parameters into ``(B, S, dim_out)`` tokens."""
|
| 127 |
+
c2ws = affine_inverse(extrinsics)
|
| 128 |
+
pose_encoding = extri_intri_to_pose_encoding(c2ws, intrinsics, image_size_hw)
|
| 129 |
+
tokens = self.pose_branch(pose_encoding.to(self.pose_branch.fc1.weight.dtype))
|
| 130 |
+
tokens = self.token_norm(tokens)
|
| 131 |
+
tokens = self.trunk(tokens)
|
| 132 |
+
tokens = self.trunk_norm(tokens)
|
| 133 |
+
return tokens
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
class CameraDec(nn.Module):
|
| 137 |
+
"""Decode the final cam token into a 9-D pose encoding.
|
| 138 |
+
|
| 139 |
+
Output layout: ``[T(3), quat_xyzw(4), fov_h, fov_w]``. The translation is
|
| 140 |
+
always predicted by the network; the quaternion and FoV can either be
|
| 141 |
+
predicted or supplied via ``camera_encoding`` (used at training time
|
| 142 |
+
when GT cameras are available -- not exercised at inference here).
|
| 143 |
+
|
| 144 |
+
Parameters mirror the upstream ``cam_dec.py`` so HF weights load directly.
|
| 145 |
+
"""
|
| 146 |
+
|
| 147 |
+
def __init__(self, dim_in: int = 1536,
|
| 148 |
+
*, device=None, dtype=None, operations=None, **_kwargs):
|
| 149 |
+
super().__init__()
|
| 150 |
+
d = dim_in
|
| 151 |
+
self.backbone = nn.Sequential(
|
| 152 |
+
operations.Linear(d, d, device=device, dtype=dtype),
|
| 153 |
+
nn.ReLU(),
|
| 154 |
+
operations.Linear(d, d, device=device, dtype=dtype),
|
| 155 |
+
nn.ReLU(),
|
| 156 |
+
)
|
| 157 |
+
self.fc_t = operations.Linear(d, 3, device=device, dtype=dtype)
|
| 158 |
+
self.fc_qvec = operations.Linear(d, 4, device=device, dtype=dtype)
|
| 159 |
+
self.fc_fov = nn.Sequential(
|
| 160 |
+
operations.Linear(d, 2, device=device, dtype=dtype),
|
| 161 |
+
nn.ReLU(),
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
def forward(self, feat: torch.Tensor,
|
| 165 |
+
camera_encoding: "torch.Tensor | None" = None) -> torch.Tensor:
|
| 166 |
+
"""Decode ``(B, N, dim_in)`` cam tokens into ``(B, N, 9)`` pose enc."""
|
| 167 |
+
B, N = feat.shape[:2]
|
| 168 |
+
feat = feat.reshape(B * N, -1)
|
| 169 |
+
feat = self.backbone(feat)
|
| 170 |
+
out_t = self.fc_t(feat.float()).reshape(B, N, 3)
|
| 171 |
+
if camera_encoding is None:
|
| 172 |
+
out_qvec = self.fc_qvec(feat.float()).reshape(B, N, 4)
|
| 173 |
+
out_fov = self.fc_fov(feat.float()).reshape(B, N, 2)
|
| 174 |
+
else:
|
| 175 |
+
out_qvec = camera_encoding[..., 3:7]
|
| 176 |
+
out_fov = camera_encoding[..., -2:]
|
| 177 |
+
return torch.cat([out_t, out_qvec, out_fov], dim=-1)
|
ComfyUI/comfy/ldm/depth_anything_3/dpt.py
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""DPT / DualDPT heads for Depth Anything 3."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import List, Optional, Sequence, Tuple
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Permute(nn.Module):
|
| 13 |
+
def __init__(self, dims: Tuple[int, ...]):
|
| 14 |
+
super().__init__()
|
| 15 |
+
self.dims = dims
|
| 16 |
+
|
| 17 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 18 |
+
return x.permute(*self.dims)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _custom_interpolate(
|
| 22 |
+
x: torch.Tensor,
|
| 23 |
+
size: Optional[Tuple[int, int]] = None,
|
| 24 |
+
scale_factor: Optional[float] = None,
|
| 25 |
+
mode: str = "bilinear",
|
| 26 |
+
align_corners: bool = True,
|
| 27 |
+
) -> torch.Tensor:
|
| 28 |
+
if size is None:
|
| 29 |
+
assert scale_factor is not None
|
| 30 |
+
size = (int(x.shape[-2] * scale_factor), int(x.shape[-1] * scale_factor))
|
| 31 |
+
INT_MAX = 1610612736
|
| 32 |
+
total = size[0] * size[1] * x.shape[0] * x.shape[1]
|
| 33 |
+
if total > INT_MAX:
|
| 34 |
+
chunks = torch.chunk(x, chunks=(total // INT_MAX) + 1, dim=0)
|
| 35 |
+
outs = [F.interpolate(c, size=size, mode=mode, align_corners=align_corners) for c in chunks]
|
| 36 |
+
return torch.cat(outs, dim=0).contiguous()
|
| 37 |
+
return F.interpolate(x, size=size, mode=mode, align_corners=align_corners)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _create_uv_grid(width: int, height: int, aspect_ratio: float, dtype, device) -> torch.Tensor:
|
| 41 |
+
"""Normalised UV grid spanning (-x_span, -y_span)..(x_span, y_span)."""
|
| 42 |
+
diag_factor = (aspect_ratio ** 2 + 1.0) ** 0.5
|
| 43 |
+
span_x = aspect_ratio / diag_factor
|
| 44 |
+
span_y = 1.0 / diag_factor
|
| 45 |
+
left_x = -span_x * (width - 1) / width
|
| 46 |
+
right_x = span_x * (width - 1) / width
|
| 47 |
+
top_y = -span_y * (height - 1) / height
|
| 48 |
+
bottom_y = span_y * (height - 1) / height
|
| 49 |
+
x_coords = torch.linspace(left_x, right_x, steps=width, dtype=dtype, device=device)
|
| 50 |
+
y_coords = torch.linspace(top_y, bottom_y, steps=height, dtype=dtype, device=device)
|
| 51 |
+
uu, vv = torch.meshgrid(x_coords, y_coords, indexing="xy")
|
| 52 |
+
return torch.stack((uu, vv), dim=-1) # (H, W, 2)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def _make_sincos_pos_embed(embed_dim: int, pos: torch.Tensor, omega_0: float = 100.0) -> torch.Tensor:
|
| 56 |
+
omega = torch.arange(embed_dim // 2, dtype=torch.float32, device=pos.device)
|
| 57 |
+
omega = 1.0 / omega_0 ** (omega / (embed_dim / 2.0))
|
| 58 |
+
pos = pos.reshape(-1)
|
| 59 |
+
out = torch.einsum("m,d->md", pos, omega)
|
| 60 |
+
return torch.cat([out.sin(), out.cos()], dim=1).float()
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _position_grid_to_embed(pos_grid: torch.Tensor, embed_dim: int, omega_0: float = 100.0) -> torch.Tensor:
|
| 64 |
+
H, W, _ = pos_grid.shape
|
| 65 |
+
pos_flat = pos_grid.reshape(-1, 2)
|
| 66 |
+
emb_x = _make_sincos_pos_embed(embed_dim // 2, pos_flat[:, 0], omega_0=omega_0)
|
| 67 |
+
emb_y = _make_sincos_pos_embed(embed_dim // 2, pos_flat[:, 1], omega_0=omega_0)
|
| 68 |
+
emb = torch.cat([emb_x, emb_y], dim=-1)
|
| 69 |
+
return emb.view(H, W, embed_dim)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def _add_pos_embed(x: torch.Tensor, W: int, H: int, ratio: float = 0.1) -> torch.Tensor:
|
| 73 |
+
"""Stateless UV positional embedding added to a feature map (B, C, h, w)."""
|
| 74 |
+
pw, ph = x.shape[-1], x.shape[-2]
|
| 75 |
+
pe = _create_uv_grid(pw, ph, aspect_ratio=W / H, dtype=x.dtype, device=x.device)
|
| 76 |
+
pe = _position_grid_to_embed(pe, x.shape[1]) * ratio
|
| 77 |
+
pe = pe.permute(2, 0, 1)[None].expand(x.shape[0], -1, -1, -1).to(dtype=x.dtype)
|
| 78 |
+
return x + pe
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _apply_activation(x: torch.Tensor, activation: str) -> torch.Tensor:
|
| 82 |
+
act = (activation or "linear").lower()
|
| 83 |
+
if act == "exp":
|
| 84 |
+
return torch.exp(x)
|
| 85 |
+
if act == "expp1":
|
| 86 |
+
return torch.exp(x) + 1
|
| 87 |
+
if act == "expm1":
|
| 88 |
+
return torch.expm1(x)
|
| 89 |
+
if act == "relu":
|
| 90 |
+
return torch.relu(x)
|
| 91 |
+
if act == "sigmoid":
|
| 92 |
+
return torch.sigmoid(x)
|
| 93 |
+
if act == "softplus":
|
| 94 |
+
return F.softplus(x)
|
| 95 |
+
if act == "tanh":
|
| 96 |
+
return torch.tanh(x)
|
| 97 |
+
return x
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
# -----------------------------------------------------------------------------
|
| 101 |
+
# Fusion building blocks
|
| 102 |
+
# -----------------------------------------------------------------------------
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class ResidualConvUnit(nn.Module):
|
| 106 |
+
def __init__(self, features: int, device=None, dtype=None, operations=None):
|
| 107 |
+
super().__init__()
|
| 108 |
+
self.conv1 = operations.Conv2d(features, features, 3, 1, 1, bias=True, device=device, dtype=dtype)
|
| 109 |
+
self.conv2 = operations.Conv2d(features, features, 3, 1, 1, bias=True, device=device, dtype=dtype)
|
| 110 |
+
self.activation = nn.ReLU(inplace=False)
|
| 111 |
+
|
| 112 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 113 |
+
out = self.activation(x)
|
| 114 |
+
out = self.conv1(out)
|
| 115 |
+
out = self.activation(out)
|
| 116 |
+
out = self.conv2(out)
|
| 117 |
+
return out + x
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class FeatureFusionBlock(nn.Module):
|
| 121 |
+
def __init__(self, features: int, has_residual: bool = True, align_corners: bool = True, device=None, dtype=None, operations=None):
|
| 122 |
+
super().__init__()
|
| 123 |
+
self.align_corners = align_corners
|
| 124 |
+
self.has_residual = has_residual
|
| 125 |
+
if has_residual:
|
| 126 |
+
self.resConfUnit1 = ResidualConvUnit(features, device=device, dtype=dtype, operations=operations)
|
| 127 |
+
else:
|
| 128 |
+
self.resConfUnit1 = None
|
| 129 |
+
self.resConfUnit2 = ResidualConvUnit(features, device=device, dtype=dtype, operations=operations)
|
| 130 |
+
self.out_conv = operations.Conv2d(features, features, 1, 1, 0, bias=True, device=device, dtype=dtype)
|
| 131 |
+
|
| 132 |
+
def forward(self, *xs: torch.Tensor, size: Optional[Tuple[int, int]] = None) -> torch.Tensor:
|
| 133 |
+
y = xs[0]
|
| 134 |
+
if self.has_residual and len(xs) > 1 and self.resConfUnit1 is not None:
|
| 135 |
+
y = y + self.resConfUnit1(xs[1])
|
| 136 |
+
y = self.resConfUnit2(y)
|
| 137 |
+
if size is None:
|
| 138 |
+
up_kwargs = {"scale_factor": 2.0}
|
| 139 |
+
else:
|
| 140 |
+
up_kwargs = {"size": size}
|
| 141 |
+
y = _custom_interpolate(y, **up_kwargs, mode="bilinear", align_corners=self.align_corners)
|
| 142 |
+
y = self.out_conv(y)
|
| 143 |
+
return y
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
class _Scratch(nn.Module):
|
| 147 |
+
"""Container that mirrors upstream ``scratch`` attribute layout."""
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def _make_scratch(in_shape: List[int], out_shape: int, device=None, dtype=None, operations=None) -> _Scratch:
|
| 151 |
+
scratch = _Scratch()
|
| 152 |
+
scratch.layer1_rn = operations.Conv2d(in_shape[0], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
|
| 153 |
+
scratch.layer2_rn = operations.Conv2d(in_shape[1], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
|
| 154 |
+
scratch.layer3_rn = operations.Conv2d(in_shape[2], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
|
| 155 |
+
scratch.layer4_rn = operations.Conv2d(in_shape[3], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
|
| 156 |
+
return scratch
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def _make_fusion_block(features: int, has_residual: bool = True, device=None, dtype=None, operations=None) -> FeatureFusionBlock:
|
| 160 |
+
return FeatureFusionBlock(features, has_residual=has_residual, align_corners=True, device=device, dtype=dtype, operations=operations)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
# -----------------------------------------------------------------------------
|
| 164 |
+
# DPT (single head + optional sky head) -- used by DA3Mono/Metric
|
| 165 |
+
# -----------------------------------------------------------------------------
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
class DPT(nn.Module):
|
| 169 |
+
"""Single-head DPT used by DA3Mono-Large and DA3Metric-Large."""
|
| 170 |
+
|
| 171 |
+
def __init__(
|
| 172 |
+
self,
|
| 173 |
+
dim_in: int,
|
| 174 |
+
patch_size: int = 14,
|
| 175 |
+
output_dim: int = 1,
|
| 176 |
+
activation: str = "exp",
|
| 177 |
+
conf_activation: str = "expp1",
|
| 178 |
+
features: int = 256,
|
| 179 |
+
out_channels: Sequence[int] = (256, 512, 1024, 1024),
|
| 180 |
+
pos_embed: bool = False,
|
| 181 |
+
down_ratio: int = 1,
|
| 182 |
+
head_name: str = "depth",
|
| 183 |
+
use_sky_head: bool = True,
|
| 184 |
+
sky_name: str = "sky",
|
| 185 |
+
sky_activation: str = "relu",
|
| 186 |
+
norm_type: str = "idt",
|
| 187 |
+
device=None, dtype=None, operations=None,
|
| 188 |
+
):
|
| 189 |
+
super().__init__()
|
| 190 |
+
self.patch_size = patch_size
|
| 191 |
+
self.activation = activation
|
| 192 |
+
self.conf_activation = conf_activation
|
| 193 |
+
self.pos_embed = pos_embed
|
| 194 |
+
self.down_ratio = down_ratio
|
| 195 |
+
self.head_main = head_name
|
| 196 |
+
self.sky_name = sky_name
|
| 197 |
+
self.out_dim = output_dim
|
| 198 |
+
self.has_conf = output_dim > 1
|
| 199 |
+
self.use_sky_head = use_sky_head
|
| 200 |
+
self.sky_activation = sky_activation
|
| 201 |
+
self.intermediate_layer_idx: Tuple[int, int, int, int] = (0, 1, 2, 3)
|
| 202 |
+
|
| 203 |
+
if norm_type == "layer":
|
| 204 |
+
self.norm = operations.LayerNorm(dim_in, device=device, dtype=dtype)
|
| 205 |
+
else:
|
| 206 |
+
self.norm = nn.Identity()
|
| 207 |
+
|
| 208 |
+
out_channels = list(out_channels)
|
| 209 |
+
self.projects = nn.ModuleList([
|
| 210 |
+
operations.Conv2d(dim_in, oc, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype)
|
| 211 |
+
for oc in out_channels
|
| 212 |
+
])
|
| 213 |
+
self.resize_layers = nn.ModuleList([
|
| 214 |
+
operations.ConvTranspose2d(out_channels[0], out_channels[0], kernel_size=4, stride=4, padding=0, device=device, dtype=dtype),
|
| 215 |
+
operations.ConvTranspose2d(out_channels[1], out_channels[1], kernel_size=2, stride=2, padding=0, device=device, dtype=dtype),
|
| 216 |
+
nn.Identity(),
|
| 217 |
+
operations.Conv2d(out_channels[3], out_channels[3], kernel_size=3, stride=2, padding=1, device=device, dtype=dtype),
|
| 218 |
+
])
|
| 219 |
+
|
| 220 |
+
self.scratch = _make_scratch(out_channels, features, device=device, dtype=dtype, operations=operations)
|
| 221 |
+
self.scratch.refinenet1 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 222 |
+
self.scratch.refinenet2 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 223 |
+
self.scratch.refinenet3 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 224 |
+
self.scratch.refinenet4 = _make_fusion_block(features, has_residual=False, device=device, dtype=dtype, operations=operations)
|
| 225 |
+
|
| 226 |
+
head_features_1 = features
|
| 227 |
+
head_features_2 = 32
|
| 228 |
+
self.scratch.output_conv1 = operations.Conv2d(
|
| 229 |
+
head_features_1, head_features_1 // 2, kernel_size=3, stride=1, padding=1,
|
| 230 |
+
device=device, dtype=dtype,
|
| 231 |
+
)
|
| 232 |
+
self.scratch.output_conv2 = nn.Sequential(
|
| 233 |
+
operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
|
| 234 |
+
nn.ReLU(inplace=False),
|
| 235 |
+
operations.Conv2d(head_features_2, output_dim, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
if self.use_sky_head:
|
| 239 |
+
self.scratch.sky_output_conv2 = nn.Sequential(
|
| 240 |
+
operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
|
| 241 |
+
nn.ReLU(inplace=False),
|
| 242 |
+
operations.Conv2d(head_features_2, 1, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
def forward(self, feats: List[torch.Tensor], H: int, W: int, patch_start_idx: int = 0, **_kwargs) -> dict:
|
| 246 |
+
# feats[i][0] is the patch-token tensor with shape (B, S, N_patch, C)
|
| 247 |
+
B, S, N, C = feats[0][0].shape
|
| 248 |
+
feats_flat = [feat[0].reshape(B * S, N, C) for feat in feats]
|
| 249 |
+
|
| 250 |
+
ph, pw = H // self.patch_size, W // self.patch_size
|
| 251 |
+
resized = []
|
| 252 |
+
for stage_idx, take_idx in enumerate(self.intermediate_layer_idx):
|
| 253 |
+
x = feats_flat[take_idx][:, patch_start_idx:]
|
| 254 |
+
x = self.norm(x)
|
| 255 |
+
x = x.permute(0, 2, 1).contiguous().reshape(B * S, C, ph, pw)
|
| 256 |
+
x = self.projects[stage_idx](x)
|
| 257 |
+
if self.pos_embed:
|
| 258 |
+
x = _add_pos_embed(x, W, H)
|
| 259 |
+
x = self.resize_layers[stage_idx](x)
|
| 260 |
+
resized.append(x)
|
| 261 |
+
|
| 262 |
+
l1_rn = self.scratch.layer1_rn(resized[0])
|
| 263 |
+
l2_rn = self.scratch.layer2_rn(resized[1])
|
| 264 |
+
l3_rn = self.scratch.layer3_rn(resized[2])
|
| 265 |
+
l4_rn = self.scratch.layer4_rn(resized[3])
|
| 266 |
+
|
| 267 |
+
out = self.scratch.refinenet4(l4_rn, size=l3_rn.shape[2:])
|
| 268 |
+
out = self.scratch.refinenet3(out, l3_rn, size=l2_rn.shape[2:])
|
| 269 |
+
out = self.scratch.refinenet2(out, l2_rn, size=l1_rn.shape[2:])
|
| 270 |
+
out = self.scratch.refinenet1(out, l1_rn)
|
| 271 |
+
|
| 272 |
+
h_out = int(ph * self.patch_size / self.down_ratio)
|
| 273 |
+
w_out = int(pw * self.patch_size / self.down_ratio)
|
| 274 |
+
|
| 275 |
+
fused = self.scratch.output_conv1(out)
|
| 276 |
+
fused = _custom_interpolate(fused, (h_out, w_out), mode="bilinear", align_corners=True)
|
| 277 |
+
if self.pos_embed:
|
| 278 |
+
fused = _add_pos_embed(fused, W, H)
|
| 279 |
+
feat = fused
|
| 280 |
+
|
| 281 |
+
main_logits = self.scratch.output_conv2(feat)
|
| 282 |
+
outs = {}
|
| 283 |
+
if self.has_conf:
|
| 284 |
+
fmap = main_logits.permute(0, 2, 3, 1)
|
| 285 |
+
pred = _apply_activation(fmap[..., :-1], self.activation)
|
| 286 |
+
conf = _apply_activation(fmap[..., -1], self.conf_activation)
|
| 287 |
+
outs[self.head_main] = pred.squeeze(-1).view(B, S, *pred.shape[1:-1])
|
| 288 |
+
outs[f"{self.head_main}_conf"] = conf.view(B, S, *conf.shape[1:])
|
| 289 |
+
else:
|
| 290 |
+
pred = _apply_activation(main_logits, self.activation)
|
| 291 |
+
outs[self.head_main] = pred.squeeze(1).view(B, S, *pred.shape[2:])
|
| 292 |
+
|
| 293 |
+
if self.use_sky_head:
|
| 294 |
+
sky_logits = self.scratch.sky_output_conv2(feat)
|
| 295 |
+
if self.sky_activation.lower() == "sigmoid":
|
| 296 |
+
sky = torch.sigmoid(sky_logits)
|
| 297 |
+
elif self.sky_activation.lower() == "relu":
|
| 298 |
+
sky = F.relu(sky_logits)
|
| 299 |
+
else:
|
| 300 |
+
sky = sky_logits
|
| 301 |
+
outs[self.sky_name] = sky.squeeze(1).view(B, S, *sky.shape[2:])
|
| 302 |
+
|
| 303 |
+
return outs
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
# -----------------------------------------------------------------------------
|
| 307 |
+
# DualDPT (depth + auxiliary "ray" head) -- used by DA3-Small / DA3-Base
|
| 308 |
+
# -----------------------------------------------------------------------------
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
class DualDPT(nn.Module):
|
| 312 |
+
"""Two-head DPT used by DA3-Small / DA3-Base."""
|
| 313 |
+
|
| 314 |
+
def __init__(
|
| 315 |
+
self,
|
| 316 |
+
dim_in: int,
|
| 317 |
+
patch_size: int = 14,
|
| 318 |
+
output_dim: int = 2,
|
| 319 |
+
activation: str = "exp",
|
| 320 |
+
conf_activation: str = "expp1",
|
| 321 |
+
features: int = 256,
|
| 322 |
+
out_channels: Sequence[int] = (256, 512, 1024, 1024),
|
| 323 |
+
pos_embed: bool = True,
|
| 324 |
+
down_ratio: int = 1,
|
| 325 |
+
aux_pyramid_levels: int = 4,
|
| 326 |
+
aux_out1_conv_num: int = 5,
|
| 327 |
+
head_names: Tuple[str, str] = ("depth", "ray"),
|
| 328 |
+
device=None, dtype=None, operations=None,
|
| 329 |
+
):
|
| 330 |
+
super().__init__()
|
| 331 |
+
self.patch_size = patch_size
|
| 332 |
+
self.activation = activation
|
| 333 |
+
self.conf_activation = conf_activation
|
| 334 |
+
self.pos_embed = pos_embed
|
| 335 |
+
self.down_ratio = down_ratio
|
| 336 |
+
self.aux_levels = aux_pyramid_levels
|
| 337 |
+
self.aux_out1_conv_num = aux_out1_conv_num
|
| 338 |
+
self.head_main, self.head_aux = head_names
|
| 339 |
+
self.intermediate_layer_idx: Tuple[int, int, int, int] = (0, 1, 2, 3)
|
| 340 |
+
# Toggle the auxiliary ray branch at runtime. Default off (mono path).
|
| 341 |
+
# DepthAnything3Net flips this on when running multi-view + ray-pose.
|
| 342 |
+
self.enable_aux: bool = False
|
| 343 |
+
|
| 344 |
+
self.norm = operations.LayerNorm(dim_in, device=device, dtype=dtype)
|
| 345 |
+
out_channels = list(out_channels)
|
| 346 |
+
self.projects = nn.ModuleList([
|
| 347 |
+
operations.Conv2d(dim_in, oc, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype)
|
| 348 |
+
for oc in out_channels
|
| 349 |
+
])
|
| 350 |
+
self.resize_layers = nn.ModuleList([
|
| 351 |
+
operations.ConvTranspose2d(out_channels[0], out_channels[0], kernel_size=4, stride=4, padding=0, device=device, dtype=dtype),
|
| 352 |
+
operations.ConvTranspose2d(out_channels[1], out_channels[1], kernel_size=2, stride=2, padding=0, device=device, dtype=dtype),
|
| 353 |
+
nn.Identity(),
|
| 354 |
+
operations.Conv2d(out_channels[3], out_channels[3], kernel_size=3, stride=2, padding=1, device=device, dtype=dtype),
|
| 355 |
+
])
|
| 356 |
+
|
| 357 |
+
self.scratch = _make_scratch(out_channels, features, device=device, dtype=dtype, operations=operations)
|
| 358 |
+
# Main fusion chain
|
| 359 |
+
self.scratch.refinenet1 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 360 |
+
self.scratch.refinenet2 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 361 |
+
self.scratch.refinenet3 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 362 |
+
self.scratch.refinenet4 = _make_fusion_block(features, has_residual=False, device=device, dtype=dtype, operations=operations)
|
| 363 |
+
# Auxiliary fusion chain (separate copies)
|
| 364 |
+
self.scratch.refinenet1_aux = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 365 |
+
self.scratch.refinenet2_aux = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 366 |
+
self.scratch.refinenet3_aux = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
|
| 367 |
+
self.scratch.refinenet4_aux = _make_fusion_block(features, has_residual=False, device=device, dtype=dtype, operations=operations)
|
| 368 |
+
|
| 369 |
+
head_features_1 = features
|
| 370 |
+
head_features_2 = 32
|
| 371 |
+
|
| 372 |
+
# Main head neck + final projection
|
| 373 |
+
self.scratch.output_conv1 = operations.Conv2d(
|
| 374 |
+
head_features_1, head_features_1 // 2, kernel_size=3, stride=1, padding=1,
|
| 375 |
+
device=device, dtype=dtype,
|
| 376 |
+
)
|
| 377 |
+
self.scratch.output_conv2 = nn.Sequential(
|
| 378 |
+
operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
|
| 379 |
+
nn.ReLU(inplace=False),
|
| 380 |
+
operations.Conv2d(head_features_2, output_dim, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
# Aux pre-head per level (multi-level pyramid)
|
| 384 |
+
self.scratch.output_conv1_aux = nn.ModuleList([
|
| 385 |
+
self._make_aux_out1_block(head_features_1, device=device, dtype=dtype, operations=operations)
|
| 386 |
+
for _ in range(self.aux_levels)
|
| 387 |
+
])
|
| 388 |
+
|
| 389 |
+
# Aux final projection per level (includes LayerNorm permute path).
|
| 390 |
+
ln_seq = [Permute((0, 2, 3, 1)),
|
| 391 |
+
operations.LayerNorm(head_features_2, device=device, dtype=dtype),
|
| 392 |
+
Permute((0, 3, 1, 2))]
|
| 393 |
+
self.scratch.output_conv2_aux = nn.ModuleList([
|
| 394 |
+
nn.Sequential(
|
| 395 |
+
operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
|
| 396 |
+
*ln_seq,
|
| 397 |
+
nn.ReLU(inplace=False),
|
| 398 |
+
operations.Conv2d(head_features_2, 7, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
|
| 399 |
+
)
|
| 400 |
+
for _ in range(self.aux_levels)
|
| 401 |
+
])
|
| 402 |
+
|
| 403 |
+
@staticmethod
|
| 404 |
+
def _make_aux_out1_block(in_ch: int, *, device=None, dtype=None, operations=None) -> nn.Sequential:
|
| 405 |
+
# aux_out1_conv_num=5 in all Apache-2.0 variants.
|
| 406 |
+
return nn.Sequential(
|
| 407 |
+
operations.Conv2d(in_ch, in_ch // 2, 3, 1, 1, device=device, dtype=dtype),
|
| 408 |
+
operations.Conv2d(in_ch // 2, in_ch, 3, 1, 1, device=device, dtype=dtype),
|
| 409 |
+
operations.Conv2d(in_ch, in_ch // 2, 3, 1, 1, device=device, dtype=dtype),
|
| 410 |
+
operations.Conv2d(in_ch // 2, in_ch, 3, 1, 1, device=device, dtype=dtype),
|
| 411 |
+
operations.Conv2d(in_ch, in_ch // 2, 3, 1, 1, device=device, dtype=dtype),
|
| 412 |
+
)
|
| 413 |
+
|
| 414 |
+
def forward(self, feats: List[torch.Tensor], H: int, W: int, patch_start_idx: int = 0, **_kwargs) -> dict:
|
| 415 |
+
B, S, N, C = feats[0][0].shape
|
| 416 |
+
feats_flat = [feat[0].reshape(B * S, N, C) for feat in feats]
|
| 417 |
+
|
| 418 |
+
ph, pw = H // self.patch_size, W // self.patch_size
|
| 419 |
+
resized = []
|
| 420 |
+
for stage_idx, take_idx in enumerate(self.intermediate_layer_idx):
|
| 421 |
+
x = feats_flat[take_idx][:, patch_start_idx:]
|
| 422 |
+
x = self.norm(x)
|
| 423 |
+
x = x.permute(0, 2, 1).contiguous().reshape(B * S, C, ph, pw)
|
| 424 |
+
x = self.projects[stage_idx](x)
|
| 425 |
+
if self.pos_embed:
|
| 426 |
+
x = _add_pos_embed(x, W, H)
|
| 427 |
+
x = self.resize_layers[stage_idx](x)
|
| 428 |
+
resized.append(x)
|
| 429 |
+
|
| 430 |
+
l1_rn = self.scratch.layer1_rn(resized[0])
|
| 431 |
+
l2_rn = self.scratch.layer2_rn(resized[1])
|
| 432 |
+
l3_rn = self.scratch.layer3_rn(resized[2])
|
| 433 |
+
l4_rn = self.scratch.layer4_rn(resized[3])
|
| 434 |
+
|
| 435 |
+
# Main pyramid (output_conv1 is applied inside the upstream `_fuse`,
|
| 436 |
+
# before interpolation -- replicate that order here).
|
| 437 |
+
m = self.scratch.refinenet4(l4_rn, size=l3_rn.shape[2:])
|
| 438 |
+
if self.enable_aux:
|
| 439 |
+
a4 = self.scratch.refinenet4_aux(l4_rn, size=l3_rn.shape[2:])
|
| 440 |
+
aux_pyr = [a4]
|
| 441 |
+
m = self.scratch.refinenet3(m, l3_rn, size=l2_rn.shape[2:])
|
| 442 |
+
if self.enable_aux:
|
| 443 |
+
aux_pyr.append(self.scratch.refinenet3_aux(aux_pyr[-1], l3_rn, size=l2_rn.shape[2:]))
|
| 444 |
+
m = self.scratch.refinenet2(m, l2_rn, size=l1_rn.shape[2:])
|
| 445 |
+
if self.enable_aux:
|
| 446 |
+
aux_pyr.append(self.scratch.refinenet2_aux(aux_pyr[-1], l2_rn, size=l1_rn.shape[2:]))
|
| 447 |
+
m = self.scratch.refinenet1(m, l1_rn)
|
| 448 |
+
if self.enable_aux:
|
| 449 |
+
aux_pyr.append(self.scratch.refinenet1_aux(aux_pyr[-1], l1_rn))
|
| 450 |
+
m = self.scratch.output_conv1(m)
|
| 451 |
+
|
| 452 |
+
h_out = int(ph * self.patch_size / self.down_ratio)
|
| 453 |
+
w_out = int(pw * self.patch_size / self.down_ratio)
|
| 454 |
+
|
| 455 |
+
m = _custom_interpolate(m, (h_out, w_out), mode="bilinear", align_corners=True)
|
| 456 |
+
if self.pos_embed:
|
| 457 |
+
m = _add_pos_embed(m, W, H)
|
| 458 |
+
main_logits = self.scratch.output_conv2(m)
|
| 459 |
+
fmap = main_logits.permute(0, 2, 3, 1)
|
| 460 |
+
depth_pred = _apply_activation(fmap[..., :-1], self.activation)
|
| 461 |
+
depth_conf = _apply_activation(fmap[..., -1], self.conf_activation)
|
| 462 |
+
|
| 463 |
+
outs = {
|
| 464 |
+
self.head_main: depth_pred.squeeze(-1).view(B, S, *depth_pred.shape[1:-1]),
|
| 465 |
+
f"{self.head_main}_conf": depth_conf.view(B, S, *depth_conf.shape[1:]),
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
if self.enable_aux:
|
| 469 |
+
# Auxiliary "ray" head (multi-level inside) -- only the last level
|
| 470 |
+
# is returned. Mirrors upstream ``DualDPT._fuse`` + ``_forward_impl``:
|
| 471 |
+
# each aux pyramid level goes through ``output_conv1_aux[i]``
|
| 472 |
+
# (5-layer conv stack that ends at ``features // 2`` channels),
|
| 473 |
+
# then the last level optionally gets a pos-embed and finally
|
| 474 |
+
# ``output_conv2_aux[-1]``.
|
| 475 |
+
aux_processed = [
|
| 476 |
+
self.scratch.output_conv1_aux[i](a) for i, a in enumerate(aux_pyr)
|
| 477 |
+
]
|
| 478 |
+
last_aux = aux_processed[-1]
|
| 479 |
+
if self.pos_embed:
|
| 480 |
+
last_aux = _add_pos_embed(last_aux, W, H)
|
| 481 |
+
last_aux_logits = self.scratch.output_conv2_aux[-1](last_aux)
|
| 482 |
+
fmap_last = last_aux_logits.permute(0, 2, 3, 1)
|
| 483 |
+
# Channels: [ray(6), ray_conf(1)]; ray uses 'linear' activation.
|
| 484 |
+
aux_pred = fmap_last[..., :-1]
|
| 485 |
+
aux_conf = _apply_activation(fmap_last[..., -1], self.conf_activation)
|
| 486 |
+
outs[self.head_aux] = aux_pred.view(B, S, *aux_pred.shape[1:])
|
| 487 |
+
outs[f"{self.head_aux}_conf"] = aux_conf.view(B, S, *aux_conf.shape[1:])
|
| 488 |
+
|
| 489 |
+
return outs
|
ComfyUI/comfy/ldm/depth_anything_3/model.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import Dict, Optional, Sequence
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
|
| 8 |
+
from comfy.image_encoders.dino2 import Dinov2Model
|
| 9 |
+
|
| 10 |
+
from .camera import CameraDec, CameraEnc
|
| 11 |
+
from .dpt import DPT, DualDPT
|
| 12 |
+
from .ray_pose import get_extrinsic_from_camray
|
| 13 |
+
from .transform import affine_inverse, pose_encoding_to_extri_intri
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
_HEAD_REGISTRY = {
|
| 17 |
+
"dpt": DPT,
|
| 18 |
+
"dualdpt": DualDPT,
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
# Backbone presets (mirror the upstream DINOv2 ViT variants).
|
| 23 |
+
_BACKBONE_PRESETS = {
|
| 24 |
+
"vits": dict(hidden_size=384, num_hidden_layers=12, num_attention_heads=6, use_swiglu_ffn=False),
|
| 25 |
+
"vitb": dict(hidden_size=768, num_hidden_layers=12, num_attention_heads=12, use_swiglu_ffn=False),
|
| 26 |
+
"vitl": dict(hidden_size=1024, num_hidden_layers=24, num_attention_heads=16, use_swiglu_ffn=False),
|
| 27 |
+
"vitg": dict(hidden_size=1536, num_hidden_layers=40, num_attention_heads=24, use_swiglu_ffn=True),
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def _build_backbone_config(
|
| 32 |
+
backbone_name: str,
|
| 33 |
+
*,
|
| 34 |
+
alt_start: int,
|
| 35 |
+
qknorm_start: int,
|
| 36 |
+
rope_start: int,
|
| 37 |
+
cat_token: bool,
|
| 38 |
+
) -> dict:
|
| 39 |
+
if backbone_name not in _BACKBONE_PRESETS:
|
| 40 |
+
raise ValueError(f"Unknown DINOv2 backbone variant: {backbone_name!r}")
|
| 41 |
+
cfg = dict(_BACKBONE_PRESETS[backbone_name])
|
| 42 |
+
cfg.update(dict(
|
| 43 |
+
layer_norm_eps=1e-6,
|
| 44 |
+
patch_size=14,
|
| 45 |
+
image_size=518,
|
| 46 |
+
# No mask_token in DA3 weights; omit param to avoid load warnings.
|
| 47 |
+
use_mask_token=False,
|
| 48 |
+
alt_start=alt_start,
|
| 49 |
+
qknorm_start=qknorm_start,
|
| 50 |
+
rope_start=rope_start,
|
| 51 |
+
cat_token=cat_token,
|
| 52 |
+
rope_freq=100.0,
|
| 53 |
+
))
|
| 54 |
+
return cfg
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class DepthAnything3Net(nn.Module):
|
| 58 |
+
|
| 59 |
+
PATCH_SIZE = 14
|
| 60 |
+
|
| 61 |
+
def __init__(
|
| 62 |
+
self,
|
| 63 |
+
# --- Backbone ---
|
| 64 |
+
backbone_name: str = "vitl",
|
| 65 |
+
out_layers: Sequence[int] = (4, 11, 17, 23),
|
| 66 |
+
alt_start: int = -1,
|
| 67 |
+
qknorm_start: int = -1,
|
| 68 |
+
rope_start: int = -1,
|
| 69 |
+
cat_token: bool = False,
|
| 70 |
+
# --- Head ---
|
| 71 |
+
head_type: str = "dpt", # dpt or dualdpt
|
| 72 |
+
head_dim_in: int = 1024,
|
| 73 |
+
head_output_dim: int = 1, # 1 = depth only, 2 = depth+conf
|
| 74 |
+
head_features: int = 256,
|
| 75 |
+
head_out_channels: Sequence[int] = (256, 512, 1024, 1024),
|
| 76 |
+
head_use_sky_head: bool = True, # ignored by DualDPT
|
| 77 |
+
head_pos_embed: Optional[bool] = None, # default: True for DualDPT, False for DPT
|
| 78 |
+
# --- Camera (multi-view) ---
|
| 79 |
+
has_cam_enc: bool = False,
|
| 80 |
+
has_cam_dec: bool = False,
|
| 81 |
+
cam_dim_out: Optional[int] = None, # CameraEnc dim_out (defaults to embed_dim)
|
| 82 |
+
cam_dec_dim_in: Optional[int] = None, # CameraDec dim_in (defaults to 2*embed_dim with cat_token)
|
| 83 |
+
# ComfyUI plumbing
|
| 84 |
+
device=None, dtype=None, operations=None,
|
| 85 |
+
**_ignored,
|
| 86 |
+
):
|
| 87 |
+
super().__init__()
|
| 88 |
+
head_cls = _HEAD_REGISTRY[head_type.lower()]
|
| 89 |
+
self.head_type = head_type.lower()
|
| 90 |
+
self.has_sky = (self.head_type == "dpt") and head_use_sky_head
|
| 91 |
+
self.has_conf = head_output_dim > 1
|
| 92 |
+
self.out_layers = list(out_layers)
|
| 93 |
+
|
| 94 |
+
backbone_cfg = _build_backbone_config(
|
| 95 |
+
backbone_name,
|
| 96 |
+
alt_start=alt_start,
|
| 97 |
+
qknorm_start=qknorm_start,
|
| 98 |
+
rope_start=rope_start,
|
| 99 |
+
cat_token=cat_token,
|
| 100 |
+
)
|
| 101 |
+
self.backbone = Dinov2Model(backbone_cfg, dtype, device, operations)
|
| 102 |
+
|
| 103 |
+
head_kwargs = dict(
|
| 104 |
+
dim_in=head_dim_in,
|
| 105 |
+
patch_size=self.PATCH_SIZE,
|
| 106 |
+
output_dim=head_output_dim,
|
| 107 |
+
features=head_features,
|
| 108 |
+
out_channels=tuple(head_out_channels),
|
| 109 |
+
device=device, dtype=dtype, operations=operations,
|
| 110 |
+
)
|
| 111 |
+
if self.head_type == "dpt":
|
| 112 |
+
head_kwargs.update(
|
| 113 |
+
use_sky_head=head_use_sky_head,
|
| 114 |
+
pos_embed=(False if head_pos_embed is None else head_pos_embed),
|
| 115 |
+
)
|
| 116 |
+
else: # dualdpt
|
| 117 |
+
head_kwargs.update(
|
| 118 |
+
pos_embed=(True if head_pos_embed is None else head_pos_embed),
|
| 119 |
+
)
|
| 120 |
+
self.head = head_cls(**head_kwargs)
|
| 121 |
+
|
| 122 |
+
# Built only if checkpoint has weights; cam_enc output dim == embed_dim.
|
| 123 |
+
embed_dim = backbone_cfg["hidden_size"]
|
| 124 |
+
if has_cam_enc:
|
| 125 |
+
self.cam_enc = CameraEnc(
|
| 126 |
+
dim_out=cam_dim_out if cam_dim_out is not None else embed_dim,
|
| 127 |
+
num_heads=max(1, embed_dim // 64),
|
| 128 |
+
device=device, dtype=dtype, operations=operations,
|
| 129 |
+
)
|
| 130 |
+
else:
|
| 131 |
+
self.cam_enc = None
|
| 132 |
+
if has_cam_dec:
|
| 133 |
+
default_dim = embed_dim * (2 if cat_token else 1)
|
| 134 |
+
self.cam_dec = CameraDec(
|
| 135 |
+
dim_in=cam_dec_dim_in if cam_dec_dim_in is not None else default_dim,
|
| 136 |
+
device=device, dtype=dtype, operations=operations,
|
| 137 |
+
)
|
| 138 |
+
else:
|
| 139 |
+
self.cam_dec = None
|
| 140 |
+
|
| 141 |
+
self.dtype = dtype
|
| 142 |
+
|
| 143 |
+
def forward(
|
| 144 |
+
self,
|
| 145 |
+
image: torch.Tensor,
|
| 146 |
+
extrinsics: Optional[torch.Tensor] = None,
|
| 147 |
+
intrinsics: Optional[torch.Tensor] = None,
|
| 148 |
+
*,
|
| 149 |
+
use_ray_pose: bool = False,
|
| 150 |
+
ref_view_strategy: str = "saddle_balanced",
|
| 151 |
+
export_feat_layers: Optional[Sequence[int]] = None,
|
| 152 |
+
**_unused,
|
| 153 |
+
) -> Dict[str, torch.Tensor]:
|
| 154 |
+
"""Run depth and optionally pose prediction."""
|
| 155 |
+
if image.ndim == 4:
|
| 156 |
+
image = image.unsqueeze(1) # (B, 1, 3, H, W)
|
| 157 |
+
assert image.ndim == 5 and image.shape[2] == 3, \
|
| 158 |
+
f"image must be (B,3,H,W) or (B,S,3,H,W); got {tuple(image.shape)}"
|
| 159 |
+
|
| 160 |
+
B, S, _, H, W = image.shape
|
| 161 |
+
assert H % self.PATCH_SIZE == 0 and W % self.PATCH_SIZE == 0, \
|
| 162 |
+
f"image H,W must be multiples of {self.PATCH_SIZE}; got {(H, W)}"
|
| 163 |
+
|
| 164 |
+
# Camera-token preparation (multi-view path).
|
| 165 |
+
cam_token = None
|
| 166 |
+
if extrinsics is not None and intrinsics is not None and self.cam_enc is not None:
|
| 167 |
+
cam_token = self.cam_enc(extrinsics, intrinsics, (H, W))
|
| 168 |
+
|
| 169 |
+
# Toggle aux ray output on/off depending on what the caller asked for.
|
| 170 |
+
if isinstance(self.head, DualDPT):
|
| 171 |
+
self.head.enable_aux = bool(use_ray_pose)
|
| 172 |
+
|
| 173 |
+
feats, aux_feats = self.backbone.get_intermediate_layers_da3(
|
| 174 |
+
image, self.out_layers, cam_token=cam_token,
|
| 175 |
+
ref_view_strategy=ref_view_strategy,
|
| 176 |
+
export_feat_layers=export_feat_layers,
|
| 177 |
+
)
|
| 178 |
+
head_out = self.head(feats, H=H, W=W, patch_start_idx=0)
|
| 179 |
+
|
| 180 |
+
# Pose prediction.
|
| 181 |
+
out: Dict[str, torch.Tensor] = {}
|
| 182 |
+
if use_ray_pose and "ray" in head_out and "ray_conf" in head_out:
|
| 183 |
+
ray = head_out["ray"]
|
| 184 |
+
ray_conf = head_out["ray_conf"]
|
| 185 |
+
extr_c2w, focal, pp = get_extrinsic_from_camray(
|
| 186 |
+
ray, ray_conf, ray.shape[-3], ray.shape[-2],
|
| 187 |
+
)
|
| 188 |
+
# Match the upstream output: w2c, drop the homogeneous row.
|
| 189 |
+
extr_w2c = affine_inverse(extr_c2w)[:, :, :3, :]
|
| 190 |
+
# Build pixel-space intrinsics from the normalised focal/pp output.
|
| 191 |
+
intr = torch.eye(3, device=ray.device, dtype=ray.dtype)
|
| 192 |
+
intr = intr[None, None].expand(extr_c2w.shape[0], extr_c2w.shape[1], 3, 3).clone()
|
| 193 |
+
intr[:, :, 0, 0] = focal[:, :, 0] / 2 * W
|
| 194 |
+
intr[:, :, 1, 1] = focal[:, :, 1] / 2 * H
|
| 195 |
+
intr[:, :, 0, 2] = pp[:, :, 0] * W * 0.5
|
| 196 |
+
intr[:, :, 1, 2] = pp[:, :, 1] * H * 0.5
|
| 197 |
+
out["extrinsics"] = extr_w2c
|
| 198 |
+
out["intrinsics"] = intr
|
| 199 |
+
elif self.cam_dec is not None and S > 1:
|
| 200 |
+
# Decode the cam-token of the final out_layer into a pose encoding.
|
| 201 |
+
cam_feat = feats[-1][1] # (B, S, dim_in_to_cam_dec)
|
| 202 |
+
pose_enc = self.cam_dec(cam_feat)
|
| 203 |
+
c2w_3x4, intr = pose_encoding_to_extri_intri(pose_enc, (H, W))
|
| 204 |
+
# Match the upstream output convention: w2c (world->camera), 3x4.
|
| 205 |
+
c2w_4x4 = torch.cat([
|
| 206 |
+
c2w_3x4,
|
| 207 |
+
torch.tensor([0, 0, 0, 1], device=c2w_3x4.device, dtype=c2w_3x4.dtype)
|
| 208 |
+
.view(1, 1, 1, 4).expand(B, S, 1, 4),
|
| 209 |
+
], dim=-2)
|
| 210 |
+
out["extrinsics"] = affine_inverse(c2w_4x4)[:, :, :3, :]
|
| 211 |
+
out["intrinsics"] = intr
|
| 212 |
+
|
| 213 |
+
# Flatten the views axis for per-pixel outputs (depth/conf/sky) so the
|
| 214 |
+
# per-image consumer keeps its (B*S, H, W) interface.
|
| 215 |
+
for k, v in head_out.items():
|
| 216 |
+
if k in ("ray", "ray_conf"):
|
| 217 |
+
# Keep multi-view shape for downstream pose work.
|
| 218 |
+
out[k] = v
|
| 219 |
+
elif v.ndim >= 3 and v.shape[0] == B and v.shape[1] == S:
|
| 220 |
+
out[k] = v.reshape(B * S, *v.shape[2:])
|
| 221 |
+
else:
|
| 222 |
+
out[k] = v
|
| 223 |
+
|
| 224 |
+
if export_feat_layers:
|
| 225 |
+
out["aux_features"] = self._reshape_aux_features(aux_feats, H, W)
|
| 226 |
+
return out
|
| 227 |
+
|
| 228 |
+
def _reshape_aux_features(self, aux_feats, H: int, W: int):
|
| 229 |
+
"""Reshape (B, S, N, C) aux features into (B, S, h_p, w_p, C)."""
|
| 230 |
+
ph, pw = H // self.PATCH_SIZE, W // self.PATCH_SIZE
|
| 231 |
+
out = []
|
| 232 |
+
for f in aux_feats:
|
| 233 |
+
B, S, N, C = f.shape
|
| 234 |
+
assert N == ph * pw, f"aux feature seq mismatch: {N} != {ph}*{pw}"
|
| 235 |
+
out.append(f.reshape(B, S, ph, pw, C))
|
| 236 |
+
return out
|
ComfyUI/comfy/ldm/depth_anything_3/preprocess.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Input/output preprocessing helpers for Depth Anything 3."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Tuple
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
|
| 9 |
+
import comfy.utils
|
| 10 |
+
|
| 11 |
+
PATCH_SIZE = 14
|
| 12 |
+
|
| 13 |
+
# ImageNet normalization constants used during DA3 training.
|
| 14 |
+
_IMAGENET_MEAN = torch.tensor([0.485, 0.456, 0.406])
|
| 15 |
+
_IMAGENET_STD = torch.tensor([0.229, 0.224, 0.225])
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _round_to_patch(x: int, patch: int = PATCH_SIZE) -> int:
|
| 19 |
+
down = (x // patch) * patch
|
| 20 |
+
up = down + patch
|
| 21 |
+
return up if abs(up - x) <= abs(x - down) else down
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def compute_target_size(orig_h: int, orig_w: int, process_res: int, method: str = "upper_bound_resize") -> Tuple[int, int]:
|
| 25 |
+
"""Compute (target_h, target_w) for a single image.
|
| 26 |
+
upper_bound_resize: scale longest side to process_res, then round each dim to nearest multiple of 14 (default upstream method).
|
| 27 |
+
lower_bound_resize: scale shortest side to process_res, then round."""
|
| 28 |
+
|
| 29 |
+
if method == "upper_bound_resize":
|
| 30 |
+
longest = max(orig_h, orig_w)
|
| 31 |
+
scale = process_res / float(longest)
|
| 32 |
+
elif method == "lower_bound_resize":
|
| 33 |
+
shortest = min(orig_h, orig_w)
|
| 34 |
+
scale = process_res / float(shortest)
|
| 35 |
+
else:
|
| 36 |
+
raise ValueError(f"Unsupported process_res_method: {method}")
|
| 37 |
+
|
| 38 |
+
new_w = max(1, _round_to_patch(int(round(orig_w * scale))))
|
| 39 |
+
new_h = max(1, _round_to_patch(int(round(orig_h * scale))))
|
| 40 |
+
return new_h, new_w
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def preprocess_image(image: torch.Tensor, process_res: int = 504, method: str = "upper_bound_resize") -> torch.Tensor:
|
| 44 |
+
assert image.ndim == 4 and image.shape[-1] == 3, f"expected (B,H,W,3) IMAGE; got {tuple(image.shape)}"
|
| 45 |
+
B, H, W, _ = image.shape
|
| 46 |
+
target_h, target_w = compute_target_size(H, W, process_res, method)
|
| 47 |
+
|
| 48 |
+
# (B, H, W, 3) -> (B, 3, H, W)
|
| 49 |
+
x = image.movedim(-1, 1).contiguous()
|
| 50 |
+
if (target_h, target_w) != (H, W):
|
| 51 |
+
# Upstream uses cv2 INTER_CUBIC (upscale) / INTER_AREA (downscale).
|
| 52 |
+
# Lanczos in ``common_upscale`` is anti-aliased and produces the
|
| 53 |
+
# closest pixel-wise match in a sweep across {bilinear, bicubic,
|
| 54 |
+
# area, lanczos, bislerp}. Used in both directions for simplicity.
|
| 55 |
+
x = comfy.utils.common_upscale(x.float(), target_w, target_h, "lanczos", "disabled",)
|
| 56 |
+
x = x.clamp(0.0, 1.0)
|
| 57 |
+
|
| 58 |
+
mean = _IMAGENET_MEAN.to(device=x.device, dtype=x.dtype).view(1, 3, 1, 1)
|
| 59 |
+
std = _IMAGENET_STD.to(device=x.device, dtype=x.dtype).view(1, 3, 1, 1)
|
| 60 |
+
x = (x - mean) / std
|
| 61 |
+
return x
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# -----------------------------------------------------------------------------
|
| 65 |
+
# Output post-processing (sky-aware clipping for Mono/Metric variants)
|
| 66 |
+
# -----------------------------------------------------------------------------
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def compute_non_sky_mask(sky_prediction: torch.Tensor, threshold: float = 0.3) -> torch.Tensor:
|
| 70 |
+
"""Boolean mask: True for non-sky pixels (sky probability < threshold)."""
|
| 71 |
+
return sky_prediction < threshold
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def apply_sky_aware_clip(depth: torch.Tensor, sky: torch.Tensor, threshold: float = 0.3, quantile: float = 0.99) -> torch.Tensor:
|
| 75 |
+
"""Clips sky regions to the 99th percentile of non-sky depth. Returns a new depth tensor."""
|
| 76 |
+
non_sky = compute_non_sky_mask(sky, threshold=threshold)
|
| 77 |
+
if non_sky.sum() <= 10 or (~non_sky).sum() <= 10:
|
| 78 |
+
return depth.clone()
|
| 79 |
+
|
| 80 |
+
non_sky_depth = depth[non_sky]
|
| 81 |
+
if non_sky_depth.numel() > 100_000:
|
| 82 |
+
idx = torch.randint(0, non_sky_depth.numel(), (100_000,), device=non_sky_depth.device)
|
| 83 |
+
sampled = non_sky_depth[idx]
|
| 84 |
+
else:
|
| 85 |
+
sampled = non_sky_depth
|
| 86 |
+
|
| 87 |
+
max_depth = torch.quantile(sampled, quantile)
|
| 88 |
+
out = depth.clone()
|
| 89 |
+
out[~non_sky] = max_depth
|
| 90 |
+
return out
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def normalize_depth_v2_style(depth: torch.Tensor, sky: torch.Tensor | None = None, low_quantile: float = 0.01, high_quantile: float = 0.99) -> torch.Tensor:
|
| 94 |
+
"""V2-style normalization computes percentile bounds over non-sky pixels (when available), then maps depth into [0, 1] with near = white (1.0)."""
|
| 95 |
+
if sky is not None:
|
| 96 |
+
mask = compute_non_sky_mask(sky)
|
| 97 |
+
if mask.any():
|
| 98 |
+
valid = depth[mask]
|
| 99 |
+
else:
|
| 100 |
+
valid = depth.flatten()
|
| 101 |
+
else:
|
| 102 |
+
valid = depth.flatten()
|
| 103 |
+
|
| 104 |
+
if valid.numel() > 100_000:
|
| 105 |
+
idx = torch.randint(0, valid.numel(), (100_000,), device=valid.device)
|
| 106 |
+
sample = valid[idx]
|
| 107 |
+
else:
|
| 108 |
+
sample = valid
|
| 109 |
+
|
| 110 |
+
lo = torch.quantile(sample, low_quantile)
|
| 111 |
+
hi = torch.quantile(sample, high_quantile)
|
| 112 |
+
rng = (hi - lo).clamp(min=1e-6)
|
| 113 |
+
norm = ((depth - lo) / rng).clamp(0.0, 1.0)
|
| 114 |
+
# Nearer pixels are brighter (1.0)
|
| 115 |
+
norm = 1.0 - norm
|
| 116 |
+
if sky is not None:
|
| 117 |
+
# Sky pixels become black (far / unknown)
|
| 118 |
+
sky_mask = ~compute_non_sky_mask(sky)
|
| 119 |
+
norm = torch.where(sky_mask, torch.zeros_like(norm), norm)
|
| 120 |
+
return norm
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def normalize_depth_min_max(depth: torch.Tensor) -> torch.Tensor:
|
| 124 |
+
"""Simple per-frame min/max normalization with near=1.0 convention."""
|
| 125 |
+
lo = depth.amin(dim=(-2, -1), keepdim=True)
|
| 126 |
+
hi = depth.amax(dim=(-2, -1), keepdim=True)
|
| 127 |
+
rng = (hi - lo).clamp(min=1e-6)
|
| 128 |
+
return 1.0 - ((depth - lo) / rng).clamp(0.0, 1.0)
|
ComfyUI/comfy/ldm/depth_anything_3/ray_pose.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Ray-to-pose conversion for the multi-view path of Depth Anything 3."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Optional, Tuple
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# qr/svd use fp32: CUDA often has no fp16/bf16 kernels for these ops.
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def _ql_decomposition(A: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 14 |
+
"""Decompose A = Q @ L with Q orthogonal and L lower-triangular.
|
| 15 |
+
Implemented in terms of QR by reversing the columns/rows; the standard
|
| 16 |
+
trick from the upstream reference. Inputs A are (3, 3)."""
|
| 17 |
+
P = torch.tensor([[0, 0, 1], [0, 1, 0], [1, 0, 0]], device=A.device, dtype=A.dtype)
|
| 18 |
+
A_tilde = A @ P
|
| 19 |
+
# CUDA QR is not implemented for fp16/bf16; upcast just for this call.
|
| 20 |
+
Q_tilde, R_tilde = torch.linalg.qr(A_tilde.float())
|
| 21 |
+
Q_tilde = Q_tilde.to(A.dtype)
|
| 22 |
+
R_tilde = R_tilde.to(A.dtype)
|
| 23 |
+
Q = Q_tilde @ P
|
| 24 |
+
L = P @ R_tilde @ P
|
| 25 |
+
d = torch.diag(L)
|
| 26 |
+
sign = torch.sign(d)
|
| 27 |
+
Q = Q * sign[None, :] # scale columns of Q
|
| 28 |
+
L = L * sign[:, None] # scale rows of L
|
| 29 |
+
return Q, L
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _homogenize_points(points: torch.Tensor) -> torch.Tensor:
|
| 33 |
+
return torch.cat([points, torch.ones_like(points[..., :1])], dim=-1)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# -----------------------------------------------------------------------------
|
| 37 |
+
# Weighted-LSQ + RANSAC homography (batched)
|
| 38 |
+
# -----------------------------------------------------------------------------
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _find_homography_weighted_lsq(src_pts: torch.Tensor, dst_pts: torch.Tensor, confident_weight: torch.Tensor,) -> torch.Tensor:
|
| 42 |
+
"""Solve a single H with weighted least-squares (DLT)."""
|
| 43 |
+
N = src_pts.shape[0]
|
| 44 |
+
if N < 4:
|
| 45 |
+
raise ValueError("At least 4 points are required to compute a homography.")
|
| 46 |
+
w = confident_weight.sqrt().unsqueeze(1) # (N, 1)
|
| 47 |
+
x = src_pts[:, 0:1]
|
| 48 |
+
y = src_pts[:, 1:2]
|
| 49 |
+
u = dst_pts[:, 0:1]
|
| 50 |
+
v = dst_pts[:, 1:2]
|
| 51 |
+
zeros = torch.zeros_like(x)
|
| 52 |
+
A1 = torch.cat([-x * w, -y * w, -w, zeros, zeros, zeros, x * u * w, y * u * w, u * w], dim=1)
|
| 53 |
+
A2 = torch.cat([zeros, zeros, zeros, -x * w, -y * w, -w, x * v * w, y * v * w, v * w], dim=1)
|
| 54 |
+
A = torch.cat([A1, A2], dim=0) # (2N, 9)
|
| 55 |
+
# CUDA SVD is not implemented for fp16/bf16; upcast just for this call.
|
| 56 |
+
_, _, Vh = torch.linalg.svd(A.float())
|
| 57 |
+
Vh = Vh.to(A.dtype)
|
| 58 |
+
H = Vh[-1].reshape(3, 3)
|
| 59 |
+
return H / H[-1, -1]
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def _find_homography_weighted_lsq_batched(src_pts_batch: torch.Tensor, dst_pts_batch: torch.Tensor, confident_weight_batch: torch.Tensor) -> torch.Tensor:
|
| 63 |
+
"""Batched DLT solver. Inputs (B, K, 2) / (B, K); output (B, 3, 3)."""
|
| 64 |
+
B, K, _ = src_pts_batch.shape
|
| 65 |
+
w = confident_weight_batch.sqrt().unsqueeze(2)
|
| 66 |
+
x = src_pts_batch[:, :, 0:1]
|
| 67 |
+
y = src_pts_batch[:, :, 1:2]
|
| 68 |
+
u = dst_pts_batch[:, :, 0:1]
|
| 69 |
+
v = dst_pts_batch[:, :, 1:2]
|
| 70 |
+
zeros = torch.zeros_like(x)
|
| 71 |
+
A1 = torch.cat([-x * w, -y * w, -w, zeros, zeros, zeros, x * u * w, y * u * w, u * w], dim=2)
|
| 72 |
+
A2 = torch.cat([zeros, zeros, zeros, -x * w, -y * w, -w, x * v * w, y * v * w, v * w], dim=2)
|
| 73 |
+
A = torch.cat([A1, A2], dim=1) # (B, 2K, 9)
|
| 74 |
+
# CUDA SVD is not implemented for fp16/bf16; upcast just for this call.
|
| 75 |
+
_, _, Vh = torch.linalg.svd(A.float())
|
| 76 |
+
Vh = Vh.to(A.dtype)
|
| 77 |
+
H = Vh[:, -1].reshape(B, 3, 3)
|
| 78 |
+
return H / H[:, 2:3, 2:3]
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _ransac_find_homography_weighted_batched(
|
| 82 |
+
src_pts: torch.Tensor, # (B, N, 2)
|
| 83 |
+
dst_pts: torch.Tensor, # (B, N, 2)
|
| 84 |
+
confident_weight: torch.Tensor, # (B, N)
|
| 85 |
+
n_sample: int,
|
| 86 |
+
n_iter: int = 100,
|
| 87 |
+
reproj_threshold: float = 3.0,
|
| 88 |
+
num_sample_for_ransac: int = 8,
|
| 89 |
+
max_inlier_num: int = 10000,
|
| 90 |
+
rand_sample_iters_idx: Optional[torch.Tensor] = None,
|
| 91 |
+
) -> torch.Tensor:
|
| 92 |
+
"""Batched weighted-RANSAC homography estimator. Returns (B, 3, 3) homography matrices."""
|
| 93 |
+
B, N, _ = src_pts.shape
|
| 94 |
+
assert N >= 4
|
| 95 |
+
device = src_pts.device
|
| 96 |
+
|
| 97 |
+
sorted_idx = torch.argsort(confident_weight, descending=True, dim=1)
|
| 98 |
+
candidate_idx = sorted_idx[:, :n_sample] # (B, n_sample)
|
| 99 |
+
|
| 100 |
+
if rand_sample_iters_idx is None:
|
| 101 |
+
rand_sample_iters_idx = torch.stack(
|
| 102 |
+
[torch.randperm(n_sample, device=device)[:num_sample_for_ransac]
|
| 103 |
+
for _ in range(n_iter)],
|
| 104 |
+
dim=0,
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
rand_idx = candidate_idx[:, rand_sample_iters_idx] # (B, n_iter, k)
|
| 108 |
+
b_idx = (
|
| 109 |
+
torch.arange(B, device=device)
|
| 110 |
+
.view(B, 1, 1)
|
| 111 |
+
.expand(B, n_iter, num_sample_for_ransac)
|
| 112 |
+
)
|
| 113 |
+
src_b = src_pts[b_idx, rand_idx]
|
| 114 |
+
dst_b = dst_pts[b_idx, rand_idx]
|
| 115 |
+
w_b = confident_weight[b_idx, rand_idx]
|
| 116 |
+
|
| 117 |
+
cB, cN = src_b.shape[:2]
|
| 118 |
+
H_batch = _find_homography_weighted_lsq_batched(
|
| 119 |
+
src_b.flatten(0, 1), dst_b.flatten(0, 1), w_b.flatten(0, 1),
|
| 120 |
+
).unflatten(0, (cB, cN)) # (B, n_iter, 3, 3)
|
| 121 |
+
|
| 122 |
+
src_homo = torch.cat([src_pts, torch.ones(B, N, 1, device=device, dtype=src_pts.dtype)], dim=2)
|
| 123 |
+
proj = torch.bmm(
|
| 124 |
+
src_homo.unsqueeze(1).expand(B, n_iter, N, 3).reshape(-1, N, 3),
|
| 125 |
+
H_batch.reshape(-1, 3, 3).transpose(1, 2),
|
| 126 |
+
) # (B*n_iter, N, 3)
|
| 127 |
+
proj_xy = (proj[:, :, :2] / proj[:, :, 2:3]).reshape(B, n_iter, N, 2)
|
| 128 |
+
err = ((proj_xy - dst_pts.unsqueeze(1)) ** 2).sum(-1).sqrt() # (B, n_iter, N)
|
| 129 |
+
inlier_mask = err < reproj_threshold
|
| 130 |
+
score = (inlier_mask * confident_weight.unsqueeze(1)).sum(dim=2)
|
| 131 |
+
best_idx = torch.argmax(score, dim=1)
|
| 132 |
+
best_inlier_mask = inlier_mask[torch.arange(B, device=device), best_idx]
|
| 133 |
+
|
| 134 |
+
# Refit with the inlier set (per-batch, since the inlier counts vary).
|
| 135 |
+
H_inlier_list = []
|
| 136 |
+
for b in range(B):
|
| 137 |
+
mask = best_inlier_mask[b]
|
| 138 |
+
in_src = src_pts[b][mask]
|
| 139 |
+
in_dst = dst_pts[b][mask]
|
| 140 |
+
in_w = confident_weight[b][mask]
|
| 141 |
+
if in_src.shape[0] < 4:
|
| 142 |
+
# Fall back to identity when RANSAC fails to find enough inliers.
|
| 143 |
+
H_inlier_list.append(torch.eye(3, device=device, dtype=src_pts.dtype))
|
| 144 |
+
continue
|
| 145 |
+
sorted_w = torch.argsort(in_w, descending=True)
|
| 146 |
+
if len(sorted_w) > max_inlier_num:
|
| 147 |
+
keep = max(int(len(sorted_w) * 0.95), max_inlier_num)
|
| 148 |
+
sorted_w = sorted_w[:keep][torch.randperm(keep, device=device)[:max_inlier_num]]
|
| 149 |
+
H_inlier_list.append(
|
| 150 |
+
_find_homography_weighted_lsq(in_src[sorted_w], in_dst[sorted_w], in_w[sorted_w])
|
| 151 |
+
)
|
| 152 |
+
return torch.stack(H_inlier_list, dim=0)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
# -----------------------------------------------------------------------------
|
| 156 |
+
# Camera-ray utilities
|
| 157 |
+
# -----------------------------------------------------------------------------
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def _unproject_identity(num_y: int, num_x: int, B: int, S: int, device, dtype) -> torch.Tensor:
|
| 161 |
+
"""Camera-space unit rays for an identity intrinsic on a 2x2 image plane."""
|
| 162 |
+
dx = 1.0 / num_x
|
| 163 |
+
dy = 1.0 / num_y
|
| 164 |
+
# Centered camera-space coords directly (skip the K^-1 step since it's
|
| 165 |
+
# just a translation by -1 on x and y when K is identity-with-center=1).
|
| 166 |
+
y = torch.linspace(-(1 - dy), (1 - dy), num_y, device=device, dtype=dtype)
|
| 167 |
+
x = torch.linspace(-(1 - dx), (1 - dx), num_x, device=device, dtype=dtype)
|
| 168 |
+
yy, xx = torch.meshgrid(y, x, indexing="ij")
|
| 169 |
+
grid = torch.stack((xx, yy), dim=-1) # (h, w, 2)
|
| 170 |
+
grid = grid.unsqueeze(0).unsqueeze(0).expand(B, S, num_y, num_x, 2)
|
| 171 |
+
return torch.cat([grid, torch.ones_like(grid[..., :1])], dim=-1)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def _camray_to_caminfo(
|
| 175 |
+
camray: torch.Tensor, # (B, S, h, w, 6)
|
| 176 |
+
confidence: Optional[torch.Tensor] = None, # (B, S, h, w)
|
| 177 |
+
reproj_threshold: float = 0.2,
|
| 178 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 179 |
+
"""Convert per-pixel camera rays to per-view (R, T, focal, principal)."""
|
| 180 |
+
if confidence is None:
|
| 181 |
+
confidence = torch.ones_like(camray[..., 0])
|
| 182 |
+
B, S, h, w, _ = camray.shape
|
| 183 |
+
device = camray.device
|
| 184 |
+
dtype = camray.dtype
|
| 185 |
+
|
| 186 |
+
rays_target = camray[..., :3] # (B, S, h, w, 3)
|
| 187 |
+
rays_origin = _unproject_identity(h, w, B, S, device, dtype)
|
| 188 |
+
|
| 189 |
+
# Flatten (B*S, h*w, *) for the RANSAC routine.
|
| 190 |
+
rays_target = rays_target.flatten(0, 1).flatten(1, 2)
|
| 191 |
+
rays_origin = rays_origin.flatten(0, 1).flatten(1, 2)
|
| 192 |
+
weights = confidence.flatten(0, 1).flatten(1, 2).clone()
|
| 193 |
+
|
| 194 |
+
# Project to 2D in homogeneous form (the upstream calls this "perspective division").
|
| 195 |
+
z_thresh = 1e-4
|
| 196 |
+
mask = (rays_target[:, :, 2].abs() > z_thresh) & (rays_origin[:, :, 2].abs() > z_thresh)
|
| 197 |
+
weights = torch.where(mask, weights, torch.zeros_like(weights))
|
| 198 |
+
src = rays_origin.clone()
|
| 199 |
+
dst = rays_target.clone()
|
| 200 |
+
src[..., 0] = torch.where(mask, src[..., 0] / src[..., 2], src[..., 0])
|
| 201 |
+
src[..., 1] = torch.where(mask, src[..., 1] / src[..., 2], src[..., 1])
|
| 202 |
+
dst[..., 0] = torch.where(mask, dst[..., 0] / dst[..., 2], dst[..., 0])
|
| 203 |
+
dst[..., 1] = torch.where(mask, dst[..., 1] / dst[..., 2], dst[..., 1])
|
| 204 |
+
src = src[..., :2]
|
| 205 |
+
dst = dst[..., :2]
|
| 206 |
+
|
| 207 |
+
N = src.shape[1]
|
| 208 |
+
n_iter = 100
|
| 209 |
+
sample_ratio = 0.3
|
| 210 |
+
num_sample_for_ransac = 8
|
| 211 |
+
n_sample = max(num_sample_for_ransac, int(N * sample_ratio))
|
| 212 |
+
rand_idx = torch.stack(
|
| 213 |
+
[torch.randperm(n_sample, device=device)[:num_sample_for_ransac] for _ in range(n_iter)],
|
| 214 |
+
dim=0,
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
# Chunk along the view axis to keep peak memory predictable.
|
| 218 |
+
chunk = 2
|
| 219 |
+
A_list = []
|
| 220 |
+
for i in range(0, src.shape[0], chunk):
|
| 221 |
+
A = _ransac_find_homography_weighted_batched(
|
| 222 |
+
src[i:i + chunk], dst[i:i + chunk], weights[i:i + chunk],
|
| 223 |
+
n_sample=n_sample, n_iter=n_iter,
|
| 224 |
+
num_sample_for_ransac=num_sample_for_ransac,
|
| 225 |
+
reproj_threshold=reproj_threshold,
|
| 226 |
+
rand_sample_iters_idx=rand_idx,
|
| 227 |
+
max_inlier_num=8000,
|
| 228 |
+
)
|
| 229 |
+
# Flip sign on dets that come out < 0 (so that the QL produces a
|
| 230 |
+
# right-handed rotation). ``det`` lacks fp16/bf16 CUDA kernels, so
|
| 231 |
+
# do the comparison in fp32.
|
| 232 |
+
flip = torch.linalg.det(A.float()) < 0
|
| 233 |
+
A = torch.where(flip[:, None, None], -A, A)
|
| 234 |
+
A_list.append(A)
|
| 235 |
+
A = torch.cat(A_list, dim=0) # (B*S, 3, 3)
|
| 236 |
+
|
| 237 |
+
R_list, f_list, pp_list = [], [], []
|
| 238 |
+
for i in range(A.shape[0]):
|
| 239 |
+
R, L = _ql_decomposition(A[i])
|
| 240 |
+
L = L / L[2][2]
|
| 241 |
+
f_list.append(torch.stack((L[0][0], L[1][1])))
|
| 242 |
+
pp_list.append(torch.stack((L[2][0], L[2][1])))
|
| 243 |
+
R_list.append(R)
|
| 244 |
+
R = torch.stack(R_list).reshape(B, S, 3, 3)
|
| 245 |
+
focal = torch.stack(f_list).reshape(B, S, 2)
|
| 246 |
+
pp = torch.stack(pp_list).reshape(B, S, 2)
|
| 247 |
+
|
| 248 |
+
# Translation: confidence-weighted average of camray direction(s).
|
| 249 |
+
cf = confidence.flatten(0, 1).flatten(1, 2)
|
| 250 |
+
T = (camray.flatten(0, 1).flatten(1, 2)[..., 3:] * cf.unsqueeze(-1)).sum(dim=1)
|
| 251 |
+
T = T / cf.sum(dim=-1, keepdim=True)
|
| 252 |
+
T = T.reshape(B, S, 3)
|
| 253 |
+
|
| 254 |
+
# Match upstream output convention: focal -> 1/focal, pp + 1.
|
| 255 |
+
return R, T, 1.0 / focal, pp + 1.0
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def get_extrinsic_from_camray(
|
| 259 |
+
camray: torch.Tensor, # (B, S, h, w, 6)
|
| 260 |
+
conf: torch.Tensor, # (B, S, h, w, 1) or (B, S, h, w)
|
| 261 |
+
patch_size_y: int,
|
| 262 |
+
patch_size_x: int,
|
| 263 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 264 |
+
"""Wrap a 4x4 extrinsic + per-view focal + principal-point output."""
|
| 265 |
+
if conf.ndim == 5 and conf.shape[-1] == 1:
|
| 266 |
+
conf = conf.squeeze(-1)
|
| 267 |
+
R, T, focal, pp = _camray_to_caminfo(camray, confidence=conf)
|
| 268 |
+
extr = torch.cat([R, T.unsqueeze(-1)], dim=-1) # (B, S, 3, 4)
|
| 269 |
+
homo_row = torch.tensor([0, 0, 0, 1], dtype=R.dtype, device=R.device)
|
| 270 |
+
homo_row = homo_row.view(1, 1, 1, 4).expand(R.shape[0], R.shape[1], 1, 4)
|
| 271 |
+
extr = torch.cat([extr, homo_row], dim=-2) # (B, S, 4, 4)
|
| 272 |
+
return extr, focal, pp
|
ComfyUI/comfy/ldm/depth_anything_3/reference_view_selector.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Reference-view selection for the multi-view path of Depth Anything 3."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Literal
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
RefViewStrategy = Literal["first", "middle", "saddle_balanced", "saddle_sim_range"]
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
# Per the upstream constants module: ``THRESH_FOR_REF_SELECTION = 3``.
|
| 14 |
+
# Reference selection only runs when there are at least this many views.
|
| 15 |
+
THRESH_FOR_REF_SELECTION: int = 3
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def select_reference_view(x: torch.Tensor, strategy: RefViewStrategy = "saddle_balanced") -> torch.Tensor:
|
| 19 |
+
"""Pick a reference view index per batch element."""
|
| 20 |
+
B, S, _, _ = x.shape
|
| 21 |
+
if S <= 1:
|
| 22 |
+
return torch.zeros(B, dtype=torch.long, device=x.device)
|
| 23 |
+
if strategy == "first":
|
| 24 |
+
return torch.zeros(B, dtype=torch.long, device=x.device)
|
| 25 |
+
if strategy == "middle":
|
| 26 |
+
return torch.full((B,), S // 2, dtype=torch.long, device=x.device)
|
| 27 |
+
|
| 28 |
+
# Feature-based strategies: normalised cls/cam token per view.
|
| 29 |
+
img_class_feat = x[:, :, 0] / x[:, :, 0].norm(dim=-1, keepdim=True) # (B,S,C)
|
| 30 |
+
|
| 31 |
+
if strategy == "saddle_balanced":
|
| 32 |
+
sim = torch.matmul(img_class_feat, img_class_feat.transpose(1, 2)) # (B,S,S)
|
| 33 |
+
sim_no_diag = sim - torch.eye(S, device=sim.device).unsqueeze(0)
|
| 34 |
+
sim_score = sim_no_diag.sum(dim=-1) / (S - 1) # (B,S)
|
| 35 |
+
feat_norm = x[:, :, 0].norm(dim=-1) # (B,S)
|
| 36 |
+
feat_var = img_class_feat.var(dim=-1) # (B,S)
|
| 37 |
+
|
| 38 |
+
def _normalize(metric):
|
| 39 |
+
mn = metric.min(dim=1, keepdim=True).values
|
| 40 |
+
mx = metric.max(dim=1, keepdim=True).values
|
| 41 |
+
return (metric - mn) / (mx - mn + 1e-8)
|
| 42 |
+
|
| 43 |
+
sim_n, norm_n, var_n = _normalize(sim_score), _normalize(feat_norm), _normalize(feat_var)
|
| 44 |
+
balance = (sim_n - 0.5).abs() + (norm_n - 0.5).abs() + (var_n - 0.5).abs()
|
| 45 |
+
return balance.argmin(dim=1)
|
| 46 |
+
|
| 47 |
+
if strategy == "saddle_sim_range":
|
| 48 |
+
sim = torch.matmul(img_class_feat, img_class_feat.transpose(1, 2))
|
| 49 |
+
sim_no_diag = sim - torch.eye(S, device=sim.device).unsqueeze(0)
|
| 50 |
+
sim_max = sim_no_diag.max(dim=-1).values
|
| 51 |
+
sim_min = sim_no_diag.min(dim=-1).values
|
| 52 |
+
return (sim_max - sim_min).argmax(dim=1)
|
| 53 |
+
|
| 54 |
+
raise ValueError(
|
| 55 |
+
f"Unknown reference view selection strategy: {strategy!r}. "
|
| 56 |
+
f"Must be one of: 'first', 'middle', 'saddle_balanced', 'saddle_sim_range'"
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def reorder_by_reference(x: torch.Tensor, b_idx: torch.Tensor) -> torch.Tensor:
|
| 61 |
+
"""Reorder x so the reference view is at position 0 in axis S."""
|
| 62 |
+
B, S = x.shape[0], x.shape[1]
|
| 63 |
+
if S <= 1:
|
| 64 |
+
return x
|
| 65 |
+
positions = torch.arange(S, device=x.device).unsqueeze(0).expand(B, -1)
|
| 66 |
+
b_idx_exp = b_idx.unsqueeze(1)
|
| 67 |
+
reorder = torch.where(
|
| 68 |
+
(positions > 0) & (positions <= b_idx_exp),
|
| 69 |
+
positions - 1,
|
| 70 |
+
positions,
|
| 71 |
+
)
|
| 72 |
+
reorder[:, 0] = b_idx
|
| 73 |
+
batch = torch.arange(B, device=x.device).unsqueeze(1)
|
| 74 |
+
return x[batch, reorder]
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def restore_original_order(x: torch.Tensor, b_idx: torch.Tensor) -> torch.Tensor:
|
| 78 |
+
"""Inverse of reorder_by_reference."""
|
| 79 |
+
B, S = x.shape[0], x.shape[1]
|
| 80 |
+
if S <= 1:
|
| 81 |
+
return x
|
| 82 |
+
target_positions = torch.arange(S, device=x.device).unsqueeze(0).expand(B, -1)
|
| 83 |
+
b_idx_exp = b_idx.unsqueeze(1)
|
| 84 |
+
restore = torch.where(target_positions < b_idx_exp, target_positions + 1, target_positions)
|
| 85 |
+
restore = torch.scatter(restore, dim=1, index=b_idx_exp, src=torch.zeros_like(b_idx_exp))
|
| 86 |
+
batch = torch.arange(B, device=x.device).unsqueeze(1)
|
| 87 |
+
return x[batch, restore]
|
ComfyUI/comfy/ldm/depth_anything_3/transform.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Geometry / camera transform helpers for Depth Anything 3."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Tuple
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
# -----------------------------------------------------------------------------
|
| 12 |
+
# Affine 4x4 helpers
|
| 13 |
+
# -----------------------------------------------------------------------------
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def as_homogeneous(ext: torch.Tensor) -> torch.Tensor:
|
| 17 |
+
"""Promote (...,3,4) extrinsics to (...,4,4) homogeneous form. No-op when the input is already ``(...,4,4)``."""
|
| 18 |
+
if ext.shape[-2:] == (4, 4):
|
| 19 |
+
return ext
|
| 20 |
+
if ext.shape[-2:] == (3, 4):
|
| 21 |
+
ones = torch.zeros_like(ext[..., :1, :4])
|
| 22 |
+
ones[..., 0, 3] = 1.0
|
| 23 |
+
return torch.cat([ext, ones], dim=-2)
|
| 24 |
+
raise ValueError(f"Invalid affine shape: {ext.shape}")
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def affine_inverse(A: torch.Tensor) -> torch.Tensor:
|
| 28 |
+
"""Inverse of an affine matrix ``[R|T; 0 0 0 1]``."""
|
| 29 |
+
R = A[..., :3, :3]
|
| 30 |
+
T = A[..., :3, 3:]
|
| 31 |
+
P = A[..., 3:, :]
|
| 32 |
+
return torch.cat([torch.cat([R.mT, -R.mT @ T], dim=-1), P], dim=-2)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
# -----------------------------------------------------------------------------
|
| 36 |
+
# Quaternion <-> rotation matrix (xyzw / scalar-last)
|
| 37 |
+
# -----------------------------------------------------------------------------
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _sqrt_positive_part(x: torch.Tensor) -> torch.Tensor:
|
| 41 |
+
"""sqrt(max(0, x)) with a zero subgradient where x == 0."""
|
| 42 |
+
ret = torch.zeros_like(x)
|
| 43 |
+
positive_mask = x > 0
|
| 44 |
+
if torch.is_grad_enabled():
|
| 45 |
+
ret[positive_mask] = torch.sqrt(x[positive_mask])
|
| 46 |
+
else:
|
| 47 |
+
ret = torch.where(positive_mask, torch.sqrt(x), ret)
|
| 48 |
+
return ret
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def standardize_quaternion(quaternions: torch.Tensor) -> torch.Tensor:
|
| 52 |
+
"""Force the real part of a unit quaternion (xyzw) to be non-negative."""
|
| 53 |
+
return torch.where(quaternions[..., 3:4] < 0, -quaternions, quaternions)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def quat_to_mat(quaternions: torch.Tensor) -> torch.Tensor:
|
| 57 |
+
"""Convert quaternions (xyzw) to (...,3,3) rotation matrices."""
|
| 58 |
+
i, j, k, r = torch.unbind(quaternions, -1)
|
| 59 |
+
two_s = 2.0 / (quaternions * quaternions).sum(-1)
|
| 60 |
+
o = torch.stack(
|
| 61 |
+
(
|
| 62 |
+
1 - two_s * (j * j + k * k),
|
| 63 |
+
two_s * (i * j - k * r),
|
| 64 |
+
two_s * (i * k + j * r),
|
| 65 |
+
two_s * (i * j + k * r),
|
| 66 |
+
1 - two_s * (i * i + k * k),
|
| 67 |
+
two_s * (j * k - i * r),
|
| 68 |
+
two_s * (i * k - j * r),
|
| 69 |
+
two_s * (j * k + i * r),
|
| 70 |
+
1 - two_s * (i * i + j * j),
|
| 71 |
+
),
|
| 72 |
+
-1,
|
| 73 |
+
)
|
| 74 |
+
return o.reshape(quaternions.shape[:-1] + (3, 3))
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def mat_to_quat(matrix: torch.Tensor) -> torch.Tensor:
|
| 78 |
+
"""Convert (...,3,3) rotation matrices to quaternions (xyzw)."""
|
| 79 |
+
if matrix.size(-1) != 3 or matrix.size(-2) != 3:
|
| 80 |
+
raise ValueError(f"Invalid rotation matrix shape {matrix.shape}.")
|
| 81 |
+
|
| 82 |
+
batch_dim = matrix.shape[:-2]
|
| 83 |
+
m00, m01, m02, m10, m11, m12, m20, m21, m22 = torch.unbind(
|
| 84 |
+
matrix.reshape(batch_dim + (9,)), dim=-1
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
q_abs = _sqrt_positive_part(
|
| 88 |
+
torch.stack(
|
| 89 |
+
[
|
| 90 |
+
1.0 + m00 + m11 + m22,
|
| 91 |
+
1.0 + m00 - m11 - m22,
|
| 92 |
+
1.0 - m00 + m11 - m22,
|
| 93 |
+
1.0 - m00 - m11 + m22,
|
| 94 |
+
],
|
| 95 |
+
dim=-1,
|
| 96 |
+
)
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
quat_by_rijk = torch.stack(
|
| 100 |
+
[
|
| 101 |
+
torch.stack([q_abs[..., 0] ** 2, m21 - m12, m02 - m20, m10 - m01], dim=-1),
|
| 102 |
+
torch.stack([m21 - m12, q_abs[..., 1] ** 2, m10 + m01, m02 + m20], dim=-1),
|
| 103 |
+
torch.stack([m02 - m20, m10 + m01, q_abs[..., 2] ** 2, m12 + m21], dim=-1),
|
| 104 |
+
torch.stack([m10 - m01, m20 + m02, m21 + m12, q_abs[..., 3] ** 2], dim=-1),
|
| 105 |
+
],
|
| 106 |
+
dim=-2,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
flr = torch.tensor(0.1).to(dtype=q_abs.dtype, device=q_abs.device)
|
| 110 |
+
quat_candidates = quat_by_rijk / (2.0 * q_abs[..., None].max(flr))
|
| 111 |
+
|
| 112 |
+
out = quat_candidates[F.one_hot(q_abs.argmax(dim=-1), num_classes=4) > 0.5, :].reshape(
|
| 113 |
+
batch_dim + (4,)
|
| 114 |
+
)
|
| 115 |
+
# Reorder rijk -> xyzw (i.e. ijkr).
|
| 116 |
+
out = out[..., [1, 2, 3, 0]]
|
| 117 |
+
return standardize_quaternion(out)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
# -----------------------------------------------------------------------------
|
| 121 |
+
# Pose-encoding <-> extrinsics + intrinsics
|
| 122 |
+
# -----------------------------------------------------------------------------
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def extri_intri_to_pose_encoding(extrinsics: torch.Tensor, intrinsics: torch.Tensor, image_size_hw: Tuple[int, int]) -> torch.Tensor:
|
| 126 |
+
"""Pack (extr, intr, image_size) into the 9-D pose-encoding vector.
|
| 127 |
+
extrinsics: camera-to-world (c2w) (B,S,4,4) matrices,
|
| 128 |
+
intrinsics: pixel-space (B,S,3,3) matrices,
|
| 129 |
+
image_size_hw: is a (H, W) pair.
|
| 130 |
+
"""
|
| 131 |
+
R = extrinsics[..., :3, :3]
|
| 132 |
+
T = extrinsics[..., :3, 3]
|
| 133 |
+
quat = mat_to_quat(R)
|
| 134 |
+
H, W = image_size_hw
|
| 135 |
+
fov_h = 2 * torch.atan((H / 2) / intrinsics[..., 1, 1])
|
| 136 |
+
fov_w = 2 * torch.atan((W / 2) / intrinsics[..., 0, 0])
|
| 137 |
+
return torch.cat([T, quat, fov_h[..., None], fov_w[..., None]], dim=-1).float()
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def pose_encoding_to_extri_intri(pose_encoding: torch.Tensor, image_size_hw: Tuple[int, int]) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 141 |
+
"""Inverse of extri_intri_to_pose_encoding."""
|
| 142 |
+
T = pose_encoding[..., :3]
|
| 143 |
+
quat = pose_encoding[..., 3:7]
|
| 144 |
+
fov_h = pose_encoding[..., 7]
|
| 145 |
+
fov_w = pose_encoding[..., 8]
|
| 146 |
+
# Normalize to unit quaternion. CameraDec outputs raw values; a near-zero
|
| 147 |
+
# quaternion causes two_s = 2/norm² → inf in quat_to_mat → NaN extrinsics.
|
| 148 |
+
quat = quat / quat.norm(dim=-1, keepdim=True).clamp(min=1e-6)
|
| 149 |
+
R = quat_to_mat(quat)
|
| 150 |
+
extrinsics = torch.cat([R, T[..., None]], dim=-1)
|
| 151 |
+
H, W = image_size_hw
|
| 152 |
+
fy = (H / 2.0) / torch.clamp(torch.tan(fov_h / 2.0), 1e-6)
|
| 153 |
+
fx = (W / 2.0) / torch.clamp(torch.tan(fov_w / 2.0), 1e-6)
|
| 154 |
+
intrinsics = torch.zeros(pose_encoding.shape[:2] + (3, 3), device=pose_encoding.device, dtype=pose_encoding.dtype)
|
| 155 |
+
intrinsics[..., 0, 0] = fx
|
| 156 |
+
intrinsics[..., 1, 1] = fy
|
| 157 |
+
intrinsics[..., 0, 2] = W / 2
|
| 158 |
+
intrinsics[..., 1, 2] = H / 2
|
| 159 |
+
intrinsics[..., 2, 2] = 1.0
|
| 160 |
+
return extrinsics, intrinsics
|
ComfyUI/comfy/ldm/ernie/model.py
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
|
| 6 |
+
from comfy.ldm.modules.attention import optimized_attention
|
| 7 |
+
import comfy.model_management
|
| 8 |
+
import comfy.ops
|
| 9 |
+
import comfy.quant_ops
|
| 10 |
+
|
| 11 |
+
def rope(pos: torch.Tensor, dim: int, theta: int) -> torch.Tensor:
|
| 12 |
+
assert dim % 2 == 0
|
| 13 |
+
if not comfy.model_management.supports_fp64(pos.device):
|
| 14 |
+
device = torch.device("cpu")
|
| 15 |
+
else:
|
| 16 |
+
device = pos.device
|
| 17 |
+
|
| 18 |
+
scale = torch.arange(0, dim, 2, dtype=torch.float64, device=device) / dim
|
| 19 |
+
omega = 1.0 / (theta**scale)
|
| 20 |
+
out = torch.einsum("...n,d->...nd", pos.to(device), omega)
|
| 21 |
+
out = torch.stack([torch.cos(out), torch.sin(out)], dim=0)
|
| 22 |
+
return out.to(dtype=torch.float32, device=pos.device)
|
| 23 |
+
|
| 24 |
+
class ErnieImageEmbedND3(nn.Module):
|
| 25 |
+
def __init__(self, dim: int, theta: int, axes_dim: tuple):
|
| 26 |
+
super().__init__()
|
| 27 |
+
self.dim = dim
|
| 28 |
+
self.theta = theta
|
| 29 |
+
self.axes_dim = list(axes_dim)
|
| 30 |
+
|
| 31 |
+
def forward(self, ids: torch.Tensor) -> torch.Tensor:
|
| 32 |
+
emb = torch.cat([rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(3)], dim=-1)
|
| 33 |
+
cos_ = emb[0]
|
| 34 |
+
sin_ = emb[1]
|
| 35 |
+
N = cos_.shape[-1]
|
| 36 |
+
half = N // 2
|
| 37 |
+
cos_top = cos_[..., :half].repeat_interleave(2, dim=-1)
|
| 38 |
+
sin_top = sin_[..., :half].repeat_interleave(2, dim=-1)
|
| 39 |
+
cos_bot = cos_[..., half:].repeat_interleave(2, dim=-1)
|
| 40 |
+
sin_bot = sin_[..., half:].repeat_interleave(2, dim=-1)
|
| 41 |
+
rot = torch.stack([cos_top, -sin_top, sin_bot, cos_bot], dim=-1)
|
| 42 |
+
return rot.reshape(*rot.shape[:-1], 2, 2).unsqueeze(2)
|
| 43 |
+
|
| 44 |
+
class ErnieImagePatchEmbedDynamic(nn.Module):
|
| 45 |
+
def __init__(self, in_channels: int, embed_dim: int, patch_size: int, operations, device=None, dtype=None):
|
| 46 |
+
super().__init__()
|
| 47 |
+
self.patch_size = patch_size
|
| 48 |
+
self.proj = operations.Conv2d(in_channels, embed_dim, kernel_size=patch_size, stride=patch_size, bias=True, device=device, dtype=dtype)
|
| 49 |
+
|
| 50 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 51 |
+
x = self.proj(x)
|
| 52 |
+
batch_size, dim, height, width = x.shape
|
| 53 |
+
return x.reshape(batch_size, dim, height * width).transpose(1, 2).contiguous()
|
| 54 |
+
|
| 55 |
+
class Timesteps(nn.Module):
|
| 56 |
+
def __init__(self, num_channels: int, flip_sin_to_cos: bool = False):
|
| 57 |
+
super().__init__()
|
| 58 |
+
self.num_channels = num_channels
|
| 59 |
+
self.flip_sin_to_cos = flip_sin_to_cos
|
| 60 |
+
|
| 61 |
+
def forward(self, timesteps: torch.Tensor) -> torch.Tensor:
|
| 62 |
+
half_dim = self.num_channels // 2
|
| 63 |
+
exponent = -math.log(10000) * torch.arange(half_dim, dtype=torch.float32, device=timesteps.device) / half_dim
|
| 64 |
+
emb = torch.exp(exponent)
|
| 65 |
+
emb = timesteps[:, None].float() * emb[None, :]
|
| 66 |
+
if self.flip_sin_to_cos:
|
| 67 |
+
emb = torch.cat([torch.cos(emb), torch.sin(emb)], dim=-1)
|
| 68 |
+
else:
|
| 69 |
+
emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
|
| 70 |
+
return emb
|
| 71 |
+
|
| 72 |
+
class TimestepEmbedding(nn.Module):
|
| 73 |
+
def __init__(self, in_channels: int, time_embed_dim: int, operations, device=None, dtype=None):
|
| 74 |
+
super().__init__()
|
| 75 |
+
Linear = operations.Linear
|
| 76 |
+
self.linear_1 = Linear(in_channels, time_embed_dim, bias=True, device=device, dtype=dtype)
|
| 77 |
+
self.act = nn.SiLU()
|
| 78 |
+
self.linear_2 = Linear(time_embed_dim, time_embed_dim, bias=True, device=device, dtype=dtype)
|
| 79 |
+
|
| 80 |
+
def forward(self, sample: torch.Tensor) -> torch.Tensor:
|
| 81 |
+
sample = self.linear_1(sample)
|
| 82 |
+
sample = self.act(sample)
|
| 83 |
+
sample = self.linear_2(sample)
|
| 84 |
+
return sample
|
| 85 |
+
|
| 86 |
+
class ErnieImageAttention(nn.Module):
|
| 87 |
+
def __init__(self, query_dim: int, heads: int, dim_head: int, eps: float = 1e-6, operations=None, device=None, dtype=None):
|
| 88 |
+
super().__init__()
|
| 89 |
+
self.heads = heads
|
| 90 |
+
self.head_dim = dim_head
|
| 91 |
+
self.inner_dim = heads * dim_head
|
| 92 |
+
|
| 93 |
+
Linear = operations.Linear
|
| 94 |
+
RMSNorm = operations.RMSNorm
|
| 95 |
+
|
| 96 |
+
self.to_q = Linear(query_dim, self.inner_dim, bias=False, device=device, dtype=dtype)
|
| 97 |
+
self.to_k = Linear(query_dim, self.inner_dim, bias=False, device=device, dtype=dtype)
|
| 98 |
+
self.to_v = Linear(query_dim, self.inner_dim, bias=False, device=device, dtype=dtype)
|
| 99 |
+
|
| 100 |
+
self.norm_q = RMSNorm(dim_head, eps=eps, elementwise_affine=True, device=device, dtype=dtype)
|
| 101 |
+
self.norm_k = RMSNorm(dim_head, eps=eps, elementwise_affine=True, device=device, dtype=dtype)
|
| 102 |
+
|
| 103 |
+
self.to_out = nn.ModuleList([Linear(self.inner_dim, query_dim, bias=False, device=device, dtype=dtype)])
|
| 104 |
+
|
| 105 |
+
def forward(self, x: torch.Tensor, attention_mask: torch.Tensor = None, image_rotary_emb: torch.Tensor = None) -> torch.Tensor:
|
| 106 |
+
B, S, _ = x.shape
|
| 107 |
+
|
| 108 |
+
q_flat = self.to_q(x)
|
| 109 |
+
k_flat = self.to_k(x)
|
| 110 |
+
v_flat = self.to_v(x)
|
| 111 |
+
|
| 112 |
+
query = q_flat.view(B, S, self.heads, self.head_dim)
|
| 113 |
+
key = k_flat.view(B, S, self.heads, self.head_dim)
|
| 114 |
+
|
| 115 |
+
if image_rotary_emb is not None and not comfy.model_management.in_training:
|
| 116 |
+
q_scale, _, q_offload_stream = comfy.ops.cast_bias_weight(self.norm_q, query, offloadable=True)
|
| 117 |
+
k_scale, _, k_offload_stream = comfy.ops.cast_bias_weight(self.norm_k, key, offloadable=True)
|
| 118 |
+
query, key = comfy.quant_ops.ck.rms_rope_split_half(query, key, image_rotary_emb, q_scale, k_scale, self.norm_q.eps)
|
| 119 |
+
comfy.ops.uncast_bias_weight(self.norm_q, q_scale, None, q_offload_stream)
|
| 120 |
+
comfy.ops.uncast_bias_weight(self.norm_k, k_scale, None, k_offload_stream)
|
| 121 |
+
else:
|
| 122 |
+
query = self.norm_q(query)
|
| 123 |
+
key = self.norm_k(key)
|
| 124 |
+
if image_rotary_emb is not None:
|
| 125 |
+
query, key = comfy.quant_ops.ck.apply_rope_split_half(query, key, image_rotary_emb)
|
| 126 |
+
|
| 127 |
+
q_flat = query.reshape(B, S, -1)
|
| 128 |
+
k_flat = key.reshape(B, S, -1)
|
| 129 |
+
|
| 130 |
+
hidden_states = optimized_attention(q_flat, k_flat, v_flat, self.heads, mask=attention_mask)
|
| 131 |
+
|
| 132 |
+
return self.to_out[0](hidden_states)
|
| 133 |
+
|
| 134 |
+
class ErnieImageFeedForward(nn.Module):
|
| 135 |
+
def __init__(self, hidden_size: int, ffn_hidden_size: int, operations, device=None, dtype=None):
|
| 136 |
+
super().__init__()
|
| 137 |
+
Linear = operations.Linear
|
| 138 |
+
self.gate_proj = Linear(hidden_size, ffn_hidden_size, bias=False, device=device, dtype=dtype)
|
| 139 |
+
self.up_proj = Linear(hidden_size, ffn_hidden_size, bias=False, device=device, dtype=dtype)
|
| 140 |
+
self.linear_fc2 = Linear(ffn_hidden_size, hidden_size, bias=False, device=device, dtype=dtype)
|
| 141 |
+
|
| 142 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 143 |
+
return self.linear_fc2(self.up_proj(x) * F.gelu(self.gate_proj(x)))
|
| 144 |
+
|
| 145 |
+
class ErnieImageSharedAdaLNBlock(nn.Module):
|
| 146 |
+
def __init__(self, hidden_size: int, num_heads: int, ffn_hidden_size: int, eps: float = 1e-6, operations=None, device=None, dtype=None):
|
| 147 |
+
super().__init__()
|
| 148 |
+
RMSNorm = operations.RMSNorm
|
| 149 |
+
|
| 150 |
+
self.adaLN_sa_ln = RMSNorm(hidden_size, eps=eps, device=device, dtype=dtype)
|
| 151 |
+
self.self_attention = ErnieImageAttention(
|
| 152 |
+
query_dim=hidden_size,
|
| 153 |
+
dim_head=hidden_size // num_heads,
|
| 154 |
+
heads=num_heads,
|
| 155 |
+
eps=eps,
|
| 156 |
+
operations=operations,
|
| 157 |
+
device=device,
|
| 158 |
+
dtype=dtype
|
| 159 |
+
)
|
| 160 |
+
self.adaLN_mlp_ln = RMSNorm(hidden_size, eps=eps, device=device, dtype=dtype)
|
| 161 |
+
self.mlp = ErnieImageFeedForward(hidden_size, ffn_hidden_size, operations=operations, device=device, dtype=dtype)
|
| 162 |
+
|
| 163 |
+
def forward(self, x, rotary_pos_emb, temb, attention_mask=None):
|
| 164 |
+
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = temb
|
| 165 |
+
|
| 166 |
+
residual = x
|
| 167 |
+
x_norm = self.adaLN_sa_ln(x)
|
| 168 |
+
x_norm = x_norm * (1 + scale_msa) + shift_msa
|
| 169 |
+
|
| 170 |
+
attn_out = self.self_attention(x_norm, attention_mask=attention_mask, image_rotary_emb=rotary_pos_emb)
|
| 171 |
+
x = residual + gate_msa * attn_out
|
| 172 |
+
|
| 173 |
+
residual = x
|
| 174 |
+
x_norm = self.adaLN_mlp_ln(x)
|
| 175 |
+
x_norm = x_norm * (1 + scale_mlp) + shift_mlp
|
| 176 |
+
|
| 177 |
+
return residual + gate_mlp * self.mlp(x_norm)
|
| 178 |
+
|
| 179 |
+
class ErnieImageAdaLNContinuous(nn.Module):
|
| 180 |
+
def __init__(self, hidden_size: int, eps: float = 1e-6, operations=None, device=None, dtype=None):
|
| 181 |
+
super().__init__()
|
| 182 |
+
LayerNorm = operations.LayerNorm
|
| 183 |
+
Linear = operations.Linear
|
| 184 |
+
self.norm = LayerNorm(hidden_size, elementwise_affine=False, eps=eps, device=device, dtype=dtype)
|
| 185 |
+
self.linear = Linear(hidden_size, hidden_size * 2, device=device, dtype=dtype)
|
| 186 |
+
|
| 187 |
+
def forward(self, x: torch.Tensor, conditioning: torch.Tensor) -> torch.Tensor:
|
| 188 |
+
scale, shift = self.linear(conditioning).chunk(2, dim=-1)
|
| 189 |
+
x = self.norm(x)
|
| 190 |
+
x = torch.addcmul(shift.unsqueeze(1), x, 1 + scale.unsqueeze(1))
|
| 191 |
+
return x
|
| 192 |
+
|
| 193 |
+
class ErnieImageModel(nn.Module):
|
| 194 |
+
def __init__(
|
| 195 |
+
self,
|
| 196 |
+
hidden_size: int = 4096,
|
| 197 |
+
num_attention_heads: int = 32,
|
| 198 |
+
num_layers: int = 36,
|
| 199 |
+
ffn_hidden_size: int = 12288,
|
| 200 |
+
in_channels: int = 128,
|
| 201 |
+
out_channels: int = 128,
|
| 202 |
+
patch_size: int = 1,
|
| 203 |
+
text_in_dim: int = 3072,
|
| 204 |
+
rope_theta: int = 256,
|
| 205 |
+
rope_axes_dim: tuple = (32, 48, 48),
|
| 206 |
+
eps: float = 1e-6,
|
| 207 |
+
qk_layernorm: bool = True,
|
| 208 |
+
device=None,
|
| 209 |
+
dtype=None,
|
| 210 |
+
operations=None,
|
| 211 |
+
**kwargs
|
| 212 |
+
):
|
| 213 |
+
super().__init__()
|
| 214 |
+
self.dtype = dtype
|
| 215 |
+
self.hidden_size = hidden_size
|
| 216 |
+
self.num_heads = num_attention_heads
|
| 217 |
+
self.head_dim = hidden_size // num_attention_heads
|
| 218 |
+
self.patch_size = patch_size
|
| 219 |
+
self.out_channels = out_channels
|
| 220 |
+
|
| 221 |
+
Linear = operations.Linear
|
| 222 |
+
|
| 223 |
+
self.x_embedder = ErnieImagePatchEmbedDynamic(in_channels, hidden_size, patch_size, operations, device, dtype)
|
| 224 |
+
self.text_proj = Linear(text_in_dim, hidden_size, bias=False, device=device, dtype=dtype) if text_in_dim != hidden_size else None
|
| 225 |
+
|
| 226 |
+
self.time_proj = Timesteps(hidden_size, flip_sin_to_cos=False)
|
| 227 |
+
self.time_embedding = TimestepEmbedding(hidden_size, hidden_size, operations, device, dtype)
|
| 228 |
+
|
| 229 |
+
self.pos_embed = ErnieImageEmbedND3(dim=self.head_dim, theta=rope_theta, axes_dim=rope_axes_dim)
|
| 230 |
+
|
| 231 |
+
self.adaLN_modulation = nn.Sequential(
|
| 232 |
+
nn.SiLU(),
|
| 233 |
+
Linear(hidden_size, 6 * hidden_size, device=device, dtype=dtype)
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
self.layers = nn.ModuleList([
|
| 237 |
+
ErnieImageSharedAdaLNBlock(hidden_size, num_attention_heads, ffn_hidden_size, eps, operations, device, dtype)
|
| 238 |
+
for _ in range(num_layers)
|
| 239 |
+
])
|
| 240 |
+
|
| 241 |
+
self.final_norm = ErnieImageAdaLNContinuous(hidden_size, eps, operations, device, dtype)
|
| 242 |
+
self.final_linear = Linear(hidden_size, patch_size * patch_size * out_channels, device=device, dtype=dtype)
|
| 243 |
+
|
| 244 |
+
def forward(self, x, timesteps, context, **kwargs):
|
| 245 |
+
device, dtype = x.device, x.dtype
|
| 246 |
+
B, C, H, W = x.shape
|
| 247 |
+
p, Hp, Wp = self.patch_size, H // self.patch_size, W // self.patch_size
|
| 248 |
+
N_img = Hp * Wp
|
| 249 |
+
|
| 250 |
+
img_bsh = self.x_embedder(x)
|
| 251 |
+
|
| 252 |
+
text_bth = context
|
| 253 |
+
if self.text_proj is not None and text_bth.numel() > 0:
|
| 254 |
+
text_bth = self.text_proj(text_bth)
|
| 255 |
+
Tmax = text_bth.shape[1]
|
| 256 |
+
|
| 257 |
+
hidden_states = torch.cat([img_bsh, text_bth], dim=1)
|
| 258 |
+
|
| 259 |
+
text_ids = torch.zeros((B, Tmax, 3), device=device, dtype=torch.float32)
|
| 260 |
+
text_ids[:, :, 0] = torch.linspace(0, Tmax - 1, steps=Tmax, device=x.device, dtype=torch.float32)
|
| 261 |
+
index = float(Tmax)
|
| 262 |
+
|
| 263 |
+
transformer_options = kwargs.get("transformer_options", {})
|
| 264 |
+
rope_options = transformer_options.get("rope_options", None)
|
| 265 |
+
|
| 266 |
+
h_len, w_len = float(Hp), float(Wp)
|
| 267 |
+
h_offset, w_offset = 0.0, 0.0
|
| 268 |
+
|
| 269 |
+
if rope_options is not None:
|
| 270 |
+
h_len = (h_len - 1.0) * rope_options.get("scale_y", 1.0) + 1.0
|
| 271 |
+
w_len = (w_len - 1.0) * rope_options.get("scale_x", 1.0) + 1.0
|
| 272 |
+
index += rope_options.get("shift_t", 0.0)
|
| 273 |
+
h_offset += rope_options.get("shift_y", 0.0)
|
| 274 |
+
w_offset += rope_options.get("shift_x", 0.0)
|
| 275 |
+
|
| 276 |
+
image_ids = torch.zeros((Hp, Wp, 3), device=device, dtype=torch.float32)
|
| 277 |
+
image_ids[:, :, 0] = image_ids[:, :, 1] + index
|
| 278 |
+
image_ids[:, :, 1] = image_ids[:, :, 1] + torch.linspace(h_offset, h_len - 1 + h_offset, steps=Hp, device=device, dtype=torch.float32).unsqueeze(1)
|
| 279 |
+
image_ids[:, :, 2] = image_ids[:, :, 2] + torch.linspace(w_offset, w_len - 1 + w_offset, steps=Wp, device=device, dtype=torch.float32).unsqueeze(0)
|
| 280 |
+
|
| 281 |
+
image_ids = image_ids.view(1, N_img, 3).expand(B, -1, -1)
|
| 282 |
+
|
| 283 |
+
rotary_pos_emb = self.pos_embed(torch.cat([image_ids, text_ids], dim=1))
|
| 284 |
+
del image_ids, text_ids
|
| 285 |
+
|
| 286 |
+
sample = self.time_proj(timesteps).to(dtype)
|
| 287 |
+
c = self.time_embedding(sample)
|
| 288 |
+
|
| 289 |
+
shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = [
|
| 290 |
+
t.unsqueeze(1).contiguous() for t in self.adaLN_modulation(c).chunk(6, dim=-1)
|
| 291 |
+
]
|
| 292 |
+
|
| 293 |
+
temb = [shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp]
|
| 294 |
+
for layer in self.layers:
|
| 295 |
+
hidden_states = layer(hidden_states, rotary_pos_emb, temb)
|
| 296 |
+
|
| 297 |
+
hidden_states = self.final_norm(hidden_states, c).type_as(hidden_states)
|
| 298 |
+
|
| 299 |
+
patches = self.final_linear(hidden_states)[:, :N_img, :]
|
| 300 |
+
output = (
|
| 301 |
+
patches.view(B, Hp, Wp, p, p, self.out_channels)
|
| 302 |
+
.permute(0, 5, 1, 3, 2, 4)
|
| 303 |
+
.contiguous()
|
| 304 |
+
.view(B, self.out_channels, H, W)
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
return output
|
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/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/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/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/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/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/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
|