Skip to content

Models

All models are config-driven mlx.nn.Modules with from_pretrained / save_pretrained from ModelMixin. Inputs/outputs are channels-last (B, H, W, C).

DiT — Diffusion Transformer

The general-purpose backbone: patchify → adaLN-Zero transformer → unpatchify. Pairs naturally with flow-matching; supports optional class conditioning with classifier-free-guidance dropout.

from mlx_diffuser import DiT, DiTConfig

model = DiT(DiTConfig(
    in_channels=3, patch_size=2, hidden_size=384, depth=12, num_heads=6,
    num_classes=1000,        # 0 => unconditional
))
out = model(x, t, y)          # (B, H, W, C)

adaLN-Zero means an untrained DiT outputs zeros (identity residual path), which stabilizes training.

UNet2D

A Stable-Diffusion-style convolutional denoiser with down/mid/up skip connections, per-level attention, and optional cross-attention for text conditioning:

from mlx_diffuser import UNet2D, UNet2DConfig

unet = UNet2D(UNet2DConfig(
    in_channels=4, out_channels=4,
    block_out_channels=(320, 640, 1280),
    cross_attention_dim=768,   # enables text conditioning via `context`
))
out = unet(latents, t, context=text_embeddings)

AutoencoderKL (VAE)

Maps images ↔ latents for latent diffusion:

from mlx_diffuser import AutoencoderKL, AutoencoderKLConfig

vae = AutoencoderKL(AutoencoderKLConfig(in_channels=3, latent_channels=4))
posterior = vae.encode(image)             # DiagonalGaussian
latents = posterior.sample() * vae.scaling_factor
recon = vae.decode(latents / vae.scaling_factor)

Video models — VideoDiT & AutoencoderKLVideo

The video backbone (the architecture behind LTX-Video and the WAN series): a video latent (B, T, H, W, C) is patchified over all three axes, processed by transformer blocks combining adaLN-Zero timestep conditioning, 3D-RoPE self-attention, and text cross-attention, then unpatchified. VideoDiTConfig ships presets that mirror the published model shapes:

from mlx_diffuser import VideoDiT, VideoDiTConfig

model = VideoDiT(VideoDiTConfig.wan_t2v_1_3b())   # or .wan_t2v_14b(), .ltx_video()
out = model(latents, t, context=text_embeddings)  # (B, T, H, W, C)

AutoencoderKLVideo is the matching causal-3D-convolution VAE: it compresses a video both spatially (2 ** (len(block_out_channels) - 1)) and temporally (temporal_compression) into latents. Causal time convolutions mean a frame only depends on itself and past frames.

from mlx_diffuser import AutoencoderKLVideo, AutoencoderKLVideoConfig

vae = AutoencoderKLVideo(AutoencoderKLVideoConfig(
    in_channels=3, latent_channels=16,
    block_out_channels=(128, 256, 512),   # spatial compression 4
    temporal_compression=4,
))
latents = vae.encode(video).sample() * vae.scaling_factor

TextToVideoPipeline and examples/text_to_video.py show the full sampling loop, including the --quantize low-memory path. The architectures are implemented from scratch; loading official LTX-Video / WAN weights needs a separate checkpoint converter.

Saving & loading

model.save_pretrained("my-model")                         # config.json + safetensors
model = DiT.from_pretrained("my-model", dtype="bf16", quantize=4)
model.save_pretrained("my-model", push_to_hub="me/my-model")   # needs [hub]

Quantization is a load-time choice and works for every model, including VideoDiTVideoDiT.from_pretrained(path, quantize=4) weight-quantizes the transformer so large video models fit in 16 GB of unified memory.