机器学习HMM

HMM(Hidden Markov Model,隐马尔可夫模型) 是一种用"隐状态序列 + 观测序列"描述时序数据的概率模型。

  • 蓝层 :隐状态 q 1 q 2 q 3 q4 ,按转移矩阵 A 沿时间跳转;q1 由初始分布 π 产生
  • 橙层 :观测 o 1 , o 2 , o 3 , o4 ,每个隐状态按发射矩阵 B "发射"一个观测
  • 参数λ= π AB

1. 基本设定

HMM 由五元组构成,notebook 中代码对应:

符号 含义 notebook
N 隐状态个数 N=4
M 观测符号个数 M=2
π 初始状态概率 pi
A 状态转移矩阵 A ij =P q t =j∥ q t-1=i A
B 观测概率矩阵 B ik =P o t =k∥ q t=i B

两条核心假设:

  1. 马尔可夫性:下一状态只依赖当前状态
  2. 观测独立性:当前观测只依赖当前隐状态

生成过程(notebook 的 generate):

只能看到观测 O= o 1 ... oT ,隐状态 Q= q 1 ... qT 不可见。

2. 三大基本问题(本 notebook 覆盖前两个)

问题 问什么 notebook 解法
概率计算 已知 λ= π AB ,求 PO∥λ 前向算法 prob_calc
解码(序列标注) 已知 λ O ,求最可能隐状态路径 维特比 viterbi_decode
学习 已知 O ,估计 π,A,B 本 notebook 未实现(常用 Baum-Welch)

3. 前向算法(算观测概率)

定义前向变量:

  • 初值α 1 i = π i B i o1 → alpha = pi * B:, O\[0]
  • 递推

→ notebook 中 np.sum(A:,j * alpha * Bj,o)

  • 终止P O∣λ = i α Ti → alpha.sum()

例:O= 1 0 1 00 ,得到约 0.01983。

直接枚举所有路径是 O NT ,前向降到 O T N2

4. 维特比算法(找最优隐状态路径)

目标:

用动态规划:

  • 初值δ 1 i = π i B i o1
  • 递推δ t j = maxi δ t-1 i A ijB j ot ,并用 varphi 记下最优前驱
  • 回溯 :从终点 argmaxi δ Ti 沿 varphi 往回走,得到整条路径

例:O= 1 0 1 10 → 最优路径 0,1,2,3,3

5. 一句话概括

HMM = 隐状态按马尔可夫链跳转,每个状态再"发射"一个观测;

本 notebook 用 前向 算观测似然,用 维特比 做序列标注(解码)。

6.应用场景

HMM 主要用在「观测可见、状态隐藏」的序列问题上,典型场景包括:

领域 典型应用 对应 HMM 问题
自然语言处理 中文分词、词性标注(POS)、命名实体识别 序列标注(维特比解码)
语音 语音识别(声学建模)、说话人相关序列建模 概率计算 + 解码
生物信息 DNA/蛋白质序列比对与结构预测、基因区域识别 解码 / 概率计算
金融 市场隐状态(牛/熊市)推断、波动制度切换 解码 / 学习
行为与传感 手势识别、人体活动识别、异常检测 分类 / 解码
通信与信号处理 信道解码、符号序列恢复 解码

结合你 notebook 里的内容:

  • 前向算法:判断某段观测有多像该模型(评分、过滤)
  • 维特比:给观测打上最可能的隐状态标签(分词、词性标注等最常用)

现在很多 NLP 任务已更多用 CRF、神经网络(BiLSTM-CRF、Transformer),但 HMM 仍是序列标注的经典基线,在语音、生物信息等场景里也仍常见。

7.代码实战

复制代码
import numpy as np

### 定义HMM模型
class HMM:
    def __init__(self, N, M, pi=None, A=None, B=None):
        # 可能的状态数
        self.N = N
        # 可能的观测数
        self.M = M
        # 初始状态概率向量
        self.pi = pi
        # 状态转移概率矩阵
        self.A = A
        # 观测概率矩阵
        self.B = B

    # 根据给定的概率分布随机返回数据
    def rdistribution(self, dist): 
        r = np.random.rand()
        for ix, p in enumerate(dist):
            if r < p: 
                return ix
            r -= p

    # 生成HMM观测序列
    def generate(self, T):
        # 根据初始概率分布生成第一个状态
        i = self.rdistribution(self.pi)  
        # 生成第一个观测数据
        o = self.rdistribution(self.B[i])  
        observed_data = [o]
        # 遍历生成剩下的状态和观测数据
        for _ in range(T-1):        
            i = self.rdistribution(self.A[i])
            o = self.rdistribution(self.B[i])
            observed_data.append(o)
        return observed_data


# 初始状态概率分布
pi = np.array([0.25, 0.25, 0.25, 0.25])
# 状态转移概率矩阵
A = np.array([
    [0,  1,  0, 0],
    [0.4, 0, 0.6, 0],
    [0, 0.4, 0, 0.6],
[0, 0, 0.5, 0.5]])
# 观测概率矩阵
B = np.array([
    [0.5, 0.5],
    [0.6, 0.4],
    [0.2, 0.8],
    [0.3, 0.7]])
# 可能的状态数和观测数
N = 4
M = 2
# 创建HMM实例
hmm = HMM(N, M, pi, A, B)
# 生成观测序列
print(hmm.generate(5))

1, 0, 0, 1, 0

复制代码
### 前向算法计算条件概率
def prob_calc(O):
    '''
    输入:
    O:观测序列
    输出:
    alpha.sum():条件概率
    '''
    # 初值
    alpha = pi * B[:, O[0]]
    # 递推
    for o in O[1:]:
        alpha_next = np.empty(4)
        for j in range(4):
            alpha_next[j] = np.sum(A[:,j] * alpha * B[j,o])
        alpha = alpha_next
    return alpha.sum()

# 给定观测
O = [1,0,1,0,0]
print(prob_calc(O))

0.01983169125

前向算法(Forward Algorithm) ,用来算:在已知模型 λ= π AB 时,观测序列 O 出现的概率 PO∣λ

在算什么

定义前向变量:

即:前 t 个观测已经发生,且第 t 步隐状态是 i 的联合概率。

最终:

对应代码最后的 return alpha.sum()。

和代码的对应关系

1. 初值

alpha = pi * B:, O\[0]

第一个状态按 π 出现,再按 B 发出第一个观测 O0

2. 递推

for o in O1::

for j in range(4):

alpha_nextj = np.sum(A:,j * alpha * Bj,o)

含义拆开:

  • A:,j * alpha:从所有状态 i 转移到 j ,把上一时刻的 α ti 加权求和
  • 再乘 Bj,o:状态 j 发出当前观测 o

3. 终止

return alpha.sum()

对最后一步所有状态的 α_T (i) 求和,得到整条观测序列的概率。

直观理解

不枚举所有隐状态路径(那是 O(N^T )),而是按时间一步步把概率"向前推":每一步只做 N×N 的转移累加,复杂度 O(TN^2 )。

对例子 O=1,0,1,0,0,输出约 0.01983,就是这条观测在当前 π,A,B 下的似然。

复制代码
### 序列标注问题和维特比算法
def viterbi_decode(O):
    '''
    输入:
    O:观测序列
    输出:
    path:最优隐状态路径
    '''    
    # 序列长度和初始观测
    T, o = len(O), O[0]
    # 初始化delta变量
    delta = pi * B[:, o]
    # 初始化varphi变量
    varphi = np.zeros((T, 4), dtype=int)
    path = [0] * T
    # 递推
    for i in range(1, T):
        delta = delta.reshape(-1, 1)     
        tmp = delta * A
        varphi[i, :] = np.argmax(tmp, axis=0)
        delta = np.max(tmp, axis=0) * B[:, O[i]]
    # 终止
    path[-1] = np.argmax(delta)
    # 回溯最优路径
    for i in range(T-1, 0, -1):
        path[i-1] = varphi[i, path[i]]
    return path

# 给定观测序列
O = [1,0,1,1,0]
# 输出最可能的隐状态序列
print(viterbi_decode(O))

0, 1, 2, 3, 3

维特比算法(Viterbi) ,用来做 解码 / 序列标注 :在已知模型 λ= π AB 和观测 O 时,找一条最可能的隐状态路径

和前向算法的区别

前向 prob_calc 维特比 viterbi_decode
目标 PO∣λ 找最优路径 Q
核心运算 求和 取最大 max
额外记录 不需要 用 varphi 记最优前驱,便于回溯

核心量

即:第 t 步落在状态 i ,且前缀路径概率最大时的那个值。

和代码的对应

1. 初值

delta = pi * B:, o

和前向一样,只是后面递推用 max 而不是 sum。

2. 递推

tmp = delta * A # δ_{t-1}(i) * A_{ij}

varphii, : = np.argmax(tmp, axis=0) # 记下到达 j 的最优前驱 i*

delta = np.max(tmp, axis=0) * B:, O\[i]

  • np.max(..., axis=0):对每个目标状态 j ,在所有来源 i 里取最大
  • np.argmax 写入 varphi:记下是从哪个 i 过来的(回溯要用)

3. 终止

path-1 = np.argmax(delta)

最后一步选 δT 最大的状态,作为路径终点。

4. 回溯

for i in range(T-1, 0, -1):

pathi-1 = varphii, path\[i]

从终点往前:已知 qt ,查 varphit, q_t 得到 qt-1 ,一路还原整条路径。

直观理解

每一步不为所有路径留账,只保留「到达每个状态的最好一条」;最后从终点顺着指针往回走,得到全局最优路径(在马尔可夫假设下)。

对例子 O=1,0,1,1,0,输出 0,1,2,3,3,就是该观测下最可能的隐状态序列。

相关推荐
东方小月2 小时前
从零开发一个 Coding Agent(九):实现 Agent 的工具调用闭环
人工智能·前端框架·node.js
Luhui Dev2 小时前
如何在 WorkBuddy 中使用大角几何:从 MCP 接入到 AI 几何作图
人工智能·数学·算法·agent·luhuidev
孙启超3 小时前
【AI应用开发】什么是混合检索(Hybrid Search)?向量检索 + BM25 关键词检索,适用场景与 RRF 融合原理
人工智能·缓存·llm·向量数据库·bm25·向量化·ai应用开发
The moon forgets3 小时前
Qwen团队提出Ego2Robot, 第一人称视频助力具身VLA训练新数据
人工智能·机器学习·音视频
szxinmai主板定制专家3 小时前
基于 RK3588 + Xilinx Kintex-7 FPGA 异构工业主控板设计方案
arm开发·人工智能·嵌入式硬件·fpga开发·zynq
青山科技分享3 小时前
跨境电商如何做GEO,让商品被AI搜索推荐?
大数据·人工智能·跨境电商
Cloud云卷云舒3 小时前
HaishanDB(海山)|磐维数据库|YashanDB(崖山)深度对比分析
数据库·人工智能·海山数据库·haishandb·移动云海山数据库
泰迪智能科技3 小时前
滇西科技师范学院AI人工智能实验室案例分享
人工智能·科技