| --- |
| datasets: |
| - tiny_imagenet |
| language: en |
| library_name: jax |
| license: mit |
| pipeline_tag: text-to-image |
| tags: |
| - diffusion-model |
| - flax |
| - flax-nnx |
| - jax |
| - unet |
| - tiny-imagenet |
| - conditional-image-generation |
| - from-scratch |
| --- |
| |
| # Conditional MediumUNet Diffusion Model (Flax NNX, JAX) |
|
|
| A class-conditional, from-scratch DDPM-style diffusion model trained on **Tiny ImageNet ($64 imes 64 imes 3$, 200 classes)**, built using **JAX** and **Flax NNX**. |
| The model architecture (`ConditionalUNet`) predicts the clean image from a noisy input, conditioned simultaneously on a sinusoidally embedded continuous noise level and a class label embedding. |
|
|
| ## Model Details |
|
|
| - **Architecture:** Medium U-Net with Class Conditioning (featuring sinusoidal time embeddings, learnable class embeddings, strided convolutions for downsampling, transposed convolutions for upsampling, and functional residual skip connections) |
| - **Conditioning:** |
| 1. Scalar noise level `amount ∈ [0, 1]` via sinusoidal positional embeddings. |
| 2. Class index `label ∈ [0, 199]` via dense embedding vectors injected into the residual blocks. |
| - **Framework:** JAX / Flax NNX |
| - **Training data:** Tiny ImageNet (200 classes) — full 100,000-image Tiny ImageNet training set |
| - **Optimizer:** AdamW with warmup-cosine-decay LR schedule, gradient clipping (global norm 1.0) |
| - **Epochs:** 120 |
| - **Batch size:** 256 |
|
|
| ## Exported Formats |
|
|
| This repository bundles multiple export formats of the same trained weights: |
|
|
| | File / Folder | Format | Notes | |
| |---|---|---| |
| | `model_checkpoint/` | Orbax checkpoint | Native JAX/Flax NNX state, load with `orbax.checkpoint` | |
| | `conditional_medium_unet.mlir` | MLIR | StableHLO dialect, via `jax.export` | |
| | `conditional_medium_unet.stablehlo` | StableHLO bytecode | Portable compiled representation (**with 100% baked-in weights**) | |
|
|
| ## Usage (JAX / Flax NNX Native) |
|
|
| ```python |
| import orbax.checkpoint as ocp |
| from flax import nnx |
| |
| # Recreate the model skeleton, then restore the trained parameters |
| model = ConditionalUNet(in_features=3, out_features=3, num_classes=200, rngs=nnx.Rngs(0)) |
| graphdef, abstract_state = nnx.split(model) |
| |
| checkpointer = ocp.Checkpointer(ocp.StandardCheckpointHandler()) |
| state = checkpointer.restore("model_checkpoint", abstract_state) |
| model = nnx.merge(graphdef, state) |
| ``` |
|
|
| ## ⚡ How to use the StableHLO Model (Production Inference) |
|
|
| The ultimate advantage of this pipeline is that you **don't need the Python architecture class anymore, nor do you need to manage raw weight parameters!** Because the model was exported via a functional static JIT trace, all trained weights are **fully baked directly into the StableHLO bytecode binary**. The compiled graph acts as a completely autonomous black-box function. |
|
|
| Here is how to properly load and execute the pre-compiled StableHLO graph to perform a full iterative diffusion reverse loop using pure JAX with the **HQ quadratic schedule (80 steps)**: |
|
|
| ```python |
| import jax |
| import jax.numpy as jnp |
| import numpy as np |
| |
| # 1. Load the pre-compiled StableHLO bytecode from file |
| with open("conditional_medium_unet.stablehlo", "rb") as f: |
| stablehlo_bytecode = f.read() |
| |
| # 2. Deserialize it back into a callable JAX artifact and JIT compile it |
| loaded_artifact = jax.export.deserialize(stablehlo_bytecode) |
| compiled_fn = jax.jit(loaded_artifact.call) |
| |
| # 3. Initialize pure normal noise (batch_size=1, 64x64, 3 channels) |
| key = jax.random.PRNGKey(42) |
| x = jax.random.normal(key, shape=(1, 64, 64, 3)) |
| |
| # Choose class label (e.g., 42 for 'mantis') |
| CHOSEN_CLASS = 42 |
| sample_label = jnp.array([CHOSEN_CLASS], dtype=jnp.int32) |
| |
| # 4. Set up the HQ Quadratic Schedule (80 steps) |
| n_steps = 80 |
| steps_line = jnp.linspace(1.0, 0.0, n_steps + 1) |
| ts = steps_line ** 2 |
| |
| print("🚀 Running conditional inference loop via independent StableHLO runtime...") |
| for i in range(n_steps): |
| t_curr = ts[i] |
| t_next = ts[i + 1] |
| current_amount = jnp.array([t_curr], dtype=jnp.float32) |
| |
| # 🔥 LIGHTNING-FAST HARDWARE INFERENCE: |
| # Pass image, time step, and class label. No model state or weights needed! |
| pred_clean = compiled_fn(x, current_amount, sample_label) |
| pred_clean = jnp.clip(pred_clean, -1.0, 1.0) |
| |
| if i == n_steps - 1: |
| x = pred_clean |
| break |
| |
| # DDIM Mathematical Correction |
| eps_estimated = (x - (1.0 - t_curr) * pred_clean) / (t_curr + 1e-8) |
| x = (1.0 - t_next) * pred_clean + t_next * eps_estimated |
| |
| # Rescale from [-1, 1] back to [0, 1] for image display |
| final_image = np.array((x + 1.0) / 2.0)[0] |
| print("✅ Image generated successfully! Output shape:", final_image.shape) |
| ``` |
|
|
| ## Technical Parameters & Design Choices |
|
|
| - **Color & Resolution:** Native support for $64 imes 64$ RGB images, a massive step up in spatial complexity compared to standard MNIST benchmarks. |
| - **Class-Conditional Latent Guidance:** Embedding-driven injection allows targeted generation of specific objects among 200 distinct classes, turning the network from an unconditional sampler into a controllable asset. |
| - **Production Readiness:** Ideal for specialized target applications on low-power edge nodes or cross-language backends (C++, Rust) via the native StableHLO runtime interface. |
|
|
|
|