Hidden Markov Models › Parameter Estimation from Data
Language AI HandbookAll chapters Hidden Markov Models › Parameter Estimation from DataOne section · Sections in this chapter5 / 13 ↓
Reading tools Definitions & inline links
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
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.