Scikit的隐马尔可夫模型接受不归一的观察概率

1 投票
1 回答
1526 浏览
提问于 2025-04-18 05:40

我正在尝试用“MultinomialHMM”模块构建一个简单的隐马尔可夫模型,这个模块是scikit-learn库的一部分。我遇到的问题是,这个模块在状态的观察概率加起来超过1或者少于1的时候,依然可以接受并生成预测。举个例子:

import numpy
from sklearn import hmm
startprob = numpy.array([0.5, 0.5])
transition_matrix = numpy.array([[0.5, 0.5], [0.5, 0.5]])
model = hmm.MultinomialHMM(2, startprob, transition_matrix)
model.emissionprob_ = numpy.array([[0, 0, 0.2], [0.6, 0.4, 0]])

注意,状态0发出的信号概率是[0,0,0.2](加起来是0.2)。当我让这个模块生成一组观察值时,它并没有报错:

model.sample(10) 
(array([1, 0, 0, 0, 0, 2, 1, 0, 0, 0], dtype=int64), array([1, 1, 0, 1, 1, 0, 1, 0, 0, 0]))

我还可以指定一些发射概率,它们的总和超过1,而模型依然可以生成预测,没有任何问题。

这是正常的行为吗?这些概率是不是以某种方式被规范化了?如果是的话,具体是怎么做的呢?

1 个回答

2

首先,HMMsklearn 中已经不再推荐使用了。你需要去看看这个链接:https://github.com/hmmlearn/hmmlearn,它是一个在Python中实现的隐马尔可夫模型,使用的接口和scikit-learn类似

顺便说一下,你提到的问题看起来像是个bug。当你设置 emissionprob_ 时,会调用 _set_emissionprob。这个过程会尝试通过调用 normalize(emissionprob) 来进行“重新归一化”:

if not np.alltrue(emissionprob):
    normalize(emissionprob)

不过,这段代码有两个问题:

  1. 没有正确设置轴。
  2. 虽然文档上说是就地修改,但实际上并不是。

所以修改成了:

if not np.alltrue(emissionprob):
    normalize(emissionprob, 1) # added axis term

以及

def normalize(A, axis=None):
    A += EPS
    Asum = A.sum(axis)
    if axis and A.ndim > 1:
        # Make sure we don't divide by zero.
        Asum[Asum == 0] = 1
        shape = list(A.shape)
        shape[axis] = 1
        Asum.shape = shape
    A /= Asum # this is true in-place, it was `return A / Asum`  <<= here

撰写回答