This section is about steering an image generator that is already trained. A text prompt describes a whole family of images, but people often want one particular image. They want this pose, this face, or this layout, or a real photo with one thing changed. Retraining a large generator for each wish is out of the question, so every method here leaves most of the trained model untouched.
Here is the problem in one concrete case. A designer has a pencil sketch of a car's outline, a product photo from the client, and a sentence. No sentence can say "this outline, this exact logo, and change only the sky". Retraining is not an option either, since training Stable Diffusion 1.5, an open text-to-image model, took about 150,000 A100 GPU hours (§34.1). So the question is practical. How do we give a trained generator that much control without retraining it, and without breaking what it already knows?
The section answers in four parts. First come new input paths that carry a sketch, a depth map, or a reference photo into the frozen model. We prove that the safest of them start by changing nothing at all. Then come ways to teach the model one new subject, such as your dog, from a few photos. Then come five ways to edit a real photo, by noising it, by inverting it to noise, by steering attention, by instruction, and by filling a masked region. Last, we ask how anyone can tell a generated image from a real one, and what watermarks and signed records can and cannot prove.
A few facts from earlier chapters run through the whole section, so here they are in brief. The generators work on a latent, a small grid of numbers into which an autoencoder compresses an image and from which it expands it back (§34.1). Training mixes a clean latent z0 with Gaussian noise ϵ∼N(0,I) along a straight line, zt=(1−t)z0+tϵ. The time t runs from 0, clean data, to 1, pure noise. This straight path is the one a rectified flow uses (§33.6).
The network that removes noise is the denoiser. It reads the noisy latent zt, the time t, and a condition c such as a prompt. It predicts either the noise, written ϵθ, or the velocityvθ(zt,t,c)≈E[ϵ−z0∣zt], the direction from data toward noise. A sampler makes an image by starting from pure noise at t=1 and stepping against the velocity down to t=0.
One more tool appears everywhere. Classifier-free guidance makes the model follow its condition more strictly (§33.5). It runs the network twice per step, once with the condition and once with an empty prompt, and extrapolates from the second prediction through the first, v∅+γ(vc−v∅). Here vc and v∅ are the two predictions and γ is the guidance scale. The value γ=1 is plain conditional sampling, and larger values follow the prompt harder. Some papers write the scale as w or s, and we translate their numbers into γ.
Three ways to steer a frozen generator
A frozen model is one whose weights we do not change. Every method in this section keeps the big generator frozen, or almost frozen, and does one of three things.
Add an input path. New layers carry a new condition, such as an edge map, a depth map, or a reference photo, into the frozen denoiser. The design question is how to add them without disturbing the model when training starts. The cleanest answer is a path whose output starts at exactly zero.
Change a few parameters. To teach the model a new subject, we train as little as one new word vector. Larger options are a low-rank update to some weight matrices, a chosen subset of the weights, or all of them with a penalty that protects the old behavior.
Change the sampling, not the model. Nothing is trained. We start sampling from a noised copy of a photo, or find the noise from which the model regenerates it. Or we rewrite attention while sampling, or overwrite the known part of an image at every step.
The first family rests on one trick, a new branch whose last layer starts at zero. Here it is with numbers, before any architecture.
So a branch that ends in a zero layer starts as an exact no-op and still learns. Two facts make that work. The frozen output passes through unchanged, and the zero layer still sees a nonzero input, so it receives a gradient. The rest of this part scales the toy up to a real model.
ControlNet, a trainable copy behind two zero layers
ControlNet adds a spatial condition, such as an edge map, a depth map, or a human pose skeleton, to a frozen text-to-image model (Zhang, Rao, and Agrawala 2023). It is the toy above at full scale. To see where its branch goes, recall the diffusion U-Net of §34.2. Its encoder is a stack of blocks that shrinks the latent grid level by level. A middle block works on the smallest grid, and a decoder grows the grid back. Skip connections hand each encoder level's features straight to the decoder level of the same size. The encoder is where the layout of the image is analyzed, so that is where a spatial condition should enter.
Two more terms are needed. A 1×1 convolution applies the same linear map, plus a bias, to the channel vector at every pixel. A zero convolution is a 1×1 convolution whose weights and bias start at zero.
In plain words, the locked model keeps doing what it did. The copy reads the condition and computes corrections, with a backbone that already understands images. The zero layers decide how much of each correction reaches the locked model, and at the start they let none of it through.
Why copy the encoder instead of training a small new network? A fresh network would have to learn to read images from the condition data alone. The paper tested this. Replacing each copied block by a single convolution gave a variant the authors call ControlNet-lite, and it failed whenever the prompt did not already describe the scene. Why zero layers instead of ordinary ones? The same study replaced the zero convolutions by convolutions with Gaussian random weights, and quality fell to the level of ControlNet-lite. The authors attribute this to the random layers damaging the copy's pretrained weights early in fine-tuning.
The copy is not free. The cell builds the Stable Diffusion 1.5 U-Net layer by layer from its released configuration, checks the total against the known size, and then counts the ControlNet.
Python · cell 1
Why the zero layers do no harm and still learn
The hand example generalizes. The argument is the one that showed a DiT block starts as the identity when its modulation layer starts at zero, the adaLN-Zero initialization of §34.2. A 1×1 convolution acts at each position (i,j) as an affine map, Z(h)ij=Whij+b. So its gradients are sums over positions of outer products, exactly as for a linear layer applied to each token (§0.2).
∂ Proof of the zero-start theorem
With Wz1=0 and bz1=0 the copy's input is x, so h=F(x;Θc)=F(x;Θ). With Wz2=0 and bz2=0 the second term is zero, which is item 1. At each position the output zero layer computes Wz2hij+bz2, and the linear-layer gradients give item 2 and dhij=Wz2⊤dyc,ij=0. Everything that affects the loss only through h (the copy's weights, the input zero layer, and the condition encoder that produces cf) therefore gets zero gradient, which is item 3. After a step with learning rate η, Wz2=−ηdWz2=0, so dh=0 in general. The input zero layer then receives dWz1=∑ijduijcf,ij⊤, where du=JF⊤dh is the gradient at the copy's input. That is item 4. ∎
In plain words, at the start the output zero layer blocks everything behind it, but it still sees the copy's features. So it is the only part that learns on the first step, and its gradient measures how well those features line up with what the loss wants. The copy and the input layer start to learn only once the output layer is nonzero. Two consequences follow. The first steps of training cannot inject random features into the frozen model. And the copy's pretrained weights are never pushed around by gradients that passed through a random layer.
The cell checks the theorem on a toy with one tanh block of width 8. The branch must learn to add an effect of the control signal to the frozen block's output.
Python · cell 2
At step 0 only the output layer has a gradient, and from step 1 on all three do. The widget runs the same toy and lets you replace the zero layers by random ones.
How can a new control branch start doing nothing and still learn?
A frozen tanh block plus a trainable copy, joined by two new layers that start at zero or at random.
Loading interactive figure…
Small random starts behave almost like zero, because the branch's output and its noisy gradients both scale with the initial weights. Zero is the limit of that. It is the one choice that is exactly harmless at any learning rate and any depth. An interviewer who asks "why zero and not small random?" wants this answer.
Core
A ControlNet block and its gradients
Build the toy ControlNet of the theorem and check its gradients. The frozen block is F(U;W)=tanh(UW⊤). It acts on a batch of rows U∈Rn×d, with weights W∈Rd×d. For inputs X∈Rn×d and controls C∈Rn×dc, the controlled block and its loss are
Implement controlnet_init(W, d_c). It returns a dict with the frozen W, a trainable copy Wc that is a separate array, and two zero layers, Z1 of shape (d,dc) and Z2 of shape (d,d).
Implement controlnet_forward_and_grads(X, C, Y_tgt, p). It returns (Y, L, grads), where the dict grads holds the gradients of L with respect to Z1, Z2, and Wc, under those keys.
The tests check the theorem at initialization, compare your gradients with finite differences away from it, and check that one step opens the branch.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Training and using a ControlNet
Training uses the base model's ordinary denoising loss, with the condition image as an extra input. Only the copy, the zero layers, and the small condition encoder are trained. One detail matters a lot. The authors replaced half of the training prompts by the empty string. That forces the copy to read the condition image itself instead of leaning on the text.
At sampling time a conditioning scale multiplies the ControlNet's outputs before they are added. It trades how strictly the image follows the condition against how freely it follows the prompt. Several ControlNets compose by adding their outputs, for example pose from one and depth from another, and the paper found that this needs no extra weighting.
Guidance needs one decision, because the control can join either prediction or both. Suppose it joins both the conditional and the unconditional prediction. Then with an empty prompt the two predictions are identical, and guidance does nothing. Suppose instead it joins only the conditional prediction. Then the whole control effect sits inside vc−v∅, guidance multiplies it by γ, and the control becomes very strong. The authors add it to the conditional prediction only and then weight each level by its resolution, as the box below explains.
Lighter adapters for structure and for image prompts
A ControlNet is heavy because it copies the encoder. Two lighter designs keep the base model fully frozen and add far fewer weights.
The first is the T2I-Adapter (Mou et al. 2023), a small separate network for structure. It rearranges the 512×512 condition image onto the 64×64 latent grid by moving blocks of pixels into channels. Four feature blocks, with three downsamplings between them, turn the result into four feature maps. Their shapes match the four levels of the U-Net encoder, and each is added to the encoder's features at its level, F^enci=Fenci+Fci. The adapter has about 77 million parameters.
The adapter does not read the time t. So its features are computed once per image and reused at every sampling step, while a ControlNet runs at every step. The price is that the adapter cannot adapt its features to the noise level. Several adapters compose with weights, Fc=∑kωkFADk(Ck), where FADk is the k-th adapter and Ck its condition image. A sketch for structure plus a color palette is a typical pair.
The second, IP-Adapter (Ye et al. 2023), solves a different problem. It lets an image act as a prompt, as in "in the style of this" or "this object". Text enters the U-Net through cross-attention. There the image tokens supply the queries, and the text tokens supply the keys and values (§4.6, §34.3). The obvious move is to append image tokens to the text tokens. The authors found that this works poorly, and gave the image its own attention instead.
Why keep two softmaxes when one longer softmax over all the tokens would be simpler? The theorem below says what each choice does to the text path.
∂ Proof that one softmax blends the two attentions
Item 1 is the definition. For item 2, write st,j for the query's scaled score on text key j. The joint softmax's denominator is Zt+Zi. Split the weighted sum of values into its text part and its image part, and multiply and divide each by its own partial normalizer,
and the same for the image part. This is the identity that §34.2 proved for joint attention, where image and text tokens share one softmax. ∎
In plain words, concatenation makes the image compete with the text for each query's attention. How much the image takes is set by the query's own scores, and the user cannot control it. Concatenation also forces the frozen text projections to handle image tokens they never saw. Decoupling adds the image's contribution on top of an untouched text path, with a dial λip that returns exactly the base model at zero.
One caveat is worth stating. IP-Adapter does not start as the identity during training. Its new projections are copies of WK and WV, which speeds up training, and its image tokens come from a newly initialized projection.
Python · cell 3
The total, 22.3 million, matches the paper's "about 22M". That is 2.6 percent of the 860 million parameters of the U-Net it steers. Because the base model stays frozen, the same adapter works with fine-tuned versions of that base model and alongside ControlNets.
Teaching the model one new subject
You have four photos of your dog and want "your dog on the moon". Teaching a trained model one new subject from a few images is called personalization. An image encoder like IP-Adapter needs no training per subject, but it generalizes only as far as its own training data. Fine details such as a particular coat pattern often do not survive one embedding. The alternative is to optimize something for this subject alone, which costs minutes per subject. The methods below differ in what they optimize, from one word vector up to every weight.
Textual inversion, one new word
The smallest thing we can train is the vector for one new word. The text encoder starts by looking each token up in a table of embeddings, one learned vector per token (§1.2). We add one row to that table and train only that row.
Textual inversion comes from Gal et al. 2022. They initialized the new embedding from a one-word description such as "cat" or "sculpture", and ran 5,000 steps at batch size 4 on two V100 GPUs. For Stable Diffusion the result is 768 numbers, 3 KB, and it works inside any prompt.
Its ceiling is the frozen model. One embedding can only steer the model toward images it can already make from some input. Fine identity details that no input reaches stay out of reach. That limit is the reason the authors of the next method fine-tuned the weights instead.
Core
Textual inversion on a frozen linear denoiser
Train one new embedding against a frozen toy model, and see its ceiling. The setup builds a frozen linear denoiser that predicts the clean image as x^0=atzt+btMc, with fixed numbers at and bt for each noise level and a fixed matrix M. The prompt embedding is c=q+Pv. Here q is what the rest of the prompt contributes, P is a frozen linear text encoder, and v∈R5 is the new token's embedding. The noisy inputs are fixed, ztj=(1−tj)xi+tjϵij, for N=4 images xi and J=6 noise levels tj.
Implement ti_loss_grad(v, prob). It returns the loss L(v)=NJ1∑i,j∥x^0(ztj,c)−xi∥2 and its gradient with respect to v.
Implement textual_inversion(prob, lr, steps), gradient descent from v=0. It returns the final v and a list of losses, one per step, each computed before that step's update.
Nothing in prob may change. The tests also check that the loss stays large at the optimum, which is the ceiling in action.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
DreamBooth, fine-tuning with the model's own samples as a guard
DreamBooth fine-tunes the whole denoiser on the subject's photos (Ruiz et al. 2022). Each photo is captioned with a unique identifier next to the subject's class, as in "a [V] dog". The class word lets the model reuse what it knows about dogs. The identifier should be a token the model associates with almost nothing. A real word like "special" carries a meaning the model would have to unlearn. A random string of letters is split into single-letter tokens, which carry strong meanings of their own. So the authors sampled rare tokens from the vocabulary. For the Imagen model they used tokens of at most three characters with ids between 5,000 and 10,000 in its T5 vocabulary.
Fine-tuning every weight on four photos fails in two ways. The model forgets how to make other dogs, so "a dog" starts producing your dog. This is called language drift, a term the authors borrowed from work on fine-tuning language models. And its outputs collapse onto the few poses and views in the photos. The fix is to keep training on the model's own idea of the class.
In plain words, the first term teaches the model your dog. The second keeps asking it to make dogs the way it used to, with its own earlier samples as the data. The weight λ sets how far the class may move. The authors used λ=1. For Stable Diffusion they trained about 1,000 steps at learning rate 5×10−6, about 5 minutes on one A100.
A linear toy shows how the prior term works. A frozen "generator" maps a prompt embedding c to a mean image Mc. The prompt "a [V] dog" is the sum of a small rare-token vector and the "dog" vector. We fine-tune M on one subject, with and without the prior term.
Python · cell 4
Without the prior term, 91 percent of the subject's offset leaks into every "a dog". The reason is that the gradient of a squared error in Mu is an outer product with u. So each step changes M only along the training prompt's embedding u=eV+edog. That direction is mostly "dog". With the prior term, the same fit puts the subject on the rare token and leaves the class where it was. A prompt orthogonal to the training prompt, here "a cat", does not move in either case. That is why drift shows up first in the class and its neighbors. The toy tracks only the mean image, so it shows drift but not the second failure, the loss of variety, which the prior term also counters.
Custom Diffusion and LoRA, fine-tuning less
Fine-tuning every weight gives a full copy of the model per subject. Two methods train far less.
The first, Custom Diffusion (Kumari et al. 2022), started from a measurement. When a text-to-image model is fine-tuned on a new concept, the weights that change most are in the cross-attention layers. There, only the key and value projections read the text (§34.3). So Custom Diffusion trains only those projections, plus a new modifier token V∗. It guards against drift with about 200 real images from a web dataset whose captions closely match the target prompt, instead of generated ones.
The cell above counted those matrices at 19.2 million numbers, 77 MB in 32-bit floats. That is consistent with the paper's report of storing 75 MB instead of 3 GB. Training took about 6 minutes on two A100s. Two concepts trained separately can be merged afterwards, by solving a constrained least-squares problem on the key and value matrices in closed form.
The second is LoRA (§14.5). It restricts the update of a frozen weight matrix W0 to a product of two thin matrices, h=W0x+rαBAx. Here A has r rows, B has r columns, the rank r is small, and B=0 at the start. That is the same zero start as ControlNet's output layer. We write the scale as α/r, not γ, to keep γ for guidance.
In open-source tooling the common practice is DreamBooth's loss with LoRA adapters instead of full fine-tuning. The cell above shows why the files are tiny. Rank 4 on the cross-attention keys and values is 198,144 numbers, 0.4 MB in 16-bit floats. Adapters on every attention projection of the SD 1.5 U-Net, self-attention and cross-attention alike, take 1.6 MB at rank 4 and 13 MB at rank 32.
Stacking several LoRAs is not free. Their updates add, ΔW=ΔW1+ΔW2. So each acts on the other's prompts wherever their input directions overlap, and the network's nonlinearities compound the interaction. That is why two concepts trained separately can blend into each other when loaded together.
Method
What is trained
Size at SD 1.5 scale
Time per subject
Strength
Typical failure
Textual inversion
one token embedding
768 numbers, 3 KB
about 5,000 steps
composable, tiny
cannot reach details the frozen model cannot make
DreamBooth
all denoiser weights
860M, 3.4 GB in fp32
about 1,000 steps, 5 minutes on an A100
highest fidelity
language drift and lost variety without the prior term, overfitting
Custom Diffusion
cross-attention WK,WV and a token
19.2M, 77 MB
250 steps, 6 minutes on 2 A100s
several concepts at once, mergeable
less fidelity than full fine-tuning on some subjects
DreamBooth with LoRA
low-rank adapters
0.2M to a few M
minutes
small files, swappable per user
adapters interfere when stacked
Encoders (IP-Adapter, PhotoMaker)
nothing per subject
one shared adapter
one forward pass
instant
identity detail limited by one embedding
In-context references (FLUX.2, Nano Banana Pro)
nothing per subject
none
one generation
instant, many references
closed or very large models, attention cost grows with references
In an interview the question is usually which row to pick for a product. The answer turns on fidelity, storage per user, and latency, and the second interview question at the end walks through it.
Editing a real image by noising it
Suppose you have a crude painting, a few colored strokes for a sky, a field, and a tree, and you want a photograph with that layout. A diffusion model can start sampling from any noise level, not only from pure noise. Under enough noise, the painting and a real photo with the same layout look alike, since the noise hides the difference in texture. So noise the painting to that level and let the model denoise it into a photo. This method is called SDEdit (Meng et al. 2021).
This is the strength argument of every image-to-image pipeline. Libraries round differently. The diffusers library runs ⌊t0S⌋ steps. It starts from the point of its fixed sampling grid that leaves exactly that many steps, which can lie a little below t0. SDXL's refiner, a second model that adds a little noise to the base model's output and denoises it again (§34.1), is SDEdit at a low strength.
What does the strength trade? For Gaussian data we can answer exactly. The result needs one quantity, the fraction of the guide that survives.
∂ Proof, by moving to the coordinates of the fast-sampling section
Divide by the signal coefficient, y=zt/(1−t) and σ=t/(1−t), so that y=x0+σϵ. In these coordinates §33.4 showed that every Gaussian diffusion has the same probability-flow ODE, dy/dσ=(y−D(y;σ))/σ, with D the posterior mean of x0. For Gaussian data, D(y;σ)=μ+s2+σ2s2(y−μ), the Wiener filter of §33.3. Substituting,
since the right side's logarithmic derivative is 21dlog(s2+σ2)/dσ=σ/(s2+σ2). At σ=0 the output is μ+(y0−μ)s/s2+σ02, where σ0=t0/(1−t0) and y0=g+σ0ϵ. Multiplying numerator and denominator by 1−t0 shows that s/s2+σ02=ρ(t0). So the output is μ+ρ(g−μ)+ρσ0ϵ, with variance ρ2σ02=s2σ02/(s2+σ02)=s2(1−ρ2), which is item 1.
For item 2, the exact reverse process started at y0 ends at a sample from the posterior p(x0∣y0), because the reverse-time process has the same joint law as the forward one (§33.3). That posterior is Gaussian with mean μ+k(y0−μ) and variance s2(1−k), where k=s2/(s2+σ02)=ρ2. Averaging over ϵ, the mean is μ+ρ2(g−μ) and the variance is k2σ02+s2(1−k). Using σ02=s2(1−k)/k, the first term is s2k(1−k), and the total is s2(1−k)(1+k)=s2(1−ρ4).
If g∼N(μ,s2), item 1's output has variance ρ2s2+s2(1−ρ2)=s2 and item 2's has ρ4s2+s2(1−ρ4)=s2. In d dimensions, rotate to the eigenvectors of Σ. The forward process adds the same noise in every direction, so the coordinates stay independent and each obeys the one-dimensional result. ∎
In plain words, SDEdit keeps a fraction ρ of how far the guide is from typical data and replaces the rest with fresh, realistic variation. The fraction falls from 1 at t0=0 to 0 at t0=1. The stochastic sampler keeps only ρ2, so at the same strength it is less faithful to the guide than the deterministic one.
The cell runs SDEdit with an exact denoiser and Euler steps, and compares the simulated output with the theorem over a range of strengths.
Python · cell 5
The simulated means and variances match the theorem to within the error of the Euler steps. At t0=1 the variance is 0.975 instead of 1. That gap is the error of Euler's method, which is first order in the step size (§33.4). It is about 0.05 with 50 steps and about 0.01 with 200.
The last line holds the most useful intuition. In d dimensions each direction keeps its own fraction. At t0=0.5, a direction with standard deviation 3 keeps 95 percent of the guide's deviation, while one with standard deviation 0.1 keeps 10 percent. Natural images put most of their variance in coarse, low-frequency structure and little in fine detail (§33.1). So a moderate strength keeps the guide's layout and colors and regenerates its texture, which is exactly what one wants from a stroke painting. Meng et al. found that strengths between 0.3 and 0.6, in the time variable of their model, worked for reasonable guides. A guide far from any real image forces a larger strength and a looser match.
Interview
SDEdit with an exact denoiser
Implement sdedit(z0, t0, denoise, S, rng) in three steps.
Noise the guide z0 to zt0=(1−t0)z0+t0ϵ, drawing ϵ with rng.standard_normal(z0.shape).
Take n=⌈t0S⌉ equal Euler steps of the flow ODE from t0 down to 0. Each step calls denoise(z, t) once, which returns x^0, and moves along the velocity (z−x^0)/t.
For t0=0, return the input unchanged.
The setup provides gauss_denoiser(s), the exact denoiser for N(0,s2) data, and the tests compare your outputs with the theorem.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Inverting an image, and why guidance breaks it
SDEdit throws away the guide's fine detail by design. To edit a real photograph while keeping everything except the thing we change, we want the opposite. We find the exact noise at t=1 from which the model's deterministic sampler regenerates the photo. Then we run the sampler again from that noise with a modified prompt.
Running the deterministic sampler backward like this is DDIM inversion. §33.4 introduced it as a property of the sampling ODE, the differential equation that the deterministic sampler solves step by step. An ODE with a smooth velocity carries each noise to exactly one image, so it can be run backward from the image to its noise. In this chapter's coordinates DDIM inversion is one line, because for a rectified flow a DDIM step and an Euler step are the same step. An Euler step moves from the current point along the current velocity for one step size.
So DDIM inversion on a grid 0=t(0)<t(1)<⋯<t(S)=1 is zk+1=zk+hkv(zk,t(k)), with step hk=t(k+1)−t(k). It reads the velocity at the point it stands on, the only point it has. Generation walks the same grid down, z^k=z^k+1−hkv(z^k+1,t(k+1)). So each step of the round trip reads the velocity at two different points, one at each end of the step. That mismatch is the whole source of error, as a toy we can do by hand shows.
The toy already shows the three facts this part proves and then measures. The error is first order in the step size. Guidance makes it larger. And inverting with one guidance scale and regenerating with another never converges. To state the first two in general we need one term. A field is L-Lipschitz if moving the input by a distance δ moves the output by at most Lδ.
∂ Proof of the round-trip bound
Let ek=z^k−zk, so eS=0. Subtract the inversion step zk+1=zk+hkv(zk,t(k)) from the generation step,
after adding and subtracting hkv(zk+1,t(k+1)). The first bracket is at most L∥ek+1∥ by the Lipschitz condition and the second at most Mhk, so ∥ek∥≤(1+Lhk)∥ek+1∥+Mhk2. Unrolling from k=S−1 down to 0 gives the sum, in which the error made at step k is amplified by every later backward step j<k. Each factor satisfies 1+Lhj≤eLhj and the steps sum to 1, so the product is at most eL, and ∑khk2≤h∑khk=h. For the guided field, ∥vγ(a)−vγ(b)∥≤∣1−γ∣∥v∅(a)−v∅(b)∥+γ∥vc(a)−vc(b)∥, and the same holds for the change along the path. ∎
In plain words, each round-trip step errs by the step size times how much the velocity changed during the step. Later steps amplify earlier errors by at most the field's Lipschitz constant. The error is first order, so doubling the number of steps halves the bound. Guidance multiplies the difference between the conditional and unconditional predictions by γ. So the guided velocity changes faster along the path and is more sensitive to where it is read, and the bound grows with γ.
A bound says only what can happen. The two cells below measure what does happen, on a mixture of three 2D Gaussian classes. For this data both branches of guidance are known exactly, so every error we see comes from the sampler.
Python · cell 6
Python · cell 7
The second cell shows the three facts at scale. First, inverting and regenerating with the same guidance follows one ODE both ways, so the error is pure discretization. With 50 steps it grows with γ, from 0.028 at γ=1 to 0.102 at γ=7.5. It also falls in proportion to the step size, fourfold from 50 to 200 steps, first order as the theorem says.
Second, the inverted noise at γ=7.5 is nothing like a Gaussian draw. Its mean is (11.6,−4.5) and its standard deviations are near 7 and 8, instead of 0 and 1. Mokady et al. 2022 observed the same for Stable Diffusion. There, inverting with a large guidance scale gives a noise vector that may fall outside the Gaussian distribution, which hurts editing.
Third, and worst for editing, is the mixed case. Inverting without guidance gives good, typical noise, and regenerating with guidance is what makes edits take effect. But these follow two different ODEs, which never meet however small the steps. So the error stays at 0.24 for γ=2 and 0.81 for γ=7.5, at 50 steps and at 200. Editing needs guidance, so plain inversion cannot be both accurate and editable. That is the interview answer to "why does DDIM inversion fail with classifier-free guidance?"
Null-text inversion, bending the guided path back
The fix keeps the accurate unguided inversion as a reference. Then it adjusts the one input of the guided step that the user does not care about. In classifier-free guidance the unconditional branch reads an embedding of the empty prompt, the null embedding∅. Changing it per step bends the guided path without changing the model or the prompt.
In plain words, the unguided inversion finds where the image's path goes, and the per-step null embeddings bend the guided path back onto it. Mokady et al. used 50 DDIM steps, guidance 7.5, and 10 optimization iterations per step with early stopping, about one minute per image on an A100. They found that one embedding per step reconstructs far better than a single shared one.
In our toy, the stand-in for the null embedding is the unconditional branch's three mixture weights, three numbers per step. We fit them by Gauss-Newton, which linearizes the step around the current weights and solves the resulting least-squares problem, a few times per grid point.
Python · cell 8
Optimizing the null branch brings the guided reconstruction error from between 0.24 and 0.81 down to 0.030, about the round-trip error of unguided inversion itself. It cannot go lower here, for a reason worth understanding. Once the noise is low enough that a point's class is unambiguous, the mixture weights no longer change the prediction. So the null branch has no grip on the last steps. A real null embedding has tens of thousands of numbers and a grip on every step, and Mokady et al. report reconstructions close to what the autoencoder alone allows.
The widget runs the three methods on one image of the same mixture, which you can drag. Dashed lines invert it to noise and solid lines regenerate it with guidance. The plot below draws the round-trip error of all three methods against γ, the current one in bold.
Why does guidance make DDIM inversion drift?
Drag the real image. Dashed inverts it to noise, solid regenerates it with guidance toward the shaded class.
Loading interactive figure…
Research
Inversion error against guidance
Measure the three facts above yourself. The setup gives velocity(z, t, gamma), the exact guided velocity for class 0 of a three-class 2D mixture, and real_images(n). Implement three functions.
ddim_invert(x, S, gamma) takes Euler steps upward on the grid np.linspace(0, 1, S + 1).
ddim_generate(z, S, gamma) takes Euler steps downward on the same grid.
round_trip_errors(x, S, gammas, gamma_invert=None) returns one error per γ in gammas. For each, it inverts with gamma_invert, or with γ itself when gamma_invert is None, regenerates with γ, and measures the mean Euclidean distance to x.
The tests check the theorem's predictions. Inversion without guidance is nearly exact, the error grows with γ, it converges at first order, and mismatched guidance does not converge.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Editing by steering attention
Inversion gives us the noise. To change only the dog into a cat, the edited generation must also keep the original's layout, and the layout lives in the cross-attention maps. Recall from §34.3 that the cross-attention weights form a matrix A∈RT×Tc, with one row per image token and one column per prompt token. The column for a word, its attention map, marks where on the image that word is read. These maps fix the composition early in sampling. So copying them from one generation to another copies the composition. That observation is the basis of Prompt-to-Prompt (Hertz et al. 2022).
The threshold trades fidelity to the edit against fidelity to the layout. In the paper's example of turning a bicycle into a car, injecting the source maps at every step forced the car into a bicycle's shape. Injecting them for only the early steps kept the scene and let the shape change.
The method needs no training. It only runs the two generations as one batch. But it has two requirements. The prompts must share structure, since the maps are matched token by token. And for a real image the source generation must reproduce the photo, which is what null-text inversion provides. That is why the two methods are usually used together.
Editing by instruction and in context
Inversion and attention control work, but they ask the user for a full description of the image and need a minute of optimization per photo. The current approach trains the model to take the image itself as an input, together with a sentence that says what to change.
InstructPix2Pix (Brooks, Holynski, and Efros 2022) had no editing data, so it made some. The authors wrote 700 triplets by hand, each an image caption, an edit instruction, and the edited caption. They fine-tuned the language model GPT-3 on these, and it generated a corpus of 454,445 triplets from captions in LAION, a web-scraped image-caption dataset. For each caption pair, Stable Diffusion with Prompt-to-Prompt generated 100 candidate before-and-after image pairs, and a CLIP-based filter kept the consistent ones.
The model starts from Stable Diffusion 1.5 with extra input channels in its first convolution. It reads the noisy latent stacked with the latent of the input image. The weights on the new channels start at zero, the same zero start once more. With two conditions, the input image cI and the instruction cT, guidance gets two scales.
In plain words, the first difference pushes toward the input image, and the second pushes toward the instruction given that image. Raising sT makes the edit stronger, and raising sI keeps more of the input's structure. The authors found sT between 5 and 10 and sI between 1 and 1.5 usually best. An edit took about 9 seconds on an A100.
∂ Where the two-scale formula comes from
The formula is classifier-free guidance applied to a chain rule of probabilities. By Bayes, logp(z∣cI,cT)=logp(z)+logp(cI∣z)+logp(cT∣cI,z)+const. So the conditional score is the unconditional one plus two terms. One is ∇logp(z∣cI)−∇logp(z), for the image. The other is ∇logp(z∣cI,cT)−∇logp(z∣cI), for the instruction given the image. Scaling each term separately and converting scores to noise predictions (§33.5) gives the definition. ∎
In-context editing removes the special input channels. FLUX.1 Kontext (Black Forest Labs 2025) encodes the input image with the same frozen autoencoder as the output. It appends the input's latent tokens y to the target's tokens x in the image stream of its flow transformer. It starts from the FLUX.1 text-to-image model and trains with the plain rectified-flow loss ∥vθ(zt,t,y,c)−(ϵ−x)∥2, on millions of curated (input, instruction, output) triples.
The context tokens are clean, never noised. They are told apart from the target by their position code. Each token carries a 3D rotary position (i,h,w). The first coordinate is 0 for every target token and i for every token of the i-th context image, while the spatial positions (h,w) of the images still line up. When there is no input image its tokens are left out, so one model does both generation and editing. The authors also tried stacking the input along channels, as InstructPix2Pix does, and found it worse. FLUX.1 Kontext edits a 1024×1024 image in 3 to 5 seconds. Its open [dev] version is a transformer with 12 billion parameters. It is guidance-distilled, meaning it was trained to output the guided prediction in one network call instead of two.
Inpainting, filling a masked region
Inpainting regenerates the pixels inside a region so that they fit the rest of the image. The region is given by a mask m, which is 1 where the image is known and 0 in the hole. The training-free method works with any model. It overwrites the known part at every step with a noised copy of the original.
The re-noising formula is the forward process run from t′ to t. Given z0, it maps N((1−t′)z0,t′2I) to a Gaussian with mean κ(1−t′)z0=(1−t)z0 and variance κ2t′2+t2−κ2t′2=t2, which is the forward distribution at t. The square root is real because t(1−t′)≥t′(1−t) when t≥t′.
Why is replacement alone not enough? §34.5 worked this out for extending a video. The model's update of the hole moves toward E[z0hole∣zt], the best guess given the whole noisy image. Correct inpainting needs E[z0hole∣zt,z0known], the best guess given the clean known region too. The freshly noised known region carries only part of that information. Resampling gives the model several chances at each level to reconcile the two parts, at r times the calls. RePaint, the method that introduced it, used 250 steps, r=10, and jumps of 10 steps back and forth.
A smooth 1D Gaussian signal with a hole in the middle shows how far each method gets. For Gaussian data the exact conditional distribution of the hole is known, so we can score each method against the right answer.
Python · cell 9
Python · cell 10
Replacement alone misses the right answer by 0.61 on average. It also leaves the hole far too variable, with standard deviation 0.74 where the truth is 0.26, the signature of a fill that ignores its surroundings. Four passes per level cut the error to 0.38, and ten passes cut it to 0.29, at 4 and 10 times the calls. A model that sees the mask and the known region as inputs learns the conditional directly. With an exact model its error is essentially zero, at one times the calls.
That is why production systems train dedicated inpainting models. Stable Diffusion's inpainting model reads 9 input channels. Four hold the noisy latent, four the encoded masked image, and one the mask, shrunk to the latent grid. The weights of the 5 new channels were initialized to zero on top of the ordinary checkpoint. It was trained for 440,000 steps on synthetic masks, a quarter of which masked everything, so it remains a text-to-image model.
Interview
Inpainting by replacement, with resampling
Implement inpaint_replace(x_known, mask, denoise, S, rng, resample=0, trace=None) for rows of 1D signals. Start from rng.standard_normal(x_known.shape) at t=1 and walk np.linspace(1, 0, S + 1). For each step from a to b, make resample + 1 passes, so resample is r−1 in the definition's terms. Each pass does four things.
Compute x^0 = denoise(z, a).
Take the DDIM step zgen=(1−b)x^0+bϵ^, with ϵ^=(z−(1−a)x^0)/a.
Noise the known signal to level b and combine it with zgen through the mask.
Except after the last pass, re-noise back up to level a.
If trace is a list, append a copy of the state at the end of each step. Draw every random number from rng.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Two practical details decide how an inpainting result looks. First, masks are usually drawn by a segmentation model, today a promptable one such as SAM (§27.6). They are shrunk by the autoencoder's factor f=8 to the latent grid, so the latent mask is blocky and cannot follow the drawn outline exactly. Second, the decoder re-renders the whole image, so even the known region changes slightly after the round trip through the latent space. That is why tools commonly paste the original pixels back outside the mask and blend across the boundary. Outpainting, extending an image beyond its border, is the same problem with the mask on the outside.
Measuring an edit
An edit has two jobs. It must do what was asked, and it must leave everything else alone. Every metric measures one of the two, and the tools of §34.6 split along that line. Two embedding models appear throughout. CLIP maps images and captions into one space where matching pairs point the same way (§30.2). DINO is an image encoder trained without labels (§28.4).
Personalization. DreamBooth measured subject fidelity as the average cosine similarity between embeddings of generated and real images of the subject, with DINO and CLIP image embeddings. It measured prompt fidelity with the CLIP score between each generated image and its prompt. The authors argued for DINO because, trained without labels, it keeps the differences between two subjects of the same class, which CLIP's embedding tends to merge.
Instruction editing. InstructPix2Pix plotted consistency with the input image (CLIP image similarity) against agreement with the instruction. The second is CLIP directional similarity, the cosine between the change in image embeddings and the change in caption embeddings, cos(EI(xedit)−EI(x),ET(Pout)−ET(Pin)), where EI and ET are CLIP's image and text encoders. Sweeping sI traces a curve, and a method is better only if it is better at matched consistency.
Preservation. For a local edit, compare the unedited region of input and output directly. PSNR measures the pixel error on a log scale, SSIM compares local structure, and LPIPS is a distance between deep features, all computed outside the mask. For faces, add the similarity of an identity embedding. FLUX.1 Kontext added multi-turn consistency, whether a character survives several edits in a row without drifting.
Judgment. As for generation, the final word is human preference on a fixed benchmark, with the statistical care of §34.6. KontextBench is one, with 1,026 image-prompt pairs over local edits, global edits, character reference, style reference, and text editing.
Provenance, watermarks, and their limits
Everything above makes it easier to produce a convincing image of something that never happened. Two questions follow. Was this image generated, and has it been altered?
The two directions are not symmetric. If a provider marks everything its model makes, the provider can later show, with a secret key, that an image came from its model. No one can show from the pixels that an image is real, because a missing mark proves nothing. The image may come from another model, from an open model that marks nothing, or have had its mark removed. Proving where something came from takes signed records attached to it, which is what content credentials do.
Watermarks inside the generator
A watermark is a signal hidden in an image that a detector with the right key can read and a viewer cannot see. The first design builds it into the model. Stable Signature (Fernandez et al. 2023) works in two stages. First it trains a watermark extractor network to read a k-bit message from images, robustly to crops and compression. Then it fine-tunes the latent decoder with two losses, a perceptual loss that keeps images unchanged and a message loss through the frozen extractor. Afterwards every image the decoder produces carries one fixed signature.
Fine-tuning took about a minute on one GPU and saw under 500 images. The mark lives in the decoder's weights, so every image a released model makes carries it with no extra step. Removing it takes replacing or retraining the decoder. Detection is a hypothesis test, and the threshold is a calculation you can do.
Python · cell 11
The thresholds, 41 matching bits for one signature and 44 for a thousand, are the ones the paper reports. At that false positive rate it detected about 90 percent of generated images even after they were cropped to 10 percent of their area.
The second design needs no training. Tree-Ring (Wen et al. 2023) writes a key into the Fourier transform of the initial noise. The key is constant on rings around the zero frequency, so the pattern survives rotations. The unmodified sampler then runs as usual.
To detect the key, the provider inverts a suspect image to its noise by DDIM inversion and looks for the rings there. It inverts with the empty prompt, since the original prompt is unknown. So detection inherits every inversion error this section measured. Its statistics must be calibrated on the noise that inversion actually returns. The authors note that the inverted noise of natural images is close to Gaussian and derive a p-value from that. They used 50 steps, a ring radius of 10, and guidance 7.5. The ring version then reached an AUC of 1.000 on clean Stable Diffusion images and 0.975 averaged over a set of attacks. The AUC is the area under the ROC curve, 1 for a perfect detector and 0.5 for a coin flip (§0.11).
Research
Detecting a Tree-Ring key
Build the detector side of a Tree-Ring watermark. Work on a 32×32 noise array through its centered spectrum, F = np.fft.fftshift(np.fft.fft2(x, norm="ortho")), in which white noise has complex coefficients of unit average power. The setup gives ring_mask(), the frequencies at radius 1 to 8 from the center, and ring_key(), a real key that is constant on each ring. Implement three functions.
embed_ring(noise, key, mask) overwrites the masked coefficients with the key and returns the real array.
ring_score(noise_hat, key, mask) returns the matched-filter statistic ζ below, which is standard normal for clean noise.
detect(noise_hat, key, mask, alpha) is a one-sided test at false positive rate α.
ζ=∑i∈M∣ki∣2∑i∈MRe(kˉiyi),y=the centered spectrum of the recovered noise.
Here M is the set of masked frequencies and ki the key's coefficient at frequency i. The tests feed you noise recovered through a lossy inversion that keeps only 30 percent of the signal.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.
Watermarks applied afterwards, and what they cannot do
SynthID-Image (Gowal et al. 2025) takes the other route. It is a post-hoc watermark, applied to an image after generation and independent of the model. The paper documents what deploying one at internet scale requires. Quality is judged largely by human studies. The mark must survive everyday transformations and resist a range of attackers. And quality, robustness, and the number of hidden bits trade off against each other. SynthID has marked over ten billion images and video frames. Google states that all media generated by its tools, including Nano Banana Pro, carry it, and it offers a verification tool in the Gemini app.
Removal by regeneration is SDEdit at a small strength. So the noising argument of this section is also the main attack on invisible watermarks. The practical consequence is defense in depth (§19.7), several imperfect layers instead of one. Watermarks raise the cost of passing off a provider's images as real. Signed provenance records the history of content that carries it. Neither replaces judgment about content that carries neither.
Signed provenance with C2PA
C2PA, the Coalition for Content Provenance and Authenticity, publishes the standard for content credentials. Its central object is the manifest.
In plain words, a manifest is a signed receipt that travels with the file. It proves who signed the stated history and that the file has not changed since. It says nothing about a file that does not carry one. Stripping the metadata removes it. That is why the specification allows manifests to be stored externally and recovered through a soft binding, and why watermarks and credentials complement each other. A manifest also moves trust to the signer. A valid signature from a camera maker or a model provider is only as meaningful as the signer's honesty and key security.
Erasing a concept from the weights
Watermarks label outputs. A provider may also want a model that cannot make certain things at all. Filtering the training data is the upstream fix, but it is expensive and imperfect. Stable Diffusion 2.0 was trained on data filtered to remove explicit images. Yet it still produced 417 images that a nudity detector flagged, on the 4,703 prompts of the Inappropriate Image Prompts benchmark. SD 1.4 produced 796, as measured by Gandikota et al. 2023.
Their method, Erased Stable Diffusion (ESD), fine-tunes the model against its own guidance. For a concept prompt c, it trains ϵθ(zt,c,t) toward
ϵθ∗(zt,t)−η[ϵθ∗(zt,c,t)−ϵθ∗(zt,t)],
where θ∗ is the frozen original and η>0 sets the strength. That target is classifier-free guidance with scale γ=−η, pointing away from the concept, distilled into the weights. So prompting for c now steers away from it. Fine-tuning only the cross-attention weights (ESD-x) erases what a specific prompt calls up, such as an artist's style. Fine-tuning the other weights (ESD-u) erases a concept however it is worded, such as nudity. On the same benchmark, ESD cut the 796 flagged images of SD 1.4 to 134.
Like every safety fine-tune, erasure is measured, not guaranteed. Against reproducing training images, the memorization measurements of §34.6 and deduplication of the training data remain the first line of defense.
Choosing a method
Goal
First choice
What it costs
Watch for
Follow a sketch, pose, or depth map
ControlNet
42 percent more weights, every step
text and control disagreeing at high conditioning scale
Same, cheaply, or several conditions at once
T2I-Adapter
77M weights, run once per image
weaker control, since it ignores the noise level
Use an image as a prompt (style, object)
IP-Adapter
22M weights
identity details lost in one embedding
Put a specific subject in new scenes
DreamBooth with LoRA, prior preservation
minutes per subject, small file
drift and lost variety without the prior term, stacking interference
Keep the layout of a painting, regenerate the rest
SDEdit
nothing, fewer steps than sampling
the realism and faithfulness trade-off set by t0
Change one thing in a real photo, open model
null-text inversion and Prompt-to-Prompt
about a minute per image
prompts that do not share structure
Change one thing by instruction
an instruction or in-context editing model
one generation, longer sequence
drift over many turns
Replace a region
a mask-conditioned inpainting model
one generation
seams and decoder changes outside the mask
Show that images came from your model
watermark at generation, plus C2PA credentials
small quality cost, key management
removal by regeneration, spoofing, stripped metadata
In PyTorch
The block checks the two structural facts of this section with autograd. The first is ControlNet's zero start around a small convolutional block. The second is that IP-Adapter recovers the base layer exactly at λip=0, while concatenation changes the text path.
🔥 In PyTorchfor a machine with PyTorch
With the diffusers library, the production versions of these pieces take a few lines. The block is shown, not run, since it downloads models.
🎬 ControlNet with Diffusion Models | Explanation and PyTorch Implementation · ExplainingAIBuilds ControlNet in PyTorch, with the trainable copy, the zero convolutions, and where the outputs join the U-Net's skip connections.🎬 NULL-text Inversion for Editing Real Images using Guided Diffusion Models · ron mokadyThe first author explains why guidance breaks DDIM inversion, the pivotal inversion at guidance 1, and per-step null-embedding optimization used with Prompt-to-Prompt.🎬 Deterministic Image Editing with DDPM Inversion, DDIM Inversion, Null Inversion and Prompt-to-Prompt · Gabriel MongarasWorks through DDIM inversion, null-text inversion, and Prompt-to-Prompt attention injection step by step, a companion to the inversion and attention-editing parts of the section.🎬 Tree-Ring Watermarks: Fingerprints for Diffusion Images that are Invisible and Robust (Explained) · Yannic KilcherA careful paper walkthrough of writing a key into the Fourier spectrum of the initial noise and detecting it by DDIM inversion, with the robustness results and their limits.