06 / 37 · Classical NLP
N-gram language models
A language model assigns a probability to a sequence; the chain rule and the Markov assumption turn that into counting short n-grams. The note derives perplexity from the sequence probability through cross-entropy, shows why unseen n-grams make it explode, and fixes that with smoothing.
On this page 11
The bag-of-words classifier answers “which topic”, not “what comes next”. To generate text, autocomplete a query, or score whether a sentence is plausible, we need a model of the sequence: a probability for any token sequence. This note builds the simplest such model from counts, introduces the metric used for every language model in the course, and runs into the problem that a count-based model cannot generalize.
Notation. A sequence of tokens is , written . means . counts occurrences of a token sequence in the training corpus. is the vocabulary.
From a joint probability to a product of conditionals#
Every joint distribution over a sequence factorizes by the chain rule of probability, with no assumption:
Each factor is the probability of the next token given everything before it. This is the form that every language model in the course takes, from this note to the GPT note: a language model is a next-token predictor, and predicting the next token is a classification problem with classes.
The factors cannot be estimated directly. conditions on a prefix that, for beyond a few tokens, has never occurred before in any corpus. The Markov assumption cuts the prefix to the last tokens:
The resulting model is an n-gram language model. For (bigram) each token depends on one predecessor; for (trigram) on two. The assumption is wrong (a pronoun can refer back fifty tokens) and useful (two tokens of context already fix most of the local grammar).
Baseline: estimate the conditionals by counting#
With a finite context, each conditional is a categorical distribution over given a specific prefix, and its maximum-likelihood estimate is a ratio of counts:
Two special tokens make the sequence boundaries explicit. The first tokens of a line have no full prefix, so the line is padded on the left with copies of a start symbol; an end symbol [EOS] is appended so that the model can learn to stop.
Worked example#
A toy corpus of three lines and a bigram model ():
the cat sat
the cat ran
the dog sat
Pad each line with one start symbol [S] and end with [EOS]. Bigram counts:
| prefix | next | count | |
|---|---|---|---|
[S] | the | 3 | 1 |
the | cat | 2 | 2/3 |
the | dog | 1 | 1/3 |
cat | sat | 1 | 1/2 |
cat | ran | 1 | 1/2 |
dog | sat | 1 | 1 |
sat | [EOS] | 2 | 1 |
ran | [EOS] | 1 | 1 |
The probability of the new line the dog ran is
dog ran never occurred, so the whole line gets probability zero. Hold on to this: it is the failure that the rest of the note is about.
The table is easier to read as a graph, which is also how the model generates:
A bigram model is a graph of transitions
Generation#
Because the model factorizes left to right, it generates the same way. Start from the padding, sample , append it, sample , and stop at [EOS]. The output at each step is fed back as input to the next: generation is autoregressive, and this loop is the same in every generative language model in the course. The alternative to sampling, taking the most probable token at each step, is one of the decoding strategies compared in the GPT note.
A trigram model trained on a corpus of short Russian jokes, with BPE tokens, produces lines that are locally grammatical and globally meaningless, such as
мужчина должен постро ##ить дом и вот я однажды мгнове ##нно удалил это сообщение , …
Every run of three consecutive tokens occurred somewhere in the corpus; the sentence as a whole did not and does not cohere. Two tokens of memory are enough for agreement between neighbours and for nothing else.
Perplexity: from probability to a number#
To compare two language models we need a score on held-out text. The sequence probability itself is unusable: it shrinks with length, so a long test set always has a tiny probability whatever the model. Take the logarithm, which turns the product into a sum, and divide by length, which gives a per-token average:
This is the cross-entropy of the test text under the model, in nats per token (bits if the logarithm is base 2). It is the same quantity as the classifier loss of the previous note, seen four ways:
- As a sum over classes. At position the “true distribution” is one-hot on the observed token : . The cross-entropy between and the model is . Every term but one vanishes.
- As an expectation. Averaging over positions is a Monte Carlo estimate of , the expected surprise of the model on text drawn from the true distribution.
- As negative log-likelihood. by the chain rule, so minimizing is maximum-likelihood estimation of the sequence.
- As entropy plus a divergence. If the true next-token distribution is , then . The entropy of language is fixed; the model can only reduce the KL term. The decomposition is derived in the Generative Models course.
Perplexity is the exponential of the cross-entropy:
The last form is the geometric mean of the inverse per-token probabilities, which is why perplexity reads as an effective branching factor: a model with perplexity 50 is, on average, as uncertain as if it chose uniformly among 50 tokens at every step. Two checks fix the scale. A model that assigns probability 1 to every token has and . A uniform model over tokens has and . Lower is better, and the base of the logarithm cancels as long as the same base is used for and .
On the toy corpus the line the dog ran has probability 0, so and the perplexity is infinite. On training data the opposite happens: on five lines used both to fit and to score, a unigram model has perplexity 81.7, a trigram model 1.14 and a 10-gram model 1.10. The long-context models have memorized the lines, and the number says nothing about new text.
Every perplexity measured on the joke corpus in this note is a mean over lines: the perplexity of each line is computed separately, and the values are averaged. That weights every line equally, where the formula above weights every token equally, so the values are not the corpus perplexity. A toy example shows the difference: a line of 2 tokens with per-token NLL 1 has perplexity , a line of 8 tokens with NLL 3 has . Their mean is 11.4, while the corpus perplexity, the exponential of the summed NLL over the summed token count, is . The corpus perplexity is the standard recipe and the one to use for new measurements; the means over lines are kept here because they are the measured results this note reports, comparable only within one table, where every model is scored the same way.
Unseen n-grams make perplexity explode#
Split the joke corpus 75/25, fit on the training part and score the held-out part with word tokens. The perplexities are on the order of for the unigram model and for the trigram model. The cause is the zero from the worked example: any test n-gram that never occurred in training has , and . Probabilities below are clipped to so that the sum stays finite, and the clipped terms dominate everything else. The more context the model uses, the more test n-grams are unseen, so trigrams score worse than unigrams. On this measurement the model with the most memory is the worst, and the measurement is the problem: it reports how many test n-grams were unseen, not how well the model predicts.
Smoothing#
Additive (Laplace) smoothing adds a pseudo-count to every token in every context:
The denominator grows by , so the mass given to unseen tokens in a context is , shared equally among them. With and a vocabulary the size of this corpus’s BPE vocabulary, 37,580 tokens, a context seen 10 times keeps only of its mass for the tokens actually observed. That is far too much smoothing, which is why add-one is the illustrative method rather than the practical one, but it removes the zeros:
| Tokens | |||
|---|---|---|---|
| Words, | 39,956 | 30,058 | 70,786 |
| BPE, | 2,696 | 3,860 | 15,598 |
Read the table within a row; the two rows are not comparable. A BPE text has more tokens than the same text in words, each easier to predict, so a lower per-token perplexity does not mean a better model, and per word the ranking can reverse (the tokenization note does the conversion). Within a row two things are visible. The perplexities are finite and in a range where differences mean something. And the trigram model is worse than the bigram model: with more context, each prefix is seen fewer times, the counts are small, and add-one smoothing flattens the distribution towards uniform. The best is a trade-off between context and data sparsity, and under a crude smoother it is small.
Better smoothers fix the uniform-sharing flaw. Interpolation mixes the trigram, bigram and unigram estimates with learned weights, so a rare trigram context falls back on its bigram statistics rather than on uniform. Kneser–Ney smoothing (Kneser and Ney, 1995; Chen and Goodman, 1999) subtracts a fixed discount from every seen count and redistributes it in proportion to how many distinct contexts a token appears in, which is the state of the art for count-based models. A good perplexity for a modern neural model on English web text is in the tens; for the small joke corpus here, anything below a few hundred would be a reasonable target that add-one smoothing does not reach.
What changed#
| Bag-of-words classifier | N-gram language model | |
|---|---|---|
| Question | Which label? | What comes next? |
| Statistic | Token counts per document | Counts of -token sequences |
| Output | One distribution over labels | One distribution over per position |
| Order | Ignored | Last tokens |
| Can generate | No | Yes, autoregressively |
| Metric | Accuracy, | Perplexity |
Limitations#
- Sparsity grows with . The number of possible n-grams is . Most are never seen, so the model cannot use more than a few tokens of context however much data it has.
- No generalization across tokens.
the cat satandthe dog satare unrelated events; seeing the first does not raise the probability of the second. Every token is an atom, as in the bag of words. This is the limitation that the next note addresses. - Storage. The model is a table of counts for every observed prefix. It grows with the corpus and is awkward to compress.
- Perplexity is intrinsic. A lower perplexity does not guarantee better generations or a better downstream system; see the evaluation note. It is also not comparable across different tokenizers, because the per-token average changes when the tokens change.
What you should remember#
- Chain rule: . A language model is a next-token classifier over .
- Markov assumption: condition on the last tokens; estimate by count ratios; pad with start and end symbols.
- Cross-entropy is the per-token negative log-likelihood; perplexity is , an effective branching factor; uniform over gives .
- An unseen n-gram gives and infinite perplexity. Smoothing reserves mass for the unseen; add- is the simplest and over-smooths large vocabularies.
- Generation is autoregressive: sample, append, repeat.
Notebook#
week03_lm.ipynb. Cell numbers count every cell, markdown included, from 1. Quoted numbers come from the run saved with the course; cells changed since then for current library versions have no saved output.
- Cells 4–5 load a corpus of short Russian jokes, one per paragraph; cells 6–17 build the BPE tokenizer described in the tokenization note.
- Cells 18–28 state the chain rule and the Markov assumption, implement
count_ngrams(left-padding with copies of[UNK], which replaces the start symbol and counts as an ordinary token, and appending[EOS]) and theNGramLanguageModelclass, which turns counts into conditional probabilities exactly as in the formula above. Cells 22–25 show the trigram counts on five lines. - Cells 30–33 fit a trigram model with BPE tokens on the whole corpus and sample a joke token by token with
np.random.choice; the generated line quoted above is the saved output of cell 33. - Cells 34–36 implement perplexity with a
min_probclip of , as the mean of per-line perplexities, and report 81.663, 1.142 and 1.103 for on the five training lines. - Cell 38 splits 75/25 and reports the held-out word-token perplexities of order to for .
- Cells 39–43 implement
LaplaceLanguageModeland report the smoothed held-out perplexities in the table above, first with word tokens, then with BPE tokens. In the first version the class’sget_tokens_and_probscalled a method that does not exist in the parent class, so sampling from the smoothed model failed (scoring, which usesget_token_prob, was unaffected); the current notebook calls the parent’sget_tokens_and_probs, and cell 41 checks that the smoothed probabilities over the vocabulary sum to 1 for . - Cells 44–55 start a recurrent language model on the same tokens; the RNN note picks it up.
Next#
The n-gram model treats every token as an atom with no relation to any other. It cannot transfer what it learned about cat sat to dog sat, and with a vocabulary of tens of thousands of atoms, most short sequences are never seen at all. The next note replaces the atom with a vector, so that similar tokens have similar vectors and whatever the model learns for one of them carries over to its neighbours.
References#
- D. Jurafsky, J. H. Martin. Speech and Language Processing, 3rd ed. draft. Chapter 3 (n-gram language models, perplexity, smoothing).
- R. Kneser, H. Ney. Improved backing-off for M-gram language modeling. ICASSP, 1995.
- S. F. Chen, J. Goodman. An empirical study of smoothing techniques for language modeling. Computer Speech & Language, 13(4), 1999. The comparison that established modified Kneser–Ney.
- C. E. Shannon. Prediction and Entropy of Printed English. Bell System Technical Journal, 30(1), 1951. Entropy per character of English and the n-gram approximations.
- T. M. Cover, J. A. Thomas. Elements of Information Theory, 2nd ed. Wiley, 2006. §2.3 (relative entropy) for the entropy-plus-KL decomposition.