94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
1125 lines
37 KiB
Python
1125 lines
37 KiB
Python
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
|
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
import torch
|
|
import torch.nn as nn
|
|
|
|
from sglang.multimodal_gen.configs.models.dits import HunyuanVideoConfig
|
|
from sglang.multimodal_gen.configs.sample.teacache import TeaCacheParams
|
|
from sglang.multimodal_gen.runtime.distributed import divide, get_tp_world_size
|
|
from sglang.multimodal_gen.runtime.distributed.parallel_state import (
|
|
get_ring_parallel_world_size,
|
|
get_sp_parallel_rank,
|
|
get_sp_world_size,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.attention import (
|
|
LocalAttention,
|
|
UlyssesAttention,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.elementwise import MulAdd
|
|
from sglang.multimodal_gen.runtime.layers.layernorm import (
|
|
LayerNormScaleShift,
|
|
RMSNorm,
|
|
ScaleResidualLayerNormScaleShift,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.linear import (
|
|
MergedColumnParallelLinear,
|
|
ReplicatedLinear,
|
|
RowParallelLinear,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.mlp import MLP
|
|
from sglang.multimodal_gen.runtime.layers.quantization.configs.base_config import (
|
|
QuantizationConfig,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.rotary_embedding import (
|
|
_apply_rotary_emb,
|
|
get_rotary_pos_embed,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.visual_embedding import (
|
|
ModulateProjection,
|
|
PatchEmbed,
|
|
TimestepEmbedder,
|
|
unpatchify,
|
|
)
|
|
from sglang.multimodal_gen.runtime.managers.forward_context import get_forward_context
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers.layerwise_offload import (
|
|
LayerwiseOffloadableModuleMixin,
|
|
)
|
|
from sglang.multimodal_gen.runtime.models.dits.base import CachableDiT
|
|
from sglang.multimodal_gen.runtime.models.dits.common import modulate
|
|
from sglang.multimodal_gen.runtime.platforms import (
|
|
AttentionBackendEnum,
|
|
current_platform,
|
|
)
|
|
|
|
|
|
class MixedRowParallelLinear(RowParallelLinear):
|
|
def __init__(self, input_sizes: list[int], output_size: int, **kwargs):
|
|
self.input_sizes = input_sizes
|
|
super().__init__(sum(input_sizes), output_size, **kwargs)
|
|
|
|
def weight_loader(self, param: nn.Parameter, loaded_weight: torch.Tensor):
|
|
input_dim = getattr(param, "input_dim", None)
|
|
if input_dim is not None:
|
|
shards = []
|
|
offset = 0
|
|
for input_size in self.input_sizes:
|
|
loaded_shard = loaded_weight.narrow(input_dim, offset, input_size)
|
|
shard_size = input_size // self.tp_size
|
|
loaded_shard = loaded_shard.narrow(
|
|
input_dim, self.tp_rank * shard_size, shard_size
|
|
)
|
|
shards.append(loaded_shard)
|
|
offset += input_size
|
|
loaded_weight = torch.cat(shards, dim=input_dim)
|
|
if len(loaded_weight.shape) == 0:
|
|
loaded_weight = loaded_weight.reshape(1)
|
|
param.data.copy_(loaded_weight)
|
|
|
|
def weight_loader_v2(self, param: nn.Parameter, loaded_weight: torch.Tensor):
|
|
self.weight_loader(param, loaded_weight)
|
|
|
|
|
|
class MMDoubleStreamBlock(nn.Module):
|
|
"""
|
|
A multimodal DiT block with separate modulation for text and image/video,
|
|
using distributed attention and linear layers.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
hidden_size: int,
|
|
num_attention_heads: int,
|
|
mlp_ratio: float,
|
|
dtype: torch.dtype | None = None,
|
|
supported_attention_backends: set[AttentionBackendEnum] | None = None,
|
|
prefix: str = "",
|
|
quant_config: QuantizationConfig | None = None,
|
|
):
|
|
super().__init__()
|
|
|
|
self.deterministic = False
|
|
self.num_attention_heads = num_attention_heads
|
|
tp_size = get_tp_world_size()
|
|
self.local_num_attention_heads = divide(num_attention_heads, tp_size)
|
|
head_dim = hidden_size // num_attention_heads
|
|
mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
|
|
|
# Image modulation components
|
|
self.img_mod = ModulateProjection(
|
|
hidden_size,
|
|
factor=6,
|
|
act_layer="silu",
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.img_mod",
|
|
)
|
|
|
|
# Fused operations for image stream
|
|
self.img_attn_norm = LayerNormScaleShift(
|
|
hidden_size, elementwise_affine=False, dtype=dtype
|
|
)
|
|
self.img_attn_residual_mlp_norm = ScaleResidualLayerNormScaleShift(
|
|
hidden_size, elementwise_affine=False, dtype=dtype
|
|
)
|
|
self.img_mlp_residual = MulAdd()
|
|
|
|
# Image attention components
|
|
self.img_attn_qkv = MergedColumnParallelLinear(
|
|
hidden_size,
|
|
[hidden_size] * 3,
|
|
bias=True,
|
|
gather_output=False,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.img_attn_qkv",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
self.img_attn_q_norm = RMSNorm(head_dim, eps=1e-6, dtype=dtype)
|
|
self.img_attn_k_norm = RMSNorm(head_dim, eps=1e-6, dtype=dtype)
|
|
|
|
self.img_attn_proj = RowParallelLinear(
|
|
hidden_size,
|
|
hidden_size,
|
|
bias=True,
|
|
input_is_parallel=True,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.img_attn_proj",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
self.img_mlp = MLP(
|
|
hidden_size,
|
|
mlp_hidden_dim,
|
|
bias=True,
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.img_mlp",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
# Text modulation components
|
|
self.txt_mod = ModulateProjection(
|
|
hidden_size,
|
|
factor=6,
|
|
act_layer="silu",
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.txt_mod",
|
|
)
|
|
|
|
# Fused operations for text stream
|
|
self.txt_attn_norm = LayerNormScaleShift(
|
|
hidden_size, elementwise_affine=False, dtype=dtype
|
|
)
|
|
self.txt_attn_residual_mlp_norm = ScaleResidualLayerNormScaleShift(
|
|
hidden_size, elementwise_affine=False, dtype=dtype
|
|
)
|
|
self.txt_mlp_residual = MulAdd()
|
|
|
|
# Text attention components
|
|
self.txt_attn_qkv = MergedColumnParallelLinear(
|
|
hidden_size,
|
|
[hidden_size] * 3,
|
|
bias=True,
|
|
gather_output=False,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.txt_attn_qkv",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
# QK norm layers for text
|
|
self.txt_attn_q_norm = RMSNorm(head_dim, eps=1e-6, dtype=dtype)
|
|
self.txt_attn_k_norm = RMSNorm(head_dim, eps=1e-6, dtype=dtype)
|
|
|
|
self.txt_attn_proj = RowParallelLinear(
|
|
hidden_size,
|
|
hidden_size,
|
|
bias=True,
|
|
input_is_parallel=True,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.txt_attn_proj",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
self.txt_mlp = MLP(
|
|
hidden_size,
|
|
mlp_hidden_dim,
|
|
bias=True,
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.txt_mlp",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
# Use UlyssesAttention to replace Distributed attention
|
|
self.attn = UlyssesAttention(
|
|
num_heads=self.local_num_attention_heads,
|
|
head_size=head_dim,
|
|
causal=False,
|
|
supported_attention_backends=supported_attention_backends,
|
|
prefix=f"{prefix}.attn",
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
img: torch.Tensor,
|
|
txt: torch.Tensor,
|
|
vec: torch.Tensor,
|
|
freqs_cis: tuple,
|
|
txt_is_sharded: bool = False,
|
|
seq_lens: list[int] | None = None,
|
|
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
# Process modulation vectors
|
|
img_mod_outputs = self.img_mod(vec)
|
|
(
|
|
img_attn_shift,
|
|
img_attn_scale,
|
|
img_attn_gate,
|
|
img_mlp_shift,
|
|
img_mlp_scale,
|
|
img_mlp_gate,
|
|
) = torch.chunk(img_mod_outputs, 6, dim=-1)
|
|
|
|
txt_mod_outputs = self.txt_mod(vec)
|
|
(
|
|
txt_attn_shift,
|
|
txt_attn_scale,
|
|
txt_attn_gate,
|
|
txt_mlp_shift,
|
|
txt_mlp_scale,
|
|
txt_mlp_gate,
|
|
) = torch.chunk(txt_mod_outputs, 6, dim=-1)
|
|
|
|
# Prepare image for attention using fused operation
|
|
img_attn_input = self.img_attn_norm(img, img_attn_shift, img_attn_scale)
|
|
# Get QKV for image
|
|
img_qkv, _ = self.img_attn_qkv(img_attn_input)
|
|
batch_size, image_seq_len = img_qkv.shape[0], img_qkv.shape[1]
|
|
|
|
# Split QKV
|
|
img_qkv = img_qkv.view(
|
|
batch_size, image_seq_len, 3, self.local_num_attention_heads, -1
|
|
)
|
|
img_q, img_k, img_v = img_qkv[:, :, 0], img_qkv[:, :, 1], img_qkv[:, :, 2]
|
|
|
|
# Apply QK-Norm if needed
|
|
|
|
img_q = self.img_attn_q_norm(img_q.contiguous()).to(img_v)
|
|
img_k = self.img_attn_k_norm(img_k.contiguous()).to(img_v)
|
|
# Apply rotary embeddings
|
|
cos, sin = freqs_cis
|
|
img_q, img_k = (
|
|
_apply_rotary_emb(img_q, cos, sin, is_neox_style=False),
|
|
_apply_rotary_emb(img_k, cos, sin, is_neox_style=False),
|
|
)
|
|
# Prepare text for attention using fused operation
|
|
txt_attn_input = self.txt_attn_norm(txt, txt_attn_shift, txt_attn_scale)
|
|
|
|
# Get QKV for text
|
|
txt_qkv, _ = self.txt_attn_qkv(txt_attn_input)
|
|
batch_size, text_seq_len = txt_qkv.shape[0], txt_qkv.shape[1]
|
|
|
|
# Split QKV
|
|
txt_qkv = txt_qkv.view(
|
|
batch_size, text_seq_len, 3, self.local_num_attention_heads, -1
|
|
)
|
|
txt_q, txt_k, txt_v = txt_qkv[:, :, 0], txt_qkv[:, :, 1], txt_qkv[:, :, 2]
|
|
|
|
# Apply QK-Norm if needed
|
|
txt_q = self.txt_attn_q_norm(txt_q.contiguous()).to(txt_q.dtype)
|
|
txt_k = self.txt_attn_k_norm(txt_k.contiguous()).to(txt_k.dtype)
|
|
|
|
# Run distributed attention
|
|
if txt_is_sharded:
|
|
attn, _ = self.attn(
|
|
torch.cat((img_q, txt_q), dim=1),
|
|
torch.cat((img_k, txt_k), dim=1),
|
|
torch.cat((img_v, txt_v), dim=1),
|
|
seq_lens=seq_lens,
|
|
)
|
|
img_attn, txt_attn = attn.split([image_seq_len, text_seq_len], dim=1)
|
|
else:
|
|
img_attn, txt_attn = self.attn(img_q, img_k, img_v, txt_q, txt_k, txt_v)
|
|
img_attn_out, _ = self.img_attn_proj(
|
|
img_attn.reshape(batch_size, image_seq_len, -1)
|
|
)
|
|
# Use fused operation for residual connection, normalization, and modulation
|
|
img_mlp_input, img_residual = self.img_attn_residual_mlp_norm(
|
|
img, img_attn_out, img_attn_gate, img_mlp_shift, img_mlp_scale
|
|
)
|
|
|
|
# Process image MLP
|
|
img_mlp_out = self.img_mlp(img_mlp_input)
|
|
img = self.img_mlp_residual(img_mlp_out, img_mlp_gate, img_residual)
|
|
|
|
# Process text attention output
|
|
txt_attn_out, _ = self.txt_attn_proj(
|
|
txt_attn.reshape(batch_size, text_seq_len, -1)
|
|
)
|
|
|
|
# Use fused operation for residual connection, normalization, and modulation
|
|
txt_mlp_input, txt_residual = self.txt_attn_residual_mlp_norm(
|
|
txt, txt_attn_out, txt_attn_gate, txt_mlp_shift, txt_mlp_scale
|
|
)
|
|
|
|
# Process text MLP
|
|
txt_mlp_out = self.txt_mlp(txt_mlp_input)
|
|
txt = self.txt_mlp_residual(txt_mlp_out, txt_mlp_gate, txt_residual)
|
|
|
|
return img, txt
|
|
|
|
|
|
class MMSingleStreamBlock(nn.Module):
|
|
"""
|
|
A DiT block with parallel linear layers using distributed attention
|
|
and tensor parallelism.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
hidden_size: int,
|
|
num_attention_heads: int,
|
|
mlp_ratio: float = 4.0,
|
|
dtype: torch.dtype | None = None,
|
|
supported_attention_backends: set[AttentionBackendEnum] | None = None,
|
|
prefix: str = "",
|
|
quant_config: QuantizationConfig | None = None,
|
|
):
|
|
super().__init__()
|
|
|
|
self.deterministic = False
|
|
self.hidden_size = hidden_size
|
|
self.num_attention_heads = num_attention_heads
|
|
tp_size = get_tp_world_size()
|
|
self.local_num_attention_heads = divide(num_attention_heads, tp_size)
|
|
head_dim = hidden_size // num_attention_heads
|
|
self.head_dim = head_dim
|
|
mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
|
self.mlp_hidden_dim = mlp_hidden_dim
|
|
self.local_mlp_hidden_dim = divide(mlp_hidden_dim, tp_size)
|
|
|
|
# Combined QKV and MLP input projection
|
|
self.linear1 = MergedColumnParallelLinear(
|
|
hidden_size,
|
|
[hidden_size] * 3 + [mlp_hidden_dim],
|
|
bias=True,
|
|
gather_output=False,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.linear1",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
# Combined projection and MLP output
|
|
self.linear2 = MixedRowParallelLinear(
|
|
[hidden_size, mlp_hidden_dim],
|
|
hidden_size,
|
|
bias=True,
|
|
input_is_parallel=True,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.linear2",
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
# QK norm layers
|
|
self.q_norm = RMSNorm(head_dim, eps=1e-6, dtype=dtype)
|
|
self.k_norm = RMSNorm(head_dim, eps=1e-6, dtype=dtype)
|
|
|
|
# Fused operations with better naming
|
|
self.input_norm_scale_shift = LayerNormScaleShift(
|
|
hidden_size,
|
|
eps=1e-6,
|
|
elementwise_affine=False,
|
|
dtype=dtype,
|
|
)
|
|
self.output_residual = MulAdd()
|
|
|
|
# Activation function
|
|
self.mlp_act = nn.GELU(approximate="tanh")
|
|
|
|
# Modulation
|
|
self.modulation = ModulateProjection(
|
|
hidden_size,
|
|
factor=3,
|
|
act_layer="silu",
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.modulation",
|
|
)
|
|
|
|
# Use UlyssesAttention to replace Distributed attention
|
|
self.attn = UlyssesAttention(
|
|
num_heads=self.local_num_attention_heads,
|
|
head_size=head_dim,
|
|
causal=False,
|
|
supported_attention_backends=supported_attention_backends,
|
|
prefix=f"{prefix}.attn",
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
x: torch.Tensor,
|
|
vec: torch.Tensor,
|
|
txt_len: int,
|
|
freqs_cis: tuple[torch.Tensor, torch.Tensor],
|
|
txt_is_sharded: bool = False,
|
|
seq_lens: list[int] | None = None,
|
|
) -> torch.Tensor:
|
|
# Process modulation
|
|
mod_shift, mod_scale, mod_gate = self.modulation(vec).chunk(3, dim=-1)
|
|
|
|
# Apply pre-norm and modulation using fused operation
|
|
x_mod = self.input_norm_scale_shift(x, mod_shift, mod_scale)
|
|
|
|
# Get combined projections
|
|
linear1_out, _ = self.linear1(x_mod)
|
|
|
|
# Split into QKV and MLP parts
|
|
local_qkv_dim = 3 * self.local_num_attention_heads * self.head_dim
|
|
qkv, mlp = torch.split(
|
|
linear1_out,
|
|
[local_qkv_dim, self.local_mlp_hidden_dim],
|
|
dim=-1,
|
|
)
|
|
|
|
# Process QKV
|
|
batch_size, seq_len = qkv.shape[0], qkv.shape[1]
|
|
qkv = qkv.view(batch_size, seq_len, 3, self.local_num_attention_heads, -1)
|
|
q, k, v = qkv[:, :, 0], qkv[:, :, 1], qkv[:, :, 2]
|
|
|
|
# Apply QK-Norm
|
|
q = self.q_norm(q.contiguous()).to(v.dtype)
|
|
k = self.k_norm(k.contiguous()).to(v.dtype)
|
|
|
|
# Split into image and text parts
|
|
img_q, txt_q = q[:, :-txt_len], q[:, -txt_len:]
|
|
img_k, txt_k = k[:, :-txt_len], k[:, -txt_len:]
|
|
img_v, txt_v = v[:, :-txt_len], v[:, -txt_len:]
|
|
# Apply rotary embeddings to image parts
|
|
cos, sin = freqs_cis
|
|
img_q, img_k = (
|
|
_apply_rotary_emb(img_q, cos, sin, is_neox_style=False),
|
|
_apply_rotary_emb(img_k, cos, sin, is_neox_style=False),
|
|
)
|
|
|
|
# Run distributed attention
|
|
if txt_is_sharded:
|
|
attn_output, _ = self.attn(
|
|
torch.cat((img_q, txt_q), dim=1),
|
|
torch.cat((img_k, txt_k), dim=1),
|
|
torch.cat((img_v, txt_v), dim=1),
|
|
seq_lens=seq_lens,
|
|
)
|
|
else:
|
|
img_attn_output, txt_attn_output = self.attn(
|
|
img_q, img_k, img_v, txt_q, txt_k, txt_v
|
|
)
|
|
attn_output = torch.cat((img_attn_output, txt_attn_output), dim=1)
|
|
attn_output = attn_output.view(batch_size, seq_len, -1)
|
|
# Process MLP activation
|
|
mlp_output = self.mlp_act(mlp)
|
|
|
|
# Combine attention and MLP outputs
|
|
combined = torch.cat((attn_output, mlp_output), dim=-1)
|
|
|
|
# Final projection
|
|
output, _ = self.linear2(combined)
|
|
|
|
# Apply residual connection with gating using fused operation
|
|
return self.output_residual(output, mod_gate, x)
|
|
|
|
|
|
class HunyuanVideoTransformer3DModel(CachableDiT, LayerwiseOffloadableModuleMixin):
|
|
"""
|
|
HunyuanVideo Transformer backbone adapted for distributed training.
|
|
|
|
This implementation uses distributed attention and linear layers for efficient
|
|
parallel processing across multiple GPUs.
|
|
|
|
Based on the architecture from:
|
|
- Flux.1: https://github.com/black-forest-labs/flux
|
|
- MMDiT: http://arxiv.org/abs/2403.03206
|
|
"""
|
|
|
|
# PY: we make the input args the same as HF config
|
|
|
|
# shard single stream, double stream blocks, and refiner_blocks
|
|
_fsdp_shard_conditions = HunyuanVideoConfig()._fsdp_shard_conditions
|
|
_compile_conditions = HunyuanVideoConfig()._compile_conditions
|
|
_supported_attention_backends = HunyuanVideoConfig()._supported_attention_backends
|
|
param_names_mapping = HunyuanVideoConfig().param_names_mapping
|
|
reverse_param_names_mapping = HunyuanVideoConfig().reverse_param_names_mapping
|
|
lora_param_names_mapping = HunyuanVideoConfig().lora_param_names_mapping
|
|
|
|
def __init__(
|
|
self,
|
|
config: HunyuanVideoConfig,
|
|
hf_config: dict[str, Any],
|
|
quant_config: QuantizationConfig | None = None,
|
|
):
|
|
super().__init__(config=config, hf_config=hf_config)
|
|
|
|
self.patch_size = [config.patch_size_t, config.patch_size, config.patch_size]
|
|
self.in_channels = config.in_channels
|
|
self.num_channels_latents = config.num_channels_latents
|
|
self.out_channels = (
|
|
config.in_channels if config.out_channels is None else config.out_channels
|
|
)
|
|
self.unpatchify_channels = self.out_channels
|
|
self.guidance_embeds = config.guidance_embeds
|
|
self.rope_dim_list = list(config.rope_axes_dim)
|
|
self.rope_theta = config.rope_theta
|
|
self.text_states_dim = config.text_embed_dim
|
|
self.text_states_dim_2 = config.pooled_projection_dim
|
|
# TODO(will): hack?
|
|
self.dtype = config.dtype
|
|
|
|
pe_dim = config.hidden_size // config.num_attention_heads
|
|
if sum(config.rope_axes_dim) != pe_dim:
|
|
raise ValueError(
|
|
f"Got {config.rope_axes_dim} but expected positional dim {pe_dim}"
|
|
)
|
|
|
|
self.hidden_size = config.hidden_size
|
|
self.num_attention_heads = config.num_attention_heads
|
|
self.num_channels_latents = config.num_channels_latents
|
|
|
|
# Image projection
|
|
self.img_in = PatchEmbed(
|
|
self.patch_size,
|
|
self.in_channels,
|
|
self.hidden_size,
|
|
dtype=config.dtype,
|
|
prefix=f"{config.prefix}.img_in",
|
|
)
|
|
|
|
self.txt_in = SingleTokenRefiner(
|
|
self.text_states_dim,
|
|
config.hidden_size,
|
|
config.num_attention_heads,
|
|
depth=config.num_refiner_layers,
|
|
dtype=config.dtype,
|
|
prefix=f"{config.prefix}.txt_in",
|
|
)
|
|
|
|
# Time modulation
|
|
self.time_in = TimestepEmbedder(
|
|
self.hidden_size,
|
|
act_layer="silu",
|
|
dtype=config.dtype,
|
|
prefix=f"{config.prefix}.time_in",
|
|
)
|
|
|
|
# Text modulation
|
|
self.vector_in = MLP(
|
|
self.text_states_dim_2,
|
|
self.hidden_size,
|
|
self.hidden_size,
|
|
act_type="silu",
|
|
dtype=config.dtype,
|
|
prefix=f"{config.prefix}.vector_in",
|
|
)
|
|
|
|
# Guidance modulation
|
|
self.guidance_in = (
|
|
TimestepEmbedder(
|
|
self.hidden_size,
|
|
act_layer="silu",
|
|
dtype=config.dtype,
|
|
prefix=f"{config.prefix}.guidance_in",
|
|
)
|
|
if self.guidance_embeds
|
|
else None
|
|
)
|
|
|
|
# Double blocks
|
|
self.double_blocks = nn.ModuleList(
|
|
[
|
|
MMDoubleStreamBlock(
|
|
config.hidden_size,
|
|
config.num_attention_heads,
|
|
mlp_ratio=config.mlp_ratio,
|
|
dtype=config.dtype,
|
|
supported_attention_backends=self._supported_attention_backends,
|
|
prefix=f"{config.prefix}.double_blocks.{i}",
|
|
quant_config=quant_config,
|
|
)
|
|
for i in range(config.num_layers)
|
|
]
|
|
)
|
|
|
|
# Single blocks
|
|
self.single_blocks = nn.ModuleList(
|
|
[
|
|
MMSingleStreamBlock(
|
|
config.hidden_size,
|
|
config.num_attention_heads,
|
|
mlp_ratio=config.mlp_ratio,
|
|
dtype=config.dtype,
|
|
supported_attention_backends=self._supported_attention_backends,
|
|
prefix=f"{config.prefix}.single_blocks.{i + config.num_layers}",
|
|
quant_config=quant_config,
|
|
)
|
|
for i in range(config.num_single_layers)
|
|
]
|
|
)
|
|
|
|
self.final_layer = FinalLayer(
|
|
config.hidden_size,
|
|
self.patch_size,
|
|
self.out_channels,
|
|
dtype=config.dtype,
|
|
prefix=f"{config.prefix}.final_layer",
|
|
)
|
|
|
|
self.__post_init__()
|
|
|
|
self.layer_names = ["double_blocks", "single_blocks"]
|
|
|
|
# TODO: change the input the FORWARD_BATCH Dict
|
|
# TODO: change output to a dict
|
|
def forward(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
encoder_hidden_states: torch.Tensor | list[torch.Tensor],
|
|
timestep: torch.LongTensor,
|
|
encoder_hidden_states_image: torch.Tensor | list[torch.Tensor] | None = None,
|
|
pooled_projections: torch.Tensor | None = None,
|
|
guidance=None,
|
|
**kwargs,
|
|
):
|
|
"""
|
|
Forward pass of the HunyuanDiT model.
|
|
|
|
Args:
|
|
hidden_states: Input image/video latents [B, C, T, H, W]
|
|
encoder_hidden_states: Text embeddings [B, L, D]
|
|
timestep: Diffusion timestep
|
|
guidance: Guidance scale for CFG
|
|
|
|
Returns:
|
|
Tuple of (output)
|
|
"""
|
|
forward_context = get_forward_context()
|
|
forward_batch = forward_context.forward_batch
|
|
enable_teacache = forward_batch is not None and forward_batch.enable_teacache
|
|
|
|
if guidance is None:
|
|
guidance = torch.tensor(
|
|
[6016.0], device=hidden_states.device, dtype=hidden_states.dtype
|
|
)
|
|
|
|
img = x = hidden_states
|
|
t = timestep
|
|
|
|
# Split text embeddings - first token is global, rest are per-token
|
|
if isinstance(encoder_hidden_states, torch.Tensor):
|
|
if pooled_projections is None:
|
|
txt = encoder_hidden_states[:, 1:]
|
|
text_states_2 = encoder_hidden_states[:, 0, : self.text_states_dim_2]
|
|
else:
|
|
txt = encoder_hidden_states
|
|
text_states_2 = pooled_projections
|
|
else:
|
|
txt = encoder_hidden_states[0]
|
|
text_states_2 = encoder_hidden_states[1]
|
|
|
|
# Get spatial dimensions
|
|
_, _, ot, oh, ow = x.shape # codespell:ignore
|
|
tt, th, tw = (
|
|
ot // self.patch_size[0], # codespell:ignore
|
|
oh // self.patch_size[1],
|
|
ow // self.patch_size[2],
|
|
)
|
|
|
|
# Get rotary embeddings
|
|
freqs_cos, freqs_sin = get_rotary_pos_embed(
|
|
(tt * get_sp_world_size(), th, tw),
|
|
self.hidden_size,
|
|
self.num_attention_heads,
|
|
self.rope_dim_list,
|
|
self.rope_theta,
|
|
)
|
|
freqs_cos = freqs_cos.to(x.device)
|
|
freqs_sin = freqs_sin.to(x.device)
|
|
# Prepare modulation vectors
|
|
vec = self.time_in(t)
|
|
|
|
# Add text modulation
|
|
vec = vec + self.vector_in(text_states_2)
|
|
|
|
# Add guidance modulation if needed
|
|
if self.guidance_in and guidance is not None:
|
|
vec = vec + self.guidance_in(guidance)
|
|
# Embed image and text
|
|
img = self.img_in(img)
|
|
txt = self.txt_in(txt, t)
|
|
txt_seq_len = txt.shape[1]
|
|
sp_size = get_sp_world_size()
|
|
txt_is_sharded = (
|
|
sp_size > 1
|
|
and get_ring_parallel_world_size() == 1
|
|
and txt_seq_len >= sp_size
|
|
and not torch.is_grad_enabled()
|
|
)
|
|
seq_lens = None
|
|
if txt_is_sharded:
|
|
sp_rank = get_sp_parallel_rank()
|
|
base_text_shard_len = txt_seq_len // sp_size
|
|
extra_text_tokens = txt_seq_len % sp_size
|
|
text_seq_lens = [
|
|
base_text_shard_len + (1 if rank < extra_text_tokens else 0)
|
|
for rank in range(sp_size)
|
|
]
|
|
text_shard_start = base_text_shard_len * sp_rank + min(
|
|
sp_rank, extra_text_tokens
|
|
)
|
|
text_shard_len = text_seq_lens[sp_rank]
|
|
txt = txt[
|
|
:, text_shard_start : text_shard_start + text_shard_len
|
|
].contiguous()
|
|
txt_seq_len = text_shard_len
|
|
img_seq_len = img.shape[1]
|
|
if txt_is_sharded:
|
|
seq_lens = [img_seq_len + text_len for text_len in text_seq_lens]
|
|
|
|
freqs_cis = (freqs_cos, freqs_sin) if freqs_cos is not None else None
|
|
|
|
should_skip_forward = self.should_skip_forward_for_cached_states(
|
|
img=img, vec=vec
|
|
)
|
|
|
|
if should_skip_forward:
|
|
img = self.retrieve_cached_states(img)
|
|
else:
|
|
if enable_teacache:
|
|
original_img = img.clone()
|
|
|
|
# Process through double stream blocks
|
|
for index, block in enumerate(self.double_blocks):
|
|
double_block_args = [
|
|
img,
|
|
txt,
|
|
vec,
|
|
freqs_cis,
|
|
txt_is_sharded,
|
|
seq_lens,
|
|
]
|
|
img, txt = block(*double_block_args)
|
|
# Merge txt and img to pass through single stream blocks
|
|
x = torch.cat((img, txt), 1)
|
|
|
|
# Process through single stream blocks
|
|
if len(self.single_blocks) > 0:
|
|
for index, block in enumerate(self.single_blocks):
|
|
single_block_args = [
|
|
x,
|
|
vec,
|
|
txt_seq_len,
|
|
freqs_cis,
|
|
txt_is_sharded,
|
|
seq_lens,
|
|
]
|
|
x = block(*single_block_args)
|
|
|
|
# Extract image features
|
|
img = x[:, :img_seq_len, ...]
|
|
|
|
if enable_teacache:
|
|
self.maybe_cache_states(img, original_img)
|
|
|
|
# Final layer processing
|
|
img = self.final_layer(img, vec)
|
|
# Unpatchify to get original shape
|
|
img = unpatchify(img, tt, th, tw, self.patch_size, self.out_channels)
|
|
|
|
return img
|
|
|
|
def maybe_cache_states(
|
|
self, hidden_states: torch.Tensor, original_hidden_states: torch.Tensor
|
|
) -> None:
|
|
self.previous_residual = hidden_states - original_hidden_states
|
|
|
|
def should_skip_forward_for_cached_states(self, **kwargs) -> bool:
|
|
forward_context = get_forward_context()
|
|
forward_batch = forward_context.forward_batch
|
|
if forward_batch is None:
|
|
return False
|
|
current_timestep = forward_context.current_timestep
|
|
enable_teacache = forward_batch.enable_teacache
|
|
|
|
if not enable_teacache:
|
|
return False
|
|
raise NotImplementedError("teacache is not supported yet for HunyuanVideo")
|
|
|
|
teacache_params = forward_batch.teacache_params
|
|
assert teacache_params is not None, "teacache_params is not initialized"
|
|
assert isinstance(
|
|
teacache_params, TeaCacheParams
|
|
), "teacache_params is not a TeaCacheParams"
|
|
num_inference_steps = forward_batch.num_inference_steps
|
|
teache_thresh = teacache_params.teacache_thresh
|
|
|
|
coefficients = teacache_params.coefficients
|
|
|
|
if current_timestep == 0:
|
|
self.cnt = 0
|
|
|
|
inp = kwargs["img"].clone()
|
|
vec_ = kwargs["vec"].clone()
|
|
# convert to DTensor
|
|
vec_ = torch.distributed.tensor.DTensor.from_local(
|
|
vec_,
|
|
torch.distributed.DeviceMesh(
|
|
current_platform.device_type,
|
|
list(range(get_sp_world_size())),
|
|
mesh_dim_names=("dp",),
|
|
),
|
|
[torch.distributed.tensor.Replicate()],
|
|
)
|
|
|
|
inp = torch.distributed.tensor.DTensor.from_local(
|
|
inp,
|
|
torch.distributed.DeviceMesh(
|
|
current_platform.device_type,
|
|
list(range(get_sp_world_size())),
|
|
mesh_dim_names=("dp",),
|
|
),
|
|
[torch.distributed.tensor.Replicate()],
|
|
)
|
|
|
|
# txt_ = kwargs["txt"].clone()
|
|
|
|
# inp = img.clone()
|
|
# vec_ = vec.clone()
|
|
# txt_ = txt.clone()
|
|
(
|
|
img_mod1_shift,
|
|
img_mod1_scale,
|
|
img_mod1_gate,
|
|
img_mod2_shift,
|
|
img_mod2_scale,
|
|
img_mod2_gate,
|
|
) = (
|
|
self.double_blocks[0].img_mod(vec_).chunk(6, dim=-1)
|
|
)
|
|
normed_inp = self.double_blocks[0].img_attn_norm.norm(inp)
|
|
modulated_inp = modulate(normed_inp, shift=img_mod1_shift, scale=img_mod1_scale)
|
|
if self.cnt == 0 or self.cnt == num_inference_steps - 1:
|
|
should_calc = True
|
|
self.accumulated_rel_l1_distance = 0
|
|
else:
|
|
coefficients = [
|
|
7.33226126e02,
|
|
-4.01131952e02,
|
|
6.75869174e01,
|
|
-3.14987800e00,
|
|
9.61237896e-02,
|
|
]
|
|
rescale_func = np.poly1d(coefficients)
|
|
assert (
|
|
self.previous_modulated_input is not None
|
|
), "previous_modulated_input is not initialized"
|
|
self.accumulated_rel_l1_distance += rescale_func(
|
|
(
|
|
(modulated_inp - self.previous_modulated_input).abs().mean()
|
|
/ self.previous_modulated_input.abs().mean()
|
|
)
|
|
.cpu()
|
|
.item()
|
|
)
|
|
if self.accumulated_rel_l1_distance < teache_thresh:
|
|
should_calc = False
|
|
else:
|
|
should_calc = True
|
|
self.accumulated_rel_l1_distance = 0
|
|
self.previous_modulated_input = modulated_inp
|
|
self.cnt += 1
|
|
|
|
return not should_calc
|
|
|
|
def retrieve_cached_states(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
|
return hidden_states + self.previous_residual
|
|
|
|
|
|
class SingleTokenRefiner(nn.Module):
|
|
"""
|
|
A token refiner that processes text embeddings with attention to improve
|
|
their representation for cross-attention with image features.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
in_channels,
|
|
hidden_size,
|
|
num_attention_heads,
|
|
depth=2,
|
|
qkv_bias=True,
|
|
dtype=None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
|
|
# Input projection
|
|
self.input_embedder = ReplicatedLinear(
|
|
in_channels,
|
|
hidden_size,
|
|
bias=True,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.input_embedder",
|
|
)
|
|
|
|
# Timestep embedding
|
|
self.t_embedder = TimestepEmbedder(
|
|
hidden_size, act_layer="silu", dtype=dtype, prefix=f"{prefix}.t_embedder"
|
|
)
|
|
|
|
# Context embedding
|
|
self.c_embedder = MLP(
|
|
in_channels,
|
|
hidden_size,
|
|
hidden_size,
|
|
act_type="silu",
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.c_embedder",
|
|
)
|
|
|
|
# Refiner blocks
|
|
self.refiner_blocks = nn.ModuleList(
|
|
[
|
|
IndividualTokenRefinerBlock(
|
|
hidden_size,
|
|
num_attention_heads,
|
|
qkv_bias=qkv_bias,
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.refiner_blocks.{i}",
|
|
)
|
|
for i in range(depth)
|
|
]
|
|
)
|
|
|
|
def forward(self, x, t):
|
|
# Get timestep embeddings
|
|
timestep_aware_representations = self.t_embedder(t)
|
|
|
|
# Get context-aware representations
|
|
|
|
context_aware_representations = torch.mean(x, dim=1)
|
|
|
|
context_aware_representations = self.c_embedder(context_aware_representations)
|
|
c = timestep_aware_representations + context_aware_representations
|
|
# Project input
|
|
x, _ = self.input_embedder(x)
|
|
# Process through refiner blocks
|
|
for block in self.refiner_blocks:
|
|
x = block(x, c)
|
|
return x
|
|
|
|
|
|
class IndividualTokenRefinerBlock(nn.Module):
|
|
"""
|
|
A transformer block for refining individual tokens with self-attention.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
hidden_size,
|
|
num_attention_heads,
|
|
mlp_ratio=4.0,
|
|
qkv_bias=True,
|
|
dtype=None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
self.num_attention_heads = num_attention_heads
|
|
tp_size = get_tp_world_size()
|
|
self.local_num_attention_heads = divide(num_attention_heads, tp_size)
|
|
mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
|
head_dim = hidden_size // num_attention_heads
|
|
|
|
# Normalization and attention
|
|
self.norm1 = nn.LayerNorm(
|
|
hidden_size, eps=1e-6, elementwise_affine=True, dtype=dtype
|
|
)
|
|
|
|
self.self_attn_qkv = MergedColumnParallelLinear(
|
|
hidden_size,
|
|
[hidden_size] * 3,
|
|
bias=qkv_bias,
|
|
gather_output=False,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.self_attn_qkv",
|
|
)
|
|
|
|
self.self_attn_proj = RowParallelLinear(
|
|
hidden_size,
|
|
hidden_size,
|
|
bias=qkv_bias,
|
|
input_is_parallel=True,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.self_attn_proj",
|
|
)
|
|
|
|
# MLP
|
|
self.norm2 = nn.LayerNorm(
|
|
hidden_size, eps=1e-6, elementwise_affine=True, dtype=dtype
|
|
)
|
|
self.mlp = MLP(
|
|
hidden_size,
|
|
mlp_hidden_dim,
|
|
bias=True,
|
|
act_type="silu",
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.mlp",
|
|
)
|
|
|
|
# Modulation
|
|
self.adaLN_modulation = ModulateProjection(
|
|
hidden_size,
|
|
factor=2,
|
|
act_layer="silu",
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.adaLN_modulation",
|
|
)
|
|
|
|
# Scaled dot product attention
|
|
self.attn = LocalAttention(
|
|
num_heads=self.local_num_attention_heads,
|
|
head_size=head_dim,
|
|
# TODO: remove hardcode; remove STA
|
|
supported_attention_backends=(
|
|
AttentionBackendEnum.FA,
|
|
AttentionBackendEnum.AITER,
|
|
AttentionBackendEnum.TORCH_SDPA,
|
|
),
|
|
)
|
|
|
|
def forward(self, x, c):
|
|
# Get modulation parameters
|
|
gate_msa, gate_mlp = self.adaLN_modulation(c).chunk(2, dim=-1)
|
|
# Self-attention
|
|
norm_x = self.norm1(x)
|
|
qkv, _ = self.self_attn_qkv(norm_x)
|
|
|
|
batch_size, seq_len = qkv.shape[0], qkv.shape[1]
|
|
qkv = qkv.view(batch_size, seq_len, 3, self.local_num_attention_heads, -1)
|
|
q, k, v = qkv[:, :, 0], qkv[:, :, 1], qkv[:, :, 2]
|
|
|
|
# Run scaled dot product attention
|
|
attn_output = self.attn(q, k, v) # [B, L, H, D]
|
|
attn_output = attn_output.reshape(batch_size, seq_len, -1) # [B, L, H*D]
|
|
|
|
# Project and apply residual connection with gating
|
|
attn_out, _ = self.self_attn_proj(attn_output)
|
|
x = x + attn_out * gate_msa.unsqueeze(1)
|
|
|
|
# MLP
|
|
mlp_out = self.mlp(self.norm2(x))
|
|
x = x + mlp_out * gate_mlp.unsqueeze(1)
|
|
|
|
return x
|
|
|
|
|
|
class FinalLayer(nn.Module):
|
|
"""
|
|
The final layer of DiT that projects features to pixel space.
|
|
"""
|
|
|
|
def __init__(
|
|
self, hidden_size, patch_size, out_channels, dtype=None, prefix: str = ""
|
|
) -> None:
|
|
super().__init__()
|
|
|
|
# Normalization
|
|
self.norm_final = nn.LayerNorm(
|
|
hidden_size, eps=1e-6, elementwise_affine=False, dtype=dtype
|
|
)
|
|
|
|
output_dim = patch_size[0] * patch_size[1] * patch_size[2] * out_channels
|
|
|
|
self.linear = ReplicatedLinear(
|
|
hidden_size,
|
|
output_dim,
|
|
bias=True,
|
|
params_dtype=dtype,
|
|
prefix=f"{prefix}.linear",
|
|
)
|
|
|
|
# Modulation
|
|
self.adaLN_modulation = ModulateProjection(
|
|
hidden_size,
|
|
factor=2,
|
|
act_layer="silu",
|
|
dtype=dtype,
|
|
prefix=f"{prefix}.adaLN_modulation",
|
|
)
|
|
|
|
def forward(self, x, c):
|
|
# What the heck HF? Why you change the scale and shift order here???
|
|
scale, shift = self.adaLN_modulation(c).chunk(2, dim=-1)
|
|
x = self.norm_final(x) * (1.0 + scale.unsqueeze(1)) + shift.unsqueeze(1)
|
|
x, _ = self.linear(x)
|
|
return x
|
|
|
|
|
|
EntryClass = HunyuanVideoTransformer3DModel
|