← learning.n0tls.com
§ a field guide, part three

Inside the transformer

You've now run all-MiniLM-L6-v2 on your own GPU, watched it learn from your corpus, and seen it win and lose against bigger, pretrained encoders. This is what's actually happening inside it — from raw text to the 384 numbers it hands back — using MiniLM's own real shape as the running example throughout.

01 text isn't the input — tokens are

A transformer never sees the string "reset workspace config". Before anything else, a tokenizer — a separate, much simpler piece of software with a fixed vocabulary (30,522 entries for MiniLM's, inherited from BERT) — chops the string into subword pieces and looks each one up as an integer.

02 integers become vectors

Each token id indexes a row in an embedding table — a 30,522 × 384 matrix of learned numbers, one 384-dimensional row per vocabulary entry. Because a transformer has no built-in sense of word order (unlike an RNN, which reads one token at a time), a second vector — the positional embedding, one per position 0, 1, 2, … — is added on top, so "config workspace" and "workspace config" don't look identical to the model.

Every token in the input ends up as one such 384-number vector. A 6-word query becomes a 6×384 matrix — this is what actually enters the first transformer block.

03 self-attention: tokens looking at each other

This is the mechanism that made transformers replace RNNs: every token's vector gets updated by looking at every other token in the sequence at once, weighted by how relevant each one is. Each token produces three projections of itself — a query (what am I looking for), a key (what do I offer), and a value (what do I actually contribute) — all three are just learned matrix multiplications of the same input vector.

Attention(Q, K, V) = softmax( QKT / √d ) V

Read it as: score every token's key against my query (QKT), scale it down so the softmax doesn't saturate (/√d), turn the scores into weights that sum to 1 (softmax), then take a weighted average of everyone's value vectors. "Reset" ends up attending heavily to "config" and "workspace" — the softmax literally learns that those tokens matter for disambiguating what "reset" means here.

04 heads, blocks, and depth

Why 12 heads instead of 1

A single attention pass can only learn one notion of "relevant." Running 12 of them in parallel — each with its own learned Q/K/V projections, on a 32-dimensional slice of the 384 — lets different heads specialize: one might track subject-verb agreement, another might link a pronoun back to its noun, another might just look at adjacent tokens. Their 12 outputs are concatenated back to 384 dimensions and passed through one more learned projection.

The full block, and stacking six of them

Multi-head attention is one half of a transformer block. The other half is a small feedforward network (384 → 1536 → 384, with a nonlinearity in between) applied to every token independently — attention mixes information across tokens, the feedforward layer processes each token's mixed result on its own. Both halves get a residual connection (add the input back to the output) and a layer normalization, which is what makes it possible to stack these blocks deeply without training collapsing.

05 from N vectors to the one you actually use

After 6 blocks, a 6-token query is still 6 separate 384-dim vectors — one per token, now richly contextualized. But the bi-encoder from the first field guide needs exactly one vector per query and one per document to take a dot product. Pooling is the step that collapses the many into the one, and it's the one sentence-transformers-specific idea in this whole page — a base BERT model doesn't do this on its own.

06 why MiniLM isn't Claude

Everything above — tokens, embeddings, attention, blocks, pooling — is shared machinery. The one structural difference between an embedding model like MiniLM and a chat model like Claude or GPT is which tokens are allowed to attend to which.

07 what each one was actually trained to do
1. encoder pretraining

Masked language modeling. BERT (MiniLM's teacher) saw sentences with 15% of tokens replaced by [MASK] and learned to predict the missing word from both directions of context. This is where it learned English — grammar, word sense, world facts — from ~3.3 billion words of BooksCorpus + Wikipedia.

2. distillation

MiniLM learned to imitate BERT. A small 6-layer model was trained to match a large 12-layer BERT's attention patterns and outputs — most of the teacher's knowledge, a fraction of the size, without needing the full pretraining run again.

3. sentence-pair pretraining

Contrastive training on ~1 billion sentence pairs (Reddit comments, Q&A pairs, captions) — mined from the open web, not written for this purpose. This is what turned a language model into an embedding model: the same InfoNCE loss from field guide §05, just at a scale of a billion pairs instead of 1,372.

4. your fine-tuning

The same loss again, on 1,372 pairs from one corpus. Not a new skill — a small nudge to an already-formed 384-dimensional space, aiming it at your specific vocabulary and documents.

08 why "from scratch on this corpus" would struggle

Steps 1–3 above are where a model actually learns language and general-purpose meaning. Step 4 — the only one you've run — assumes all of that already exists and just steers it. Training a model from nothing directly on the deno-docs corpus means skipping straight to step 4 with no steps 1–3 underneath it: there's no pretrained space to steer, because nothing has taught the model what a sentence is yet.

~3.3B
words BERT pretrained on
(BooksCorpus + Wikipedia)
vs.
~350K
words in the entire
457-doc deno-docs corpus

That's roughly a 10,000× gap. It isn't just "less data, worse model" — below a certain scale, a transformer trained from random initial weights doesn't reliably learn grammar, word relationships, or general semantics at all; it just memorizes surface patterns in the 350K words it saw. That's why every field-guide result so far started from a pretrained checkpoint and only fine-tuned: the pretraining is what makes the space worth steering, and no static 2,000-document corpus is anywhere near enough text to grow one from nothing.

Check your understanding— optional, 5 questions

1. Why does a transformer operate on tokens instead of raw characters or whole words directly?

2. What does self-attention let a token's representation do that a plain word embedding alone can't?

3. Why do transformers use multiple attention heads instead of just one?

4. What does pooling do, and why does retrieval need it?

5. Why would training a transformer from scratch on this project's ~350K-word corpus almost certainly fail to learn general language understanding?