Core ML1989intermediate13 min read

A Tutorial on Hidden Markov Models and Selected Applications in Speech Recognition

دليل تعليمي في نماذج ماركوف المخفية وتطبيقاتها في التعرّف على الكلام

Rabiner, L. R. — Proceedings of the IEEE

The problem

Real-world signals — speech waveforms, biological sequences, financial data — are generated by processes whose internal states we cannot observe directly. We see only the outputs. Simple Markov chains can sequences, but they assume states are visible. When states are hidden and only noisy observations are available, we need a principled framework to (1) evaluate how likely an is, (2) decode the best sequence, and (3) learn the model parameters from data — all efficiently.

The contribution

A unified tutorial consolidating the theory and practice of Hidden Markov Models. Rabiner presents three canonical problems and their solutions: the for evaluation (computing observation ), the for decoding (finding the most likely state sequence), and the (a special case of EM) for learning model parameters. The tutorial then demonstrates HMMs on three levels of : isolated words, connected words, and continuous speech, establishing HMMs as the dominant paradigm for speech recognition for the following two decades.

The impact

This tutorial became the single most-cited reference for HMMs in engineering and computer science, with over 30,000 citations. It trained a generation of researchers and engineers in probabilistic sequence modeling. HMMs built on this framework powered commercial speech recognition systems (Dragon, AT&T, Nuance) until deep learning replaced them around 2012. The mathematical tools Rabiner unified — , EM, models — remain foundational to modern sequence models including CRFs, neural HMMs, and even mechanisms.

Imagine a friend is cooking behind a closed kitchen door. You can't see what they're doing — but you hear clanging pots, running water, the oven beeping, and sizzling oil. From this sequence of sounds alone, you reconstruct the recipe steps: chopping → boiling → frying → baking.

That's a . The recipe steps are hidden states you never see directly. The kitchen sounds are observations — noisy clues. And you know from experience that chopping usually leads to boiling (not baking), so there are transition probabilities between states. The whole framework lets you do three things: judge how likely a sound sequence is, guess the recipe, and learn the cooking patterns from many meals.

From Markov chains to hidden states

A is a system that hops between a finite set of states, where the next state depends only on the current state — not on the history. This "memoryless" property is what makes the math tractable.

In a simple weather model, today's weather (sunny or rainy) determines tomorrow's. If it's sunny today, there's a 70% chance of sun tomorrow and 30% chance of rain. These numbers live in a transition AA, where aija_{ij} is the of moving from state ii to state jj.

But what if we can't observe the weather directly — we can only see whether our colleague carries an umbrella? Now the weather is hidden, and the umbrella is the observation. This is the key insight behind HMMs: the states are invisible, and we must infer them from indirect, noisy signals.

Open in Lab
Left: a Markov chain with visible states. Right: an HMM where states are hidden and only observations are visible. Toggle to compare.
The demo wakes as you arrive…

The five elements of an HMM

An HMM is fully specified by five components, compactly written as λ=(N,M,A,B,π)\lambda = (N, M, A, B, \pi):

  • NN — the number of hidden states. In speech, each state might represent a phonetic segment. The states are S={S1,S2,…,SN}S = \{S_1, S_2, \ldots, S_N\}.
  • MM — the number of distinct observation symbols. If we vector-quantize a speech signal into a codebook of 256 entries, then M=256M = 256.
  • AA — the state matrix. Entry aij=P(qt+1=Sj∣qt=Si)a_{ij} = P(q_{t+1} = S_j \mid q_t = S_i) tells how likely the system is to move from state ii to state jj.
  • BB — the observation probability . Entry bj(k)=P(Ot=vk∣qt=Sj)b_j(k) = P(O_t = v_k \mid q_t = S_j) gives the probability of emitting symbol vkv_k when in state SjS_j. Think of it as each state having its own "fingerprint" of which observations it tends to produce.
  • π\pi — the initial state distribution. πi=P(q1=Si)\pi_i = P(q_1 = S_i) is the probability that the system starts in state ii.
Open in Lab
Click each element to see its role in a concrete weather/umbrella example.
The demo wakes as you arrive…

The three fundamental problems

Rabiner frames the entire HMM toolkit around three questions. Every application — from speech recognition to gene finding — maps onto one or more of them:

Problem 1 — Evaluation: Given a model λ\lambda and an observation sequence OO, what is P(O∣λ)P(O \mid \lambda)? In other words, how well does this model explain these observations? This is the scoring problem — used to select the best model among competitors.

Problem 2 — Decoding: Given λ\lambda and OO, what is the most likely hidden state sequence? What was probably happening behind the curtain? This is the problem — finding the hidden story.

Problem 3 — Learning: Given observation sequences, how do we adjust λ=(A,B,π)\lambda = (A, B, \pi) to maximize P(O∣λ)P(O \mid \lambda)? How do we train the model from data? This is the problem.

Open in Lab
Click each problem to see its input, output, and real-world use case.
The demo wakes as you arrive…

Problem 1: the Forward-Backward algorithm

To compute P(O∣λ)P(O \mid \lambda) by brute force, we'd sum over all possible state sequences — NTN^T of them for TT time steps and NN states. For N=5N = 5 and T=100T = 100, that's 5100≈10705^{100} \approx 10^{70} paths: impossible.

The Forward solves this in O(N2T)O(N^2 T) time using dynamic programming. The idea is beautifully simple: define the forward variable αt(i)=P(O1,O2,…,Ot,qt=Si∣λ)\alpha_t(i) = P(O_1, O_2, \ldots, O_t, q_t = S_i \mid \lambda) — the probability of seeing the first tt observations AND being in state ii at time tt. Then build it up step by step:

  1. Initialize: α1(i)=πi⋅bi(O1)\alpha_1(i) = \pi_i \cdot b_i(O_1) — start probability times emission probability.
  2. Recurse: αt+1(j)=[∑i=1Nαt(i)⋅aij]⋅bj(Ot+1)\alpha_{t+1}(j) = \left[\sum_{i=1}^{N} \alpha_t(i) \cdot a_{ij}\right] \cdot b_j(O_{t+1}) — sum all paths into state jj, then multiply by the emission.
  3. Terminate: P(O∣λ)=∑i=1NαT(i)P(O \mid \lambda) = \sum_{i=1}^{N} \alpha_T(i).

Think of it as water flowing through a pipe network: at each time step, the flow arriving at state jj is the total from all incoming pipes, multiplied by how likely jj is to produce the observed sound.

αt+1(j)=[∑i=1Nαt(i)⋅aij]⋅bj(Ot+1)\alpha_{t+1}(j) = \left[\sum_{i=1}^{N} \alpha_t(i) \cdot a_{ij}\right] \cdot b_j(O_{t+1})
Forward recursion — the engine of evaluation — Sum the incoming α's weighted by transition probabilities, then scale by the emission probability of the observed symbol. This collapses N^T paths into N²T operations.
Open in Lab
Step through the forward algorithm on a 3-state weather HMM. Watch the α table fill column by column.
The demo wakes as you arrive…

The Backward algorithm is the mirror image: define βt(i)=P(Ot+1,…,OT∣qt=Si,λ)\beta_t(i) = P(O_{t+1}, \ldots, O_T \mid q_t = S_i, \lambda) — the probability of the future observations given that we're in state ii now. It recurses backward from time TT to time 1. On its own, the backward variable isn't useful for evaluation, but combined with α\alpha it unlocks the probability of being in any state at any time: γt(i)=P(qt=Si∣O,λ)=αt(i)βt(i)P(O∣λ)\gamma_t(i) = P(q_t = S_i \mid O, \lambda) = \frac{\alpha_t(i) \beta_t(i)}{P(O \mid \lambda)}. This posterior is the key ingredient for the learning algorithm (Problem 3).

Problem 2: the Viterbi algorithm

The forward algorithm sums over all paths — good for scoring, but it doesn't tell us which path is most likely. The Viterbi algorithm is almost identical in structure, except it replaces the sum with a max:

δt(j)=max⁡i[δt−1(i)⋅aij]⋅bj(Ot)\delta_t(j) = \max_{i} \left[\delta_{t-1}(i) \cdot a_{ij}\right] \cdot b_j(O_t)

Instead of summing all incoming paths, we keep only the best one — and store a backpointer ψt(j)=arg⁡max⁡i[δt−1(i)⋅aij]\psi_t(j) = \arg\max_i [\delta_{t-1}(i) \cdot a_{ij}] so we can trace back the winning path at the end.

The metaphor is a race: at each checkpoint, every runner (state) remembers only who handed them the baton fastest. At the finish line, we trace back through handoffs to reconstruct the fastest relay team.

δt(j)=max⁡1≤i≤N[δt−1(i)⋅aij]⋅bj(Ot)\delta_t(j) = \max_{1 \le i \le N} \left[\delta_{t-1}(i) \cdot a_{ij}\right] \cdot b_j(O_t)
Viterbi recursion — max replaces sum — Identical to the forward recursion but with max instead of Σ. The backpointer ψ records which predecessor won, enabling traceback of the optimal path.
Open in Lab
Watch Viterbi trace the most likely state path. Arrows show backpointers — follow them from the end to reconstruct the best sequence.
The demo wakes as you arrive…

Problem 3: learning with Baum-Welch (EM)

How do we find the best parameters (A,B,π)(A, B, \pi) when we only have observation sequences and no labeled state sequences? This is a classic incomplete-data problem, and the Baum-Welch algorithm — a special case of the — solves it iteratively:

E-step (Expectation): Using the current model λ\lambda, compute the expected number of times each transition i→ji \to j is used, and the expected number of times each symbol kk is emitted from state jj. These expectations come from the γ\gamma and ξ\xi posteriors, which use both the forward (α\alpha) and backward (β\beta) variables.

M-step (Maximization): Re-estimate AA, BB, and π\pi by normalizing those expected counts into probabilities.

Repeat E and M until the likelihood P(O∣λ)P(O \mid \lambda) converges. Baum et al. proved a remarkable guarantee: P(O∣λˉ)≥P(O∣λ)P(O \mid \bar{\lambda}) \geq P(O \mid \lambda) — every iteration improves the likelihood or stays the same. The model climbs the likelihood surface monotonically until it reaches a local maximum.

Open in Lab
Watch Baum-Welch iterate. The likelihood climbs monotonically with each EM cycle. Try different initializations.
The demo wakes as you arrive…

HMMs for speech: left-right models

Speech unfolds in time — you don't jump from the end of a word back to its beginning. This temporal constraint motivates left-right (or Bakis) HMMs, where the state index can only stay the same or increase: aij=0a_{ij} = 0 for j<ij < i. The system can linger in a state (modeling duration) or advance, but never retreat.

For speech recognition, each word in the gets its own left-right HMM, typically with 5–10 states. The states loosely correspond to phonetic segments: the initial consonant, the vowel, the final consonant. Each state's emission distribution captures the spectral characteristics of that segment.

Recognition works by running the evaluation problem (Problem 1) for every word model and choosing the word whose model gives the highest score: W∗=arg⁡max⁡wP(O∣λw)W^* = \arg\max_w P(O \mid \lambda_w). The observation sequence OO comes from analyzing the speech signal — typically extracting features like LPC cepstral coefficients every 10 milliseconds, then vector-quantizing them.

Open in Lab
A left-right HMM for the word "six". States only move forward through the phonetic segments. Click a state to hear its spectral profile.
The demo wakes as you arrive…

Three levels of speech recognition

Rabiner demonstrates HMMs on three progressively harder tasks:

Isolated word recognition — the simplest case. The speaker says one word at a time with pauses between words. Train one HMM per vocabulary word, score each model against the input, pick the winner. Error rates below 1% were achieved on digit recognition.

Connected word recognition — the speaker says a sequence of words without pauses, but from a known small vocabulary (like digit strings "one five seven"). The system concatenates word-level HMMs into a network and finds the best path through it using a modified Viterbi search.

Continuous speech recognition — unrestricted, natural speech with a large vocabulary and complex grammar. This requires sub-word units (phonemes or triphones), language models to constrain search, and sophisticated decoding strategies. The HMM framework scales to all three levels because the same algorithms (forward, Viterbi, Baum-Welch) apply regardless of granularity.

Open in Lab
Compare isolated, connected, and continuous speech recognition. See how HMM complexity grows with each level.
The demo wakes as you arrive…

The algorithms in code

Forward algorithm and Viterbi decoding, completepython

Simplified to show the idea — not the real implementation.

import numpy as np

def forward(A, B, pi, O):
    """Forward algorithm: compute P(O | lambda).
    A: (N, N) transition matrix
    B: (N, M) emission matrix
    pi: (N,) initial distribution
    O: (T,) observation indices
    """
    N = A.shape[0]
    T = len(O)
    alpha = np.zeros((T, N))

    # Initialize
    alpha[0] = pi * B[:, O[0]]

    # Recurse
    for t in range(T - 1):
        for j in range(N):
            alpha[t+1, j] = np.sum(alpha[t] * A[:, j]) * B[j, O[t+1]]

    # Terminate: P(O | lambda)
    return np.sum(alpha[-1]), alpha

def viterbi(A, B, pi, O):
    """Viterbi: find the most likely state sequence.
    Returns: best_path, best_prob
    """
    N = A.shape[0]
    T = len(O)
    delta = np.zeros((T, N))
    psi = np.zeros((T, N), dtype=int)   # backpointers

    # Initialize
    delta[0] = pi * B[:, O[0]]

    # Recurse: max replaces sum
    for t in range(1, T):
        for j in range(N):
            scores = delta[t-1] * A[:, j]
            psi[t, j] = np.argmax(scores)       # who handed baton fastest
            delta[t, j] = scores[psi[t, j]] * B[j, O[t]]

    # Traceback
    path = np.zeros(T, dtype=int)
    path[-1] = np.argmax(delta[-1])
    for t in range(T - 2, -1, -1):
        path[t] = psi[t + 1, path[t + 1]]

    return path, np.max(delta[-1])

Practical considerations: scaling and continuous observations

Two practical issues arise when implementing HMMs:

Numerical underflow. As TT grows, the forward variable αt(i)\alpha_t(i) becomes astronomically small — products of many probabilities. The standard fix is to scale (normalize) α\alpha at each time step, dividing by ct=∑iαt(i)c_t = \sum_i \alpha_t(i). The log-likelihood is then log⁡P(O∣λ)=−∑tlog⁡ct\log P(O \mid \lambda) = -\sum_t \log c_t. This keeps all numbers in a manageable range without changing the final answer.

Continuous observations. The discrete observation model (MM symbols from a codebook) limits resolution. For richer modeling, replace the discrete emission BB with Gaussian mixture models (GMMs): bj(x)=∑m=1Mjcjm N(x;μjm,Σjm)b_j(\mathbf{x}) = \sum_{m=1}^{M_j} c_{jm} \, \mathcal{N}(\mathbf{x}; \boldsymbol{\mu}_{jm}, \boldsymbol{\Sigma}_{jm}). Each state emits continuous vectors drawn from a mixture of Gaussians. The Baum-Welch re-estimation formulas generalize naturally to update the means, covariances, and mixture weights.

Why it mattered

  1. 1966

    Baum & Petrie

    Introduced the mathematical foundations of HMMs — statistical estimation of probabilistic functions of Markov chains.

  2. 1967

    Viterbi algorithm

    Andrew Viterbi published his decoding algorithm for convolutional codes. Later adopted as the standard HMM decoding method.

  3. 1975

    Baker & Jelinek (CMU & IBM)

    First applied HMMs to speech recognition, demonstrating continuous speech recognition with statistical language models.

  4. 1989

    Rabiner's tutorial

    Unified HMM theory and practice in one definitive reference. Over 30,000 citations. Trained a generation of speech and ML researchers.

  5. 2001

    CRFs (Lafferty et al.)

    Conditional Random Fields addressed HMM limitations — discriminative training, no independence assumption on observations.

  6. 2012

    Deep learning replaces HMMs

    Deep neural networks combined with CTC and attention surpassed HMM-based systems in speech recognition, ending HMMs' two-decade reign.

The mathematical DNA of HMMs lives on. The CRF is a discriminative cousin that removed HMMs' observation independence assumption. DeepSpeech 2 replaced the entire HMM pipeline with end-to-end neural networks. But the conceptual framework — latent states, transition dynamics, observation models, and the evaluate/decode/learn trichotomy — remains the vocabulary of sequential modeling.

CitationRabiner, L. R.. A Tutorial on Hidden Markov Models and Selected Applications in Speech Recognition. Proceedings of the IEEE, 1989.

Terms in this paper