杰瑞科技汇

hmmlearn Python教程怎么学?

hmmlearn Python 完整教程

hmmlearn 是 Python 中一个强大的库,用于实现隐马尔可夫模型,它基于 scikit-learn 的 API 设计,因此如果你熟悉 scikit-learn,上手会非常快,HMM 在语音识别、自然语言处理、金融分析、生物信息学等领域有着广泛的应用。

hmmlearn Python教程怎么学?-图1
(图片来源网络,侵删)

目录

  1. 第一部分:核心概念回顾
    • 什么是隐马尔可夫模型?
    • HMM 的三大核心问题
  2. 第二部分:hmmlearn 快速入门
    • 安装
    • API 设计理念
    • 创建并训练第一个 HMM
  3. 第三部分:深入 hmmlearn 模型
    • GaussianHMM:连续观测值
    • MultinomialHMM:离散观测值
    • GMMHMM:混合高斯观测值
    • CategoricalHMM:替代 MultinomialHMM 的新版本
  4. 第四部分:实战项目:股票市场趋势分析
    • 项目目标
    • 数据准备
    • 模型训练
    • 结果解读与可视化
  5. 第五部分:高级技巧与注意事项
    • 模型选择:如何选择 HMM 的状态数 n_components
    • 避免局部最优:多次初始化
    • 模型评估:对数似然值与 AIC/BIC
    • 预测未来的状态
  6. 第六部分:总结与资源

第一部分:核心概念回顾

在写代码之前,我们快速回顾一下 HMM 的基本思想。

什么是隐马尔可夫模型?

HMM 是一个统计模型,用来描述一个含有隐藏的未知参数的马尔可夫过程,它包含两个序列:

  1. 状态序列:一个隐藏的、我们无法直接观察到的序列,每天的真实天气(晴天、雨天)。
  2. 观测序列:一个我们能观察到的序列,它由状态序列生成,我们每天能看到的“是否带伞”。

HMM 有两个核心假设:

  • 马尔可夫假设:当前状态只依赖于前一个状态(一阶马尔可夫链)。
  • 观测独立性假设:当前时刻的观测值只依赖于当前的状态。

HMM 的三大核心问题

hmmlearn 主要帮助我们解决以下三个问题:

hmmlearn Python教程怎么学?-图2
(图片来源网络,侵删)
  1. 评估问题:给定一个模型 λ=(A, B, π) 和一个观测序列 O,如何计算这个模型生成该观测序列的概率 P(O|λ)

    • hmmlearn 使用前向算法高效地解决这个问题。
    • 用途:可以用来比较不同模型生成同一观测序列的可能性,从而选择最佳模型。
  2. 解码问题:给定一个模型 λ=(A, B, π) 和一个观测序列 O,如何找到最有可能产生这个观测序列的状态序列 Q*

    • hmmlearn 使用维特比算法来解决。
    • 用途:这是 HMM 最常见的应用,例如在语音识别中,根据声音信号序列推断出最可能的文字序列。
  3. 学习问题:给定一个观测序列 O,如何调整模型参数 λ=(A, B, π),使得模型生成该观测序列的概率 P(O|λ) 最大?

    • hmmlearn 使用鲍姆-韦尔奇算法(一种 EM 算法的变体)来解决。
    • 用途:这是模型训练的过程,通过数据“学习”出 HMM 的内部参数。

第二部分:hmmlearn 快速入门

安装

hmmlearn 可以通过 pip 轻松安装:

pip install hmmlearn

API 设计理念

hmmlearn 的 API 设计与 scikit-learn 高度一致,主要步骤如下:

  1. 导入模型from hmmlearn import hmm
  2. 实例化模型model = hmm.GaussianHMM(n_components=2)
  3. 训练模型model.fit(X)X 是观测数据。
  4. 使用模型
    • model.predict(X):解码问题(维特比算法)。
    • model.score(X):评估问题(计算对数似然值)。
    • model.sample(n_samples=100):生成新的观测序列和对应的状态序列。

创建并训练第一个 HMM

让我们从一个最简单的例子开始:模拟一个有两个隐藏状态的 HMM,并训练它。

import numpy as np
from hmmlearn import hmm
# 1. 准备数据
# 假设我们有一些观测数据,例如一天中不同时间的温度
# 这里我们人为生成一些数据来模拟
np.random.seed(42)
# 创建一个带有2个状态的HMM模型
# n_components: 隐藏状态的数量
model = hmm.GaussianHMM(n_components=2, covariance_type="diag", n_iter=100)
# 我们需要告诉模型观测数据的形状,通常是 (n_samples, n_features)
# 这里我们模拟一维数据,n_features=1
# model.startprob_ = np.array([0.6, 0.4]) # 初始状态概率 (可选)
# model.transmat_ = np.array([[0.7, 0.3], [0.4, 0.6]]) # 状态转移矩阵 (可选)
# model.means_ = np.array([[0.0], [10.0]]) # 每个状态的高斯分布均值 (可选)
# model.covars_ = np.array([[1.0], [1.0]]) # 每个状态的高斯分布方差 (可选)
# 2. 训练模型
# 通常情况下,我们只有观测数据 X,而不知道状态序列 Z
# 我们让模型自己从数据中学习参数
# 为了演示,我们先让模型生成一些数据,然后再用这些数据去训练它
# (这有点像“作弊”,因为现实中我们不会有真实的模型参数)
X, Z = model.sample(1000)
# 现在我们有了观测数据 X,我们用它来训练一个新的模型
# 注意:我们通常会重新实例化一个模型,而不是用已经sample过的模型
new_model = hmm.GaussianHMM(n_components=2, covariance_type="diag", n_iter=100)
new_model.fit(X)
# 3. 查看学习到的参数
print("学习到的初始状态概率:", new_model.startprob_)
print("学习到的状态转移矩阵:\n", new_model.transmat_)
print("学习到的每个状态的均值:", new_model.means_)
print("学习到的每个状态的方差:", new_model.covars_)
# 4. 使用模型进行预测(解码)
# 假设我们有一组新的观测数据
new_X, new_Z = model.sample(20)
predicted_states = new_model.predict(new_X)
print("\n真实状态序列:", new_Z)
print("预测状态序列:", predicted_states)
# 5. 评估模型
# 计算模型生成观测序列的对数似然值
log_likelihood = new_model.score(new_X)
print("\n新观测序列的对数似然值:", log_likelihood)

代码解释

  1. 我们创建了一个 GaussianHMM 模型,假设隐藏状态为2。
  2. 我们让这个模型生成1000个样本的观测数据 X 和对应的状态序列 Z,在真实场景中,我们只有 X
  3. 我们用 X 去训练一个新的 new_modelfit 方法会自动执行学习算法(鲍姆-韦尔奇)。
  4. 训练完成后,我们可以查看模型学习到的参数(startprob_, transmat_, means_, covars_),它们应该和我们最初设定的参数很接近。
  5. predict 方法使用维特比算法找到最可能的状态序列。
  6. score 方法计算对数似然值,值越高说明模型越“喜欢”这个观测序列。

第三部分:深入 hmmlearn 模型

hmmlearn 提供了多种 HMM 模型,以适应不同类型的观测数据。

GaussianHMM:连续观测值

这是最常用的模型,适用于观测数据是连续值(如温度、股价、声音信号强度等)的情况,它假设每个隐藏状态下的观测值服从一个高斯分布。

关键参数

  • covariance_type:协方差矩阵的类型。
    • "spherical":所有维度共享相同的方差。
    • "diag":每个维度有自己的方差(对角矩阵)。最常用
    • "tied":所有状态共享同一个协方差矩阵。
    • "full":每个状态都有自己的完整的协方差矩阵。最灵活,但参数最多,容易过拟合

MultinomialHMM:离散观测值

适用于观测数据是离散整数(如掷骰子的点数、文本中的单词ID等)的情况,它假设每个隐藏状态下生成观测值的概率服从一个多项式分布。

关键参数

  • n_features:可能观测值的数量(掷骰子时 n_features=6)。

示例:模拟掷骰子 假设有两个隐藏状态:"公平的骰子""灌铅的骰子",我们观测到一系列掷骰子的结果。

import numpy as np
from hmmlearn import hmm
# 假设我们有100次掷骰子的观测结果
# 0代表1点,1代表2点,...,5代表6点
# 这里我们人为生成一些数据
np.random.seed(42)
# 创建模型
# n_components=2 (公平/灌铅), n_features=6 (骰子有6个面)
model = hmm.MultinomialHMM(n_components=2, n_iter=100)
# 设置初始参数 (为了演示,实际中模型会学习)
model.startprob_ = np.array([0.5, 0.5])
# 状态转移: 公平 -> 公平(0.9), 公平 -> 灌铅(0.1); 灌铅 -> 公平(0.2), 灌铅 -> 灌铅(0.8)
model.transmat_ = np.array([[0.9, 0.1], [0.2, 0.8]])
# 发射概率: 状态0(公平)时,每个面出现的概率均等
# 状态1(灌铅)时,6点出现的概率更高
model.emissionprob_ = np.array([[1/6, 1/6, 1/6, 1/6, 1/6, 1/6],
                                [0.1, 0.1, 0.1, 0.1, 0.1, 0.5]])
# 生成观测序列 (注意: MultinomialHMM需要2D数组)
X, Z = model.sample(100)
print("观测到的掷骰子结果 (前10个):", X[:10].T[0])
print("真实状态序列 (前10个):", Z[:10])
# 训练一个新模型
new_model = hmm.MultinomialHMM(n_components=2, n_iter=100)
# 需要将观测数据转换为整数列表
new_model.fit(X)
print("\n学习到的发射概率:\n", new_model.emissionprob_)
# 你会发现第二个状态的发射概率,最后一个值(6点)会明显高于其他值

GMMHMM:混合高斯观测值

这是 GaussianHMM 的扩展,当单个高斯分布无法很好地描述一个状态下的观测值分布时(数据是多峰的),可以使用 GMMHMM,它假设每个隐藏状态下的观测值服从一个高斯混合模型。

CategoricalHMM:替代 MultinomialHMM

CategoricalHMMhmmlearn 0.2.8 版本引入的新模型,是 MultinomialHMM 的更现代、更稳定的替代品,对于离散观测数据,推荐使用 CategoricalHMM


第四部分:实战项目:股票市场趋势分析

这是一个经典的 HMM 应用场景,我们假设股市的“隐藏状态”是“牛市”、“熊市”或“震荡市”,而我们能观测到的是每天的收盘价或收益率。

项目目标

  1. 使用历史股票收益率数据训练一个 HMM 模型。
  2. 识别出数据中隐藏的“市场状态”(如高波动、低波动)。
  3. 预测明天的市场状态。

数据准备

我们将使用 yfinance 库来获取股票数据,如果未安装,请先安装:pip install yfinance

import yfinance as yf
import numpy as np
import pandas as pd
from hmmlearn import hmm
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")
# 1. 获取数据
ticker = 'AAPL' # 苹果公司股票
data = yf.download(ticker, start='2025-01-01', end='2025-12-31')
# 2. 数据预处理
# 我们使用每日收益率作为观测数据
# 收益率 = (今日收盘价 - 昨日收盘价) / 昨日收盘价
data['Returns'] = data['Close'].pct_change().dropna()
# HMM需要2D数组,且需要去除NaN值
returns = data['Returns'].values.reshape(-1, 1)
returns = returns[~np.isnan(returns)]
# 3. 训练模型
# 我们不知道最优的状态数,这里先尝试3个状态
# covariance_type="full" 是一个比较通用的选择
model = hmm.GaussianHMM(n_components=3, covariance_type="full", random_state=42)
model.fit(returns)
# 4. 解码隐藏状态
# 使用predict方法得到每个交易日对应的最可能状态
hidden_states = model.predict(returns)
# 5. 结果可视化
# 将状态添加到原始数据中
data['Hidden State'] = np.nan
data.loc[data.index[1:], 'Hidden State'] = hidden_states # 因为收益率从第二个数据点开始
# 绘制收盘价和隐藏状态
fig, axes = plt.subplots(2, 1, figsize=(15, 10))
# 绘制收盘价
axes[0].plot(data.index, data['Close'])
axes[0].set_title(f'{ticker} Closing Price')
axes[0].set_xlabel('Date')
axes[0].set_ylabel('Price (USD)')
# 绘制隐藏状态
state_colors = ['blue', 'red', 'green'] # 为每个状态分配一个颜色
for i in range(model.n_components):
    state = (data['Hidden State'] == i)
    axes[1].scatter(data.index[state], data['Close'][state], c=state_colors[i], label=f'State {i}', alpha=0.6)
axes[1].set_title('Hidden States')
axes[1].set_xlabel('Date')
axes[1].set_ylabel('Price (USD)')
axes[1].legend()
plt.tight_layout()
plt.show()
# 6. 分析每个状态的特性
print("模型学习到的参数:")
print(f"状态转移矩阵:\n{model.transmat_}\n")
print(f"每个状态的均值 (平均收益率):\n{model.means_}\n")
print(f"每个状态的方差 (波动率):\n{model.covars_}\n")
# 根据均值和方差,我们可以给状态赋予有意义的解释
# 状态0: 高波动,状态1: 低波动,状态2: 中等波动
# 状态2的均值最高,可能是“牛市”
# 状态0的方差最大,可能是“高波动/震荡市”
# 状态1的均值和方差都较低,可能是“熊市/盘整市”
# 7. 预测下一个状态
# 使用模型预测下一个交易日的状态
last_return = returns[-1].reshape(1, -1)
next_state = model.predict(last_return)
print(f"模型预测下一个交易日的隐藏状态为: {next_state[0]}")

项目解读

  • 可视化:通过将收盘价图和隐藏状态图叠加,我们可以直观地看到模型是如何划分市场阶段的,红色区域可能对应着股价剧烈波动的时期。
  • 参数分析
    • means_:告诉我们每个状态下的平均收益率,一个正的均值可能代表“牛市”,一个负的均值代表“熊市”。
    • covars_:告诉我们每个状态下的波动性,高方差代表高波动,低方差代表稳定。
  • 预测model.predict 可以用于预测未来短期的状态。

第五部分:高级技巧与注意事项

模型选择:如何选择 HMM 的状态数 n_components

这是 HMM 应用中最关键也最困难的一步,没有完美的方法,通常采用以下策略:

  1. 领域知识:如果你对业务有了解,可以给出一个合理的范围,在分析金融市场时,3-5个状态是常见的选择。
  2. 信息准则:这是最科学的方法,我们训练一系列不同 n_components 的模型,并计算每个模型的 AIC (Akaike Information Criterion) 或 BIC (Bayesian Information Criterion),选择 AIC/BIC 值最小的模型。
    • AIC = -2 log_likelihood + 2 k
    • BIC = -2 log_likelihood + log(N) k
    • k 是模型参数的数量,N 是观测数据的数量,BIC 对模型复杂度的惩罚比 AIC 更重。
n_components_range = range(2, 7)
models = []
for n in n_components_range:
    model = hmm.GaussianHMM(n_components=n, covariance_type="full", random_state=42)
    model.fit(returns)
    models.append(model)
# 计算AIC/BIC
aics = [model.aic(returns) for model in models]
bics = [model.bic(returns) for model in models]
plt.figure(figsize=(10, 5))
plt.plot(n_components_range, aics, label='AIC')
plt.plot(n_components_range, bics, label='BIC')
plt.xlabel('Number of Components')
plt.ylabel('Score')
plt.legend()'AIC/BIC for Model Selection')
plt.show()

避免局部最优:多次初始化

鲍姆-韦尔奇算法是一种局部优化算法,可能会陷入局部最优解。hmmlearn 允许你通过 init_paramsn_init 参数来控制初始化过程。

  • init_params:指定哪些参数需要随机初始化(默认是所有参数)。
  • n_init:模型会用不同的随机初始值训练多次,并选择对数似然值最高的那次作为最终结果。
# 训练模型,使用5次不同的随机初始化
best_model = hmm.GaussianHMM(n_components=3, covariance_type="full", n_init=5, random_state=42)
best_model.fit(returns)

模型评估:对数似然值与 AIC/BIC

  • 对数似然值 (score):值越高越好,但它只衡量模型对数据的拟合程度,不考虑模型复杂度,容易过拟合。
  • AIC/BIC:在拟合度和模型复杂度之间取得平衡,值越低越好,它们更适合用于模型选择。

预测未来的状态

model.predict() 只能根据当前观测序列解码出最可能的历史状态,它不能预测未来的状态。

要预测未来的状态,你需要一个预测器,一个简单的方法是使用状态转移矩阵

  1. 获取最后一个时刻的状态 s_t
  2. 从转移矩阵 model.transmat_[s_t] 中找到概率最高的下一个状态 s_t+1
# 获取最后一个状态
last_state = hidden_states[-1]
# 预测下一个状态
next_state_probs = model.transmat_[last_state]
predicted_next_state = np.argmax(next_state_probs)
print(f"最后一个状态是: {last_state}")
print(f"下一个状态的概率分布是: {next_state_probs}")
print(f"预测的下一个状态是: {predicted_next_state}")

第六部分:总结与资源

hmmlearn 是一个功能强大且易于使用的 Python 库,用于实现隐马尔可夫模型,通过本教程,你应该已经掌握了:

  • HMM 的核心概念和三大问题。
  • hmmlearn 的基本 API 和工作流程。
  • 如何选择和使用不同的 HMM 模型 (GaussianHMM, MultinomialHMM 等)。
  • 如何通过一个完整的实战项目(股票分析)来应用 HMM。
  • 如何进行模型选择(AIC/BIC)和避免过拟合。

资源

  • 官方文档hmmlearn Documentation - 最权威的资料。
  • 原始论文:学习鲍姆-韦尔奇算法和维特比算法的经典论文。
  • 在线课程:Coursera 上的 Sequence Models 课程(吴恩达主讲)有非常棒的 HMM 讲解。
  • 相关库:对于更复杂的时间序列模型,可以了解 pomegranate 库,它也实现了 HMM 并提供了更丰富的功能。
分享:
扫描分享到社交APP
上一篇
下一篇