| import torch
|
| from torch import nn
|
| from torch.nn import functional as F
|
| from einops import rearrange
|
|
|
| from comfy.ldm.modules.attention import optimized_attention
|
| from comfy.ldm.modules.diffusionmodules.mmdit import (RMSNorm, JointBlock,)
|
|
|
|
|
| class AdaLayerNorm(nn.Module):
|
| """
|
| Norm layer adaptive layer norm zero (adaLN-Zero).
|
|
|
| Parameters:
|
| embedding_dim (`int`): The size of each embedding vector.
|
| num_embeddings (`int`): The size of the embeddings dictionary.
|
| """
|
|
|
| def __init__(self, embedding_dim: int, time_embedding_dim=None, mode="normal"):
|
| super().__init__()
|
|
|
| self.silu = nn.SiLU()
|
| num_params_dict = dict(
|
| zero=6,
|
| normal=2,
|
| )
|
| num_params = num_params_dict[mode]
|
| self.linear = nn.Linear(
|
| time_embedding_dim or embedding_dim, num_params * embedding_dim, bias=True
|
| )
|
| self.norm = nn.LayerNorm(embedding_dim, elementwise_affine=False, eps=1e-6)
|
| self.mode = mode
|
|
|
| def forward(
|
| self,
|
| x,
|
| hidden_dtype=None,
|
| emb=None,
|
| ):
|
| emb = self.linear(self.silu(emb))
|
| if self.mode == "normal":
|
| shift_msa, scale_msa = emb.chunk(2, dim=1)
|
| x = self.norm(x) * (1 + scale_msa[:, None]) + shift_msa[:, None]
|
| return x
|
|
|
| elif self.mode == "zero":
|
| shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = emb.chunk(
|
| 6, dim=1
|
| )
|
| x = self.norm(x) * (1 + scale_msa[:, None]) + shift_msa[:, None]
|
| return x, gate_msa, shift_mlp, scale_mlp, gate_mlp
|
|
|
|
|
| class IPAttnProcessor(nn.Module):
|
|
|
| def __init__(
|
| self,
|
| hidden_size=None,
|
| cross_attention_dim=None,
|
| ip_hidden_states_dim=None,
|
| ip_encoder_hidden_states_dim=None,
|
| head_dim=None,
|
| timesteps_emb_dim=1280,
|
| ):
|
| super().__init__()
|
|
|
| self.norm_ip = AdaLayerNorm(
|
| ip_hidden_states_dim, time_embedding_dim=timesteps_emb_dim
|
| )
|
| self.to_k_ip = nn.Linear(ip_hidden_states_dim, hidden_size, bias=False)
|
| self.to_v_ip = nn.Linear(ip_hidden_states_dim, hidden_size, bias=False)
|
| self.norm_q = RMSNorm(head_dim, 1e-6)
|
| self.norm_k = RMSNorm(head_dim, 1e-6)
|
| self.norm_ip_k = RMSNorm(head_dim, 1e-6)
|
|
|
| def forward(
|
| self,
|
| ip_hidden_states,
|
| img_query,
|
| img_key=None,
|
| img_value=None,
|
| t_emb=None,
|
| n_heads=1,
|
| ):
|
| if ip_hidden_states is None:
|
| return None
|
|
|
| if not hasattr(self, "to_k_ip") or not hasattr(self, "to_v_ip"):
|
| return None
|
|
|
|
|
| norm_ip_hidden_states = self.norm_ip(ip_hidden_states, emb=t_emb)
|
|
|
|
|
| ip_key = self.to_k_ip(norm_ip_hidden_states)
|
| ip_value = self.to_v_ip(norm_ip_hidden_states)
|
|
|
|
|
| img_query = rearrange(img_query, "b l (h d) -> b h l d", h=n_heads)
|
| img_key = rearrange(img_key, "b l (h d) -> b h l d", h=n_heads)
|
|
|
|
|
|
|
| img_value = torch.transpose(img_value, 1, 2)
|
| ip_key = rearrange(ip_key, "b l (h d) -> b h l d", h=n_heads)
|
| ip_value = rearrange(ip_value, "b l (h d) -> b h l d", h=n_heads)
|
|
|
|
|
| img_query = self.norm_q(img_query)
|
| img_key = self.norm_k(img_key)
|
| ip_key = self.norm_ip_k(ip_key)
|
|
|
|
|
| key = torch.cat([img_key, ip_key], dim=2)
|
| value = torch.cat([img_value, ip_value], dim=2)
|
|
|
|
|
| ip_hidden_states = F.scaled_dot_product_attention(
|
| img_query, key, value, dropout_p=0.0, is_causal=False
|
| )
|
| ip_hidden_states = rearrange(ip_hidden_states, "b h l d -> b l (h d)")
|
| ip_hidden_states = ip_hidden_states.to(img_query.dtype)
|
| return ip_hidden_states
|
|
|
|
|
| class JointBlockIPWrapper:
|
| """To be used as a patch_replace with Comfy"""
|
|
|
| def __init__(
|
| self,
|
| original_block: JointBlock,
|
| adapter: IPAttnProcessor,
|
| ip_options=None,
|
| ):
|
| self.original_block = original_block
|
| self.adapter = adapter
|
| if ip_options is None:
|
| ip_options = {}
|
| self.ip_options = ip_options
|
|
|
| def block_mixing(self, context, x, context_block, x_block, c):
|
| """
|
| Comes from mmdit.py. Modified to add ipadapter attention.
|
| """
|
| context_qkv, context_intermediates = context_block.pre_attention(context, c)
|
|
|
| if x_block.x_block_self_attn:
|
| x_qkv, x_qkv2, x_intermediates = x_block.pre_attention_x(x, c)
|
| else:
|
| x_qkv, x_intermediates = x_block.pre_attention(x, c)
|
|
|
| qkv = tuple(torch.cat((context_qkv[j], x_qkv[j]), dim=1) for j in range(3))
|
|
|
| attn = optimized_attention(
|
| qkv[0],
|
| qkv[1],
|
| qkv[2],
|
| heads=x_block.attn.num_heads,
|
| )
|
| context_attn, x_attn = (
|
| attn[:, : context_qkv[0].shape[1]],
|
| attn[:, context_qkv[0].shape[1] :],
|
| )
|
|
|
|
|
| if (
|
| self.ip_options["hidden_states"] is not None
|
| and self.ip_options["t_emb"] is not None
|
| ):
|
|
|
| ip_attn = self.adapter(
|
| self.ip_options["hidden_states"],
|
| *x_qkv,
|
| self.ip_options["t_emb"],
|
| x_block.attn.num_heads,
|
| )
|
| x_attn = x_attn + ip_attn * self.ip_options["weight"]
|
|
|
|
|
| if not context_block.pre_only:
|
| context = context_block.post_attention(context_attn, *context_intermediates)
|
|
|
| else:
|
| context = None
|
| if x_block.x_block_self_attn:
|
| attn2 = optimized_attention(
|
| x_qkv2[0],
|
| x_qkv2[1],
|
| x_qkv2[2],
|
| heads=x_block.attn2.num_heads,
|
| )
|
| x = x_block.post_attention_x(x_attn, attn2, *x_intermediates)
|
| else:
|
| x = x_block.post_attention(x_attn, *x_intermediates)
|
| return context, x
|
|
|
| def __call__(self, args, _):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| c, x = self.block_mixing(
|
| args["txt"],
|
| args["img"],
|
| self.original_block.context_block,
|
| self.original_block.x_block,
|
| c=args["vec"],
|
| )
|
| return {"txt": c, "img": x} |