Before a model can do anything with text, the text has to be cut into pieces, and each piece has to be mapped to an ID in a fixed vocabulary. Each piece then gets its own embedding vector. This first step is called tokenization, and its pieces are called tokens.
The obvious choice is to make every word a token. This reading explains why that choice runs into trouble, how an <UNK> token patches the problem, and how subword tokenization, in particular Byte-Pair Encoding (BPE), solves it more gracefully. BPE, or a close relative of it, is what nearly every modern language model uses.
<UNK> handles rare and unseen words, and what it loses · run the BPE learning algorithm by hand · tokenize a new word by applying learned merges in order · name the main BPE variants used in practice.1Words as tokens
The simplest tokenizer splits text on spaces and separates punctuation:
Every distinct token needs its own embedding vector, so the vocabulary size $|V|$ determines how many embeddings we have to store and learn. How big should $|V|$ be? If we keep every word that appears in a large corpus, the vocabulary explodes. Word frequencies have a very long tail: a few words (the, of, and) appear constantly, while most distinct strings appear only once or twice. That tail contains typos (teh), random strings (zcvahu), symbols (#$^&*), rare names, and creative spellings (sooooo).
Keeping all of them is costly. The embedding table has $|V|\times d$ parameters, and a model that predicts the next word must compute a score for every vocabulary entry at every step. Worse, the embeddings of words seen only once or twice are barely trained, so they are of little use anyway. And no matter how large the vocabulary is, new text will always contain words that weren't in it.
2The unknown token
The standard fix is to keep only the most frequent words, for example every word that appears at least $k$ times, and replace everything else with a special unknown token, <UNK>. <UNK> gets its own embedding, which is trained on all the replaced words together:
I like this movie sooooo much. → Ilikethismovie<UNK>much.
<UNK>.This keeps the vocabulary small and lets the model process any input. Try changing the frequency cutoff on a small corpus below:
<UNK>s. Words shown faded in the corpus fall below the cutoff and would be trained as <UNK>.But <UNK> throws information away. After tokenization, sooooo (very positive), &*# (noise), and Transformerify (a new verb) all look identical to the model. A human could guess what most of these mean, because words have internal structure:
- Elongated spellings: sooooooo, taaaasty.
- Shared stems with different endings: happy, happier, happiest; drive, driving, driven.
- Shared prefixes and suffixes: unlikely, unhappy, unhealthy; beautiful, trustful, grateful.
- New words built from familiar parts: Transformer + -ify.
Word-level tokenization can't use any of this structure. happiest and happier are as unrelated to the tokenizer as happiest and giraffe.
3Subword tokenization
Subword tokenization uses pieces of words as the basic units: happ + iest, driv + ing, grate + ful. Frequent words can still be single tokens, while rare words are split into frequent pieces. This gets the best of both worlds:
- The vocabulary stays small (tens of thousands of entries), and we choose its size.
- Unseen words can still be represented, by splitting them into known pieces, so
<UNK>becomes rare or disappears entirely. - Related words share pieces, so they share parameters: what the model learns about -est from newest transfers to lowest.
The remaining question is how to choose the pieces. We could hand-write a list of English prefixes and suffixes, but that wouldn't work for other languages, typos, code, or names. Instead, we let the data decide.
4Byte-Pair Encoding
Byte-Pair Encoding (BPE) was originally a data-compression algorithm (Gage, 1994): repeatedly replace the most common pair of adjacent bytes with a single new symbol. Sennrich, Haddow & Birch (2016) adapted it to build subword vocabularies. The idea is the same: if two symbols often appear next to each other, merge them into one.
Learning the merges
- Start by splitting every word in the training corpus into characters, and add a special end-of-word symbol
</w>to the end of each word. The initial vocabulary is the set of all characters, plus</w>. - Count every pair of adjacent symbols across the corpus (weighted by how often each word occurs), and find the most frequent pair $(x, y)$.
- Merge: add the new symbol $xy$ to the vocabulary, record the merge rule $x + y \Rightarrow xy$, and replace every occurrence of the pair in the corpus with $xy$.
- Repeat steps 2–3 until the vocabulary reaches the desired size.
The end-of-word symbol matters: it lets BPE tell a suffix from the middle of a word. est</w> (as in newest) is a different symbol from est (as in estimate). Step through the algorithm on the example from the slides:
e s, s t, and t </w>. The algorithm doesn't say which to pick; implementations just use a fixed rule (for example, the first pair encountered). Different tie-breaking can produce different merge orders, but the final vocabularies are usually very similar.Tokenizing new words
Once training is done, the output is the ordered list of merge rules. To tokenize a new word, split it into characters plus </w>, then apply the merges in the order they were learned, each to every place it matches. Characters that never appeared in training have no entry in the vocabulary and become <UNK>. Try it with the merges learned in Figure 4:
After the nine merges shown in the slides (step 9 in Figure 4), lowest becomes low + est</w>: a word never seen in training, built from two meaningful pieces. powest becomes <UNK> o w est</w>, because p never appeared in the training corpus. In practice, the training corpus is huge, so every ordinary character is in the base vocabulary, and only truly exotic characters would become <UNK>.
5BPE in practice
Choosing the vocabulary size
The number of merges is a hyperparameter. With zero merges, every word is a sequence of characters: the vocabulary is tiny, but sequences are long and each token carries little meaning. With very many merges, almost every word becomes a single token, and we are back to the problems of word-level tokenization. Real systems sit in between: GPT-2's tokenizer has 50,257 tokens, and BERT's has about 30,000. With a vocabulary of that size, common words are single tokens and rare words split into a few pieces.
Variants you will meet
- Byte-level BPE (GPT-2 and later GPT models): instead of characters, start from the 256 possible bytes of UTF-8 text. Since every string is a sequence of bytes, any input can be tokenized, in any language, including emoji and code, and
<UNK>is never needed. - WordPiece (BERT): builds the vocabulary by merging pairs like BPE, but chooses the pair that most increases the likelihood of the training data rather than the most frequent pair. Pieces that continue a word are marked with
##, as inplay ##ing. - SentencePiece (T5, LLaMA, and many multilingual models): a toolkit that treats the input as a raw stream of characters, including spaces, which it marks with
▁. This avoids language-specific pre-splitting into words, which matters for languages such as Chinese and Japanese that don't separate words with spaces. It supports both BPE and a related unigram method.
Why tokenization matters downstream
Tokenization decides what the model actually sees, and its quirks show up in surprising places. The same word can be split differently depending on capitalization or a leading space (Hello vs. ␣hello). Numbers are often split into arbitrary chunks, which makes arithmetic harder. And because tokenizers are usually trained mostly on English, text in other languages tends to need more tokens for the same content, which makes it slower and more expensive to process.
6Check your understanding
Exercise: BPE by hand
A corpus contains hug ×10, pug ×5, pun ×12, bun ×4, and hugs ×5. Using an end-of-word symbol </w>, find the first three merges. Break ties by picking the pair that appears first when reading the corpus in this order.
u g = 10 + 5 + 5 = 20; p u = 5 + 12 = 17; u n = n </w> = 16; h u = g </w> = 15; the rest are smaller. Merge u + g ⇒ ug.Merge 2. Now
u n = 16 and n </w> = 16 tie for the top (h ug and ug </w> are 15). u n appears first (in pun), so merge u + n ⇒ un.Merge 3.
un </w> = 12 + 4 = 16 is now the most frequent pair. Merge un + </w> ⇒ un</w>.You can check this with the “hug / pug / pun / bun” corpus in Figure 4.
Quick quiz
7Further reading
- Jurafsky & Martin, Speech and Language Processing (3rd ed. draft), Ch. 2, “Regular Expressions, Tokenization, Edit Distance,” section on subword tokenization (BPE).
- Gage (1994). A New Algorithm for Data Compression. The C Users Journal.
- Sennrich, Haddow & Birch (2016). Neural Machine Translation of Rare Words with Subword Units. ACL.
- Schuster & Nakajima (2012). Japanese and Korean Voice Search. ICASSP. (WordPiece.)
- Kudo & Richardson (2018). SentencePiece: A Simple and Language Independent Subword Tokenizer and Detokenizer for Neural Text Processing. EMNLP (Demo).
- Radford et al. (2019). Language Models are Unsupervised Multitask Learners. (GPT-2; byte-level BPE.)
- Hugging Face NLP Course, Byte-Pair Encoding tokenization.