47.0 What this chapter gives you#
- You will be able to say exactly what one parameter is: a single number in a
single position of a single table inside the model.
- You will be able to take an architecture description and compute the
parameter count by hand, and say which parts of the model hold the billions.
- You will be able to tell parameters apart from hyperparameters, activations,
context and tokens, and use all five words correctly in one sentence.
- You will be able to turn a parameter count into a memory figure, for
inference and for training, and explain the gap between them.
- You will be able to explain quantization, name the common formats, and work
out whether a given model fits on a given machine.
- You will be able to say honestly why parameter count is a weak predictor of
how useful a model is, and cite the evidence for that.
- You will be able to explain byte-pair encoding by working it through by hand
on a tiny corpus, and name the three other tokenizer families.
- You will be able to predict roughly how many tokens a piece of text costs,
and explain why Hindi costs several times more than English for one meaning.
- You will be able to explain why a model cannot count the letters in a word,
and why it fails at arithmetic on long numbers, from tokenization alone.
- You will be able to explain what an embedding is, what similarity between
embeddings means, and what a context window really limits.
47.1 What a parameter actually IS in a language model#
PLAIN47.1.1 in simple words#
- A language model is, on the inside, a very large pile of numbers.
- Nothing else. No sentences stored. No facts written down as text. Numbers.
- One of those numbers is called a parameter.
- Chapter 46 fitted a straight line to five points. That line had exactly two
parameters: a slope and an intercept, both found by gradient descent, which
is the process of nudging numbers downhill until the error stops falling.
- A language model is the same idea with the count raised enormously. Two
parameters becomes eight billion, and the shape of the thing being fitted
becomes a network of layers instead of a line.
- Everything else in this section is that one sentence unpacked.
- So a parameter is not a setting you choose. It is not an option in a menu.
- It is one plain number, like -0.0113 or 0.0392, sitting in one fixed slot.
- The slots are arranged in rectangles, called matrices. Rows and columns.
- A parameter has an address: this matrix, this row, this column.
- When the model was trained, every one of those numbers was slowly adjusted
until the model got good at predicting the next piece of text.
- When training finished, the numbers were frozen and written to a file.
- Every time you use the model, it reads the same frozen numbers.
- Your question does not change them. Your conversation does not change them.
- When somebody says a model has 7 billion parameters, they mean the file
contains 7 billion such numbers.
- That is the whole idea. A parameter is one learned number in one slot.
PLAIN47.1.2 a picture in your head#
- Picture an enormous sound mixing desk, the kind in a recording studio.
- A mixing desk has knobs. Each knob controls how loudly one thing is heard.
- Turn one knob and the guitar gets louder. Turn another and the drums fade.
- Now imagine a desk with seven billion knobs instead of forty.
- Each knob is set to some exact position, like 0.0392 out of a range.
- Training was a very long process of nudging every knob a tiny amount, over
and over, until the whole desk produced a good sound.
- Shipping the model means writing down every knob position in a file.
- Loading the model means setting all seven billion knobs back to those exact
positions.
- Running the model means pushing sound through the desk without touching a
single knob.
Where this comparison breaks: on a real mixing desk, a human knows what each
knob does, and there is a label under it. In a model, almost no single
parameter has a meaning you can name. Its effect only exists in combination
with millions of others. Also, a mixing desk has knobs a person turns during
a performance. Model parameters are never turned during use. They are turned
only during training, and then never again unless the model is retrained.
PLAIN47.1.3 a worked example#
- Here is a genuine kind of object from inside a model: a weight matrix.
- This is a four-by-four corner of one, with values rounded for printing.
A 4 x 4 corner of one weight matrix, values rounded
col 0 col 1 col 2 col 3
row 0 -0.0113 0.0271 -0.0004 0.0158
row 1 0.0392 -0.0087 0.0221 -0.0310
row 2 -0.0056 0.0143 0.0079 0.0002
row 3 0.0018 -0.0225 -0.0166 0.0407
- Look at row 1, column 2. The value there is 0.0221.
- That is a parameter. That one number. Not the row. Not the matrix. That.
- Its full address inside a real model would be something like: layer 7, the
query projection matrix, row 1, column 2.
- There are 16 parameters in this little corner, because it is 4 rows times
4 columns.
- The real matrix this corner came from would be 4096 by 4096.
- That is 4096 x 4096 = 16,777,216 parameters in that one matrix alone.
- A model of this size has hundreds of matrices like it.
- Every one of those numbers started as a random value before training, and
ended up at its final value because training pushed it there.
- Nobody chose 0.0221. Nobody can explain 0.0221 on its own.
PLAIN47.1.4 what is really happening inside#
- The model’s job at each step is to turn a list of numbers into another list
of numbers.
- The main operation it uses is multiply-and-add, done many times at once.
- Take an incoming list of 4096 numbers. Call it the input vector.
- Take one column of the weight matrix, also 4096 numbers.
- Multiply them pairwise and add the results up. That gives one output number.
- Repeat for every column. That gives the whole output vector.
- So each parameter is a multiplier. It says how much one incoming number
contributes to one outgoing number.
- The parameter at row 1, column 2 says: take input number 1, multiply it by
0.0221, and add that into output number 2.
- That is literally all a weight does. It is a multiplier in one cell of a
giant multiply-and-add.
- There is a second, smaller kind of parameter called a bias.
- A bias is added rather than multiplied. It shifts an output up or down by a
fixed amount regardless of the input.
- Biases are also learned during training, and are also parameters.
- Some model families use biases everywhere, some use almost none.
- When the model runs, it walks through layer after layer doing this. The
parameters never move. Only the numbers flowing through them change.
TECHNICAL47.1.5 the engineer’s version#
- A parameter is one scalar element of a learnable tensor, updated by the
optimizer during training and fixed at inference time.
- In PyTorch terms, a parameter is one element of a tensor wrapped in
torch.nn.Parameter, which is a tensor with requires_grad=True that is
registered in the module’s parameter list.
- The words weight and parameter are used interchangeably in practice,
and in almost every sentence you will read they mean the same thing.
- The precise distinction is that a weight is a multiplicative parameter
in a linear map, while a bias is an additive parameter. Both are
parameters. So all weights are parameters, but not all parameters are
weights.
- Normalization scale and shift terms, often called gamma and beta, are also
parameters. So are learned position embeddings where a model uses them.
- In a decoder-only transformer of the Llama family, the linear layers carry
no bias terms at all, and RMSNorm carries a scale but no shift. In GPT-2 the
linear layers do carry biases and LayerNorm carries both scale and shift.
- That difference is tiny in count. In GPT-2 small, all biases and all
normalization parameters together are 121,344 out of 124,439,808 parameters,
which is 0.098 per cent of the model.
- Parameters are stored in the model’s state dictionary under dotted names.
Real names from a Llama-family checkpoint look like this:
model.embed_tokens.weight [128256, 4096]
model.layers.0.self_attn.q_proj.weight [4096, 4096]
model.layers.0.self_attn.k_proj.weight [1024, 4096]
model.layers.0.mlp.gate_proj.weight [14336, 4096]
model.layers.0.input_layernorm.weight [4096]
lm_head.weight [128256, 4096]
- The bracketed pair is the tensor shape. The number of parameters in a tensor
is the product of its dimensions.
- Counting them in PyTorch is one line:
sum(p.numel() for p in model.parameters()). Counting trainable ones only
adds if p.requires_grad.
- For a file on disk,
safetensors stores a JSON header listing every tensor
name, dtype and shape, so the count can be read without loading the model.
For GGUF files, gguf-dump prints the same information.
- Terminology note, and it is a standard one: in statistics a “parameter” is a
quantity of a distribution, and in software a “parameter” is a function
argument. Neither meaning applies here. The machine learning meaning is the
one in this chapter.
| Kind of parameter |
Operation |
Typical share |
| Linear layer weight |
Multiply |
Over 99 per cent |
| Bias |
Add |
Under 0.1 per cent |
| Norm scale (gamma) |
Multiply |
Under 0.01 per cent |
| Embedding row |
Lookup |
0.5 to 25 per cent |
WORDS47.1.6 remember these#
- Parameter — one learned number in the model — one scalar element of a
learnable tensor, updated by the optimizer, frozen at inference.
- Weight — a number that multiplies something — a multiplicative parameter in a
linear map, stored as an element of a weight matrix.
- Bias — a number that is added on — an additive parameter applied after a
linear map, one per output dimension.
- Matrix — a rectangle of numbers — a rank-2 tensor with shape
[out_features, in_features] for a linear layer.
- Tensor — a box of numbers with a shape — an n-dimensional array with a dtype
and a shape tuple.
- State dictionary — the list of all the model’s numbers with names — an
ordered mapping from parameter name to tensor, saved to disk.
- Checkpoint — the saved file of a model — a serialized state dictionary, in
formats such as safetensors, PyTorch .pt or GGUF.
- Frozen — not changing any more — parameters with
requires_grad=False, or
any parameter during inference.
47.2 What 7B, 70B and 405B actually count#
PLAIN47.2.1 in simple words#
- The B in 7B means billion. It is a count of parameters.
- A 7B model contains about seven thousand million individual numbers.
- A 405B model contains about four hundred and five thousand million of them.
- Nothing else is being counted. Not files. Not facts. Not words known.
- To get the total you add up the size of every matrix in the model.
- Every matrix size is just rows times columns.
- So the whole count is a long sum of simple multiplications.
- The numbers are not round because the shapes are not round.
- A model called “8B” often turns out to hold 8,030,261,248 parameters exactly.
- The advertised name is rounded. The real count is exact and computable.
- Once you know four or five shape numbers about a model, you can compute the
total yourself in a few minutes.
- That is what this section does, all the way through, for a real model.
PLAIN47.2.2 a picture in your head#
- Imagine a large office block with 126 identical floors.
- Every floor has exactly the same furniture plan: seven big filing cabinets.
- Some cabinets are wide, some are narrow, but every floor gets the same set.
- To count the drawers in the whole building you do not walk every floor.
- You count the drawers on one floor, then multiply by 126.
- Then you add the reception desk at the bottom and the roof office at the top,
because those exist only once.
- That is exactly how a transformer parameter count works.
- The floors are the layers. The identical furniture is the same set of
matrices repeated in every layer.
- The reception desk is the embedding table. The roof office is the output
layer. Both exist once, not once per floor.
Where this comparison breaks: real buildings sometimes have odd floors. In most
transformer models every layer genuinely is the same shape, which is why the
multiply-by-number-of-layers trick works exactly. But some newer designs break
this on purpose: mixture-of-experts models have layers with many parallel
expert blocks, and only some are used per token, so the total count and the
count actually used per token stop being the same number.
PLAIN47.2.3 a worked example#
- Let us count a real model completely: Llama 3.1 8B, released by Meta on
23 July 2024.
- Five shape numbers describe it, and they are published:
| Setting |
Value |
| Layers |
32 |
| Hidden size (d) |
4096 |
| Feed-forward size |
14336 |
| Attention heads |
32 |
| Key-value heads |
8 |
| Vocabulary size |
128256 |
- Head size is hidden size divided by heads: 4096 / 32 = 128.
- Now count one layer. Attention first, four matrices:
q_proj 4096 x 4096 = 16,777,216
k_proj 4096 x 1024 = 4,194,304
v_proj 4096 x 1024 = 4,194,304
o_proj 4096 x 4096 = 16,777,216
attention total per layer = 41,943,040
- The k and v matrices are narrower because of grouped-query attention: only
8 key-value heads, so 8 x 128 = 1024 columns instead of 4096.
- Now the feed-forward part, three matrices:
gate_proj 4096 x 14336 = 58,720,256
up_proj 4096 x 14336 = 58,720,256
down_proj 14336 x 4096 = 58,720,256
feed-forward total per layer = 176,160,768
- Two normalization vectors of 4096 each add 8,192 more.
- One layer therefore holds 41,943,040 + 176,160,768 + 8,192 = 218,112,000.
- There are 32 layers: 32 x 218,112,000 = 6,979,584,000.
- The embedding table is vocabulary times hidden size:
128,256 x 4,096 = 525,336,576.
- The output layer is the same shape again: 525,336,576.
- The final normalization adds 4,096.
- Add them all: 6,979,584,000 + 525,336,576 + 525,336,576 + 4,096 =
8,030,261,248.
- That is 8.03 billion. The model is sold as “8B”. The arithmetic closes.
PLAIN47.2.4 what is really happening inside#
- Here is the whole 8B model drawn as a structure, with the parameter count
written next to every piece.
INPUT TOKEN IDS
|
v
+--------------------------+
| embedding table | 525,336,576
| 128,256 rows x 4,096 |
+--------------------------+
|
v
+==========================+ <-- repeated 32 times
| q_proj 16,777,216 |
| k_proj 4,194,304 |
| v_proj 4,194,304 | attention
| o_proj 16,777,216 | 41,943,040
| ---------------------- |
| gate 58,720,256 |
| up 58,720,256 | feed-forward
| down 58,720,256 | 176,160,768
| ---------------------- |
| 2 norms 8,192 |
| layer total 218,112,000 |
+==========================+
| x 32 layers = 6,979,584,000
v
+--------------------------+
| final norm 4,096 |
+--------------------------+
|
v
+--------------------------+
| output head | 525,336,576
| 128,256 rows x 4,096 |
+--------------------------+
|
v
ONE SCORE PER TOKEN TOTAL 8,030,261,248
- Look at where that total came from and a clear pattern appears.
- The feed-forward matrices are the biggest single item, by a wide margin.
- In the 8B model, feed-forward is 70 per cent of all parameters.
- Attention is about 17 per cent.
- The embedding and output tables together are about 13 per cent.
- Normalization is a rounding error: three thousandths of one per cent.
- Now scale the model up and watch the shares move.
- In the 70B model, feed-forward rises to 80 per cent and embeddings fall to
3 per cent, because the vocabulary stays the same size while the layers grow.
- In the 405B model, feed-forward is 81 per cent and embeddings are 1 per cent.
- So the honest one-line answer to “where do the billions live” is: in the
feed-forward blocks, repeated once per layer.
- That matters practically. When people compress models, they attack the
feed-forward matrices first, because that is where the mass is.
- It also explains why small models feel embedding-heavy. In GPT-2 small,
the embedding table alone is 25 per cent of the whole model.
TECHNICAL47.2.5 the engineer’s version#
- The general formula for a decoder-only transformer with grouped-query
attention, SwiGLU feed-forward and RMSNorm, with untied embeddings, is:
per_layer = 2*d*d (q_proj and o_proj)
+ 2*d*n_kv*head_dim (k_proj and v_proj)
+ 3*d*d_ff (gate, up, down)
+ 2*d (two RMSNorm scales)
total = V*d (input embedding)
+ L*per_layer (all layers)
+ d (final RMSNorm)
+ V*d (output head, if untied)
- Here d is hidden size, d_ff feed-forward size, L layers, V vocabulary size,
n_kv the number of key-value heads and head_dim the per-head width.
- Applying it to the three published Llama 3.1 configurations gives exact
totals that match the marketing names:
| Model |
Layers x d x d_ff |
Computed total |
| Llama 3.1 8B |
32 x 4096 x 14336 |
8,030,261,248 |
| Llama 3.1 70B |
80 x 8192 x 28672 |
70,553,706,496 |
| Llama 3.1 405B |
126 x 16384 x 53248 |
405,853,388,800 |
- All three use vocabulary 128,256, head dimension 128, and 8 key-value heads.
The 405B model has 128 query heads, which is why its k and v projections are
only 16384 x 1024 while q and o are 16384 x 16384.
- Where the parameters sit, as a percentage of the total:
| Block |
8B |
70B |
405B |
| Feed-forward |
70.20% |
79.90% |
81.25% |
| Attention |
16.71% |
17.12% |
17.71% |
| Input embedding |
6.54% |
1.49% |
0.52% |
| Output head |
6.54% |
1.49% |
0.52% |
- Norm parameters are 0.0033 per cent for 8B, 0.0019 per cent for 70B and
0.0010 per cent for 405B, and are omitted from the table above for width.
- Tied embeddings are an implementation choice, not a standard: the output
projection reuses the transposed input embedding matrix, saving V x d
parameters. GPT-2 ties them. Llama 3.1 8B, 70B and 405B do not. Several
small models such as Gemma tie them, which is why their embedding share is
larger than you would guess.
- Verifying GPT-2 small the same way, including biases, gives exactly
124,439,808 parameters: 38,597,376 token embeddings, 786,432 learned
position embeddings, 12 layers of 7,087,872 and a final LayerNorm of 1,536.
The commonly quoted “117M” for GPT-2 small comes from an older count that
excluded the position embeddings and some terms; 124M is the count that
matches the released checkpoint.
- For mixture-of-experts models the count splits in two. Report the total
parameter count and the active parameter count separately, because only
a few experts run per token. A model with 8 experts of which 2 run has
roughly a quarter of its feed-forward parameters active at any moment.
Quoting only the total for such a model is a marketing choice, not a
technical one.
- Tools:
sum(p.numel() for p in model.parameters()) in PyTorch,
model.num_parameters() in Hugging Face transformers, the safetensors
index JSON header, and gguf-dump --no-tensors file.gguf for GGUF metadata.
WORDS47.2.6 remember these#
- Hidden size — how wide the model is — d_model, the width of the residual
stream carried between layers, commonly 4096 or 8192.
- Feed-forward size — the width of the wide middle part — d_ff, usually
between 2.7 and 4 times d_model in modern designs.
- Layer — one repeat of the model’s standard block — one transformer block
containing attention, feed-forward and two normalizations.
- Vocabulary size — how many different tokens exist — the number of rows in
the embedding matrix, for example 128,256 in Llama 3.1.
- Grouped-query attention — sharing key and value work across heads — GQA,
where n_kv is smaller than n_heads, shrinking k and v projections and the
key-value cache.
- Tied embeddings — reusing the input table as the output table — weight tying
between
embed_tokens and lm_head, saving V x d parameters.
- Active parameters — the ones that actually run for a token — in a
mixture-of-experts model, the subset routed to per token, as against the
total stored.
47.3 Parameters versus the four things they get confused with#
PLAIN47.3.1 in simple words#
- Five words get mixed up constantly. Here they are, one line each.
- Parameters are the learned numbers inside the model, frozen after
training.
- Hyperparameters are the choices a human made before training started.
They are not learned. Nobody trained them.
- Activations are the temporary numbers created while the model runs on
one input, and thrown away as soon as the answer is produced.
- Context is the text you are giving the model right now. It is data.
- Tokens are the small pieces that text is chopped into. Also data.
- Now the sharp test. Which of these are in the model file on disk?
- Only the parameters. The other four are not in the file.
- Hyperparameters are recorded next to the file, in a small configuration
note, so the loader knows what shape to build. But they were not learned.
- Activations exist only for a moment while a request is being answered.
- Context and tokens are whatever you happen to be asking about today.
- So: parameters are what the model is. The rest is how it was built, what it
is thinking about right now, and what you asked.
PLAIN47.3.2 a picture in your head#
- Think of a bakery.
- The recipe decisions are the hyperparameters: oven temperature, tin size,
how long to bake. A person chose these before any baking started.
- The baker’s learned skill is the parameters: the exact feel for how long
to knead, built up over years and now fixed in the baker’s hands.
- The ingredients on the counter today are the context: this flour, this
water, this order for a birthday cake.
- The individual scoops and spoonfuls are the tokens: the units the
ingredients get measured into.
- The half-made dough at each stage is the activations: sticky, temporary,
real, and gone the moment the bread is out.
- Now the useful part. Changing the oven temperature is changing a
hyperparameter, and you have to re-train the baker to suit it. Giving new
ingredients is changing the context, and needs no re-training at all.
Where this comparison breaks: a baker learns a little from every loaf. A
deployed model does not. Its parameters are identical after your millionth
message and after your first. Any apparent learning inside a conversation is
the context being carried along, not the parameters changing.
PLAIN47.3.3 a worked example#
- Here is one scenario with all five words used correctly in it.
- A team trains a model. They choose 32 layers, hidden size 4096, learning
rate 0.0003 and batch size 4 million tokens. Those are hyperparameters.
- Training runs for weeks and produces 8,030,261,248 parameters, which are
saved to a file of about 16 gigabytes.
- A user types “Summarize this contract” and pastes 3,000 words of contract.
That whole thing is the context.
- The tokenizer chops that context into 4,182 tokens, each an integer.
- The model runs. As those 4,182 tokens flow through 32 layers, it creates
millions of temporary numbers at every layer. Those are activations, and
they are discarded when the answer is finished.
- Now the one-sentence version the reader asked for, all five in one line:
- “The 8B model’s frozen parameters were produced by a training run whose
hyperparameters fixed 32 layers and a learning rate of 0.0003; at inference
the 4,182 tokens of the user’s context are turned into activations that flow
through those parameters and are then thrown away.”
- Read it twice. Every one of the five words is doing a different job.
PLAIN47.3.4 what is really happening inside#
- Trace the lifetime of each of the five things, in order of when it exists.
- Hyperparameters exist first, before there is a model at all. A human writes
them into a configuration file.
- The training program reads them and builds an empty model of that shape,
filled with random numbers.
- Training begins. The parameters change, millions of times, over weeks.
- Training ends. The parameters are written to disk and never change again.
- Later, somebody loads the file. Now the parameters are in memory.
- A user sends text. That is the context. The tokenizer converts it to tokens.
- The tokens flow in. At every layer the model computes new temporary numbers.
Those are activations.
- The activations are what carries the meaning of your specific question. The
parameters are what does the transforming.
- When the reply is finished, the activations are freed. The parameters are
still sitting there, byte for byte identical.
- This is why inference needs more memory than the model file size. The
activations, and the cached keys and values for the conversation so far,
need room too.
- And it is why a longer conversation costs more memory but does not make the
model bigger. The model is exactly as big as it was.
TECHNICAL47.3.5 the engineer’s version#
| Thing |
Learned? |
Lives where |
| Parameter |
Yes, by optimizer |
Checkpoint, then GPU |
| Hyperparameter |
No, chosen by human |
config.json, run script |
| Activation |
No, computed |
Transient GPU buffers |
| Context |
No, supplied |
Request payload |
| Token |
No, derived |
Integer array |
- Hyperparameters split into two families that people wrongly lump
together. Architectural hyperparameters fix the shape and therefore fix the
parameter count: number of layers, hidden size, feed-forward size, head
count, vocabulary size, maximum position count. Optimization hyperparameters
do not change the count at all: learning rate, warmup steps, weight decay,
batch size, dropout rate, gradient clipping threshold, number of epochs.
- Sampling settings supplied per request, such as temperature, top-p, top-k
and repetition penalty, are sometimes loosely called hyperparameters. They
are inference settings. They change decoding, not the model.
- Activations are the intermediate tensors of the forward pass. During
inference with no gradient tracking, most are freed as soon as the next
layer consumes them, so peak activation memory is a few hundred megabytes
for a single sequence, not gigabytes.
- During training the picture inverts. Activations must be retained for the
backward pass. Using the accounting from the 2022 NVIDIA paper Reducing
Activation Recomputation in Large Transformer Models, activation memory per
layer per sample is about
s*b*h*(34 + 5*a*s/h) bytes in 16-bit, where s is
sequence length, b batch, h hidden size and a head count.
- For a 7B-class model at sequence length 2048 and batch 1 that is about
912 MiB per layer, so about 28.5 GiB across 32 layers for a single sample,
before any recomputation is applied. Gradient checkpointing trades compute
for memory to cut that sharply.
- The key-value cache is a special, persistent kind of activation. It is
the only activation deliberately kept across decoding steps, and it grows
linearly with context length. It is covered with numbers in section 47.4.
- Context is data on the request path. It has no gradient, no persistence
and no effect on any stored parameter. This is the technical reason a model
cannot “learn from your chat” unless a separate fine-tuning job is run.
- Tokens are integers in the range 0 to V-1. They index rows of the
embedding matrix. They are not parameters; the rows they select are.
- Observing them:
config.json and generation_config.json hold
hyperparameters, nvidia-smi and torch.cuda.max_memory_allocated() show
activation and cache growth, and any tokenizer’s encode returns the tokens.
WORDS47.3.6 remember these#
- Hyperparameter — a choice made before training — a configuration value not
updated by gradient descent, either architectural or optimization-related.
- Activation — a temporary number inside the model while it runs — an
intermediate tensor of the forward pass, freed unless retained for backward.
- Context — the text being processed right now — the input token sequence
supplied per request, including any system prompt and history.
- Key-value cache — the model’s notes on the conversation so far — cached
per-layer key and value tensors reused across decoding steps.
- Inference — using the model — a forward pass with no gradient computation
and no parameter update.
- Forward pass — pushing data through the model once — computing outputs layer
by layer from inputs.
- Gradient checkpointing — recomputing instead of storing — discarding
activations during forward and recomputing them during backward to save
memory at the cost of extra compute.
47.4 Parameters, memory and precision#
PLAIN47.4.1 in simple words#
- Every parameter has to be stored somewhere, and storage takes space.
- So the memory a model needs is a simple multiplication.
- Memory = number of parameters x bytes used per parameter.
- That is the whole rule. There is no magic in it.
- How many bytes per parameter? That is your choice, and it is a trade.
- Four bytes stores a number very accurately. Two bytes stores it less
accurately but takes half the space.
- One byte is less accurate still. Half a byte, which means four bits, is
cruder again.
- A 7 billion parameter model at two bytes each needs about 14 gigabytes.
- The same model at half a byte each needs about 3.5 gigabytes.
- Same model. Same parameters. Different amount of detail kept per number.
- But the weights are not the only thing in memory when the model runs.
- There is also the model’s running notes on your conversation, the temporary
numbers, and the software’s own overhead.
- And training is far worse again, because training keeps several extra copies
of every parameter. We will work that out too.
PLAIN47.4.2 a picture in your head#
- Think of writing down the price of something.
- You could write 12.345678. That is very precise and takes many characters.
- You could write 12.35. Less precise, fewer characters.
- You could write 12. Cruder still, fewest characters.
- If you have to write down seven billion prices, the choice matters enormously
for how much paper you need.
- And for most purposes 12.35 is good enough. Nobody notices the missing
digits when you are adding up a shopping basket.
- That is exactly the precision trade in a model. Fewer digits per number,
much less memory, slightly less exact answers.
- Now the training part. To train, you do not only need the current prices.
- You also need, for every price, a note of which direction to move it, and
two running averages of how it has been moving lately.
- So training needs about four to five sheets of paper for every one sheet
that using the finished model needs.
Where this comparison breaks: numbers in a computer are not stored as decimal
digits. They are stored in a floating point format with a sign, an exponent
and a fraction, and the loss of accuracy is not a clean chopping of digits. It
is a coarser grid of representable values, and the grid spacing changes with
magnitude.
PLAIN47.4.3 a worked example#
- Take the same models at four different precisions. Here is the arithmetic
for weights only, using 1 GiB = 1,073,741,824 bytes.
- 7 billion x 4 bytes = 28,000,000,000 bytes = 26.1 GiB.
- 7 billion x 2 bytes = 14,000,000,000 bytes = 13.0 GiB.
- 7 billion x 1 byte = 7,000,000,000 bytes = 6.5 GiB.
- 7 billion x 0.5 bytes = 3,500,000,000 bytes = 3.3 GiB.
- Repeat for the other sizes and the full table falls out:
| Model |
fp32 GiB |
fp16 GiB |
| 7B |
26.1 |
13.0 |
| 13B |
48.4 |
24.2 |
| 70B (70.55B) |
262.8 |
131.4 |
| 405B (405.85B) |
1511.9 |
756.0 |
| Model |
int8 GiB |
int4 GiB |
| 7B |
6.5 |
3.3 |
| 13B |
12.1 |
6.1 |
| 70B (70.55B) |
65.7 |
32.9 |
| 405B (405.85B) |
378.0 |
189.0 |
- Sanity check against reality: the published fp16 file for Llama 3.1 70B is
about 141 GB in decimal gigabytes, which is 131 GiB. The arithmetic matches
the real file.
- Now add what inference needs beyond the weights.
- The key-value cache for Llama 3.1 70B is 320 KiB per token. At 8,000 tokens
that is 2.5 GiB. At 32,000 tokens it is 10 GiB.
- Temporary activations for one sequence are roughly 0.5 to 1 GiB.
- The framework’s own overhead, buffers and fragmentation is another 1 to
2 GiB in practice.
- So a realistic total for 70B at fp16 with an 8,000 token context is
131.4 + 2.5 + 1.0 + 2.0 = about 137 GiB, not 131.
PLAIN47.4.4 what is really happening inside#
- Training memory is a different story, and much bigger, for a simple reason.
- Training must keep, for every single parameter, four extra things.
- First, the parameter itself, usually kept twice: a fast low-precision copy
used for the maths and an accurate master copy used for the update.
- Second, the gradient, which is one number per parameter saying which way to
nudge it.
- Third and fourth, the optimizer’s two running averages. The standard
optimizer, called Adam, keeps a running average of recent gradients and a
running average of recent squared gradients.
- That is what “Adam’s two moments” means: two extra numbers per parameter.
- Count the bytes for the common mixed-precision recipe:
16-bit working weights 2 bytes
32-bit master weights 4 bytes
gradients 4 bytes
Adam first moment (m) 4 bytes
Adam second moment (v) 4 bytes
----------
total 18 bytes per parameter
- For a 7B model: 7,000,000,000 x 18 = 126,000,000,000 bytes = 117 GiB.
- That is before a single activation is stored.
- Add training activations, which for a 7B-class model at sequence length
2048 come to roughly 28 GiB per sample without recomputation.
- So training a 7B model is a 150 GiB-plus problem, not a 14 GiB problem.
- That is why a model you can happily run on one 24 GiB graphics card cannot
be fully fine-tuned on it. Using needs weights. Training needs weights plus
four more copies plus history.
- It is also why techniques that train only a small extra set of parameters,
which Chapter 50 covers, exist at all.
TECHNICAL47.4.5 the engineer’s version#
- The precision formats, with their real layouts:
| Format |
Bits |
Layout (sign/exp/frac) |
| fp32 |
32 |
1 / 8 / 23 |
| fp16 |
16 |
1 / 5 / 10 |
| bf16 |
16 |
1 / 8 / 7 |
| fp8 E4M3 |
8 |
1 / 4 / 3 |
- bfloat16 was introduced by Google for its Tensor Processing Units and keeps
fp32’s 8-bit exponent, so it has the same dynamic range as fp32 with far
fewer fraction bits. That is why bf16 training rarely needs loss scaling
while fp16 training usually does.
- fp16 has a maximum finite value of 65504 and underflows below about 6e-8,
which is why gradients in fp16 are scaled up before the backward pass and
scaled down afterwards.
- fp8 in the E4M3 and E5M2 variants was standardized across NVIDIA, Arm and
Intel in a 2022 joint proposal, and is supported in hardware from NVIDIA’s
Hopper generation, announced March 2022. NVIDIA’s Blackwell generation,
announced 18 March 2024, added hardware support for 4-bit floating point.
- Key-value cache size per token is exact and worth memorizing:
kv_bytes_per_token = 2 * L * n_kv * head_dim * bytes_per_element
- The leading 2 is for keys and values. Real figures at fp16:
| Config |
Per token |
32k ctx |
128k ctx |
| Llama 3.1 8B (GQA 8) |
128 KiB |
4.0 GiB |
16.0 GiB |
| Llama 3.1 70B (GQA 8) |
320 KiB |
10.0 GiB |
40.0 GiB |
| Llama 3.1 405B (GQA 8) |
504 KiB |
15.8 GiB |
63.0 GiB |
| 70B-shape without GQA |
2560 KiB |
80.0 GiB |
320.0 GiB |
- That last row is the point of grouped-query attention. Cutting key-value
heads from 64 to 8 cuts the cache by a factor of 8, which is what makes long
contexts affordable at all.
- Full-fine-tuning memory in bytes per parameter, by optimizer:
| Optimizer state |
Bytes/param |
7B total |
| Adam, mixed precision |
18 |
117 GiB |
| Adam, pure fp32 |
16 |
104 GiB |
| SGD with momentum |
12 |
78 GiB |
| Inference, fp16 |
2 |
13 GiB |
- Adam was published by Diederik Kingma and Jimmy Ba in 2014 as Adam: A Method
for Stochastic Optimization; AdamW, the decoupled weight decay variant used
by almost every modern language model, came from Ilya Loshchilov and Frank
Hutter in 2017. Both keep two moment estimates per parameter, which is the
source of the 8 bytes.
- ZeRO, from the 2019 Microsoft paper ZeRO: Memory Optimizations Toward
Training Trillion Parameter Models, shards optimizer states, gradients and
parameters across data-parallel ranks in three stages, so the per-device
cost falls roughly by the number of devices. It does not reduce the total.
- Practical observation commands:
nvidia-smi --query-gpu=memory.used --format=csv, torch.cuda.memory_summary(), and for serving,
vLLM’s reported KV cache blocks and --gpu-memory-utilization setting.
WORDS47.4.6 remember these#
- Precision — how many digits are kept per number — the numeric format’s bit
width and its split between exponent and fraction.
- fp32 — the accurate, expensive format — IEEE 754 single precision,
4 bytes, 24 bits of significand including the implicit bit.
- bf16 — the training-friendly 16-bit format — brain floating point, fp32’s
exponent range with 8 bits of significand.
- Key-value cache — the model’s stored notes on tokens seen so far — cached K
and V tensors per layer, sized 2 x L x n_kv x head_dim per token.
- Optimizer state — the extra bookkeeping training needs — Adam’s first and
second moment estimates, one of each per parameter.
- Mixed precision — computing in 16-bit, updating in 32-bit — a training
recipe with a low-precision working copy and an fp32 master copy.
- GiB versus GB — two different gigabytes — 2^30 = 1,073,741,824 bytes versus
10^9 bytes; file sizes are usually quoted in the decimal form and memory in
the binary form, and confusing them costs you 7 per cent.
47.5 Quantization#
PLAIN47.5.1 in simple words#
- Quantization means storing each of the model’s numbers using fewer bits.
- That is the whole idea in one line. Fewer bits per number, smaller model.
- The trained numbers are typically stored in 16 bits each.
- Quantization rewrites them into 8 bits, or 4 bits, or sometimes fewer.
- Nothing about the model’s shape changes. It still has 70 billion parameters.
- Each of those 70 billion is just written down more roughly.
- To do it you need a trick, because a 4-bit slot holds only 16 different
values, and the real weights are spread over a wide range.
- The trick is to store a small group of weights together with one shared
scaling number.
- The 4-bit value says “which of my 16 steps”, and the shared scale says “and
here is how big a step is”.
- Multiply them back together at load time or on the fly, and you get an
approximation of the original number.
- The approximation is not exact. The question is whether the error matters.
- Mostly, at 8 bits and 5 bits, it barely does. At 4 bits it is small. Below
that it starts to hurt in ways you can measure.
PLAIN47.5.2 a picture in your head#
- Imagine a shop that must label 10,000 items with prices.
- The prices range from 1 rupee to 3,000 rupees.
- The labels only have room for a single digit, 0 to 9.
- That looks impossible, until you split the shop into departments.
- In the pencil department, prices run 1 to 10 rupees, so you write on the
department sign: “each step on the label = 1 rupee”.
- In the furniture department, prices run 1,000 to 3,000, so that sign says
“each step on the label = 200 rupees”.
- Now a label reading 7 means 7 rupees in one department and 1,400 in the
other, and one digit is enough everywhere.
- The department sign is the scale. The department is the group.
- Smaller departments mean more accurate prices but more signs to store.
- That is the entire design space of quantization: how small the groups are,
and how much extra you pay to store the signs.
Where this comparison breaks: real quantization also needs a zero point
when the values are not centred on zero, so the label’s 0 does not have to mean
a value of 0. And unlike a shop, a badly chosen department boundary does not
just annoy a customer; it puts an outlier weight far outside the range and
distorts every weight in the group with it.
PLAIN47.5.3 a worked example#
- Take eight real-looking weights and quantize them by hand.
- The values: 0.42, -1.13, 0.05, 2.31, -0.77, 1.02, -2.90, 0.66.
- Symmetric 8-bit first. Find the largest absolute value: 2.90.
- Scale = 2.90 / 127 = 0.022835.
- Divide each value by the scale and round:
value /scale rounded int8
0.42 18.39 18
-1.13 -49.49 -49
0.05 2.19 2
2.31 101.16 101
-0.77 -33.72 -34
1.02 44.67 45
-2.90 -127.00 -127
0.66 28.90 29
- To use them, multiply back by the scale. 18 x 0.022835 = 0.4110.
- Original 0.42, recovered 0.4110. Error 0.0090.
- Across all eight, the largest error is 0.0111. Small.
- Now 4-bit, asymmetric, using minimum and maximum instead of absolute maximum.
- Minimum -2.90, maximum 2.31. Scale = (2.31 - (-2.90)) / 15 = 0.347333.
- Zero point = round(2.90 / 0.347333) = 8. So the stored code 8 means 0.
- Quantized codes: 9, 5, 8, 15, 6, 11, 0, 10.
- Recovered values: 0.3473, -1.0420, 0.0000, 2.4313, -0.6947, 1.0420,
-2.7787, 0.6947.
- Largest error now 0.1213, about eleven times worse than 8-bit. That is the
price of four bits instead of eight.
- Note the storage cost of the scale itself. If one 16-bit scale and one
16-bit zero point cover a group of 64 weights, the real cost is
4 + 32/64 = 4.5 bits per weight, not 4.
PLAIN47.5.4 what is really happening inside#
- There are three levels of granularity, and they are the main design choice.
- Per-tensor: one scale for an entire matrix. Cheapest to store, worst
accuracy, because one outlier anywhere ruins the scale for everything.
- Per-channel: one scale per row or per column. Much better, because a
badly behaved output channel no longer poisons its neighbours.
- Per-group: one scale for every fixed run of weights, typically 32, 64,
128 or 256 of them. Best accuracy, most scales to store.
- Then there is the question of when you quantize.
- Post-training quantization takes a finished model and compresses it.
Fast, needs no training run, sometimes needs a few hundred sample texts to
calibrate the scales.
- Quantization-aware training simulates the rounding during training, so
the model learns weights that survive rounding well. Much more expensive,
better results at very low bit widths.
- Finally: not everything gets squeezed equally.
- The embedding table and the output layer are often kept at higher precision,
because errors there hit every token directly and there is no later layer to
absorb the damage.
- Normalization scales, which are a tiny fraction of parameters, are almost
always left at 16 or 32 bits.
- Some formats keep specific matrices, such as the attention value projection
and the feed-forward down projection, at a higher bit width because
measurements showed those layers are the most sensitive.
- And the key-value cache is a separate decision. You can run 4-bit weights
with a 16-bit cache, or quantize the cache too.
TECHNICAL47.5.5 the engineer’s version#
- The affine quantization map, which is the standard, is:
q = clamp(round(x / s) + z, qmin, qmax)
x_hat = (q - z) * s
- Symmetric quantization fixes z = 0 and sets s = max|x| / qmax. Asymmetric
quantization derives s from the range and z from the offset. Symmetric is
cheaper at run time because the zero point drops out of the arithmetic.
- The named methods, with correct attribution:
| Method |
Year |
One-line idea |
| LLM.int8() |
2022 |
Split out outlier channels |
| GPTQ |
2022 |
Second-order error correction |
| AWQ |
2023 |
Protect salient weights |
| NF4 (QLoRA) |
2023 |
Normal-distribution 4-bit |
- LLM.int8(), by Tim Dettmers and colleagues in 2022, observed that a small
number of feature dimensions carry extreme magnitudes and must stay in 16
bits, while the rest go to int8. This is mixed-precision decomposition.
- GPTQ, by Elias Frantar, Saleh Ashkboos, Torsten Hoefler and Dan Alistarh,
posted October 2022 and published at ICLR 2023, quantizes weights column by
column and uses approximate second-order information from a small
calibration set to adjust the not-yet-quantized weights to compensate for
the error just introduced.
- AWQ, Activation-aware Weight Quantization, by Ji Lin and colleagues at MIT,
posted June 2023 and awarded Best Paper at MLSys 2024, observes that
protecting roughly 1 per cent of weights, chosen by activation magnitude
rather than weight magnitude, recovers most of the loss, and implements the
protection by per-channel scaling rather than by keeping mixed precision.
- NF4, NormalFloat4, comes from the QLoRA paper by Tim Dettmers, Artidoro
Pagnoni, Ari Holtzman and Luke Zettlemoyer, May 2023. Its 16 levels are
placed at the quantiles of a normal distribution rather than evenly, because
trained weights are approximately normally distributed. QLoRA also applies
double quantization, quantizing the scales themselves, saving about 0.37
bits per parameter.
- GGUF is the file format used by llama.cpp, introduced on 21 August 2023 as
the successor to GGML, GGMF and GGJT. Its k-quant types use a super-block of
256 weights subdivided into blocks of 32. Exact sizes, read from the ggml
header:
| Type |
Bytes per 256 |
Bits per weight |
| Q2_K |
84 |
2.625 |
| Q3_K |
110 |
3.4375 |
| Q4_K |
144 |
4.5 |
| Q5_K |
176 |
5.5 |
| Q6_K |
210 |
6.5625 |
- The suffixes S and M mean small and medium mixes. Q4_K_M keeps the attention
value projections and the feed-forward down projections at Q6_K in part of
the network, so its measured average is about 4.8 bits per weight rather
than 4.5. Checking against real published files: Llama 3.1 8B at Q4_K_M is
4.9 GB, and 8.03e9 x 4.83 / 8 = 4.85e9 bytes. The published 405B Q4_K_M file
is 243 GB, and 405.85e9 x 4.79 / 8 = 243e9. The arithmetic closes both ways.
- Quality cost, measured. From a January 2026 evaluation of llama.cpp
quantization on Llama-3.1-8B-Instruct, WikiText-2 perplexity, lower better:
| Format |
Perplexity |
Size cut |
| fp16 baseline |
7.32 |
0% |
| Q8_0 |
7.33 |
46.9% |
| Q6_K |
7.35 |
59.0% |
| Q5_K_M |
7.40 |
64.4% |
| Q4_K_M |
7.56 |
69.4% |
| Q3_K_M |
7.96 |
75.0% |
| Q3_K_S |
8.96 |
77.2% |
- Read that table honestly. Q8_0 costs 0.01 perplexity, which is noise.
Q4_K_M costs 0.24, about 3.3 per cent relative, for a 69 per cent size cut.
Q3_K_S costs 1.64, about 22 per cent relative, and that is a real
degradation you will notice.
- The same study reported that on aggregate downstream scores across GSM8K,
HellaSwag, IFEval, MMLU and TruthfulQA, several quantized variants scored
within noise of the fp16 baseline, and Q5_0 scored marginally above it.
That last result is a reminder that benchmark noise at these margins is
larger than the quantization effect, not evidence that quantizing helps.
- Now the laptop arithmetic the question asks for. Assume about 75 per cent
of installed memory is usable for the model, the rest going to the
operating system and everything else.
Machine with 64 GiB, about 48 GiB usable:
70B at fp16 131.4 GiB weights -> does not fit
70B at int8 65.7 GiB weights -> does not fit
70B at Q4_K_M 39.7 GiB weights
+ 2.5 GiB KV at 8k
+ 2.5 GiB other -> 44.7 GiB, fits
Machine with 16 GiB, about 12 GiB usable:
8B at fp16 15.0 GiB weights -> does not fit
8B at int8 7.5 GiB weights
+ 1.0 GiB KV at 8k
+ 1.3 GiB other -> 9.8 GiB, fits
8B at Q4_K_M 4.5 GiB weights -> 6.8 GiB total, fits easily
70B at Q4_K_M 39.7 GiB weights -> does not fit
- So the honest answer to “why can a 70B model run on a laptop at 4-bit but
not at 16-bit” is: 131.4 GiB does not fit in 48 GiB and 39.7 GiB does, and
that is the entire reason. Note the 64 GiB case only works with a modest
context. Raise the context to 32,000 tokens and the cache alone adds 10 GiB,
taking the total to about 52 GiB, which no longer fits.
- On Apple silicon the calculation uses unified memory, and the default cap
on how much the GPU may claim is around 65 to 75 per cent of installed RAM
depending on machine and macOS version, which is where the 75 per cent rule
of thumb above comes from.
- Speed, separately from memory: at batch size 1, generation is memory
bandwidth bound, so halving the bytes per weight roughly doubles tokens per
second. That is often a bigger practical win than the memory saving.
- Tools:
llama-quantize in llama.cpp, AutoGPTQ and GPTQModel,
autoawq, bitsandbytes for NF4 and int8, llm-compressor for
vLLM-targeted formats, and optimum-quanto in the Hugging Face stack.
WORDS47.5.6 remember these#
- Quantization — storing each number in fewer bits — mapping high-precision
weights onto a small integer grid with a scale and optional zero point.
- Scale — how big one step of the integer grid is — the multiplier s in
x_hat = (q - z) * s.
- Zero point — where the integer grid’s zero sits — the offset z that lets an
asymmetric range be represented.
- Group size — how many weights share one scale — commonly 32, 64 or 128;
smaller means more accurate and more metadata.
- Post-training quantization — squeezing a finished model — PTQ, applied to
trained weights with at most a small calibration set.
- Quantization-aware training — training with the rounding simulated — QAT,
with fake quantization in the forward pass and straight-through gradients.
- Perplexity — how surprised the model is by real text — the exponential of
the mean negative log-likelihood per token; lower is better, and only
comparable between models sharing a tokenizer.
- Outlier channel — a feature dimension with extreme values — a dimension whose
magnitude forces a bad scale for everything grouped with it.
47.6 Why capability does not scale linearly with parameter count#
PLAIN47.6.1 in simple words#
- Bigger models are, on average, better. That much is true.
- But parameter count is a weak way to guess how good a model is.
- A model is the product of at least five things, not one.
- How many parameters it has. How much text it was trained on. How good that
text was. How much computing time was spent. What was done to it afterwards.
- Get any one of those badly wrong and a huge model performs poorly.
- Get them all right and a small model can beat a much larger one.
- This is not a rare exception. It happens constantly.
- In 2022 a 70 billion parameter model called Chinchilla beat a 280 billion
parameter model called Gopher, using the same computing budget.
- It won by being smaller and reading four times as much text.
- That result changed how the whole field trains models.
- So when a company says its model has more parameters than a rival’s, that is
a fact about the file size, not a claim about usefulness that you should
accept without evidence.
- The honest position: parameter count sets a ceiling. It does not tell you
how close to that ceiling the model got.
PLAIN47.6.2 a picture in your head#
- Think of a student preparing for an exam.
- Parameter count is how much the student could remember if they studied.
Call it brain capacity.
- Training data is how many books they read.
- Data quality is whether the books were good ones or nonsense from the
internet.
- Training compute is how many hours they actually spent studying.
- The stages after training are the coaching: practice papers, feedback on
answers, learning how to lay out a solution clearly.
- Now: a student with enormous capacity who read three books and never
practised will fail.
- A student with ordinary capacity who read widely, studied hard and got good
coaching will do well.
- Everyone knows this about students. The same thing is true about models, and
the industry spent years talking as though only capacity mattered.
Where this comparison breaks: a student’s capacity is fixed and unknown; a
model’s is chosen and exactly known. And a model has no motivation, no sleep and
no forgetting, so the analogy tells you nothing about the training dynamics. It
only tells you that one input out of five does not determine the output.
PLAIN47.6.3 a worked example#
- Take the Chinchilla experiment, published by Jordan Hoffmann and colleagues
at DeepMind in March 2022, in Training Compute-Optimal Large Language Models.
- Gopher: 280 billion parameters, trained on 300 billion tokens.
- Chinchilla: 70 billion parameters, trained on 1.4 trillion tokens.
- Both used about the same total computing budget. That is the key control.
- Chinchilla is four times smaller and read four and a half times more.
- On MMLU, a broad multiple-choice benchmark, Chinchilla scored 67.5 per cent,
more than 7 percentage points above Gopher.
- It also beat GPT-3 at 175 billion parameters, Jurassic-1 at 178 billion and
Megatron-Turing NLG at 530 billion.
- A 70B model beat a 530B model. That is a factor of seven and a half in
parameter count, going the wrong way for the bigger model.
- The paper’s rule of thumb: for a fixed compute budget, model size and
training tokens should grow together, roughly 20 tokens per parameter.
- By that rule, GPT-3 at 175B should have been trained on about 3.5 trillion
tokens. It was trained on 300 billion. It was undertrained by roughly a
factor of ten.
- So the entire generation of models before 2022 was too big for the amount of
text it had read.
PLAIN47.6.4 what is really happening inside#
- Training reduces a single measured quantity: the average surprise the model
feels when it sees real text.
- That quantity falls smoothly and predictably as you add parameters, add data
or add compute. Those smooth curves are called scaling laws.
- A scaling law is a fitted curve, not a law of nature. It describes what has
been measured over the ranges measured.
- The curves are also very shallow. To halve the error you need far more than
double the size.
- And low surprise on text is not the same thing as being useful to a person.
- That gap is closed by the stages after pre-training: supervised fine-tuning
on demonstrations, and preference training on human comparisons.
- Those stages change very few things about the model’s knowledge and a great
deal about its behaviour.
- A raw pre-trained model of 70 billion parameters is often unusable as an
assistant. The same model after post-training is helpful. Same parameters,
different usefulness.
- Data quality does something similar. Training on carefully filtered,
deduplicated, high-quality text produces much better models per parameter
than training on raw web scrape.
- So the causal chain from parameter count to usefulness runs through at least
four other factors, each of which can dominate.
TECHNICAL47.6.5 the engineer’s version#
- Separate the three kinds of claim, as the reader asked.
- Established fact. Loss falls as a power law in parameters, data and
compute over several orders of magnitude. Kaplan and colleagues at OpenAI
established this in January 2020 in Scaling Laws for Neural Language Models.
- Established fact. Kaplan’s recommended allocation was corrected by
Hoffmann and colleagues in March 2022. Under a fixed compute budget, the
compute-optimal ratio is roughly 20 training tokens per parameter, and
pre-2022 models were badly undertrained.
- Established fact. The Chinchilla scaling coefficients were partially
disputed. Epoch AI published a replication attempt in 2024 finding that the
paper’s third estimation approach had fitting problems, though the broad
conclusion, that data and parameters should scale together, survived.
- Established fact, and important. Compute-optimal is not
deployment-optimal. If a model will serve billions of requests, it is
rational to train a smaller model far past its Chinchilla point, because
inference cost is paid forever and training cost is paid once. Llama 3 8B
was trained on about 15 trillion tokens, which is roughly 1,875 tokens per
parameter, nearly 100 times the Chinchilla ratio, and it is a much better 8B
model for it.
- Established fact. Data curation matters enormously per parameter.
Microsoft’s phi line, starting with the June 2023 paper Textbooks Are All
You Need, showed that filtered and synthetic textbook-style data lets very
small models reach scores that previously needed models many times larger,
though critics noted benchmark contamination risk in such setups.
- Established fact. Mistral 7B, released 27 September 2023, outperformed
Llama 2 13B on all benchmarks its authors reported. A 7B beat a 13B. Neither
architecture nor data was held constant, which is exactly the point:
parameter count alone did not decide it.
- Active research. How much of frontier capability comes from post-training
as against pre-training scale; whether reasoning-focused post-training and
test-time compute change the scaling picture; how to measure data quality
before spending the compute.
- Marketing claim. Any headline of the form “our model has N parameters,
therefore it is better”. Also any comparison that quotes total parameters
for a mixture-of-experts model against dense parameters for a rival without
saying so.
- Where experts disagree. Some argue continued scaling of pre-training is
still the main driver of progress; others argue returns from pre-training
scale have flattened and that data quality, post-training and inference-time
compute now dominate. Both camps include serious researchers, and the
public evidence does not settle it, partly because frontier training details
are no longer published.
- Practical consequence for an engineer: never select a model by parameter
count. Select by measured performance on your own task, then by cost and
latency. Parameter count is only useful for predicting memory and speed,
which it predicts extremely well.
| Model |
Params |
Tokens trained |
Ratio |
| GPT-3 (2020) |
175B |
300B |
1.7 |
| Gopher (2021) |
280B |
300B |
1.1 |
| Chinchilla (2022) |
70B |
1.4T |
20 |
| Llama 3 8B (2024) |
8B |
~15T |
~1875 |
WORDS47.6.6 remember these#
- Scaling law — a curve saying how much better a bigger model gets — a fitted
power law relating loss to parameters, data or compute.
- Compute-optimal — the best size for a fixed training budget — the
parameter-and-token split minimizing loss at fixed FLOPs, about 20 tokens
per parameter per Chinchilla.
- Undertrained — too big for the text it read — a model whose token count is
far below its compute-optimal allocation.
- Overtrained (deliberately) — trained far past compute-optimal on purpose —
a smaller model given many more tokens to reduce lifetime inference cost.
- Pre-training — the long first phase — self-supervised next-token prediction
on a very large corpus.
- Post-training — everything done afterwards — supervised fine-tuning,
preference optimization and safety training, covered in Chapter 50.
- MMLU — a broad general-knowledge exam for models — Massive Multitask Language
Understanding, 57 subjects of multiple-choice questions, released 2020.
47.7 Tokens: why text is not fed in as words or characters#
PLAIN47.7.1 in simple words#
- A model works on numbers, so text must become numbers first.
- The obvious plan is: one number per word. It fails, for four reasons.
- First, there is no fixed list of all words. New ones appear every day.
- Second, typos. “recieve” is not in any word list, so it becomes an unknown.
- Third, many languages do not put spaces between words at all.
- Fourth, a word list big enough to be useful is enormous, and the model would
need one row of numbers per word, which is very expensive.
- The opposite plan is: one number per letter. That fails too.
- A page of text becomes thousands of letters, so sequences get very long, and
long sequences are expensive.
- Worse, the model must then learn spelling from scratch before it can learn
anything about meaning.
- So neither extreme works. The answer is in between: chop text into pieces
that are usually bigger than a letter and often smaller than a word.
- Those pieces are called tokens or subwords.
- Common words become one token. Rare words become several pieces.
- A word nobody has ever seen still gets encoded, because in the worst case
it falls back to very small pieces.
- Nothing is ever unknown. That is the property that makes it work.
PLAIN47.7.2 a picture in your head#
- Think about how a printer’s typecase used to work.
- One option: cut a separate metal block for every word in the language.
Millions of blocks, and a new one every time somebody coins a word.
- Another option: keep only 26 letter blocks and build everything letter by
letter. Very few blocks, but setting one page takes forever.
- What real printers did was in between. They kept letters, and they also kept
ligatures: single blocks for common combinations like “fi” and “ffl”.
- Frequent combinations got their own block because it saved work.
- Rare combinations were built up from letters.
- That is exactly what subword tokenization does, chosen automatically by
counting which combinations are frequent in a big pile of text.
Where this comparison breaks: a printer’s ligatures were chosen by human
craftsmen for how they looked. A tokenizer’s pieces are chosen by a counting
algorithm for how often they occur, and many of them look strange to a human
eye. There is also no requirement that a token be a meaningful unit. Tokens
regularly cut straight through the middle of a morpheme.
PLAIN47.7.3 a worked example#
- Here is byte-pair encoding worked completely, on a tiny corpus.
- The corpus is four words with counts: low appears 5 times, lower 2 times,
newest 6 times, widest 3 times.
- Start by splitting every word into characters, with an underscore marking the
end of a word.
- The starting symbol set is 11 symbols:
_ d e i l n o r s t w.
- Now count every adjacent pair, weighted by word frequency, and merge the most
frequent pair. Repeat.
step 1: top pairs es=9 st=9 t_=9 we=8 lo=7 ow=7
merge e+s -> es
l o w _ (5)
l o w e r _ (2)
n e w es t _ (6)
w i d es t _ (3)
step 2: top pairs est=9 t_=9 lo=7 ow=7 ne=6 ew=6
merge es+t -> est
n e w est _ (6)
w i d est _ (3)
step 3: merge est+_ -> est_ (count 9)
n e w est_ (6)
w i d est_ (3)
step 4: merge l+o -> lo (count 7)
step 5: merge lo+w -> low (count 7)
low _ (5)
low e r _ (2)
step 6: merge n+e -> ne (count 6)
step 7: merge ne+w -> new (count 6)
step 8: merge new+est_ -> newest_ (count 6)
step 9: merge low+_ -> low_ (count 5)
step 10: merge w+i -> wi (count 3)
- After 10 merges the words look like this:
low_ (5), low e r _ (2),
newest_ (6), wi d est_ (3).
- The learned merge list, in order, is:
es, est, est_, lo, low, ne, new, newest_, low_, wi.
- The vocabulary is now 11 starting symbols + 10 merges = 21 tokens.
- Notice what happened. “newest” became a single token because it was frequent.
- “lower” is still in pieces, because it appeared only twice.
- “est_” became a token that means, roughly, the superlative ending. Nobody
told the algorithm about grammar. It fell out of counting.
- Real training does this 30,000 to 200,000 times instead of 10 times, on
billions of words instead of 16.
PLAIN47.7.4 what is really happening inside#
- Training a tokenizer and using one are two different programs.
- Training: count pairs, merge the most frequent, repeat until the vocabulary
reaches the target size. Save the ordered merge list and the vocabulary.
- Using: split the incoming text into characters, then apply the saved merges
in exactly the order they were learned. Same order, every time.
- That ordering is what makes the process deterministic. The same text always
gives the same tokens.
- Then each final piece is looked up in the vocabulary to get its integer id.
- A crucial detail: modern tokenizers do not start from characters. They start
from bytes.
- There are exactly 256 possible byte values, so every possible input, in any
language, in any encoding, including broken text, can always be represented.
- That is why you never see an “unknown token” error from a modern model.
- The cost of the byte fallback is that unusual scripts get split into
individual bytes, and one character can become three or four tokens.
- There is also a pre-tokenization step. Before any merging, the text is split
by a fixed pattern, usually keeping leading spaces attached to words and
splitting runs of digits into groups.
- That pattern is why " the" with a leading space is a different token from
“the” without one, and both exist in the vocabulary.
TECHNICAL47.7.5 the engineer’s version#
- The four families you need to be able to name and tell apart:
| Family |
Chooses merges by |
First described |
| BPE |
Highest pair frequency |
Gage 1994; Sennrich 2016 |
| WordPiece |
Highest likelihood gain |
Schuster, Nakajima 2012 |
| Unigram LM |
Prunes a large set |
Kudo 2018 |
| SentencePiece |
Framework, not algorithm |
Kudo, Richardson 2018 |
- Byte-pair encoding was invented as a data compression algorithm by
Philip Gage, published in The C Users Journal in February 1994. Rico
Sennrich, Barry Haddow and Alexandra Birch adapted it to machine translation
in Neural Machine Translation of Rare Words with Subword Units, posted 2015
and published at ACL 2016. GPT-2 in 2019 moved BPE to operate on raw bytes,
giving the 256-symbol base alphabet and eliminating unknown tokens.
- WordPiece came from Mike Schuster and Kaisuke Nakajima at Google in the
2012 ICASSP paper Japanese and Korean Voice Search. It merges the pair that
most increases the likelihood of the training data under a unigram language
model, rather than the most frequent pair. Concretely it maximizes
count(xy) / (count(x) * count(y)), so a pair of two rare symbols can beat a
pair of two common ones. BERT, released by Google in October 2018, uses
WordPiece, and marks word continuations with a double hash prefix.
- Unigram language model tokenization was introduced by Taku Kudo in the
2018 ACL paper Subword Regularization. It works in the opposite direction:
start with a large candidate vocabulary, score every candidate by how much
removing it would hurt the corpus likelihood, and iteratively prune the worst
until the target size is reached. Because it keeps a probability per token,
it can sample alternative segmentations of the same string, which is used as
a data augmentation technique.
- SentencePiece is a library, not an algorithm, from Taku Kudo and John
Richardson, EMNLP 2018 system demonstrations. It implements both BPE and
Unigram, and its real contribution is treating input as a raw stream with no
language-specific pre-tokenization, encoding the space character itself as a
visible symbol so that detokenization is exactly reversible. That is what
makes it work for Japanese, Chinese and Thai, which do not delimit words with
spaces. T5, Llama 1 and Llama 2 use SentencePiece; Llama 3 moved to a
tiktoken-style byte-level BPE with a 128,000-entry vocabulary.
- Real vocabulary sizes, for scale:
| Tokenizer |
Vocabulary |
Used by |
| GPT-2 (r50k) |
50,257 |
GPT-2, GPT-3 |
| cl100k_base |
100,277 |
GPT-3.5, GPT-4 |
| o200k_base |
200,019 |
GPT-4o and later |
| Llama 3 |
128,256 |
Llama 3.x family |
- Larger vocabularies mean fewer tokens per document, which means cheaper
attention and shorter sequences, but a larger embedding and output matrix.
Going from 50k to 200k vocabulary at hidden size 4096 costs about 1.2 billion
extra parameters across the two tables.
- Special tokens are added outside the learned merges: sequence start, sequence
end, padding, and chat role markers such as a header start marker. These are
reserved ids that the merge process can never produce from text, which is a
deliberate security property: user text cannot forge a role boundary.
- Tools:
tiktoken for the OpenAI encodings, tokenizers and
AutoTokenizer in the Hugging Face stack, sentencepiece for the T5 and
Llama 2 lineage, and llama-tokenize in llama.cpp.
WORDS47.7.6 remember these#
- Token — a small piece of text the model works in — an integer id indexing one
row of the embedding matrix, produced by the tokenizer.
- Subword — a piece smaller than a word — a token that is part of a word, such
as “ization” in “tokenization”.
- Byte-pair encoding — repeatedly glue the most common pair together — BPE, a
greedy merge algorithm producing an ordered merge list.
- Byte-level — starting from raw bytes, not letters — using the 256 byte values
as the base alphabet so no input is ever unrepresentable.
- Pre-tokenization — the split done before merging — a regular expression pass
that fixes where merges may not cross, such as across whitespace.
- Vocabulary — the full list of possible tokens — the id-to-string table, size
V, equal to the number of rows in the embedding matrix.
- Special token — a marker that is not ordinary text — a reserved id such as
sequence start or a chat role header, unreachable by merging user text.
- Detokenization — turning tokens back into text — concatenating the token
strings; exactly reversible in SentencePiece and byte-level BPE.
47.8 What tokenization does in practice#
PLAIN47.8.1 in simple words#
- Now let us look at real text going through a real tokenizer.
- All the counts below are measured, not estimated.
- English prose splits neatly. Most common words are one token each.
- Numbers get chopped into groups of digits, and the groups do not line up with
how you would read the number.
- Web addresses split into many small pieces, because slashes, dots and
question marks all break the text apart.
- Program code splits reasonably well, because tokenizers are trained on a lot
of code these days.
- Hindi and other Indian scripts split badly on older tokenizers and much
better on newer ones, but still worse than English.
- An emoji is one character to you and often two tokens to an older model.
- Two consequences follow directly, and they explain famous model failures.
- The model cannot see letters, so it cannot reliably count them or reverse a
word.
- The model sees digits in arbitrary groups, so arithmetic on long numbers
goes wrong for reasons that have nothing to do with reasoning.
PLAIN47.8.2 a picture in your head#
- Imagine reading a book through a narrow slot that shows a few characters at
a time, but you do not control where the slot boundaries fall.
- Somebody else cut the page into strips before handing it to you.
- For English the strips mostly line up with words, so reading is easy.
- For a number like 17394205 the strips fell at 173, 942, 05.
- You can read the strips perfectly. You just never saw the digits as digits in
their proper columns.
- Now somebody asks you how many times the letter r appears in “strawberry”.
- You never saw letters. You saw three strips: “str”, “aw”, “berry”.
- You can guess, from having read the answer somewhere. You cannot count.
Where this comparison breaks: the model is not blind to letters in principle.
It has seen enough spelled-out text during training to have learned a lot about
which letters are inside which tokens. So it often gets these right. The point
is that it is doing recall and inference rather than direct observation, which
is why it fails unpredictably rather than never.
PLAIN47.8.3 a worked example#
- Real measurements, taken with the actual OpenAI tokenizers. cl100k_base is
the GPT-4 era encoding; o200k_base is the later one.
| Text |
cl100k |
o200k |
| The quick brown fox... (44 ch) |
10 |
10 |
| I live in India. |
5 |
5 |
| strawberry |
3 |
3 |
| 1234567890 |
4 |
4 |
| A 55-character URL |
17 |
18 |
| A 32-character Python function |
12 |
12 |
- The exact splits, which are the interesting part:
"strawberry" cl100k -> "str" "aw" "berry"
"strawberry" o200k -> "st" "raw" "berry"
"1234567890" both -> "123" "456" "789" "0"
"17394205" both -> "173" "942" "05"
"unbelievable" cl100k -> "un" "belie" "vable"
"tokenization" both -> "token" "ization"
- Look at “17394205”. The groups are 173, 942, 05. That is not thousands,
hundreds, tens. It is an arbitrary cut.
- Now the code example, tokenized by cl100k_base into 12 tokens:
"def" " add" "(a" "," " b" "):NL" " " " return" " a" " +" " b"
(and a final newline token), where NL stands for a newline
- Note that the four-space indent became a single token of three spaces plus a
space attached to “return”. Indentation is compressed, which is why code
tokenizes efficiently.
- Now Devanagari. Take the Hindi sentence meaning “I live in India”, which is
मैं भारत में रहता हूँ। In roman letters that is “main bharat mein rahta
hoon”. It is 22 characters and 58 bytes of UTF-8, because each Devanagari
character takes three bytes where an English letter takes one.
| Encoding |
Tokens for the Hindi |
English is |
| GPT-2 (r50k) |
34 |
5 |
| cl100k_base |
24 |
5 |
| o200k_base |
6 |
5 |
- On the GPT-4 era tokenizer, the same meaning costs 24 tokens in Hindi and
5 in English. That is 4.8 times more, for identical content.
- A longer measured pair: a 57-character Hindi sentence about tokenization
costs 61 tokens on cl100k_base and 21 on o200k_base. The equivalent English
sentence costs 11 on both.
- So on cl100k_base the Hindi is 5.5 times the cost. On o200k_base it is
1.9 times. The gap narrowed a lot but did not close.
- Emoji: the grinning face character, code point U+1F600, is one character and
four UTF-8 bytes. cl100k_base spends 2 tokens on it. o200k_base spends 1.
PLAIN47.8.4 what is really happening inside#
- Why does Devanagari cost so much on the older tokenizers?
- Because the tokenizer’s merges were learned from a corpus that was mostly
English. Frequent English sequences got merged into single tokens.
- Devanagari sequences were rare, so few merges were learned for them.
- What is left is the byte fallback. Each Devanagari character is three bytes
in UTF-8, and if no merge covers it, those three bytes become separate
tokens.
- That is the 3x penalty in its rawest form, before you even count the missing
word-level merges.
- Newer tokenizers were trained on far more multilingual text and given much
larger vocabularies, so many whole Hindi words now have their own token. That
is why o200k_base needed only 6 tokens where cl100k_base needed 24.
- Now the letter-counting failure, mechanically.
- The model receives the id for “berry” as a single integer. It receives no
information whatsoever about the five letters inside it, except what it
learned indirectly during training.
- Asking it to count the letter r in “strawberry” is asking it to recall a fact
about the internal composition of three token ids.
- It can often do this, because spelling appears in training data. It fails
when the tokens are unusual, or when the counting has to be exact.
- Reversing a word is worse, because it requires the letters in order, which
is even less directly available.
- Arithmetic fails for the same class of reason. When 17394205 arrives as
173, 942, 05, the place values that column-wise addition depends on are not
aligned with anything the model can see.
- This is why some model families deliberately force digits to be split one at
a time, and why models are much better at arithmetic when allowed to write
the working out step by step rather than answering in one shot.
TECHNICAL47.8.5 the engineer’s version#
- Measured compression ratios, cl100k_base, on real text samples:
| Text type |
Chars/token |
Tokens/word |
| Plain narrative prose |
3.78 |
1.25 |
| Ordinary email |
4.58 |
1.12 |
| Technical prose |
4.87 |
1.07 |
| Python code |
3.76 |
2.07 |
| Compact JSON |
2.76 |
3.22 |
- The common rules of thumb, roughly 4 characters per token and roughly 0.75
words per token in English, are conventions rather than standards, and the
table shows they hold for prose and fail badly for structured data. JSON at
2.76 characters per token is 45 per cent worse than the rule of thumb
suggests, which matters when you are budgeting a prompt full of API output.
- The unfairness is documented. Aleksandar Petrov, Emanuele La Malfa,
Philip Torr and Adel Bibi published Language Model Tokenizers Introduce
Unfairness Between Languages at NeurIPS 2023, measuring the same content
across many languages and finding token-count ratios up to 15 times between
the best and worst served languages, with over 4 times difference remaining
even for character and byte-level encoders.
- The three consequences they name are exactly the ones that hurt an Indian
developer: cost, because APIs bill per token; latency, because more tokens
means more forward passes; and effective context, because a fixed window
holds far less meaning in a penalized language.
- Work the third one out. A 128,000-token window at 4.8 characters per token
holds about 614,000 characters of English. At the cl100k_base Hindi rate of
about 0.93 characters per token, the same window holds about 119,000
characters of Hindi. The window is the same. The content it holds is a fifth.
- Mitigations that actually work: choose a model whose tokenizer was trained
with substantial data in your language, measure with the real tokenizer
rather than trusting a rule of thumb, and check whether the provider’s
multilingual pricing is per token, which it almost always is.
- On the letter-counting failure: the standard explanation is tokenization, and
it is largely right, but be precise. Character-level models also make
counting mistakes, and models can spell tokens correctly when asked
step by step. So tokenization makes the information indirect rather than
absent. That is an important distinction and it is often stated too strongly.
- On arithmetic: the tokenizer’s digit grouping is a genuine and measurable
cause. Research on numeric tokenization has shown that forcing single-digit
tokenization, or right-to-left digit grouping, measurably improves
multi-digit arithmetic. Some model families now tokenize digits individually
for this reason.
- Practical tools:
tiktoken.get_encoding("cl100k_base").encode(text) returns
the ids; enc.decode_single_token_bytes(i) shows exactly what each id is;
Hugging Face’s tokenizer.tokenize(text) shows the string pieces.
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
ids = enc.encode("The cat sat on the mat.")
print(len(ids), ids)
for i in ids:
print(i, enc.decode_single_token_bytes(i))
WORDS47.8.6 remember these#
- Compression ratio — how much text fits in one token — measured characters per
token or tokens per word for a given tokenizer and text type.
- Byte fallback — what happens when no merge covers the text — encoding
characters as their individual UTF-8 bytes, one token each.
- Tokenizer fertility — how many tokens a tokenizer spends per word — a
standard fairness measure; higher fertility means a worse-served language.
- Digit grouping — how numbers get cut up — the pre-tokenizer rule that splits
runs of digits, commonly into groups of up to three.
- UTF-8 — the standard way text becomes bytes — 1 byte for ASCII, 2 for Latin
accented and Greek, 3 for Devanagari and most CJK, 4 for emoji.
- Effective context — how much meaning your window actually holds — the window
size divided by your language’s tokens-per-meaning cost.
47.9 Token economics#
PLAIN47.9.1 in simple words#
- When you use a model through a company’s service, you pay per token.
- Not per question. Not per minute. Per token in and per token out.
- This is not arbitrary. Tokens are what the machine actually does work on.
- One token in costs the model one pass through the multiply-and-add machinery,
though those can be done for many tokens at once.
- One token out costs a full pass through the entire model, and those cannot be
done in parallel, because each output token depends on the one before.
- That asymmetry is why output tokens cost more than input tokens.
- Typically output is priced at four to six times input.
- So a long question with a short answer is cheap. A short question with a very
long answer is not.
- There is a second, sneakier cost. Long conversations resend everything.
- If you have a chat with 50 turns, every new question sends all 50 previous
turns as input, because the model has no memory between requests.
- So the input token count grows with the conversation and you pay for the
whole history every single time.
- That is the single most common reason a bill is bigger than expected.
PLAIN47.9.2 a picture in your head#
- Think of a translator you hire, who charges by the page.
- Pages they read cost you a little. Pages they write cost you more, because
writing is slower than reading.
- That is input versus output pricing.
- Now the important twist. This translator has no memory at all.
- Every time you ask a follow-up question, you must hand them the entire
previous conversation to read again, from the beginning.
- On question one you hand over one page. On question fifty you hand over
sixty pages, and pay to have all sixty read again.
- Some translators offer a discount: if you hand them the exact same bundle of
pages you handed them a few minutes ago, they charge much less for those.
- That discount is prompt caching, and it is real.
Where this comparison breaks: a human translator reading sixty pages takes an
hour. A model processes the input tokens of a long prompt in parallel, so
sending a long history costs money and some latency but nothing like
proportionally more wall-clock time than a short one.
PLAIN47.9.3 a worked example#
- Build a realistic support assistant and cost it.
- Every request contains: a system prompt of 800 tokens, four retrieved
document chunks totalling 4,000 tokens, 1,200 tokens of conversation history,
and the user’s 150-token question.
- Input total: 800 + 4,000 + 1,200 + 150 = 6,150 tokens.
- The answer averages 350 tokens of output.
- Prices per million tokens, checked on 13 August 2026, input then output:
| Model |
Input $/1M |
Output $/1M |
Per request |
| Claude Sonnet 5 |
2.00 |
10.00 |
$0.0158 |
| Claude Opus 5 |
5.00 |
25.00 |
$0.0395 |
| gpt-5.6-sol |
2.50 |
15.00 |
$0.0206 |
| Gemini 3.6 Flash |
1.50 |
7.50 |
$0.0119 |
- Work one out fully, for Claude Sonnet 5.
- Input: 6,150 / 1,000,000 x $2.00 = $0.0123.
- Output: 350 / 1,000,000 x $10.00 = $0.0035.
- Total per request: $0.0158.
- At 10,000 requests a day that is $158 a day, or $4,740 a month.
- On Claude Opus 5 the same traffic is $11,850 a month.
- On a small cheap model such as gpt-5.6-luna at $0.10 in and $0.60 out, the
same traffic is $247.50 a month.
- Notice that the input tokens dominate: $0.0123 of the $0.0158, or 78 per
cent. Cutting retrieved context is worth far more than shortening answers.
PLAIN47.9.4 what is really happening inside#
- Watch what happens when the context grows and everything else stays fixed.
| Input tokens |
Cost per request |
Per 10,000 |
| 6,150 |
$0.0158 |
$158 |
| 14,150 |
$0.0318 |
$318 |
| 38,150 |
$0.0798 |
$798 |
| 106,150 |
$0.2158 |
$2,158 |
| 406,150 |
$0.8158 |
$8,158 |
- Those are all the same question, the same answer, and the same model. The
only change is how much context was stuffed in.
- Going from 6,000 to 400,000 tokens of context multiplies the bill by 52.
- This is why “just put the whole document in the context window” is a
financial decision, not only a technical one.
- Now caching. The system prompt and the retrieved chunks are often identical
between requests within a short window.
- Providers offer a large discount on input tokens that are an exact prefix
match of a recent request. One current example prices cached input at one
tenth of fresh input.
- Rework the example on a provider charging $2.50 fresh, $0.25 cached and
$15.00 output. Without caching: $0.0206 per request.
- With the 4,800-token system-plus-documents prefix cached: 4,800 at $0.25 plus
1,350 at $2.50 plus 350 at $15.00 = $0.0098.
- That is a 52 per cent saving for changing nothing but the ordering and
stability of the prompt.
- The rule that follows: put the stable parts of your prompt first and the
variable parts last, because prefix caching only matches from the start.
TECHNICAL47.9.5 the engineer’s version#
- Why output costs more, precisely. Processing input is the prefill phase:
all input tokens go through the model in one batched forward pass, and the
hardware is compute-bound and highly efficient.
- Producing output is the decode phase: one token at a time, each requiring
a full pass over every weight, and at small batch sizes the hardware is
memory-bandwidth-bound and mostly idle in its arithmetic units.
- So a decoded token costs far more machine time than a prefilled one, and the
pricing reflects that. The typical ratio of 4x to 6x is a convention that has
held across providers, not a standard.
- Reasoning models add a third category. Tokens generated internally before the
visible answer are billed as output tokens even though you never see them,
and they can be several times the visible answer’s length. Budget for them
explicitly.
- Long-context surcharges are now common. As of August 2026, one provider
prices prompts over 200,000 tokens at double the input rate and 1.5 times the
output rate; another prices a long-context tier at double input and 1.5 times
output above a threshold. Check the specific model’s page, because this
changes often.
- Batch APIs typically halve the price in exchange for asynchronous delivery
within a window, commonly 24 hours. If your workload is not interactive, this
is the largest single discount available.
- Estimating before you build: tokenize a representative sample of your real
prompts with the actual tokenizer and take the mean. Do not estimate from
character counts if your content is JSON, code or a non-Latin script, because
the rule of thumb is wrong by up to a factor of five in those cases.
- A checklist that reduces token spend, in order of typical impact:
1. Retrieve less. Four chunks instead of twenty.
2. Cache the stable prefix; put variable text last.
3. Summarize old conversation turns instead of resending.
4. Use a small model for routing and a large one only when needed.
5. Cap max output tokens per request.
6. Batch anything that is not interactive.
7. Strip whitespace and boilerplate from injected documents.
- Self-hosting changes the shape of the cost, not always its size. You pay for
the machine by the hour whether it is busy or not, so self-hosting wins at
high steady utilization and loses badly at low or spiky utilization.
- Observability: every provider returns a usage object with input and output
token counts per response. Log it per request with a feature label, or you
will not be able to attribute the bill later.
WORDS47.9.6 remember these#
- Input token — a token you send — a token in the prompt, processed in the
prefill phase, billed at the lower rate.
- Output token — a token the model writes — a token produced in the decode
phase, billed at the higher rate, including hidden reasoning tokens.
- Prefill — reading the prompt — the parallel forward pass over all input
tokens, populating the key-value cache.
- Decode — writing the answer — the sequential per-token generation loop, one
full forward pass per token.
- Prompt caching — paying less for a repeated prefix — reusing a stored
key-value cache for an exact matching prompt prefix, at a discounted rate.
- Batch API — cheaper if you can wait — asynchronous processing at roughly half
price with a delivery window, commonly 24 hours.
- Context stuffing — putting everything in the prompt — a strategy whose cost
grows linearly in tokens and whose attention cost grows faster.
47.10 Embeddings: turning a token id into a vector#
PLAIN47.10.1 in simple words#
- After tokenization you have a list of integers. The model cannot use those
directly.
- An integer id like 8415 is just a name. Token 8416 is not “one more than”
token 8415 in any meaningful way.
- So each id has to be turned into something with actual structure.
- It is turned into a list of numbers. A long one: often 4,096 numbers.
- That list is called the token’s embedding.
- Where does the list come from? A big table.
- The table has one row per possible token, and one column per number in the
list.
- If there are 128,256 possible tokens and each embedding is 4,096 numbers,
the table is 128,256 rows by 4,096 columns.
- Turning an id into an embedding is just reading row number 8415 of the table.
- That is a lookup. No multiplication, no cleverness. Fetch the row.
- The numbers in the table were learned during training, so the table is made
of parameters, exactly like every other matrix in the model.
- And that is the entrance to the model. Text became ids. Ids became rows of
numbers. Now the layers can do arithmetic.
PLAIN47.10.2 a picture in your head#
- Think of a very long index card box, with one card per word.
- On each card, instead of a definition, there are 4,096 numbers.
- To look up a word you find its card and copy the numbers off it.
- The numbers mean nothing to you individually. Number 1,872 on the card for
“cat” is 0.031, and there is no sentence explaining why.
- But the cards are arranged in a very particular way in an imaginary space.
- Cards for similar things ended up near each other. The card for “cat” is
close to the card for “dog”, and far from the card for “invoice”.
- Nobody sorted them deliberately. They ended up there because training pushed
words used in similar ways toward similar numbers.
- So the meaning is in the arrangement, not in any single number on any card.
Where this comparison breaks: index cards sit on a table, which is two
dimensional. These cards sit in a space with 4,096 directions. Almost every
intuition you have about “near” and “far” from two dimensions gets weaker in
4,096, and some of them break completely. That gets its own honest treatment
below.
PLAIN47.10.3 a worked example#
- Take a real sentence and follow it in.
- The sentence: “The cat sat on the mat.”
- Tokenized with cl100k_base it becomes 7 tokens with these real ids:
| Id |
Piece |
| 791 |
The |
| 8415 |
(space)cat |
| 7731 |
(space)sat |
| 389 |
(space)on |
| 279 |
(space)the |
| 5634 |
(space)mat |
| 13 |
. |
- Note that “The” at the start and " the" in the middle are different tokens
with different ids, because one has a leading space and a capital letter.
- Now the lookup. The embedding matrix for an 8B-class model is
128,256 x 4,096.
- Row 791 is fetched. It is 4,096 numbers. Row 8415 is fetched. Also 4,096.
- Do that for all seven ids and stack them.
- The result is a block of numbers 7 rows by 4,096 columns.
- In the shape notation engineers use, the tensor went from [7] to [7, 4096].
- That is 28,672 numbers describing a six-word sentence.
- Size of the table itself: 128,256 x 4,096 = 525,336,576 parameters.
- At two bytes each, that one table is about 1.05 gigabytes, which is 6.5 per
cent of the whole 8B model.
PLAIN47.10.4 what is really happening inside#
- The lookup is mathematically a matrix multiplication, done the lazy way.
- The formal version: make a vector of 128,256 numbers that is all zeros except
a single 1 in position 8415. That is called a one-hot vector.
- Multiply that one-hot vector by the embedding matrix. The result is row 8415.
- Every implementation skips the multiplication and just indexes the row,
because multiplying by 128,255 zeros is a waste.
- That is why the operation is called an embedding lookup rather than an
embedding multiply, even though it is a linear layer.
- One thing is missing at this point: order.
- The lookup gives the same vector for “cat” whether it is the first word or
the fiftieth. Position information has to be added separately.
- Older models added a learned position vector to the token vector. Modern
models mostly rotate the query and key vectors inside attention instead,
which Chapter 48 covers.
- From here the stacked embeddings flow into layer 1, then layer 2, and so on.
- Critically, the vector at each position keeps being updated as it passes
through layers. It starts as “the meaning of this token in general” and
becomes “the meaning of this token in this sentence”.
- At the very end, the vector at the last position is multiplied by the output
matrix to produce one score per possible token, and the highest scores are
the model’s prediction of what comes next.
TECHNICAL47.10.5 the engineer’s version#
- The embedding matrix has shape [V, d] and is exactly V x d parameters.
| Model |
V |
d |
Embedding params |
| GPT-2 small |
50,257 |
768 |
38,597,376 |
| Llama 3.1 8B |
128,256 |
4,096 |
525,336,576 |
| Llama 3.1 70B |
128,256 |
8,192 |
1,050,673,152 |
| Llama 3.1 405B |
128,256 |
16,384 |
2,101,346,304 |
- In PyTorch this is
nn.Embedding(V, d), whose forward pass is
F.embedding, which is an index-select, not a matmul.
- The output projection,
lm_head, has shape [V, d] as well and produces
logits: one raw score per vocabulary entry. Softmax over logits gives the
next-token distribution.
- If embeddings are tied,
lm_head.weight is the same storage as
embed_tokens.weight. Tying was popularized by Press and Wolf in 2016 in
Using the Output Embedding to Improve Language Models, and independently by
Inan, Khosravi and Socher the same year.
- Common hidden sizes, which are also the embedding dimensions: 768 for GPT-2
small, 1024 and 1600 for larger GPT-2 variants, 2048 for many 1B-class
models, 4096 for 7B and 8B, 5120 for 13B, 8192 for 70B, 16384 for 405B.
- Now the honest note about dimensionality that the reader asked for.
- You cannot visualize 4,096 dimensions. Nobody can. Any picture you have seen
of word vectors as arrows on a page is a projection down to two dimensions
by a method such as PCA, t-SNE or UMAP, and those projections distort
distances substantially. t-SNE in particular preserves local neighbourhoods
and does not preserve global distances at all, so cluster sizes and the gaps
between clusters in a t-SNE plot are not meaningful.
- What does carry over from 2D and 3D: dot products, cosine of the angle
between vectors, Euclidean distance, addition and subtraction of vectors, and
the idea that direction can encode something.
- What does not carry over, and this is the part people get wrong:
- In high dimensions almost every pair of random vectors is nearly
orthogonal, so a cosine of 0 means “unrelated”, not “opposite”.
- Distances concentrate: the ratio between the nearest and farthest point in
a random set shrinks toward 1 as dimension grows, which is one face of the
curse of dimensionality.
- The volume of a high-dimensional ball is almost entirely near its surface,
so intuitions about “the middle of the cloud” are wrong.
- There is far more room than you expect. In d dimensions you can pack
exponentially many nearly-orthogonal directions, which is why a 4,096-wide
space can hold far more than 4,096 distinguishable concepts.
- That last point has a name in current research: superposition, the
hypothesis that models represent many more features than they have
dimensions, by using nearly-orthogonal directions and tolerating small
interference. Anthropic’s 2022 paper Toy Models of Superposition is the
standard reference. This is active research, not settled fact.
WORDS47.10.6 remember these#
- Embedding — a token turned into a list of numbers — a row of the embedding
matrix, a vector in R^d.
- Embedding matrix — the lookup table — a [V, d] parameter tensor mapping token
ids to vectors.
- One-hot vector — all zeros with a single 1 — the formal input to an embedding
layer, never materialized in practice.
- Logits — raw scores before they become probabilities — the [V] output of the
final projection, converted by softmax.
- Dimension — how many numbers are in one vector — d, the model’s hidden size,
typically 768 to 16,384.
- Curse of dimensionality — high-dimensional space behaves strangely — the
family of effects including distance concentration and near-orthogonality of
random vectors.
- Superposition — packing more ideas than there are directions — the hypothesis
that features are stored along nearly-orthogonal directions with tolerated
interference. Active research.
47.11 What embeddings capture#
PLAIN47.11.1 in simple words#
- If two words have similar embeddings, the model treats them similarly.
- So the natural question is: how do you measure “similar” for two lists of
numbers?
- Two standard answers, both simple.
- Distance: treat each list as a point, and measure how far apart the points
are. Closer means more similar.
- Angle: treat each list as an arrow from the origin, and measure the angle
between the arrows. Smaller angle means more similar.
- The angle version is the one almost everybody uses, and it is called
cosine similarity.
- It gives 1 for arrows pointing the same way, 0 for arrows at right angles,
and -1 for arrows pointing opposite ways.
- The angle version ignores how long the arrows are and looks only at
direction, which is usually what you want.
- There is also a famous claim that these vectors capture relationships, not
just similarity.
- The claim is that king minus man plus woman lands near queen.
- It is a real effect. It is also considerably weaker than the popular story,
and the popular demonstration was rigged in a specific way. Section 47.11.3
and 47.11.5 give the honest account.
PLAIN47.11.2 a picture in your head#
- Think of a map of a country, where each town is a dot.
- Two towns close together on the map are similar in location. That is
distance.
- Now draw an arrow from the capital to every town.
- Two towns roughly in the same direction from the capital, one near and one
far, have arrows at a small angle. That is cosine similarity.
- On a map, “50 km north-east” is a direction you can apply anywhere. Start at
any town, go 50 km north-east, arrive somewhere new.
- The analogy claim is that word space has directions like that. There is a
direction that means “make it female”, and applying it to king lands on
queen.
- The map picture makes clear why this could work and why it might not.
- On a real map, “50 km north-east” from a coastal town lands in the sea.
Where this comparison breaks: a map has two dimensions and every direction is
meaningful. Word space has thousands, and only a tiny number of directions
correspond to anything a human would name. Most directions in the space mean
nothing at all.
PLAIN47.11.3 a worked example#
- Cosine similarity, computed by hand on small vectors.
- Take four-number toy vectors, made up for illustration:
cat = [0.8, 0.1, 0.7, 0.2]
dog = [0.7, 0.2, 0.8, 0.1]
car = [0.1, 0.9, 0.0, 0.8]
- The formula: cosine = (a . b) / (|a| x |b|), where a . b is the sum of
pairwise products and |a| is the square root of the sum of squares.
- Step 1, the dot product of cat and dog:
0.8x0.7 + 0.1x0.2 + 0.7x0.8 + 0.2x0.1 = 0.56 + 0.02 + 0.56 + 0.02 = 1.16.
- Step 2, the length of cat: sqrt(0.64 + 0.01 + 0.49 + 0.04) = sqrt(1.18)
= 1.0863.
- Step 3, the length of dog: also 1.0863.
- Step 4: cosine = 1.16 / (1.0863 x 1.0863) = 1.16 / 1.18 = 0.9831.
- An angle of 10.6 degrees. Very similar.
- Now cat against car. Dot product = 0.08 + 0.09 + 0.00 + 0.16 = 0.33.
Lengths 1.0863 and 1.2083. Cosine = 0.33 / 1.3126 = 0.2514.
- An angle of 75.4 degrees. Nearly unrelated.
- Compare with straight-line distance: cat to dog is 0.20, cat to car is 1.41.
Both measures agree here, which is common but not guaranteed.
- Now the analogy, on a three-number toy set:
king = [0.92, 0.81, 0.12]
man = [0.88, 0.14, 0.09]
woman = [0.13, 0.16, 0.91]
queen = [0.15, 0.74, 0.86]
princess = [0.20, 0.60, 0.88]
- king - man + woman = [0.17, 0.83, 0.94].
- Cosine of that result against every word in the toy set:
| Word |
Cosine with result |
| queen |
0.9999 |
| princess |
0.9911 |
| woman |
0.8556 |
| king |
0.6041 |
| man |
0.3092 |
- Queen wins. The demonstration works. But look at the third row: woman, one
of the inputs, scores 0.8556, well above king and man.
- That third row is where the honest correction lives.
PLAIN47.11.4 what is really happening inside#
- The standard way to answer an analogy from vectors is called 3CosAdd.
- Compute b - a + c, then find the vocabulary word whose vector has the highest
cosine with the result.
- There is a rule buried in the standard implementation: the three input words
are excluded from the search.
- So when you compute king - man + woman, the code refuses to answer king, man
or woman, no matter how well they score.
- That matters a great deal, because very often the highest-scoring word is one
of the inputs.
- The result you are shown is therefore the best answer after the most likely
answers have been forbidden.
- That does not make the effect fake. Queen really does score highly, and
consistently, on many word pairs.
- It makes the effect smaller and less magical than the demonstrations imply.
- When researchers removed the exclusion rule and let the inputs compete,
accuracy collapsed. The numbers are in the technical block below.
- There is a second, separate honesty point about these vectors.
- In word2vec, each word has exactly one vector, forever. “Bank” gets one
vector that has to serve both the river bank and the money bank.
- Modern models do not work that way. The vector for a word changes depending
on the sentence it appears in.
TECHNICAL47.11.5 the engineer’s version#
- Word2vec was published by Tomas Mikolov and colleagues at Google in 2013, in
two papers: Efficient Estimation of Word Representations in Vector Space in
January 2013, and Distributed Representations of Words and Phrases and their
Compositionality at NeurIPS 2013. The analogy result appeared in a third,
Linguistic Regularities in Continuous Space Word Representations, by Mikolov,
Yih and Zweig at NAACL in June 2013.
- GloVe followed from Jeffrey Pennington, Richard Socher and Christopher
Manning at Stanford, EMNLP 2014, fitting vectors to global co-occurrence
counts rather than local context windows.
- The vector offset method, 3CosAdd, ranks candidate d by
cos(d, b - a + c). Omer Levy and Yoav Goldberg proposed 3CosMul in 2014 in
Linguistic Regularities in Sparse and Explicit Word Representations, which
multiplies similarities instead of adding offsets and performs better.
- Now the correction, which is well documented and not fringe.
- Tal Linzen’s 2016 RepEval paper Issues in Evaluating Semantic Spaces Using
Word Analogies showed that a large part of reported analogy accuracy is
explained by simple baselines: for many analogy items, just returning the
nearest neighbour of c, ignoring a and b entirely, scores well.
- Malvina Nissim, Rik van Noord and Rob van der Goot published Fair Is Better
than Sensational: Man Is to Doctor as Woman Is to Doctor in Computational
Linguistics, volume 46 issue 2, 2020. They showed the standard
implementations forbid returning any of the three input words, and that this
is not a detail.
- Their measured result: when they modified the algorithms to allow input words
as answers, 3CosAdd accuracy fell from 71 per cent to 21 per cent, and
3CosMul fell from 73 per cent to 45 per cent.
- Read that carefully. On the standard benchmark, roughly two thirds of
3CosAdd’s apparent success disappears when the exclusion is lifted. The
frequent correct-if-allowed answer is one of the inputs, usually b.
- So the accurate statement is: the offset direction carries real relational
information, and it is much weaker than the “king - man + woman = queen”
story suggests. Both halves of that sentence are true, and popular accounts
usually give only the first half.
- The same paper makes a separate methodological point about bias claims:
asking a system “man is to doctor as woman is to what” while forbidding it
from answering “doctor” guarantees a different answer, and then reporting
that different answer as evidence of bias is unsound. Bias in embeddings is
real and separately demonstrated; that particular demonstration is not
sound evidence of it.
- Static versus contextual embeddings. Word2vec and GloVe are static: one
vector per word type, fixed. Contextual embeddings give a different vector
per occurrence.
- ELMo, from Matthew Peters and colleagues at the Allen Institute, NAACL 2018,
Deep Contextualized Word Representations, was the first widely used
contextual model. BERT, from Jacob Devlin and colleagues at Google, posted
October 2018 and published at NAACL 2019, made it standard.
- In a transformer, the row fetched from the embedding matrix is static, and
it becomes contextual immediately: after the first attention layer the
vector at that position has mixed in information from the other tokens.
- So in the sentence “I sat on the river bank” and “I went to the bank for a
loan”, the input embedding for “bank” is byte-for-byte identical, and the
representation at layer 12 is quite different. That is the whole point of
the architecture.
| Property |
Static (word2vec) |
Contextual |
| Vectors per word |
One |
One per occurrence |
| Handles polysemy |
No |
Yes |
| Needs a model to compute |
No, table lookup |
Yes, forward pass |
| Typical year |
2013 to 2017 |
2018 onward |
WORDS47.11.6 remember these#
- Cosine similarity — how aligned two lists of numbers are — the dot product
divided by the product of the norms; 1 identical direction, 0 orthogonal.
- Euclidean distance — straight-line distance between two points — the square
root of the sum of squared differences.
- Dot product — pairwise multiply and add — the unnormalized similarity, which
grows with vector length as well as alignment.
- Vector offset — subtracting one vector from another — the b - a term in the
3CosAdd analogy method, treated as a relation direction.
- Static embedding — one fixed vector per word — word2vec or GloVe style, a
pure lookup with no sentence context.
- Contextual embedding — a different vector each time — the hidden state at a
position after attention has mixed in surrounding tokens.
- Polysemy — one word with several meanings — the phenomenon static embeddings
cannot represent and contextual embeddings can.
47.12 Embeddings as a tool in their own right#
PLAIN47.12.1 in simple words#
- So far embeddings have been a part inside a language model.
- They are also a useful product on their own, sold separately.
- The idea: instead of one vector per token, get one vector for a whole
sentence, paragraph or document.
- Now you can compare pieces of text by comparing numbers.
- That single capability unlocks five very common jobs.
- Semantic search: find documents that mean the same thing as a query, even
if they share no words with it.
- Clustering: group thousands of support tickets into themes automatically.
- Recommendation: suggest articles near the ones a reader liked.
- Deduplication: find near-identical items that keyword matching misses.
- Retrieval for a model: fetch the few relevant paragraphs from a large
collection and paste them into the prompt.
- That last one is the foundation of retrieval-augmented generation, which
Chapter 51 covers in full.
- The thing that makes all five work is the same: meaning became geometry, and
geometry is cheap to compute with.
PLAIN47.12.2 a picture in your head#
- Imagine a library where every book has been assigned a position in a very
large warehouse.
- Books are not shelved by author or title. They are shelved by what they are
about.
- Two books about monsoon farming end up on adjacent shelves even if one is in
English and one in Hindi and they share no words.
- To answer a question you do not read anything. You compute where the question
would sit in the warehouse, walk to that spot, and take the nearest ten books.
- That is semantic search, exactly.
- Keyword search is the old catalogue: it finds books whose title contains your
exact words, and misses everything phrased differently.
Where this comparison breaks: a warehouse has aisles you can walk. A vector
space has thousands of dimensions and no aisles, so finding the nearest items
needs a specialized index. Doing it by brute force means comparing against every
item, which is fine for ten thousand documents and far too slow for a hundred
million.
PLAIN47.12.3 a worked example#
- Suppose four short documents and one query, each already turned into a
four-number vector by an embedding model.
query = [0.75, 0.15, 0.60, 0.20]
doc A "how to feed a kitten" = [0.80, 0.10, 0.70, 0.20]
doc B "puppy training basics" = [0.70, 0.20, 0.80, 0.10]
doc C "car insurance renewal" = [0.10, 0.90, 0.00, 0.80]
doc D "truck loading limits" = [0.20, 0.80, 0.10, 0.90]
- Compute the cosine of the query against each.
- The query’s length is sqrt(0.5625 + 0.0225 + 0.36 + 0.04) = sqrt(0.985)
= 0.9925.
- Query and doc A: dot = 0.60 + 0.015 + 0.42 + 0.04 = 1.075. Doc A’s length is
1.0863. Cosine = 1.075 / (0.9925 x 1.0863) = 0.9971.
- Query and doc B: dot = 0.525 + 0.03 + 0.48 + 0.02 = 1.055. Cosine = 0.9786.
- Query and doc C: dot = 0.075 + 0.135 + 0.00 + 0.16 = 0.37. Cosine = 0.3085.
- Query and doc D: dot = 0.15 + 0.12 + 0.06 + 0.18 = 0.51. Cosine = 0.4196.
| Document |
Cosine |
Rank |
| A, kitten feeding |
0.9971 |
1 |
| B, puppy training |
0.9786 |
2 |
| D, truck loading |
0.4196 |
3 |
| C, car insurance |
0.3085 |
4 |
- The two animal documents rank above the two vehicle documents, and no word
from the query appears in any of them.
- In a real system the vectors have 384, 768, 1024 or 3072 numbers instead of
four, and there are millions of documents, but the computation per pair is
exactly this one.
PLAIN47.12.4 what is really happening inside#
- Where does a whole-sentence vector come from?
- Run the text through a model and you get one vector per token. You need one
vector for the whole thing.
- Three common ways to collapse them.
- Mean pooling: average all the token vectors. Simple, works well, still
the most common method.
- CLS pooling: use the vector at a special marker token placed at the
start, which was trained to summarize the sequence.
- Last-token pooling: in a decoder-only model, use the vector at the final
position, which has seen everything before it.
- But a raw language model’s vectors are not well suited to comparison
straight out of the box.
- So embedding models are trained specially, with a method called contrastive
training.
- You show the model many pairs. Some pairs mean the same thing, some do not.
- You push the matching pairs’ vectors together and the non-matching pairs’
vectors apart.
- After enough pairs, cosine similarity in that space genuinely tracks meaning
similarity, which it does not automatically do otherwise.
- Finally, vectors are usually normalized to length 1, which makes cosine
similarity and dot product the same computation, and makes the search
faster.
TECHNICAL47.12.5 the engineer’s version#
- Embedding models are separate, smaller models from the generative ones, and
they are usually encoder-style rather than decoder-style.
- Typical output dimensions and what they cost to store, per million documents:
| Dimensions |
Bytes at fp32 |
Per 1M docs |
| 384 |
1,536 |
1.5 GB |
| 768 |
3,072 |
3.1 GB |
| 1,024 |
4,096 |
4.1 GB |
| 3,072 |
12,288 |
12.3 GB |
- Sentence-BERT, by Nils Reimers and Iryna Gurevych, EMNLP 2019, is the paper
that made sentence embeddings practical, by fine-tuning BERT with a siamese
network so that cosine similarity between sentence vectors is meaningful.
Before it, comparing raw BERT sentence vectors performed worse than averaging
GloVe vectors.
- Contrastive objectives in common use: InfoNCE, multiple-negatives ranking
loss, and in-batch negatives, where every other item in the batch is treated
as a negative example for free.
- Matryoshka representation learning, from a 2022 paper by Aditya Kusupati and
colleagues, trains a model so that the first 256 or 512 numbers of a 3,072-
number vector are usable on their own. Several current embedding APIs expose
this as a dimension parameter, letting you trade accuracy for storage without
re-embedding.
- Nearest-neighbour search at scale uses an approximate index rather than brute
force. HNSW, Hierarchical Navigable Small World graphs, from Yury Malkov and
Dmitry Yashunin in 2016, is the most widely deployed. IVF with product
quantization, from the FAISS library released by Facebook AI Research in
2017, is the other common family.
- Approximate means approximate: these indexes return the true nearest
neighbours most of the time, not always, and the recall is a tunable
parameter traded against speed. That is a real correctness consideration and
is often glossed over.
- Vector databases and vector indexes in wide use as of 2026 include FAISS,
pgvector for PostgreSQL, Qdrant, Weaviate, Milvus, Chroma and the vector
features of Elasticsearch and OpenSearch.
- Retrieval-augmented generation, named in a 2020 paper by Patrick Lewis and
colleagues at Facebook AI Research, is embedding search plus prompt
construction: embed the query, retrieve the top k chunks by cosine, paste
them into the context, and let the generative model answer from them.
Chapter 51 covers the whole pipeline including chunking and reranking.
- Honest limitation: pure vector search is worse than keyword search at exact
matching, such as product codes, error numbers and names. Production systems
almost always combine both, which is called hybrid search, usually merged
with reciprocal rank fusion.
- A second honest limitation: an embedding has no notion of truth or recency.
Two documents that contradict each other on a fact will sit close together,
because they are about the same thing.
WORDS47.12.6 remember these#
- Embedding model — a model whose output is a vector, not text — usually an
encoder trained with a contrastive objective for similarity.
- Pooling — turning many token vectors into one — mean, CLS or last-token
aggregation over the sequence.
- Contrastive training — pull matches together, push non-matches apart — an
objective such as InfoNCE using in-batch negatives.
- Semantic search — search by meaning, not words — ranking documents by cosine
similarity between query and document embeddings.
- Approximate nearest neighbour — fast, nearly-correct nearest search — index
structures such as HNSW or IVF-PQ that trade recall for speed.
- Hybrid search — combining keyword and vector search — merging BM25 and vector
rankings, commonly with reciprocal rank fusion.
- Normalization — scaling every vector to length 1 — makes dot product equal
cosine similarity and speeds up search.
47.13 The context window#
PLAIN47.13.1 in simple words#
- The context window is the maximum number of tokens the model can be given
at once, including everything: system instructions, history and your question.
- It is measured in tokens, not words and not characters.
- It is a hard limit set by the model, not a preference.
- Why is there a limit at all? Because of how attention works.
- Attention lets every token look at every other token.
- With 100 tokens that is 10,000 pairs. With 1,000 tokens, a million pairs.
- Double the length and the number of pairs goes up four times, not two.
- So cost grows with the square of the length. Chapter 48 works through why.
- When you exceed the window, one of two things happens.
- Either the service refuses the request with an error, or something silently
gets cut off, usually the oldest part of the conversation.
- Silent cutting is the dangerous one, because the model then answers
confidently based on text it never received.
- And there is a further catch. A model that accepts a long input does not
necessarily use all of it well.
PLAIN47.13.2 a picture in your head#
- Think of a desk with a fixed area.
- You can spread out papers on it, and you can read anything on the desk
instantly.
- But when the desk is full and you add another page, one falls off the edge.
- Nobody tells you which one fell off, or that anything fell at all.
- A bigger desk helps. It also costs more, and it takes longer to scan.
- Now the awkward part. Even with a huge desk, if you spread out four hundred
pages, you will reliably notice the first few and the last few and be much
vaguer about the middle.
- That is not a memory limit. It is an attention limit, and models have a
measurable version of the same thing.
Where this comparison breaks: a desk keeps papers between work sessions. A
context window does not persist at all. When the request ends, everything on
the desk is swept away, and the next request starts empty. Anything that
appears to persist was resent by the application.
PLAIN47.13.3 a worked example#
- Work out what a window actually holds.
- At about 4 characters per token, a 128,000-token window holds roughly 512,000
characters of English.
- A typical printed page is about 2,000 characters, so that is about 256 pages.
- But you never get all of it. Reserve space for the answer, and for the system
prompt.
- Now the attention arithmetic, which is why longer costs so much more:
| Tokens |
Pairs (n squared) |
Relative to 512 |
| 512 |
262,144 |
1x |
| 2,048 |
4,194,304 |
16x |
| 8,192 |
67,108,864 |
256x |
| 32,768 |
1,073,741,824 |
4,096x |
| 131,072 |
17,179,869,184 |
65,536x |
| 1,048,576 |
1,099,511,627,776 |
4,194,304x |
- Going from 512 to 1,048,576 tokens is 2,048 times the length and about four
million times the attention pair count.
- Memory grows too, but linearly, through the key-value cache: 40 GiB for a
70B model at 128,000 tokens at 16-bit, as computed in section 47.4.
- So a million-token context is not a small engineering step from a
thousand-token one. It requires different algorithms, not just more memory.
PLAIN47.13.4 what is really happening inside#
- What happens when the input is too long? Four common strategies.
- Reject: return an error. Honest, and the only one that cannot silently
mislead.
- Truncate: cut the input to fit. Usually the oldest turns go first. Cheap,
and information is genuinely lost.
- Sliding window: keep a moving window of the most recent tokens and drop
what falls out of the back. Used in streaming settings.
- Summarize and compact: replace old turns with a shorter summary generated
by the model, keeping recent turns verbatim. This is what most long-running
assistants do.
- Each strategy loses something, and you should know which one your framework
is using, because they fail differently.
- Now the harder problem. Suppose the whole document does fit.
- The model still does not attend evenly across it.
- Researchers put a single fact somewhere in a long context and asked a
question that requires it, varying only the position.
- Accuracy was highest when the fact was at the very beginning or the very
end, and lowest when it was in the middle.
- The shape of the curve is a U. The finding is called “lost in the middle”.
- So placing your most important instruction in the middle of a long prompt is
a measurably bad idea.
TECHNICAL47.13.5 the engineer’s version#
- Context windows have grown by more than three orders of magnitude in seven
years:
| Model and year |
Context window |
| GPT-2, 2019 |
1,024 |
| GPT-3, 2020 |
2,048 |
| Llama 2, 2023 |
4,096 |
| Llama 3.1, 2024 |
131,072 |
- As of 13 August 2026, current commercial figures: Claude Opus 5, Claude
Sonnet 5 and Claude Fable 5 accept 1,000,000 input tokens with up to 128,000
output tokens; Claude Haiku 4.5 accepts 200,000. The GPT-5.6 family accepts
about 1.05 million input tokens, which is 2^20, with 128,000 output. These
figures change often; check the provider’s model page.
- The cost of attention is O(n^2) in time for the score matrix and O(n) in
memory for the key-value cache. FlashAttention, from Tri Dao and colleagues
in 2022, does not change the O(n^2) compute but avoids materializing the
n x n matrix in high-bandwidth memory, which removes the O(n^2) memory term
and is what made long contexts practical. Chapter 48 covers the mechanism.
- Position encoding is the second limit. RoPE, rotary position embedding from
Jianlin Su and colleagues in 2021, encodes position by rotating query and key
vectors. Extending a trained model beyond its training length needs the
rotation frequencies adjusted, by methods such as position interpolation or
the NTK-aware and YaRN scalings. A model advertised at 128,000 tokens is
often a model trained at 8,000 and extended.
- Lost in the middle: Nelson Liu, Kevin Lin, John Hewitt, Ashwin Paranjape,
Michele Bevilacqua, Fabio Petroni and Percy Liang, arXiv July 2023,
published in TACL 2024. Across multi-document question answering and
key-value retrieval, performance was highest when the relevant information
was at the start or end of the input and degraded significantly in the
middle, and extended-context models were not reliably better than their
shorter counterparts at using the extra length.
- RULER, from Cheng-Ping Hsieh and colleagues at NVIDIA, April 2024,
extended the needle-in-a-haystack test with multi-hop tracing and aggregation
tasks and evaluated 17 long-context models. Its headline finding: although
all claimed 32,000 tokens or more, only about half maintained satisfactory
performance at 32,000, despite near-perfect scores on the simple retrieval
test.
- So state the distinction plainly. Claimed context is the number the API
will accept without an error. Effective context is the length at which
the model still performs near its short-context accuracy. The second number
is smaller, is model-specific, and is rarely published by vendors.
- The needle-in-a-haystack test, popularized by Greg Kamradt in November 2023,
is a weak measure on its own: passing it shows the model can find one exact
verbatim string, which is much easier than reasoning over dispersed
information.
- Practical rules that follow: put critical instructions at the beginning and
repeat them at the end; retrieve fewer, better chunks rather than more;
measure your own task at your own lengths rather than trusting the advertised
number; and log token counts so you notice when truncation starts.
- Tools: every API returns input token counts in its usage object; Anthropic
and OpenAI both expose a token counting endpoint or library so you can check
before sending;
tiktoken and AutoTokenizer let you count offline.
WORDS47.13.6 remember these#
- Context window — the most tokens the model accepts at once — the maximum
input sequence length, a fixed architectural and training limit.
- Truncation — cutting input to make it fit — dropping tokens, usually oldest
first, often silently.
- Sliding window — a moving view of recent tokens — keeping the last n tokens
and discarding what falls out.
- Lost in the middle — the dip in accuracy for content in the centre — the
U-shaped position-versus-accuracy curve documented by Liu and colleagues.
- Effective context — how much of the window is actually usable — the length at
which task accuracy still holds up, usually shorter than the claimed window.
- RoPE — how position is encoded in modern models — rotary position embedding,
rotating query and key vectors by an angle proportional to position.
- Needle in a haystack — the simple long-context test — planting one verbatim
fact and asking for it back; necessary but far from sufficient.
47.14 Putting it together: one prompt from text to numbers#
PLAIN47.14.1 in simple words#
- Everything in this chapter meets in one short journey.
- You type a sentence. It is characters.
- The tokenizer chops it into pieces and looks each piece up, giving integers.
- Each integer picks one row out of the embedding table, giving a list of
numbers.
- Those lists are stacked into a block, one row per token.
- The block goes into layer 1, comes out changed, goes into layer 2, and so on.
- Each layer transforms the block using its own frozen parameters.
- At the end, the row belonging to the last token is turned into one score for
every possible next token.
- The scores are turned into probabilities, one is picked, and that becomes the
next token.
- Then the whole thing repeats with that token added on the end.
- Nothing in the model changed at any point. Only the numbers flowing through.
PLAIN47.14.2 a picture in your head#
- Picture a factory line with 32 stations.
- At the entrance, a machine cuts the incoming text into standard-sized parts
and stamps each with a part number.
- A stores clerk takes each part number and fetches the matching tray of 4,096
components.
- The trays travel down the line. Every station reworks all the trays a little,
using its own fixed jigs and tools.
- At the last station, the final tray is compared against a catalogue of
128,256 possible products, and the closest matches are scored.
- One is chosen and shipped. Then the whole line runs again with that product
added to the input.
Where this comparison breaks: on a factory line each station works on one item
at a time. In a transformer every station works on all positions at once, and
crucially each position can look at the others, which no factory station does.
PLAIN47.14.3 a worked example#
- The prompt: “The cat sat on the mat.” Model: an 8B-class model with hidden
size 4,096, 32 layers, vocabulary 128,256.
stage 0 raw text
"The cat sat on the mat." 23 characters
stage 1 tokenize (cl100k_base)
7 tokens
791, 8415, 7731, 389, 279, 5634, 13
"The" " cat" " sat" " on" " the" " mat" "."
shape: [7]
stage 2 embedding lookup
fetch rows 791, 8415, ... from a
[128256, 4096] table
shape: [7, 4096] = 28,672 numbers
stage 3 through layer 1 ... layer 32
shape stays [7, 4096] at every layer boundary
inside a layer it widens to [7, 14336] in the
feed-forward middle and comes back
stage 4 final normalization
shape: [7, 4096]
stage 5 take the last row only, position 6
shape: [4096]
stage 6 multiply by the output matrix [128256, 4096]
shape: [128256] one logit per token
stage 7 softmax -> probabilities, then sample
one token id comes out, say 13 for "."
stage 8 append and repeat from stage 2 with 8 tokens
- Real sizes at each stage, at 16-bit: stage 2 holds 28,672 numbers, which is
56 KiB. Stage 6 produces 128,256 numbers, which is 251 KiB.
- The key-value cache after this prompt holds 7 tokens x 128 KiB = 896 KiB.
- Every one of those numbers is an activation. Every number in the tables they
passed through is a parameter.
PLAIN47.14.4 what is really happening inside#
- Notice the two places where the vocabulary size appears.
- Once at the entrance, as a table you index into. Once at the exit, as a
table you multiply against.
- Those two are the same shape and are sometimes literally the same numbers.
- Notice that the shape [7, 4096] is stable through the whole stack.
- That stable shape is called the residual stream. Every layer reads from it
and writes back into it by adding.
- Notice that only the last row is used to predict the next token, during
generation. The other rows were computed anyway, and their keys and values
are cached so they do not need recomputing next time.
- Notice what the second pass costs. It processes one new token, not eight.
All previous work is in the cache.
- That is why the first token of a reply is slower than the rest: the first one
pays for the whole prompt, and each later one pays for a single token.
TECHNICAL47.14.5 the engineer’s version#
- The shapes with a batch dimension, which is how it really runs:
| Stage |
Tensor shape |
dtype |
| Token ids |
[B, S] |
int64 |
| After embedding |
[B, S, 4096] |
bf16 |
| FFN intermediate |
[B, S, 14336] |
bf16 |
| Logits |
[B, S, 128256] |
fp32 |
- Logits are usually computed in fp32 even in a bf16 model, because softmax
over 128,256 entries is numerically sensitive.
- During prefill, S is the full prompt length and all positions are computed.
During decode, S is 1 and only the new position is computed, with the cache
supplying the rest.
- Per-token generation cost in floating point operations is approximately
2 x N, where N is the parameter count, ignoring attention over the cache. For
an 8B model that is about 16 billion operations per token.
- Prompt processing cost is approximately 2 x N x S, so a 6,150-token prompt on
an 8B model is about 98 trillion operations, which is why prefill is
compute-bound and decode is bandwidth-bound.
- Memory traffic per decoded token at batch 1 is roughly the entire weight
file, because every weight is read once. At 4-bit that is about 4.5 GiB per
token for an 8B model, so a machine with 100 GB/s of memory bandwidth caps
out near 21 tokens per second regardless of how fast its arithmetic is.
- That single fact explains most local inference performance questions, and it
is why quantization speeds up generation as well as shrinking it.
- Inspecting the journey in practice:
tokenizer(text, return_tensors="pt")
gives stage 1; model.get_input_embeddings()(ids) gives stage 2;
model(ids, output_hidden_states=True).hidden_states gives every stage 3
boundary as a tuple of 33 tensors for a 32-layer model.
WORDS47.14.6 remember these#
- Residual stream — the stable block of numbers every layer reads and writes —
the [B, S, d] tensor threaded through the network by residual connections.
- Prefill — processing the prompt — one parallel forward pass over all input
positions, filling the key-value cache.
- Decode — producing one token — a forward pass with sequence length 1 reusing
the cache.
- Logit — a raw score for one possible next token — one entry of the [V] output
vector before softmax.
- Softmax — turning scores into probabilities — exponentiate and divide by the
sum, giving a distribution over the vocabulary.
- Time to first token — how long before the reply starts — the latency of
prefill, growing with prompt length.
- Memory bandwidth bound — limited by moving numbers, not by computing — the
regime single-stream decoding runs in, where tokens per second is set by
bytes read per token.
47.98 Common wrong ideas#
- Wrong: more parameters means a smarter model. Right: parameter count sets a
ceiling and predicts memory and speed very well, but Chinchilla at 70B beat
Megatron-Turing NLG at 530B, and Mistral 7B beat Llama 2 13B. Data quantity,
data quality, training compute and post-training all matter as much or more.
- Wrong: a token is a word. Right: a token is a subword piece. English averages
about 1.1 to 1.3 tokens per word, JSON averages over 3, and a Hindi sentence
can cost 24 tokens where the English equivalent costs 5.
- Wrong: the context window is the model’s memory, and a model that accepts
1,000,000 tokens uses all of them equally. Right: it is the input size limit
for one request, nothing persists between requests, any apparent memory is
your application resending the history, and accuracy dips for content in the
middle, with RULER finding only about half of models claiming 32,000 tokens
performed well at that length.
- Wrong: quantization makes a model dumber in proportion to the bits removed.
Right: the curve is very flat then steep. Going 16-bit to 8-bit cost 0.01
perplexity on a measured Llama-3.1-8B test; 16-bit to Q4_K_M cost 0.24; going
to Q3_K_S cost 1.64. The first two thirds of the saving is nearly free.
- Wrong: embeddings understand meaning. Right: they encode statistical
co-occurrence as position in a space. That is enough for similarity to be
useful and is not understanding. Two contradictory documents about the same
fact sit close together, because they are about the same thing.
- Wrong: hyperparameters are just small parameters. Right: hyperparameters are
chosen by a human before training and are never learned. Parameters are
learned by gradient descent and are never chosen.
- Wrong: a 7B model needs 14 GB to train. Right: 14 GB is inference at 16-bit.
Full training with Adam needs about 18 bytes per parameter for states alone,
which is 117 GiB for 7B, before activations.
- Wrong: king minus man plus woman equals queen proves embeddings capture
relationships cleanly. Right: it is a real but weak effect, and the standard
evaluation forbids returning the input words. With that exclusion removed,
3CosAdd accuracy fell from 71 per cent to 21 per cent.
- Wrong: the model cannot count letters because it is stupid. Right: it
receives “strawberry” as three integer ids and never sees individual
letters. It answers from indirect knowledge, which is why it succeeds
sometimes and fails unpredictably.
- Wrong: input and output tokens cost the same. Right: output is typically
priced four to six times higher, because generating is sequential and
bandwidth-bound while reading the prompt is parallel and compute-bound.
47.99 Chapter summary in 20 lines#
- A parameter is one learned number sitting in one position of one matrix,
set during training and frozen thereafter; “weight” and “parameter” are used
interchangeably, with biases being additive parameters rather than weights.
- A model’s parameter count is the sum of the sizes of all its matrices, and it
is computable by hand from five published shape numbers.
- Worked exactly, Llama 3.1 8B holds 8,030,261,248 parameters: 32 layers of
218,112,000 plus two 525,336,576 vocabulary tables plus a final norm.
- The billions live in the feed-forward blocks: 70 per cent for 8B, 80 per cent
for 70B, 81 per cent for 405B, with attention near 17 per cent throughout.
- Hyperparameters are pre-training human choices, activations are temporary
numbers created while running, context is the input text, tokens are the
pieces it is chopped into. Only parameters are in the model file.
- Memory equals parameter count times bytes per parameter: 7B is 26.1 GiB at
fp32, 13.0 at fp16, 6.5 at int8 and 3.3 at int4.
- Inference needs more than the weights: a key-value cache of 320 KiB per token
for a 70B model, plus activations and framework overhead.
- Training needs about 18 bytes per parameter for weights, master copy,
gradients and Adam’s two moments, so a 7B model needs about 117 GiB of state
before activations are counted.
- Quantization stores each weight in fewer bits using a shared scale and
optional zero point, per tensor, per channel or per group of 32 to 256.
- GPTQ, AWQ, NF4 and the GGUF k-quants are the common formats; Q4_K is exactly
4.5 bits per weight, and Q4_K_M averages about 4.8 because sensitive tensors
are kept at Q6_K.
- A 70B model at Q4_K_M needs about 39.7 GiB of weights and fits a 64 GiB
machine at a modest context; at fp16 it needs 131.4 GiB and does not.
- Capability does not scale linearly with parameters: Chinchilla in 2022
showed models were undertrained, recommended about 20 tokens per parameter,
and a 70B model beat a 530B one at equal compute.
- Text is tokenized into subwords because whole words give an unbounded
vocabulary and characters give sequences that are far too long.
- Byte-pair encoding repeatedly merges the most frequent adjacent pair; on a
four-word corpus, ten merges produce a 21-token vocabulary in which “newest”
is a single token and “est_” emerges as a suffix without anyone naming it.
- WordPiece merges by likelihood gain, Unigram prunes down from a large
candidate set, and SentencePiece is the framework that makes both work
without language-specific pre-tokenization.
- English runs about 4 characters or 1.1 to 1.3 tokens per word; JSON is far
worse; and a Hindi sentence costing 5 tokens in English cost 24 on the
GPT-4-era tokenizer and 6 on the later one, which is real, measurable cost
and context inequality.
- Models cannot reliably count letters, reverse words or do long arithmetic
because they never see letters or aligned digits, only token ids.
- An embedding turns a token id into a row of a [V, d] table; the numbers mean
nothing individually and everything collectively, and 4,096 dimensions
cannot be visualized, with distance concentration and near-orthogonality
breaking most 2D intuitions.
- Similarity is measured by cosine of the angle; the king minus man plus woman
result is real but much weaker than told, since removing the input-word
exclusion dropped 3CosAdd accuracy from 71 to 21 per cent.
- The context window is limited because attention cost grows with the square
of length; exceeding it truncates or errors; and accepting a long context is
not the same as using it well, as the lost-in-the-middle and RULER results
show.