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

目录
- 第一部分:核心概念回顾
- 什么是隐马尔可夫模型?
- HMM 的三大核心问题
- 第二部分:
hmmlearn快速入门- 安装
- API 设计理念
- 创建并训练第一个 HMM
- 第三部分:深入
hmmlearn模型GaussianHMM:连续观测值MultinomialHMM:离散观测值GMMHMM:混合高斯观测值CategoricalHMM:替代MultinomialHMM的新版本
- 第四部分:实战项目:股票市场趋势分析
- 项目目标
- 数据准备
- 模型训练
- 结果解读与可视化
- 第五部分:高级技巧与注意事项
- 模型选择:如何选择 HMM 的状态数
n_components? - 避免局部最优:多次初始化
- 模型评估:对数似然值与 AIC/BIC
- 预测未来的状态
- 模型选择:如何选择 HMM 的状态数
- 第六部分:总结与资源
第一部分:核心概念回顾
在写代码之前,我们快速回顾一下 HMM 的基本思想。
什么是隐马尔可夫模型?
HMM 是一个统计模型,用来描述一个含有隐藏的未知参数的马尔可夫过程,它包含两个序列:
- 状态序列:一个隐藏的、我们无法直接观察到的序列,每天的真实天气(晴天、雨天)。
- 观测序列:一个我们能观察到的序列,它由状态序列生成,我们每天能看到的“是否带伞”。
HMM 有两个核心假设:
- 马尔可夫假设:当前状态只依赖于前一个状态(一阶马尔可夫链)。
- 观测独立性假设:当前时刻的观测值只依赖于当前的状态。
HMM 的三大核心问题
hmmlearn 主要帮助我们解决以下三个问题:

-
评估问题:给定一个模型
λ=(A, B, π)和一个观测序列O,如何计算这个模型生成该观测序列的概率P(O|λ)?hmmlearn使用前向算法高效地解决这个问题。- 用途:可以用来比较不同模型生成同一观测序列的可能性,从而选择最佳模型。
-
解码问题:给定一个模型
λ=(A, B, π)和一个观测序列O,如何找到最有可能产生这个观测序列的状态序列Q*?hmmlearn使用维特比算法来解决。- 用途:这是 HMM 最常见的应用,例如在语音识别中,根据声音信号序列推断出最可能的文字序列。
-
学习问题:给定一个观测序列
O,如何调整模型参数λ=(A, B, π),使得模型生成该观测序列的概率P(O|λ)最大?hmmlearn使用鲍姆-韦尔奇算法(一种 EM 算法的变体)来解决。- 用途:这是模型训练的过程,通过数据“学习”出 HMM 的内部参数。
第二部分:hmmlearn 快速入门
安装
hmmlearn 可以通过 pip 轻松安装:
pip install hmmlearn
API 设计理念
hmmlearn 的 API 设计与 scikit-learn 高度一致,主要步骤如下:
- 导入模型:
from hmmlearn import hmm - 实例化模型:
model = hmm.GaussianHMM(n_components=2) - 训练模型:
model.fit(X),X是观测数据。 - 使用模型:
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)
代码解释:
- 我们创建了一个
GaussianHMM模型,假设隐藏状态为2。 - 我们让这个模型生成1000个样本的观测数据
X和对应的状态序列Z,在真实场景中,我们只有X。 - 我们用
X去训练一个新的new_model,fit方法会自动执行学习算法(鲍姆-韦尔奇)。 - 训练完成后,我们可以查看模型学习到的参数(
startprob_,transmat_,means_,covars_),它们应该和我们最初设定的参数很接近。 predict方法使用维特比算法找到最可能的状态序列。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
CategoricalHMM 是 hmmlearn 0.2.8 版本引入的新模型,是 MultinomialHMM 的更现代、更稳定的替代品,对于离散观测数据,推荐使用 CategoricalHMM。
第四部分:实战项目:股票市场趋势分析
这是一个经典的 HMM 应用场景,我们假设股市的“隐藏状态”是“牛市”、“熊市”或“震荡市”,而我们能观测到的是每天的收盘价或收益率。
项目目标
- 使用历史股票收益率数据训练一个 HMM 模型。
- 识别出数据中隐藏的“市场状态”(如高波动、低波动)。
- 预测明天的市场状态。
数据准备
我们将使用 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 应用中最关键也最困难的一步,没有完美的方法,通常采用以下策略:
- 领域知识:如果你对业务有了解,可以给出一个合理的范围,在分析金融市场时,3-5个状态是常见的选择。
- 信息准则:这是最科学的方法,我们训练一系列不同
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_params 和 n_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() 只能根据当前观测序列解码出最可能的历史状态,它不能预测未来的状态。
要预测未来的状态,你需要一个预测器,一个简单的方法是使用状态转移矩阵:
- 获取最后一个时刻的状态
s_t。 - 从转移矩阵
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 并提供了更丰富的功能。
