from .attention import Attention from einops import repeat, rearrange import math import torch class HunyuanDiTRotaryEmbedding(torch.nn.Module): def __init__(self, q_norm_shape=88, k_norm_shape=88, rotary_emb_on_k=True): super().__init__() self.q_norm = torch.nn.LayerNorm((q_norm_shape,), elementwise_affine=True, eps=1e-06) self.k_norm = torch.nn.LayerNorm((k_norm_shape,), elementwise_affine=True, eps=1e-06) self.rotary_emb_on_k = rotary_emb_on_k self.k_cache, self.v_cache = [], [] def reshape_for_broadcast(self, freqs_cis, x): ndim = x.ndim shape = [d if i == ndim - 2 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)] return freqs_cis[0].view(*shape), freqs_cis[1].view(*shape) def rotate_half(self, x): x_real, x_imag = x.float().reshape(*x.shape[:-1], -1, 2).unbind(-1) return torch.stack([-x_imag, x_real], dim=-1).flatten(3) def apply_rotary_emb(self, xq, xk, freqs_cis): xk_out = None cos, sin = self.reshape_for_broadcast(freqs_cis, xq) cos, sin = cos.to(xq.device), sin.to(xq.device) xq_out = (xq.float() * cos + self.rotate_half(xq.float()) * sin).type_as(xq) if xk is not None: xk_out = (xk.float() * cos + self.rotate_half(xk.float()) * sin).type_as(xk) return xq_out, xk_out def forward(self, q, k, v, freqs_cis_img, to_cache=False): # norm q = self.q_norm(q) k = self.k_norm(k) # RoPE if self.rotary_emb_on_k: q, k = self.apply_rotary_emb(q, k, freqs_cis_img) else: q, _ = self.apply_rotary_emb(q, None, freqs_cis_img) if to_cache: self.k_cache.append(k) self.v_cache.append(v) elif len(self.k_cache) > 0 and len(self.v_cache) > 0: k = torch.concat([k] + self.k_cache, dim=2) v = torch.concat([v] + self.v_cache, dim=2) self.k_cache, self.v_cache = [], [] return q, k, v class FP32_Layernorm(torch.nn.LayerNorm): def forward(self, inputs): origin_dtype = inputs.dtype return torch.nn.functional.layer_norm(inputs.float(), self.normalized_shape, self.weight.float(), self.bias.float(), self.eps).to(origin_dtype) class FP32_SiLU(torch.nn.SiLU): def forward(self, inputs): origin_dtype = inputs.dtype return torch.nn.functional.silu(inputs.float(), inplace=False).to(origin_dtype) class HunyuanDiTFinalLayer(torch.nn.Module): def __init__(self, final_hidden_size=1408, condition_dim=1408, patch_size=2, out_channels=8): super().__init__() self.norm_final = torch.nn.LayerNorm(final_hidden_size, elementwise_affine=False, eps=1e-6) self.linear = torch.nn.Linear(final_hidden_size, patch_size * patch_size * out_channels, bias=True) self.adaLN_modulation = torch.nn.Sequential( FP32_SiLU(), torch.nn.Linear(condition_dim, 2 * final_hidden_size, bias=True) ) def modulate(self, x, shift, scale): return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) def forward(self, hidden_states, condition_emb): shift, scale = self.adaLN_modulation(condition_emb).chunk(2, dim=1) hidden_states = self.modulate(self.norm_final(hidden_states), shift, scale) hidden_states = self.linear(hidden_states) return hidden_states class HunyuanDiTBlock(torch.nn.Module): def __init__( self, hidden_dim=1408, condition_dim=1408, num_heads=16, mlp_ratio=4.3637, text_dim=1024, skip_connection=False ): super().__init__() self.norm1 = FP32_Layernorm((hidden_dim,), eps=1e-6, elementwise_affine=True) self.rota1 = HunyuanDiTRotaryEmbedding(hidden_dim//num_heads, hidden_dim//num_heads) self.attn1 = Attention(hidden_dim, num_heads, hidden_dim//num_heads, bias_q=True, bias_kv=True, bias_out=True) self.norm2 = FP32_Layernorm((hidden_dim,), eps=1e-6, elementwise_affine=True) self.rota2 = HunyuanDiTRotaryEmbedding(hidden_dim//num_heads, hidden_dim//num_heads, rotary_emb_on_k=False) self.attn2 = Attention(hidden_dim, num_heads, hidden_dim//num_heads, kv_dim=text_dim, bias_q=True, bias_kv=True, bias_out=True) self.norm3 = FP32_Layernorm((hidden_dim,), eps=1e-6, elementwise_affine=True) self.modulation = torch.nn.Sequential(FP32_SiLU(), torch.nn.Linear(condition_dim, hidden_dim, bias=True)) self.mlp = torch.nn.Sequential( torch.nn.Linear(hidden_dim, int(hidden_dim*mlp_ratio), bias=True), torch.nn.GELU(approximate="tanh"), torch.nn.Linear(int(hidden_dim*mlp_ratio), hidden_dim, bias=True) ) if skip_connection: self.skip_norm = FP32_Layernorm((hidden_dim * 2,), eps=1e-6, elementwise_affine=True) self.skip_linear = torch.nn.Linear(hidden_dim * 2, hidden_dim, bias=True) else: self.skip_norm, self.skip_linear = None, None def forward(self, hidden_states, condition_emb, text_emb, freq_cis_img, residual=None, to_cache=False): # Long Skip Connection if self.skip_norm is not None and self.skip_linear is not None: hidden_states = torch.cat([hidden_states, residual], dim=-1) hidden_states = self.skip_norm(hidden_states) hidden_states = self.skip_linear(hidden_states) # Self-Attention shift_msa = self.modulation(condition_emb).unsqueeze(dim=1) attn_input = self.norm1(hidden_states) + shift_msa hidden_states = hidden_states + self.attn1(attn_input, qkv_preprocessor=lambda q, k, v: self.rota1(q, k, v, freq_cis_img, to_cache=to_cache)) # Cross-Attention attn_input = self.norm3(hidden_states) hidden_states = hidden_states + self.attn2(attn_input, text_emb, qkv_preprocessor=lambda q, k, v: self.rota2(q, k, v, freq_cis_img)) # FFN Layer mlp_input = self.norm2(hidden_states) hidden_states = hidden_states + self.mlp(mlp_input) return hidden_states class AttentionPool(torch.nn.Module): def __init__(self, spacial_dim, embed_dim, num_heads, output_dim = None): super().__init__() self.positional_embedding = torch.nn.Parameter(torch.randn(spacial_dim + 1, embed_dim) / embed_dim ** 0.5) self.k_proj = torch.nn.Linear(embed_dim, embed_dim) self.q_proj = torch.nn.Linear(embed_dim, embed_dim) self.v_proj = torch.nn.Linear(embed_dim, embed_dim) self.c_proj = torch.nn.Linear(embed_dim, output_dim or embed_dim) self.num_heads = num_heads def forward(self, x): x = x.permute(1, 0, 2) # NLC -> LNC x = torch.cat([x.mean(dim=0, keepdim=True), x], dim=0) # (L+1)NC x = x + self.positional_embedding[:, None, :].to(x.dtype) # (L+1)NC x, _ = torch.nn.functional.multi_head_attention_forward( query=x[:1], key=x, value=x, embed_dim_to_check=x.shape[-1], num_heads=self.num_heads, q_proj_weight=self.q_proj.weight, k_proj_weight=self.k_proj.weight, v_proj_weight=self.v_proj.weight, in_proj_weight=None, in_proj_bias=torch.cat([self.q_proj.bias, self.k_proj.bias, self.v_proj.bias]), bias_k=None, bias_v=None, add_zero_attn=False, dropout_p=0, out_proj_weight=self.c_proj.weight, out_proj_bias=self.c_proj.bias, use_separate_proj_weight=True, training=self.training, need_weights=False ) return x.squeeze(0) class PatchEmbed(torch.nn.Module): def __init__( self, patch_size=(2, 2), in_chans=4, embed_dim=1408, bias=True, ): super().__init__() self.proj = torch.nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size, bias=bias) def forward(self, x): x = self.proj(x) x = x.flatten(2).transpose(1, 2) # BCHW -> BNC return x def timestep_embedding(t, dim, max_period=10000, repeat_only=False): # https://github.com/openai/glide-text2im/blob/main/glide_text2im/nn.py if not repeat_only: half = dim // 2 freqs = torch.exp( -math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half ).to(device=t.device) # size: [dim/2], 一个指数衰减的曲线 args = t[:, None].float() * freqs[None] embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) if dim % 2: embedding = torch.cat( [embedding, torch.zeros_like(embedding[:, :1])], dim=-1 ) else: embedding = repeat(t, "b -> b d", d=dim) return embedding class TimestepEmbedder(torch.nn.Module): def __init__(self, hidden_size=1408, frequency_embedding_size=256): super().__init__() self.mlp = torch.nn.Sequential( torch.nn.Linear(frequency_embedding_size, hidden_size, bias=True), torch.nn.SiLU(), torch.nn.Linear(hidden_size, hidden_size, bias=True), ) self.frequency_embedding_size = frequency_embedding_size def forward(self, t): t_freq = timestep_embedding(t, self.frequency_embedding_size).type(self.mlp[0].weight.dtype) t_emb = self.mlp(t_freq) return t_emb class HunyuanDiT(torch.nn.Module): def __init__(self, num_layers_down=21, num_layers_up=19, in_channels=4, out_channels=8, hidden_dim=1408, text_dim=1024, t5_dim=2048, text_length=77, t5_length=256): super().__init__() # Embedders self.text_emb_padding = torch.nn.Parameter(torch.randn(text_length + t5_length, text_dim, dtype=torch.float32)) self.t5_embedder = torch.nn.Sequential( torch.nn.Linear(t5_dim, t5_dim * 4, bias=True), FP32_SiLU(), torch.nn.Linear(t5_dim * 4, text_dim, bias=True), ) self.t5_pooler = AttentionPool(t5_length, t5_dim, num_heads=8, output_dim=1024) self.style_embedder = torch.nn.Parameter(torch.randn(hidden_dim)) self.patch_embedder = PatchEmbed(in_chans=in_channels) self.timestep_embedder = TimestepEmbedder() self.extra_embedder = torch.nn.Sequential( torch.nn.Linear(256 * 6 + 1024 + hidden_dim, hidden_dim * 4), FP32_SiLU(), torch.nn.Linear(hidden_dim * 4, hidden_dim), ) # Transformer blocks self.num_layers_down = num_layers_down self.num_layers_up = num_layers_up self.blocks = torch.nn.ModuleList( [HunyuanDiTBlock(skip_connection=False) for _ in range(num_layers_down)] + \ [HunyuanDiTBlock(skip_connection=True) for _ in range(num_layers_up)] ) # Output layers self.final_layer = HunyuanDiTFinalLayer() self.out_channels = out_channels def prepare_text_emb(self, text_emb, text_emb_t5, text_emb_mask, text_emb_mask_t5): text_emb_mask = text_emb_mask.bool() text_emb_mask_t5 = text_emb_mask_t5.bool() text_emb_t5 = self.t5_embedder(text_emb_t5) text_emb = torch.cat([text_emb, text_emb_t5], dim=1) text_emb_mask = torch.cat([text_emb_mask, text_emb_mask_t5], dim=-1) text_emb = torch.where(text_emb_mask.unsqueeze(2), text_emb, self.text_emb_padding.to(text_emb)) return text_emb def prepare_extra_emb(self, text_emb_t5, timestep, size_emb, dtype, batch_size): # Text embedding pooled_text_emb_t5 = self.t5_pooler(text_emb_t5) # Timestep embedding timestep_emb = self.timestep_embedder(timestep) # Size embedding size_emb = timestep_embedding(size_emb.view(-1), 256).to(dtype) size_emb = size_emb.view(-1, 6 * 256) # Style embedding style_emb = repeat(self.style_embedder, "D -> B D", B=batch_size) # Concatenate all extra vectors extra_emb = torch.cat([pooled_text_emb_t5, size_emb, style_emb], dim=1) condition_emb = timestep_emb + self.extra_embedder(extra_emb) return condition_emb def unpatchify(self, x, h, w): return rearrange(x, "B (H W) (P Q C) -> B C (H P) (W Q)", H=h, W=w, P=2, Q=2) def build_mask(self, data, is_bound): _, _, H, W = data.shape h = repeat(torch.arange(H), "H -> H W", H=H, W=W) w = repeat(torch.arange(W), "W -> H W", H=H, W=W) border_width = (H + W) // 4 pad = torch.ones_like(h) * border_width mask = torch.stack([ pad if is_bound[0] else h + 1, pad if is_bound[1] else H - h, pad if is_bound[2] else w + 1, pad if is_bound[3] else W - w ]).min(dim=0).values mask = mask.clip(1, border_width) mask = (mask / border_width).to(dtype=data.dtype, device=data.device) mask = rearrange(mask, "H W -> 1 H W") return mask def tiled_block_forward(self, block, hidden_states, condition_emb, text_emb, freq_cis_img, residual, torch_dtype, data_device, computation_device, tile_size, tile_stride): B, C, H, W = hidden_states.shape weight = torch.zeros((1, 1, H, W), dtype=torch_dtype, device=data_device) values = torch.zeros((B, C, H, W), dtype=torch_dtype, device=data_device) # Split tasks tasks = [] for h in range(0, H, tile_stride): for w in range(0, W, tile_stride): if (h-tile_stride >= 0 and h-tile_stride+tile_size >= H) or (w-tile_stride >= 0 and w-tile_stride+tile_size >= W): continue h_, w_ = h + tile_size, w + tile_size if h_ > H: h, h_ = H - tile_size, H if w_ > W: w, w_ = W - tile_size, W tasks.append((h, h_, w, w_)) # Run for hl, hr, wl, wr in tasks: hidden_states_batch = hidden_states[:, :, hl:hr, wl:wr].to(computation_device) hidden_states_batch = rearrange(hidden_states_batch, "B C H W -> B (H W) C") if residual is not None: residual_batch = residual[:, :, hl:hr, wl:wr].to(computation_device) residual_batch = rearrange(residual_batch, "B C H W -> B (H W) C") else: residual_batch = None # Forward hidden_states_batch = block(hidden_states_batch, condition_emb, text_emb, freq_cis_img, residual_batch).to(data_device) hidden_states_batch = rearrange(hidden_states_batch, "B (H W) C -> B C H W", H=hr-hl) mask = self.build_mask(hidden_states_batch, is_bound=(hl==0, hr>=H, wl==0, wr>=W)) values[:, :, hl:hr, wl:wr] += hidden_states_batch * mask weight[:, :, hl:hr, wl:wr] += mask values /= weight return values def forward( self, hidden_states, text_emb, text_emb_t5, text_emb_mask, text_emb_mask_t5, timestep, size_emb, freq_cis_img, tiled=False, tile_size=64, tile_stride=32, to_cache=False, use_gradient_checkpointing=False, ): # Embeddings text_emb = self.prepare_text_emb(text_emb, text_emb_t5, text_emb_mask, text_emb_mask_t5) condition_emb = self.prepare_extra_emb(text_emb_t5, timestep, size_emb, hidden_states.dtype, hidden_states.shape[0]) # Input height, width = hidden_states.shape[-2], hidden_states.shape[-1] hidden_states = self.patch_embedder(hidden_states) # Blocks def create_custom_forward(module): def custom_forward(*inputs): return module(*inputs) return custom_forward if tiled: hidden_states = rearrange(hidden_states, "B (H W) C -> B C H W", H=height//2) residuals = [] for block_id, block in enumerate(self.blocks): residual = residuals.pop() if block_id >= self.num_layers_down else None hidden_states = self.tiled_block_forward( block, hidden_states, condition_emb, text_emb, freq_cis_img, residual, torch_dtype=hidden_states.dtype, data_device=hidden_states.device, computation_device=hidden_states.device, tile_size=tile_size, tile_stride=tile_stride ) if block_id < self.num_layers_down - 2: residuals.append(hidden_states) hidden_states = rearrange(hidden_states, "B C H W -> B (H W) C") else: residuals = [] for block_id, block in enumerate(self.blocks): residual = residuals.pop() if block_id >= self.num_layers_down else None if self.training and use_gradient_checkpointing: hidden_states = torch.utils.checkpoint.checkpoint( create_custom_forward(block), hidden_states, condition_emb, text_emb, freq_cis_img, residual, use_reentrant=False, ) else: hidden_states = block(hidden_states, condition_emb, text_emb, freq_cis_img, residual, to_cache=to_cache) if block_id < self.num_layers_down - 2: residuals.append(hidden_states) # Output hidden_states = self.final_layer(hidden_states, condition_emb) hidden_states = self.unpatchify(hidden_states, height//2, width//2) hidden_states, _ = hidden_states.chunk(2, dim=1) return hidden_states @staticmethod def state_dict_converter(): return HunyuanDiTStateDictConverter() class HunyuanDiTStateDictConverter(): def __init__(self): pass def from_diffusers(self, state_dict): state_dict_ = {} for name, param in state_dict.items(): name_ = name name_ = name_.replace(".default_modulation.", ".modulation.") name_ = name_.replace(".mlp.fc1.", ".mlp.0.") name_ = name_.replace(".mlp.fc2.", ".mlp.2.") name_ = name_.replace(".attn1.q_norm.", ".rota1.q_norm.") name_ = name_.replace(".attn2.q_norm.", ".rota2.q_norm.") name_ = name_.replace(".attn1.k_norm.", ".rota1.k_norm.") name_ = name_.replace(".attn2.k_norm.", ".rota2.k_norm.") name_ = name_.replace(".q_proj.", ".to_q.") name_ = name_.replace(".out_proj.", ".to_out.") name_ = name_.replace("text_embedding_padding", "text_emb_padding") name_ = name_.replace("mlp_t5.0.", "t5_embedder.0.") name_ = name_.replace("mlp_t5.2.", "t5_embedder.2.") name_ = name_.replace("pooler.", "t5_pooler.") name_ = name_.replace("x_embedder.", "patch_embedder.") name_ = name_.replace("t_embedder.", "timestep_embedder.") name_ = name_.replace("t5_pooler.to_q.", "t5_pooler.q_proj.") name_ = name_.replace("style_embedder.weight", "style_embedder") if ".kv_proj." in name_: param_k = param[:param.shape[0]//2] param_v = param[param.shape[0]//2:] state_dict_[name_.replace(".kv_proj.", ".to_k.")] = param_k state_dict_[name_.replace(".kv_proj.", ".to_v.")] = param_v elif ".Wqkv." in name_: param_q = param[:param.shape[0]//3] param_k = param[param.shape[0]//3:param.shape[0]//3*2] param_v = param[param.shape[0]//3*2:] state_dict_[name_.replace(".Wqkv.", ".to_q.")] = param_q state_dict_[name_.replace(".Wqkv.", ".to_k.")] = param_k state_dict_[name_.replace(".Wqkv.", ".to_v.")] = param_v elif "style_embedder" in name_: state_dict_[name_] = param.squeeze() else: state_dict_[name_] = param return state_dict_ def from_civitai(self, state_dict): return self.from_diffusers(state_dict)