Transformers from the ground up
Tokens and embeddings
A transformer never sees letters. It sees integer ids from a fixed vocabulary, mapped to vectors that move during training. Tokenisation is the first place a model can fail.
The model sees integers
Text arrives as a string and leaves the tokenizer as a list of integers. A tokenizer splits the string into pieces. A piece can be a whole word, a word fragment, a punctuation mark or a single byte. The tokenizer looks each piece up in a vocabulary, a fixed list of pieces set before training. The model never sees the original characters. It sees the ids, plus whatever position signal the model adds.
Modern tokenizers learn their pieces with byte-pair encoding. Start from characters. Count adjacent pairs in a large corpus. Merge the most frequent pair, and repeat until the vocabulary reaches its target size. A frequent word survives as one token. A rare word splits into the pieces that built it. So “unhappiness” can cost three tokens while “the” costs one.
A vocabulary is usually between 30,000 and 200,000 entries. The exact size is a trade. A larger vocabulary makes common text shorter. It also gives every token a rarer row to learn, and it makes the output softmax wider. A tokenizer trained on English prose will still spend several tokens on a chemical formula or a line of Chinese. Those patterns were not frequent enough to earn a merge.
An embedding is a lookup table
The first layer is a matrix with one row per vocabulary entry and one column per model dimension. An embedding is a lookup: token id 4,271 selects row 4,271. That row is the token’s starting vector. Nothing is computed at this step, which is why the layer is cheap. Its parameters are still learned. Gradients from every occurrence of the token push the same row.
Tokens used in similar contexts end up with vectors that point in similar directions. Cosine similarity is the usual measure. It compares the angle between two rows and ignores their length. “Cat” and “dog” are close. “Cat” and “the” are not. The geometry summarises how the words were used in the corpus. It is not a definition, and it carries the biases of that corpus.
The same two steps in a library
from transformers import AutoModel, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModel.from_pretrained("bert-base-uncased")
batch = tokenizer(["a cat sat", "on the mat"], return_tensors="pt", padding=True)
output = model(**batch)
print(batch["input_ids"].shape) # (2, tokens)
print(output.last_hidden_state.shape) # (2, tokens, 768)A tokenizer you can read
Run the greedy tokenizer on four words, then add a merged piece to the vocabulary and tokenise “cats” again.
Tokenisation leaks into behaviour
The vocabulary is fixed, so it decides what the model can see cheaply. Numbers are a familiar failure. A tokenizer trained mostly on prose may split “2024” into two or three pieces. Arithmetic on years is then harder than arithmetic on small integers. The same text in a language rare in the corpus often costs two or three times as many tokens as English.
That cost is not cosmetic. The context window is measured in tokens. So is the bill. Attention time grows with the square of the sequence length. A prompt that looks short in words can be long in ids. Counting words is a rough proxy at best. Counting tokens is the measurement that matters.
Special tokens are part of the same vocabulary and are easy to forget. A beginning-of-sequence marker, a padding token and a mask token all occupy ids. The model can read them and predict them. If a padding token is not masked out of the loss, the model learns to produce it. A generation that ends with it then looks like a bug in the sampler. The real bug is in the training data.
Log the token count next to the character count when you build a prompt. A sudden jump usually means a rare word, a number or a code block is being shredded into single characters.
From ids to vectors, and back
At the output end, the final hidden state for each position is multiplied by a matrix. That matrix maps the vector back to vocabulary size. The scores are logits, raw numbers before any normalisation. A softmax turns them into a distribution over the next token. Many models tie the output matrix to the input embedding matrix. The same rows are then read at the start and written at the end.
Tying saves parameters and gives the geometry a second job. A token’s input direction is also the direction the model must produce to predict it. The vector for a token is therefore shaped by two things. One is what it means when read. The other is what it must look like when chosen. That shared pressure is one reason the space is more structured than a random lookup table.
Vectors that can do arithmetic
Run the analogy, then add vectors for paris, france and italy and check whether the same subtraction and addition lands on the right city.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does a tokenizer hand to the model?
