学习PyMC中的离散HMM参数

时间:2014-05-04 06:16:02

标签: python time-series hidden-markov-models pymc mcmc

我正在尝试使用PyMC学习简单离散HMM的参数。我正在使用HMM上的Wiki页面为阳光充足的模型建模。该模型如下所示:

我正在使用以下先验。

theta_start_state ~ beta(20,10)
theta_transition_rainy ~beta(8,2)
theta_transition_sunny ~beta(2,8)
theta_emission_rainy ~ Dirichlet(3,4,3)
theta_emission_sunny ~ Dirichlet(10,6,4)

最初,我使用此设置创建如下的训练集。

## Some not so informative priors!
# Prior on start state
theta_start_state = pm.Beta('theta_start_state',12,8)

# Prior on transition from rainy
theta_transition_rainy = pm.Beta('transition_rainy',8,2)

# Prior on transition from sunny
theta_transition_sunny = pm.Beta('transition_sunny',2,8)

# Prior on emission from rainy
theta_emission_rainy = pm.Dirichlet('emission_rainy',[3,4,3])

# Prior on emission from sunny
theta_emission_sunny = pm.Dirichlet('emission_sunny',[10,6,4])

# Start state
x_train_0 = pm.Categorical('x_0',[theta_start_state, 1-theta_start_state])

N = 100

# Create a train set for hidden states
x_train = np.empty(N, dtype=object)

# Creating a train set of observations
y_train = np.empty(N, dtype=object)

x_train[0] = x_train_0

for i in xrange(1, N):
    if x_train[i-1].value==0:
        x_train[i] = pm.Categorical('x_train_%d'%i,[theta_transition_rainy, 1- theta_transition_rainy])
    else:
        x_train[i] = pm.Categorical('x_train_%d'%i,[theta_transition_sunny, 1- theta_transition_sunny])


for i in xrange(0,N):
    if x_train[i].value == 0:
        # Rain
        y_train[i] = pm.Categorical('y_train_%d' %i, theta_emission_rainy)
    else:
        y_train[i] = pm.Categorical('y_train_%d' %i, theta_emission_sunny)

但是,我无法理解如何使用PyMC学习这些参数。我开始如下。

@pm.observed
def y(x=x_train, value =y_train):    
    N = len(x)    
    out = np.empty(N, dtype=object)
    for i in xrange(0,N):
        if x[i].value == 0:
            # Rain
            out[i] = pm.Categorical('y_%d' %i, theta_emission_rainy)
        else:
            out[i] = pm.Categorical('y_%d' %i, theta_emission_sunny)
    return out

可以找到包含此代码的完整笔记本here

除此之外:包含高斯的HMM代码的gist真的很难理解! (未记录)

更新

根据以下答案,我尝试按如下方式更改代码:

@pm.stochastic(observed=True)
def y(value=y_train, hidden_states = x_train):
    def logp(value, hidden_states):
        logprob = 0 
        for i in xrange(0,len(hidden_states)):
            if hidden_states[i].value == 0:
                # Rain
                logprob = logprob + pm.categorical_like(value[i], theta_emission_rainy)
            else:
                # Sunny
                logprob = logprob + pm.categorical_like(value[i], theta_emission_sunny)
        return logprob 

下一步是创建模型然后运行MCMC算法。但是,以上 编辑过的代码也行不通。它给出ZeroProbability error

我不确定我是否正确解释了答案。

2 个答案:

答案 0 :(得分:2)

对此有一些想法:

  • Sunny和Rainy是相互排斥和详尽的隐藏状态。为什么不将它们编码为单个分类天气变量,它可以采用两个值中的一个(编码为晴天,下雨)?
  • 在你的似然函数中,你似乎观察到Rainy / Sunny。我在模型图中看到它的方式,这些似乎是隐藏的,而不是观察到的变量(这将是"步行","购物""清洁&#34 ;)

在您的似然函数中,您需要求和(对于所有时间步骤t)观察值的对数概率(分别为步行,购物和清洁)给出当前(采样)值多雨/晴天(即天气)同时进行步骤t。

<强>学习

如果您想学习模型的参数,您可能需要考虑切换到PyMC3,它更适合自动计算logp函数的渐变。但在这种情况下(因为你选择了共轭先验),这不是真正必要的。如果您不知道Conjugate Priors是什么,或者需要概述,请向维基百科询问共轭推荐列表,它有一篇很棒的文章。

根据您的目的,您可以在此处选择一些选项。如果您想从所有参数的后验分布中进行采样,只需像您一样指定您的MCMC模型,然后按推理按钮,之后只绘制并总结您感兴趣的参数的边际分布,然后您完成了。

如果您对边缘后验分布不感兴趣,而是寻找联合 MAP参数,则可以考虑使用期望最大化(EM)学习或模拟退火。两者都应该在MCMC框架内合理地运作。

对于EM学习,只需重复这些步骤直到收敛:

  • E(期望)步骤:运行MCMC链一段时间并收集样本
  • M(最大化)步骤:更新Beta和Dirichlet Priors的超参数,就好像这些样本是实际观察结果一样。如果您不知道如何做,请查看Beta和Dirichlet发行版。

我会使用较小的学习速率因子,因此您不会陷入第一个局部最优(现在我们正在接近模拟退火):而不是将您从MCMC链中生成的N个样本视为实际通过将学习速率因子K / N缩放到超参数的更新,将它们视为K个观察值(对于值K

答案 1 :(得分:1)

突然出现的第一件事就是你的可能性的回报值。 PyMC期望标量返回值,而不是列表/数组。你需要在返回之前对数组求和。

此外,当您使用Dirichlet作为分类的先验时,PyMC会检测到并填写最后一个概率。以下是我对x_train / y_train循环进行编码的方法:

p = []
for i in xrange(1, N):
    # This will return the first variable if prev=0, and the second otherwise
    p.append(pm.Lambda('p_%i' % i, lambda prev=x_train[i-1]: (theta_transition_rainy, theta_transition_sunny)[bool(prev)]))
    x_train[i] = pm.Categorical('x_train_%i' % i, p[-1])

因此,您使用Lambda获取适当的概率,并将其用作分类的参数。