Image-Text-to-Video
Diffusers
Safetensors
text-to-video
image-to-video
text-to-audio-video
image-to-audio-video
reference-to-audio-video
audio-video-generation
multimodal
lora
Instructions to use multimodalart/MiniMax-H3-Pruned with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use multimodalart/MiniMax-H3-Pruned with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("MiniMaxAI/MiniMax-H3", dtype=torch.bfloat16, device_map="cuda") pipe.load_lora_weights("multimodalart/MiniMax-H3-Pruned") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Inference
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
Config, modular index and the pruned transformer class
Browse files- modular_model_index.json +131 -0
- transformer/config.json +31 -0
- transformer/modeling_minimax_h3_pruned.py +243 -0
- transformer_ref/config.json +31 -0
- transformer_ref/modeling_minimax_h3_pruned.py +243 -0
modular_model_index.json
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "MiniMaxH3ModularPipeline",
|
| 3 |
+
"_diffusers_version": "0.40.0.dev0",
|
| 4 |
+
"_blocks_class_name": "MiniMaxH3Blocks",
|
| 5 |
+
"text_encoder": [
|
| 6 |
+
"transformers",
|
| 7 |
+
"Qwen3VLForConditionalGeneration",
|
| 8 |
+
{
|
| 9 |
+
"type_hint": [
|
| 10 |
+
"transformers",
|
| 11 |
+
"Qwen3VLForConditionalGeneration"
|
| 12 |
+
],
|
| 13 |
+
"pretrained_model_name_or_path": "MiniMaxAI/MiniMax-H3",
|
| 14 |
+
"subfolder": "text_encoder",
|
| 15 |
+
"variant": null,
|
| 16 |
+
"revision": null
|
| 17 |
+
}
|
| 18 |
+
],
|
| 19 |
+
"tokenizer": [
|
| 20 |
+
"transformers",
|
| 21 |
+
"Qwen2TokenizerFast",
|
| 22 |
+
{
|
| 23 |
+
"type_hint": [
|
| 24 |
+
"transformers",
|
| 25 |
+
"Qwen2TokenizerFast"
|
| 26 |
+
],
|
| 27 |
+
"pretrained_model_name_or_path": "MiniMaxAI/MiniMax-H3",
|
| 28 |
+
"subfolder": "tokenizer",
|
| 29 |
+
"variant": null,
|
| 30 |
+
"revision": null
|
| 31 |
+
}
|
| 32 |
+
],
|
| 33 |
+
"processor": [
|
| 34 |
+
"transformers",
|
| 35 |
+
"Qwen3VLProcessor",
|
| 36 |
+
{
|
| 37 |
+
"type_hint": [
|
| 38 |
+
"transformers",
|
| 39 |
+
"Qwen3VLProcessor"
|
| 40 |
+
],
|
| 41 |
+
"pretrained_model_name_or_path": "MiniMaxAI/MiniMax-H3",
|
| 42 |
+
"subfolder": "processor",
|
| 43 |
+
"variant": null,
|
| 44 |
+
"revision": null
|
| 45 |
+
}
|
| 46 |
+
],
|
| 47 |
+
"vae": [
|
| 48 |
+
"diffusers",
|
| 49 |
+
"AutoencoderKLMiniMaxH3",
|
| 50 |
+
{
|
| 51 |
+
"type_hint": [
|
| 52 |
+
"diffusers",
|
| 53 |
+
"AutoencoderKLMiniMaxH3"
|
| 54 |
+
],
|
| 55 |
+
"pretrained_model_name_or_path": "MiniMaxAI/MiniMax-H3",
|
| 56 |
+
"subfolder": "vae",
|
| 57 |
+
"variant": null,
|
| 58 |
+
"revision": null
|
| 59 |
+
}
|
| 60 |
+
],
|
| 61 |
+
"audio_vae": [
|
| 62 |
+
"diffusers",
|
| 63 |
+
"AutoencoderKLMiniMaxH3Audio",
|
| 64 |
+
{
|
| 65 |
+
"type_hint": [
|
| 66 |
+
"diffusers",
|
| 67 |
+
"AutoencoderKLMiniMaxH3Audio"
|
| 68 |
+
],
|
| 69 |
+
"pretrained_model_name_or_path": "MiniMaxAI/MiniMax-H3",
|
| 70 |
+
"subfolder": "audio_vae",
|
| 71 |
+
"variant": null,
|
| 72 |
+
"revision": null
|
| 73 |
+
}
|
| 74 |
+
],
|
| 75 |
+
"transformer": [
|
| 76 |
+
"diffusers",
|
| 77 |
+
"AutoModel",
|
| 78 |
+
{
|
| 79 |
+
"type_hint": [
|
| 80 |
+
"diffusers",
|
| 81 |
+
"AutoModel"
|
| 82 |
+
],
|
| 83 |
+
"pretrained_model_name_or_path": "multimodalart/MiniMax-H3-Pruned",
|
| 84 |
+
"subfolder": "transformer",
|
| 85 |
+
"variant": null,
|
| 86 |
+
"revision": null
|
| 87 |
+
}
|
| 88 |
+
],
|
| 89 |
+
"transformer_ref": [
|
| 90 |
+
"diffusers",
|
| 91 |
+
"AutoModel",
|
| 92 |
+
{
|
| 93 |
+
"type_hint": [
|
| 94 |
+
"diffusers",
|
| 95 |
+
"AutoModel"
|
| 96 |
+
],
|
| 97 |
+
"pretrained_model_name_or_path": "multimodalart/MiniMax-H3-Pruned",
|
| 98 |
+
"subfolder": "transformer_ref",
|
| 99 |
+
"variant": null,
|
| 100 |
+
"revision": null
|
| 101 |
+
}
|
| 102 |
+
],
|
| 103 |
+
"scheduler": [
|
| 104 |
+
"diffusers",
|
| 105 |
+
"MiniMaxH3Scheduler",
|
| 106 |
+
{
|
| 107 |
+
"type_hint": [
|
| 108 |
+
"diffusers",
|
| 109 |
+
"MiniMaxH3Scheduler"
|
| 110 |
+
],
|
| 111 |
+
"pretrained_model_name_or_path": "MiniMaxAI/MiniMax-H3",
|
| 112 |
+
"subfolder": "scheduler",
|
| 113 |
+
"variant": null,
|
| 114 |
+
"revision": null
|
| 115 |
+
}
|
| 116 |
+
],
|
| 117 |
+
"audio_scheduler": [
|
| 118 |
+
"diffusers",
|
| 119 |
+
"MiniMaxH3Scheduler",
|
| 120 |
+
{
|
| 121 |
+
"type_hint": [
|
| 122 |
+
"diffusers",
|
| 123 |
+
"MiniMaxH3Scheduler"
|
| 124 |
+
],
|
| 125 |
+
"pretrained_model_name_or_path": "MiniMaxAI/MiniMax-H3",
|
| 126 |
+
"subfolder": "audio_scheduler",
|
| 127 |
+
"variant": null,
|
| 128 |
+
"revision": null
|
| 129 |
+
}
|
| 130 |
+
]
|
| 131 |
+
}
|
transformer/config.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "MiniMaxH3PrunedTransformer3DModel",
|
| 3 |
+
"_diffusers_version": "0.40.0.dev0",
|
| 4 |
+
"adaln_rank": 8,
|
| 5 |
+
"attention_head_dim": 128,
|
| 6 |
+
"audio_in_channels": 32,
|
| 7 |
+
"auto_map": {
|
| 8 |
+
"AutoModel": "modeling_minimax_h3_pruned.MiniMaxH3PrunedTransformer3DModel"
|
| 9 |
+
},
|
| 10 |
+
"ffn_dim": 14336,
|
| 11 |
+
"final_norm_eps": 1e-05,
|
| 12 |
+
"freq_dim": 256,
|
| 13 |
+
"hidden_size": 5376,
|
| 14 |
+
"in_channels": 24,
|
| 15 |
+
"norm_eps": 1e-05,
|
| 16 |
+
"num_attention_heads": 56,
|
| 17 |
+
"num_layers": 50,
|
| 18 |
+
"num_refiner_layers": 2,
|
| 19 |
+
"patch_size": [
|
| 20 |
+
1,
|
| 21 |
+
2,
|
| 22 |
+
2
|
| 23 |
+
],
|
| 24 |
+
"qk_norm_eps": 1e-05,
|
| 25 |
+
"rope_freq_dim": 16,
|
| 26 |
+
"rope_theta": 10000.0,
|
| 27 |
+
"text_dim": 5120,
|
| 28 |
+
"time_embed_dim": 2688,
|
| 29 |
+
"time_embed_hidden_dim": 5376,
|
| 30 |
+
"time_table_size": 1025
|
| 31 |
+
}
|
transformer/modeling_minimax_h3_pruned.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The MiniMax Team and The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""AdaLN-pruned MiniMax-H3 transformer.
|
| 15 |
+
|
| 16 |
+
Everything outside the timestep path is inherited from `MiniMaxH3Transformer3DModel`: the attention, the blocks, the
|
| 17 |
+
token refiner, the output heads and `forward` itself are the released implementation, unmodified. Only what feeds the
|
| 18 |
+
AdaLN projections changes.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
import torch.nn as nn
|
| 23 |
+
from diffusers.configuration_utils import register_to_config
|
| 24 |
+
from diffusers.models.modeling_utils import get_parameter_dtype
|
| 25 |
+
from diffusers.models.transformers.transformer_minimax_h3 import (
|
| 26 |
+
MINIMAX_H3_MODALITY_NUM,
|
| 27 |
+
MiniMaxH3RotaryPosEmbed,
|
| 28 |
+
MiniMaxH3TokenRefiner,
|
| 29 |
+
MiniMaxH3Transformer3DModel,
|
| 30 |
+
MiniMaxH3TransformerBlock,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class MiniMaxH3PrunedTimeEmbedder(nn.Module):
|
| 35 |
+
r"""The released timestep MLP, replaced by an interpolated table of AdaLN coordinates.
|
| 36 |
+
|
| 37 |
+
Every AdaLN projection in the released model consumes `silu(time_embedder(time_proj(t)))`, which depends on the
|
| 38 |
+
scalar timestep alone: over `t` in `[0, 1]` it traces a one-dimensional curve in `R^{time_embed_dim}`. A rank-8
|
| 39 |
+
affine subspace reproduces that curve to about 1.5e-5 relative RMS, so only the curve's coordinates in that
|
| 40 |
+
subspace are stored - sampled on a uniform grid of `table_size` timesteps and linearly interpolated in between.
|
| 41 |
+
The subspace offset is folded into the AdaLN biases and its basis into the AdaLN weights, which is why the
|
| 42 |
+
projections take an `adaln_rank`-wide input here instead of `time_embed_dim`.
|
| 43 |
+
|
| 44 |
+
The module stands in for `time_proj` and `time_embedder` together: it consumes the raw timestep, so the released
|
| 45 |
+
`forward` needs no change once `time_proj` is an identity.
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
def __init__(self, table_size: int = 1025, adaln_rank: int = 8) -> None:
|
| 49 |
+
super().__init__()
|
| 50 |
+
self.register_buffer("table", torch.zeros(table_size, adaln_rank), persistent=True)
|
| 51 |
+
|
| 52 |
+
def forward(self, timestep: torch.Tensor) -> torch.Tensor:
|
| 53 |
+
table = self.table
|
| 54 |
+
steps = table.shape[0] - 1
|
| 55 |
+
position = timestep.to(table.dtype).flatten().clamp(0.0, 1.0) * steps
|
| 56 |
+
lower = position.floor().clamp(max=steps - 1).long()
|
| 57 |
+
weight = (position - lower).unsqueeze(-1)
|
| 58 |
+
return torch.lerp(table.index_select(0, lower), table.index_select(0, lower + 1), weight)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class MiniMaxH3PrunedTimeProj(nn.Module):
|
| 62 |
+
r"""Identity stand-in for `Timesteps`: the pruned time embedder indexes the raw timestep."""
|
| 63 |
+
|
| 64 |
+
def forward(self, timestep: torch.Tensor) -> torch.Tensor:
|
| 65 |
+
return timestep
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class MiniMaxH3PrunedAdaLayerNormModulation(nn.Module):
|
| 69 |
+
r"""`MiniMaxH3AdaLayerNormModulation` over the pruned timestep coordinates.
|
| 70 |
+
|
| 71 |
+
Two differences from the released module. It applies no `silu` - the table already holds the coordinates of the
|
| 72 |
+
activated curve. And the folded bias is a float32 buffer applied outside the projection rather than the
|
| 73 |
+
projection's own bias: it carries almost the entire modulation (the coordinate term contributes a few tenths of
|
| 74 |
+
it), so storing it in bfloat16 would put a full output-scale rounding step into every evaluation. Kept in
|
| 75 |
+
float32 it costs 0.4 MB per block and leaves the pruned AdaLN function closer to an exact float64 evaluation
|
| 76 |
+
than the released bfloat16 checkpoint's own arithmetic is.
|
| 77 |
+
|
| 78 |
+
`linear` stays a bias-free `nn.Linear` so PEFT wraps it exactly as it wraps the released projection.
|
| 79 |
+
"""
|
| 80 |
+
|
| 81 |
+
def __init__(self, adaln_rank: int, hidden_size: int) -> None:
|
| 82 |
+
super().__init__()
|
| 83 |
+
self.hidden_size = hidden_size
|
| 84 |
+
out_features = 6 * hidden_size * MINIMAX_H3_MODALITY_NUM
|
| 85 |
+
self.linear = nn.Linear(adaln_rank, out_features, bias=False)
|
| 86 |
+
self.register_buffer("folded_bias", torch.zeros(out_features), persistent=True)
|
| 87 |
+
|
| 88 |
+
def forward(self, temb: torch.Tensor) -> tuple[torch.Tensor, ...]:
|
| 89 |
+
dtype = get_parameter_dtype(self.linear)
|
| 90 |
+
temb = self.linear(temb.to(dtype))
|
| 91 |
+
temb = (temb.float() + self.folded_bias).to(dtype)
|
| 92 |
+
temb = temb.view(-1, 6 * self.hidden_size)
|
| 93 |
+
return temb.chunk(6, dim=-1)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
class MiniMaxH3PrunedAdaLayerNormOut(nn.Module):
|
| 97 |
+
r"""`MiniMaxH3AdaLayerNormOut` over the pruned timestep coordinates; see the modulation module above."""
|
| 98 |
+
|
| 99 |
+
def __init__(self, hidden_size: int, adaln_rank: int, eps: float) -> None:
|
| 100 |
+
super().__init__()
|
| 101 |
+
self.norm = nn.RMSNorm(hidden_size, eps=eps)
|
| 102 |
+
self.linear = nn.Linear(adaln_rank, 2 * hidden_size, bias=False)
|
| 103 |
+
self.register_buffer("folded_bias", torch.zeros(2 * hidden_size), persistent=True)
|
| 104 |
+
|
| 105 |
+
def forward(self, hidden_states: torch.Tensor, temb: torch.Tensor, timestep_indices: torch.Tensor) -> torch.Tensor:
|
| 106 |
+
dtype = get_parameter_dtype(self.linear)
|
| 107 |
+
temb = self.linear(temb.to(dtype))
|
| 108 |
+
shift, scale = (temb.float() + self.folded_bias).to(dtype).chunk(2, dim=-1)
|
| 109 |
+
hidden_states = self.norm(hidden_states)
|
| 110 |
+
return hidden_states * (1.0 + scale.index_select(0, timestep_indices)) + shift.index_select(
|
| 111 |
+
0, timestep_indices
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class MiniMaxH3PrunedTransformer3DModel(MiniMaxH3Transformer3DModel):
|
| 116 |
+
r"""MiniMax-H3's DiT with the AdaLN input projections reduced to their reachable rank.
|
| 117 |
+
|
| 118 |
+
The released checkpoint spends 13.03B of its 33.14B parameters on the 50 per-block `adaln_proj.linear` matrices
|
| 119 |
+
plus `norm_out.linear`, all of which read the same 2688-wide timestep embedding. Because that embedding is a
|
| 120 |
+
function of the scalar timestep, its reachable set is a curve an 8-dimensional affine subspace covers to ~1.5e-5
|
| 121 |
+
relative RMS - far below one bfloat16 rounding step of the weights themselves. Folding the subspace into the
|
| 122 |
+
projections leaves an 8-wide input and removes 26 GB per partition.
|
| 123 |
+
|
| 124 |
+
Only what builds the timestep path differs from [`MiniMaxH3Transformer3DModel`]: `time_proj` becomes an identity,
|
| 125 |
+
`time_embedder` becomes [`MiniMaxH3PrunedTimeEmbedder`], and the AdaLN projections take `adaln_rank` inputs.
|
| 126 |
+
`forward` is inherited unchanged. The module names are the released ones, so LoRAs trained against a pruned
|
| 127 |
+
checkpoint - what the common trainers use by default - load natively.
|
| 128 |
+
|
| 129 |
+
Args:
|
| 130 |
+
adaln_rank (`int`, defaults to `8`):
|
| 131 |
+
The width of the timestep coordinates every AdaLN projection consumes.
|
| 132 |
+
time_table_size (`int`, defaults to `1025`):
|
| 133 |
+
The number of uniformly spaced timesteps the coordinate table holds; values in between are interpolated
|
| 134 |
+
linearly.
|
| 135 |
+
|
| 136 |
+
Every other argument is [`MiniMaxH3Transformer3DModel`]'s and carries the same meaning. `freq_dim` and
|
| 137 |
+
`time_embed_hidden_dim` are kept in the config, unused, so a pruned config still records the shape of the
|
| 138 |
+
released timestep MLP it was folded from.
|
| 139 |
+
"""
|
| 140 |
+
|
| 141 |
+
_supports_gradient_checkpointing = True
|
| 142 |
+
_no_split_modules = ["MiniMaxH3TransformerBlock", "MiniMaxH3TokenRefinerBlock", "MiniMaxH3PrunedAdaLayerNormOut"]
|
| 143 |
+
_repeated_blocks = ["MiniMaxH3TransformerBlock", "MiniMaxH3TokenRefinerBlock"]
|
| 144 |
+
_skip_layerwise_casting_patterns = ["norm"]
|
| 145 |
+
# The released checkpoint's mixed-precision split - patch projections, output heads and the timestep path in
|
| 146 |
+
# float32, the block stack in bfloat16 - plus the folded AdaLN biases, for the reason given on the modulation
|
| 147 |
+
# module. Entries are matched against the dot-separated segments of each parameter name.
|
| 148 |
+
_keep_in_fp32_modules = [
|
| 149 |
+
"proj_in",
|
| 150 |
+
"audio_proj_in",
|
| 151 |
+
"time_embedder",
|
| 152 |
+
"proj_out",
|
| 153 |
+
"audio_proj_out",
|
| 154 |
+
"rope",
|
| 155 |
+
"folded_bias",
|
| 156 |
+
]
|
| 157 |
+
|
| 158 |
+
@register_to_config
|
| 159 |
+
def __init__(
|
| 160 |
+
self,
|
| 161 |
+
num_attention_heads: int = 56,
|
| 162 |
+
attention_head_dim: int = 128,
|
| 163 |
+
hidden_size: int = 5376,
|
| 164 |
+
num_layers: int = 50,
|
| 165 |
+
num_refiner_layers: int = 2,
|
| 166 |
+
ffn_dim: int = 14336,
|
| 167 |
+
in_channels: int = 24,
|
| 168 |
+
audio_in_channels: int = 32,
|
| 169 |
+
patch_size: tuple[int, int, int] = (1, 2, 2),
|
| 170 |
+
text_dim: int = 5120,
|
| 171 |
+
freq_dim: int = 256,
|
| 172 |
+
time_embed_hidden_dim: int = 5376,
|
| 173 |
+
time_embed_dim: int = 2688,
|
| 174 |
+
rope_freq_dim: int = 16,
|
| 175 |
+
rope_theta: float = 10000.0,
|
| 176 |
+
norm_eps: float = 1e-5,
|
| 177 |
+
qk_norm_eps: float = 1e-5,
|
| 178 |
+
final_norm_eps: float = 1e-5,
|
| 179 |
+
adaln_rank: int = 8,
|
| 180 |
+
time_table_size: int = 1025,
|
| 181 |
+
) -> None:
|
| 182 |
+
# `MiniMaxH3Transformer3DModel.__init__` is itself wrapped by `register_to_config`, so calling it would
|
| 183 |
+
# register the released config over this one - and would allocate the 26 GB of AdaLN projections this class
|
| 184 |
+
# exists to avoid. The module tree is built here instead; everything but the timestep path is verbatim.
|
| 185 |
+
nn.Module.__init__(self)
|
| 186 |
+
|
| 187 |
+
video_patch_dim = in_channels * patch_size[0] * patch_size[1] * patch_size[2]
|
| 188 |
+
|
| 189 |
+
# 1. Per-modality input projections
|
| 190 |
+
self.proj_in = nn.Linear(video_patch_dim, hidden_size, bias=True)
|
| 191 |
+
self.audio_proj_in = nn.Linear(audio_in_channels, hidden_size, bias=True)
|
| 192 |
+
self.context_embedder = nn.Linear(text_dim, hidden_size, bias=True)
|
| 193 |
+
|
| 194 |
+
# 2. Timestep coordinates, shared by every AdaLN projection
|
| 195 |
+
self.time_proj = MiniMaxH3PrunedTimeProj()
|
| 196 |
+
self.time_embedder = MiniMaxH3PrunedTimeEmbedder(table_size=time_table_size, adaln_rank=adaln_rank)
|
| 197 |
+
|
| 198 |
+
# 3. Rotary embedding over the packed (t, h, w) grid
|
| 199 |
+
self.rope = MiniMaxH3RotaryPosEmbed(rope_freq_dim=rope_freq_dim, rope_theta=rope_theta)
|
| 200 |
+
|
| 201 |
+
# 4. Text stream refiner
|
| 202 |
+
self.token_refiner = MiniMaxH3TokenRefiner(
|
| 203 |
+
hidden_size=hidden_size,
|
| 204 |
+
num_attention_heads=num_attention_heads,
|
| 205 |
+
attention_head_dim=attention_head_dim,
|
| 206 |
+
ffn_dim=ffn_dim,
|
| 207 |
+
num_layers=num_refiner_layers,
|
| 208 |
+
norm_eps=norm_eps,
|
| 209 |
+
qk_norm_eps=qk_norm_eps,
|
| 210 |
+
final_norm_eps=final_norm_eps,
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
# 5. The block stack, with each block's AdaLN projection narrowed to the timestep coordinates. The block is
|
| 214 |
+
# built with `time_embed_dim=adaln_rank` so its own projection is already the right shape, then swapped
|
| 215 |
+
# for the pruned module, which drops the `silu` and moves the bias to float32.
|
| 216 |
+
self.transformer_blocks = nn.ModuleList(
|
| 217 |
+
[
|
| 218 |
+
MiniMaxH3TransformerBlock(
|
| 219 |
+
hidden_size=hidden_size,
|
| 220 |
+
num_attention_heads=num_attention_heads,
|
| 221 |
+
attention_head_dim=attention_head_dim,
|
| 222 |
+
ffn_dim=ffn_dim,
|
| 223 |
+
time_embed_dim=adaln_rank,
|
| 224 |
+
norm_eps=norm_eps,
|
| 225 |
+
qk_norm_eps=qk_norm_eps,
|
| 226 |
+
)
|
| 227 |
+
for _ in range(num_layers)
|
| 228 |
+
]
|
| 229 |
+
)
|
| 230 |
+
for block in self.transformer_blocks:
|
| 231 |
+
block.adaln_proj = MiniMaxH3PrunedAdaLayerNormModulation(adaln_rank=adaln_rank, hidden_size=hidden_size)
|
| 232 |
+
|
| 233 |
+
# 6. Shared output norm and the two per-modality output heads
|
| 234 |
+
self.norm_out = MiniMaxH3PrunedAdaLayerNormOut(
|
| 235 |
+
hidden_size=hidden_size, adaln_rank=adaln_rank, eps=final_norm_eps
|
| 236 |
+
)
|
| 237 |
+
self.proj_out = nn.Linear(hidden_size, video_patch_dim, bias=True)
|
| 238 |
+
self.audio_proj_out = nn.Linear(hidden_size, audio_in_channels, bias=True)
|
| 239 |
+
|
| 240 |
+
self.gradient_checkpointing = False
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
MiniMaxH3PrunedTransformer3DModel.register_for_auto_class("AutoModel")
|
transformer_ref/config.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_class_name": "MiniMaxH3PrunedTransformer3DModel",
|
| 3 |
+
"_diffusers_version": "0.40.0.dev0",
|
| 4 |
+
"adaln_rank": 8,
|
| 5 |
+
"attention_head_dim": 128,
|
| 6 |
+
"audio_in_channels": 32,
|
| 7 |
+
"auto_map": {
|
| 8 |
+
"AutoModel": "modeling_minimax_h3_pruned.MiniMaxH3PrunedTransformer3DModel"
|
| 9 |
+
},
|
| 10 |
+
"ffn_dim": 14336,
|
| 11 |
+
"final_norm_eps": 1e-05,
|
| 12 |
+
"freq_dim": 256,
|
| 13 |
+
"hidden_size": 5376,
|
| 14 |
+
"in_channels": 24,
|
| 15 |
+
"norm_eps": 1e-05,
|
| 16 |
+
"num_attention_heads": 56,
|
| 17 |
+
"num_layers": 50,
|
| 18 |
+
"num_refiner_layers": 2,
|
| 19 |
+
"patch_size": [
|
| 20 |
+
1,
|
| 21 |
+
2,
|
| 22 |
+
2
|
| 23 |
+
],
|
| 24 |
+
"qk_norm_eps": 1e-05,
|
| 25 |
+
"rope_freq_dim": 16,
|
| 26 |
+
"rope_theta": 10000.0,
|
| 27 |
+
"text_dim": 5120,
|
| 28 |
+
"time_embed_dim": 2688,
|
| 29 |
+
"time_embed_hidden_dim": 5376,
|
| 30 |
+
"time_table_size": 1025
|
| 31 |
+
}
|
transformer_ref/modeling_minimax_h3_pruned.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The MiniMax Team and The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""AdaLN-pruned MiniMax-H3 transformer.
|
| 15 |
+
|
| 16 |
+
Everything outside the timestep path is inherited from `MiniMaxH3Transformer3DModel`: the attention, the blocks, the
|
| 17 |
+
token refiner, the output heads and `forward` itself are the released implementation, unmodified. Only what feeds the
|
| 18 |
+
AdaLN projections changes.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
import torch.nn as nn
|
| 23 |
+
from diffusers.configuration_utils import register_to_config
|
| 24 |
+
from diffusers.models.modeling_utils import get_parameter_dtype
|
| 25 |
+
from diffusers.models.transformers.transformer_minimax_h3 import (
|
| 26 |
+
MINIMAX_H3_MODALITY_NUM,
|
| 27 |
+
MiniMaxH3RotaryPosEmbed,
|
| 28 |
+
MiniMaxH3TokenRefiner,
|
| 29 |
+
MiniMaxH3Transformer3DModel,
|
| 30 |
+
MiniMaxH3TransformerBlock,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class MiniMaxH3PrunedTimeEmbedder(nn.Module):
|
| 35 |
+
r"""The released timestep MLP, replaced by an interpolated table of AdaLN coordinates.
|
| 36 |
+
|
| 37 |
+
Every AdaLN projection in the released model consumes `silu(time_embedder(time_proj(t)))`, which depends on the
|
| 38 |
+
scalar timestep alone: over `t` in `[0, 1]` it traces a one-dimensional curve in `R^{time_embed_dim}`. A rank-8
|
| 39 |
+
affine subspace reproduces that curve to about 1.5e-5 relative RMS, so only the curve's coordinates in that
|
| 40 |
+
subspace are stored - sampled on a uniform grid of `table_size` timesteps and linearly interpolated in between.
|
| 41 |
+
The subspace offset is folded into the AdaLN biases and its basis into the AdaLN weights, which is why the
|
| 42 |
+
projections take an `adaln_rank`-wide input here instead of `time_embed_dim`.
|
| 43 |
+
|
| 44 |
+
The module stands in for `time_proj` and `time_embedder` together: it consumes the raw timestep, so the released
|
| 45 |
+
`forward` needs no change once `time_proj` is an identity.
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
def __init__(self, table_size: int = 1025, adaln_rank: int = 8) -> None:
|
| 49 |
+
super().__init__()
|
| 50 |
+
self.register_buffer("table", torch.zeros(table_size, adaln_rank), persistent=True)
|
| 51 |
+
|
| 52 |
+
def forward(self, timestep: torch.Tensor) -> torch.Tensor:
|
| 53 |
+
table = self.table
|
| 54 |
+
steps = table.shape[0] - 1
|
| 55 |
+
position = timestep.to(table.dtype).flatten().clamp(0.0, 1.0) * steps
|
| 56 |
+
lower = position.floor().clamp(max=steps - 1).long()
|
| 57 |
+
weight = (position - lower).unsqueeze(-1)
|
| 58 |
+
return torch.lerp(table.index_select(0, lower), table.index_select(0, lower + 1), weight)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class MiniMaxH3PrunedTimeProj(nn.Module):
|
| 62 |
+
r"""Identity stand-in for `Timesteps`: the pruned time embedder indexes the raw timestep."""
|
| 63 |
+
|
| 64 |
+
def forward(self, timestep: torch.Tensor) -> torch.Tensor:
|
| 65 |
+
return timestep
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class MiniMaxH3PrunedAdaLayerNormModulation(nn.Module):
|
| 69 |
+
r"""`MiniMaxH3AdaLayerNormModulation` over the pruned timestep coordinates.
|
| 70 |
+
|
| 71 |
+
Two differences from the released module. It applies no `silu` - the table already holds the coordinates of the
|
| 72 |
+
activated curve. And the folded bias is a float32 buffer applied outside the projection rather than the
|
| 73 |
+
projection's own bias: it carries almost the entire modulation (the coordinate term contributes a few tenths of
|
| 74 |
+
it), so storing it in bfloat16 would put a full output-scale rounding step into every evaluation. Kept in
|
| 75 |
+
float32 it costs 0.4 MB per block and leaves the pruned AdaLN function closer to an exact float64 evaluation
|
| 76 |
+
than the released bfloat16 checkpoint's own arithmetic is.
|
| 77 |
+
|
| 78 |
+
`linear` stays a bias-free `nn.Linear` so PEFT wraps it exactly as it wraps the released projection.
|
| 79 |
+
"""
|
| 80 |
+
|
| 81 |
+
def __init__(self, adaln_rank: int, hidden_size: int) -> None:
|
| 82 |
+
super().__init__()
|
| 83 |
+
self.hidden_size = hidden_size
|
| 84 |
+
out_features = 6 * hidden_size * MINIMAX_H3_MODALITY_NUM
|
| 85 |
+
self.linear = nn.Linear(adaln_rank, out_features, bias=False)
|
| 86 |
+
self.register_buffer("folded_bias", torch.zeros(out_features), persistent=True)
|
| 87 |
+
|
| 88 |
+
def forward(self, temb: torch.Tensor) -> tuple[torch.Tensor, ...]:
|
| 89 |
+
dtype = get_parameter_dtype(self.linear)
|
| 90 |
+
temb = self.linear(temb.to(dtype))
|
| 91 |
+
temb = (temb.float() + self.folded_bias).to(dtype)
|
| 92 |
+
temb = temb.view(-1, 6 * self.hidden_size)
|
| 93 |
+
return temb.chunk(6, dim=-1)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
class MiniMaxH3PrunedAdaLayerNormOut(nn.Module):
|
| 97 |
+
r"""`MiniMaxH3AdaLayerNormOut` over the pruned timestep coordinates; see the modulation module above."""
|
| 98 |
+
|
| 99 |
+
def __init__(self, hidden_size: int, adaln_rank: int, eps: float) -> None:
|
| 100 |
+
super().__init__()
|
| 101 |
+
self.norm = nn.RMSNorm(hidden_size, eps=eps)
|
| 102 |
+
self.linear = nn.Linear(adaln_rank, 2 * hidden_size, bias=False)
|
| 103 |
+
self.register_buffer("folded_bias", torch.zeros(2 * hidden_size), persistent=True)
|
| 104 |
+
|
| 105 |
+
def forward(self, hidden_states: torch.Tensor, temb: torch.Tensor, timestep_indices: torch.Tensor) -> torch.Tensor:
|
| 106 |
+
dtype = get_parameter_dtype(self.linear)
|
| 107 |
+
temb = self.linear(temb.to(dtype))
|
| 108 |
+
shift, scale = (temb.float() + self.folded_bias).to(dtype).chunk(2, dim=-1)
|
| 109 |
+
hidden_states = self.norm(hidden_states)
|
| 110 |
+
return hidden_states * (1.0 + scale.index_select(0, timestep_indices)) + shift.index_select(
|
| 111 |
+
0, timestep_indices
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class MiniMaxH3PrunedTransformer3DModel(MiniMaxH3Transformer3DModel):
|
| 116 |
+
r"""MiniMax-H3's DiT with the AdaLN input projections reduced to their reachable rank.
|
| 117 |
+
|
| 118 |
+
The released checkpoint spends 13.03B of its 33.14B parameters on the 50 per-block `adaln_proj.linear` matrices
|
| 119 |
+
plus `norm_out.linear`, all of which read the same 2688-wide timestep embedding. Because that embedding is a
|
| 120 |
+
function of the scalar timestep, its reachable set is a curve an 8-dimensional affine subspace covers to ~1.5e-5
|
| 121 |
+
relative RMS - far below one bfloat16 rounding step of the weights themselves. Folding the subspace into the
|
| 122 |
+
projections leaves an 8-wide input and removes 26 GB per partition.
|
| 123 |
+
|
| 124 |
+
Only what builds the timestep path differs from [`MiniMaxH3Transformer3DModel`]: `time_proj` becomes an identity,
|
| 125 |
+
`time_embedder` becomes [`MiniMaxH3PrunedTimeEmbedder`], and the AdaLN projections take `adaln_rank` inputs.
|
| 126 |
+
`forward` is inherited unchanged. The module names are the released ones, so LoRAs trained against a pruned
|
| 127 |
+
checkpoint - what the common trainers use by default - load natively.
|
| 128 |
+
|
| 129 |
+
Args:
|
| 130 |
+
adaln_rank (`int`, defaults to `8`):
|
| 131 |
+
The width of the timestep coordinates every AdaLN projection consumes.
|
| 132 |
+
time_table_size (`int`, defaults to `1025`):
|
| 133 |
+
The number of uniformly spaced timesteps the coordinate table holds; values in between are interpolated
|
| 134 |
+
linearly.
|
| 135 |
+
|
| 136 |
+
Every other argument is [`MiniMaxH3Transformer3DModel`]'s and carries the same meaning. `freq_dim` and
|
| 137 |
+
`time_embed_hidden_dim` are kept in the config, unused, so a pruned config still records the shape of the
|
| 138 |
+
released timestep MLP it was folded from.
|
| 139 |
+
"""
|
| 140 |
+
|
| 141 |
+
_supports_gradient_checkpointing = True
|
| 142 |
+
_no_split_modules = ["MiniMaxH3TransformerBlock", "MiniMaxH3TokenRefinerBlock", "MiniMaxH3PrunedAdaLayerNormOut"]
|
| 143 |
+
_repeated_blocks = ["MiniMaxH3TransformerBlock", "MiniMaxH3TokenRefinerBlock"]
|
| 144 |
+
_skip_layerwise_casting_patterns = ["norm"]
|
| 145 |
+
# The released checkpoint's mixed-precision split - patch projections, output heads and the timestep path in
|
| 146 |
+
# float32, the block stack in bfloat16 - plus the folded AdaLN biases, for the reason given on the modulation
|
| 147 |
+
# module. Entries are matched against the dot-separated segments of each parameter name.
|
| 148 |
+
_keep_in_fp32_modules = [
|
| 149 |
+
"proj_in",
|
| 150 |
+
"audio_proj_in",
|
| 151 |
+
"time_embedder",
|
| 152 |
+
"proj_out",
|
| 153 |
+
"audio_proj_out",
|
| 154 |
+
"rope",
|
| 155 |
+
"folded_bias",
|
| 156 |
+
]
|
| 157 |
+
|
| 158 |
+
@register_to_config
|
| 159 |
+
def __init__(
|
| 160 |
+
self,
|
| 161 |
+
num_attention_heads: int = 56,
|
| 162 |
+
attention_head_dim: int = 128,
|
| 163 |
+
hidden_size: int = 5376,
|
| 164 |
+
num_layers: int = 50,
|
| 165 |
+
num_refiner_layers: int = 2,
|
| 166 |
+
ffn_dim: int = 14336,
|
| 167 |
+
in_channels: int = 24,
|
| 168 |
+
audio_in_channels: int = 32,
|
| 169 |
+
patch_size: tuple[int, int, int] = (1, 2, 2),
|
| 170 |
+
text_dim: int = 5120,
|
| 171 |
+
freq_dim: int = 256,
|
| 172 |
+
time_embed_hidden_dim: int = 5376,
|
| 173 |
+
time_embed_dim: int = 2688,
|
| 174 |
+
rope_freq_dim: int = 16,
|
| 175 |
+
rope_theta: float = 10000.0,
|
| 176 |
+
norm_eps: float = 1e-5,
|
| 177 |
+
qk_norm_eps: float = 1e-5,
|
| 178 |
+
final_norm_eps: float = 1e-5,
|
| 179 |
+
adaln_rank: int = 8,
|
| 180 |
+
time_table_size: int = 1025,
|
| 181 |
+
) -> None:
|
| 182 |
+
# `MiniMaxH3Transformer3DModel.__init__` is itself wrapped by `register_to_config`, so calling it would
|
| 183 |
+
# register the released config over this one - and would allocate the 26 GB of AdaLN projections this class
|
| 184 |
+
# exists to avoid. The module tree is built here instead; everything but the timestep path is verbatim.
|
| 185 |
+
nn.Module.__init__(self)
|
| 186 |
+
|
| 187 |
+
video_patch_dim = in_channels * patch_size[0] * patch_size[1] * patch_size[2]
|
| 188 |
+
|
| 189 |
+
# 1. Per-modality input projections
|
| 190 |
+
self.proj_in = nn.Linear(video_patch_dim, hidden_size, bias=True)
|
| 191 |
+
self.audio_proj_in = nn.Linear(audio_in_channels, hidden_size, bias=True)
|
| 192 |
+
self.context_embedder = nn.Linear(text_dim, hidden_size, bias=True)
|
| 193 |
+
|
| 194 |
+
# 2. Timestep coordinates, shared by every AdaLN projection
|
| 195 |
+
self.time_proj = MiniMaxH3PrunedTimeProj()
|
| 196 |
+
self.time_embedder = MiniMaxH3PrunedTimeEmbedder(table_size=time_table_size, adaln_rank=adaln_rank)
|
| 197 |
+
|
| 198 |
+
# 3. Rotary embedding over the packed (t, h, w) grid
|
| 199 |
+
self.rope = MiniMaxH3RotaryPosEmbed(rope_freq_dim=rope_freq_dim, rope_theta=rope_theta)
|
| 200 |
+
|
| 201 |
+
# 4. Text stream refiner
|
| 202 |
+
self.token_refiner = MiniMaxH3TokenRefiner(
|
| 203 |
+
hidden_size=hidden_size,
|
| 204 |
+
num_attention_heads=num_attention_heads,
|
| 205 |
+
attention_head_dim=attention_head_dim,
|
| 206 |
+
ffn_dim=ffn_dim,
|
| 207 |
+
num_layers=num_refiner_layers,
|
| 208 |
+
norm_eps=norm_eps,
|
| 209 |
+
qk_norm_eps=qk_norm_eps,
|
| 210 |
+
final_norm_eps=final_norm_eps,
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
# 5. The block stack, with each block's AdaLN projection narrowed to the timestep coordinates. The block is
|
| 214 |
+
# built with `time_embed_dim=adaln_rank` so its own projection is already the right shape, then swapped
|
| 215 |
+
# for the pruned module, which drops the `silu` and moves the bias to float32.
|
| 216 |
+
self.transformer_blocks = nn.ModuleList(
|
| 217 |
+
[
|
| 218 |
+
MiniMaxH3TransformerBlock(
|
| 219 |
+
hidden_size=hidden_size,
|
| 220 |
+
num_attention_heads=num_attention_heads,
|
| 221 |
+
attention_head_dim=attention_head_dim,
|
| 222 |
+
ffn_dim=ffn_dim,
|
| 223 |
+
time_embed_dim=adaln_rank,
|
| 224 |
+
norm_eps=norm_eps,
|
| 225 |
+
qk_norm_eps=qk_norm_eps,
|
| 226 |
+
)
|
| 227 |
+
for _ in range(num_layers)
|
| 228 |
+
]
|
| 229 |
+
)
|
| 230 |
+
for block in self.transformer_blocks:
|
| 231 |
+
block.adaln_proj = MiniMaxH3PrunedAdaLayerNormModulation(adaln_rank=adaln_rank, hidden_size=hidden_size)
|
| 232 |
+
|
| 233 |
+
# 6. Shared output norm and the two per-modality output heads
|
| 234 |
+
self.norm_out = MiniMaxH3PrunedAdaLayerNormOut(
|
| 235 |
+
hidden_size=hidden_size, adaln_rank=adaln_rank, eps=final_norm_eps
|
| 236 |
+
)
|
| 237 |
+
self.proj_out = nn.Linear(hidden_size, video_patch_dim, bias=True)
|
| 238 |
+
self.audio_proj_out = nn.Linear(hidden_size, audio_in_channels, bias=True)
|
| 239 |
+
|
| 240 |
+
self.gradient_checkpointing = False
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
MiniMaxH3PrunedTransformer3DModel.register_for_auto_class("AutoModel")
|