Files
wehub-resource-sync cddb07a176
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:06 +08:00

239 lines
8.3 KiB
Python

# Adapted from https://github.com/aigc-apps/VideoX-Fun/blob/main/videox_fun/models/z_image_transformer2d_control.py
# Copyright (c) Alibaba, Inc. and its affiliates.
# Apache License 2.0
"""
Z-Image Control Adapter for InvokeAI.
This module provides a standalone control adapter that can be combined with
a base ZImageTransformer2DModel at runtime. The adapter contains only the
control-specific layers (control_layers, control_all_x_embedder, control_noise_refiner).
"""
from typing import List, Optional
import torch
import torch.nn as nn
from diffusers.configuration_utils import ConfigMixin, register_to_config
from diffusers.models.modeling_utils import ModelMixin
from diffusers.models.transformers.transformer_z_image import (
SEQ_MULTI_OF,
ZImageTransformerBlock,
)
from torch.nn.utils.rnn import pad_sequence
class ZImageControlTransformerBlock(ZImageTransformerBlock):
"""Control-specific transformer block with skip connections for hint generation."""
def __init__(
self,
layer_id: int,
dim: int,
n_heads: int,
n_kv_heads: int,
norm_eps: float,
qk_norm: bool,
modulation: bool = True,
block_id: int = 0,
):
super().__init__(layer_id, dim, n_heads, n_kv_heads, norm_eps, qk_norm, modulation)
self.block_id = block_id
if block_id == 0:
self.before_proj = nn.Linear(dim, dim)
nn.init.zeros_(self.before_proj.weight)
nn.init.zeros_(self.before_proj.bias)
self.after_proj = nn.Linear(dim, dim)
nn.init.zeros_(self.after_proj.weight)
nn.init.zeros_(self.after_proj.bias)
def forward(
self,
c: torch.Tensor,
x: torch.Tensor,
attn_mask: torch.Tensor,
freqs_cis: torch.Tensor,
adaln_input: Optional[torch.Tensor] = None,
) -> torch.Tensor:
if self.block_id == 0:
c = self.before_proj(c) + x
all_c: list[torch.Tensor] = []
else:
all_c = list(torch.unbind(c))
c = all_c.pop(-1)
c = super().forward(c, attn_mask=attn_mask, freqs_cis=freqs_cis, adaln_input=adaln_input)
c_skip = self.after_proj(c)
all_c += [c_skip, c]
c = torch.stack(all_c)
return c
class ZImageControlAdapter(ModelMixin, ConfigMixin):
"""Standalone Z-Image Control Adapter.
This adapter contains only the control-specific layers and can be combined
with a base ZImageTransformer2DModel at runtime. It computes control hints
that are added to the transformer's hidden states.
The adapter supports 5 control modes: Canny, HED, Depth, Pose, MLSD.
Recommended control_context_scale: 0.65-0.80.
"""
@register_to_config
def __init__(
self,
num_control_blocks: int = 6, # Number of control layer blocks
control_in_dim: int = 16,
all_patch_size: tuple[int, ...] = (2,),
all_f_patch_size: tuple[int, ...] = (1,),
dim: int = 3840,
n_refiner_layers: int = 2,
n_heads: int = 30,
n_kv_heads: int = 30,
norm_eps: float = 1e-5,
qk_norm: bool = True,
):
super().__init__()
self.dim = dim
self.control_in_dim = control_in_dim
self.all_patch_size = all_patch_size
self.all_f_patch_size = all_f_patch_size
# Control patch embeddings
all_x_embedder = {}
for patch_size, f_patch_size in zip(all_patch_size, all_f_patch_size, strict=True):
x_embedder = nn.Linear(
f_patch_size * patch_size * patch_size * control_in_dim,
dim,
bias=True,
)
all_x_embedder[f"{patch_size}-{f_patch_size}"] = x_embedder
self.control_all_x_embedder = nn.ModuleDict(all_x_embedder)
# Control noise refiner
self.control_noise_refiner = nn.ModuleList(
[
ZImageTransformerBlock(
1000 + layer_id,
dim,
n_heads,
n_kv_heads,
norm_eps,
qk_norm,
modulation=True,
)
for layer_id in range(n_refiner_layers)
]
)
# Control transformer blocks
self.control_layers = nn.ModuleList(
[
ZImageControlTransformerBlock(
i,
dim,
n_heads,
n_kv_heads,
norm_eps,
qk_norm,
block_id=i,
)
for i in range(num_control_blocks)
]
)
# Padding token for control context
self.x_pad_token = nn.Parameter(torch.empty(dim))
nn.init.normal_(self.x_pad_token, std=0.02)
def forward(
self,
control_context: List[torch.Tensor],
unified_hidden_states: torch.Tensor,
cap_feats: torch.Tensor,
timestep_emb: torch.Tensor,
attn_mask: torch.Tensor,
freqs_cis: torch.Tensor,
rope_embedder,
patchify_fn,
patch_size: int = 2,
f_patch_size: int = 1,
) -> tuple[torch.Tensor, ...]:
"""Compute control hints from control context.
Args:
control_context: List of control image latents [C, 1, H, W]
unified_hidden_states: Combined image+caption embeddings from main path
cap_feats: Caption feature embeddings
timestep_emb: Timestep embeddings
attn_mask: Attention mask
freqs_cis: RoPE frequencies
rope_embedder: RoPE embedder from base model
patchify_fn: Patchify function from base model
patch_size: Spatial patch size
f_patch_size: Frame patch size
Returns:
Tuple of hint tensors to be added at each control layer position
"""
bsz = len(control_context)
device = control_context[0].device
# Patchify control context using base model's patchify
(
control_context_patches,
x_size,
x_pos_ids,
x_inner_pad_mask,
) = patchify_fn(control_context, patch_size, f_patch_size, cap_feats.size(1))
# Embed control context
x_item_seqlens = [len(_) for _ in control_context_patches]
assert all(_ % SEQ_MULTI_OF == 0 for _ in x_item_seqlens)
x_max_item_seqlen = max(x_item_seqlens)
control_context_cat = torch.cat(control_context_patches, dim=0)
control_context_cat = self.control_all_x_embedder[f"{patch_size}-{f_patch_size}"](control_context_cat)
# Match timestep dtype
adaln_input = timestep_emb.type_as(control_context_cat)
control_context_cat[torch.cat(x_inner_pad_mask)] = self.x_pad_token
control_context_list = list(control_context_cat.split(x_item_seqlens, dim=0))
x_freqs_cis = list(rope_embedder(torch.cat(x_pos_ids, dim=0)).split(x_item_seqlens, dim=0))
control_context_padded = pad_sequence(control_context_list, batch_first=True, padding_value=0.0)
x_freqs_cis = pad_sequence(x_freqs_cis, batch_first=True, padding_value=0.0)
x_attn_mask = torch.zeros((bsz, x_max_item_seqlen), dtype=torch.bool, device=device)
for i, seq_len in enumerate(x_item_seqlens):
x_attn_mask[i, :seq_len] = 1
# Refine control context
for layer in self.control_noise_refiner:
control_context_padded = layer(control_context_padded, x_attn_mask, x_freqs_cis, adaln_input)
# Unify with caption features
cap_item_seqlens = [cap_feats.size(1)] * bsz
control_context_unified = []
for i in range(bsz):
x_len = x_item_seqlens[i]
cap_len = cap_item_seqlens[i]
control_context_unified.append(torch.cat([control_context_padded[i][:x_len], cap_feats[i][:cap_len]]))
control_context_unified = pad_sequence(control_context_unified, batch_first=True, padding_value=0.0)
c = control_context_unified
# Process through control layers
for layer in self.control_layers:
c = layer(
c,
x=unified_hidden_states,
attn_mask=attn_mask,
freqs_cis=freqs_cis,
adaln_input=adaln_input,
)
hints = torch.unbind(c)[:-1]
return hints