Part IV ยท Ch. 7 โ€” LoRAs and Adapters

Part IV ยท Chapter 7 of 8

LoRAs and Adapters

Teaching a frozen model one new thing with a very small file


The apple the prompt cannot name

Chapter 6 ended on a hard edge: the model will follow any prompt it already understands, and not one it has never seen. Prompts steer the model within what it knows. Teaching it something new is a different job โ€” and that is this chapter.

Here is the situation, in apples. There is a particular apple you care about: an heirloom variety with a russet shoulder and a squat, lopsided profile. You type it into the prompt. You get an apple โ€” a good one, a plausible one, and not that one. No arrangement of words reaches it, because the words the noise predictor was trained against never pointed at your apple in the first place.

The obvious fix is to keep training. Take the finished model, feed it thirty photographs of your apple, and let Chapter 3's gradient steps keep rolling. That is full fine-tuning, and it works. It also hands you a bill. A model of this kind typically holds billions of learned numbers โ€” the parameters $\theta$ from Chapter 3, whose hundreds-of-millions U-Net is counted here together with the other trained pieces packed into the same checkpoint โ€” and full fine-tuning nudges every one of them, each needing its own gradient worked out and its own bookkeeping kept while training runs. What you get back at the end is an entire second copy of the model: gigabytes, for one apple.

There is a much smaller way to write down a change, and it is the subject of the rest of this chapter: a LoRA, short for Low-Rank Adaptation. It leaves the model's own billions of numbers frozen, in exactly Chapter 5's sense of the word: the training process is forbidden to change those numbers at all. They are read, they are multiplied by, and they are never touched. That one sentence is what makes everything else cheap. The same idea, with the same numbers, appears in the sister book's LoRA chapter โ€” deliberately, so the two books agree digit for digit. We will build it from scratch here anyway, and then do the part that belongs to images.

Two panels side by side. On the left, headed 'full fine-tuning', a large square grid of cells is drawn entirely in violet with every cell shaded, labeled 'every weight moves' and beneath it '1,000,000 numbers trained'. On the right, headed 'LoRA', the same size grid is drawn in muted gray with a small padlock in its corner and the label 'frozen โ€” untouched'; beside it stand two narrow mint strips, a tall thin one labeled B and a short wide one labeled A, with the caption '16,000 numbers trained'. A mint plus sign sits between the gray grid and the mint strips.
Two ways to change a model. Full fine-tuning moves every violet cell and hands you a whole new model. A LoRA leaves the grid frozen and learns the two mint strips beside it โ€” and the frozen grid is still doing nearly all of the work.

Learn the change, not the weights

Inside the noise predictor, the numbers that do the work are arranged in rectangular grids called weight matrices. Pick any one of them and call it $\mathbf{W}$ โ€” "the matrix W", a block of numbers the network multiplies things by. After a fine-tune it has become some new matrix $\mathbf{W}'$ โ€” "W-prime" โ€” and whatever fine-tuning did to it, we can always write the result as the original plus a grid of changes. Writing $\Delta\mathbf{W}$ for that grid of changes (the Greek letter delta, the traditional symbol for "a change"):

$$\mathbf{W}' = \mathbf{W} + \Delta\mathbf{W}$$

In words: the adapted matrix is the original matrix plus a matrix of changes, added cell by matching cell.

On its own that reframe saves nothing, and it would be dishonest to pretend otherwise. $\Delta\mathbf{W}$ has exactly as many cells as $\mathbf{W}$; a million-cell matrix still needs a million-cell correction. The reframe only pays if the change can be described more cheaply than the thing it changes. So: how complicated is the change a narrow fine-tune actually needs?

The LoRA bet is that it is very simple, and there is a precise way to say "simple" about a matrix. A matrix built by multiplying one tall skinny column by one flat wide row can only ever push its outputs along a single direction โ€” it has rank one, the simplest non-zero change there is. Watch it happen with the smallest example that shows anything. Take a column $\mathbf{B}$ and a row $\mathbf{A}$:

$$\mathbf{B} = \begin{bmatrix} 2 \\ 1 \end{bmatrix}, \qquad \mathbf{A} = \begin{bmatrix} 3 & 1 \end{bmatrix}, \qquad \Delta\mathbf{W} = \mathbf{B}\mathbf{A}$$

In words: the whole grid of changes is one column of numbers standing up, multiplied by one row of numbers lying flat.

Worked example โ€” every entry of the product

Multiplying a column by a row fills a grid: the entry in row $i$, column $j$ is the $i$-th number of $\mathbf{B}$ times the $j$-th number of $\mathbf{A}$. All four, one at a time:

  • Row 1, column 1: $2 \times 3 = 6$.
  • Row 1, column 2: $2 \times 1 = 2$.
  • Row 2, column 1: $1 \times 3 = 3$.
  • Row 2, column 2: $1 \times 1 = 1$.
$$\mathbf{B}\mathbf{A} = \begin{bmatrix} 2 \\ 1 \end{bmatrix}\begin{bmatrix} 3 & 1 \end{bmatrix} = \begin{bmatrix} 6 & 2 \\ 3 & 1 \end{bmatrix}$$

In words: two numbers and two numbers โ€” four numbers stored โ€” reconstruct all four cells of the change grid.

Count that honestly: four numbers stored to rebuild four cells. On a toy this small the technique breaks exactly even, and I want you to notice that, because the saving is not magic. It arrives when the grids get big, and we will count it properly at the end of the chapter.

And now the property that makes it "low rank". Feed this change grid a few different inputs and look at where the answers land. "Feeding a grid an input" means exactly one thing, and we are about to do it in full: take each row of the grid, dot it with the input โ€” multiply matching slots, add the results โ€” and those answers, top row first, are the output list. That is all multiplying by a matrix is.

  • Input $\begin{bmatrix} 1 \\ 0 \end{bmatrix}$: top $= 6 \times 1 + 2 \times 0 = 6$, bottom $= 3 \times 1 + 1 \times 0 = 3$, so the output is $\begin{bmatrix} 6 \\ 3 \end{bmatrix}$.
  • Input $\begin{bmatrix} 0 \\ 1 \end{bmatrix}$: top $= 6 \times 0 + 2 \times 1 = 2$, bottom $= 3 \times 0 + 1 \times 1 = 1$, so the output is $\begin{bmatrix} 2 \\ 1 \end{bmatrix}$.
  • Input $\begin{bmatrix} 1 \\ 1 \end{bmatrix}$: top $= 6 + 2 = 8$, bottom $= 3 + 1 = 4$, so the output is $\begin{bmatrix} 8 \\ 4 \end{bmatrix}$.

Three different inputs; every answer is some number of copies of $[2, 1]$ โ€” three copies, one copy, four copies. That is true for every possible input, and it is not a coincidence: the row $\mathbf{A}$ crushes whatever comes in down to a single number, and then the column $\mathbf{B}$ stretches that one number back out along one fixed direction. Everything has to squeeze through a one-number bottleneck.

That bottleneck width is the knob. Let $r$ numbers through instead of one โ€” "r, the rank" โ€” and $\mathbf{B}$ becomes a matrix with $r$ columns, $\mathbf{A}$ a matrix with $r$ rows, and the change can span up to $r$ independent directions. In practice $r$ is small: 4, 8, 16, 32, against matrices a thousand numbers wide. Small works because the change a narrow fine-tune needs really is simple. You are not teaching the model to see. You are teaching it one apple. Everything else about training is unchanged โ€” the loss is Chapter 3's, in the latent, conditioned form Chapters 4 and 6 left it in, $L(\theta) = \|\boldsymbol{\epsilon} - \boldsymbol{\epsilon}_\theta(\mathbf{z}_t, t, \mathbf{c})\|^2$, with the gradient allowed to move only $\mathbf{B}$ and $\mathbf{A}$.

Left to right. A mint column of two boxed numbers, 2 above 1, labeled B, the column. A multiplication sign. A mint row of two boxed numbers, 3 and 1, labeled A, the row. An equals sign. A two-by-two grid of violet-outlined boxes holding 6 and 2 on the top row and 3 and 1 on the bottom, labeled BA, the change grid. To the right, a panel lists all four entries computed one at a time: row 1 column 1, 2 times 3 equals 6; row 1 column 2, 2 times 1 equals 2; row 2 column 1, 1 times 3 equals 3; row 2 column 2, 1 times 1 equals 1. A mint line across the bottom reads: 4 numbers stored โ€” 4 cells reconstructed โ€” every output on one line: rank 1.
A column times a row. Each cell of the change grid is one number from $\mathbf{B}$ times one number from $\mathbf{A}$ โ€” no cell is free to be anything else, which is exactly what "low rank" costs you and exactly what makes the file small.

The strength dial, as arithmetic

A LoRA ships with one more number attached: a scale that says how loudly the learned change should speak. Written into the update, it is the fraction $\frac{\alpha}{r}$:

$$\mathbf{W}' = \mathbf{W} + \frac{\alpha}{r}\,\mathbf{B}\mathbf{A}$$

In words: the working matrix is the frozen matrix plus alpha-over-r times the learned low-rank change.

Two symbols, both re-introduced in words. $r$ is the rank โ€” the bottleneck width we just chose. And $\alpha$ is "alpha, the LoRA scale", a volume knob. Dividing by $r$ is a courtesy to your future self: double the rank and the product $\mathbf{B}\mathbf{A}$ picks up roughly twice as many contributions, so dividing by $r$ keeps the overall loudness comparable when you change rank.

Worked example โ€” the frozen matrix, adapted

Let the frozen matrix be a plain doubling machine, $\mathbf{W} = \begin{bmatrix} 2 & 0 \\ 0 & 2 \end{bmatrix}$ โ€” chosen because you can see at a glance what the base model is contributing, so anything else in the answer must have come from the adapter. Keep $\mathbf{B}\mathbf{A} = \begin{bmatrix} 6 & 2 \\ 3 & 1 \end{bmatrix}$ from the last section, and set $\alpha = 2$ with $r = 1$, so $\frac{\alpha}{r} = \frac{2}{1} = 2$. First scale the change, cell by cell: $2 \times 6 = 12$, $2 \times 2 = 4$, $2 \times 3 = 6$, $2 \times 1 = 2$. Then add it on, cell by matching cell: $2 + 12 = 14$, $0 + 4 = 4$, $0 + 6 = 6$, $2 + 2 = 4$.

$$\mathbf{W}' = \begin{bmatrix} 2 & 0 \\ 0 & 2 \end{bmatrix} + 2\begin{bmatrix} 6 & 2 \\ 3 & 1 \end{bmatrix} = \begin{bmatrix} 14 & 4 \\ 6 & 4 \end{bmatrix}$$

In words: the adapted matrix is the frozen doubler with the scaled change folded into it, one cell at a time.

Now push the input $\begin{bmatrix} 1 \\ 1 \end{bmatrix}$ through both. The frozen matrix gives top $= 2 \times 1 + 0 \times 1 = 2$ and bottom $= 0 \times 1 + 2 \times 1 = 2$. The adapted matrix gives top $= 14 \times 1 + 4 \times 1 = 18$ and bottom $= 6 \times 1 + 4 \times 1 = 10$.

$$\mathbf{W}\begin{bmatrix} 1 \\ 1 \end{bmatrix} = \begin{bmatrix} 2 \\ 2 \end{bmatrix}, \qquad \mathbf{W}'\begin{bmatrix} 1 \\ 1 \end{bmatrix} = \begin{bmatrix} 18 \\ 10 \end{bmatrix}$$

In words: the same input that the base model turned into $[2, 2]$ now comes out as $[18, 10]$ โ€” the adapter has bent this one matrix's behavior hard.

Our toy shouts on purpose, so that every digit stays visible; real adapters nudge. And notice the folding: once you have computed $\mathbf{W}'$ you can throw the two strips away and run the adapted matrix directly. That is why a merged LoRA costs nothing extra when you generate.

Now the number the user actually turns. In a workflow it is labeled LoRA strength, and mathematically it is nothing more exotic than the multiplier in front of $\mathbf{B}\mathbf{A}$. Sweep it across the same input $[1, 1]$, remembering that $\mathbf{B}\mathbf{A}\begin{bmatrix} 1 \\ 1 \end{bmatrix} = \begin{bmatrix} 8 \\ 4 \end{bmatrix}$ from the worked example above, and the base path contributes $[2, 2]$ every time:

Worked example โ€” the slider is pure arithmetic

$\frac{\alpha}{r}$change contributedoutputwhat the picture does
0$0 \times [8, 4] = [0, 0]$$[2 + 0,\; 2 + 0] = [2, 2]$the concept is absent โ€” this is the base model
0.5$0.5 \times [8, 4] = [4, 2]$$[2 + 4,\; 2 + 2] = [6, 4]$the concept shows up, faintly
1.0$1.0 \times [8, 4] = [8, 4]$$[2 + 8,\; 2 + 4] = [10, 6]$the concept is clearly present
1.5$1.5 \times [8, 4] = [12, 6]$$[2 + 12,\; 2 + 6] = [14, 8]$the change swamps the base model's contribution

Read the middle column against the constant $[2, 2]$ the base path supplies. At 0 the adapter is silent. At 1.0 it already contributes four times what the base matrix does on this input, and by 1.5 the base model's opinion is a rounding error โ€” which is what an over-strength LoRA looks like on screen: the concept everywhere, at the cost of composition, background and lighting.

A horizontal strip of four square panels sharing one prompt and one seed, labeled underneath strength 0, strength 0.5, strength 1.0 and strength 1.5, and beneath those the words absent, subtle, correct and scorched. The first panel holds a dim, faded amber apple; the second the same apple a little brighter with one russet speckle; the third brighter still with its russet speckles fully drawn; the fourth at full strength and enlarged until it crowds the panel edges. A mint bracket under the middle two panels reads 'the useful band', and a line above the strip reads 'same prompt, same seed โ€” only the multiplier in front of BA changes'. An amber-bordered banner across the middle of the strip reads PLACEHOLDER - replace with four real renders.
The same prompt, the same seed, four strengths. Only the multiplier in front of $\mathbf{B}\mathbf{A}$ changes across the strip. The failure at the top end is not a quality problem โ€” it is the arithmetic above, with the adapter's contribution overwhelming everything the frozen model wanted to say. This figure is currently a hand-drawn placeholder; the real strip needs four renders from an actual model.

Where the adapter attaches

Which matrices get the treatment? Almost always the ones inside the cross-attention blocks of the U-Net โ€” the machinery from Chapter 6 where each patch of the latent asks the conditioning $\mathbf{c}$ what it should be. We are not re-deriving that here; Chapter 6 owns it. What matters is the consequence. Those are precisely the matrices that decide how strongly a word in your prompt pulls on a region of the image, so a small correction there is a large change in how the model reads language.

That is the honest explanation for trigger words. Whoever trains an adapter captions the training images with a rare token โ€” "sks apple", something the base model has no strong opinion about โ€” and the adapter learns to attach a large correction to that token's pull, precisely because there was nothing there before to interfere with. Leave the trigger word out of your prompt and such an adapter can look almost inert; put it in and the whole thing switches on. You are choosing whether the corrected path ever gets a strong query to answer.

Everything else is exactly as you left it. Re-picture Chapter 4's latent-diffusion-pipeline map: encoder, loop in latent space, decoder. A LoRA changes nothing about it โ€” not the VAE, not the sampler, no extra stage. It edits a handful of matrices inside the noise predictor, and Chapter 5's loop then runs as before, calling a very slightly different function at every sampler step.

Why varied apples matter

Back in Chapter 1 we laid out nine apple thumbnails โ€” deliberately different: red, green, golden, spotted, large, small, photographed from two angles โ€” and the caption made a promise: these training apples are all different, and that is going to matter a lot when we get to LoRAs. This is where that debt gets paid.

Training an adapter is Chapter 3's procedure on a very small dataset. Encode one of your photographs to a latent, pick a random timestep $t$, add the matching noise using Chapter 2's closed form, ask the noise predictor for that noise, compare, and push $\mathbf{B}$ and $\mathbf{A}$ downhill. Repeat over your twenty or thirty images. The learned change is whatever consistently reduces that loss across your set.

Read that last sentence again, because it contains the whole trap. The adapter learns whatever is consistent. It has no idea which parts of your photographs are the concept and which are the circumstances. Suppose all thirty photographs of your heirloom apple sit on the same wooden table, in the same warm afternoon light, shot from the same height. "Russet shoulder" is consistent across your set โ€” and so is "wooden table", and so is "warm light", and so is "shot from above". The gradient cannot tell them apart, so the adapter learns the whole bundle as one concept. Load it up, prompt for your apple in a snowfield, and the wooden table comes to the snowfield with it. That is not a bug in the method; it is the method working exactly as specified on data that specified the wrong thing.

The fix is variety, and you apply it with a camera, not a setting. Photograph the apple on a table, on grass, in a hand, against a white wall; morning light, lamp light, flat overcast; close, far, above, side. Now the only thing consistent across the set is the apple, so the only thing the adapter can learn is the apple. Variety is not "more data" โ€” it is what isolates the concept from its accidents, and twenty varied photographs beat two hundred from one sitting.

Two rows, each running training data on the left to a generated result on the right. The top row is outlined in red and labeled 'all shot on the same wooden table': five near-identical gray thumbnails, each holding the same amber apple sitting on an amber slab, captioned 'same table, same light, same angle'. A muted arrow leads to a snowfield panel in which the apple appears with the amber slab still under it, labeled in red 'the table came along'. The bottom row is outlined in mint and labeled 'varied backgrounds, light and angles': five thumbnails of five visibly different apples โ€” amber and mint-green, varying in size, shape, lean and speckling โ€” against five different background tints, captioned 'grass, wall, hand, lamplight, overhead'. Its arrow leads to a snowfield panel holding the apple alone with no slab, labeled in mint 'only the apple came along'.
The direct sequel to Chapter 1's varied apples. The adapter learns whatever is consistent across the training set. If the background is consistent, the background is part of the concept โ€” and it will be dragged into every image you generate.

Two neighboring failures deserve names. Overfitting is training for too many passes over too few images: the adapter stops learning "this apple" and starts memorising "these thirty pictures", and the giveaway is a generation that reproduces a training photo's exact composition whatever else you ask for. And data quality beats quantity outright: five sharp, well-lit, correctly captioned photographs teach a cleaner concept than fifty blurry ones, because every blurry image quietly teaches "blurry" as part of the target.

Counting the savings

Now the arithmetic that made the technique famous, on one honest matrix. Take a weight matrix that is $1{,}000$ numbers wide and $1{,}000$ numbers tall โ€” a typical size for a matrix inside a model like this. A full change $\Delta\mathbf{W}$ to it has one entry per cell:

$$1{,}000 \times 1{,}000 = 1{,}000{,}000$$

In words: changing that matrix the ordinary way means writing down and training one million numbers.

Now the low-rank version at a typical rank of $r = 8$. The column block $\mathbf{B}$ is $1{,}000$ tall and $8$ wide; the row block $\mathbf{A}$ is $8$ tall and $1{,}000$ wide:

$$\underbrace{(1{,}000 \times 8)}_{\mathbf{B}} + \underbrace{(8 \times 1{,}000)}_{\mathbf{A}} = 8{,}000 + 8{,}000 = 16{,}000$$

In words: sixteen thousand trained numbers stand in for a million.

The ratio, worked: $16{,}000 \div 1{,}000{,}000 = 0.016$, which is $1.6\%$. Multiply that across the handful of cross-attention matrices a LoRA touches and the file lands in the low tens of megabytes, sitting beside a base model of several gigabytes โ€” figures typical of models of this kind, not laws. That is the whole reason a shelf of adapters is a practical thing to own: one frozen base model, and a stack of small files, each teaching it one thing.

Because the corrections are additions, they compose. Load two adapters and the corrections add up on each shared matrix, each with its own strength:

$$\mathbf{W}' = \mathbf{W} + s_1\mathbf{B}_1\mathbf{A}_1 + s_2\mathbf{B}_2\mathbf{A}_2$$

In words: the adapted matrix is the frozen matrix plus the first adapter's change at its own strength, plus the second adapter's change at its own strength. (These strengths $s_1$ and $s_2$ are LoRA strengths โ€” they are not Chapter 6's guidance scale $s$, which lives in an entirely different equation.)

Composition works right up until it does not. Each adapter was trained separately, assuming the frozen matrix underneath it was untouched and knowing nothing of the other. If they push the same matrices in conflicting directions โ€” a style adapter that flattens everything against a subject adapter that insists on photographic texture โ€” the sum is not a compromise but a fight, and the usual repair is turning both strengths down.

See it move

Watch (19:33): what to notice โ€” the moment the million-cell violet grid freezes and the two mint strips appear beside it, and then the strength dial turning the same apple from absent, to correct, to scorched, with nothing else in the pipeline moving.

Where you'll meet this

In a workflow graph, this chapter is one node โ€” usually called something like Load LoRA, sitting after the checkpoint loader, taking the base model in on one wire and handing a modified model out on another. That wiring is the picture of the whole chapter: the adapter never stands alone in the graph, because there is nothing for it to do without a model flowing through it.

The number in that node's box is $\frac{\alpha}{r}$ from the sweep table โ€” the same multiplier, with the same arithmetic behind it. Some interfaces give you two of them, one for the correction applied inside the U-Net and one for the correction applied to the text encoder, because a LoRA can carry strips for both. And when a model page tells you to include a strange token like "sks apple" in your prompt, you now know exactly what you are doing: handing the corrected cross-attention path the one query it was trained to answer loudly.

Three habits fall straight out of the math. If a generation looks untouched, check the trigger word first. If it looks scorched, drop the strength before you rewrite the prompt โ€” you are turning down the multiplier in front of $\mathbf{B}\mathbf{A}$, nothing more. And if a LoRA does nothing at all, check which base model it was trained against. (LoRA is the famous member of a larger family of adaptation methods, and the others are a topic for another day.)

What you now know

  • A fine-tune never has to rewrite a weight matrix โ€” it can be written as the original plus a change, $\mathbf{W}' = \mathbf{W} + \Delta\mathbf{W}$, and the whole game is describing that change cheaply.
  • A LoRA describes the change as a column times a row: $\mathbf{B} = [2, 1]$ times $\mathbf{A} = [3, 1]$ fills the grid $\begin{bmatrix} 6 & 2 \\ 3 & 1 \end{bmatrix}$, and every output it can produce lies along the single direction $[2, 1]$ โ€” rank 1.
  • The strength you turn in a workflow is the multiplier $\frac{\alpha}{r}$ in $\mathbf{W}' = \mathbf{W} + \frac{\alpha}{r}\mathbf{B}\mathbf{A}$, and it is pure arithmetic: on our toy it moves the output from $[2, 2]$ at strength 0 to $[18, 10]$ at $\frac{\alpha}{r} = 2$. This $\alpha$ is not the noise schedule's $\alpha_t$.
  • Adapters usually attach to the cross-attention matrices from Chapter 6, which is why trigger words switch them on so sharply, and a rank-8 adapter on a $1{,}000 \times 1{,}000$ matrix trains $16{,}000$ numbers instead of $1{,}000{,}000$ โ€” $1.6\%$, megabytes riding on gigabytes.
  • The adapter learns whatever is consistent across your training images, so varied backgrounds, light and angles are what separate your apple from the wooden table it sat on โ€” the promise Chapter 1's varied apples made.
  • A LoRA is not a model: it holds a correction to specific matrices, it is inert on its own, and it runs only on top of the base model it was trained against.

Where we're headed. You can now teach a frozen model one new thing with a file small enough to email, and read every number on the node that loads it. What you cannot do is make it last longer than an instant: the adapter bends the noise predictor, the sampler walks its loop, the decoder hands back a picture, and that is the end of it. Chapter 8 adds the axis we have been missing, treating a whole sequence of frames as one object and using temporal attention to keep the apple the same apple from frame to frame โ€” because everything so far has produced one still image, frozen in a single instant.