The Core Thesis: Before neural networks and Transformers existed, how did computers predict the next word on your phone keyboard or score whether a sentence sounded like real English? They used N-Gram Language Models—statistical engines that count how often short sequences of words appear together in a massive text corpus. Understanding N-Grams gives you the exact mathematical foundation of modern Large Language Models, because GPT-4 and Llama-3 solve the exact same next-token probability equation[cite: 10]!
- Beginner Foundations: What is a Language Model?
At its core, a Language Model (LM) is any mathematical system that does two equivalent things:
Assigns a probability to an entire sequence of words so a speech recognizer or translator knows that "recognize speech" is much more likely than "wreck a nice beach"!
P("the cat sat") >> P("sat cat the")
Given the preceding words, computes the conditional probability of the upcoming word [cite: 10]—powering autocomplete and text generation[cite: 10]!
P("mat" | "the cat sat on the")
How are these two tasks connected? Through the Chain Rule of Probability! The probability of any full sentence is simply the product of each word's conditional probability given all the words that came before it:
Why Counting Full Histories Fails Immediately
Suppose we want to compute P(\text{"mat"} \mid \text{"Yesterday the fluffy ginger cat sat quietly on the"}) by counting how many times that exact -word history appeared on the internet. Because language is infinitely creative, almost any long sentence has appeared times in your dataset! We need a shortcut that looks only at the most recent few words.
- Beginner: What is an N-Gram and the Markov Assumption?
An N-Gram is simply a contiguous chunk of consecutive tokens (words) from a piece of text. Depending on the window size , we give them specific names:
Single isolated words ( words of context). Assumes every word is chosen independently based purely on how common it is.
2-word pairs. Predicts the next word by looking at only the immediately preceding word: .
3-word triplets. Predicts the next word by looking at the preceding words: .
To make counting possible, an N-Gram model makes the Markov Assumption: instead of conditioning on the entire paragraph history, we assume the probability of the next word depends only on the last words:
In a Trigram () language model, how many previous words of context does the model look at when predicting the next word ?
A) 2 previous words (because N - 1 = 3 - 1 = 2)▼
B) 3 previous words▼
- Intermediate: Maximum Likelihood Estimation (Counting & Dividing)
How do we "train" a Bigram () model on a text dataset? There is no gradient descent required! By Maximum Likelihood Estimation (MLE), the probability is simply the number of times the pair appeared together, divided by the total number of times appeared:
Step-by-Step Worked Example: Calculating Bigram Probabilities by Hand
Suppose our training corpus has sentences framed with start <s> and end </s> tokens:
-
<s> I love deep learning </s>
-
<s> I love machine learning </s>
-
<s> deep learning is fun </s>
- Intermediate: The Zero-Probability Trap, Smoothing & Perplexity
What happens at test time if a user types a completely valid phrase like "I love Python", where the bigram ("love", "Python") never appeared in our small training set?
Because , our MLE formula gives . Worse yet, because sentence probability multiplies all word probabilities together (), a single unseen N-gram makes the probability of the entire document collapse to ZERO (and )!
The simplest fix: add a small count (e.g., for Laplace smoothing, or ) to every possible bigram in the vocabulary of size , and add to the denominator so probabilities still sum to :
For large vocabularies, Linear Interpolation blends Trigram, Bigram, and Unigram probabilities together ( where ) so the model falls back to shorter contexts when a 3-gram is unseen!
How We Evaluate ANY Language Model: Perplexity ()
How do researchers compare an N-Gram model, Llama-3, and GPT-4 on a test dataset? We use Perplexity (), which is simply the exponential of the average Cross-Entropy (Negative Log-Likelihood) Loss over test tokens:
Intuitive Meaning of Perplexity (Lower is Better!)
Perplexity measures the effective branching factor—how "surprised" or confused the model is by the test text. If a language model has a Perplexity of , it means that on average, at each step, the model is as uncertain as if it had to choose uniformly among equally likely words. A perfect model that predicts every word with certainty () has the lowest possible Perplexity of !
You evaluate two language models on a validation dataset. Model A achieves an average Cross-Entropy Loss of (), while Model B achieves a Cross-Entropy Loss of (). Which model is better?
A) Model A (Lower Perplexity 7.39 means higher probability assigned to the true test words!)▼
B) Model B (because 54.60 is a larger number)▼
- Advanced: Why N-Grams Hit a Wall & How Neural LLMs Replaced Them
Why can't we just build a -gram count model to rival ChatGPT? Because classical N-Gram models suffer from three fatal scaling bottlenecks that only Neural Language Models (Embeddings + Transformers) could solve:
| Bottleneck | Classical N-Gram Count Model | Modern Neural LLM (Transformer) |
|---|---|---|
| 1. Curse of Dimensionality | Table size explodes as . For , a 4-gram table has cells! | Uses fixed-size weight matrices that scale linearly/quadratically with dimension , not . |
| 2. Zero Semantic Sharing | Seeing "the cat chased the mouse" teaches the model zero about "the dog pursued the rodent"! | Vector Embeddings place "cat" and "dog" nearby in , generalizing across synonyms automatically! |
| 3. Myopic Context Window | Blind to anything beyond words ago; forgets a character's name from the previous sentence. | Self-Attention attends directly across tokens of context! |
Where N-Grams Are Still Used in Modern AI Today!
Even in the age of GPT-4, N-Grams are used every day for: (1) Evaluation Metrics like BLEU (precision of grams in machine translation) and ROUGE-N (summarization overlap), (2) BM25 / TF-IDF Keyword Retrieval inside Hybrid RAG systems, and (3) N-Gram Speculative Decoding, where a fast N-gram lookup drafts candidate tokens from the prompt and the LLM verifies them in parallel!
A Trigram model is trained on a corpus containing "The surgeon walked into the operating room" times, but the phrase "The physician walked into the operating room" never appeared once. Why does a Neural Language Model assign a high probability to the second sentence while the unsmoothed Trigram model assigns ?
A) Neural models represent "surgeon" and "physician" as similar dense vectors; N-grams treat words as unrelated discrete symbols▼
B) Because Neural Language Models do not predict conditional probabilities▼
- Visual Explanation: From N-Gram Counts to Perplexity & Generation
Look at how a statistical N-Gram Language Model extracts sliding windows of tokens, builds a smoothed conditional probability table, evaluates test sentences via Perplexity, and generates new text autoregressively:
- Python Implementation: Smoothed Bigram Language Model & Perplexity Calculator
Here is a complete, runnable Python implementation of a Laplace-Smoothed Bigram Language Model that trains on a text corpus, computes exact Perplexity on an unseen test sentence, and generates text autoregressively:
import mathfrom collections import Counter # 1. Training Corpus Framed with <s> (BOS) and </s> (EOS)corpus = [ "<s> neural networks learn representations </s>", "<s> neural networks predict tokens </s>", "<s> language models predict tokens </s>"] unigramCounts = Counter()bigramCounts = Counter()vocab = set() for sentence in corpus: tokens = sentence.split() vocab.update(tokens) for i in range(len(tokens)): unigramCounts[tokens[i]] += 1 if i > 0: bigramCounts[(tokens[i - 1], tokens[i])] += 1 vocabSize = len(vocab) # 2. Add-k (Laplace) Smoothed Bigram Probability P(w_t | w_)def bigramProb(prevWord: str, targetWord: str, k: float = 0.1) -> float: num = bigramCounts[(prevWord, targetWord)] + k den = unigramCounts[prevWord] + (k * vocabSize) return num / den # 3. Compute Sentence Perplexity: exp( -1/T * sum(ln P(w_t | w_)) )def computePerplexity(sentence: str, k: float = 0.1) -> float: tokens = sentence.split() logProbSum = 0.0 numPredictions = len(tokens) - 1 for i in range(1, len(tokens)): prob = bigramProb(tokens[i - 1], tokens[i], k=k) logProbSum += math.log(prob) avgNegLogLikelihood = -logProbSum / numPredictions return math.exp(avgNegLogLikelihood) seenSentence = "<s> neural networks predict tokens </s>"unseenMix = "<s> language models learn representations </s>"print("P(networks | neural):", round(bigramProb("neural", "networks"), 4))print("Perplexity on Seen Sentence:", round(computePerplexity(seenSentence), 3))print("Perplexity on Unseen Mix:", round(computePerplexity(unseenMix), 3))Pro Tip (Why We Sum Log-Probabilities Instead of Multiplying Raw Probabilities):
Look at logProbSum += math.log(prob) inside computePerplexity()! If a test book has words and each word has probability , multiplying immediately underflows to 0.0 in 64-bit floating-point arithmetic. Summing in log-space and taking at the very end completely prevents numerical underflow!
Key Points
Common Mistakes
✕ Evaluating an unsmoothed N-gram model on unseen test data.
Without smoothing or backoff, a single unseen word pair yields , causing and an infinite Perplexity score! Always apply smoothing or interpolation.
✕ Multiplying raw probabilities P_1 * P_2 * ... * P_T instead of adding log-probabilities.
Multiplying more than a few dozen probabilities smaller than causes floating-point underflow to zero. Always compute in log space.
✕ Comparing Perplexity scores between two models that use different tokenizers/vocabularies.
Perplexity is a branching factor over a specific vocabulary . A model with a vocabulary will naturally have a lower per-token Perplexity than a model with a vocabulary! Only compare Perplexity directly when models share the same tokenizer (or normalize by character/byte count).
✕ Forgetting to add start <s> and end </s> boundary tokens before counting N-grams.
Without <s>, the model does not know which words are likely to start a sentence; without </s>, generated sequences have no way to know when a sentence is complete and probabilities over all finite-length strings do not sum to .
The Big Picture
Classical N-Gram Language Models (Count Tables)
Look Back Only 2–4 Words → Exact String Matching → Zero Synonym Sharing → Explodes at Large N
Neural Language Models / LLMs (Embeddings + Self-Attention)
Same Next-Token Equation P(w_t | context) → Continuous Vector Space → 100k+ Context Window!
The important conceptual shift is realizing that the goal of language modeling has never changed: both a 1990s Trigram model and GPT-4 are autoregressive engines trying to minimize Perplexity by predicting [cite: 10]. What changed is how we compute that probability—moving from brittle, discrete count tables to continuous Word Embeddings and Transformer Self-Attention.
Remember: N-Grams proved that simple next-word prediction could capture grammar and local fluency, and their core failure—treating words as isolated symbols with zero shared meaning—directly motivated the invention of Word Embeddings!