我可以使用什么pymc3 Monte-Carlo步进器进行自定义分类分发?

时间:2017-01-27 17:13:21

标签: theano bayesian hidden-markov-models pymc3

我正在努力在pymc3中实现隐藏马尔可夫链。我在实现隐藏状态方面已经走得很远了。下面,我展示了一个简单的2状态马尔可夫链:

import numpy as np
import pymc3 as pm
import theano.tensor as tt

# Markov chain sample with 2 states that was created
# to have prob 0->1 = 0.1 and prob 1->0 = 0.3
sample = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0,
   0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
   1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
   0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0],
dtype=np.uint8)

我现在正在定义一个描述状态的类。作为输入,我需要知道从状态0移动到状态1的概率P1,以及从1到0移动的P2。我还需要知道第一个状态为0的概率PA。

class HMMStates(pm.Discrete):
    """
    Hidden Markov Model States
    Parameters
    ----------
    P1 : tensor
        probability to remain in state 1
    P2 : tensor
        probability to move from state 2 to state 1

    """

    def __init__(self, PA=None, P1=None, P2=None,
                 *args, **kwargs):
        super(HMMStates, self).__init__(*args, **kwargs)
        self.PA = PA
        self.P1 = P1
        self.P2 = P2
        self.mean = 0.
        self.mode = tt.cast(0,dtype='int64')

    def logp(self, x):
        PA = self.PA
        P1 = self.P1
        P2 = self.P2

        # now we need to create an array with probabilities
        # so that for x=A: PA=P1, PB=(1-P1)
        # and for x=B: PA=P2, PB=(1-P2)

        choice = tt.stack((P1,P2))
        P = choice[x[:-1]]

        x_i = x[1:]

        ou_like = pm.Categorical.dist(P).logp(x_i)
        return pm.Categorical.dist(PA).logp(x[0]) + tt.sum(ou_like)

我为在theano google小组学到的高级索引忍者技巧感到自豪。您也可以使用tt.switch实现相同的功能。我不太确定的是self.mode。我只是给它0以避免测试值错误。以下是如何在模型中使用该类来测试它是否有效。在这种情况下,状态不是隐藏的,而是观察到的。

with pm.Model() as model:
    # 2 state model
    # P1 is probablility to stay in state 1
    # P2 is probability to move from state 2 to state 1
    P1 = pm.Dirichlet('P1', a=np.ones(2))
    P2 = pm.Dirichlet('P2', a=np.ones(2))

    PA = pm.Deterministic('PA',P2/(P2+1-P1))

    states = HMMStates('states',PA,P1,P2, observed=sample)

    start = pm.find_MAP()

    trace = pm.sample(5000, start=start)

输出很好地再现了数据。在下一个模型中,我将展示问题。在这里,我不直接观察状态,而是添加了一些高斯噪声的状态(因此隐藏状态)。如果你使用Metropolis步进器运行模型,那么它会因索引错误而崩溃,我追溯到与使用Metropolis stepper on Categorical Distributions相关的问题。不幸的是,唯一适用于我的类的Stepper是CategoricalGibbsMetropolis步进器,但它拒绝使用我的类,因为它不是明确的分类分发。

gauss_sample = sample*1.0 + 0.1*np.random.randn(len(sample))
from scipy import optimize
with pm.Model() as model2:
    # 2 state model
    # P1 is probablility to stay in state 1
    # P2 is probability to move from state 2 to state 1
    P1 = pm.Dirichlet('P1', a=np.ones(2))
    P2 = pm.Dirichlet('P2', a=np.ones(2))

    S = pm.InverseGamma('S',alpha=2.1, beta=1.1)

    PA = pm.Deterministic('PA',P2/(P2+1-P1))

    states = HMMStates('states',PA,P1,P2, shape=len(gauss_sample))

    emission = pm.Normal('emission',
                         mu=tt.cast(states,dtype='float64'),
                         sd=S,
                         observed = gauss_sample)

    start2 = pm.find_MAP(fmin=optimize.fmin_powell)
    step1 = pm.Metropolis(vars=[P1, P2, S, PA, emission])
    step2 = pm.ElemwiseCategorical(vars=[states], values=[0,1])
    trace2 = pm.sample(10000, start=start, step=[step1,step2])

ElemwiseCategorical使其运行,但不为我的状态指定正确的值。状态全部为0或全部为1。

如何告诉ElemwiseCategorial分配状态为1和0的向量,或者我如何让CategorialGibbsMetropolis将我的分布识别为分类。这必须是自定义分发的常见问题。

1 个答案:

答案 0 :(得分:1)

由于我没有听到任何人的问题,我自己回答了。我使用的技巧是由Chris Fonnesbeck在pymc3 github上提出的,我在那里打开了这个问题。他建议继承pm.Categorical。

class HMMStates(pm.Categorical):
    """
    Hidden Markov Model States
    Parameters
    ----------
    P1 : tensor
        probability to remain in state 1
    P2 : tensor
        probability to move from state 2 to state 1

    """

    def __init__(self, PA=None, P1=None, P2=None,
                 *args, **kwargs):
        super(pm.Categorical, self).__init__(*args, **kwargs)
        self.PA = PA
        self.P1 = P1
        self.P2 = P2
        self.k = 2 # two state model
        self.mean = 0.
        self.mode = tt.cast(0,dtype='int64')

    def logp(self, x):
        PA = self.PA
        P1 = self.P1
        P2 = self.P2

        # now we need to create an array with probabilities
        # so that for x=A: PA=P1, PB=(1-P1)
        # and for x=B: PA=P2, PB=(1-P2)
        PT = tt.stack((P1,P2))

        P = PT[x[:-1]]

        x_i = x[1:]

        ou_like = pm.Categorical.dist(P, shape=(N_chain-1,2)).logp(x_i)
        return pm.Categorical.dist(PA).logp(x[0]) + tt.sum(ou_like)

我的HMMStates无法真正调用pm.Categorical超级初始化,因此我调用的是pm.Categorical的超类,即pm.Discrete。这个技巧使它通过了BinaryGibbsMetropolis和CategoricalGibbsMetropolis的测试。

如果您对实现2状态和多状态HMM感兴趣,我将实现所有这些情况here