首先抄下《统计学习方法》中HMM的定义和相关知识点:隐马尔科夫模型是关于时序的概率模型,描述由一个隐藏的马尔科夫链随机生成不可观测的状态随机序列,再由各个状态生成一个观测而产生观测随机序列的过程。隐马尔科夫模型由初始状态概率向量、状态转移概率矩阵
和观测概率矩阵
(也可称为发射概率矩阵)组成。隐马尔科夫模型做了两个基本假设——一是任意时刻的隐状态只与前一时刻的隐状态有关,二是任意时刻的观测只与当前时刻的隐状态有关。隐马尔科夫模型如下图所示:

其中,

对于概率预测问题,我们假设前向概率为


hmmlearn源码中实现3种模型——GaussianHMM、GMMHMM和MultinomialHMM。其中GaussianHMM假设观测状态是连续型且满足高斯分布,GMMHMM假设观测状态是连续型且满足混合高斯分布,MultinomialGMM假设观测状态是离散型。
在代码实现上,hmmlearn在base.py中定义了基本HMM模型baseHMM类,类中定义了2个重要参数——self.startprob(HMM的初始状态,维度是[n_components])和self.transmat_(HMM的状态转移矩阵
,维度是[n_components, n_components]),基类中并没有定义发射矩阵
,因为只有观测状态是离散型的HMM模型才有发射矩阵(代码在MultinomialGMM类中单独定义了self.emissionprob_)。而HMM中的3个基本问题则对应了类中的3个函数:
- 概率计算问题,对应了_baseHMM类中的score函数。代码中利用前向算法来计算概率,核心部分如下:
for i, j in iter_from_X_lengths(X, lengths):
framelogprob = self._compute_log_likelihood(X[i:j])
logprobij, _fwdlattice = self._do_forward_pass(framelogprob)
logprob += logprobij
其中,_do_forward_pass函数实现了前向算法中的,代码在https://github.com/hmmlearn/hmmlearn/blob/master/lib/hmmlearn/_hmmc.pyx中。具体核心代码如下:
for t in range(1, n_samples):
for j in range(n_components):
for i in range(n_components):
work_buffer[i] = fwdlattice[t - 1, i] + log_transmat[i, j] # 由于取log操作,这里的*运算变成了+运算
fwdlattice[t, j] = _logsumexp(work_buffer) + framelogprob[t, j]
hmmlearn在计算概率时都对结果进行了取log操作,是为了防止序列过长而导致计算过程中出现数值下溢的情况,这在PRML的13.2.4节中有提到。当然,代码中也是实现了后向算法,可见_do_backward_pass函数:
for t in range(n_samples - 2, -1, -1):
for i in range(n_components):
for j in range(n_components):
work_buffer[j] = (log_transmat[i, j] + framelogprob[t + 1, j] + bwdlattice[t + 1, j])
bwdlattice[t, i] = _logsumexp(work_buffer)
- 学习问题,对应了_baseHMM类中的fit函数。核心代码如下:
for i, j in iter_from_X_lengths(X, lengths):
framelogprob = self._compute_log_likelihood(X[i:j])
logprob, fwdlattice = self._do_forward_pass(framelogprob)
curr_logprob += logprob
bwdlattice = self._do_backward_pass(framelogprob)
posteriors = self._compute_posteriors(fwdlattice, bwdlattice)
self._accumulate_sufficient_statistics(stats, X[i:j], framelogprob, posteriors, fwdlattice, bwdlattice)
其中,framelogprob就是发射概率(如果观测状态是离散型就是矩阵B,如果观测状态是连续型就是高斯对数密度),fwdlattice和bwdlattice分别是前向概率和后向概率
,posteriors就是
。
for i, j in iter_from_X_lengths(X, lengths):
logprobij, state_sequenceij = decoder(X[i:j])
logprob += logprobij
state_sequence[i:j] = state_sequenceij
其中最重要的维特比算法实现代码部分如下:
for t in range(1, n_samples):
for i in range(n_components):
for j in range(n_components):
work_buffer[j] = (log_transmat[j, i] + viterbi_lattice[t - 1, j])
viterbi_lattice[t, i] = _max(work_buffer) + framelogprob[t, i]
其中,viterbi_lattice保存的就是每个中间时刻的隐状态的最大累积概率。
网友评论