隐马尔可夫模型(Hidden Markov Model, HMM)是一种能够对隐藏状态序列和观测序列进行联合建模的统计模型。它在语音识别、自然语言处理、生物信息(基因序列分析)及很多时序预测领域都有广泛的应用。本教程将以通俗易懂的方式详细介绍从马尔可夫链到隐马尔可夫模型,并给出核心算法和典型的数学公式推导,力求让读者更好地理解其原理。
一个随机过程 { X t } \{X_t\} {Xt}(这里 t t t 可以是离散时刻)满足马尔可夫性质,表示当前状态对未来状态的影响只与当前状态本身有关,而与过去的历史状态无关。用数学语言描述:
P ( X t + 1 = x ∣ X 1 = x 1 , X 2 = x 2 , … , X t = x t ) = P ( X t + 1 = x ∣ X t = x t ) . P\bigl(X_{t+1} = x \,\big\vert\, X_1 = x_1, X_2 = x_2, \dots, X_t = x_t\bigr) = P\bigl(X_{t+1} = x \,\big\vert\, X_t = x_t\bigr). P(Xt+1=x X1=x1,X2=x2,…,Xt=xt)=P(Xt+1=x Xt=xt).
对于“一阶马尔可夫链”,此性质足够描述。若需要考虑记忆性更长的过程,可扩展为高阶马尔可夫链。
马尔可夫链状态空间设为 S = { s 1 , s 2 , … , s N } S = \{s_1, s_2, \dots, s_N\} S={s1,s2,…,sN}。定义状态转移概率:
a i j = P ( X t + 1 = s j ∣ X t = s i ) , ∑ j = 1 N a i j = 1. a_{ij} = P(X_{t+1} = s_j \mid X_t = s_i), \quad \sum_{j=1}^N a_{ij} = 1. aij=P(Xt+1=sj∣Xt=si),j=1∑Naij=1.
如果我们有初始分布 π = ( π 1 , π 2 , … , π N ) \pi = (\pi_1, \pi_2, \dots, \pi_N) π=(π1,π2,…,πN),其中
π i = P ( X 1 = s i ) , ∑ i = 1 N π i = 1 , \pi_i = P(X_1 = s_i), \quad \sum_{i=1}^N \pi_i = 1, πi=P(X1=si),i=1∑Nπi=1,
那么,对于时刻 1 1 1 到 T T T 的状态序列 ( X 1 , X 2 , … , X T ) (X_1, X_2, \dots, X_T) (X1,X2,…,XT),它的联合概率可写为:
P ( X 1 = s i 1 , X 2 = s i 2 , … , X T = s i T ) = π i 1 a i 1 i 2 a i 2 i 3 ⋯ a i T − 1 i T . P(X_1 = s_{i_1}, X_2 = s_{i_2}, \dots, X_T = s_{i_T}) = \pi_{i_1}\,a_{i_1 i_2}\,a_{i_2 i_3}\,\cdots\,a_{i_{T-1} i_T}. P(X1=si1,X2=si2,…,XT=siT)=πi1ai1i2ai2i3⋯aiT−1iT.
在普通马尔可夫链中,状态 X t X_t Xt 是可直接观察的。而隐马尔可夫模型提出:每个时刻 t t t 的状态 X t X_t Xt 是不可见(隐藏的),但我们可以观测到一个与之相关的随机变量(或向量) O t O_t Ot。因此,实际观测到的是序列 ( O 1 , O 2 , … , O T ) (O_1,O_2,\dots,O_T) (O1,O2,…,OT),而状态序列 ( X 1 , X 2 , … , X T ) (X_1,X_2,\dots,X_T) (X1,X2,…,XT) 就是 “隐”的马尔可夫链。
一个HMM由如下参数刻画:
常将这些参数记为 λ = ( A , B , π ) \lambda = (A, B, \pi) λ=(A,B,π)。
在HMM中,如果我们同时考虑状态序列 X X X 和观测序列 O O O,它们的联合概率可写为:
P ( X , O ∣ λ ) = P ( X 1 ) ∏ t = 2 T P ( X t ∣ X t − 1 ) ∏ t = 1 T P ( O t ∣ X t ) . P(X, O \mid \lambda) = P(X_1)\,\prod_{t=2}^T P(X_t \mid X_{t-1}) \,\prod_{t=1}^T P(O_t \mid X_t). P(X,O∣λ)=P(X1)t=2∏TP(Xt∣Xt−1)t=1∏TP(Ot∣Xt).
展开后,我们有:
P ( X , O ∣ λ ) = π x 1 ∏ t = 1 T − 1 a x t , x t + 1 ∏ t = 1 T b x t ( o t ) . P(X, O \mid \lambda) = \pi_{x_1}\,\prod_{t=1}^{T-1} a_{x_t, x_{t+1}} \,\prod_{t=1}^{T} b_{x_t}(o_t). P(X,O∣λ)=πx1t=1∏T−1axt,xt+1t=1∏Tbxt(ot).
这里 x t x_t xt 表示时刻 t t t 的隐藏状态在 { 1 , … , N } \{1,\dots,N\} {1,…,N}中的具体取值; o t o_t ot 表示时刻 t t t 的观测值在观测集合中的索引(或具体向量)。
在时刻 t t t,“真实”状态为 X t X_t Xt,但我们只能观测到 O t O_t Ot。贯穿整个序列有长度为 T T T 的 { O 1 , O 2 , … , O T } \{O_1,O_2,\dots,O_T\} {O1,O2,…,OT} 和 { X 1 , X 2 , … , X T } \{X_1,X_2,\dots,X_T\} {X1,X2,…,XT}。
在实际应用中,我们常常会遇到以下三个核心问题:
我们先考察:如何快速计算给定HMM( λ \lambda λ)生成观测序列 O O O 的概率 P ( O ∣ λ ) P(O \mid \lambda) P(O∣λ)?这就是评估问题,可用前向-后向算法在多项式时间内完成。
定义“前向变量” α t ( i ) \alpha_t(i) αt(i):
α t ( i ) = P ( o 1 , o 2 , … , o t , X t = s i ∣ λ ) . \alpha_t(i) = P(o_1, o_2, \dots, o_t, X_t = s_i \mid \lambda). αt(i)=P(o1,o2,…,ot,Xt=si∣λ).
即“从时刻1到时刻 t t t的所有观测值为 o 1 , … , o t o_1,\dots,o_t o1,…,ot,并且时刻 t t t的隐藏状态为 s i s_i si”的联合概率。
初始化:
α 1 ( i ) = π i b i ( o 1 ) , 1 ≤ i ≤ N . \alpha_1(i) = \pi_i \, b_i(o_1), \quad 1 \le i \le N. α1(i)=πibi(o1),1≤i≤N.
因为在时刻1,我们处于状态 s i s_i si的概率是 π i \pi_i πi,同时观测到 o 1 o_1 o1的概率是 b i ( o 1 ) b_i(o_1) bi(o1)。
递推:
α t + 1 ( j ) = [ ∑ i = 1 N α t ( i ) a i j ] b j ( o t + 1 ) , 1 ≤ j ≤ N , 1 ≤ t ≤ T − 1. \alpha_{t+1}(j) = \Bigl[\sum_{i=1}^N \alpha_t(i)\, a_{ij}\Bigr] \, b_j(o_{t+1}), \quad 1 \le j \le N,\ 1\le t \le T-1. αt+1(j)=[i=1∑Nαt(i)aij]bj(ot+1),1≤j≤N, 1≤t≤T−1.
这里 ∑ i = 1 N α t ( i ) a i j \sum_{i=1}^N \alpha_t(i)\, a_{ij} ∑i=1Nαt(i)aij 表示从时刻 t t t的所有可能状态转移到时刻 t + 1 t+1 t+1状态 s j s_j sj的联合概率之和,然后再乘以在状态 s j s_j sj下产生观测 o t + 1 o_{t+1} ot+1的概率。
终止:
P ( O ∣ λ ) = ∑ i = 1 N α T ( i ) . P(O \mid \lambda) = \sum_{i=1}^N \alpha_T(i). P(O∣λ)=i=1∑NαT(i).
即时刻 T T T可能处于任何状态下的联合概率之和。
从而可得观测序列的概率。该算法时间复杂度为 O ( N 2 T ) O(N^2 T) O(N2T)。
“后向算法”同样能得到 P ( O ∣ λ ) P(O \mid \lambda) P(O∣λ),定义“后向变量” β t ( i ) \beta_t(i) βt(i):
β t ( i ) = P ( o t + 1 , o t + 2 , … , o T ∣ X t = s i , λ ) . \beta_t(i) = P(o_{t+1}, o_{t+2}, \dots, o_T \mid X_t = s_i, \lambda). βt(i)=P(ot+1,ot+2,…,oT∣Xt=si,λ).
即“在时刻 t t t处于状态 s i s_i si时,后面时刻 t + 1 t+1 t+1到 T T T的观测序列为 o t + 1 , … , o T o_{t+1},\dots,o_T ot+1,…,oT的概率”。
初始化:
β T ( i ) = 1 , 1 ≤ i ≤ N . \beta_T(i) = 1, \quad 1 \le i \le N. βT(i)=1,1≤i≤N.
递推:
β t ( i ) = ∑ j = 1 N a i j b j ( o t + 1 ) β t + 1 ( j ) , 1 ≤ i ≤ N , t = T − 1 , … , 1. \beta_t(i) = \sum_{j=1}^N a_{ij} \, b_j(o_{t+1}) \, \beta_{t+1}(j), \quad 1 \le i \le N, \ t = T-1,\dots,1. βt(i)=j=1∑Naijbj(ot+1)βt+1(j),1≤i≤N, t=T−1,…,1.
终止:
P ( O ∣ λ ) = ∑ i = 1 N π i b i ( o 1 ) β 1 ( i ) . P(O \mid \lambda) = \sum_{i=1}^N \pi_i \, b_i(o_1) \, \beta_1(i). P(O∣λ)=i=1∑Nπibi(o1)β1(i).
和前向算法一样,后向算法也只需要 O ( N 2 T ) O(N^2 T) O(N2T) 的时间。
在后面谈学习问题时,我们还会用到前向-后向算法来求各时刻处于某状态的后验概率等量。常见定义有:
这两个量可用 α t ( i ) \alpha_t(i) αt(i) 与 β t ( i ) \beta_t(i) βt(i) 以及观测概率进行计算。例如,
γ t ( i ) = P ( X t = s i ∣ O , λ ) = α t ( i ) β t ( i ) P ( O ∣ λ ) . \gamma_t(i) = P(X_t = s_i \mid O, \lambda) = \frac{\alpha_t(i)\,\beta_t(i)}{P(O \mid \lambda)}. γt(i)=P(Xt=si∣O,λ)=P(O∣λ)αt(i)βt(i).
ξ t ( i , j ) = P ( X t = s i , X t + 1 = s j ∣ O , λ ) = α t ( i ) a i j b j ( o t + 1 ) β t + 1 ( j ) P ( O ∣ λ ) . \xi_t(i,j) = P(X_t = s_i, X_{t+1} = s_j \mid O, \lambda) = \frac{\alpha_t(i)\, a_{ij}\, b_j(o_{t+1})\, \beta_{t+1}(j)}{P(O \mid \lambda)}. ξt(i,j)=P(Xt=si,Xt+1=sj∣O,λ)=P(O∣λ)αt(i)aijbj(ot+1)βt+1(j).
解码问题想要得到的是:给定观测序列 O O O 和模型 λ \lambda λ,哪一条隐藏状态序列 X = ( X 1 , … , X T ) X=(X_1,\dots,X_T) X=(X1,…,XT) 最有可能出现?即
X ^ = arg max X P ( X ∣ O , λ ) . \hat{X} = \arg\max_X P(X \mid O,\lambda). X^=argXmaxP(X∣O,λ).
因为在HMM下等价于最大化 P ( X , O ∣ λ ) P(X, O \mid \lambda) P(X,O∣λ),所以可用维特比算法进行动态规划求解。
定义“维特比变量” δ t ( i ) \delta_t(i) δt(i):
δ t ( i ) = max X 1 , … , X t − 1 P ( X 1 , … , X t − 1 , X t = s i , o 1 , … , o t ∣ λ ) . \delta_t(i) = \max_{X_1,\dots,X_{t-1}} P(X_1,\dots,X_{t-1}, X_t=s_i,\; o_1,\dots,o_t \mid \lambda). δt(i)=X1,…,Xt−1maxP(X1,…,Xt−1,Xt=si,o1,…,ot∣λ).
初始化:
δ 1 ( i ) = π i b i ( o 1 ) . \delta_1(i) = \pi_i \, b_i(o_1). δ1(i)=πibi(o1).
递推:
δ t + 1 ( j ) = [ max 1 ≤ i ≤ N δ t ( i ) a i j ] b j ( o t + 1 ) . \delta_{t+1}(j) = \Bigl[\max_{1\le i \le N} \delta_t(i)\,a_{ij}\Bigr] \, b_j(o_{t+1}). δt+1(j)=[1≤i≤Nmaxδt(i)aij]bj(ot+1).
并令
ψ t + 1 ( j ) = arg max 1 ≤ i ≤ N δ t ( i ) a i j . \psi_{t+1}(j) = \arg\max_{1 \le i \le N} \delta_t(i)\,a_{ij}. ψt+1(j)=arg1≤i≤Nmaxδt(i)aij.
这里 ψ t + 1 ( j ) \psi_{t+1}(j) ψt+1(j) 是记录“最优路径”从上一时刻哪个状态转移而来。
终止:
P ∗ = max 1 ≤ i ≤ N δ T ( i ) , X T ∗ = arg max 1 ≤ i ≤ N δ T ( i ) . P^* = \max_{1\le i \le N} \delta_T(i), \quad X_T^* = \arg\max_{1 \le i \le N} \delta_T(i). P∗=1≤i≤NmaxδT(i),XT∗=arg1≤i≤NmaxδT(i).
回溯:根据 ψ t ( ⋅ ) \psi_t(\cdot) ψt(⋅) 从后向前逐步找出最优状态序列。
当HMM的参数 λ \lambda λ(即 A , B , π A, B, \pi A,B,π)未知时,我们可以通过观测序列 O O O 来“训练”或“学习”这些参数,使得该模型在一定意义下“最好地”解释观测数据。最常用的方法是Baum-Welch算法,可视为对HMM应用EM思想。
令 λ ( m ) \lambda^{(m)} λ(m) 表示第 m m m次迭代得到的模型参数。
在一次迭代中,“E步”计算基于 λ ( m ) \lambda^{(m)} λ(m) 时的后验概率 γ t ( i ) \gamma_t(i) γt(i) 和 ξ t ( i , j ) \xi_t(i,j) ξt(i,j);“M步”则更新下式:
初始状态分布:
π i ( m + 1 ) = γ 1 ( i ) . \pi_i^{(m+1)} = \gamma_1(i). πi(m+1)=γ1(i).
状态转移概率:
a i j ( m + 1 ) = ∑ t = 1 T − 1 ξ t ( i , j ) ∑ t = 1 T − 1 γ t ( i ) . a_{ij}^{(m+1)} = \frac{\sum_{t=1}^{T-1} \xi_t(i,j)}{\sum_{t=1}^{T-1} \gamma_t(i)}. aij(m+1)=∑t=1T−1γt(i)∑t=1T−1ξt(i,j).
观测概率(离散情况):
b j ( m + 1 ) ( k ) = ∑ t = 1 T 1 ( o t = v k ) γ t ( j ) ∑ t = 1 T γ t ( j ) , b_j^{(m+1)}(k) = \frac{\sum_{t=1}^T \mathbf{1}(o_t = v_k)\,\gamma_t(j)}{\sum_{t=1}^T \gamma_t(j)}, bj(m+1)(k)=∑t=1Tγt(j)∑t=1T1(ot=vk)γt(j),
其中 1 ( ⋅ ) \mathbf{1}(\cdot) 1(⋅) 为指示函数,若 o t = v k o_t = v_k ot=vk 则为1,否则为0。
对于连续情况,会对 γ t ( j ) \gamma_t(j) γt(j) 做加权似然估计,具体地估计高斯均值、方差等混合参数。
通过多次迭代(E步 + M步),参数会收敛到某个局部极值,从而得到可以解释训练序列的HMM参数。
以下为一个简化的离散观测HMM示例(Python),演示核心:前向、后向和维特比算法。实际应用中通常还需数值稳定处理(例如取对数规避下溢),以及Baum-Welch学习等部分。
import numpy as np
class DiscreteHMM:
def __init__(self, A, B, pi):
"""
A: 状态转移矩阵 (N x N)
B: 观测概率矩阵 (N x M),B[j,k] 表示在状态 j 时观测到 k 的概率
pi: 初始状态概率向量 (N,)
"""
self.A = np.array(A) # (N, N)
self.B = np.array(B) # (N, M)
self.pi = np.array(pi) # (N,)
self.N = self.A.shape[0] # 状态数 N
self.M = self.B.shape[1] # 观测种类数 M
def forward(self, O):
"""
前向算法计算 P(O | 模型)
O: 观测序列 (长度 T 的离散索引列表),如 [0, 2, 1, ...]
返回: P(O | 模型)
"""
T = len(O)
alpha = np.zeros((T, self.N))
# 初始化
alpha[0, :] = self.pi * self.B[:, O[0]]
# 递推
for t in range(1, T):
for j in range(self.N):
# alpha[t, j] = sum_{i=1 to N}(alpha[t-1,i] * A[i,j]) * B[j,O[t]]
alpha[t, j] = np.dot(alpha[t-1, :], self.A[:, j]) * self.B[j, O[t]]
# 终止:把时刻 T 所有状态下的概率加总
return np.sum(alpha[T-1, :])
def backward(self, O):
"""
后向算法计算 P(O | 模型)
O: 观测序列
返回: P(O | 模型)
"""
T = len(O)
beta = np.zeros((T, self.N))
# 初始化
beta[T-1, :] = 1.0
# 递推
for t in range(T-2, -1, -1):
for i in range(self.N):
# beta[t,i] = sum_{j=1 to N}(A[i,j] * B[j,O[t+1]] * beta[t+1,j])
beta[t, i] = np.sum(self.A[i, :] * self.B[:, O[t+1]] * beta[t+1, :])
# 终止:使用初始状态概率 pi 和 B[:,O[0]] 以及 beta[0,:]
return np.sum(self.pi * self.B[:, O[0]] * beta[0, :])
def viterbi(self, O):
"""
维特比算法求最优状态序列
O: 观测序列
返回: (最佳路径概率, 最优状态序列)
"""
T = len(O)
delta = np.zeros((T, self.N))
psi = np.zeros((T, self.N), dtype=int)
# 初始化
delta[0, :] = self.pi * self.B[:, O[0]]
# 递推
for t in range(1, T):
for j in range(self.N):
# 对各 i, 取 argmax (delta[t-1,i] * A[i,j])
temp_vals = delta[t-1, :] * self.A[:, j]
psi[t, j] = np.argmax(temp_vals)
delta[t, j] = np.max(temp_vals) * self.B[j, O[t]]
# 终止:找到时刻 T 最可能的最后状态
p_star = np.max(delta[T-1, :])
best_last_state = np.argmax(delta[T-1, :])
# 回溯:依靠 psi[] 把最优路径检索出来
best_path = [best_last_state]
for t in range(T-1, 0, -1):
best_path.insert(0, psi[t, best_last_state])
best_last_state = psi[t, best_last_state]
return p_star, best_path
if __name__ == "__main__":
# 示例模型参数
A = [
[0.7, 0.3],
[0.4, 0.6]
]
B = [
[0.5, 0.5], # 状态0时,观测0和1的概率
[0.1, 0.9] # 状态1时,观测0和1的概率
]
pi = [0.6, 0.4]
hmm = DiscreteHMM(A, B, pi)
# 示例观测序列 O (长度 T=4),假设观测字典是 {0,1}
O = [0, 1, 1, 0]
# 1) 前向算法
prob_forward = hmm.forward(O)
print(f"Forward算法计算得到的序列概率: {prob_forward}")
# 2) 后向算法
prob_backward = hmm.backward(O)
print(f"Backward算法计算得到的序列概率: {prob_backward}")
# 3) 维特比算法
p_star, best_path = hmm.viterbi(O)
print(f"Viterbi算法的最优路径概率: {p_star}")
print(f"Viterbi算法的最优状态序列: {best_path}")