Skip to content
628 changes: 0 additions & 628 deletions scripts/convert_cosmos3_to_diffusers.py

This file was deleted.

142 changes: 112 additions & 30 deletions src/diffusers/models/transformers/transformer_cosmos3.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,39 @@
# limitations under the License.

import math
from dataclasses import dataclass

import torch
import torch.nn as nn

from ...configuration_utils import ConfigMixin, register_to_config
from ...loaders import PeftAdapterMixin
from ...utils import BaseOutput
from ..attention import AttentionMixin, AttentionModuleMixin
from ..attention_dispatch import dispatch_attention_fn
from ..embeddings import TimestepEmbedding, Timesteps
from ..modeling_utils import ModelMixin
from ..normalization import RMSNorm


@dataclass
class Cosmos3OmniTransformerOutput(BaseOutput):
"""Output of [`Cosmos3OmniTransformer`].

Args:
sample (`list[torch.Tensor]`):
Per-item vision velocity predictions.
sound (`list[torch.Tensor]`, *optional*):
Per-item sound velocity predictions when sound generation is enabled.
action (`list[torch.Tensor]`, *optional*):
Per-item action velocity predictions when action generation is enabled.
"""

sample: list[torch.Tensor]
sound: list[torch.Tensor] | None = None
action: list[torch.Tensor] | None = None


class Cosmos3AttnProcessor:
"""Dual-pathway attention processor for Cosmos3.

Expand Down Expand Up @@ -139,15 +159,35 @@ def forward(self, position_ids, device, dtype):
return emb.cos().to(dtype=dtype), emb.sin().to(dtype=dtype) # each: [B,N,head_dim]


class Cosmos3NemotronRMSNorm(nn.Module):
def __init__(self, dim: int, eps: float):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))

def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
input_dtype = hidden_states.dtype
hidden_states = hidden_states.float()
variance = hidden_states.pow(2).mean(-1, keepdim=True)
hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
return (self.weight.float() * hidden_states).to(input_dtype)


class Cosmos3VLTextMLP(nn.Module):
def __init__(self, hidden_size: int, intermediate_size: int):
def __init__(self, hidden_size: int, intermediate_size: int, hidden_act: str = "silu"):
super().__init__()
self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
if hidden_act not in ("relu2", "silu"):
raise ValueError(f"Cosmos3 only supports `hidden_act` values 'relu2' and 'silu', got {hidden_act!r}.")
self.hidden_act = hidden_act
if hidden_act == "silu":
self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
self.up_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
self.down_proj = nn.Linear(intermediate_size, hidden_size, bias=False)
self.act_fn = nn.SiLU()
self.act_fn = nn.SiLU() if hidden_act == "silu" else None

def forward(self, x):
if self.hidden_act == "relu2":
return self.down_proj(torch.relu(self.up_proj(x)).square())
return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))


Expand Down Expand Up @@ -183,11 +223,11 @@ def forward(self, x: torch.Tensor, domain_id: torch.Tensor) -> torch.Tensor:


class Cosmos3PackedMoTAttention(nn.Module, AttentionModuleMixin):
"""Dual-pathway packed attention for Qwen3VL MoT — separate projections for
understanding (causal) and generation (full) token streams."""
"""Dual-pathway packed attention with separate projections for the understanding and generation token streams."""

_default_processor_cls = Cosmos3AttnProcessor
_available_processors = [Cosmos3AttnProcessor]
_supports_qkv_fusion = False

def __init__(
self,
Expand All @@ -197,6 +237,8 @@ def __init__(
num_key_value_heads: int,
attention_bias: bool,
rms_norm_eps: float,
qk_norm_for_text: bool = True,
norm_type: str = "rms_norm",
processor=None,
):
super().__init__()
Expand All @@ -212,16 +254,27 @@ def __init__(
self.to_k = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias)
self.to_v = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias)
self.to_out = nn.Linear(num_attention_heads * head_dim, hidden_size, bias=attention_bias)
self.norm_q = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.norm_k = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)
if not qk_norm_for_text:
self.norm_q = nn.Identity()
self.norm_k = nn.Identity()
elif norm_type == "nemotron_rms_norm":
self.norm_q = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps)
self.norm_k = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps)
else:
self.norm_q = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.norm_k = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)

# Generation pathway
self.add_q_proj = nn.Linear(hidden_size, num_attention_heads * head_dim, bias=attention_bias)
self.add_k_proj = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias)
self.add_v_proj = nn.Linear(hidden_size, num_key_value_heads * head_dim, bias=attention_bias)
self.to_add_out = nn.Linear(num_attention_heads * head_dim, hidden_size, bias=attention_bias)
self.norm_added_q = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.norm_added_k = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)
if norm_type == "nemotron_rms_norm":
self.norm_added_q = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps)
self.norm_added_k = Cosmos3NemotronRMSNorm(head_dim, eps=rms_norm_eps)
else:
self.norm_added_q = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.norm_added_k = RMSNorm(head_dim, eps=rms_norm_eps, elementwise_affine=True, bias=False)

if processor is None:
processor = self._default_processor_cls()
Expand All @@ -237,12 +290,7 @@ def forward(


class Cosmos3VLTextMoTDecoderLayer(nn.Module):
"""
Qwen3VL text MoT (Mixture of Tokens) decoder layer. Features dual-pathway attention for understanding vs
generation.

This is used for both Dense and MoE models.
"""
"""Cosmos3 text MoT decoder layer for the Qwen3 and Nemotron dense backbones."""

def __init__(
self,
Expand All @@ -253,28 +301,43 @@ def __init__(
intermediate_size: int,
attention_bias: bool,
rms_norm_eps: float,
hidden_act: str = "silu",
qk_norm_for_text: bool = True,
):
super().__init__()
self.hidden_size = hidden_size
norm_type = "nemotron_rms_norm" if hidden_act == "relu2" else "rms_norm"
self.self_attn = Cosmos3PackedMoTAttention(
hidden_size=hidden_size,
head_dim=head_dim,
num_attention_heads=num_attention_heads,
num_key_value_heads=num_key_value_heads,
attention_bias=attention_bias,
rms_norm_eps=rms_norm_eps,
qk_norm_for_text=qk_norm_for_text,
norm_type=norm_type,
)

self.mlp = Cosmos3VLTextMLP(hidden_size=hidden_size, intermediate_size=intermediate_size)
self.mlp_moe_gen = Cosmos3VLTextMLP(hidden_size=hidden_size, intermediate_size=intermediate_size)

self.input_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.input_layernorm_moe_gen = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.post_attention_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.post_attention_layernorm_moe_gen = RMSNorm(
hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False
self.mlp = Cosmos3VLTextMLP(
hidden_size=hidden_size, intermediate_size=intermediate_size, hidden_act=hidden_act
)
self.mlp_moe_gen = Cosmos3VLTextMLP(
hidden_size=hidden_size, intermediate_size=intermediate_size, hidden_act=hidden_act
)

if norm_type == "nemotron_rms_norm":
self.input_layernorm = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps)
self.input_layernorm_moe_gen = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps)
self.post_attention_layernorm = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps)
self.post_attention_layernorm_moe_gen = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps)
else:
self.input_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.input_layernorm_moe_gen = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.post_attention_layernorm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.post_attention_layernorm_moe_gen = RMSNorm(
hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False
)

def forward(
self,
und_seq: torch.Tensor,
Expand Down Expand Up @@ -335,10 +398,16 @@ def __init__(
sound_latent_fps: float = 25.0,
timestep_scale: float = 0.001,
vocab_size: int = 151936,
hidden_act: str = "silu",
qk_norm_for_text: bool = True,
rope_axes_dim: tuple[int, int, int] | list[int] | None = None,
):
super().__init__()

rope_axes_dim = rope_scaling.get("mrope_section", [24, 20, 20]) if rope_scaling is not None else [24, 20, 20]
if rope_axes_dim is None:
rope_axes_dim = (
rope_scaling.get("mrope_section", [24, 20, 20]) if rope_scaling is not None else [24, 20, 20]
)
self.register_to_config(rope_axes_dim=rope_axes_dim)

# Text-model layers live directly on the transformer (flat layout). The published
Expand All @@ -355,12 +424,18 @@ def __init__(
intermediate_size=intermediate_size,
attention_bias=attention_bias,
rms_norm_eps=rms_norm_eps,
hidden_act=hidden_act,
qk_norm_for_text=qk_norm_for_text,
)
for _ in range(num_hidden_layers)
]
)
self.norm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.norm_moe_gen = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
if hidden_act == "relu2":
self.norm = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps)
self.norm_moe_gen = Cosmos3NemotronRMSNorm(hidden_size, eps=rms_norm_eps)
else:
self.norm = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.norm_moe_gen = RMSNorm(hidden_size, eps=rms_norm_eps, elementwise_affine=True, bias=False)
self.rotary_emb = Cosmos3VLTextRotaryEmbedding(
head_dim=head_dim, rope_theta=rope_theta, rope_axes_dim=rope_axes_dim
)
Expand Down Expand Up @@ -577,7 +652,10 @@ def forward(
action_timesteps: torch.Tensor | None = None,
action_noisy_frame_indexes: list[torch.Tensor] | None = None,
action_domain_ids: list[torch.Tensor] | None = None,
) -> tuple[list[torch.Tensor], list[torch.Tensor] | None, list[torch.Tensor] | None]:
return_dict: bool = True,
) -> (
Cosmos3OmniTransformerOutput | tuple[list[torch.Tensor], list[torch.Tensor] | None, list[torch.Tensor] | None]
):
"""Run a full denoising-step forward pass.

Args:
Expand Down Expand Up @@ -605,10 +683,11 @@ def forward(
action_timesteps: Optional per-token diffusion timesteps for action tokens.
action_noisy_frame_indexes: Optional noisy frame indices per action item.
action_domain_ids: Optional per-item domain IDs selecting the action head weights.
return_dict: Whether to return a [`Cosmos3OmniTransformerOutput`] instead of a tuple.

Returns:
``(preds_vision, preds_sound, preds_action)`` — lists of per-modality predictions. Optional modalities
return ``None`` when their inputs are omitted.
A [`Cosmos3OmniTransformerOutput`] or a tuple of per-modality prediction lists. Optional modalities return
``None`` when their inputs are omitted.
"""
has_sound = sound_tokens is not None and sound_sequence_indexes is not None
has_action = action_tokens is not None and action_sequence_indexes is not None
Expand Down Expand Up @@ -722,4 +801,7 @@ def forward(
preds_action_packed, action_token_shapes, action_noisy_frame_indexes
)

return preds_vision, preds_sound, preds_action
if not return_dict:
return preds_vision, preds_sound, preds_action

return Cosmos3OmniTransformerOutput(sample=preds_vision, sound=preds_sound, action=preds_action)
17 changes: 15 additions & 2 deletions src/diffusers/modular_pipelines/cosmos/before_denoise.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import copy

import numpy as np
import torch

from ...models.transformers.transformer_cosmos3 import Cosmos3OmniTransformer
from ...pipelines.cosmos.pipeline_cosmos3_omni import _EMBODIMENT_TO_DOMAIN_ID, CosmosActionCondition
from ...schedulers import UniPCMultistepScheduler
from ...utils.torch_utils import randn_tensor
from ..modular_pipeline import ModularPipelineBlocks, PipelineState
from ..modular_pipeline_utils import ComponentSpec, InputParam, OutputParam
from ..modular_pipeline_utils import ComponentSpec, ConfigSpec, InputParam, OutputParam
from .modular_pipeline import Cosmos3OmniModularPipeline


Expand Down Expand Up @@ -936,6 +937,10 @@ def description(self) -> str:
def expected_components(self) -> list[ComponentSpec]:
return [ComponentSpec("scheduler", UniPCMultistepScheduler)]

@property
def expected_configs(self) -> list[ConfigSpec]:
return [ConfigSpec(name="use_native_flow_schedule", default=False)]

@property
def inputs(self) -> list[InputParam]:
return [
Expand All @@ -953,7 +958,15 @@ def intermediate_outputs(self) -> list[OutputParam]:
def __call__(self, components: Cosmos3OmniModularPipeline, state: PipelineState) -> PipelineState:
block_state = self.get_block_state(state)
device = components._execution_device
components.scheduler.set_timesteps(block_state.num_inference_steps, device=device)
if components.config.use_native_flow_schedule:
sigmas = np.linspace(
1.0 - 1.0 / components.scheduler.config.num_train_timesteps,
0.0,
block_state.num_inference_steps + 1,
)[:-1]
components.scheduler.set_timesteps(block_state.num_inference_steps, device=device, sigmas=sigmas)
else:
components.scheduler.set_timesteps(block_state.num_inference_steps, device=device)
block_state.timesteps = components.scheduler.timesteps
block_state.num_warmup_steps = (
len(block_state.timesteps) - block_state.num_inference_steps * components.scheduler.order
Expand Down
3 changes: 2 additions & 1 deletion src/diffusers/modular_pipelines/cosmos/denoise.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def __call__(self, components: Cosmos3OmniModularPipeline, block_state: BlockSta
transformer_kwargs = {
name: value for name, value in transformer_kwargs.items() if name in transformer_args
}
preds_vision, preds_sound, preds_action = components.transformer(**transformer_kwargs)
preds_vision, preds_sound, preds_action = components.transformer(**transformer_kwargs, return_dict=False)
velocities[pass_name] = components._mask_velocity_predictions(
preds_vision,
preds_sound,
Expand Down Expand Up @@ -633,6 +633,7 @@ def _forward(components, static, vision_tokens, vision_timesteps):
vision_mse_loss_indexes=static["vision_mse_loss_indexes"],
vision_timesteps=vision_timesteps,
vision_noisy_frame_indexes=static["vision_noisy_frame_indexes"],
return_dict=False,
)
return preds_vision[-1]

Expand Down
Loading
Loading