Part of Language AI Handbook
Explains how Hidden Markov Models use transition and emission probabilities to solve sequence labeling tasks like POS tagging, with Python implementation.
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Hidden Markov Models
Sequence labeling tasks like POS tagging require more than just looking up each word in a dictionary. The word "bank" could be a noun (a financial institution) or a verb (to bank on something). The word "flies" could be a noun (the insects) or a verb (the action of flying). Context determines the correct label, and that context often spans multiple words in a sequence.
Hidden Markov Models (HMMs) provide a principled probabilistic framework for sequence labeling. An HMM models two interacting processes: a hidden sequence of states (the tags you want to predict) and an observable sequence of outputs (the words you can see). The model learns statistical patterns about how states transition to each other and how each state generates observations. Given a new sentence, the HMM uses these learned patterns to infer the most likely hidden sequence.
Think of an HMM as a kind of hidden storyteller. Imagine a narrator who moves secretly through a sequence of rooms: the tags: and calls out a word from each room. You can hear the words, but you cannot see which room the narrator is in. Your job is to figure out the narrator's path through the rooms based solely on the words you hear. The HMM formalizes exactly this: it provides probabilities that describe which rooms connect to which, and which words get called out from each room. Given a sequence of heard words, the model can then work backwards to estimate the most probable path.
The main insight behind HMMs is that language is not a bag of independent tokens. When you see the word "the," you know with high confidence that a noun phrase is coming. When you see a verb, you know it probably follows a noun phrase. These regularities exist at two levels simultaneously: the syntactic level (how tags relate to one another) and the lexical level (which words are associated with which tags). HMMs capture both levels in a single probabilistic model, and the interaction between them is what makes disambiguation possible.
This chapter covers HMM components and their probabilistic foundations. You'll learn how to estimate HMM parameters from annotated data, understand the key assumptions that make HMMs tractable, and implement a working POS tagger. We'll also examine where HMMs succeed and where they fall short, setting up the motivation for more advanced models like Conditional Random Fields. By the end, you will have a working intuition for why this 1960s signal-processing technique became the backbone of practical NLP systems for nearly two decades.
Hidden Markov Models were originally developed for speech recognition in the late 1960s and early 1970s by Leonard Baum and colleagues at the Institute for Defense Analyses. The classic Baum-Welch algorithm for training HMMs dates to 1972. Throughout the 1980s, IBM researchers adapted HMMs to machine translation and speech, and by the early 1990s the technique had migrated into text-based NLP. Eric Brill's influential 1992 dissertation on transformation-based learning and the subsequent adoption of HMMs in systems like the Xerox tagger established HMM-based POS tagging as the standard approach for most of the decade. The Penn Treebank, released in 1993, provided the labeled data needed to train high-quality HMM taggers, and the combination of clean annotated corpora and HMM inference algorithms pushed tagging accuracy above 95% for the first time. HMMs were eventually displaced by Maximum Entropy Markov Models and Conditional Random Fields in the early 2000s, which themselves gave way to neural approaches, but the conceptual framework they established, that sequence labeling is a structured prediction problem over hidden state sequences, remains central to NLP today.
The HMM Framework
An HMM defines a joint probability distribution over two sequences: a hidden state sequence and an observable output sequence. In POS tagging, the hidden states are the part-of-speech tags, and the observations are the words. The hidden states are "hidden" in a literal sense: you never observe them directly. You see only the output words, and everything about the underlying grammar must be inferred from those observations.
A Hidden Markov Model is a probabilistic sequence model with two components: an unobservable (hidden) state sequence that evolves according to Markov dynamics, and an observable output sequence where each output depends only on the current hidden state. The "hidden" refers to the fact that you cannot directly observe the states, only infer them from the observations.
The model has five components:
- States (): A finite set of hidden states. For POS tagging, these are the tags like NN, VB, DT, JJ. The state set defines the vocabulary of labels the model can assign.
- Observations (): A finite vocabulary of observable symbols. These are the words in your corpus. Every word you will ever encounter at training or test time must belong to this set (with unknown words handled separately by smoothing).
- Initial probabilities (): The probability of starting in each state. For sentences, this captures which tags typically begin sentences. English sentences far more often begin with a determiner or noun than with a verb or preposition, and encodes exactly that regularity.
- Transition probabilities (): The probability of moving from one state to another. This captures tag-to-tag patterns like "determiners are often followed by nouns." The full transition matrix encodes a compressed statistical description of English syntax.
- Emission probabilities (): The probability of generating each observation from each state. This captures which words are associated with which tags. The word "the" has very high emission probability from DET and near-zero from VERB; "run" has meaningful probability from both NOUN and VERB. This reflects lexical ambiguity.
The beauty of this decomposition is that each component captures a different kind of linguistic knowledge. The transition matrix captures syntax: how grammatical categories sequence. The emission matrix captures lexical associations: which words belong to which categories. The initial distribution captures positional tendencies: how sentences typically begin. Together they form a compact and learnable representation of a language's statistical structure.
Let's define these components mathematically. For a state sequence and observation sequence , we define three probability distributions.
The initial probability captures how likely each state is to appear at the start of a sequence:
where is the probability that the first state in the sequence is state .
The transition probability captures how states follow one another:
where:
- : the probability of transitioning from state to state
- : the state at position in the sequence
- : the state at the previous position
The emission probability captures how each state generates observations:
where:
- : the probability of observing symbol when in state
- : the observation at position
- : the hidden state at position
These three distributions fully characterize an HMM's behavior. The initial probabilities determine how sequences start, transitions determine how states evolve, and emissions determine what we observe. Notice that the parameters are not hand-crafted: they are estimated automatically from annotated data. This is what makes HMMs powerful. You do not need a linguist to write grammar rules. You need labeled sentences, and the model learns the statistical regularities on its own.
To make these concepts concrete, let's build a toy HMM for POS tagging. We'll use just three states (determiner, noun, verb) and five words, which is enough to see the key patterns while keeping the numbers tractable.
# A toy HMM for POS tagging with 3 states and 5 words
states = ["DT", "NN", "VB"] # Determiner, Noun, Verb
vocab = ["the", "dog", "cat", "runs", "sleeps"]
# Initial probabilities: sentences often start with determiners
pi = {"DT": 0.6, "NN": 0.3, "VB": 0.1}
# Transition probabilities
# P(next_state | current_state)
A = {
"DT": {"DT": 0.1, "NN": 0.8, "VB": 0.1}, # DT usually followed by NN
"NN": {"DT": 0.1, "NN": 0.3, "VB": 0.6}, # NN often followed by VB
"VB": {"DT": 0.3, "NN": 0.4, "VB": 0.3}, # VB can lead to various
}
# Emission probabilities
# P(word | state)
B = {
"DT": {"the": 0.9, "dog": 0.02, "cat": 0.02, "runs": 0.03, "sleeps": 0.03},
"NN": {"the": 0.01, "dog": 0.45, "cat": 0.45, "runs": 0.05, "sleeps": 0.04},
"VB": {"the": 0.01, "dog": 0.02, "cat": 0.02, "runs": 0.50, "sleeps": 0.45},
}HMM Components for POS Tagging
==================================================
States (Tags): ['DT', 'NN', 'VB']
Vocabulary: ['the', 'dog', 'cat', 'runs', 'sleeps']
Initial Probabilities π:
P(start with DT) = 0.60
P(start with NN) = 0.30
P(start with VB) = 0.10
Sample Transition Probabilities A:
P(NN | DT) = 0.8 (determiners often precede nouns)
P(VB | NN) = 0.6 (nouns often precede verbs)
Sample Emission Probabilities B:
P('the' | DT) = 0.9 (determiners emit 'the' frequently)
P('dog' | NN) = 0.45 (nouns emit 'dog')
P('runs' | VB) = 0.5 (verbs emit 'runs')The transition matrix encodes grammatical patterns. Determiners (DT) are followed by nouns (NN) with probability 0.8, capturing the common English pattern "the dog" or "a cat." The emission matrix encodes lexical associations. The word "the" is emitted by DT with probability 0.9, while "runs" is emitted by VB with probability 0.5.
In practice, these probabilities need not be set by hand. The numbers above are illustrative, but when you train on a large corpus the same patterns emerge automatically from counting. The model "discovers" that determiners precede nouns because the data contains thousands of examples of exactly that pattern. This is maximum likelihood estimation at its simplest: the best description of the data is the one that assigns the highest probability to the observed events.
The Two HMM Assumptions
HMMs make two key independence assumptions that simplify computation. These assumptions trade off modeling power for tractability. Without them, exact inference over sequences would require considering every possible combination of hidden states, which grows exponentially with sequence length. With them, dynamic programming algorithms like Viterbi can find exact solutions in polynomial time. Understanding these assumptions deeply will help you know when HMMs are appropriate and when you need a more expressive model.
The Markov Assumption
The first assumption is the Markov property: the probability of transitioning to a state depends only on the current state, not on the full history.
where:
- : the state at position (what we want to predict)
- : the full history of previous states
- : only the immediately preceding state
The equation states that knowing the entire history provides no additional information beyond knowing just the previous state. For POS tagging, this means the probability of a tag depends only on the previous tag, not on tags from earlier in the sentence. If the previous tag is DT (determiner), the probability of NN (noun) is the same regardless of what came before the determiner.
This is a strong assumption. Consider "the old old man" versus "the very old man." After seeing "old" once, the probability of another adjective might differ from seeing "old" for the first time. A first-order HMM cannot capture this distinction. Higher-order HMMs condition on the previous two or three tags rather than just one, which helps. A second-order HMM replaces with , giving the model memory of the last two tags. The downside is that the transition table grows quadratically with tag set size: for tags, a first-order model has transition parameters, while a second-order model has . For a 40-tag set like Penn Treebank, that difference is 1,600 versus 64,000 parameters, requiring substantially more training data for reliable estimates.
The Output Independence Assumption
The second assumption is output independence: the probability of an observation depends only on the current state, not on other observations or states.
where:
- : the observation at position (the word we see)
- : all hidden states in the sequence
- : all other observations (past and future words)
- : only the current hidden state
The equation states that the probability of emitting a particular word depends solely on the current tag, not on any surrounding context. For POS tagging, this means the word "bank" has the same emission probability from the NN state regardless of surrounding words. The model cannot use the context "river bank" to increase the probability of the noun interpretation, nor can it use "bank account" to do the same. All disambiguation must happen indirectly through transition probabilities.
This is perhaps the most consequential limitation of HMMs for NLP. In natural language, the meaning and function of a word are heavily influenced by its neighbors. The word "light" is more likely an adjective after "a" and before "lunch" than after "switch" and before "on." A model that ignores this surrounding evidence will make systematic errors on ambiguous words. Conditional Random Fields, which you will study in a later chapter, address exactly this limitation by allowing the model to condition on arbitrary features of the surrounding context when making labeling decisions.
# Demonstrating the Markov assumption
# P(NN | DT) is the same regardless of history
# History 1: Started with DT
history_1 = ["DT"]
# History 2: Started with VB, then DT
history_2 = ["VB", "DT"]
# Under Markov assumption, both give same P(NN | DT)
prob_nn_given_dt = A["DT"]["NN"]The Markov Assumption in Action ---------------------------------------- History 1: ['DT'] History 2: ['VB', 'DT'] P(next=NN | current=DT) = 0.8 Both histories give the same probability because HMM only conditions on the immediately previous state.
These assumptions enable efficient algorithms for inference and learning. The Viterbi algorithm (covered in the next chapter) finds the most likely state sequence in time, where is the sequence length and is the number of states. Without these assumptions, exact inference would require exponential time. The Markov assumption is what allows the Viterbi algorithm to break the global optimization into independent subproblems: once you know the best way to reach state at position , you do not need to remember how you got there to compute the best way to reach any state at position . That "forgetfulness" is not a bug; it is the computational feature that makes the algorithm feasible.
Computing Sequence Probabilities
Given an HMM, how do we compute the probability of a particular observation sequence and state sequence? This question has two related but distinct versions. You might want to know the probability of a specific combination of words and tags: for example, how likely is the sentence "the dog runs" tagged as "DT NN VB"? Or you might want to know the probability of the observation sequence alone, summed over all possible hidden state sequences: how likely is the sentence "the dog runs" to appear at all, regardless of how it is tagged? The first question gives you the joint probability ; the second gives you the marginal . Both are important, and the joint probability decomposition is the foundation for computing either one.
The joint probability factors according to the HMM structure, exploiting the independence assumptions to decompose a complex probability into simple terms.
For an observation sequence and state sequence , the joint probability is:
where:
- : the joint probability of observing sequence while the hidden states follow sequence
- : the initial probability of starting in state , denoted
- : the emission probability of the first observation given the first state, denoted
- : the product over all positions from 2 to
- : the transition probability from state to state , denoted
- : the emission probability of observation from state , denoted
The formula captures the generative story of an HMM: start in some state (with probability ), emit the first word (with probability ), then repeatedly transition to a new state (with probability ) and emit the next word (with probability ). This story is "generative" because you can use it to generate sentences: sample a starting tag from , sample a word from , sample a next tag from , sample the next word from , and so on. The HMM describes a process that could plausibly have produced the training data, and parameter estimation is the task of finding the process whose description fits that data best.
Let's compute this for a concrete example: the sentence "the dog runs" with tags "DT NN VB." We'll trace through each step of the computation, showing how the probabilities multiply together to give the joint probability of this particular word-tag pairing.
def compute_joint_probability(observations, states, pi, A, B):
"""Compute P(observations, states) for an HMM."""
n = len(observations)
# Start with initial probability and first emission
prob = pi[states[0]] * B[states[0]][observations[0]]
# Multiply by transition and emission for each subsequent position
for t in range(1, n):
transition_prob = A[states[t - 1]][states[t]]
emission_prob = B[states[t]][observations[t]]
prob *= transition_prob * emission_prob
return prob
# Our example sentence
observations = ["the", "dog", "runs"]
tag_sequence = ["DT", "NN", "VB"]
joint_prob = compute_joint_probability(observations, tag_sequence, pi, A, B)Computing P('the dog runs', 'DT NN VB')
==================================================
1. Initial: P(DT) = 0.6
2. Emission: P('the'|DT) = 0.9
3. Transition: P(NN|DT) = 0.8
4. Emission: P('dog'|NN) = 0.45
5. Transition: P(VB|NN) = 0.6
6. Emission: P('runs'|VB) = 0.5
Joint probability: 0.6 × 0.9 × 0.8 × 0.45 × 0.6 × 0.5
= 0.058320The joint probability is quite small because we're multiplying many probabilities together. For longer sequences, these products become vanishingly small, leading to numerical underflow. A 20-word sentence with probabilities around 0.1 at each step would produce a product on the order of , which many floating-point implementations round to zero. In practice, we work in log space, converting multiplications to additions:
Since is a monotonically increasing function, maximizing the log probability is equivalent to maximizing the probability itself. Working in log space prevents numerical underflow and is standard practice for probabilistic sequence models.
Parameter Estimation from Data
An HMM is only useful if we can learn its parameters from data. For POS tagging, we have access to annotated corpora where each word is labeled with its correct tag. This is supervised learning, and parameter estimation is straightforward: we count occurrences and normalize.
The intuition behind maximum likelihood estimation is simple: the best model is the one that assigns the highest probability to the observed data. If you saw the bigram "DT NN" a thousand times in training but "DT VB" only ten times, the data is telling you that determiners are followed by nouns far more often than by verbs, and your transition probabilities should reflect that. Maximum likelihood formalized this intuition into a precise estimation procedure.
Maximum Likelihood Estimation
The maximum likelihood estimates for HMM parameters are simple frequency ratios derived from counting occurrences in the training data. The estimates have a satisfying form: every probability is just the count of the event you care about divided by the count of the conditioning context. There are no free hyperparameters, no optimization loops, and no gradient descent. You count, you divide, and you are done.
For initial probabilities, we count how often each state starts a sequence:
where:
- : the estimated probability of starting in state
- : the number of sequences that begin with state
- : the total number of sequences in the training data
For transition probabilities, we count how often state follows state :
where:
- : the estimated probability of transitioning from state to state
- : the number of times state is immediately followed by state
- : the total number of times state appears (except at sequence end)
For emission probabilities, we count how often state emits observation :
where:
- : the estimated probability of emitting observation from state
- : the number of times state emits observation
- : the total number of times state appears
In words: the initial probability of state is the fraction of sequences that start with . The transition probability from to is the fraction of times is followed by . The emission probability of from is the fraction of times state emits .
Notice that each distribution is estimated independently. Transition probabilities depend only on pairs of consecutive tags; emission probabilities depend only on individual tag-word pairs. This independence makes estimation extremely fast and scales to large corpora without difficulty. The Brown corpus used below contains over 57,000 sentences and more than a million words; the entire parameter estimation completes in seconds.
Let's implement this with NLTK's tagged corpora. The Brown corpus contains over 57,000 sentences with POS tags, giving us enough data to estimate reliable probabilities. We'll use the Universal tagset, which maps the fine-grained Brown tags to 17 coarse categories for cleaner patterns.
# Download required data (run once interactively, then comment out)
# nltk.download("brown", quiet=True)
# nltk.download("universal_tagset", quiet=True)
# Load Brown corpus with Universal POS tags
from nltk.corpus import brown
tagged_sents = brown.tagged_sents(tagset="universal")from collections import defaultdict
def estimate_hmm_parameters(tagged_sentences):
"""Estimate HMM parameters from tagged sentences using MLE."""
# Counts for estimation
initial_counts = defaultdict(int)
transition_counts = defaultdict(lambda: defaultdict(int))
emission_counts = defaultdict(lambda: defaultdict(int))
tag_counts = defaultdict(int)
for sentence in tagged_sentences:
if len(sentence) == 0:
continue
# Count initial state
first_tag = sentence[0][1]
initial_counts[first_tag] += 1
# Count emissions and transitions
prev_tag = None
for word, tag in sentence:
word = word.lower() # Normalize case
emission_counts[tag][word] += 1
tag_counts[tag] += 1
if prev_tag is not None:
transition_counts[prev_tag][tag] += 1
prev_tag = tag
# Convert counts to probabilities
num_sentences = len(tagged_sentences)
# Initial probabilities
pi = {tag: count / num_sentences for tag, count in initial_counts.items()}
# Transition probabilities
A = {}
for tag1, next_tags in transition_counts.items():
total = sum(next_tags.values())
A[tag1] = {tag2: count / total for tag2, count in next_tags.items()}
# Emission probabilities
B = {}
for tag, words in emission_counts.items():
total = tag_counts[tag]
B[tag] = {word: count / total for word, count in words.items()}
return pi, A, B, tag_counts
# Estimate parameters from Brown corpus
pi_est, A_est, B_est, tag_counts = estimate_hmm_parameters(tagged_sents)HMM Parameters Estimated from Brown Corpus
==================================================
Training sentences: 57,340
Unique tags: 12
Transition Probabilities (examples):
P(NOUN | DET) = 0.6266
P(VERB | NOUN) = 0.1593
P(ADV | VERB) = 0.1033
Emission Probabilities (examples):
P('the' | DET) = 0.5106
P('is' | VERB) = 0.0553
P('good' | ADJ) = 0.0087The estimated parameters reveal clear patterns. The initial probability distribution shows which tags typically begin sentences in English:
| Tag | Initial Probability | Common Examples | |:---:|-------------------:|:----------------| | DET | 0.2134 | "The dog...", "A cat..." | | PRON | 0.1597 | "He said...", "It was..." | | NOUN | 0.1411 | "Dogs are...", "Time flies..." | | ADP | 0.1228 | "In 1990,...", "For example,..." | | ADV | 0.0913 | "However,...", "Then..." | | . | 0.0889 | Sentence fragments | | CONJ | 0.0491 | "And then...", "But..." | | VERB | 0.0451 | "Run!", "Consider..." | | PRT | 0.0367 | Rare as sentence start | | ADJ | 0.0343 | "Good morning", "Many people..." | | NUM | 0.0168 | "1984 was...", "Two men..." | | X | 0.0005 | Rare |
: Initial state probabilities estimated from the Brown corpus. Determiners (DET), nouns (NOUN), and pronouns (PRON) dominate sentence beginnings. This reflects common English patterns. {#tbl-initial-probs}
Determiners, nouns, and pronouns dominate sentence beginnings, together accounting for over 75% of sentence starts.
Another revealing view is how emission probabilities vary across tags for ambiguous words. The word "run" can be a noun ("a morning run") or a verb ("I run daily"). Let's see how the learned emission probabilities capture this ambiguity:
| Word | P(word \| NOUN) | P(word \| VERB) | P(word \| ADJ) | Primary Tag | |:----:|---------------:|---------------:|---------------:|:-----------:| | time | 0.005796 | 0.000005 | 0.000000 | NOUN | | run | 0.000200 | 0.000859 | 0.000000 | VERB | | set | 0.000323 | 0.001778 | 0.000000 | VERB |
: Emission probabilities for ambiguous words across POS tags. "Time" shows moderate noun vs. verb ambiguity. "Run" is more strongly verbal. "Set" has large probability from both noun and verb tags. This reflects its high ambiguity in English. {#tbl-emission-ambiguity}
The table reveals that "time" has higher emission probability from NOUN than VERB, but both are non-zero. This reflects lexical ambiguity. These patterns form the statistical backbone of HMM-based tagging.
Handling Unknown Words
A critical challenge emerges when we encounter words not seen during training. If a word never appeared with tag , then . When we multiply by zero, the entire sequence probability becomes zero, making it impossible to tag sentences containing unknown words.
This is not a minor edge case. In real text, a significant fraction of words at test time will never have appeared in training. Technical documents use specialized vocabulary. Named entities change constantly. New words enter the language every year. An HMM tagger without a strategy for unknown words would completely fail on any non-trivial text.
Smoothing techniques address this problem. The simplest approach is add-one (Laplace) smoothing: add a small count to every word-tag combination before normalizing.
where:
- : the smoothed emission probability of observation from state
- : the number of times state emits observation (zero for unseen combinations)
- : the total number of times state appears
- : the vocabulary size (number of unique observations)
The numerator adds 1 to every count. This ensures no probability is ever zero. The denominator adds to compensate, keeping the probabilities properly normalized (summing to 1). This ensures every word has a non-zero probability from every tag, though the probability is very small for unseen combinations.
def estimate_smoothed_emissions(tagged_sentences, alpha=1.0):
"""Estimate emission probabilities with add-alpha smoothing."""
emission_counts = defaultdict(lambda: defaultdict(int))
tag_counts = defaultdict(int)
vocab = set()
for sentence in tagged_sentences:
for word, tag in sentence:
word = word.lower()
emission_counts[tag][word] += 1
tag_counts[tag] += 1
vocab.add(word)
vocab_size = len(vocab)
# Smoothed emission probabilities
B_smoothed = {}
for tag in tag_counts:
B_smoothed[tag] = {}
denominator = tag_counts[tag] + alpha * vocab_size
for word in vocab:
count = emission_counts[tag].get(word, 0)
B_smoothed[tag][word] = (count + alpha) / denominator
# Store smoothed probability for unknown words
B_smoothed[tag]["<UNK>"] = alpha / denominator
return B_smoothed, vocab
B_smoothed, vocab = estimate_smoothed_emissions(tagged_sents)Smoothed Emission Probabilities
----------------------------------------
Vocabulary size: 49,815
Known word 'the':
Unsmoothed P('the' | DET) = 0.510645
Smoothed P('the' | DET) = 0.374498
Unknown word '<UNK>':
Unsmoothed P('<UNK>' | DET) = 0.000000
Smoothed P('<UNK>' | DET) = 0.00000535
Smoothed P('<UNK>' | NOUN) = 0.00000307With smoothing, unseen words get a small but non-zero probability from each tag. This allows the HMM to tag sentences containing new words by relying on transition probabilities to disambiguate. The smoother the unknown-word distribution, the more the model falls back on syntax (transitions) and the less it can rely on lexical identity. In practice, Laplace smoothing is often too aggressive: it assigns the same small probability to an unknown word from every tag, which means a rare capitalized word like "Zuckerberg" gets equal emission probability from NOUN, VERB, and ADJ, leaving disambiguation entirely to surrounding context.
More sophisticated approaches use morphological clues. Words ending in "-tion" are almost always nouns; words ending in "-ly" are almost always adverbs; capitalized words in the middle of a sentence are often proper nouns. By clustering unknown words into morphological classes and learning separate emission probabilities for each class, you can handle unknowns much more accurately without abandoning the HMM framework. This is the strategy used by the TNT tagger (Brants, 2000) and similar high-accuracy HMM systems.
HMM for POS Tagging
Now let's build a complete HMM POS tagger. Given a sentence, we want to find the most likely tag sequence. This is called the decoding problem because it is analogous to decoding a message in a noisy channel: the "true signal" is the tag sequence, the "noise" is the mapping from tags to words, and your task is to recover the most probable original signal given what you received.
where:
- : the optimal (most likely) state sequence
- : "the value of that maximizes" the following expression
- : the posterior probability of state sequence given observations
- : the joint probability of observations and states
The second equality follows from Bayes' rule: . Since is constant for a given observation sequence (it does not depend on which state sequence we are evaluating), maximizing the posterior is equivalent to maximizing the joint probability.
A naive approach would enumerate all possible tag sequences, compute their joint probabilities, and return the maximum. But with tags and words, there are possible sequences. For 12 tags and a 20-word sentence, that is over 3.8 quadrillion possibilities. Even at a billion evaluations per second, exhaustive search would take over 3 million years.
The Viterbi algorithm solves this efficiently in time using dynamic programming. We will cover Viterbi in detail in the next chapter. For now, let's implement a simplified greedy decoder that selects the locally best tag at each position.
Greedy decoding works as follows: for the first word, we pick the tag that maximizes . For each subsequent word, we pick the tag that maximizes given the previous tag we chose. This is fast but can make mistakes when a locally optimal choice leads to a globally suboptimal sequence.
class SimpleHMMTagger:
"""A simple HMM POS tagger with greedy decoding."""
def __init__(self, pi, A, B, vocab):
self.pi = pi
self.A = A
self.B = B
self.vocab = vocab
self.tags = list(A.keys())
def get_emission_prob(self, tag, word):
"""Get emission probability, handling unknown words."""
word = word.lower()
if word in self.B[tag]:
return self.B[tag][word]
return self.B[tag]["<UNK>"]
def tag_greedy(self, sentence):
"""Tag sentence using greedy (locally optimal) decoding."""
tags = []
for i, word in enumerate(sentence):
if i == 0:
# First word: use initial and emission probabilities
best_tag = None
best_prob = -1
for tag in self.tags:
prob = self.pi.get(tag, 1e-10) * self.get_emission_prob(
tag, word
)
if prob > best_prob:
best_prob = prob
best_tag = tag
else:
# Subsequent words: use transition and emission
prev_tag = tags[-1]
best_tag = None
best_prob = -1
for tag in self.tags:
trans_prob = self.A.get(prev_tag, {}).get(tag, 1e-10)
emit_prob = self.get_emission_prob(tag, word)
prob = trans_prob * emit_prob
if prob > best_prob:
best_prob = prob
best_tag = tag
tags.append(best_tag)
return tags
# Create tagger from estimated parameters
tagger = SimpleHMMTagger(pi_est, A_est, B_smoothed, vocab)# Test on some sentences
test_sentences = [
["The", "dog", "runs", "quickly"],
["She", "reads", "books"],
["The", "old", "man", "the", "boat"], # Classic garden path sentence
]
predictions = []
for sent in test_sentences:
tags = tagger.tag_greedy(sent)
predictions.append(list(zip(sent, tags)))Greedy HMM Tagger Results ================================================== Sentence 1: The dog runs quickly Tags: The/DET dog/NOUN runs/NOUN quickly/ADV Sentence 2: She reads books Tags: She/PRON reads/VERB books/NOUN Sentence 3: The old man the boat Tags: The/DET old/ADJ man/NOUN the/DET boat/NOUN
The greedy tagger works reasonably well for straightforward sentences but can make errors when local decisions conflict with global coherence. The sentence "The old man the boat" is a famous garden path sentence where "man" functions as a verb (meaning "to operate") and "old" is a noun phrase ("the elderly people"). Greedy decoding often misses such non-local dependencies because each decision only considers the immediately preceding tag.
Garden path sentences illustrate a broader point about greedy decoding: a locally optimal choice can lock you into a globally suboptimal path. Once the greedy decoder decides "man" is a noun, it has committed to that interpretation and there is no backtracking. The Viterbi algorithm avoids this by postponing final decisions. Rather than committing to a single path at each step, Viterbi maintains the best partial sequence ending in each state, and only commits to a full sequence once all positions have been processed. This global search within the dynamic programming framework is what separates Viterbi from greedy decoding and why it consistently outperforms greedy by several percentage points on held-out data.
Visualizing HMM Structure
The transition matrix is the heart of an HMM's syntactic knowledge. By visualizing it as a heatmap, we can see which tag sequences the model considers likely and which it considers rare. This provides insight into both the structure of English grammar and the quality of our learned model. Notice that the matrix is quite sparse: most entries are small because most tag pairs rarely occur consecutively in well-formed English. The few high-probability entries correspond to the syntactic rules that generate most English sentences.

The transition matrix reveals clear syntactic patterns. Determiners (DET) have high transition probability to nouns (NOUN) and adjectives (ADJ). Nouns frequently transition to verbs (VERB) or punctuation. Prepositions (ADP) strongly predict nouns. This reflects prepositional phrase structure. These patterns are exactly what we'd expect from English grammar.
Evaluating the Tagger
How well does our HMM tagger perform? To answer this rigorously, we need to evaluate on data the model hasn't seen during training. We'll split the Brown corpus into 90% training and 10% test, re-estimate all parameters on the training portion, and measure accuracy on the held-out test sentences.
We'll also track performance separately for known words (those seen during training) versus unknown words. This distinction matters because the model handles these cases very differently: known words have learned emission probabilities, while unknown words must rely entirely on transition patterns and smoothed probabilities.
from sklearn.model_selection import train_test_split
# Split data into train and test
train_sents, test_sents = train_test_split(
tagged_sents, test_size=0.1, random_state=42
)
# Re-estimate parameters on training data only
pi_train, A_train, B_train, _ = estimate_hmm_parameters(train_sents)
B_train_smoothed, vocab_train = estimate_smoothed_emissions(train_sents)
# Create tagger with training parameters
test_tagger = SimpleHMMTagger(pi_train, A_train, B_train_smoothed, vocab_train)def evaluate_tagger(tagger, test_sentences):
"""Evaluate tagger accuracy on test sentences."""
correct = 0
total = 0
known_correct = 0
known_total = 0
unknown_correct = 0
unknown_total = 0
for sentence in test_sentences:
words = [word for word, tag in sentence]
gold_tags = [tag for word, tag in sentence]
pred_tags = tagger.tag_greedy(words)
for word, gold, pred in zip(words, gold_tags, pred_tags):
total += 1
is_known = word.lower() in tagger.vocab
if gold == pred:
correct += 1
if is_known:
known_correct += 1
else:
unknown_correct += 1
if is_known:
known_total += 1
else:
unknown_total += 1
return {
"overall": correct / total if total > 0 else 0,
"known": known_correct / known_total if known_total > 0 else 0,
"unknown": unknown_correct / unknown_total if unknown_total > 0 else 0,
"unknown_rate": unknown_total / total if total > 0 else 0,
}
results = evaluate_tagger(test_tagger, test_sents)Training sentences: 51,606 Test sentences: 5,734 Unknown word rate: 2.00% | Metric | Accuracy | |:-------|--------:| | Overall | 92.5% | | Known words | 93.8% | | Unknown words | 25.7% |
: HMM tagger accuracy on the Brown corpus test set. The model achieves high accuracy on known words where emission probabilities provide strong signals. Unknown word accuracy drops substantially because the model must rely solely on transition probabilities and smoothed emissions. {#tbl-accuracy}
The accuracy gap between known and unknown words is striking. For known words, the model performs reasonably well because emission probabilities provide strong signals. For unknown words, the model must rely entirely on transition probabilities, which provide weaker disambiguation.
This evaluation uses greedy decoding, which makes locally optimal choices. The Viterbi algorithm (next chapter) finds the globally optimal sequence and typically improves accuracy by 2-5 percentage points, especially for difficult cases where local and global optima differ.
The accuracy numbers here may seem lower than expected for a model praised as a major advance. Remember that we are evaluating with the Universal tagset, which has only 12 coarse categories. With fewer categories, random chance already accounts for a reasonable baseline. HMM taggers evaluated on the Penn Treebank's 36-tag set regularly exceeded 95% overall accuracy, a figure that remained the benchmark for over a decade. The gap between known-word and unknown-word accuracy also motivates the morphological suffix features mentioned earlier: with better handling of unknowns, overall performance rises considerably.
To better understand where the model's disambiguation power comes from, let's visualize the emission probability profiles for several ambiguous words. The degree to which these profiles differ across tags is what determines how much the emission matrix helps versus hinders disambiguation.

The log-scale y-axis is necessary here because emission probabilities span several orders of magnitude. A word that appears frequently as a NOUN will have emission probability on the order of , while the same word appearing rarely as a VERB might have probability on the order of . Both are small in absolute terms, but the ratio between them is what the model exploits for disambiguation. When the bars for NOUN and VERB are nearly the same height (indicating similar emission probabilities from both tags), the model must rely on the transition matrix to pick the right tag.
Worked Example: Tagging "Time flies"
To understand how an HMM resolves ambiguity, let's trace through the complete probability computation for the classic ambiguous sentence "Time flies." This two-word sentence has two plausible interpretations, and both words can function as either nouns or verbs:
- "Time flies" could mean "Time passes quickly" (NOUN VERB)
- "Time flies" could mean "Measure how fast flies move" (VERB NOUN)
The second interpretation is grammatically valid but pragmatically unusual. Most readers parse "Time flies" as the first interpretation without hesitation, which is exactly what we want the HMM to do. The question is: does the statistical evidence from the corpus lead the model to the same conclusion?
We will compute the joint probability for each interpretation and see which one the HMM prefers. Since we are multiplying many small probabilities, we will work in log space to avoid numerical underflow. Each log probability is a negative number (since all probabilities are between 0 and 1), so the interpretation with the higher (less negative) total log probability is the preferred one.
# Use parameters estimated from training data
def compute_log_joint(words, tags, pi, A, B):
"""Compute log P(words, tags) to avoid underflow."""
import math
log_prob = 0
# Initial probability
init_p = pi.get(tags[0], 1e-10)
log_prob += math.log(init_p)
# First emission
word = words[0].lower()
emit_p = B.get(tags[0], {}).get(
word, B.get(tags[0], {}).get("<UNK>", 1e-10)
)
log_prob += math.log(emit_p)
# Subsequent transitions and emissions
for i in range(1, len(words)):
trans_p = A.get(tags[i - 1], {}).get(tags[i], 1e-10)
word = words[i].lower()
emit_p = B.get(tags[i], {}).get(
word, B.get(tags[i], {}).get("<UNK>", 1e-10)
)
log_prob += math.log(trans_p) + math.log(emit_p)
return log_prob
words = ["Time", "flies"]
interpretations = [
(["NOUN", "VERB"], "Time (subject) flies (action)"),
(["VERB", "NOUN"], "Time (action) flies (object)"),
]
log_probs = []
for tags, description in interpretations:
log_p = compute_log_joint(words, tags, pi_train, A_train, B_train_smoothed)
log_probs.append((tags, log_p, description))Analyzing 'Time flies' ================================================== Interpretation: NOUN VERB Meaning: Time (subject) flies (action) Log probability: -19.7839 Interpretation: VERB NOUN Meaning: Time (action) flies (object) Log probability: -27.3867 ================================================== Most likely interpretation: NOUN VERB (Time (subject) flies (action)) NOUN VERB is 2003.79x more likely than VERB NOUN
To understand why the HMM prefers NOUN VERB, let's decompose the log probability into its components: initial probability, emissions, and transitions.

The HMM prefers "Time flies" as NOUN VERB. This makes sense: in the training data, sentences starting with nouns are more common than sentences starting with verbs, and noun-verb transitions are frequent. The emission probability of "time" from NOUN is also higher than from VERB, since "time" appears as a noun more often in general text.
This example illustrates how HMMs combine multiple sources of evidence: initial probabilities favor starting with nouns, transitions favor noun-verb patterns, and emissions favor "time" as a noun and "flies" as a verb. The bar chart above lets you see exactly how each component contributes to the total log probability. When the NOUN VERB interpretation wins, it is not because of a single dominant factor; it is because several moderately sized factors all point in the same direction.
The key insight from this example is that HMMs do not disambiguate words in isolation. "Flies" has substantial probability from both NOUN and VERB, so the emission alone would not clearly favor either interpretation. What tips the balance is the combination of initial probability (nouns are more common sentence starters), transition probability (noun-to-verb transitions are common), and emission probability (time-as-noun and flies-as-verb are both plausible). No single source of evidence is conclusive; together they provide enough signal to make the correct call. This is the power of the joint probability framework.
Limitations and Impact
HMMs were the dominant approach to sequence labeling from the 1980s through the early 2000s. They remain instructive for understanding probabilistic sequence models and provide a foundation for more advanced techniques. To appreciate why HMMs were eventually replaced, you need to understand the abstract limitations and the concrete situations where those limitations cause real errors. And to appreciate why they lasted so long, you need to understand what they enabled that was simply not possible before.
Where HMMs Fall Short
The independence assumptions that make HMMs tractable also limit their power. The emission independence assumption is particularly restrictive: an HMM cannot condition on neighboring words when deciding what tag to emit. Consider the word "set." In "the set of numbers," it's a noun. In "set the table," it's a verb. An HMM can only use transition probabilities to disambiguate, ignoring the direct evidence from surrounding words like "the" before "set" or "table" after "set." The transition probabilities capture some of this information indirectly (if the previous tag is DET, a NOUN is more likely than a VERB), but they cannot capture word-specific context. The word "the" before "set" is strong evidence for a noun, but an HMM encodes only the tag of the previous word, not the word itself.
The Markov assumption also causes problems for long-distance dependencies. In "The dog that the cat chased runs," the verb "runs" must agree with "dog," but an HMM only sees the immediately preceding "chased." Higher-order HMMs (conditioning on two or three previous tags) help but create exponentially more parameters to estimate. In practice, second-order HMMs offer a meaningful improvement over first-order, but third-order and beyond suffer from data sparsity: the required counts simply do not accumulate reliably even in large corpora.
Unknown words present another persistent challenge. With Laplace smoothing, unknown words get similar emission probabilities from all tags, forcing the model to rely on transitions for disambiguation. But transition patterns alone often carry insufficient information. Consider the sentence "The zylothene process requires careful monitoring." The word "zylothene" is unknown, and the tagger must guess its tag from context. The preceding DET suggests a noun or adjective, which is helpful. But without any lexical signal from the word itself, distinguishing noun from adjective is difficult. Modern approaches address this by using character-level features: words ending in "-tion" are almost always nouns, words starting with a capital mid-sentence are often proper nouns, and words ending in "-ness" or "-ment" are reliably nouns. HMMs can incorporate such features only awkwardly, by creating special pseudo-word categories and routing unknown words through them.
Feature independence is perhaps the deepest limitation. HMMs encode the feature "current word" through the emission distribution, and that is essentially the only input feature available. A CRF, by contrast, can include arbitrary features of the input: the current word, surrounding words, word prefixes and suffixes, capitalization, whether the word appears in a gazetteer, and so on. All of these features can influence the tag prediction jointly, without assuming independence. This flexibility is why CRFs became the preferred model once the computational machinery for training them was worked out.
What HMMs Made Possible
Despite these limitations, HMMs achieved remarkable success and enabled practical NLP systems for the first time. Their contributions extend well beyond POS tagging.
The Viterbi algorithm made efficient exact inference possible, allowing HMM taggers to process text at thousands of words per second. This speed made NLP practical for large document collections. Before Viterbi, sequence labeling was either heuristic or computationally prohibitive at scale. The algorithm's complexity meant that tagging a 100-word document with 40 tags required only 160,000 operations, fast enough to process millions of documents in reasonable time.
The forward-backward algorithm enabled unsupervised learning from raw text. Given an HMM architecture with a fixed number of states but no labeled training data, the Baum-Welch algorithm iteratively re-estimates parameters to maximize the likelihood of the observed text. You could discover latent structure in tag sequences without any human annotation. While unsupervised HMMs never matched supervised accuracy, they opened up possibilities for low-resource languages where annotated corpora did not exist, and for tasks where defining labels ahead of time was difficult.
The probabilistic framework integrated naturally with other statistical models. HMM outputs could feed into named entity recognizers, parsers, and information extraction systems, with probabilities propagating through the pipeline. When you have a full probability distribution over tag sequences rather than a single deterministic output, downstream systems can weight competing hypotheses appropriately rather than treating the tagger's output as certain.
HMMs also established the paradigm of sequence labeling as a structured prediction problem. They formalized how labels depend on both the inputs and other labels. This structured view led directly to CRFs, which relax the emission independence assumption while preserving the Markov structure of transitions. From CRFs, the conceptual lineage runs to BiLSTM-CRF models, where a bidirectional LSTM replaces the emission distribution with a contextual neural encoder, and then to transformer-based taggers like BERT fine-tuned on sequence labeling tasks. Every step in this lineage builds on the HMM's foundational insight that the joint probability over a label sequence matters alongside the probability of each label in isolation.
Key Parameters
When implementing HMM-based taggers, several parameters affect model performance. Understanding what each parameter controls and how it interacts with the others will help you tune your model effectively on new tasks and datasets.
-
Smoothing constant (): Controls how much probability mass is reserved for unseen word-tag combinations. Higher values (e.g., 1.0 for Laplace smoothing) give more weight to unseen events but dilute the learned emission probabilities for known words. Lower values (e.g., 0.01) preserve learned distributions better but may leave unknown words dangerously undersmoothed. Start with and tune based on unknown word accuracy on a development set. If your test domain has very different vocabulary from training (e.g., medical text trained on news), you will likely need to increase .
-
Tag set granularity: Finer tag sets (like Penn Treebank's 36 tags) capture more distinctions but require more training data and increase computational cost. Coarser sets (like Universal Dependencies' 17 tags) are easier to learn but lose information. Choose based on your downstream task requirements. For most information extraction applications, coarse tags are sufficient; for linguistic research or parsing support, fine-grained tags matter.
-
Case normalization: Converting words to lowercase reduces vocabulary size and improves emission estimates for rare words, but loses capitalization signals that help identify proper nouns and sentence boundaries. The implementation above uses lowercase normalization, which helps with data sparsity but hurts on named entity recognition tasks where capitalization is a strong signal. A practical compromise is to lowercase everything but preserve a "starts with capital" feature in an augmented emission model.
-
Unknown word handling: Beyond smoothing, you can improve unknown word accuracy by using morphological features (suffixes, prefixes, capitalization patterns) or by backing off to character-level models. The simple
<UNK>token approach shown here provides a baseline, but production systems typically cluster unknown words by their suffix (last 3-4 characters) and learn separate emission distributions for each suffix class. This allows the model to recognize that "implementation" is very likely a noun based on its "-tion" suffix, even if "implementation" itself never appeared in training. -
Higher-order transitions: Switching from first-order (bigram) to second-order (trigram) transitions improves accuracy but requires more data and memory. For a 40-tag Penn Treebank model, the transition table grows from 1,600 to 64,000 entries. If your corpus has fewer than 100,000 sentences, trigram transitions may be too sparse to estimate reliably without additional smoothing (such as linear interpolation between trigram, bigram, and unigram estimates).
Summary
Hidden Markov Models provide a probabilistic framework for sequence labeling. The model jointly models hidden state sequences (like POS tags) and observable output sequences (like words), learning statistical patterns for how states transition and what observations each state generates. The framework needs only three counting operations, tallying initial state frequencies, tag bigram frequencies, and word-tag co-occurrence frequencies, to build a model capable of resolving linguistic ambiguity.
Key takeaways:
- HMM components include states, observations, initial probabilities, transition probabilities, and emission probabilities. These five elements fully specify the model. The transition matrix captures syntax; the emission matrix captures lexical associations; the initial distribution captures positional tendencies.
- The Markov assumption says each state depends only on the previous state, not the full history. This enables efficient algorithms but limits the model's ability to capture long-range dependencies. Higher-order HMMs extend the context window at the cost of exponentially more parameters.
- The output independence assumption says each observation depends only on its current state. This prevents the model from using surrounding context to disambiguate emissions, which is one of the primary motivations for Conditional Random Fields.
- Maximum likelihood estimation learns parameters by counting occurrences in annotated data. The estimates are straightforward frequency ratios, making HMMs fast to train even on large corpora. Smoothing handles unseen word-tag pairs that would otherwise have zero probability.
- Decoding finds the most likely state sequence given observations. Greedy decoding is fast but suboptimal; the Viterbi algorithm (next chapter) finds the globally optimal sequence in time using dynamic programming.
- HMM limitations include inability to use rich contextual features, difficulty with long-distance dependencies, and weak handling of unknown words. These motivated the development of Conditional Random Fields and, eventually, neural sequence models.
- Historical significance: HMMs established the sequence labeling paradigm, enabled the first high-accuracy POS taggers, and provided the conceptual foundation for every subsequent structured prediction model in NLP.
HMMs exemplify a recurring theme in NLP: strong independence assumptions enable efficient algorithms, but real language violates these assumptions in important ways. The tension between tractability and expressive power drives model development forward. The next chapter covers the Viterbi algorithm, which finds optimal sequences in time, and subsequent chapters introduce CRFs, which relax HMM's independence assumptions while preserving the structured prediction framework that HMMs established.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about Hidden Markov Models and sequence labeling.
Hidden Markov Models Quiz
Reference
Citation details
Cite or share this article.
Continue with the full handbook
This chapter is part of Language AI Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.
Explore Language AI HandbookStay up to date
Get articles, book updates, and news delivered to your inbox.
No spam, unsubscribe anytime.
Join the community
Sign in to remove popups, track your reading progress, and join the discussion.

Comments
No comments yet. Be the first to share your thoughts!