目录

前言废话

介绍

两个核心假设

HMM能干什么?

原理

1 前向算法——高效算 P(X|λ)

2 Viterbi——求最优隐状态路径

3 Baum-Welch——无监督训练

代码演示

结语


前言废话

做AI相关的人肯定对HMM(Hidden Markov Model,隐马尔可夫模型)绝不会陌生:

  • 20 世纪 70 年代起它就统治了语音识别;

  • 90 年代成为分词、词性标注的“黄金标准”;

  • 今天,虽然深度学习大行其道,HMM 依旧凭借可解释性强、训练速度快、数据要求低三大优点,在工业界中小规模场景下频频露脸。

介绍

说到HMM,不得不提起马尔科夫模型(Markov Model),这部分可以参考这篇文章https://blog.csdn.net/CO_ocola/article/details/154994092?fromshare=blogdetail&sharetype=blogdetail&sharerId=154994092&sharerefer=PC&sharesource=CO_ocola&sharefrom=from_link

HMM 是含有隐含(看不见)状态的概率图模型,用来描述观测序列背后那个“看不见”的状态序列的演化规律

一句话通俗解释就是:从“看得见”的线索里,把“看不见”的变化过程猜个八九不离十。

名称 符号 维度 含义
隐含状态序列 Z = z₁…z_T T 真正驱动系统的“内因”,我们看不到
观测序列 X = x₁…x_T T 我们能拿到的“外因”
初始概率 π N×1 第一个隐状态是 i 的概率 π_i
转移矩阵 A N×N 从隐状态 i → j 的概率 a_{ij}
发射矩阵(观测概率) B N×M 在隐状态 i 下观测到符号 k 的概率 b_i(k)

其中 N = 隐状态种类数,M = 观测符号种类数。

两个核心假设

  1. 齐次马尔可夫假设:当前隐状态 z_t 只与上一隐状态 z_{t-1} 有关,与更早无关。

  2. 观测独立性假设:当前观测 x_t 只与当前隐状态 z_t 有关,与别的不相干。

HMM能干什么?

  1. 评估(Evaluation)
    给定模型 λ=(π,A,B) 和观测序列 X,求 P(X|λ)。
    用途:异常检测、置信度打分。
    算法:前向(Forward)后向(Backward),O(TN²)。

  2. 解码(Decoding)
    给定模型 λ 和观测 X,求最可能对应的隐状态序列 Z*。
    用途:分词、词性标注、语音识别对齐。
    算法:Viterbi,O(TN²)。

  3. 学习(Learning)
    只有一堆观测 X,要反推出模型参数 λ=(π,A,B)。
    用途:无监督训练、领域自适应。
    算法:Baum-Welch(EM),迭代直至收敛。

原理

1 前向算法——高效算 P(X|λ)

定义前向概率
α_t(i) = P(x₁…x_t, z_t = i | λ)

递推:
α₁(i) = π_i · b_i(x₁)
α_{t+1}(j) = [Σ_i α_t(i)·a_{ij}] · b_j(x_{t+1})

最终:
P(X|λ) = Σ_i α_T(i)

2 Viterbi——求最优隐状态路径

定义
δ_t(i) = max_{z₁…z_{t-1}} P(z₁…z_{t-1}, z_t=i, x₁…x_t | λ)

递推:
δ₁(i) = π_i · b_i(x₁)
δ_t(j) = max_i [δ_{t-1}(i)·a_{ij}] · b_j(x_t)

同时记录指针 ψ_t(j) = argmax_i [δ_{t-1}(i)·a_{ij}],最后回溯即得最优路径 Z*。

3 Baum-Welch——无监督训练

E 步:用前-后向算期望计数
M 步:按期望计数重新估计 π、A、B
迭代至对数似然增量 < ε。

代码演示

import numpy as np

# 真实模型参数
pi = np.array([0.6, 0.4])          # 初始
A  = np.array([[0.7, 0.3],
               [0.4, 0.6]])        # 转移
B  = np.array([[0.9, 0.1],         # 发射(状态0下观测0概率0.9)
               [0.2, 0.8]])        # 状态1下观测0概率0.2

# 1. 随机生成一条观测序列
T = 30
z = np.zeros(T, dtype=int)
x = np.zeros(T, dtype=int)
z[0] = np.random.choice(2, p=pi)
x[0] = np.random.choice(2, p=B[z[0]])
for t in range(1, T):
    z[t] = np.random.choice(2, p=A[z[t-1]])
    x[t] = np.random.choice(2, p=B[z[t]])

# 2. Viterbi 解码
def viterbi(pi, A, B, x):
    T = len(x)
    N = len(pi)
    delta = np.zeros((T, N))
    psi   = np.zeros((T, N), dtype=int)
    delta[0] = pi * B[:, x[0]]
    for t in range(1, T):
        for j in range(N):
            tmp = delta[t-1] * A[:, j]
            psi[t, j] = np.argmax(tmp)
            delta[t, j] = tmp[psi[t, j]] * B[j, x[t]]
    # 回溯
    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

z_hat = viterbi(pi, A, B, x)
print("真实状态:", z)
print("解码状态:", z_hat)
print("准确率  :", (z == z_hat).mean())

30个时间步达到93%准确率,可见HMM解码很靠谱。

结语

  • 数据量小、算力有限、需要可解释的场景,HMM 依旧是最具性价比的方案之一。

  • 深度学习时代,HMM 常作为 baseline、预处理模块,或与神经网络混搭(如 CTC + HMM、Transformer + 隐结构)。

掌握 HMM,你不仅收获一个“老而弥坚”的算法,更能深刻理解序列建模的本质:

“看不见”的隐变量驱动“看得见”的观测,我们所有建模努力,不过是在猜测造物主手里的那枚骰子

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐