HMM for POS tagging and NER
Define a first-order HMM with emission and transition matrices, run Viterbi decoding on a short sentence, and connect it to POS/NER sequence labeling.
Sequence labeling assigns a tag to every token in a sentence. Part-of-speech (POS) tagging maps each word to its grammatical category (noun, verb, adjective, ...); named entity recognition (NER) labels spans as Person, Organization, Location, or Other.
Both tasks share a structure: the output is a sequence of the same length as the input, and adjacent labels are strongly correlated (a noun is likely followed by a verb; an "I" (inside) tag must follow a "B" (begin) tag of the same entity type). The Hidden Markov Model is the canonical probabilistic model for this structure[1, 2].
HMM definition
An HMM over observation sequence and hidden state sequence (the tags) has three components:
Transition probabilities : a matrix (rows sum to 1) encoding how likely tag follows tag .
Emission probabilities : a matrix encoding how likely word is emitted from tag .
Initial probabilities : a length- vector.
The joint probability of an observation-state sequence pair:
Training: counting
Given a labeled corpus (sentences with gold POS tags), estimate by MLE:
Each count is a simple corpus statistic. Add Laplace smoothing to handle unseen word-tag pairs.
Decoding: the Viterbi algorithm
At test time, we want the most probable tag sequence given the words:
Brute force over sequences is exponential. The Viterbi algorithm solves this in with dynamic programming[3].
Define : the probability of the best path ending in state at time .
Initialization ():
Recursion ():
Store the backpointer .
Termination: . Backtrack through to recover the full sequence.
Worked example
Tags: DT, NN, VB (determiner, noun, verb). Words: the, dog, runs.
Tiny parameters (illustrative):
("the"): , , .
("dog"): .
("runs"): .
The Viterbi path recovers DT-NN-VB, the correct tagging.
import numpy as np
# Tags: 0=DT, 1=NN, 2=VB | Words: 0=the, 1=dog, 2=runs
pi = np.array([0.6, 0.3, 0.1])
A = np.array([[0.1, 0.8, 0.1],
[0.1, 0.1, 0.8],
[0.5, 0.4, 0.1]])
B = np.array([[0.9, 0.05, 0.05],
[0.01, 0.9, 0.09],
[0.0, 0.05, 0.95]])
sentence = [0, 1, 2] # the dog runs
K, T = len(pi), len(sentence)
v = np.zeros((T, K))
psi = np.zeros((T, K), dtype=int)
v[0] = pi * B[:, sentence[0]]
for t in range(1, T):
scores = v[t - 1, :, None] * A # (K_prev, K_next)
psi[t] = scores.argmax(axis=0)
v[t] = scores.max(axis=0) * B[:, sentence[t]]
# Backtrack
path = np.zeros(T, dtype=int)
path[-1] = v[-1].argmax()
for t in range(T - 2, -1, -1):
path[t] = psi[t + 1, path[t + 1]]
tag_names = ["DT", "NN", "VB"]
word_names = ["the", "dog", "runs"]
print("Viterbi path:", [tag_names[y] for y in path])
print("Viterbi log-prob:", np.log(v[-1, path[-1]]))BIO labeling for NER
NER uses the BIO tag scheme: Beginning of an entity span, Inside, Outside. The sentence "Apple announced new products" might tag as:
The HMM transition matrix encodes the BIO constraint: (can't enter an inside tag from outside without a begin tag), enforced by setting that entry to zero before normalization.
HMM limitations
The emission probability conditions only on the current word. It cannot use surrounding words as features: "run" and "runs" have independent emission distributions.
Exercises
Exercise 1. Using the lesson's parameters, complete the worked example: compute , then the three candidate values inside , and confirm the Viterbi path is DT-NN-VB. Recall , the rows of as given, and the emission column for "runs" is .
Solution
Step 1. From initialization, , , .
Step 2. Compute for "dog" (). The transitions into NN are , , :
The argmax is DT, so the backpointer .
Step 3. For at "runs" (), the three candidates use the transition column into VB, . Taking as the dominant predecessor (the other entries are far smaller):
which dominates the DT and VB predecessors. So , with .
Step 4. Backtrack: the max at is VB, whose backpointer is NN, whose backpointer is DT. The path is DT-NN-VB, the correct tagging of "the dog runs".
Exercise 2. The BIO scheme forbids an transition (an inside tag cannot start a span). Show that setting before row normalization makes any tag sequence containing an immediately followed by an have joint probability zero under the HMM.
Solution
Step 1. The HMM joint factorizes as . Every transition between consecutive tags enters as a multiplicative factor .
Step 2. Suppose the sequence has and at some position . Then the product contains the factor .
Step 3. By construction . A single zero factor in a product of nonnegative terms makes the whole product zero, so regardless of the other factors.
Step 4. Since Viterbi maximizes this joint, it never selects a path through a zeroed transition (any competing path with nonzero probability beats it). The hard BIO constraint is enforced exactly, not just discouraged.
Exercise 3 (code). Run Viterbi on the lesson's HMM but change the emission so "dog" is much more likely a verb than a noun (, ). Confirm the decoded tag for "dog" flips from NN to VB.
Solution
import numpy as np
pi = np.array([0.6, 0.3, 0.1])
A = np.array([[0.1, 0.8, 0.1],
[0.1, 0.1, 0.8],
[0.5, 0.4, 0.1]])
# "dog" emission changed: now strongly a verb
B = np.array([[0.9, 0.05, 0.05],
[0.01, 0.10, 0.89], # NN row: dog now only 0.10
[0.0, 0.85, 0.15]]) # VB row: dog now 0.85
sentence = [0, 1, 2] # the dog runs
K, T = len(pi), len(sentence)
v = np.zeros((T, K))
psi = np.zeros((T, K), dtype=int)
v[0] = pi * B[:, sentence[0]]
for t in range(1, T):
scores = v[t - 1, :, None] * A
psi[t] = scores.argmax(axis=0)
v[t] = scores.max(axis=0) * B[:, sentence[t]]
path = np.zeros(T, dtype=int)
path[-1] = v[-1].argmax()
for t in range(T - 2, -1, -1):
path[t] = psi[t + 1, path[t + 1]]
tag_names = ["DT", "NN", "VB"]
assert tag_names[path[1]] == "VB" # "dog" now decoded as a verb
print("Viterbi path:", [tag_names[y] for y in path])With the emission shifted, the noun hypothesis for "dog" loses to the verb hypothesis even though the DT-NN transition is favored, so the decoded middle tag flips to VB.
The next lesson introduces CRFs, which replace this generative model with a discriminative one that can use arbitrary features of the full input sentence.
References
- [1]Jurafsky, D., & Martin, J. H. (2024). Speech and Language Processing (3rd ed. draft). HMMs, POS tagging, and named entity recognition.↩
- [2]Manning, C. D., & Schütze, H. (1999). Foundations of Statistical Natural Language Processing. MIT Press. Markov models and tagging.↩
- [3]Rabiner, L. R. (1989). "A tutorial on hidden Markov models and selected applications in speech recognition". Proceedings of the IEEE, 77(2), 257-286. The standard reference for the forward, Viterbi, and Baum-Welch algorithms. doi:10.1109/5.18626↩