Book Deep Read · Generative AI System Design Interview · Ch 9
Generative AI System Design Interview · Ch 9 · Ali Aminian, Hao Sheng & Alex Xu · 2024
You type "a shiba inu wearing an astronaut helmet, watercolour" into your phone, and seconds later an image appears. That pipeline is this chapter. But its subject is not how to make the picture look good — the previous chapter already settled that. From start to finish it chases one question: how do you make that sentence actually govern that picture?
A model that paints beautifully and a model that obeys are two different things. You say "wearing a helmet" and it hands you a gorgeous shiba with no helmet. What makes this failure nasty is that it does not throw an error and it does not leave a blank — it quietly drops half of what you asked for. Worse, the automatic scoring tools will still rate it highly, because the picture really does look like a real photograph.
The trick is that the sentence is not mentioned once at the start and then forgotten. The model paints by wiping and redoing the canvas over and over, and every time it redoes a small patch, it goes back and rereads that sentence — like a cook who keeps glancing at the order slip. Working on the dog's head, it asks "anything specified about the head?"; working on the background, it asks "what style again?". That is why "helmet" lands where it belongs instead of smearing across the whole canvas.
That alone is still not obedient enough. The move that actually works is slightly counterintuitive: in the same step, have the model paint twice — once reading your request, once ignoring it completely. The difference between the two is the direction your sentence pulls the picture in. And then the system is not content with that much difference: it keeps pushing further along that same direction. Want it more obedient? Turn up how far it pushes.
That multiplier is a dial you can turn in real time, and it is the most valuable design on the whole pipeline: without retraining anything, you can slide between well-behaved and wild.
Turn it down and the picture is lovely but freewheeling — your details go in one ear and out the other. Turn it up and it follows you to the letter, but the image stiffens, the colours go garish, the compositions all start to rhyme: obedient to the point of lifeless. Most products settle somewhere in the middle and expose a slider for the rest.
There is also a cheap by-product. Since one of the two passes ignores your request anyway, you may as well point that pass at the things you don't want ("blurry, extra fingers, watermark") — and the push becomes "get away from these". That is the "negative prompt" in drawing tools. It is not folklore, it is the same move seen from behind.
The cost is blunt: painting twice per step doubles the compute and the electricity bill.
The real engineering problem in text-to-image is not fidelity, it is obedience: rereading the sentence at every brushstroke puts each requirement in the right place, and painting twice and then over-shooting along the difference turns obedience into a dial you can turn live — tighter means more faithful, but also stiffer, and it costs double.
Want the mechanisms, notation and diagrams? → Switch to the deep read
The previous chapter solved "fast and large"; this one supplies the other half: control. The answer is two moves — cross-attention injects the text into every denoising layer so each position can look back and query the sentence, and classifier-free guidance (CFG) extrapolates the "obedience direction" by a multiplier at sampling time, turning compliance into a dial you can turn live without retraining. What is most often underrated here is the layers outside the model: safety, rewriting, negative prompts and caching.
512×512×3 → 64×64×4), denoise on the sketch, then decode back to full size — the core of the previous chapter.Question 8 of the ten case studies in Part II, and the middle link of the book's image trilogy: Ch7 (GAN faces) solves looking real, Ch8 (latent diffusion) solves being affordable, this one solves being right, and it sets up Ch10 — where the condition stops being a sentence and becomes a handful of selfies. It maps onto the full pipeline behind Stable Diffusion, DALL·E, Imagen and Midjourney.
The diffusion model from the last chapter gives you a beautiful but ungoverned image. What the product wants is "a shiba inu wearing an astronaut helmet, watercolour" — the model has to land subject, attribute, relation and style all at once, and missing any one of them means the user rerolls.
What happens if you do not solve it? You get the most expensive failure mode in generative systems: fluently wrong — no error, no blank, just a quiet omission of "wearing a helmet" at first-rate image quality. Worse, the automatic metrics cannot see it: FID measures whether a batch of images looks like real photographs, so a high-quality image that ignores the prompt entirely can still post a beautiful FID. So this chapter's problem is really a pair: how the condition gets in (mechanism), and how you measure whether it was heard (evaluation).
A cost constraint squeezes the design space further. A 512×512, 50-step request takes seconds on a single A100 — and CFG below runs the network twice on every step, so per-request cost is multiplied by 2. Here, "how obedient" is not an academic parameter; it is a dial billed by the multiple.
Step one hands the prompt to a text encoder — usually an off-the-shelf language model kept frozen during training — which emits one vector per token (77 token slots in the Stable Diffusion line). That sequence is the "condition" everything downstream reads. Not training the encoder, and letting the diffusion network learn to read it, saves compute and makes the text side swappable as a block — which is exactly why picking an encoder is a genuine system decision. Three routes:
A useful analogy: CLIP's text tower is an art director who has seen ten million captioned pictures — one word and they know roughly what it looks like, but they lose the thread of a long sentence. T5 is a literary editor who has only ever read books — impeccable at parsing structure, with no picture in their head.
Given the text vectors, the naive move is to add them to the network's input. That works badly — it is a one-shot, global remark, and by the time the network is painting a local patch it no longer remembers.
What works is a cross-attention layer inside every block of the denoising network (U-Net or DiT): each position in the latent emits a Query, the text tokens emit Key / Value. In plain terms — while working on the top-left patch, the network goes back and asks the sentence again: "what should this patch look like?", then fetches information from each word by relevance.
This explains a lot. Why does "helmet" land on the dog's head rather than smearing across the canvas? Because attention is inherently per-position asking, so a soft "who listens to whom" alignment table grows on its own during generation; the hat region and the scarf region are querying different words of the same sentence.
The cost is small: text is only a few dozen tokens, so this layer is nearly free next to self-attention over the whole image. The trap is elsewhere — that alignment table is soft and unsupervised, so "red hat, blue scarf" often swaps into a blue hat and a red scarf, and "three cats" often comes out as two. Attribute binding and counting are structural weaknesses; no amount of tuning fixes them.
With cross-attention alone, the model treats the prompt more like a suggestion. To push compliance to the limit, the chapter's protagonist arrives.
Its predecessor is classifier guidance: train a separate model that can classify noisy images and use its gradient to steer sampling toward a target class. Two problems — you must train an extra noise-aware classifier, and it can only steer toward a "class", never an arbitrary sentence.
CFG's idea is to throw that classifier away entirely. During training, randomly drop the condition (about one sample in ten is fed an empty prompt), so one set of weights learns both "denoise while reading the prompt" and "denoise reading nothing". At sampling time you run both, get two predictions, and extrapolate along their difference:
ε̂ = ε_uncond + w · (ε_cond − ε_uncond)
In plain terms: the difference between the two predictions is the direction this sentence drags the picture in, and w is how many times you amplify that pull. w=1 is no amplification (ordinary conditional generation); w=7 means travelling six times further along it. Intuitively it tells the model: don't just consult the sentence — go further toward looking like it.
Three properties to remember. First, it lives at sampling time and never touches the weights — the same checkpoint can expose w as a user-facing slider. Second, it costs two forward passes per step, doubling compute and latency (batching the two branches together absorbs some overhead, but the FLOPs really are doubled). Third, turning it up is not free money: adherence rises while diversity and realism fall — oversaturation, formulaic composition, and at extreme w the numerical blowouts that Imagen's dynamic thresholding was introduced to suppress.
This is the most system-design-ish section of the chapter, and the easiest to dismiss as product detail. The user's sentence cannot go straight to the model; at least four layers sit in between:
(prompt, seed, steps, w, model version) the cache key: the same inputs must yield the same image. One design buys three things — reproducibility (support can replay what the user saw), auditability (you can trace which version produced it) and savings (a hit skips the entire denoising loop). In a product with viral prompts, this is the single biggest saving.Two decisions in this chapter are actually actionable: what to use on the text path, and how high to set w.
Table 1 · Choosing a text encoder
| Choice | Good at | Cost / weakness | When to pick it |
|---|---|---|---|
| CLIP text tower | Native visual semantics, high hit rate on style and subject words; light, fast, mature ecosystem | Short context (~77 tokens in that line); weak on long sentences, negation, counting | Consumer products dominated by short, stylised prompts |
| Pure text LLM (T5 family) | Markedly better on long sentences, clauses, counting and spatial relations | Never saw an image, so diffusion-side training is harder; the encoder itself is large, raising VRAM and cold-start cost | Long prompts, and anywhere "what you say is what you get" really matters |
| Dual encoders | Visual intuition and grammatical precision together; in practice the most reliable tier today | Two sets of weights, two encoding passes; complexity and VRAM both go up | Flagship models (SDXL takes this route); the default when cost can be amortised |
Table 2 · Setting the CFG scale w
| Range | You get | You pay | Typical use |
|---|---|---|---|
w ≈ 1–3 | High diversity, natural images; the same prompt yields genuinely different results | Details in the prompt are routinely ignored; complex instructions are barely followed | Exploration — "give me a few directions" |
w ≈ 5–8 | The balance zone of adherence and naturalness; where most products land by default | Attribute mix-ups and counting errors persist, just less often | The general-purpose default, usually also exposed as a slider |
w ≳ 10 | Maximum literal compliance; specified elements almost always appear | Oversaturation, stiff composition, collapsing diversity; colour blowouts at extremes | Controlled cases that must match item by item, paired with something like dynamic thresholding |
One trade-off will not fit in a table: which side rewriting lives on. Inference-side expansion ships fast and rolls back instantly, but adds a hop of latency and a token bill to every request; training-side (retraining on detailed synthetic captions) is more thorough but takes weeks and cannot be reverted quickly. Most teams ship the inference side first and only then consider moving it into training.
Asked in an interview "how do you make it obey better", answering "use a bigger model" is not an answer. This chapter gives three separable handles: swap the text encoder (expensive, needs retraining), change how the condition is injected (expensive, touches architecture), tune w and add a rewriting layer (cheap, shippable today). Read right to left by cost, that is an action order you can recite.
Evaluation is equally examinable. Reporting one number is wrong: FID measures realism and is nearly blind to obedience; CLIP score measures adherence but can be gamed by painting the keywords in literally. Worse, the two move in opposite directions along w — turn w up and CLIP score rises while FID degrades. So you sweep w and report a curve, not a point, and still backstop it with human evaluation.
w before reaching for a different model.w no longer implies two passes. The skeleton of "cross-attention injection plus sampling-time extrapolation" has not changed.① The thesis: the hard part of text-to-image is not fidelity (settled last chapter) but control; the most expensive failure is being fluently wrong — no error, no blank, half the request quietly dropped, and FID still scores it well.
② Text first passes through a frozen encoder into token vectors; choosing CLIP's text tower (visual intuition), the T5 family (grammatical precision) or both (SDXL's route) is a genuine system decision.
③ The condition is injected by cross-attention: inside every denoising block, every position queries the sentence for itself — which is why "helmet" lands on the dog's head. The alignment table is soft, so attribute binding and counting stay unreliable.
④ CFG throws the classifier away: drop the condition at random during training, run conditional and unconditional passes at sampling time, extrapolate w× along the difference.
⑤ w is a sampling-time dial that never touches the weights: it can be a user slider, and usually defaults to 5–8. The cost is two forward passes per step, compute ×2, and turning it up sacrifices diversity and realism.
⑥ Negative prompts are a free by-product of CFG — swap the unconditional branch for "what you don't want" and the extrapolation runs away from it, at zero extra compute.
⑦ The model is one box in the pipeline: two-sided safety, rewriting/expansion, and a cache key of (prompt, seed, steps, w, model version) take the other half of the engineering — and buy reproducibility, auditability and savings at once.
⑧ Evaluation must come in pairs: FID and CLIP score move in opposite directions along w, so report a curve, not a point, and still backstop with humans — which is why Google shipped 1,600 PartiPrompts alongside its FID.