Show-o and Discrete-Diffusion Unified Models
Transfusion mixes continuous and discrete representations. Show-o (Xie et al., August 2024) goes the other way: text tokens use causal next-token prediction, and image tokens use masked discrete diffusion in the spirit of MaskGIT. Both sit inside one transformer with a hybrid attention mask. That one backbone covers VQA, text-to-image, inpainting, and mixed-modality generation, with one tokenizer per modality and one loss family (next-token prediction extended to masked prediction). This lesson walks the Show-o design, explains why masked discrete diffusion is a parallel, few-step image generator, and contrasts it with Transfusion and Emu3.
Learning Objectives
- Explain masked discrete diffusion: the schedule that masks tokens uniformly then asks the transformer to recover them.
- Compare parallel image decoding (Show-o, MaskGIT) to autoregressive image decoding (Chameleon, Emu3) on speed and quality.
- Name the four tasks Show-o handles in one checkpoint: text generation, VQA, T2I, image inpainting.
- Pick a masking schedule (cosine, linear, truncated) and reason about its effect on sample quality.
The Problem
Transfusion's two-loss training works, but the dynamics are trickier. The continuous diffusion loss lives on a different numerical scale from the discrete NTP loss, so balancing the loss weights becomes a hyperparameter search. The architecture is effective and also complex.
Show-o keeps both modalities discrete, like Chameleon, but generates images in parallel with masked discrete diffusion instead of one token at a time. The training objective collapses into a single masked-token prediction that generalizes next-token prediction.
The Concept
Masked discrete diffusion (MaskGIT)
The original Chang et al. (2022) MaskGIT trick is simple. Start from a fully masked image, where every token is the special <MASK> id. At each step, predict all the masked tokens in parallel, keep the top-K most confident predictions, and leave the rest masked. After ~8-16 iterations every token is filled in. How many tokens to unmask per step is a tuned schedule, and cosine schedules work well.
Training is simple too. Sample a masking ratio uniformly from [0, 1], apply it to the image's VQ tokens, and train the transformer to recover the masked ones. That is what BERT did for text, scaled up to image generation.
Show-o: one transformer, hybrid mask
Show-o puts MaskGIT inside a causal-language-model transformer. The attention mask is:
- Text tokens: causal (standard LLM).
- Image tokens: full bidirectional within the image block, so the masked tokens can see every other image token during prediction.
- Text-to-image: text attends to prior images, image attends to prior text.
Training alternates between:
- Standard NTP on text sequences.
- T2I samples: text → image with masked image tokens, masked-token-prediction loss.
- VQA samples: image → text with masked text tokens (really just NTP).
The unified loss is cross-entropy on <MASK> tokens. It covers text NTP (only the last token is "masked") and image masked-diffusion (a random subset is masked) with the same formulation.
Parallel sampling
Show-o generates an image in ~16 steps instead of ~1000 (autoregressive per token) or ~20 (diffusion). Each step is a full transformer pass that predicts all masked tokens at once, commits the top-K confident ones, and repeats.
Compare:
- Chameleon / Emu3 (autoregressive over tokens): N_tokens forward passes, typically 1024-4096 per image.
- Transfusion (continuous diffusion): ~20 steps, each a full transformer pass.
- Show-o (masked discrete diffusion): ~16 steps, each a full transformer pass.
Show-o is faster than Chameleon at similar-scale models. It roughly matches Transfusion's step count at a lower per-step cost, because it computes discrete vocab logits instead of a continuous MSE loss.
Tasks in one checkpoint
Show-o supports four tasks at inference, selected by prompt format:
- Text generation: standard autoregressive text output.
- VQA: image in, text out.
- T2I: text in, image out via masked discrete diffusion.
- Inpainting: image with some tokens masked, fill in.
Inpainting comes from the masked-prediction training with no extra work. Mask a region of the VQ-token grid, feed in the rest plus a text prompt, and predict the masked tokens.
Masking schedule
The schedule of how many tokens to unmask per step shapes quality. Show-o recommends cosine:
mask_ratio(t) = cos(pi * t / (2 * T)) # t = 0..T
At step 0, all tokens are masked (ratio 1.0). At step T, none are. Cosine concentrates mass on mid-range ratios, where prediction is most informative. Linear schedules also work but plateau faster.
Show-o2
Show-o2 (2025 follow-up, arXiv 2506.15564) scales Show-o with a larger LLM base, a better tokenizer, and an improved mask schedule. The architectural pattern stays the same.
Where Show-o sits
In the 2026 taxonomy:
- Discrete tokens + NTP: Chameleon, Emu3. Simple, but inference is slow.
- Discrete tokens + masked diffusion: Show-o, MaskGIT, LlamaGen, Muse. Parallel sampling, still lossy because of the tokenizer.
- Continuous + diffusion: Transfusion, MMDiT, DiT. Highest quality, more complex training.
- Continuous + flow matching in a VLM: JanusFlow, InternVL-U. Newest.
Pick by task. Use Show-o when you want T2I, inpainting, and VQA in one open model at reasonable speed. Use Transfusion when quality matters most and you can afford the two-loss plumbing.
Use It
code/main.py simulates Show-o sampling:
- A toy grid of 16 VQ tokens.
- A mock "transformer" that predicts logits based on a prompt and the currently unmasked tokens.
- Parallel masked sampling over 8 steps with a cosine schedule.
- Printed intermediate states (the mask pattern evolving) and the final tokens.
Run it and watch the mask dissolve step by step.
Ship It
This lesson produces outputs/skill-unified-gen-model-picker.md. Give it a product that needs both understanding (VQA, captioning) and generation (T2I, inpainting) under an open-weights constraint, and it picks between the Show-o family, the Transfusion/MMDiT family, and the Emu3 / Chameleon family, with concrete trade-offs.
I'd run main.py and read the trace before touching the math. It prints the cosine schedule for T=8, then one sampling run over 16 tokens with a vocab of 8, showing the masked count at each step. The masked count goes 16, 14, 12, 10, 8, 5, 2, 1, 0: two tokens per step at first, three in the middle, then single stragglers at the end.
Keep in mind what the toy is. The transformer is a mock: mock_logits adds noise, a bias toward a token set by the prompt seed and position, and a bonus for tokens already committed. So the trace shows the sampling loop and nothing about image quality. Once a token is committed here it stays, and the tokens that aren't picked simply remain masked for the next pass.
One detail I'd check by hand is how the schedule becomes a commit count. keep_ratio is max(0.15, 1 - ratio), applied to the tokens still masked rather than to all 16. That is why the first step commits only 2 tokens (int(16 * 0.15)). It is also why a few stragglers are left for the late steps: int() floors the count, and at step 7 the ratio reaches 0, so keep_ratio becomes 1.0 and the last masked position is committed. The while loop after the schedule is only a fallback and does not run in this trace. The four tasks printed at the bottom are just print statements. The loop is real, but VQA and inpainting are not run, so treat them as a claim from The Concept about the training objective.
Exercises
-
Masked discrete diffusion samples in ~16 steps. Why not 1? What breaks if you unmask everything at step 0?
-
Inpainting is free with masked diffusion. Propose a product use case (real or hypothetical) where Show-o's inpainting beats a specialist model.
-
Cosine schedule vs linear schedule: trace the number of unmasked tokens per step for T=8. Which is more balanced?
-
A 512x512 Show-o image is 1024 tokens. At vocab K=16384, the model emits 1024 * log2(16384) = 14,336 bits (~1.75 KiB) of data. Stable Diffusion outputs 51251224 bits = 6,291,456 bits (~768 KiB) of raw pixels. What is the compression ratio and what quality does it buy?
-
Read LlamaGen (arXiv:2406.06525). How is LlamaGen's class-conditional autoregressive image model different from Show-o's masked approach?
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Masked discrete diffusion | "MaskGIT-style" | Training to predict masked tokens; at inference, iteratively unmask the most-confident predictions |
| Cosine schedule | "Unmask schedule" | Decay of mask ratio over inference steps; concentrates confidence growth at mid-range |
| Parallel decoding | "All tokens at once" | Every step predicts the full sequence of masked tokens in one forward pass, then commits top-K |
| Hybrid attention | "Causal + bidirectional" | Mask that is causal over text tokens and bidirectional within image blocks |
| Inpainting | "Fill-in generation" | Condition on an image with some tokens masked, predict the missing ones; free from the training objective |
| Commitment rate | "Top-K per step" | How many tokens are declared "done" per iteration; controls inference vs quality trade-off |
Code
- main.py (python, 115 lines)