4. 隐马尔可夫模型(HMM)入门:HMM的五大要素

好,咱们进入隐马尔可夫模型的世界。

说实话,我第一次接触HMM是在做语音识别项目的时候。当时觉得这东西太抽象了,什么「隐状态」、「观测值」,绕来绕去。后来做量化交易,发现趋势跟踪里也能用上它——嗯,真香。

这一节,咱们就拆开HMM,看看它的五大要素到底是什么。你搞懂了这五个东西,HMM就算入门了。

4.1 状态集:你看不见的「幕后黑手」

HMM里有个核心概念——状态。但注意,这个状态是「隐」的,你看不见。

什么意思?

举个例子。你在看股票K线图,价格上上下下。但你心里清楚,价格波动背后是市场情绪在驱动。这个「市场情绪」就是隐状态。它可能是「牛市」、「熊市」、「震荡市」。你没法直接观测到它,但它确实存在,并且决定了价格怎么走。

在HMM里,我们把所有可能的隐状态放在一起,叫做状态集。通常用字母 Q 表示。

状态集定义: 所有可能的隐状态的集合,记为 Q = {q₁, q₂, ..., qₙ}。

比如在趋势跟踪中,我习惯把状态集设为:

  • q₁ = 上升趋势
  • q₂ = 下降趋势
  • q₃ = 横盘震荡

三个状态,够用了。你想想看,市场再复杂,本质上也就这三种走势。

4.2 观测集:你能看到的「表面现象」

有了隐状态,那观测值是什么?

观测值就是你能直接看到的东西。比如股票的价格、成交量、RSI指标等等。这些数据是公开的,你每天都能拿到。

在HMM里,我们把所有可能的观测值放在一起,叫做观测集。通常用字母 V 表示。

观测集定义: 所有可能的观测值的集合,记为 V = {v₁, v₂, ..., vₘ}。

我个人习惯把观测值做离散化处理。比如把每日收益率分成三类:

  • v₁ = 上涨(收益率 > 1%)
  • v₂ = 下跌(收益率 < -1%)
  • v₃ = 平盘(-1% ≤ 收益率 ≤ 1%)

这样处理之后,观测集就变得简洁了。你可能会问:「为什么不直接用连续值?」嗯,连续值也能做,但离散化之后模型更稳定,不容易过拟合。我在项目中吃过这个亏,后来就老老实实做离散化了。

4.3 转移矩阵:状态之间怎么跳?

状态不是静止的。今天可能是牛市,明天可能就变熊市了。状态之间的转移是有概率的。

这个概率,就记录在转移矩阵里。通常用字母 A 表示。

转移矩阵定义: 一个 N×N 的矩阵,其中 A[i][j] 表示从状态 qᵢ 转移到状态 qⱼ 的概率。

举个例子,假设我们有三个状态:上升、下降、横盘。转移矩阵可能长这样:

下一状态:上升 下一状态:下降 下一状态:横盘
当前:上升 0.7 0.1 0.2
当前:下降 0.1 0.7 0.2
当前:横盘 0.3 0.3 0.4

你看,如果当前是上升趋势,那么明天继续上升的概率是0.7,变成下降的概率只有0.1。这符合直觉——趋势有惯性。

我曾经在回测中发现,如果转移矩阵里对角线元素太小(比如小于0.5),模型就会频繁切换状态,导致交易信号乱七八糟。所以,我建议你设置转移矩阵时,让对角线元素大一些,体现趋势的持续性。

4.4 发射矩阵:状态如何产生观测?

隐状态我们看不见,但观测值我们能看见。那问题来了:给定一个隐状态,它有多大可能产生某个观测值?

这个概率,记录在发射矩阵里。通常用字母 B 表示。

发射矩阵定义: 一个 N×M 的矩阵,其中 B[i][j] 表示在状态 qᵢ 下观测到 vⱼ 的概率。

还是用刚才的例子。假设状态是「上升趋势」,观测值是「上涨」、「下跌」、「平盘」。发射矩阵可能长这样:

观测:上涨 观测:下跌 观测:平盘
状态:上升 0.8 0.05 0.15
状态:下降 0.05 0.8 0.15
状态:横盘 0.2 0.2 0.6

你看,在上升趋势中,观测到「上涨」的概率高达0.8。这很合理——趋势强的时候,价格大概率顺着趋势走。

这里有个坑:发射矩阵的每一行加起来必须等于1。这是概率的基本要求。我曾经在写代码时忘了归一化,结果模型跑出来的结果全是NaN,排查了半天才发现是这里的问题。

4.5 初始概率:故事从哪里开始?

最后一个要素,是初始概率分布。它表示在时间 t=1 时,模型处于各个状态的概率。通常用字母 π 表示。

初始概率定义: 一个长度为 N 的向量,π[i] 表示初始时刻处于状态 qᵢ 的概率。

比如,如果我们认为市场一开始有50%的概率是上升趋势,30%的概率是下降趋势,20%的概率是横盘,那么初始概率就是:

π = [0.5, 0.3, 0.2]

初始概率怎么设?

我个人的做法是:如果没有先验知识,就设成均匀分布。比如三个状态,每个概率1/3。如果有历史数据,可以用前几天的观测值来估计。但说实话,初始概率对长期结果影响不大——模型跑几步之后,状态分布就会收敛到稳态。

4.6 一张图看懂HMM

说了这么多,咱们用一张SVG图把HMM的五大要素串起来。你一看就明白了。

隐马尔可夫模型(HMM)五大要素结构图 隐状态层(不可观测) q₁ q₂ q₃ 转移矩阵 A 发射矩阵 B 观测层(可观测) v₁ 上涨 v₂ 下跌 v₃ 平盘 初始概率 π π = [0.5, 0.3, 0.2] 状态集 Q Q = {q₁, q₂, q₃} 观测集 V V = {v₁, v₂, v₃}

这张图把HMM的五大要素都串起来了。你看,隐状态之间通过转移矩阵连接,隐状态通过发射矩阵产生观测值。初始概率决定了故事从哪里开始。

4.7 代码实现:用Python定义HMM

光说不练假把式。咱们用Python把HMM的五大要素定义出来。代码很简单,就是几个矩阵和向量。

import numpy as np

# 1. 状态集 Q
states = ['上升', '下降', '横盘']
n_states = len(states)

# 2. 观测集 V
observations = ['上涨', '下跌', '平盘']
n_obs = len(observations)

# 3. 初始概率 π
pi = np.array([0.5, 0.3, 0.2])

# 4. 转移矩阵 A
A = np.array([
    [0.7, 0.1, 0.2],  # 上升 → 上升/下降/横盘
    [0.1, 0.7, 0.2],  # 下降 → 上升/下降/横盘
    [0.3, 0.3, 0.4]   # 横盘 → 上升/下降/横盘
])

# 5. 发射矩阵 B
B = np.array([
    [0.8, 0.05, 0.15],  # 上升 → 上涨/下跌/平盘
    [0.05, 0.8, 0.15],  # 下降 → 上涨/下跌/平盘
    [0.2, 0.2, 0.6]     # 横盘 → 上涨/下跌/平盘
])

# 打印检查
print("初始概率 π:", pi)
print("转移矩阵 A:\n", A)
print("发射矩阵 B:\n", B)
print("状态集 Q:", states)
print("观测集 V:", observations)

运行这段代码,你会看到五大要素都定义好了。这就是HMM的骨架。后面咱们要做的,就是在这个骨架上跑各种算法——比如维特比算法、前向后向算法。

小提示: 在实际项目中,这些矩阵的值通常不是手动设置的,而是通过EM算法(鲍姆-韦尔奇算法)从历史数据中学习出来的。手动设置只是为了演示和理解。

4.8 避坑指南

最后,分享几个我踩过的坑:

  • 概率和为1:转移矩阵的每一行、发射矩阵的每一行、初始概率向量,加起来都必须等于1。少一个都不行。
  • 状态数量别太多:我一开始设了5个状态,结果模型参数太多,数据不够,训练出来的模型泛化能力很差。3个状态通常就够了。
  • 观测值离散化要合理:分得太细,模型容易过拟合;分得太粗,信息损失太大。我一般用三分法或五分法。

好了,HMM的五大要素就讲到这里。你搞懂了状态集、观测集、转移矩阵、发射矩阵、初始概率,HMM的大门就已经为你打开了。下一节,咱们用这些要素来解码市场状态——也就是维特比算法。


公众号:蓝海资料掘金营,微信deep3321