[Diffusion] MAGI-2: regional compile coverage (#7174)
Signed-off-by: cr-gao <gaochenrui@sjtu.edu.cn>
This commit is contained in:
7 files changed
+674
-81
No files matched your search
@@ -16,6 +16,7 @@ import os
|
||||
from collections.abc import Iterable
|
||||
from enum import IntEnum
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
@@ -28,6 +29,7 @@ from .configuration_magi2 import Magi2PreviewConfig
|
||||
from .layers import (
|
||||
ElementWiseFourierEmbed,
|
||||
MHCHandler,
|
||||
MHCTensorTuple,
|
||||
ModalityDispatcher,
|
||||
MultiModalityRMSNorm,
|
||||
make_grouped_linear,
|
||||
@@ -130,14 +132,12 @@ class Magi2Attention(nn.Module):
|
||||
start = self.tp_group.rank * self.num_heads_q
|
||||
return checkpoint_tensor[:, start : start + self.num_heads_q]
|
||||
|
||||
def forward(
|
||||
def project(
|
||||
self,
|
||||
hidden_states: torch.Tensor,
|
||||
rope: torch.Tensor,
|
||||
varlen_handler: VarlenHandler,
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
cp_split_sizes: list[int] | torch.Tensor,
|
||||
) -> torch.Tensor:
|
||||
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
||||
normalized = self.pre_norm(hidden_states, modality_dispatcher)
|
||||
gates = self.linear_g(normalized, modality_dispatcher)
|
||||
qkv = self.linear_qkv(normalized, modality_dispatcher)
|
||||
@@ -159,8 +159,17 @@ class Magi2Attention(nn.Module):
|
||||
q = apply_rotary_emb(q, cos, sin).squeeze(0).to(self.config.params_dtype)
|
||||
k = apply_rotary_emb(k, cos, sin).squeeze(0).to(self.config.params_dtype)
|
||||
v = v.squeeze(0).to(self.config.params_dtype)
|
||||
return q, k, v, gates
|
||||
|
||||
output = self.packed_attention(
|
||||
def attend(
|
||||
self,
|
||||
q: torch.Tensor,
|
||||
k: torch.Tensor,
|
||||
v: torch.Tensor,
|
||||
varlen_handler: VarlenHandler,
|
||||
cp_split_sizes: list[int] | torch.Tensor,
|
||||
) -> torch.Tensor:
|
||||
return self.packed_attention(
|
||||
q,
|
||||
k,
|
||||
v,
|
||||
@@ -172,7 +181,14 @@ class Magi2Attention(nn.Module):
|
||||
}
|
||||
),
|
||||
)
|
||||
output = modality_dispatcher.permute(output)
|
||||
|
||||
def output(
|
||||
self,
|
||||
attention: torch.Tensor,
|
||||
gates: torch.Tensor,
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
) -> torch.Tensor:
|
||||
output = modality_dispatcher.permute(attention)
|
||||
output = output * torch.sigmoid(gates)
|
||||
output = output.reshape(-1, self.q_size).to(self.config.params_dtype)
|
||||
return self.linear_proj(output, modality_dispatcher)
|
||||
@@ -303,12 +319,21 @@ class Magi2MultiHeadMoELayer(nn.Module):
|
||||
modality.contiguous(), dispatcher
|
||||
)
|
||||
|
||||
def forward(self, hidden_states: torch.Tensor, dispatcher: ModalityDispatcher) -> torch.Tensor:
|
||||
def route_input(
|
||||
self,
|
||||
hidden_states: torch.Tensor,
|
||||
dispatcher: ModalityDispatcher,
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
normalized = self.pre_norm(hidden_states, dispatcher)
|
||||
routed = self.split_linear(normalized)
|
||||
routed = self.moe_mlp(routed)
|
||||
routed = self.merge_linear(routed)
|
||||
return routed + self._shared_experts(normalized, dispatcher)
|
||||
return normalized, self.split_linear(normalized)
|
||||
|
||||
def combine(
|
||||
self,
|
||||
normalized: torch.Tensor,
|
||||
routed: torch.Tensor,
|
||||
dispatcher: ModalityDispatcher,
|
||||
) -> torch.Tensor:
|
||||
return self.merge_linear(routed) + self._shared_experts(normalized, dispatcher)
|
||||
|
||||
|
||||
class Magi2PreAdapter(nn.Module):
|
||||
@@ -339,37 +364,28 @@ class Magi2PreAdapter(nn.Module):
|
||||
def forward(
|
||||
self,
|
||||
packed: torch.Tensor,
|
||||
coords_mapping: torch.Tensor,
|
||||
video_indices: torch.Tensor,
|
||||
audio_indices: torch.Tensor,
|
||||
text_indices: torch.Tensor,
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
rope = self.rope(coords_mapping)
|
||||
output = torch.zeros(
|
||||
) -> torch.Tensor:
|
||||
# Every token belongs to exactly one modality, so the three copies
|
||||
# below cover the whole buffer. Embedding in fp32 and rounding each
|
||||
# modality once matches the released cast at the block boundary.
|
||||
output = torch.empty(
|
||||
packed.shape[0],
|
||||
self.adapter_dim,
|
||||
dtype=torch.float32,
|
||||
dtype=self.config.params_dtype,
|
||||
device=packed.device,
|
||||
)
|
||||
if text_indices.numel():
|
||||
output.index_copy_(
|
||||
0,
|
||||
text_indices,
|
||||
self.text_embedder(packed.index_select(0, text_indices)[:, : self.config.text_in_channels].float()),
|
||||
)
|
||||
if audio_indices.numel():
|
||||
output.index_copy_(
|
||||
0,
|
||||
audio_indices,
|
||||
self.audio_embedder(packed.index_select(0, audio_indices)[:, : self.config.audio_in_channels].float()),
|
||||
)
|
||||
if video_indices.numel():
|
||||
output.index_copy_(
|
||||
0,
|
||||
video_indices,
|
||||
self.video_embedder(packed.index_select(0, video_indices)[:, : self.config.video_in_channels].float()),
|
||||
)
|
||||
return output, rope
|
||||
for indices, embedder, in_channels in (
|
||||
(text_indices, self.text_embedder, self.config.text_in_channels),
|
||||
(audio_indices, self.audio_embedder, self.config.audio_in_channels),
|
||||
(video_indices, self.video_embedder, self.config.video_in_channels),
|
||||
):
|
||||
if indices.numel():
|
||||
tokens = packed[:, :in_channels].index_select(0, indices).float()
|
||||
output.index_copy_(0, indices, embedder(tokens).to(output.dtype))
|
||||
return output
|
||||
|
||||
|
||||
class Magi2PostAdapter(nn.Module):
|
||||
@@ -425,8 +441,10 @@ class Magi2TransformerLayer(nn.Module):
|
||||
self.mlp: nn.Module
|
||||
if layer_index in config.moe.layers:
|
||||
self.mlp = Magi2MultiHeadMoELayer(config)
|
||||
self.region_methods = ("_attention_input", "_moe_input", "_moe_output")
|
||||
else:
|
||||
self.mlp = Magi2MLP(config, num_modality=num_modality)
|
||||
self.region_methods = ("_attention_input", "_dense_output")
|
||||
self._init_mhc(num_modality)
|
||||
|
||||
def _init_mhc(self, num_modality: int) -> None:
|
||||
@@ -525,6 +543,74 @@ class Magi2TransformerLayer(nn.Module):
|
||||
)
|
||||
return self.mhc_handler.hyper_connect(streams, output, post, residual)
|
||||
|
||||
# The layer forward is split into compile regions around the two eager
|
||||
# kernels: packed attention and the multi-head MoE.
|
||||
|
||||
def _attention_input(
|
||||
self,
|
||||
hidden_states: torch.Tensor,
|
||||
rope: torch.Tensor,
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
) -> tuple[torch.Tensor, MHCTensorTuple, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
||||
streams = hidden_states.reshape(hidden_states.shape[0], self.config.mhc.num_streams, self.config.hidden_size)
|
||||
attention_logits = self._branch_logits(streams, "attn", modality_dispatcher)
|
||||
attention_input = self._branch_input(streams, "attn", attention_logits)
|
||||
q, k, v, gates = self.attention.project(attention_input, rope, modality_dispatcher)
|
||||
return streams, attention_logits, q, k, v, gates
|
||||
|
||||
def _mlp_input(
|
||||
self,
|
||||
streams: torch.Tensor,
|
||||
attention: torch.Tensor,
|
||||
gates: torch.Tensor,
|
||||
attention_logits: MHCTensorTuple,
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
) -> tuple[torch.Tensor, MHCTensorTuple, torch.Tensor]:
|
||||
attention_output = self.attention.output(attention, gates, modality_dispatcher)
|
||||
streams = self._connect(streams, attention_output, "attn", attention_logits)
|
||||
mlp_logits = self._branch_logits(streams, "mlp", modality_dispatcher)
|
||||
return streams, mlp_logits, self._branch_input(streams, "mlp", mlp_logits)
|
||||
|
||||
def _dense_output(
|
||||
self,
|
||||
streams: torch.Tensor,
|
||||
attention: torch.Tensor,
|
||||
gates: torch.Tensor,
|
||||
attention_logits: MHCTensorTuple,
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
) -> torch.Tensor:
|
||||
streams, mlp_logits, mlp_input = self._mlp_input(
|
||||
streams, attention, gates, attention_logits, modality_dispatcher
|
||||
)
|
||||
streams = self._connect(streams, self.mlp(mlp_input, modality_dispatcher), "mlp", mlp_logits)
|
||||
return streams.reshape(streams.shape[0], -1)
|
||||
|
||||
def _moe_input(
|
||||
self,
|
||||
streams: torch.Tensor,
|
||||
attention: torch.Tensor,
|
||||
gates: torch.Tensor,
|
||||
attention_logits: MHCTensorTuple,
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
) -> tuple[torch.Tensor, MHCTensorTuple, torch.Tensor, torch.Tensor]:
|
||||
streams, mlp_logits, mlp_input = self._mlp_input(
|
||||
streams, attention, gates, attention_logits, modality_dispatcher
|
||||
)
|
||||
normalized, routed = self.mlp.route_input(mlp_input, modality_dispatcher)
|
||||
return streams, mlp_logits, normalized, routed
|
||||
|
||||
def _moe_output(
|
||||
self,
|
||||
streams: torch.Tensor,
|
||||
mlp_logits: MHCTensorTuple,
|
||||
normalized: torch.Tensor,
|
||||
routed: torch.Tensor,
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
) -> torch.Tensor:
|
||||
mlp_output = self.mlp.combine(normalized, routed, modality_dispatcher)
|
||||
streams = self._connect(streams, mlp_output, "mlp", mlp_logits)
|
||||
return streams.reshape(streams.shape[0], -1)
|
||||
|
||||
def forward(
|
||||
self,
|
||||
hidden_states: torch.Tensor,
|
||||
@@ -533,22 +619,15 @@ class Magi2TransformerLayer(nn.Module):
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
cp_split_sizes: list[int] | torch.Tensor,
|
||||
) -> torch.Tensor:
|
||||
streams = hidden_states.reshape(hidden_states.shape[0], self.config.mhc.num_streams, self.config.hidden_size)
|
||||
attention_logits = self._branch_logits(streams, "attn", modality_dispatcher)
|
||||
attention_input = self._branch_input(streams, "attn", attention_logits)
|
||||
attention_output = self.attention(
|
||||
attention_input,
|
||||
rope,
|
||||
varlen_handler,
|
||||
modality_dispatcher,
|
||||
cp_split_sizes,
|
||||
streams, attention_logits, q, k, v, gates = self._attention_input(hidden_states, rope, modality_dispatcher)
|
||||
attention = self.attention.attend(q, k, v, varlen_handler, cp_split_sizes)
|
||||
if not isinstance(self.mlp, Magi2MultiHeadMoELayer):
|
||||
return self._dense_output(streams, attention, gates, attention_logits, modality_dispatcher)
|
||||
streams, mlp_logits, normalized, routed = self._moe_input(
|
||||
streams, attention, gates, attention_logits, modality_dispatcher
|
||||
)
|
||||
streams = self._connect(streams, attention_output, "attn", attention_logits)
|
||||
mlp_logits = self._branch_logits(streams, "mlp", modality_dispatcher)
|
||||
mlp_input = self._branch_input(streams, "mlp", mlp_logits)
|
||||
mlp_output = self.mlp(mlp_input, modality_dispatcher)
|
||||
streams = self._connect(streams, mlp_output, "mlp", mlp_logits)
|
||||
return streams.reshape(streams.shape[0], -1)
|
||||
routed = self.mlp.moe_mlp(routed)
|
||||
return self._moe_output(streams, mlp_logits, normalized, routed, modality_dispatcher)
|
||||
|
||||
|
||||
class Magi2TransformerBlock(nn.Module):
|
||||
@@ -570,7 +649,6 @@ class Magi2TransformerBlock(nn.Module):
|
||||
modality_dispatcher: ModalityDispatcher,
|
||||
cp_split_sizes: list[int] | torch.Tensor,
|
||||
) -> torch.Tensor:
|
||||
hidden_states = hidden_states.to(self.config.params_dtype)
|
||||
hidden_states = modality_dispatcher.permute(hidden_states)
|
||||
for layer in self.layers:
|
||||
hidden_states = layer(
|
||||
@@ -630,6 +708,15 @@ class Magi2PreviewTransformer(nn.Module):
|
||||
|
||||
return self.block.layers
|
||||
|
||||
def compile_regions(self, **compile_kwargs: Any) -> None:
|
||||
"""Compile the dense compute between the eager attention and MoE kernels."""
|
||||
|
||||
self.pre_adapter.forward = torch.compile(self.pre_adapter.forward, **compile_kwargs)
|
||||
self.post_adapter.forward = torch.compile(self.post_adapter.forward, **compile_kwargs)
|
||||
for layer in self.block.layers:
|
||||
for name in layer.region_methods:
|
||||
setattr(layer, name, torch.compile(getattr(layer, name), **compile_kwargs))
|
||||
|
||||
def forward(
|
||||
self,
|
||||
x: torch.Tensor,
|
||||
@@ -647,6 +734,7 @@ class Magi2PreviewTransformer(nn.Module):
|
||||
assert dispatcher.split_sizes is not None
|
||||
cp_split_sizes = dispatcher.split_sizes
|
||||
|
||||
rope = self.pre_adapter.rope(coords_mapping)
|
||||
time_mask = modality_mapping == int(Modality.TIME)
|
||||
modality_mapping = torch.where(time_mask, int(Modality.TEXT), modality_mapping)
|
||||
modality_dispatcher = ModalityDispatcher(modality_mapping, 3)
|
||||
@@ -654,13 +742,7 @@ class Magi2PreviewTransformer(nn.Module):
|
||||
audio_indices = torch.nonzero(modality_mapping == int(Modality.AUDIO)).flatten()
|
||||
text_indices = torch.nonzero(modality_mapping == int(Modality.TEXT)).flatten()
|
||||
|
||||
hidden_states, rope = self.pre_adapter(
|
||||
x,
|
||||
coords_mapping,
|
||||
video_indices,
|
||||
audio_indices,
|
||||
text_indices,
|
||||
)
|
||||
hidden_states = self.pre_adapter(x, video_indices, audio_indices, text_indices)
|
||||
if time_token_sequence is not None and time_token_sequence.shape[-1] > 0:
|
||||
hidden_states[:, : time_token_sequence.shape[-1]] = time_token_sequence.to(hidden_states.dtype)
|
||||
hidden_states = self.block(
|
||||
|
||||
@@ -695,6 +695,24 @@ class Magi2Pipeline(
|
||||
|
||||
return self.video_decoder.module
|
||||
|
||||
def setup_compile(self) -> None:
|
||||
"""Compile the transformer regions; the attention and MoE kernels stay eager."""
|
||||
|
||||
granularity = self.od_config.diffusion_compile_granularity
|
||||
if granularity != "regional":
|
||||
logger.warning(
|
||||
"MAGI-2 compiles the transformer regions itself; diffusion_compile_granularity=%r is ignored.",
|
||||
granularity,
|
||||
)
|
||||
|
||||
# The mHC connections chain bf16 ops that eager rounds after every op.
|
||||
# Emulating those casts keeps the compiled rounding boundaries equal.
|
||||
self.transformer.compile_regions(
|
||||
fullgraph=True,
|
||||
dynamic=self.od_config.diffusion_compile_dynamic,
|
||||
options={"emulate_precision_casts": True},
|
||||
)
|
||||
|
||||
def load_weights(
|
||||
self,
|
||||
weights: Iterable[tuple[str, torch.Tensor]],
|
||||
|
||||
@@ -308,29 +308,11 @@ class Magi2PreviewSampler(CFGParallelMixin):
|
||||
per_token_video_t = t_batch.view(-1, 1, 1, 1, 1).expand(cfg_batch, 1, video_t, video_h, video_w)
|
||||
per_token_audio_t = t_batch.view(-1, 1, 1).expand(cfg_batch, audio_t, 1)
|
||||
|
||||
audio_lengths = torch.full(
|
||||
(cfg_batch,),
|
||||
audio_latent_len,
|
||||
device=latent.device,
|
||||
dtype=torch.long,
|
||||
)
|
||||
text_lengths = torch.tensor(
|
||||
[txt_feat_len] * batch_size + [null_txt_feat_len] * batch_size,
|
||||
device=latent.device,
|
||||
dtype=torch.long,
|
||||
)
|
||||
ref_audio_lengths = torch.full(
|
||||
(cfg_batch,),
|
||||
ref_audio_feat_len,
|
||||
device=latent.device,
|
||||
dtype=torch.long,
|
||||
)
|
||||
ref_video_lengths = torch.full(
|
||||
(cfg_batch,),
|
||||
ref_video_feat_len,
|
||||
device=latent.device,
|
||||
dtype=torch.long,
|
||||
)
|
||||
# Lengths stay host integers; the data proxy reads them as Python ints.
|
||||
audio_lengths = [audio_latent_len] * cfg_batch
|
||||
text_lengths = [txt_feat_len] * batch_size + [null_txt_feat_len] * batch_size
|
||||
ref_audio_lengths = [ref_audio_feat_len] * cfg_batch
|
||||
ref_video_lengths = [ref_video_feat_len] * cfg_batch
|
||||
|
||||
return ModelInput(
|
||||
x_t=torch.cat((latent, latent), dim=0),
|
||||
|
||||
Reference in new issue
Block a user