Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

635 lines
22 KiB
Python

"""Krea-2 (K2) single-stream MMDiT.
Text and image tokens are concatenated into a single joint-attention stream. The
model uses GQA attention with a sigmoid output gate, 6-way shared adaLN
modulation, a text-fusion transformer that fuses the selected text-encoder
hidden-state layers into one, and interleaved 3-axis RoPE. Module and parameter
names follow the released K2 checkpoint, so weights load without remapping.
"""
import math
import os
from typing import Any, Optional
import torch
import torch.nn as nn
import torch.nn.functional as F
from einops import rearrange
from torch import Tensor
from sglang.multimodal_gen.configs.models.dits.krea2 import Krea2DitConfig
from sglang.multimodal_gen.runtime.distributed import (
get_sp_world_size,
get_tp_world_size,
)
from sglang.multimodal_gen.runtime.layers.attention import USPAttention
from sglang.multimodal_gen.runtime.layers.attention.layer import build_varlen_mask_meta
from sglang.multimodal_gen.runtime.layers.linear import (
ColumnParallelLinear,
RowParallelLinear,
)
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.utils.logging_utils import init_logger
logger = init_logger(__name__)
# --------------------------------------------------------------------------- #
# Functional helpers
# --------------------------------------------------------------------------- #
def rope(pos: Tensor, dim: int, theta: float = 1e4, ntk: float = 1.0) -> Tensor:
scale = torch.arange(0, dim, 2, dtype=torch.float64, device=pos.device) / dim
omega = 1.0 / ((theta * ntk) ** scale)
out = torch.einsum("...n,d->...nd", pos, omega)
out = torch.stack(
[torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1
)
out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2)
return out.float()
def ropeapply(xq: Tensor, xk: Tensor, freqs: Tensor) -> tuple[Tensor, Tensor]:
xq_ = xq.float().reshape(*xq.shape[:-1], -1, 1, 2)
xk_ = xk.float().reshape(*xk.shape[:-1], -1, 1, 2)
freqs = freqs[:, None, :, :, :]
xq_ = freqs[..., 0] * xq_[..., 0] + freqs[..., 1] * xq_[..., 1]
xk_ = freqs[..., 0] * xk_[..., 0] + freqs[..., 1] * xk_[..., 1]
return xq_.reshape(*xq.shape).to(xq.dtype), xk_.reshape(*xk.shape).to(xk.dtype)
def _fused_qknorm_rope_enabled() -> bool:
return os.getenv("SGLANG_ENABLE_FUSED_QKNORM_ROPE", "1").lower() not in (
"0",
"false",
"off",
"no",
)
def _can_use_fused_qknorm_rope(head_dim: int, dtype: torch.dtype) -> bool:
from sglang.jit_kernel.diffusion.qknorm_rope import (
can_use_fused_inplace_qknorm_rope,
)
return can_use_fused_inplace_qknorm_rope(head_dim, head_dim, False, dtype)
def _qknorm_rope_cos_sin_cache(freqs: Tensor) -> Tensor:
"""``[num_tokens, head_dim]`` cos|sin cache for the fused QKNorm+RoPE kernel.
K2's ``rope`` packs each token's rotation as ``[[cos, -sin], [sin, cos]]`` in a
``[B, N, head_dim//2, 2, 2]`` tensor; the kernel wants the per-token cosines then
sines concatenated. Positions come from the image grid (batch-invariant), so the
first batch row is representative.
"""
return torch.cat([freqs[0, :, :, 0, 0], freqs[0, :, :, 1, 0]], dim=-1).float()
def temb(
t: Tensor,
dim: int,
period: float = 1e4,
tfactor: float = 1e3,
device: torch.device = None,
dtype: torch.dtype = None,
) -> Tensor:
half = dim // 2
freqs = torch.exp(
-math.log(period)
* torch.arange(half, dtype=torch.float32, device=device)
/ half
)
args = (t.float() * tfactor)[:, None, None] * freqs
sin, cos = torch.sin(args), torch.cos(args)
return torch.cat((cos, sin), dim=-1).to(dtype=dtype)
def norm_scale_shift(
x: Tensor, weight: Tensor, scale: Tensor, shift: Tensor, eps: float
) -> Tensor:
"""Fused RMSNorm + modulation: ``rms_norm(x) * weight * (1 + scale) + shift``.
``weight`` is the effective RMSNorm weight (K2 stores ``scale``, so callers
pass ``scale + 1``), kept off the checkpoint so the identity load is unaffected.
"""
if x.is_cuda and x.shape[-1] % 256 == 0:
from sglang.jit_kernel.diffusion.cutedsl.scale_residual_norm_scale_shift import (
fused_norm_scale_shift,
)
return fused_norm_scale_shift(
x.contiguous(),
weight.contiguous(),
None,
scale.contiguous(),
shift.contiguous(),
"rms",
eps,
)
normed = F.rms_norm(x.float(), (x.shape[-1],), weight=weight.float(), eps=eps)
return (normed.to(x.dtype) * (1 + scale) + shift).to(x.dtype)
# --------------------------------------------------------------------------- #
# Submodules
# --------------------------------------------------------------------------- #
class TimeEmbed(nn.Module):
"""Timestep embedding MLP: linear_1 -> gelu(tanh) -> linear_2."""
def __init__(self, in_dim: int, dim: int):
super().__init__()
self.linear_1 = nn.Linear(in_dim, dim)
self.linear_2 = nn.Linear(dim, dim)
def forward(self, x: Tensor) -> Tensor:
return self.linear_2(F.gelu(self.linear_1(x), approximate="tanh"))
class TxtIn(nn.Module):
"""Text-context projection: rms-norm -> linear_1 -> gelu(tanh) -> linear_2."""
def __init__(self, txt_dim: int, dim: int):
super().__init__()
self.norm = RMSNorm(txt_dim)
self.linear_1 = nn.Linear(txt_dim, dim)
self.linear_2 = nn.Linear(dim, dim)
def forward(self, x: Tensor) -> Tensor:
return self.linear_2(F.gelu(self.linear_1(self.norm(x)), approximate="tanh"))
class PositionalEncoding(nn.Module):
def __init__(self, dim, axdims: list[int], theta: float = 1e2, ntk: float = 1.0):
super().__init__()
self.axdims = axdims
self.theta = theta
self.ntk = ntk
def forward(self, pos: Tensor) -> Tensor:
return torch.cat(
[
rope(pos[..., i], d, self.theta, self.ntk)
for i, d in enumerate(self.axdims)
],
dim=-3,
)
class RMSNorm(nn.Module):
"""RMSNorm with effective scale ``weight + 1`` (``weight`` initialized to 0),
computed in fp32. The parameter is named ``weight`` to match the released
checkpoint; the ``+ 1`` is applied in the forward."""
def __init__(self, features: int, eps: float = 1e-05, device: torch.device = None):
super().__init__()
self.features = features
self.eps = eps
self.weight = nn.Parameter(
torch.zeros(features, device=device, dtype=torch.float32)
)
def forward(self, x: Tensor) -> Tensor:
t, dtype = x.float(), x.dtype
t = F.rms_norm(
t, (self.features,), eps=self.eps, weight=(self.weight.float() + 1.0)
)
return t.to(dtype)
class SwiGLU(nn.Module):
def __init__(
self, features: int, multiplier: int, bias: bool = False, multiple: int = 128
):
super().__init__()
mlpdim = int(2 * features / 3) * multiplier
mlpdim = multiple * ((mlpdim + multiple - 1) // multiple)
# Tensor-parallel: gate/up shard the hidden dim by column, down all-reduces.
self.gate = ColumnParallelLinear(
features, mlpdim, bias=bias, gather_output=False
)
self.up = ColumnParallelLinear(features, mlpdim, bias=bias, gather_output=False)
self.down = RowParallelLinear(
mlpdim, features, bias=bias, input_is_parallel=True
)
def forward(self, x: Tensor) -> Tensor:
gate, _ = self.gate(x)
up, _ = self.up(x)
out, _ = self.down(F.silu(gate) * up)
return out
class Attention(nn.Module):
def __init__(self, dim: int, heads: int, kvheads: int = None, bias: bool = False):
super().__init__()
self.heads = heads
self.kvheads = kvheads if kvheads is not None else heads
self.headdim = dim // self.heads
# Tensor-parallel: q/k/v/gate shard heads by column, to_out all-reduces.
# Parameter names match the released checkpoint (to_q/to_k/to_v/to_gate,
# norm_q/norm_k, to_out.0) so the checkpoint loads with an identity mapping.
tp = get_tp_world_size()
assert (
self.heads % tp == 0 and self.kvheads % tp == 0
), f"heads={self.heads}, kvheads={self.kvheads} must be divisible by tp={tp}"
self.local_heads = self.heads // tp
self.local_kvheads = self.kvheads // tp
self.to_q = ColumnParallelLinear(
dim, self.headdim * self.heads, bias=bias, gather_output=False
)
self.to_k = ColumnParallelLinear(
dim, self.headdim * self.kvheads, bias=bias, gather_output=False
)
self.to_v = ColumnParallelLinear(
dim, self.headdim * self.kvheads, bias=bias, gather_output=False
)
self.to_gate = ColumnParallelLinear(dim, dim, bias=bias, gather_output=False)
self.norm_q = RMSNorm(self.headdim)
self.norm_k = RMSNorm(self.headdim)
# to_out is a ModuleList ([linear]) so the param is to_out.0.weight, matching
# the diffusers Attention layout in the released checkpoint.
self.to_out = nn.ModuleList(
[RowParallelLinear(dim, dim, bias=bias, input_is_parallel=True)]
)
# Native GQA flash via the platform backend; parameterless.
self.attn = USPAttention(
num_heads=self.local_heads,
head_size=self.headdim,
num_kv_heads=self.local_kvheads,
dropout_rate=0,
softmax_scale=None,
causal=False,
)
def forward(
self,
qkv: Tensor,
freqs: Tensor | None = None,
key_mask: Tensor | None = None,
mask_meta: dict | None = None,
num_replicated_prefix: int = 0,
skip_sequence_parallel: bool = False,
) -> Tensor:
q, _ = self.to_q(qkv)
k, _ = self.to_k(qkv)
v, _ = self.to_v(qkv)
gate, _ = self.to_gate(qkv)
hd = self.headdim
# Fast path: fuse RMSNorm(q), RMSNorm(k) and RoPE into one in-place kernel on
# the [B, S, H, D] layout USPAttention consumes (also skips the [B, H, L, D]
# transpose round-trip the eager path needs). Eager fallback below preserves
# parity off CUDA / for unsupported dtypes.
if (
freqs is not None
and q.is_cuda
and q.dtype in (torch.float16, torch.bfloat16)
and _fused_qknorm_rope_enabled()
and _can_use_fused_qknorm_rope(hd, q.dtype)
):
from sglang.jit_kernel.diffusion.qknorm_rope import (
fused_inplace_qknorm_rope,
)
b, s = qkv.shape[0], qkv.shape[1]
q = q.view(b, s, self.local_heads, hd)
k = k.view(b, s, self.local_kvheads, hd)
v = v.view(b, s, self.local_kvheads, hd)
positions = torch.arange(s, device=q.device, dtype=torch.long)
if b > 1:
positions = positions.repeat(b)
fused_inplace_qknorm_rope(
q.reshape(-1, self.local_heads, hd),
k.reshape(-1, self.local_kvheads, hd),
(self.norm_q.weight.float() + 1.0).to(q.dtype),
(self.norm_k.weight.float() + 1.0).to(k.dtype),
_qknorm_rope_cos_sin_cache(freqs),
positions,
is_neox=False,
eps=self.norm_q.eps,
head_dim=hd,
rope_dim=hd,
)
out = self.attn(
q,
k,
v,
attn_mask=key_mask,
attn_mask_meta=mask_meta,
num_replicated_prefix=num_replicated_prefix,
skip_sequence_parallel_override=skip_sequence_parallel,
).flatten(2)
else:
q, k, v = (
rearrange(q, "B L (H D) -> B H L D", H=self.local_heads),
rearrange(k, "B L (H D) -> B H L D", H=self.local_kvheads),
rearrange(v, "B L (H D) -> B H L D", H=self.local_kvheads),
)
q, k = self.norm_q(q), self.norm_k(k)
if freqs is not None:
q, k = ropeapply(q, k, freqs)
# USPAttention expects [B, S, H, D]; a [B, S] key mask + varlen metadata
# routes a ragged batch through the FA varlen fast path, else maskless.
out = self.attn(
q.transpose(1, 2).contiguous(),
k.transpose(1, 2).contiguous(),
v.transpose(1, 2).contiguous(),
attn_mask=key_mask,
attn_mask_meta=mask_meta,
num_replicated_prefix=num_replicated_prefix,
skip_sequence_parallel_override=skip_sequence_parallel,
).flatten(2)
out, _ = self.to_out[0](out * F.sigmoid(gate))
return out
class LastLayer(nn.Module):
def __init__(self, features: int, patch: int, channels: int):
super().__init__()
self.norm = RMSNorm(features)
self.linear = nn.Linear(features, patch * patch * channels, bias=True)
self.scale_shift_table = nn.Parameter(torch.zeros(2, features))
def forward(self, x: Tensor, tvec: Tensor) -> Tensor:
mod = tvec + rearrange(self.scale_shift_table, "two d -> 1 two d")
scale, shift = mod.chunk(2, dim=1)
x = norm_scale_shift(x, self.norm.weight + 1, scale, shift, self.norm.eps)
x = self.linear(x)
return x
class TextFusionBlock(nn.Module):
def __init__(
self,
features: int,
heads: int,
multiplier: int,
bias: bool = False,
kvheads: int = None,
):
super().__init__()
self.norm1 = RMSNorm(features)
self.norm2 = RMSNorm(features)
self.attn = Attention(dim=features, heads=heads, bias=bias, kvheads=kvheads)
self.ff = SwiGLU(features, multiplier, bias)
def forward(
self,
x: Tensor,
key_mask: Tensor | None = None,
mask_meta: dict | None = None,
) -> Tensor:
# Text-fusion runs on the full replicated text, so skip the SP all-to-all.
x = x + self.attn(
self.norm1(x),
key_mask=key_mask,
mask_meta=mask_meta,
skip_sequence_parallel=True,
)
x = x + self.ff(self.norm2(x))
return x
class TextFusionTransformer(nn.Module):
"""Fuses `num_txt_layers` selected encoder hidden-state layers into one.
Depth is fixed at 2 layerwise + 2 refiner blocks; `num_txt_layers` is the
projector input width (the layer axis), NOT the transformer depth.
"""
def __init__(
self,
num_txt_layers: int,
txt_dim: int,
heads: int,
multiplier: int,
bias: bool = False,
kvheads: int = None,
):
super().__init__()
self.layerwise_blocks = nn.ModuleList(
[
TextFusionBlock(txt_dim, heads, multiplier, bias, kvheads)
for _ in range(2)
]
)
self.projector = nn.Linear(num_txt_layers, 1, bias=False)
self.refiner_blocks = nn.ModuleList(
[
TextFusionBlock(txt_dim, heads, multiplier, bias, kvheads)
for _ in range(2)
]
)
def forward(
self,
x: Tensor,
key_mask: Tensor | None = None,
mask_meta: dict | None = None,
) -> Tensor:
b, l, n, d = x.shape
x = x.reshape(b * l, n, d)
for block in self.layerwise_blocks:
x = block(x.contiguous())
x = rearrange(x, "(b l) n d -> b l d n", b=b, l=l)
x = self.projector(x)
x = x.squeeze(-1)
for block in self.refiner_blocks:
x = block(x, key_mask=key_mask, mask_meta=mask_meta)
return x
class SingleStreamBlock(nn.Module):
def __init__(
self,
features: int,
heads: int,
multiplier: int,
bias: bool = False,
kvheads: int = None,
):
super().__init__()
# (6, features) modulation table added to the timestep projection (AdaLN-single),
# stored directly on the block to match the released checkpoint.
self.scale_shift_table = nn.Parameter(torch.zeros(6, features))
self.norm1 = RMSNorm(features)
self.norm2 = RMSNorm(features)
self.attn = Attention(dim=features, heads=heads, bias=bias, kvheads=kvheads)
self.ff = SwiGLU(features, multiplier, bias)
def forward(
self,
hidden_states: Tensor,
vec: Tensor,
freqs: Tensor,
key_mask: Tensor | None = None,
mask_meta: dict | None = None,
num_replicated_prefix: int = 0,
) -> Tensor:
mod = vec + self.scale_shift_table.reshape(-1)
prescale, preshift, pregate, postscale, postshift, postgate = mod.chunk(
6, dim=-1
)
hidden_states = hidden_states + pregate * self.attn(
norm_scale_shift(
hidden_states,
self.norm1.weight + 1,
prescale,
preshift,
self.norm1.eps,
),
freqs,
key_mask,
mask_meta,
num_replicated_prefix=num_replicated_prefix,
)
hidden_states = hidden_states + postgate * self.ff(
norm_scale_shift(
hidden_states,
self.norm2.weight + 1,
postscale,
postshift,
self.norm2.eps,
)
)
return hidden_states
# --------------------------------------------------------------------------- #
# Top-level model
# --------------------------------------------------------------------------- #
class Krea2Transformer2DModel(CachableDiT, LayerwiseOffloadableModuleMixin):
"""K2 single-stream MMDiT for the SGLang diffusion runtime.
Attribute names follow the released K2 checkpoint, so weights load with an
identity ``param_names_mapping``.
"""
_fsdp_shard_conditions = []
_compile_conditions = []
param_names_mapping = Krea2DitConfig().arch_config.param_names_mapping
reverse_param_names_mapping = {}
def __init__(
self,
config: Krea2DitConfig,
hf_config: dict[str, Any],
quant_config: Optional[Any] = None,
) -> None:
super().__init__(config=config, hf_config=hf_config)
ac = config.arch_config
self.arch_config = ac
self.hidden_size = ac.features
self.num_attention_heads = ac.heads
self.num_channels_latents = ac.channels
self.patch = ac.patch
self.channels = ac.channels
self.tdim = ac.tdim
head_dim = ac.features // ac.heads
axes = list(ac.axes_dims)
assert sum(axes) == head_dim, f"sum(axes)={sum(axes)}, head_dim={head_dim}"
assert all(a % 2 == 0 for a in axes), f"axes={axes}"
self.posemb = PositionalEncoding(ac.features, axes, theta=ac.theta, ntk=1.0)
self.img_in = nn.Linear(ac.channels * ac.patch**2, ac.features, bias=True)
self.transformer_blocks = nn.ModuleList(
[
SingleStreamBlock(
ac.features, ac.heads, ac.multiplier, ac.bias, ac.kvheads
)
for _ in range(ac.layers)
]
)
self.time_embed = TimeEmbed(ac.tdim, ac.features)
self.text_fusion = TextFusionTransformer(
ac.txtlayers,
ac.txtdim,
ac.txtheads,
ac.multiplier,
ac.bias,
ac.txtkvheads,
)
self.txt_in = TxtIn(ac.txtdim, ac.features)
self.final_layer = LastLayer(ac.features, ac.patch, ac.channels)
# GELU(tanh) is applied in the forward; the linear matches time_mod_proj.weight.
self.time_mod_proj = nn.Linear(ac.features, ac.features * 6)
self.seq_multiple_of = ac.seq_multiple_of
# The 28 single-stream blocks (the ~24GB bulk) are streamed layer-by-layer
# under --dit-layerwise-offload, keeping only a small working set resident.
self.layer_names = ["transformer_blocks"]
def _forward_impl(
self,
img: Tensor,
context: Tensor,
t: Tensor,
pos: Tensor,
mask: Tensor | None = None,
) -> Tensor:
img = self.img_in(img)
t = self.time_embed(temb(t, self.tdim, device=img.device, dtype=img.dtype))
tvec = self.time_mod_proj(F.gelu(t, approximate="tanh"))
# A single or same-prompt batch has no padding, so attention runs maskless
# (native-GQA flash). A ragged batch builds varlen metadata from the
# key mask and takes the FA varlen path instead.
txt_key = txt_meta = joint_key = joint_meta = None
if mask is not None and not bool(mask.all()):
txt_key = mask[:, : context.shape[1]]
txt_meta = build_varlen_mask_meta(txt_key)
joint_key = mask
joint_meta = build_varlen_mask_meta(mask)
context = self.text_fusion(context, key_mask=txt_key, mask_meta=txt_meta)
context = self.txt_in(context)
txtlen, imglen = context.shape[1], img.shape[1]
combined = torch.cat((context, img), dim=1)
freqs = self.posemb(pos)
# Under SP the image tokens are sharded across ranks while the text prefix
# stays replicated; keep the leading txtlen tokens out of the all-to-all.
num_replicated_prefix = txtlen if get_sp_world_size() > 1 else 0
for block in self.transformer_blocks:
combined = block(
combined,
tvec,
freqs,
joint_key,
joint_meta,
num_replicated_prefix=num_replicated_prefix,
)
final = self.final_layer(combined, t)
output = final[:, txtlen : txtlen + imglen, :]
return output
def forward(
self,
hidden_states: Tensor,
encoder_hidden_states: Tensor,
timestep: Tensor,
encoder_hidden_states_image=None,
guidance=None,
pos: Tensor = None,
mask: Tensor = None,
**kwargs,
) -> Tensor:
return self._forward_impl(
img=hidden_states,
context=encoder_hidden_states,
t=timestep,
pos=pos,
mask=mask,
)
EntryClass = [Krea2Transformer2DModel]