Scikit的隐马尔可夫模型接受不归一的观察概率
我正在尝试用“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
首先,HMM
在 sklearn
中已经不再推荐使用了。你需要去看看这个链接:https://github.com/hmmlearn/hmmlearn,它是一个在Python中实现的隐马尔可夫模型,使用的接口和scikit-learn类似
顺便说一下,你提到的问题看起来像是个bug。当你设置 emissionprob_
时,会调用 _set_emissionprob
。这个过程会尝试通过调用 normalize(emissionprob)
来进行“重新归一化”:
if not np.alltrue(emissionprob):
normalize(emissionprob)
不过,这段代码有两个问题:
- 没有正确设置轴。
- 虽然文档上说是就地修改,但实际上并不是。
所以修改成了:
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