Instructions to use tiny-random/ltx-2-5 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use tiny-random/ltx-2-5 with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline from diffusers.utils import load_image, export_to_video # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("tiny-random/ltx-2-5", dtype=torch.bfloat16, device_map="cuda") pipe.to("cuda") prompt = "A man with short gray hair plays a red electric guitar." image = load_image( "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/guitar-man.png" ) output = pipe(image=image, prompt=prompt).frames[0] export_to_video(output, "output.mp4") - Notebooks
- Google Colab
- Kaggle
Upload folder using huggingface_hub
Browse files- .meta/create_model.py +406 -0
- .meta/env.json +38 -0
- .meta/model.log +180 -0
- README.md +91 -0
- config.json +126 -0
- diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors +3 -0
- ltx2_loader.py +162 -0
- text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors +3 -0
- vae/ltx-2.5-audio-vae-bf16.safetensors +3 -0
- vae/ltx-2.5-video-vae-bf16.safetensors +3 -0
.meta/create_model.py
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def create_model(source_model_id: str, source_revision: str, save_folder: str) -> str:
|
| 2 |
+
import json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from diffusers import AutoencoderKLLTX2Audio, AutoencoderKLLTX2Video, LTX2VideoTransformer3DModel
|
| 7 |
+
from diffusers.pipelines.ltx2.connectors import LTX2TextConnectors
|
| 8 |
+
from diffusers.pipelines.ltx2.vocoder import LTX2Vocoder
|
| 9 |
+
from huggingface_hub import hf_hub_download
|
| 10 |
+
from safetensors.torch import save_file
|
| 11 |
+
from transformers import (
|
| 12 |
+
Gemma4UnifiedConfig,
|
| 13 |
+
Gemma4UnifiedForConditionalGeneration,
|
| 14 |
+
Gemma4UnifiedTextConfig,
|
| 15 |
+
Gemma4UnifiedVisionConfig,
|
| 16 |
+
set_seed,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
tokenizer_source = "google/gemma-4-12b-it"
|
| 20 |
+
tokenizer_revision = "43c9d014b231474fa7212dbb48a762566c2b255d"
|
| 21 |
+
folder = Path(save_folder)
|
| 22 |
+
folder.mkdir(parents=True, exist_ok=True)
|
| 23 |
+
for name in ("diffusion_models", "text_encoders", "vae"):
|
| 24 |
+
(folder / name).mkdir(exist_ok=True)
|
| 25 |
+
set_seed(42)
|
| 26 |
+
|
| 27 |
+
# The split single-file layout carries no config; record the reduced
|
| 28 |
+
# dimensions so the components can be rebuilt from the published tensors.
|
| 29 |
+
config = {
|
| 30 |
+
"transformer": {
|
| 31 |
+
"in_channels": 128,
|
| 32 |
+
"out_channels": 128,
|
| 33 |
+
"audio_in_channels": 8,
|
| 34 |
+
"audio_out_channels": 8,
|
| 35 |
+
"num_layers": 4,
|
| 36 |
+
"num_attention_heads": 2,
|
| 37 |
+
"attention_head_dim": 32,
|
| 38 |
+
"audio_num_attention_heads": 2,
|
| 39 |
+
"audio_attention_head_dim": 32,
|
| 40 |
+
"cross_attention_dim": 64,
|
| 41 |
+
"audio_cross_attention_dim": 64,
|
| 42 |
+
"caption_channels": 8,
|
| 43 |
+
"use_prompt_embeddings": False,
|
| 44 |
+
"cross_attn_mod": True,
|
| 45 |
+
"audio_cross_attn_mod": True,
|
| 46 |
+
"use_prompt_adaln_single": False,
|
| 47 |
+
"use_keyframes_abs_pos_embedding": True,
|
| 48 |
+
},
|
| 49 |
+
"connectors": {
|
| 50 |
+
"caption_channels": 8,
|
| 51 |
+
"text_proj_in_factor": 3,
|
| 52 |
+
"video_connector_num_attention_heads": 2,
|
| 53 |
+
"video_connector_attention_head_dim": 32,
|
| 54 |
+
"video_connector_num_layers": 2,
|
| 55 |
+
"video_connector_num_learnable_registers": 4,
|
| 56 |
+
"audio_connector_num_attention_heads": 2,
|
| 57 |
+
"audio_connector_attention_head_dim": 32,
|
| 58 |
+
"audio_connector_num_layers": 2,
|
| 59 |
+
"audio_connector_num_learnable_registers": 4,
|
| 60 |
+
"video_hidden_dim": 64,
|
| 61 |
+
"audio_hidden_dim": 64,
|
| 62 |
+
"per_modality_projections": True,
|
| 63 |
+
},
|
| 64 |
+
"vae": {
|
| 65 |
+
"in_channels": 3,
|
| 66 |
+
"out_channels": 3,
|
| 67 |
+
"latent_channels": 128,
|
| 68 |
+
"block_out_channels": [16, 32],
|
| 69 |
+
"decoder_block_out_channels": [16],
|
| 70 |
+
"layers_per_block": [1, 1, 1],
|
| 71 |
+
"decoder_layers_per_block": [1, 1],
|
| 72 |
+
"spatio_temporal_scaling": [True, True],
|
| 73 |
+
"decoder_spatio_temporal_scaling": [True],
|
| 74 |
+
"downsample_type": ["spatiotemporal", "spatiotemporal"],
|
| 75 |
+
"upsample_type": ["spatiotemporal"],
|
| 76 |
+
"upsample_residual": [True],
|
| 77 |
+
"upsample_factor": [2],
|
| 78 |
+
"patch_size": 4,
|
| 79 |
+
"patch_size_t": 1,
|
| 80 |
+
},
|
| 81 |
+
"audio_vae": {
|
| 82 |
+
"base_channels": 8,
|
| 83 |
+
"output_channels": 2,
|
| 84 |
+
"ch_mult": [1, 2],
|
| 85 |
+
"num_res_blocks": 1,
|
| 86 |
+
"in_channels": 2,
|
| 87 |
+
"resolution": 64,
|
| 88 |
+
"latent_channels": 8,
|
| 89 |
+
"mel_bins": 4,
|
| 90 |
+
},
|
| 91 |
+
"vocoder": {
|
| 92 |
+
"in_channels": 8,
|
| 93 |
+
"hidden_channels": 32,
|
| 94 |
+
"out_channels": 2,
|
| 95 |
+
"upsample_kernel_sizes": [8, 8],
|
| 96 |
+
"upsample_factors": [4, 4],
|
| 97 |
+
"resnet_kernel_sizes": [3],
|
| 98 |
+
"resnet_dilations": [[1, 1, 1]],
|
| 99 |
+
},
|
| 100 |
+
"text_encoder": {
|
| 101 |
+
"vocab_size": 262144,
|
| 102 |
+
"hidden_size": 8,
|
| 103 |
+
"intermediate_size": 16,
|
| 104 |
+
"num_hidden_layers": 2,
|
| 105 |
+
"num_attention_heads": 1,
|
| 106 |
+
"num_key_value_heads": 1,
|
| 107 |
+
"head_dim": 32,
|
| 108 |
+
"max_position_embeddings": 4096,
|
| 109 |
+
"tie_word_embeddings": True,
|
| 110 |
+
},
|
| 111 |
+
}
|
| 112 |
+
(folder / "config.json").write_text(json.dumps(config, indent=2) + "\n")
|
| 113 |
+
|
| 114 |
+
transformer_renames = [
|
| 115 |
+
("audio_proj_in", "audio_patchify_proj"),
|
| 116 |
+
("proj_in", "patchify_proj"),
|
| 117 |
+
("av_cross_attn_video_scale_shift", "av_ca_video_scale_shift_adaln_single"),
|
| 118 |
+
("av_cross_attn_video_a2v_gate", "av_ca_a2v_gate_adaln_single"),
|
| 119 |
+
("av_cross_attn_audio_scale_shift", "av_ca_audio_scale_shift_adaln_single"),
|
| 120 |
+
("av_cross_attn_audio_v2a_gate", "av_ca_v2a_gate_adaln_single"),
|
| 121 |
+
("video_a2v_cross_attn_scale_shift_table", "scale_shift_table_a2v_ca_video"),
|
| 122 |
+
("audio_a2v_cross_attn_scale_shift_table", "scale_shift_table_a2v_ca_audio"),
|
| 123 |
+
("norm_q", "q_norm"),
|
| 124 |
+
("norm_k", "k_norm"),
|
| 125 |
+
("audio_time_embed", "audio_adaln_single"),
|
| 126 |
+
("time_embed", "adaln_single"),
|
| 127 |
+
]
|
| 128 |
+
vae_renames = [
|
| 129 |
+
("down_blocks.1", "down_blocks.0.downsamplers.0"),
|
| 130 |
+
("down_blocks.2", "down_blocks.1"),
|
| 131 |
+
("down_blocks.3", "down_blocks.1.downsamplers.0"),
|
| 132 |
+
("down_blocks.4", "down_blocks.2"),
|
| 133 |
+
("down_blocks.5", "down_blocks.2.downsamplers.0"),
|
| 134 |
+
("down_blocks.6", "down_blocks.3"),
|
| 135 |
+
("down_blocks.7", "down_blocks.3.downsamplers.0"),
|
| 136 |
+
("down_blocks.8", "mid_block"),
|
| 137 |
+
("up_blocks.0", "mid_block"),
|
| 138 |
+
("up_blocks.1", "up_blocks.0.upsamplers.0"),
|
| 139 |
+
("up_blocks.2", "up_blocks.0"),
|
| 140 |
+
("up_blocks.3", "up_blocks.1.upsamplers.0"),
|
| 141 |
+
("up_blocks.4", "up_blocks.1"),
|
| 142 |
+
("up_blocks.5", "up_blocks.2.upsamplers.0"),
|
| 143 |
+
("up_blocks.6", "up_blocks.2"),
|
| 144 |
+
("resnets", "res_blocks"),
|
| 145 |
+
("latents_mean", "per_channel_statistics.mean-of-means"),
|
| 146 |
+
("latents_std", "per_channel_statistics.std-of-means"),
|
| 147 |
+
]
|
| 148 |
+
|
| 149 |
+
def to_comfy(state, renames):
|
| 150 |
+
output = {}
|
| 151 |
+
for key, value in state.items():
|
| 152 |
+
for comfy, diffusers in reversed(renames):
|
| 153 |
+
key = key.replace(diffusers, comfy)
|
| 154 |
+
output[key] = value
|
| 155 |
+
return output
|
| 156 |
+
|
| 157 |
+
def cast(state):
|
| 158 |
+
# The source stores modulation tables in FP32 and every other weight in BF16.
|
| 159 |
+
return {k: v.float() if "scale_shift_table" in k else v.to(torch.bfloat16) for k, v in state.items()}
|
| 160 |
+
|
| 161 |
+
transformer = LTX2VideoTransformer3DModel(**config["transformer"])
|
| 162 |
+
connectors = LTX2TextConnectors(**config["connectors"])
|
| 163 |
+
state = {
|
| 164 |
+
f"model.diffusion_model.{k}": v
|
| 165 |
+
for k, v in to_comfy(cast(transformer.state_dict()), transformer_renames).items()
|
| 166 |
+
}
|
| 167 |
+
connector_weights = cast(connectors.state_dict())
|
| 168 |
+
for key, value in connector_weights.items():
|
| 169 |
+
if key.startswith("video_connector.transformer_blocks."):
|
| 170 |
+
name = key.replace(
|
| 171 |
+
"video_connector.transformer_blocks.", "video_embeddings_connector.transformer_1d_blocks."
|
| 172 |
+
)
|
| 173 |
+
elif key.startswith("audio_connector.transformer_blocks."):
|
| 174 |
+
name = key.replace(
|
| 175 |
+
"audio_connector.transformer_blocks.", "audio_embeddings_connector.transformer_1d_blocks."
|
| 176 |
+
)
|
| 177 |
+
else:
|
| 178 |
+
continue
|
| 179 |
+
state[f"model.diffusion_model.{name.replace('norm_q', 'q_norm').replace('norm_k', 'k_norm')}"] = value
|
| 180 |
+
for side in ("video", "audio"):
|
| 181 |
+
state[f"model.diffusion_model.{side}_embeddings_connector.learnable_registers"] = connector_weights[
|
| 182 |
+
f"{side}_connector.learnable_registers"
|
| 183 |
+
]
|
| 184 |
+
save_file(state, str(folder / "diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors"))
|
| 185 |
+
|
| 186 |
+
vae = AutoencoderKLLTX2Video(**config["vae"])
|
| 187 |
+
save_file(cast(to_comfy(vae.state_dict(), vae_renames)), str(folder / "vae/ltx-2.5-video-vae-bf16.safetensors"))
|
| 188 |
+
|
| 189 |
+
audio_vae = AutoencoderKLLTX2Audio(**config["audio_vae"])
|
| 190 |
+
vocoder = LTX2Vocoder(**config["vocoder"])
|
| 191 |
+
audio_state = {f"audio_vae.{k}": v for k, v in cast(audio_vae.state_dict()).items()}
|
| 192 |
+
audio_state.update({f"vocoder.{k}": v for k, v in cast(vocoder.state_dict()).items()})
|
| 193 |
+
save_file(audio_state, str(folder / "vae/ltx-2.5-audio-vae-bf16.safetensors"))
|
| 194 |
+
|
| 195 |
+
text = Gemma4UnifiedTextConfig(**{k: v for k, v in config["text_encoder"].items() if k != "tie_word_embeddings"})
|
| 196 |
+
vision = Gemma4UnifiedVisionConfig(
|
| 197 |
+
patch_size=16, pooling_kernel_size=2, mm_embed_dim=8, mm_posemb_size=16, output_proj_dims=8
|
| 198 |
+
)
|
| 199 |
+
text_config = Gemma4UnifiedConfig(text_config=text.to_dict(), vision_config=vision.to_dict())
|
| 200 |
+
text_config.tie_word_embeddings = True
|
| 201 |
+
text_encoder = Gemma4UnifiedForConditionalGeneration(text_config)
|
| 202 |
+
text_state = {}
|
| 203 |
+
for key, value in cast(text_encoder.state_dict()).items():
|
| 204 |
+
if key == "lm_head.weight":
|
| 205 |
+
continue
|
| 206 |
+
# The source names the text tower `model.*` and the vision tower `vision_model.*`.
|
| 207 |
+
key = key.replace("model.language_model.", "model.", 1)
|
| 208 |
+
key = key.replace("model.embed_vision.", "vision_model.", 1)
|
| 209 |
+
text_state[key] = value
|
| 210 |
+
# The source keeps the connector projections and the two modality projectors
|
| 211 |
+
# inside the text encoder file.
|
| 212 |
+
projection = cast(connectors.state_dict())
|
| 213 |
+
text_state["text_embedding_projection.video_aggregate_embed.weight"] = projection["video_text_proj_in.weight"]
|
| 214 |
+
text_state["text_embedding_projection.audio_aggregate_embed.weight"] = projection["audio_text_proj_in.weight"]
|
| 215 |
+
hidden = config["text_encoder"]["hidden_size"]
|
| 216 |
+
text_state["multi_modal_projector.embedding_projection.weight"] = torch.randn(hidden, hidden).to(torch.bfloat16)
|
| 217 |
+
text_state["audio_projector.embedding_projection.weight"] = torch.randn(
|
| 218 |
+
hidden, config["connectors"]["audio_hidden_dim"]
|
| 219 |
+
).to(torch.bfloat16)
|
| 220 |
+
# The source embeds the tokenizer and processor assets as U8 tensors; keep
|
| 221 |
+
# that layout instead of publishing a second copy in a tokenizer folder.
|
| 222 |
+
assets = {
|
| 223 |
+
"tokenizer_json": "tokenizer.json",
|
| 224 |
+
"hf_asset__tokenizer_config.json": "tokenizer_config.json",
|
| 225 |
+
"hf_asset__chat_template.jinja": "chat_template.jinja",
|
| 226 |
+
"hf_asset__generation_config.json": "generation_config.json",
|
| 227 |
+
"hf_asset__processor_config.json": "processor_config.json",
|
| 228 |
+
}
|
| 229 |
+
for tensor_name, file_name in assets.items():
|
| 230 |
+
data = Path(hf_hub_download(tokenizer_source, file_name, revision=tokenizer_revision)).read_bytes()
|
| 231 |
+
text_state[tensor_name] = torch.frombuffer(bytearray(data), dtype=torch.uint8)
|
| 232 |
+
save_file(text_state, str(folder / "text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors"))
|
| 233 |
+
|
| 234 |
+
loader_source = '''"""Load the tiny LTX-2.5 split checkpoint into a Diffusers pipeline."""
|
| 235 |
+
|
| 236 |
+
import json
|
| 237 |
+
import tempfile
|
| 238 |
+
from pathlib import Path
|
| 239 |
+
|
| 240 |
+
import torch
|
| 241 |
+
from diffusers import (
|
| 242 |
+
AutoencoderKLLTX2Audio,
|
| 243 |
+
AutoencoderKLLTX2Video,
|
| 244 |
+
FlowMatchEulerDiscreteScheduler,
|
| 245 |
+
LTX2Pipeline,
|
| 246 |
+
LTX2VideoTransformer3DModel,
|
| 247 |
+
)
|
| 248 |
+
from diffusers.loaders.single_file_utils import (
|
| 249 |
+
convert_ltx2_audio_vae_to_diffusers,
|
| 250 |
+
convert_ltx2_transformer_to_diffusers,
|
| 251 |
+
convert_ltx2_vae_to_diffusers,
|
| 252 |
+
)
|
| 253 |
+
from diffusers.pipelines.ltx2.connectors import LTX2TextConnectors
|
| 254 |
+
from diffusers.pipelines.ltx2.vocoder import LTX2Vocoder
|
| 255 |
+
from safetensors.torch import load_file
|
| 256 |
+
from transformers import (
|
| 257 |
+
AutoTokenizer,
|
| 258 |
+
Gemma4UnifiedConfig,
|
| 259 |
+
Gemma4UnifiedForConditionalGeneration,
|
| 260 |
+
Gemma4UnifiedTextConfig,
|
| 261 |
+
Gemma4UnifiedVisionConfig,
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
FILES = {
|
| 265 |
+
"transformer": "diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors",
|
| 266 |
+
"text_encoder": "text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors",
|
| 267 |
+
"vae": "vae/ltx-2.5-video-vae-bf16.safetensors",
|
| 268 |
+
"audio_vae": "vae/ltx-2.5-audio-vae-bf16.safetensors",
|
| 269 |
+
}
|
| 270 |
+
ASSETS = {
|
| 271 |
+
"tokenizer_json": "tokenizer.json",
|
| 272 |
+
"hf_asset__tokenizer_config.json": "tokenizer_config.json",
|
| 273 |
+
"hf_asset__chat_template.jinja": "chat_template.jinja",
|
| 274 |
+
}
|
| 275 |
+
# Diffusers name -> Comfy single-file name for the tensors the converters drop.
|
| 276 |
+
CONNECTOR_PREFIX = {
|
| 277 |
+
"video_connector.transformer_blocks.": "video_embeddings_connector.transformer_1d_blocks.",
|
| 278 |
+
"audio_connector.transformer_blocks.": "audio_embeddings_connector.transformer_1d_blocks.",
|
| 279 |
+
"video_connector.learnable_registers": "video_embeddings_connector.learnable_registers",
|
| 280 |
+
"audio_connector.learnable_registers": "audio_embeddings_connector.learnable_registers",
|
| 281 |
+
}
|
| 282 |
+
TEXT_PREFIX = {
|
| 283 |
+
"model.language_model.": "model.",
|
| 284 |
+
"model.embed_vision.": "vision_model.",
|
| 285 |
+
}
|
| 286 |
+
PROJECTIONS = {
|
| 287 |
+
"video_text_proj_in.weight": "text_embedding_projection.video_aggregate_embed.weight",
|
| 288 |
+
"audio_text_proj_in.weight": "text_embedding_projection.audio_aggregate_embed.weight",
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def _connector_name(name):
|
| 293 |
+
for local, source in CONNECTOR_PREFIX.items():
|
| 294 |
+
if name.startswith(local):
|
| 295 |
+
name = source + name[len(local) :]
|
| 296 |
+
break
|
| 297 |
+
return name.replace("norm_q", "q_norm").replace("norm_k", "k_norm")
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def _text_name(name):
|
| 301 |
+
for local, source in TEXT_PREFIX.items():
|
| 302 |
+
if name.startswith(local):
|
| 303 |
+
return source + name[len(local) :]
|
| 304 |
+
return None
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def load_transformer(config, state):
|
| 308 |
+
model = LTX2VideoTransformer3DModel(**config["transformer"])
|
| 309 |
+
plain = {name: value for name, value in state.items() if "embeddings_connector" not in name}
|
| 310 |
+
model.load_state_dict(convert_ltx2_transformer_to_diffusers(plain))
|
| 311 |
+
return model
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def load_connectors(config, transformer_state, text_state):
|
| 315 |
+
model = LTX2TextConnectors(**config["connectors"])
|
| 316 |
+
weights = {}
|
| 317 |
+
for name in model.state_dict():
|
| 318 |
+
if name in PROJECTIONS:
|
| 319 |
+
weights[name] = text_state[PROJECTIONS[name]]
|
| 320 |
+
else:
|
| 321 |
+
weights[name] = transformer_state["model.diffusion_model." + _connector_name(name)]
|
| 322 |
+
model.load_state_dict(weights, strict=True)
|
| 323 |
+
return model
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def load_text_encoder(config, text_state):
|
| 327 |
+
text = Gemma4UnifiedTextConfig(
|
| 328 |
+
**{key: value for key, value in config["text_encoder"].items() if key != "tie_word_embeddings"}
|
| 329 |
+
)
|
| 330 |
+
gemma = Gemma4UnifiedConfig(
|
| 331 |
+
text_config=text.to_dict(),
|
| 332 |
+
vision_config=Gemma4UnifiedVisionConfig(
|
| 333 |
+
patch_size=16, pooling_kernel_size=2, mm_embed_dim=8, mm_posemb_size=16, output_proj_dims=8
|
| 334 |
+
).to_dict(),
|
| 335 |
+
)
|
| 336 |
+
gemma.tie_word_embeddings = True
|
| 337 |
+
model = Gemma4UnifiedForConditionalGeneration(gemma)
|
| 338 |
+
weights = {}
|
| 339 |
+
for name in model.state_dict():
|
| 340 |
+
source = _text_name(name)
|
| 341 |
+
if source is not None and source in text_state:
|
| 342 |
+
weights[name] = text_state[source]
|
| 343 |
+
model.load_state_dict(weights, strict=False)
|
| 344 |
+
return model
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
def load_tokenizer(text_state):
|
| 348 |
+
with tempfile.TemporaryDirectory() as directory:
|
| 349 |
+
for tensor_name, file_name in ASSETS.items():
|
| 350 |
+
Path(directory, file_name).write_bytes(text_state[tensor_name].numpy().tobytes())
|
| 351 |
+
return AutoTokenizer.from_pretrained(directory)
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def load_pipeline(model_id, device=None):
|
| 355 |
+
model_id = Path(model_id)
|
| 356 |
+
config = json.loads((model_id / "config.json").read_text())
|
| 357 |
+
transformer_state = load_file(str(model_id / FILES["transformer"]))
|
| 358 |
+
text_state = load_file(str(model_id / FILES["text_encoder"]))
|
| 359 |
+
|
| 360 |
+
transformer = load_transformer(config, transformer_state)
|
| 361 |
+
connectors = load_connectors(config, transformer_state, text_state)
|
| 362 |
+
vae = AutoencoderKLLTX2Video(**config["vae"])
|
| 363 |
+
vae.load_state_dict(convert_ltx2_vae_to_diffusers(load_file(str(model_id / FILES["vae"]))))
|
| 364 |
+
|
| 365 |
+
audio_state = load_file(str(model_id / FILES["audio_vae"]))
|
| 366 |
+
audio_vae = AutoencoderKLLTX2Audio(**config["audio_vae"])
|
| 367 |
+
audio_vae.load_state_dict(
|
| 368 |
+
convert_ltx2_audio_vae_to_diffusers(
|
| 369 |
+
{name: value for name, value in audio_state.items() if not name.startswith("vocoder.")}
|
| 370 |
+
)
|
| 371 |
+
)
|
| 372 |
+
vocoder = LTX2Vocoder(**config["vocoder"])
|
| 373 |
+
vocoder.load_state_dict(
|
| 374 |
+
{
|
| 375 |
+
name.removeprefix("vocoder."): value
|
| 376 |
+
for name, value in audio_state.items()
|
| 377 |
+
if name.startswith("vocoder.")
|
| 378 |
+
}
|
| 379 |
+
)
|
| 380 |
+
|
| 381 |
+
text_encoder = load_text_encoder(config, text_state)
|
| 382 |
+
pipeline = LTX2Pipeline(
|
| 383 |
+
scheduler=FlowMatchEulerDiscreteScheduler(),
|
| 384 |
+
vae=vae,
|
| 385 |
+
audio_vae=audio_vae,
|
| 386 |
+
text_encoder=text_encoder,
|
| 387 |
+
tokenizer=load_tokenizer(text_state),
|
| 388 |
+
connectors=connectors,
|
| 389 |
+
transformer=transformer,
|
| 390 |
+
vocoder=vocoder,
|
| 391 |
+
)
|
| 392 |
+
for module in (transformer, vae, audio_vae, text_encoder, connectors, vocoder):
|
| 393 |
+
module.eval()
|
| 394 |
+
pipeline.to(device or ("cuda" if torch.cuda.is_available() else "cpu"))
|
| 395 |
+
return pipeline
|
| 396 |
+
'''
|
| 397 |
+
|
| 398 |
+
(folder / "ltx2_loader.py").write_text(loader_source)
|
| 399 |
+
return repr(transformer)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
create_model(
|
| 403 |
+
source_model_id="Lightricks/LTX-2.5",
|
| 404 |
+
source_revision="49b093a39c19169a8a81721dcc541e07e945232f",
|
| 405 |
+
save_folder="/tmp/ltx-2-5",
|
| 406 |
+
)
|
.meta/env.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"source_model": {
|
| 3 |
+
"name": "Lightricks/LTX-2.5",
|
| 4 |
+
"commit_id": "49b093a39c19169a8a81721dcc541e07e945232f"
|
| 5 |
+
},
|
| 6 |
+
"environments": {
|
| 7 |
+
"creation": {
|
| 8 |
+
"python": "3.12.13",
|
| 9 |
+
"packages": {
|
| 10 |
+
"diffusers": "0.42.0.dev0",
|
| 11 |
+
"huggingface_hub": "2.3.0",
|
| 12 |
+
"safetensors": "0.8.0",
|
| 13 |
+
"tokenizers": "0.23.3",
|
| 14 |
+
"torch": "2.13.0+cu126",
|
| 15 |
+
"transformers": "5.22.0.dev0"
|
| 16 |
+
},
|
| 17 |
+
"cuda": "12.6",
|
| 18 |
+
"hardware": {
|
| 19 |
+
"gpu_model": "NVIDIA H20"
|
| 20 |
+
}
|
| 21 |
+
},
|
| 22 |
+
"diffusers": {
|
| 23 |
+
"python": "3.12.13",
|
| 24 |
+
"packages": {
|
| 25 |
+
"diffusers": "0.42.0.dev0",
|
| 26 |
+
"huggingface_hub": "2.3.0",
|
| 27 |
+
"safetensors": "0.8.0",
|
| 28 |
+
"tokenizers": "0.23.3",
|
| 29 |
+
"torch": "2.13.0+cu126",
|
| 30 |
+
"transformers": "5.22.0.dev0"
|
| 31 |
+
},
|
| 32 |
+
"cuda": "12.6",
|
| 33 |
+
"hardware": {
|
| 34 |
+
"gpu_model": "NVIDIA H20"
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
.meta/model.log
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
LTX2VideoTransformer3DModel(
|
| 2 |
+
(proj_in): Linear(in_features=128, out_features=64, bias=True)
|
| 3 |
+
(audio_proj_in): Linear(in_features=8, out_features=64, bias=True)
|
| 4 |
+
(time_embed): LTX2AdaLayerNormSingle(
|
| 5 |
+
(emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
|
| 6 |
+
(time_proj): Timesteps()
|
| 7 |
+
(timestep_embedder): TimestepEmbedding(
|
| 8 |
+
(linear_1): Linear(in_features=256, out_features=64, bias=True)
|
| 9 |
+
(act): SiLU()
|
| 10 |
+
(linear_2): Linear(in_features=64, out_features=64, bias=True)
|
| 11 |
+
)
|
| 12 |
+
)
|
| 13 |
+
(silu): SiLU()
|
| 14 |
+
(linear): Linear(in_features=64, out_features=576, bias=True)
|
| 15 |
+
)
|
| 16 |
+
(audio_time_embed): LTX2AdaLayerNormSingle(
|
| 17 |
+
(emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
|
| 18 |
+
(time_proj): Timesteps()
|
| 19 |
+
(timestep_embedder): TimestepEmbedding(
|
| 20 |
+
(linear_1): Linear(in_features=256, out_features=64, bias=True)
|
| 21 |
+
(act): SiLU()
|
| 22 |
+
(linear_2): Linear(in_features=64, out_features=64, bias=True)
|
| 23 |
+
)
|
| 24 |
+
)
|
| 25 |
+
(silu): SiLU()
|
| 26 |
+
(linear): Linear(in_features=64, out_features=576, bias=True)
|
| 27 |
+
)
|
| 28 |
+
(av_cross_attn_video_scale_shift): LTX2AdaLayerNormSingle(
|
| 29 |
+
(emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
|
| 30 |
+
(time_proj): Timesteps()
|
| 31 |
+
(timestep_embedder): TimestepEmbedding(
|
| 32 |
+
(linear_1): Linear(in_features=256, out_features=64, bias=True)
|
| 33 |
+
(act): SiLU()
|
| 34 |
+
(linear_2): Linear(in_features=64, out_features=64, bias=True)
|
| 35 |
+
)
|
| 36 |
+
)
|
| 37 |
+
(silu): SiLU()
|
| 38 |
+
(linear): Linear(in_features=64, out_features=256, bias=True)
|
| 39 |
+
)
|
| 40 |
+
(av_cross_attn_audio_scale_shift): LTX2AdaLayerNormSingle(
|
| 41 |
+
(emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
|
| 42 |
+
(time_proj): Timesteps()
|
| 43 |
+
(timestep_embedder): TimestepEmbedding(
|
| 44 |
+
(linear_1): Linear(in_features=256, out_features=64, bias=True)
|
| 45 |
+
(act): SiLU()
|
| 46 |
+
(linear_2): Linear(in_features=64, out_features=64, bias=True)
|
| 47 |
+
)
|
| 48 |
+
)
|
| 49 |
+
(silu): SiLU()
|
| 50 |
+
(linear): Linear(in_features=64, out_features=256, bias=True)
|
| 51 |
+
)
|
| 52 |
+
(av_cross_attn_video_a2v_gate): LTX2AdaLayerNormSingle(
|
| 53 |
+
(emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
|
| 54 |
+
(time_proj): Timesteps()
|
| 55 |
+
(timestep_embedder): TimestepEmbedding(
|
| 56 |
+
(linear_1): Linear(in_features=256, out_features=64, bias=True)
|
| 57 |
+
(act): SiLU()
|
| 58 |
+
(linear_2): Linear(in_features=64, out_features=64, bias=True)
|
| 59 |
+
)
|
| 60 |
+
)
|
| 61 |
+
(silu): SiLU()
|
| 62 |
+
(linear): Linear(in_features=64, out_features=64, bias=True)
|
| 63 |
+
)
|
| 64 |
+
(av_cross_attn_audio_v2a_gate): LTX2AdaLayerNormSingle(
|
| 65 |
+
(emb): PixArtAlphaCombinedTimestepSizeEmbeddings(
|
| 66 |
+
(time_proj): Timesteps()
|
| 67 |
+
(timestep_embedder): TimestepEmbedding(
|
| 68 |
+
(linear_1): Linear(in_features=256, out_features=64, bias=True)
|
| 69 |
+
(act): SiLU()
|
| 70 |
+
(linear_2): Linear(in_features=64, out_features=64, bias=True)
|
| 71 |
+
)
|
| 72 |
+
)
|
| 73 |
+
(silu): SiLU()
|
| 74 |
+
(linear): Linear(in_features=64, out_features=64, bias=True)
|
| 75 |
+
)
|
| 76 |
+
(rope): LTX2AudioVideoRotaryPosEmbed()
|
| 77 |
+
(audio_rope): LTX2AudioVideoRotaryPosEmbed()
|
| 78 |
+
(cross_attn_rope): LTX2AudioVideoRotaryPosEmbed()
|
| 79 |
+
(cross_attn_audio_rope): LTX2AudioVideoRotaryPosEmbed()
|
| 80 |
+
(transformer_blocks): ModuleList(
|
| 81 |
+
(0-3): 4 x LTX2VideoTransformerBlock(
|
| 82 |
+
(norm1): RMSNorm()
|
| 83 |
+
(attn1): LTX2Attention(
|
| 84 |
+
(norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 85 |
+
(norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 86 |
+
(to_q): Linear(in_features=64, out_features=64, bias=True)
|
| 87 |
+
(to_k): Linear(in_features=64, out_features=64, bias=True)
|
| 88 |
+
(to_v): Linear(in_features=64, out_features=64, bias=True)
|
| 89 |
+
(to_out): ModuleList(
|
| 90 |
+
(0): Linear(in_features=64, out_features=64, bias=True)
|
| 91 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 92 |
+
)
|
| 93 |
+
)
|
| 94 |
+
(audio_norm1): RMSNorm()
|
| 95 |
+
(audio_attn1): LTX2Attention(
|
| 96 |
+
(norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 97 |
+
(norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 98 |
+
(to_q): Linear(in_features=64, out_features=64, bias=True)
|
| 99 |
+
(to_k): Linear(in_features=64, out_features=64, bias=True)
|
| 100 |
+
(to_v): Linear(in_features=64, out_features=64, bias=True)
|
| 101 |
+
(to_out): ModuleList(
|
| 102 |
+
(0): Linear(in_features=64, out_features=64, bias=True)
|
| 103 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 104 |
+
)
|
| 105 |
+
)
|
| 106 |
+
(norm2): RMSNorm()
|
| 107 |
+
(attn2): LTX2Attention(
|
| 108 |
+
(norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 109 |
+
(norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 110 |
+
(to_q): Linear(in_features=64, out_features=64, bias=True)
|
| 111 |
+
(to_k): Linear(in_features=64, out_features=64, bias=True)
|
| 112 |
+
(to_v): Linear(in_features=64, out_features=64, bias=True)
|
| 113 |
+
(to_out): ModuleList(
|
| 114 |
+
(0): Linear(in_features=64, out_features=64, bias=True)
|
| 115 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 116 |
+
)
|
| 117 |
+
)
|
| 118 |
+
(audio_norm2): RMSNorm()
|
| 119 |
+
(audio_attn2): LTX2Attention(
|
| 120 |
+
(norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 121 |
+
(norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 122 |
+
(to_q): Linear(in_features=64, out_features=64, bias=True)
|
| 123 |
+
(to_k): Linear(in_features=64, out_features=64, bias=True)
|
| 124 |
+
(to_v): Linear(in_features=64, out_features=64, bias=True)
|
| 125 |
+
(to_out): ModuleList(
|
| 126 |
+
(0): Linear(in_features=64, out_features=64, bias=True)
|
| 127 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 128 |
+
)
|
| 129 |
+
)
|
| 130 |
+
(audio_to_video_norm): RMSNorm()
|
| 131 |
+
(audio_to_video_attn): LTX2Attention(
|
| 132 |
+
(norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 133 |
+
(norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 134 |
+
(to_q): Linear(in_features=64, out_features=64, bias=True)
|
| 135 |
+
(to_k): Linear(in_features=64, out_features=64, bias=True)
|
| 136 |
+
(to_v): Linear(in_features=64, out_features=64, bias=True)
|
| 137 |
+
(to_out): ModuleList(
|
| 138 |
+
(0): Linear(in_features=64, out_features=64, bias=True)
|
| 139 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 140 |
+
)
|
| 141 |
+
)
|
| 142 |
+
(video_to_audio_norm): RMSNorm()
|
| 143 |
+
(video_to_audio_attn): LTX2Attention(
|
| 144 |
+
(norm_q): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 145 |
+
(norm_k): RMSNorm((64,), eps=1e-06, elementwise_affine=True)
|
| 146 |
+
(to_q): Linear(in_features=64, out_features=64, bias=True)
|
| 147 |
+
(to_k): Linear(in_features=64, out_features=64, bias=True)
|
| 148 |
+
(to_v): Linear(in_features=64, out_features=64, bias=True)
|
| 149 |
+
(to_out): ModuleList(
|
| 150 |
+
(0): Linear(in_features=64, out_features=64, bias=True)
|
| 151 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 152 |
+
)
|
| 153 |
+
)
|
| 154 |
+
(norm3): RMSNorm()
|
| 155 |
+
(ff): FeedForward(
|
| 156 |
+
(net): ModuleList(
|
| 157 |
+
(0): GELU(
|
| 158 |
+
(proj): Linear(in_features=64, out_features=256, bias=True)
|
| 159 |
+
)
|
| 160 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 161 |
+
(2): Linear(in_features=256, out_features=64, bias=True)
|
| 162 |
+
)
|
| 163 |
+
)
|
| 164 |
+
(audio_norm3): RMSNorm()
|
| 165 |
+
(audio_ff): FeedForward(
|
| 166 |
+
(net): ModuleList(
|
| 167 |
+
(0): GELU(
|
| 168 |
+
(proj): Linear(in_features=64, out_features=256, bias=True)
|
| 169 |
+
)
|
| 170 |
+
(1): Dropout(p=0.0, inplace=False)
|
| 171 |
+
(2): Linear(in_features=256, out_features=64, bias=True)
|
| 172 |
+
)
|
| 173 |
+
)
|
| 174 |
+
)
|
| 175 |
+
)
|
| 176 |
+
(norm_out): LayerNorm((64,), eps=1e-06, elementwise_affine=False, bias=False)
|
| 177 |
+
(proj_out): Linear(in_features=64, out_features=128, bias=True)
|
| 178 |
+
(audio_norm_out): LayerNorm((64,), eps=1e-06, elementwise_affine=False, bias=False)
|
| 179 |
+
(audio_proj_out): Linear(in_features=64, out_features=8, bias=True)
|
| 180 |
+
)
|
README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: diffusers
|
| 3 |
+
pipeline_tag: image-to-video
|
| 4 |
+
base_model:
|
| 5 |
+
- Lightricks/LTX-2.5
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
This is a randomly initialized tiny model based on [Lightricks/LTX-2.5](https://huggingface.co/Lightricks/LTX-2.5) for testing and debugging.
|
| 9 |
+
|
| 10 |
+
| Feature | Source | This repo |
|
| 11 |
+
|---|---|---|
|
| 12 |
+
| Checkpoint tensor elements | 35.03B BF16, 4.73M F32, 32.19M U8 | 4.68M BF16, 8.45K F32, 32.18M U8 |
|
| 13 |
+
| Safetensors weight files | 70.12 GB (4 files) | 41.65 MB (4 files) |
|
| 14 |
+
| Transformer layers / video inner width | 48 / 4,096 | 4 / 64 |
|
| 15 |
+
| Video heads / head dimension | 32 / 128 | 2 / 32 |
|
| 16 |
+
| Audio inner width / heads / head dimension | 2,048 / 32 / 64 | 64 / 2 / 32 |
|
| 17 |
+
| Video / audio cross-attention width | 4,096 / 2,048 | 64 / 64 |
|
| 18 |
+
| Text connector blocks / registers | 8 / 128 | 2 / 4 |
|
| 19 |
+
| Text projection factor (layers + 1) | 49 | 3 |
|
| 20 |
+
| Text encoder layers / width / head dimension | 48 / 3,840 / 256 | 2 / 8 / 32 |
|
| 21 |
+
| Video VAE latent channels / patch size | 128 / 4 | Same |
|
| 22 |
+
| Audio VAE latent channels / mel bins | 8 / 64 | 8 / 4 |
|
| 23 |
+
| Vocoder hidden width | 1,024 | 32 |
|
| 24 |
+
| Embedded tokenizer and processor assets | U8 tensors in the text encoder file | Same layout |
|
| 25 |
+
| Stored precision | BF16 weights, FP32 modulation tables | Same |
|
| 26 |
+
|
| 27 |
+
The source is a ComfyUI split single-file pack: the transformer file also carries the text connectors, and the text encoder file carries the connector projections, two modality projectors and the tokenizer assets as U8 tensors. This repo keeps that layout and adds `config.json` plus `ltx2_loader.py`, which the split files do not contain, so the reduced components can be rebuilt. LTX-2.5's tokenizer is embedded in a 26 GB file that cannot be fetched alone, so `tokenizer_json` and the `hf_asset__*` entries come from the pinned `google/gemma-4-12b-it` release, which has the same 262,144-row vocabulary. The embedded 32.17 MB tokenizer is the floor for that file; the remaining non-tokenizer weights are about 9.5 MB, so the four safetensors files total 41.65 MB. The text encoder keeps a 32-wide attention head on an 8-wide residual width to preserve the minimum head dimension.
|
| 28 |
+
|
| 29 |
+
| File path | Size |
|
| 30 |
+
|------|------|
|
| 31 |
+
| diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors | 2.3MB |
|
| 32 |
+
| text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors | 36.5MB |
|
| 33 |
+
| vae/ltx-2.5-audio-vae-bf16.safetensors | 0.1MB |
|
| 34 |
+
| vae/ltx-2.5-video-vae-bf16.safetensors | 2.7MB |
|
| 35 |
+
|
| 36 |
+
### Example usage
|
| 37 |
+
|
| 38 |
+
#### Diffusers (✅ verified)
|
| 39 |
+
|
| 40 |
+
`ltx2_loader.py` maps the split files back to Diffusers components; this runs a 2-step text-to-video request that also decodes the audio branch.
|
| 41 |
+
|
| 42 |
+
```python
|
| 43 |
+
def run_diffusers(model_id: str):
|
| 44 |
+
import sys
|
| 45 |
+
from pathlib import Path
|
| 46 |
+
|
| 47 |
+
import torch
|
| 48 |
+
|
| 49 |
+
model_id = Path(model_id).resolve()
|
| 50 |
+
sys.path.insert(0, str(model_id))
|
| 51 |
+
from ltx2_loader import load_pipeline
|
| 52 |
+
|
| 53 |
+
pipeline = load_pipeline(model_id)
|
| 54 |
+
with torch.no_grad():
|
| 55 |
+
result = pipeline(
|
| 56 |
+
prompt="A tiny red cube rotating on a white table",
|
| 57 |
+
height=64,
|
| 58 |
+
width=64,
|
| 59 |
+
num_frames=9,
|
| 60 |
+
num_inference_steps=2,
|
| 61 |
+
output_type="pt",
|
| 62 |
+
guidance_scale=1.0,
|
| 63 |
+
stg_scale=1.0,
|
| 64 |
+
modality_scale=1.0,
|
| 65 |
+
audio_guidance_scale=1.0,
|
| 66 |
+
audio_stg_scale=1.0,
|
| 67 |
+
audio_modality_scale=1.0,
|
| 68 |
+
generator=torch.Generator(pipeline.device).manual_seed(0),
|
| 69 |
+
)
|
| 70 |
+
frames, audio = result.frames, result.audio
|
| 71 |
+
assert frames.ndim == 5 and frames.shape[0] == 1
|
| 72 |
+
assert audio.shape[0] == 1 and audio.shape[1] == 2
|
| 73 |
+
assert torch.isfinite(frames).all() and torch.isfinite(audio).all()
|
| 74 |
+
print("frames", tuple(frames.shape), "audio", tuple(audio.shape))
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
if __name__ == "__main__":
|
| 78 |
+
run_diffusers(model_id="tiny-random/ltx-2-5")
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
### Creating this repo
|
| 82 |
+
|
| 83 |
+
- Code: [.meta/create_model.py](.meta/create_model.py)
|
| 84 |
+
- Model structure: [.meta/model.log](.meta/model.log)
|
| 85 |
+
- Environment: [.meta/env.json](.meta/env.json)
|
| 86 |
+
|
| 87 |
+
### Change log
|
| 88 |
+
|
| 89 |
+
- 2026-11-01: Initial tiny LTX-2.5 split checkpoint with video and audio branches.
|
| 90 |
+
|
| 91 |
+
*This repo is generated by DeepSeek-V4.1-Flash.*
|
config.json
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"transformer": {
|
| 3 |
+
"in_channels": 128,
|
| 4 |
+
"out_channels": 128,
|
| 5 |
+
"audio_in_channels": 8,
|
| 6 |
+
"audio_out_channels": 8,
|
| 7 |
+
"num_layers": 4,
|
| 8 |
+
"num_attention_heads": 2,
|
| 9 |
+
"attention_head_dim": 32,
|
| 10 |
+
"audio_num_attention_heads": 2,
|
| 11 |
+
"audio_attention_head_dim": 32,
|
| 12 |
+
"cross_attention_dim": 64,
|
| 13 |
+
"audio_cross_attention_dim": 64,
|
| 14 |
+
"caption_channels": 8,
|
| 15 |
+
"use_prompt_embeddings": false,
|
| 16 |
+
"cross_attn_mod": true,
|
| 17 |
+
"audio_cross_attn_mod": true,
|
| 18 |
+
"use_prompt_adaln_single": false,
|
| 19 |
+
"use_keyframes_abs_pos_embedding": true
|
| 20 |
+
},
|
| 21 |
+
"connectors": {
|
| 22 |
+
"caption_channels": 8,
|
| 23 |
+
"text_proj_in_factor": 3,
|
| 24 |
+
"video_connector_num_attention_heads": 2,
|
| 25 |
+
"video_connector_attention_head_dim": 32,
|
| 26 |
+
"video_connector_num_layers": 2,
|
| 27 |
+
"video_connector_num_learnable_registers": 4,
|
| 28 |
+
"audio_connector_num_attention_heads": 2,
|
| 29 |
+
"audio_connector_attention_head_dim": 32,
|
| 30 |
+
"audio_connector_num_layers": 2,
|
| 31 |
+
"audio_connector_num_learnable_registers": 4,
|
| 32 |
+
"video_hidden_dim": 64,
|
| 33 |
+
"audio_hidden_dim": 64,
|
| 34 |
+
"per_modality_projections": true
|
| 35 |
+
},
|
| 36 |
+
"vae": {
|
| 37 |
+
"in_channels": 3,
|
| 38 |
+
"out_channels": 3,
|
| 39 |
+
"latent_channels": 128,
|
| 40 |
+
"block_out_channels": [
|
| 41 |
+
16,
|
| 42 |
+
32
|
| 43 |
+
],
|
| 44 |
+
"decoder_block_out_channels": [
|
| 45 |
+
16
|
| 46 |
+
],
|
| 47 |
+
"layers_per_block": [
|
| 48 |
+
1,
|
| 49 |
+
1,
|
| 50 |
+
1
|
| 51 |
+
],
|
| 52 |
+
"decoder_layers_per_block": [
|
| 53 |
+
1,
|
| 54 |
+
1
|
| 55 |
+
],
|
| 56 |
+
"spatio_temporal_scaling": [
|
| 57 |
+
true,
|
| 58 |
+
true
|
| 59 |
+
],
|
| 60 |
+
"decoder_spatio_temporal_scaling": [
|
| 61 |
+
true
|
| 62 |
+
],
|
| 63 |
+
"downsample_type": [
|
| 64 |
+
"spatiotemporal",
|
| 65 |
+
"spatiotemporal"
|
| 66 |
+
],
|
| 67 |
+
"upsample_type": [
|
| 68 |
+
"spatiotemporal"
|
| 69 |
+
],
|
| 70 |
+
"upsample_residual": [
|
| 71 |
+
true
|
| 72 |
+
],
|
| 73 |
+
"upsample_factor": [
|
| 74 |
+
2
|
| 75 |
+
],
|
| 76 |
+
"patch_size": 4,
|
| 77 |
+
"patch_size_t": 1
|
| 78 |
+
},
|
| 79 |
+
"audio_vae": {
|
| 80 |
+
"base_channels": 8,
|
| 81 |
+
"output_channels": 2,
|
| 82 |
+
"ch_mult": [
|
| 83 |
+
1,
|
| 84 |
+
2
|
| 85 |
+
],
|
| 86 |
+
"num_res_blocks": 1,
|
| 87 |
+
"in_channels": 2,
|
| 88 |
+
"resolution": 64,
|
| 89 |
+
"latent_channels": 8,
|
| 90 |
+
"mel_bins": 4
|
| 91 |
+
},
|
| 92 |
+
"vocoder": {
|
| 93 |
+
"in_channels": 8,
|
| 94 |
+
"hidden_channels": 32,
|
| 95 |
+
"out_channels": 2,
|
| 96 |
+
"upsample_kernel_sizes": [
|
| 97 |
+
8,
|
| 98 |
+
8
|
| 99 |
+
],
|
| 100 |
+
"upsample_factors": [
|
| 101 |
+
4,
|
| 102 |
+
4
|
| 103 |
+
],
|
| 104 |
+
"resnet_kernel_sizes": [
|
| 105 |
+
3
|
| 106 |
+
],
|
| 107 |
+
"resnet_dilations": [
|
| 108 |
+
[
|
| 109 |
+
1,
|
| 110 |
+
1,
|
| 111 |
+
1
|
| 112 |
+
]
|
| 113 |
+
]
|
| 114 |
+
},
|
| 115 |
+
"text_encoder": {
|
| 116 |
+
"vocab_size": 262144,
|
| 117 |
+
"hidden_size": 8,
|
| 118 |
+
"intermediate_size": 16,
|
| 119 |
+
"num_hidden_layers": 2,
|
| 120 |
+
"num_attention_heads": 1,
|
| 121 |
+
"num_key_value_heads": 1,
|
| 122 |
+
"head_dim": 32,
|
| 123 |
+
"max_position_embeddings": 4096,
|
| 124 |
+
"tie_word_embeddings": true
|
| 125 |
+
}
|
| 126 |
+
}
|
diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a75074c834af4c9280a902807a5a6f56d1d049a1f6ed5bf1e003b0244696e402
|
| 3 |
+
size 2338424
|
ltx2_loader.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Load the tiny LTX-2.5 split checkpoint into a Diffusers pipeline."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import tempfile
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
from diffusers import (
|
| 9 |
+
AutoencoderKLLTX2Audio,
|
| 10 |
+
AutoencoderKLLTX2Video,
|
| 11 |
+
FlowMatchEulerDiscreteScheduler,
|
| 12 |
+
LTX2Pipeline,
|
| 13 |
+
LTX2VideoTransformer3DModel,
|
| 14 |
+
)
|
| 15 |
+
from diffusers.loaders.single_file_utils import (
|
| 16 |
+
convert_ltx2_audio_vae_to_diffusers,
|
| 17 |
+
convert_ltx2_transformer_to_diffusers,
|
| 18 |
+
convert_ltx2_vae_to_diffusers,
|
| 19 |
+
)
|
| 20 |
+
from diffusers.pipelines.ltx2.connectors import LTX2TextConnectors
|
| 21 |
+
from diffusers.pipelines.ltx2.vocoder import LTX2Vocoder
|
| 22 |
+
from safetensors.torch import load_file
|
| 23 |
+
from transformers import (
|
| 24 |
+
AutoTokenizer,
|
| 25 |
+
Gemma4UnifiedConfig,
|
| 26 |
+
Gemma4UnifiedForConditionalGeneration,
|
| 27 |
+
Gemma4UnifiedTextConfig,
|
| 28 |
+
Gemma4UnifiedVisionConfig,
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
FILES = {
|
| 32 |
+
"transformer": "diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors",
|
| 33 |
+
"text_encoder": "text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors",
|
| 34 |
+
"vae": "vae/ltx-2.5-video-vae-bf16.safetensors",
|
| 35 |
+
"audio_vae": "vae/ltx-2.5-audio-vae-bf16.safetensors",
|
| 36 |
+
}
|
| 37 |
+
ASSETS = {
|
| 38 |
+
"tokenizer_json": "tokenizer.json",
|
| 39 |
+
"hf_asset__tokenizer_config.json": "tokenizer_config.json",
|
| 40 |
+
"hf_asset__chat_template.jinja": "chat_template.jinja",
|
| 41 |
+
}
|
| 42 |
+
# Diffusers name -> Comfy single-file name for the tensors the converters drop.
|
| 43 |
+
CONNECTOR_PREFIX = {
|
| 44 |
+
"video_connector.transformer_blocks.": "video_embeddings_connector.transformer_1d_blocks.",
|
| 45 |
+
"audio_connector.transformer_blocks.": "audio_embeddings_connector.transformer_1d_blocks.",
|
| 46 |
+
"video_connector.learnable_registers": "video_embeddings_connector.learnable_registers",
|
| 47 |
+
"audio_connector.learnable_registers": "audio_embeddings_connector.learnable_registers",
|
| 48 |
+
}
|
| 49 |
+
TEXT_PREFIX = {
|
| 50 |
+
"model.language_model.": "model.",
|
| 51 |
+
"model.embed_vision.": "vision_model.",
|
| 52 |
+
}
|
| 53 |
+
PROJECTIONS = {
|
| 54 |
+
"video_text_proj_in.weight": "text_embedding_projection.video_aggregate_embed.weight",
|
| 55 |
+
"audio_text_proj_in.weight": "text_embedding_projection.audio_aggregate_embed.weight",
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _connector_name(name):
|
| 60 |
+
for local, source in CONNECTOR_PREFIX.items():
|
| 61 |
+
if name.startswith(local):
|
| 62 |
+
name = source + name[len(local) :]
|
| 63 |
+
break
|
| 64 |
+
return name.replace("norm_q", "q_norm").replace("norm_k", "k_norm")
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def _text_name(name):
|
| 68 |
+
for local, source in TEXT_PREFIX.items():
|
| 69 |
+
if name.startswith(local):
|
| 70 |
+
return source + name[len(local) :]
|
| 71 |
+
return None
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def load_transformer(config, state):
|
| 75 |
+
model = LTX2VideoTransformer3DModel(**config["transformer"])
|
| 76 |
+
plain = {name: value for name, value in state.items() if "embeddings_connector" not in name}
|
| 77 |
+
model.load_state_dict(convert_ltx2_transformer_to_diffusers(plain))
|
| 78 |
+
return model
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def load_connectors(config, transformer_state, text_state):
|
| 82 |
+
model = LTX2TextConnectors(**config["connectors"])
|
| 83 |
+
weights = {}
|
| 84 |
+
for name in model.state_dict():
|
| 85 |
+
if name in PROJECTIONS:
|
| 86 |
+
weights[name] = text_state[PROJECTIONS[name]]
|
| 87 |
+
else:
|
| 88 |
+
weights[name] = transformer_state["model.diffusion_model." + _connector_name(name)]
|
| 89 |
+
model.load_state_dict(weights, strict=True)
|
| 90 |
+
return model
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def load_text_encoder(config, text_state):
|
| 94 |
+
text = Gemma4UnifiedTextConfig(
|
| 95 |
+
**{key: value for key, value in config["text_encoder"].items() if key != "tie_word_embeddings"}
|
| 96 |
+
)
|
| 97 |
+
gemma = Gemma4UnifiedConfig(
|
| 98 |
+
text_config=text.to_dict(),
|
| 99 |
+
vision_config=Gemma4UnifiedVisionConfig(
|
| 100 |
+
patch_size=16, pooling_kernel_size=2, mm_embed_dim=8, mm_posemb_size=16, output_proj_dims=8
|
| 101 |
+
).to_dict(),
|
| 102 |
+
)
|
| 103 |
+
gemma.tie_word_embeddings = True
|
| 104 |
+
model = Gemma4UnifiedForConditionalGeneration(gemma)
|
| 105 |
+
weights = {}
|
| 106 |
+
for name in model.state_dict():
|
| 107 |
+
source = _text_name(name)
|
| 108 |
+
if source is not None and source in text_state:
|
| 109 |
+
weights[name] = text_state[source]
|
| 110 |
+
model.load_state_dict(weights, strict=False)
|
| 111 |
+
return model
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def load_tokenizer(text_state):
|
| 115 |
+
with tempfile.TemporaryDirectory() as directory:
|
| 116 |
+
for tensor_name, file_name in ASSETS.items():
|
| 117 |
+
Path(directory, file_name).write_bytes(text_state[tensor_name].numpy().tobytes())
|
| 118 |
+
return AutoTokenizer.from_pretrained(directory)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def load_pipeline(model_id, device=None):
|
| 122 |
+
model_id = Path(model_id)
|
| 123 |
+
config = json.loads((model_id / "config.json").read_text())
|
| 124 |
+
transformer_state = load_file(str(model_id / FILES["transformer"]))
|
| 125 |
+
text_state = load_file(str(model_id / FILES["text_encoder"]))
|
| 126 |
+
|
| 127 |
+
transformer = load_transformer(config, transformer_state)
|
| 128 |
+
connectors = load_connectors(config, transformer_state, text_state)
|
| 129 |
+
vae = AutoencoderKLLTX2Video(**config["vae"])
|
| 130 |
+
vae.load_state_dict(convert_ltx2_vae_to_diffusers(load_file(str(model_id / FILES["vae"]))))
|
| 131 |
+
|
| 132 |
+
audio_state = load_file(str(model_id / FILES["audio_vae"]))
|
| 133 |
+
audio_vae = AutoencoderKLLTX2Audio(**config["audio_vae"])
|
| 134 |
+
audio_vae.load_state_dict(
|
| 135 |
+
convert_ltx2_audio_vae_to_diffusers(
|
| 136 |
+
{name: value for name, value in audio_state.items() if not name.startswith("vocoder.")}
|
| 137 |
+
)
|
| 138 |
+
)
|
| 139 |
+
vocoder = LTX2Vocoder(**config["vocoder"])
|
| 140 |
+
vocoder.load_state_dict(
|
| 141 |
+
{
|
| 142 |
+
name.removeprefix("vocoder."): value
|
| 143 |
+
for name, value in audio_state.items()
|
| 144 |
+
if name.startswith("vocoder.")
|
| 145 |
+
}
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
text_encoder = load_text_encoder(config, text_state)
|
| 149 |
+
pipeline = LTX2Pipeline(
|
| 150 |
+
scheduler=FlowMatchEulerDiscreteScheduler(),
|
| 151 |
+
vae=vae,
|
| 152 |
+
audio_vae=audio_vae,
|
| 153 |
+
text_encoder=text_encoder,
|
| 154 |
+
tokenizer=load_tokenizer(text_state),
|
| 155 |
+
connectors=connectors,
|
| 156 |
+
transformer=transformer,
|
| 157 |
+
vocoder=vocoder,
|
| 158 |
+
)
|
| 159 |
+
for module in (transformer, vae, audio_vae, text_encoder, connectors, vocoder):
|
| 160 |
+
module.eval()
|
| 161 |
+
pipeline.to(device or ("cuda" if torch.cuda.is_available() else "cpu"))
|
| 162 |
+
return pipeline
|
text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:56630d832fc655dbb30211c578fb7b9b9d1b89b243489d8183db2a8194014335
|
| 3 |
+
size 36488059
|
vae/ltx-2.5-audio-vae-bf16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ad3fecd37ef0b7e53ec7773d50dbe7e857a398ac33a411deebc19fab5bf1eb96
|
| 3 |
+
size 120144
|
vae/ltx-2.5-video-vae-bf16.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6ec970706a5b559838e97bd663dea51d2e26135f43b12c479e249192a6f67d56
|
| 3 |
+
size 2706862
|