Signed-off-by: Abhinay1997 <devarintinagasaiabhinay@gmail.com>
572 lines
23 KiB
Python
572 lines
23 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
# Copyright 2026 Krea AI and The HuggingFace Team. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import math
|
|
from collections.abc import Iterable
|
|
from typing import TYPE_CHECKING
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.nn.functional as F
|
|
from diffusers.models.embeddings import apply_rotary_emb, get_1d_rotary_pos_embed
|
|
from vllm.logger import init_logger
|
|
from vllm.model_executor.layers.linear import ReplicatedLinear
|
|
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
|
|
|
|
from vllm_omni.diffusion.attention.backends.abstract import AttentionMetadata
|
|
from vllm_omni.diffusion.attention.layer import Attention
|
|
|
|
if TYPE_CHECKING:
|
|
from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
|
|
|
|
from vllm_omni.diffusion.data import OmniDiffusionConfig
|
|
|
|
logger = init_logger(__name__)
|
|
|
|
# Devices that do not support float64; RoPE frequencies fall back to float32 there.
|
|
_FP64_UNSUPPORTED_DEVICES = frozenset({"mps", "npu", "neuron"})
|
|
|
|
|
|
def _rope_freqs_dtype(device: torch.device) -> torch.dtype:
|
|
return torch.float32 if device.type in _FP64_UNSUPPORTED_DEVICES else torch.float64
|
|
|
|
|
|
def _join_prefix(prefix: str, name: str) -> str:
|
|
return f"{prefix}.{name}" if prefix else name
|
|
|
|
|
|
def _linear(
|
|
in_features: int,
|
|
out_features: int,
|
|
bias: bool,
|
|
quant_config: "QuantizationConfig | None",
|
|
prefix: str,
|
|
) -> ReplicatedLinear:
|
|
# ReplicatedLinear (not nn.Linear) so the diffusion LoRA manager can wrap these projections.
|
|
return ReplicatedLinear(
|
|
in_features,
|
|
out_features,
|
|
bias=bias,
|
|
quant_config=quant_config,
|
|
prefix=prefix,
|
|
return_bias=False,
|
|
)
|
|
|
|
|
|
class Krea2RMSNorm(nn.Module):
|
|
"""RMSNorm with a zero-centered scale: the effective multiplier is ``1 + weight``, matching the Krea 2 checkpoint
|
|
format. Activations are upcast so the normalization runs in float32."""
|
|
|
|
def __init__(self, dim: int, eps: float = 1e-5) -> None:
|
|
super().__init__()
|
|
self.dim = dim
|
|
self.eps = eps
|
|
self.weight = nn.Parameter(torch.zeros(dim))
|
|
|
|
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
|
dtype = hidden_states.dtype
|
|
hidden_states = F.rms_norm(hidden_states.float(), (self.dim,), weight=self.weight + 1.0, eps=self.eps)
|
|
return hidden_states.to(dtype)
|
|
|
|
|
|
class Krea2Attention(nn.Module):
|
|
"""Self-attention with grouped-query projections, q/k RMSNorm, rotary embeddings and a sigmoid output gate.
|
|
|
|
Q/K/V layout is ``[B, seq, heads, head_dim]``; ``attention_mask`` is a 2D boolean key-padding mask
|
|
``(batch, key_seq_len)`` that the backend broadcasts.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
hidden_size: int,
|
|
num_heads: int,
|
|
num_kv_heads: int | None = None,
|
|
eps: float = 1e-5,
|
|
role: str = "self",
|
|
quant_config: "QuantizationConfig | None" = None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
if hidden_size % num_heads != 0:
|
|
raise ValueError(f"hidden_size={hidden_size} must be divisible by num_heads={num_heads}")
|
|
self.hidden_size = hidden_size
|
|
self.num_heads = num_heads
|
|
self.num_kv_heads = num_kv_heads if num_kv_heads is not None else num_heads
|
|
self.head_dim = hidden_size // num_heads
|
|
|
|
q_dim = self.head_dim * self.num_heads
|
|
kv_dim = self.head_dim * self.num_kv_heads
|
|
self.to_q = _linear(hidden_size, q_dim, False, quant_config, _join_prefix(prefix, "to_q"))
|
|
self.to_k = _linear(hidden_size, kv_dim, False, quant_config, _join_prefix(prefix, "to_k"))
|
|
self.to_v = _linear(hidden_size, kv_dim, False, quant_config, _join_prefix(prefix, "to_v"))
|
|
self.to_gate = _linear(hidden_size, hidden_size, False, quant_config, _join_prefix(prefix, "to_gate"))
|
|
self.norm_q = Krea2RMSNorm(self.head_dim, eps=eps)
|
|
self.norm_k = Krea2RMSNorm(self.head_dim, eps=eps)
|
|
self.to_out = nn.ModuleList(
|
|
[_linear(hidden_size, hidden_size, False, quant_config, _join_prefix(prefix, "to_out.0")), nn.Dropout(0.0)]
|
|
)
|
|
|
|
self.attn = Attention(
|
|
num_heads=self.num_heads,
|
|
head_size=self.head_dim,
|
|
softmax_scale=1.0 / (self.head_dim**0.5),
|
|
causal=False,
|
|
num_kv_heads=self.num_kv_heads,
|
|
role=role,
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
attention_mask: torch.Tensor | None = None,
|
|
image_rotary_emb: tuple[torch.Tensor, torch.Tensor] | None = None,
|
|
) -> torch.Tensor:
|
|
query = self.to_q(hidden_states).unflatten(-1, (self.num_heads, self.head_dim))
|
|
key = self.to_k(hidden_states).unflatten(-1, (self.num_kv_heads, self.head_dim))
|
|
value = self.to_v(hidden_states).unflatten(-1, (self.num_kv_heads, self.head_dim))
|
|
gate = self.to_gate(hidden_states)
|
|
|
|
query = self.norm_q(query)
|
|
key = self.norm_k(key)
|
|
|
|
if image_rotary_emb is not None:
|
|
query = apply_rotary_emb(query, image_rotary_emb, sequence_dim=1)
|
|
key = apply_rotary_emb(key, image_rotary_emb, sequence_dim=1)
|
|
query = query.to(value.dtype)
|
|
key = key.to(value.dtype)
|
|
|
|
attn_metadata = AttentionMetadata(attn_mask=attention_mask) if attention_mask is not None else None
|
|
hidden_states = self.attn(query, key, value, attn_metadata)
|
|
hidden_states = hidden_states.flatten(2, 3)
|
|
hidden_states = hidden_states * torch.sigmoid(gate)
|
|
return self.to_out[0](hidden_states)
|
|
|
|
|
|
class Krea2SwiGLU(nn.Module):
|
|
"""SwiGLU feed-forward network."""
|
|
|
|
def __init__(
|
|
self, dim: int, hidden_dim: int, quant_config: "QuantizationConfig | None" = None, prefix: str = ""
|
|
) -> None:
|
|
super().__init__()
|
|
self.gate = _linear(dim, hidden_dim, False, quant_config, _join_prefix(prefix, "gate"))
|
|
self.up = _linear(dim, hidden_dim, False, quant_config, _join_prefix(prefix, "up"))
|
|
self.down = _linear(hidden_dim, dim, False, quant_config, _join_prefix(prefix, "down"))
|
|
|
|
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
|
return self.down(F.silu(self.gate(hidden_states)) * self.up(hidden_states))
|
|
|
|
|
|
class Krea2TextFusionBlock(nn.Module):
|
|
"""Pre-norm transformer block (no rotary embeddings, no time modulation) used by the text fusion stage."""
|
|
|
|
def __init__(
|
|
self,
|
|
dim: int,
|
|
num_heads: int,
|
|
num_kv_heads: int,
|
|
intermediate_size: int,
|
|
eps: float,
|
|
quant_config: "QuantizationConfig | None" = None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
self.norm1 = Krea2RMSNorm(dim, eps=eps)
|
|
self.norm2 = Krea2RMSNorm(dim, eps=eps)
|
|
self.attn = Krea2Attention(
|
|
dim,
|
|
num_heads,
|
|
num_kv_heads,
|
|
eps=eps,
|
|
role="self",
|
|
quant_config=quant_config,
|
|
prefix=_join_prefix(prefix, "attn"),
|
|
)
|
|
self.ff = Krea2SwiGLU(dim, intermediate_size, quant_config=quant_config, prefix=_join_prefix(prefix, "ff"))
|
|
|
|
def forward(self, hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None) -> torch.Tensor:
|
|
hidden_states = hidden_states + self.attn(self.norm1(hidden_states), attention_mask=attention_mask)
|
|
hidden_states = hidden_states + self.ff(self.norm2(hidden_states))
|
|
return hidden_states
|
|
|
|
|
|
class Krea2TextFusion(nn.Module):
|
|
"""Fuses the stack of tapped text-encoder hidden states into a single sequence of text features.
|
|
|
|
Two ``layerwise_blocks`` attend across the ``num_text_layers`` axis independently for every token, a linear
|
|
``projector`` collapses that axis, and two ``refiner_blocks`` attend across the token sequence.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
num_text_layers: int,
|
|
dim: int,
|
|
num_heads: int,
|
|
num_kv_heads: int,
|
|
intermediate_size: int,
|
|
num_layerwise_blocks: int,
|
|
num_refiner_blocks: int,
|
|
eps: float,
|
|
quant_config: "QuantizationConfig | None" = None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
self.layerwise_blocks = nn.ModuleList(
|
|
[
|
|
Krea2TextFusionBlock(
|
|
dim,
|
|
num_heads,
|
|
num_kv_heads,
|
|
intermediate_size,
|
|
eps,
|
|
quant_config=quant_config,
|
|
prefix=_join_prefix(prefix, f"layerwise_blocks.{i}"),
|
|
)
|
|
for i in range(num_layerwise_blocks)
|
|
]
|
|
)
|
|
self.projector = _linear(num_text_layers, 1, False, quant_config, _join_prefix(prefix, "projector"))
|
|
self.refiner_blocks = nn.ModuleList(
|
|
[
|
|
Krea2TextFusionBlock(
|
|
dim,
|
|
num_heads,
|
|
num_kv_heads,
|
|
intermediate_size,
|
|
eps,
|
|
quant_config=quant_config,
|
|
prefix=_join_prefix(prefix, f"refiner_blocks.{i}"),
|
|
)
|
|
for i in range(num_refiner_blocks)
|
|
]
|
|
)
|
|
|
|
def forward(self, encoder_hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None) -> torch.Tensor:
|
|
batch_size, seq_len, num_text_layers, dim = encoder_hidden_states.shape
|
|
|
|
hidden_states = encoder_hidden_states.reshape(batch_size * seq_len, num_text_layers, dim)
|
|
for block in self.layerwise_blocks:
|
|
hidden_states = block(hidden_states.contiguous())
|
|
|
|
hidden_states = hidden_states.reshape(batch_size, seq_len, num_text_layers, dim).permute(0, 1, 3, 2)
|
|
hidden_states = self.projector(hidden_states).squeeze(-1)
|
|
|
|
for block in self.refiner_blocks:
|
|
hidden_states = block(hidden_states, attention_mask=attention_mask)
|
|
|
|
return hidden_states
|
|
|
|
|
|
class Krea2TransformerBlock(nn.Module):
|
|
def __init__(
|
|
self,
|
|
hidden_size: int,
|
|
intermediate_size: int,
|
|
num_heads: int,
|
|
num_kv_heads: int,
|
|
norm_eps: float,
|
|
quant_config: "QuantizationConfig | None" = None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
self.scale_shift_table = nn.Parameter(torch.zeros(6, hidden_size))
|
|
self.norm1 = Krea2RMSNorm(hidden_size, eps=norm_eps)
|
|
self.norm2 = Krea2RMSNorm(hidden_size, eps=norm_eps)
|
|
self.attn = Krea2Attention(
|
|
hidden_size,
|
|
num_heads,
|
|
num_kv_heads,
|
|
eps=norm_eps,
|
|
role="self",
|
|
quant_config=quant_config,
|
|
prefix=_join_prefix(prefix, "attn"),
|
|
)
|
|
self.ff = Krea2SwiGLU(
|
|
hidden_size, intermediate_size, quant_config=quant_config, prefix=_join_prefix(prefix, "ff")
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
temb: torch.Tensor,
|
|
image_rotary_emb: tuple[torch.Tensor, torch.Tensor],
|
|
attention_mask: torch.Tensor | None = None,
|
|
) -> torch.Tensor:
|
|
# temb: (B, 1, 6 * hidden_size), shared across all blocks; each block only learns an additive table.
|
|
modulation = temb.unflatten(-1, (6, -1)) + self.scale_shift_table
|
|
prescale, preshift, pregate, postscale, postshift, postgate = modulation.unbind(-2)
|
|
|
|
attn_out = self.attn(
|
|
(1.0 + prescale) * self.norm1(hidden_states) + preshift,
|
|
attention_mask=attention_mask,
|
|
image_rotary_emb=image_rotary_emb,
|
|
)
|
|
hidden_states = hidden_states + pregate * attn_out
|
|
ff_out = self.ff((1.0 + postscale) * self.norm2(hidden_states) + postshift)
|
|
hidden_states = hidden_states + postgate * ff_out
|
|
return hidden_states
|
|
|
|
|
|
class Krea2TimestepEmbedding(nn.Module):
|
|
"""Sinusoidal flow-time embedding (cos-first, input scaled by 1000) followed by a two-layer MLP.
|
|
|
|
Keeps the sequence dimension at size 1 so the per-block modulations broadcast over tokens.
|
|
"""
|
|
|
|
def __init__(
|
|
self, embed_dim: int, hidden_size: int, quant_config: "QuantizationConfig | None" = None, prefix: str = ""
|
|
) -> None:
|
|
super().__init__()
|
|
self.embed_dim = embed_dim
|
|
self.linear_1 = _linear(embed_dim, hidden_size, True, quant_config, _join_prefix(prefix, "linear_1"))
|
|
self.linear_2 = _linear(hidden_size, hidden_size, True, quant_config, _join_prefix(prefix, "linear_2"))
|
|
|
|
def forward(self, timestep: torch.Tensor, dtype: torch.dtype) -> torch.Tensor:
|
|
half = self.embed_dim // 2
|
|
freqs = torch.exp(-math.log(1e4) * torch.arange(half, dtype=torch.float32, device=timestep.device) / half)
|
|
args = (timestep.float() * 1e3)[:, None, None] * freqs
|
|
emb = torch.cat([torch.cos(args), torch.sin(args)], dim=-1).to(dtype)
|
|
return self.linear_2(F.gelu(self.linear_1(emb), approximate="tanh"))
|
|
|
|
|
|
class Krea2TextProjection(nn.Module):
|
|
"""Projects the fused text features into the transformer width."""
|
|
|
|
def __init__(
|
|
self,
|
|
text_dim: int,
|
|
hidden_size: int,
|
|
eps: float,
|
|
quant_config: "QuantizationConfig | None" = None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
self.norm = Krea2RMSNorm(text_dim, eps=eps)
|
|
self.linear_1 = _linear(text_dim, hidden_size, True, quant_config, _join_prefix(prefix, "linear_1"))
|
|
self.linear_2 = _linear(hidden_size, hidden_size, True, quant_config, _join_prefix(prefix, "linear_2"))
|
|
|
|
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
|
hidden_states = self.linear_1(self.norm(hidden_states))
|
|
return self.linear_2(F.gelu(hidden_states, approximate="tanh"))
|
|
|
|
|
|
class Krea2FinalLayer(nn.Module):
|
|
"""Final adaptive RMSNorm and output projection."""
|
|
|
|
def __init__(
|
|
self,
|
|
hidden_size: int,
|
|
out_channels: int,
|
|
eps: float,
|
|
quant_config: "QuantizationConfig | None" = None,
|
|
prefix: str = "",
|
|
) -> None:
|
|
super().__init__()
|
|
self.scale_shift_table = nn.Parameter(torch.zeros(2, hidden_size))
|
|
self.norm = Krea2RMSNorm(hidden_size, eps=eps)
|
|
self.linear = _linear(hidden_size, out_channels, True, quant_config, _join_prefix(prefix, "linear"))
|
|
|
|
def forward(self, hidden_states: torch.Tensor, temb: torch.Tensor) -> torch.Tensor:
|
|
modulation = temb + self.scale_shift_table
|
|
scale, shift = modulation.chunk(2, dim=1)
|
|
hidden_states = (1.0 + scale) * self.norm(hidden_states) + shift
|
|
return self.linear(hidden_states)
|
|
|
|
|
|
class Krea2RotaryPosEmbed(nn.Module):
|
|
"""Multi-axis (t, h, w) rotary position embedding, following the Flux/Krea 2 convention."""
|
|
|
|
def __init__(self, theta: int, axes_dim: list[int]):
|
|
super().__init__()
|
|
self.theta = theta
|
|
self.axes_dim = axes_dim
|
|
|
|
def forward(self, ids: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
|
n_axes = ids.shape[-1]
|
|
cos_out = []
|
|
sin_out = []
|
|
pos = ids.float()
|
|
freqs_dtype = _rope_freqs_dtype(ids.device)
|
|
for i in range(n_axes):
|
|
cos, sin = get_1d_rotary_pos_embed(
|
|
self.axes_dim[i],
|
|
pos[:, i],
|
|
theta=self.theta,
|
|
repeat_interleave_real=True,
|
|
use_real=True,
|
|
freqs_dtype=freqs_dtype,
|
|
)
|
|
cos_out.append(cos)
|
|
sin_out.append(sin)
|
|
freqs_cos = torch.cat(cos_out, dim=-1).to(ids.device)
|
|
freqs_sin = torch.cat(sin_out, dim=-1).to(ids.device)
|
|
return freqs_cos, freqs_sin
|
|
|
|
|
|
class Krea2Transformer2DModel(nn.Module):
|
|
r"""The single-stream MMDiT flow-matching backbone used by the Krea 2 pipeline (vLLM-Omni port).
|
|
|
|
Text conditioning enters as a stack of hidden states tapped from several layers of a multimodal text encoder. A
|
|
small text-fusion transformer collapses the layer axis and refines the token sequence; the result is concatenated
|
|
with the patchified image latents into a single ``[text, image]`` sequence processed by the transformer blocks.
|
|
The timestep conditions every block through one shared modulation vector plus per-block learned tables.
|
|
"""
|
|
|
|
_repeated_blocks = ["Krea2TransformerBlock"]
|
|
_layerwise_offload_blocks_attrs = ["transformer_blocks"]
|
|
|
|
@staticmethod
|
|
def _is_transformer_block(name: str, module) -> bool:
|
|
return "transformer_blocks" in name and name.split(".")[-1].isdigit()
|
|
|
|
_hsdp_shard_conditions = [_is_transformer_block]
|
|
|
|
def __init__(
|
|
self,
|
|
in_channels: int = 64,
|
|
num_layers: int = 28,
|
|
attention_head_dim: int = 128,
|
|
num_attention_heads: int = 48,
|
|
num_key_value_heads: int = 12,
|
|
intermediate_size: int = 16384,
|
|
timestep_embed_dim: int = 256,
|
|
text_hidden_dim: int = 2560,
|
|
num_text_layers: int = 12,
|
|
text_num_attention_heads: int = 20,
|
|
text_num_key_value_heads: int = 20,
|
|
text_intermediate_size: int = 6912,
|
|
num_layerwise_text_blocks: int = 2,
|
|
num_refiner_text_blocks: int = 2,
|
|
axes_dims_rope: tuple[int, int, int] = (32, 48, 48),
|
|
rope_theta: float = 1000.0,
|
|
norm_eps: float = 1e-5,
|
|
od_config: "OmniDiffusionConfig | None" = None,
|
|
quant_config: "QuantizationConfig | None" = None,
|
|
) -> None:
|
|
super().__init__()
|
|
# The pipeline reads ``self.dtype`` to cast inputs before the transformer forward.
|
|
self.dtype = od_config.dtype if od_config is not None else torch.get_default_dtype()
|
|
self.od_config = od_config
|
|
|
|
hidden_size = attention_head_dim * num_attention_heads
|
|
if sum(axes_dims_rope) != attention_head_dim:
|
|
raise ValueError(
|
|
f"sum(axes_dims_rope)={sum(axes_dims_rope)} must equal attention_head_dim={attention_head_dim}"
|
|
)
|
|
|
|
self.in_channels = in_channels
|
|
self.out_channels = in_channels
|
|
self.hidden_size = hidden_size
|
|
self.num_layers = num_layers
|
|
|
|
self.img_in = _linear(in_channels, hidden_size, True, quant_config, "img_in")
|
|
self.time_embed = Krea2TimestepEmbedding(timestep_embed_dim, hidden_size, quant_config, "time_embed")
|
|
self.time_mod_proj = _linear(hidden_size, 6 * hidden_size, True, quant_config, "time_mod_proj")
|
|
self.text_fusion = Krea2TextFusion(
|
|
num_text_layers=num_text_layers,
|
|
dim=text_hidden_dim,
|
|
num_heads=text_num_attention_heads,
|
|
num_kv_heads=text_num_key_value_heads,
|
|
intermediate_size=text_intermediate_size,
|
|
num_layerwise_blocks=num_layerwise_text_blocks,
|
|
num_refiner_blocks=num_refiner_text_blocks,
|
|
eps=norm_eps,
|
|
quant_config=quant_config,
|
|
prefix="text_fusion",
|
|
)
|
|
self.txt_in = Krea2TextProjection(text_hidden_dim, hidden_size, norm_eps, quant_config, "txt_in")
|
|
self.rotary_emb = Krea2RotaryPosEmbed(theta=rope_theta, axes_dim=list(axes_dims_rope))
|
|
|
|
self.transformer_blocks = nn.ModuleList(
|
|
[
|
|
Krea2TransformerBlock(
|
|
hidden_size=hidden_size,
|
|
intermediate_size=intermediate_size,
|
|
num_heads=num_attention_heads,
|
|
num_kv_heads=num_key_value_heads,
|
|
norm_eps=norm_eps,
|
|
quant_config=quant_config,
|
|
prefix=f"transformer_blocks.{i}",
|
|
)
|
|
for i in range(num_layers)
|
|
]
|
|
)
|
|
|
|
self.final_layer = Krea2FinalLayer(hidden_size, in_channels, norm_eps, quant_config, "final_layer")
|
|
|
|
def forward(
|
|
self,
|
|
hidden_states: torch.Tensor,
|
|
encoder_hidden_states: torch.Tensor,
|
|
timestep: torch.Tensor,
|
|
position_ids: torch.Tensor,
|
|
encoder_attention_mask: torch.Tensor | None = None,
|
|
) -> torch.Tensor:
|
|
r"""Predict the flow-matching velocity for the image tokens.
|
|
|
|
Args:
|
|
hidden_states: Packed (patchified) noisy image latents ``(batch, image_seq_len, in_channels)``.
|
|
encoder_hidden_states: Tapped text-encoder hidden states
|
|
``(batch, text_seq_len, num_text_layers, text_hidden_dim)``.
|
|
timestep: Flow-matching time in ``[0, 1]`` of shape ``(batch,)``.
|
|
position_ids: ``(t, h, w)`` rotary coordinates ``(text_seq_len + image_seq_len, 3)``.
|
|
encoder_attention_mask: Boolean mask marking valid text tokens ``(batch, text_seq_len)`` or ``None``.
|
|
"""
|
|
if position_ids.ndim != 2 or position_ids.shape[-1] != 3:
|
|
raise ValueError(f"`position_ids` must have shape (sequence_length, 3), got {tuple(position_ids.shape)}.")
|
|
|
|
batch_size, image_seq_len, _ = hidden_states.shape
|
|
|
|
temb = self.time_embed(timestep, dtype=hidden_states.dtype)
|
|
temb_mod = self.time_mod_proj(F.gelu(temb, approximate="tanh"))
|
|
|
|
# 2D key-padding masks: padded text tokens are excluded as keys; their lanes are dropped at the output slice.
|
|
text_attention_mask = None
|
|
attention_mask = None
|
|
if encoder_attention_mask is not None:
|
|
text_attention_mask = encoder_attention_mask.bool()
|
|
image_mask = text_attention_mask.new_ones((batch_size, image_seq_len))
|
|
attention_mask = torch.cat([text_attention_mask, image_mask], dim=1)
|
|
|
|
encoder_hidden_states = self.text_fusion(encoder_hidden_states, attention_mask=text_attention_mask)
|
|
encoder_hidden_states = self.txt_in(encoder_hidden_states)
|
|
|
|
text_seq_len = encoder_hidden_states.shape[1]
|
|
hidden_states = self.img_in(hidden_states)
|
|
hidden_states = torch.cat([encoder_hidden_states, hidden_states], dim=1)
|
|
|
|
image_rotary_emb = self.rotary_emb(position_ids)
|
|
|
|
for block in self.transformer_blocks:
|
|
hidden_states = block(hidden_states, temb_mod, image_rotary_emb, attention_mask)
|
|
|
|
hidden_states = hidden_states[:, text_seq_len:]
|
|
return self.final_layer(hidden_states, temb)
|
|
|
|
def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
|
|
# Module/parameter names mirror the diffusers ``Krea2Transformer2DModel`` exactly, so weights map 1:1.
|
|
params_dict = dict(self.named_parameters())
|
|
loaded_params: set[str] = set()
|
|
for name, loaded_weight in weights:
|
|
if name not in params_dict:
|
|
continue
|
|
param = params_dict[name]
|
|
weight_loader = getattr(param, "weight_loader", default_weight_loader)
|
|
weight_loader(param, loaded_weight)
|
|
loaded_params.add(name)
|
|
return loaded_params
|