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
752 lines
26 KiB
Python
752 lines
26 KiB
Python
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
|
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import math
|
|
from typing import Any
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
from torch.nn.attention.flex_attention import (
|
|
BlockMask,
|
|
create_block_mask,
|
|
flex_attention,
|
|
)
|
|
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers.layerwise_offload import (
|
|
LayerwiseOffloadableModuleMixin,
|
|
)
|
|
|
|
# wan 1.3B model has a weird channel / head configurations and require max-autotune to work with flexattention
|
|
# see https://github.com/pytorch/pytorch/issues/133254
|
|
# change to default for other models
|
|
flex_attention = torch.compile(
|
|
flex_attention, dynamic=False, mode="max-autotune-no-cudagraphs"
|
|
)
|
|
import torch.distributed as dist
|
|
|
|
from sglang.multimodal_gen.configs.models.dits import WanVideoConfig
|
|
from sglang.multimodal_gen.runtime.distributed import (
|
|
divide,
|
|
get_sp_world_size,
|
|
get_tp_rank,
|
|
get_tp_world_size,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.attention import LocalAttention
|
|
from sglang.multimodal_gen.runtime.layers.elementwise import MulAdd
|
|
from sglang.multimodal_gen.runtime.layers.kvcache.causal_attention_cache import (
|
|
CausalSelfAttentionKVCache,
|
|
CrossAttentionKVCache,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.layernorm import (
|
|
FP32LayerNorm,
|
|
LayerNormScaleShift,
|
|
RMSNorm,
|
|
ScaleResidualLayerNormScaleShift,
|
|
tensor_parallel_rms_norm,
|
|
)
|
|
from sglang.multimodal_gen.runtime.layers.linear import (
|
|
ColumnParallelLinear,
|
|
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 PatchEmbed
|
|
from sglang.multimodal_gen.runtime.models.dits.base import BaseDiT
|
|
from sglang.multimodal_gen.runtime.models.dits.wanvideo import (
|
|
WanT2VCrossAttention,
|
|
WanTimeTextImageEmbedding,
|
|
)
|
|
from sglang.multimodal_gen.runtime.platforms import (
|
|
AttentionBackendEnum,
|
|
current_platform,
|
|
)
|
|
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
|
|
from sglang.srt.utils import add_prefix
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
|
|
class CausalWanSelfAttention(nn.Module):
|
|
def __init__(
|
|
self,
|
|
dim: int,
|
|
num_heads: int,
|
|
local_attn_size: int = -1,
|
|
sink_size: int = 0,
|
|
qk_norm=True,
|
|
eps=1e-6,
|
|
parallel_attention=False,
|
|
head_dim: int | None = None,
|
|
head_start: int = 0,
|
|
) -> None:
|
|
if head_dim is None:
|
|
assert dim % num_heads == 0
|
|
head_dim = dim // num_heads
|
|
super().__init__()
|
|
self.dim = dim
|
|
self.num_heads = num_heads
|
|
self.head_dim = head_dim
|
|
self.head_start = head_start
|
|
self.local_attn_size = local_attn_size
|
|
self.sink_size = sink_size
|
|
self.qk_norm = qk_norm
|
|
self.eps = eps
|
|
self.parallel_attention = parallel_attention
|
|
|
|
# Scaled dot product attention
|
|
self.attn = LocalAttention(
|
|
num_heads=num_heads,
|
|
head_size=self.head_dim,
|
|
dropout_rate=0,
|
|
softmax_scale=None,
|
|
causal=False,
|
|
supported_attention_backends=(
|
|
AttentionBackendEnum.FA,
|
|
AttentionBackendEnum.AITER,
|
|
AttentionBackendEnum.TORCH_SDPA,
|
|
),
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
q: torch.Tensor,
|
|
k: torch.Tensor,
|
|
v: torch.Tensor,
|
|
freqs_cis: tuple[torch.Tensor, torch.Tensor],
|
|
block_mask: BlockMask,
|
|
kv_cache: CausalSelfAttentionKVCache | None = None,
|
|
current_start: int = 0,
|
|
cache_start: int | None = None,
|
|
):
|
|
r"""
|
|
Args:
|
|
x(Tensor): Shape [B, L, num_heads, C / num_heads]
|
|
seq_lens(Tensor): Shape [B]
|
|
grid_sizes(Tensor): Shape [B, 3], the second dimension contains (F, H, W)
|
|
freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2]
|
|
"""
|
|
cos, sin = freqs_cis
|
|
roped_query = _apply_rotary_emb(q, cos, sin, is_neox_style=False).type_as(v)
|
|
roped_key = _apply_rotary_emb(k, cos, sin, is_neox_style=False).type_as(v)
|
|
|
|
if kv_cache is None:
|
|
# Padding for flex attention
|
|
padded_length = math.ceil(q.shape[1] / 128) * 128 - q.shape[1]
|
|
padded_roped_query = torch.cat(
|
|
[
|
|
roped_query,
|
|
torch.zeros(
|
|
[q.shape[0], padded_length, q.shape[2], q.shape[3]],
|
|
device=q.device,
|
|
dtype=v.dtype,
|
|
),
|
|
],
|
|
dim=1,
|
|
)
|
|
|
|
padded_roped_key = torch.cat(
|
|
[
|
|
roped_key,
|
|
torch.zeros(
|
|
[k.shape[0], padded_length, k.shape[2], k.shape[3]],
|
|
device=k.device,
|
|
dtype=v.dtype,
|
|
),
|
|
],
|
|
dim=1,
|
|
)
|
|
|
|
padded_v = torch.cat(
|
|
[
|
|
v,
|
|
torch.zeros(
|
|
[v.shape[0], padded_length, v.shape[2], v.shape[3]],
|
|
device=v.device,
|
|
dtype=v.dtype,
|
|
),
|
|
],
|
|
dim=1,
|
|
)
|
|
|
|
x = flex_attention(
|
|
query=padded_roped_query.transpose(2, 1),
|
|
key=padded_roped_key.transpose(2, 1),
|
|
value=padded_v.transpose(2, 1),
|
|
block_mask=block_mask,
|
|
)[:, :, :-padded_length].transpose(2, 1)
|
|
else:
|
|
if kv_cache.can_direct_current_attention(roped_key.shape[1]):
|
|
return self.attn(roped_query, roped_key, v)
|
|
|
|
cache_view = kv_cache.update_and_get_attention_kv(
|
|
key=roped_key,
|
|
value=v,
|
|
current_chunk_start=current_start,
|
|
cache_head_start=self.head_start,
|
|
debug_name="CausalWan KV cache",
|
|
)
|
|
x = self.attn(
|
|
roped_query,
|
|
cache_view.k,
|
|
cache_view.v,
|
|
)
|
|
|
|
return x
|
|
|
|
|
|
class CausalWanTransformerBlock(nn.Module):
|
|
def __init__(
|
|
self,
|
|
dim: int,
|
|
ffn_dim: int,
|
|
num_heads: int,
|
|
local_attn_size: int = -1,
|
|
sink_size: int = 0,
|
|
qk_norm: str = "rms_norm_across_heads",
|
|
cross_attn_norm: bool = False,
|
|
eps: float = 1e-6,
|
|
added_kv_proj_dim: int | None = None,
|
|
supported_attention_backends: set[AttentionBackendEnum] | None = None,
|
|
prefix: str = "",
|
|
quant_config: QuantizationConfig | None = None,
|
|
):
|
|
super().__init__()
|
|
|
|
# 1. Self-attention
|
|
self.norm1 = FP32LayerNorm(dim, eps, elementwise_affine=False)
|
|
use_megatron_tp = getattr(
|
|
self, "_use_megatron_tp", type(self) is CausalWanTransformerBlock
|
|
)
|
|
if use_megatron_tp:
|
|
self.to_q = ColumnParallelLinear(
|
|
dim,
|
|
dim,
|
|
bias=True,
|
|
gather_output=False,
|
|
quant_config=quant_config,
|
|
prefix=add_prefix("to_q", prefix),
|
|
)
|
|
self.to_k = ColumnParallelLinear(
|
|
dim,
|
|
dim,
|
|
bias=True,
|
|
gather_output=False,
|
|
quant_config=quant_config,
|
|
prefix=add_prefix("to_k", prefix),
|
|
)
|
|
self.to_v = ColumnParallelLinear(
|
|
dim,
|
|
dim,
|
|
bias=True,
|
|
gather_output=False,
|
|
quant_config=quant_config,
|
|
prefix=add_prefix("to_v", prefix),
|
|
)
|
|
|
|
self.to_out = RowParallelLinear(
|
|
dim,
|
|
dim,
|
|
bias=True,
|
|
input_is_parallel=True,
|
|
quant_config=quant_config,
|
|
prefix=add_prefix("to_out", prefix),
|
|
)
|
|
# megatron-style tp shards the weight (qkv) column-wise, effectively splitting the attention heads
|
|
tp_size = get_tp_world_size()
|
|
self.local_num_heads = divide(num_heads, tp_size)
|
|
head_start = get_tp_rank() * self.local_num_heads
|
|
else:
|
|
self.to_q = ReplicatedLinear(dim, dim, bias=True, quant_config=quant_config)
|
|
self.to_k = ReplicatedLinear(dim, dim, bias=True, quant_config=quant_config)
|
|
self.to_v = ReplicatedLinear(dim, dim, bias=True, quant_config=quant_config)
|
|
self.to_out = ReplicatedLinear(
|
|
dim, dim, bias=True, quant_config=quant_config
|
|
)
|
|
tp_size = 1
|
|
self.local_num_heads = num_heads
|
|
head_start = 0
|
|
dim_head = dim // num_heads
|
|
self.attn1 = CausalWanSelfAttention(
|
|
dim,
|
|
self.local_num_heads,
|
|
local_attn_size=local_attn_size,
|
|
sink_size=sink_size,
|
|
qk_norm=qk_norm,
|
|
eps=eps,
|
|
head_dim=dim_head,
|
|
head_start=head_start,
|
|
)
|
|
self.hidden_dim = dim
|
|
self.num_attention_heads = num_heads
|
|
self.local_attn_size = local_attn_size
|
|
self.dim_head = dim_head
|
|
if qk_norm == "rms_norm":
|
|
self.norm_q = RMSNorm(dim_head, eps=eps)
|
|
self.norm_k = RMSNorm(dim_head, eps=eps)
|
|
elif qk_norm == "rms_norm_across_heads":
|
|
# LTX applies qk norm across all heads
|
|
self.norm_q = RMSNorm(dim, eps=eps)
|
|
self.norm_k = RMSNorm(dim, eps=eps)
|
|
else:
|
|
print("QK Norm type not supported")
|
|
raise Exception
|
|
self.tp_rmsnorm = (
|
|
use_megatron_tp and qk_norm == "rms_norm_across_heads" and tp_size > 1
|
|
)
|
|
assert cross_attn_norm is True
|
|
self.self_attn_residual_norm = ScaleResidualLayerNormScaleShift(
|
|
dim, eps=eps, elementwise_affine=True, dtype=torch.float32
|
|
)
|
|
|
|
# 2. Cross-attention
|
|
# Only T2V for now
|
|
cross_attn_backends = {
|
|
b for b in supported_attention_backends if not b.is_sparse
|
|
}
|
|
self.attn2 = WanT2VCrossAttention(
|
|
dim,
|
|
num_heads,
|
|
qk_norm=qk_norm,
|
|
eps=eps,
|
|
supported_attention_backends=cross_attn_backends,
|
|
quant_config=quant_config,
|
|
)
|
|
self.cross_attn_residual_norm = ScaleResidualLayerNormScaleShift(
|
|
dim, eps=eps, elementwise_affine=False, dtype=torch.float32
|
|
)
|
|
|
|
# 3. Feed-forward
|
|
self.ffn = MLP(
|
|
dim, ffn_dim, act_type="gelu_pytorch_tanh", quant_config=quant_config
|
|
)
|
|
self.mlp_residual = MulAdd()
|
|
|
|
self.scale_shift_table = nn.Parameter(torch.randn(1, 6, dim) / dim**0.5)
|
|
|
|
def forward(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
encoder_hidden_states: torch.Tensor,
|
|
temb: torch.Tensor,
|
|
freqs_cis: tuple[torch.Tensor, torch.Tensor],
|
|
block_mask: BlockMask,
|
|
kv_cache: CausalSelfAttentionKVCache | None = None,
|
|
crossattn_cache: CrossAttentionKVCache | None = None,
|
|
current_start: int = 0,
|
|
cache_start: int | None = None,
|
|
) -> torch.Tensor:
|
|
# hidden_states.shape: [batch_size, seq_length, inner_dim]
|
|
# temb.shape: [batch_size, num_frames, 6, inner_dim]
|
|
if hidden_states.dim() == 4:
|
|
hidden_states = hidden_states.squeeze(1)
|
|
num_frames = temb.shape[1]
|
|
frame_seqlen = hidden_states.shape[1] // num_frames
|
|
bs, seq_length, _ = hidden_states.shape
|
|
orig_dtype = hidden_states.dtype
|
|
# assert orig_dtype != torch.float32
|
|
e = self.scale_shift_table + temb.float()
|
|
# e.shape: [batch_size, num_frames, 6, inner_dim]
|
|
assert e.shape == (bs, num_frames, 6, self.hidden_dim)
|
|
shift_msa, scale_msa, gate_msa, c_shift_msa, c_scale_msa, c_gate_msa = e.chunk(
|
|
6, dim=2
|
|
)
|
|
# *_msa.shape: [batch_size, num_frames, 1, inner_dim]
|
|
assert shift_msa.dtype == torch.float32
|
|
|
|
# 1. Self-attention
|
|
norm_hidden_states = (
|
|
(
|
|
self.norm1(hidden_states.float()).unflatten(
|
|
dim=1, sizes=(num_frames, frame_seqlen)
|
|
)
|
|
* (1 + scale_msa)
|
|
+ shift_msa
|
|
)
|
|
.flatten(1, 2)
|
|
.to(orig_dtype)
|
|
)
|
|
query, _ = self.to_q(norm_hidden_states)
|
|
key, _ = self.to_k(norm_hidden_states)
|
|
value, _ = self.to_v(norm_hidden_states)
|
|
|
|
if self.norm_q is not None:
|
|
if self.tp_rmsnorm:
|
|
query = tensor_parallel_rms_norm(query, self.norm_q)
|
|
else:
|
|
query = self.norm_q(query)
|
|
if self.norm_k is not None:
|
|
if self.tp_rmsnorm:
|
|
key = tensor_parallel_rms_norm(key, self.norm_k)
|
|
else:
|
|
key = self.norm_k(key)
|
|
|
|
query = query.squeeze(1).unflatten(2, (self.local_num_heads, self.dim_head))
|
|
key = key.squeeze(1).unflatten(2, (self.local_num_heads, self.dim_head))
|
|
value = value.squeeze(1).unflatten(2, (self.local_num_heads, self.dim_head))
|
|
|
|
attn_output = self.attn1(
|
|
query,
|
|
key,
|
|
value,
|
|
freqs_cis,
|
|
block_mask,
|
|
kv_cache,
|
|
current_start,
|
|
cache_start,
|
|
)
|
|
attn_output = attn_output.flatten(2)
|
|
attn_output, _ = self.to_out(attn_output)
|
|
attn_output = attn_output.squeeze(1)
|
|
|
|
null_shift = null_scale = torch.zeros(
|
|
(1,), device=hidden_states.device, dtype=hidden_states.dtype
|
|
)
|
|
norm_hidden_states, hidden_states = self.self_attn_residual_norm(
|
|
hidden_states, attn_output, gate_msa, null_shift, null_scale
|
|
)
|
|
norm_hidden_states, hidden_states = norm_hidden_states.to(
|
|
orig_dtype
|
|
), hidden_states.to(orig_dtype)
|
|
|
|
# 2. Cross-attention
|
|
attn_output = self.attn2(
|
|
norm_hidden_states,
|
|
context=encoder_hidden_states,
|
|
context_lens=None,
|
|
crossattn_cache=crossattn_cache,
|
|
)
|
|
norm_hidden_states, hidden_states = self.cross_attn_residual_norm(
|
|
hidden_states, attn_output, 1, c_shift_msa, c_scale_msa
|
|
)
|
|
norm_hidden_states, hidden_states = norm_hidden_states.to(
|
|
orig_dtype
|
|
), hidden_states.to(orig_dtype)
|
|
|
|
# 3. Feed-forward
|
|
ff_output = self.ffn(norm_hidden_states)
|
|
hidden_states = self.mlp_residual(ff_output, c_gate_msa, hidden_states)
|
|
hidden_states = hidden_states.to(orig_dtype)
|
|
|
|
return hidden_states
|
|
|
|
|
|
class CausalWanTransformer3DModel(BaseDiT, LayerwiseOffloadableModuleMixin):
|
|
_fsdp_shard_conditions = WanVideoConfig()._fsdp_shard_conditions
|
|
_compile_conditions = WanVideoConfig()._compile_conditions
|
|
_supported_attention_backends = WanVideoConfig()._supported_attention_backends
|
|
param_names_mapping = WanVideoConfig().param_names_mapping
|
|
reverse_param_names_mapping = WanVideoConfig().reverse_param_names_mapping
|
|
lora_param_names_mapping = WanVideoConfig().lora_param_names_mapping
|
|
|
|
def __init__(
|
|
self,
|
|
config: WanVideoConfig,
|
|
hf_config: dict[str, Any],
|
|
quant_config: QuantizationConfig | None = None,
|
|
) -> None:
|
|
super().__init__(config=config, hf_config=hf_config)
|
|
|
|
inner_dim = config.num_attention_heads * config.attention_head_dim
|
|
self.hidden_size = config.hidden_size
|
|
self.num_attention_heads = config.num_attention_heads
|
|
self.attention_head_dim = config.attention_head_dim
|
|
self.in_channels = config.in_channels
|
|
self.out_channels = config.out_channels
|
|
self.num_channels_latents = config.num_channels_latents
|
|
self.patch_size = config.patch_size
|
|
self.text_len = config.text_len
|
|
self.local_attn_size = config.local_attn_size
|
|
|
|
# 1. Patch & position embedding
|
|
self.patch_embedding = PatchEmbed(
|
|
in_chans=config.in_channels,
|
|
embed_dim=inner_dim,
|
|
patch_size=config.patch_size,
|
|
flatten=False,
|
|
)
|
|
|
|
# 2. Condition embeddings
|
|
self.condition_embedder = WanTimeTextImageEmbedding(
|
|
dim=inner_dim,
|
|
time_freq_dim=config.freq_dim,
|
|
text_embed_dim=config.text_dim,
|
|
image_embed_dim=config.image_dim,
|
|
)
|
|
|
|
# 3. Transformer blocks
|
|
self.blocks = nn.ModuleList(
|
|
[
|
|
CausalWanTransformerBlock(
|
|
inner_dim,
|
|
config.ffn_dim,
|
|
config.num_attention_heads,
|
|
config.local_attn_size,
|
|
config.sink_size,
|
|
config.qk_norm,
|
|
config.cross_attn_norm,
|
|
config.eps,
|
|
config.added_kv_proj_dim,
|
|
self._supported_attention_backends,
|
|
prefix=f"{config.prefix}.blocks.{i}",
|
|
quant_config=quant_config,
|
|
)
|
|
for i in range(config.num_layers)
|
|
]
|
|
)
|
|
|
|
# 4. Output norm & projection
|
|
self.norm_out = LayerNormScaleShift(
|
|
inner_dim,
|
|
eps=config.eps,
|
|
elementwise_affine=False,
|
|
dtype=torch.float32,
|
|
)
|
|
self.proj_out = nn.Linear(
|
|
inner_dim, config.out_channels * math.prod(config.patch_size)
|
|
)
|
|
self.scale_shift_table = nn.Parameter(
|
|
torch.randn(1, 2, inner_dim) / inner_dim**0.5
|
|
)
|
|
|
|
self.gradient_checkpointing = False
|
|
|
|
# Causal-specific
|
|
self.block_mask = None
|
|
self.num_frame_per_block = config.arch_config.num_frames_per_block
|
|
assert self.num_frame_per_block <= 3
|
|
self.independent_first_frame = False
|
|
|
|
self.__post_init__()
|
|
|
|
self.layer_names = [
|
|
"blocks",
|
|
]
|
|
|
|
@staticmethod
|
|
def _prepare_blockwise_causal_attn_mask(
|
|
device: torch.device | str,
|
|
num_frames: int = 21,
|
|
frame_seqlen: int = 1560,
|
|
num_frame_per_block=1,
|
|
local_attn_size=-1,
|
|
) -> BlockMask:
|
|
"""
|
|
we will divide the token sequence into the following format
|
|
[1 latent frame] [1 latent frame] ... [1 latent frame]
|
|
We use flexattention to construct the attention mask
|
|
"""
|
|
total_length = num_frames * frame_seqlen
|
|
|
|
# we do right padding to get to a multiple of 128
|
|
padded_length = math.ceil(total_length / 128) * 128 - total_length
|
|
|
|
ends = torch.zeros(
|
|
total_length + padded_length, device=device, dtype=torch.long
|
|
)
|
|
|
|
# Block-wise causal mask will attend to all elements that are before the end of the current chunk
|
|
frame_indices = torch.arange(
|
|
start=0,
|
|
end=total_length,
|
|
step=frame_seqlen * num_frame_per_block,
|
|
device=device,
|
|
)
|
|
|
|
for tmp in frame_indices:
|
|
ends[tmp : tmp + frame_seqlen * num_frame_per_block] = (
|
|
tmp + frame_seqlen * num_frame_per_block
|
|
)
|
|
|
|
def attention_mask(b, h, q_idx, kv_idx):
|
|
if local_attn_size == -1:
|
|
return (kv_idx < ends[q_idx]) | (q_idx == kv_idx)
|
|
else:
|
|
return (
|
|
(kv_idx < ends[q_idx])
|
|
& (kv_idx >= (ends[q_idx] - local_attn_size * frame_seqlen))
|
|
) | (q_idx == kv_idx)
|
|
# return ((kv_idx < total_length) & (q_idx < total_length)) | (q_idx == kv_idx) # bidirectional mask
|
|
|
|
block_mask = create_block_mask(
|
|
attention_mask,
|
|
B=None,
|
|
H=None,
|
|
Q_LEN=total_length + padded_length,
|
|
KV_LEN=total_length + padded_length,
|
|
_compile=False,
|
|
device=device,
|
|
)
|
|
|
|
if not dist.is_initialized() or dist.get_rank() == 0:
|
|
print(
|
|
f" cache a block wise causal mask with block size of {num_frame_per_block} frames"
|
|
)
|
|
print(block_mask)
|
|
|
|
# import imageio
|
|
# import numpy as np
|
|
# from torch.nn.attention.flex_attention import create_mask
|
|
|
|
# mask = create_mask(attention_mask, B=None, H=None, Q_LEN=total_length +
|
|
# padded_length, KV_LEN=total_length + padded_length, device=device)
|
|
# import cv2
|
|
# mask = cv2.resize(mask[0, 0].cpu().float().numpy(), (1024, 1024))
|
|
# imageio.imwrite("mask_%d.jpg" % (0), np.uint8(255. * mask))
|
|
|
|
return block_mask
|
|
|
|
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,
|
|
kv_cache: list[CausalSelfAttentionKVCache] | None = None,
|
|
crossattn_cache: list[CrossAttentionKVCache] | None = None,
|
|
current_start: int = 0,
|
|
cache_start: int = 0,
|
|
start_frame: int = 0,
|
|
) -> torch.Tensor:
|
|
r"""
|
|
Run the diffusion model with kv caching.
|
|
See Algorithm 2 of CausVid paper https://arxiv.org/abs/2412.07772 for details.
|
|
This function will be run for num_frame times.
|
|
Process the latent frames one by one (1560 tokens each)
|
|
"""
|
|
|
|
orig_dtype = hidden_states.dtype
|
|
if not isinstance(encoder_hidden_states, torch.Tensor):
|
|
encoder_hidden_states = encoder_hidden_states[0]
|
|
if (
|
|
isinstance(encoder_hidden_states_image, list)
|
|
and len(encoder_hidden_states_image) > 0
|
|
):
|
|
encoder_hidden_states_image = encoder_hidden_states_image[0]
|
|
else:
|
|
encoder_hidden_states_image = None
|
|
|
|
batch_size, num_channels, num_frames, height, width = hidden_states.shape
|
|
p_t, p_h, p_w = self.patch_size
|
|
post_patch_num_frames = num_frames // p_t
|
|
post_patch_height = height // p_h
|
|
post_patch_width = width // p_w
|
|
|
|
# Get rotary embeddings
|
|
d = self.hidden_size // self.num_attention_heads
|
|
rope_dim_list = [d - 4 * (d // 6), 2 * (d // 6), 2 * (d // 6)]
|
|
freqs_cos, freqs_sin = get_rotary_pos_embed(
|
|
(
|
|
post_patch_num_frames * get_sp_world_size(),
|
|
post_patch_height,
|
|
post_patch_width,
|
|
),
|
|
self.hidden_size,
|
|
self.num_attention_heads,
|
|
rope_dim_list,
|
|
dtype=(
|
|
torch.float64
|
|
if current_platform.is_float64_supported()
|
|
else torch.float32
|
|
),
|
|
rope_theta=10000,
|
|
start_frame=start_frame, # Assume that start_frame is 0 when kv_cache is None
|
|
)
|
|
freqs_cos = freqs_cos.to(hidden_states.device)
|
|
freqs_sin = freqs_sin.to(hidden_states.device)
|
|
freqs_cis = (
|
|
(freqs_cos.float(), freqs_sin.float()) if freqs_cos is not None else None
|
|
)
|
|
|
|
hidden_states = self.patch_embedding(hidden_states)
|
|
hidden_states = hidden_states.flatten(2).transpose(1, 2)
|
|
|
|
(
|
|
temb,
|
|
timestep_proj,
|
|
encoder_hidden_states,
|
|
encoder_hidden_states_image,
|
|
) = self.condition_embedder(
|
|
timestep.flatten(), encoder_hidden_states, encoder_hidden_states_image
|
|
)
|
|
timestep_proj = timestep_proj.unflatten(1, (6, self.hidden_size)).unflatten(
|
|
dim=0, sizes=timestep.shape
|
|
)
|
|
|
|
if encoder_hidden_states_image is not None:
|
|
encoder_hidden_states = torch.concat(
|
|
[encoder_hidden_states_image, encoder_hidden_states], dim=1
|
|
)
|
|
|
|
encoder_hidden_states = (
|
|
encoder_hidden_states.to(orig_dtype)
|
|
if current_platform.is_mps()
|
|
else encoder_hidden_states
|
|
) # cast to orig_dtype for MPS
|
|
|
|
assert encoder_hidden_states.dtype == orig_dtype
|
|
|
|
# 4. Transformer blocks
|
|
for block_index, block in enumerate(self.blocks):
|
|
if torch.is_grad_enabled() and self.gradient_checkpointing:
|
|
causal_kwargs = {
|
|
"kv_cache": kv_cache[block_index],
|
|
"current_start": current_start,
|
|
"cache_start": cache_start,
|
|
"block_mask": self.block_mask,
|
|
}
|
|
hidden_states = self._gradient_checkpointing_func(
|
|
block,
|
|
hidden_states,
|
|
encoder_hidden_states,
|
|
timestep_proj,
|
|
freqs_cis,
|
|
**causal_kwargs,
|
|
)
|
|
else:
|
|
causal_kwargs = {
|
|
"kv_cache": kv_cache[block_index],
|
|
"crossattn_cache": crossattn_cache[block_index],
|
|
"current_start": current_start,
|
|
"cache_start": cache_start,
|
|
"block_mask": self.block_mask,
|
|
}
|
|
hidden_states = block(
|
|
hidden_states,
|
|
encoder_hidden_states,
|
|
timestep_proj,
|
|
freqs_cis,
|
|
**causal_kwargs,
|
|
)
|
|
|
|
# 5. Output norm, projection & unpatchify
|
|
temb = temb.unflatten(dim=0, sizes=timestep.shape).unsqueeze(2)
|
|
shift, scale = (self.scale_shift_table.unsqueeze(1) + temb).chunk(2, dim=2)
|
|
hidden_states = self.norm_out(hidden_states, shift, scale)
|
|
hidden_states = self.proj_out(hidden_states)
|
|
|
|
hidden_states = hidden_states.reshape(
|
|
batch_size,
|
|
post_patch_num_frames,
|
|
post_patch_height,
|
|
post_patch_width,
|
|
p_t,
|
|
p_h,
|
|
p_w,
|
|
-1,
|
|
)
|
|
hidden_states = hidden_states.permute(0, 7, 1, 4, 2, 5, 3, 6)
|
|
output = hidden_states.flatten(6, 7).flatten(4, 5).flatten(2, 3)
|
|
|
|
return output
|
|
|
|
|
|
EntryClass = CausalWanTransformer3DModel
|