马尔可夫链蒙特卡洛(Markov chain Monte Carlo/MCMC)学习

随机模拟的基本思想

假设有一个矩形区域S,其内部有一个不规则区域M,怎样求M的面积?

  1. 将不规则区域M划分成很多规则的小区域,求所有小区域面积之和;
  2. 随机的在S内部取点,计算点落在M内部的比例,该比例可近似S和M的面积比。

方法2采用的就是随机模拟的方法解决问题,随机模拟的关键在于如何将实际复杂的问题,转化为抽样可以解决的问题。

采样算法

给定统计样本集,如何估计产生这个样本集的随机变量概率密度函数,是我们比较熟悉的概率密度估计问题。求解概率密度估计问题的常用方法是最大似然估计、最大后验估计等。

但是,我们思考概率密度估计问题的逆问题:给定一个概率分布 p ( x ) p(x) ,如何让计算机生成满足这个概率分布的样本,这个问题就是统计模拟中研究的重要问题–采样(Sampling)

计算机可以直接生成均匀分布的样本,现在已经有很多方法,那么我们的核心问题就是如何利用均匀抽样,借助随机模拟的方法,实现满足特定概率分布 p ( x ) p(x) 的抽样。

接受-拒绝抽样

对于非均匀分布,我们如果想要生成符合其分布的样本,就需要转换思路,利用均匀抽样和随机模拟来达到目的。
拒绝采样
对于上图中的 p ~ ( z ) \widetilde{p}(z) ,是较为复杂的概率密度函数,我们通过以下步骤来获得符合其分布的样本:

  1. 构造一个比较简单的容易抽样的高斯分布 k q ( z ) kq(z) ,满足 k q ( z ) p ~ ( z ) kq(z)\geq\widetilde{p}(z)
  2. 对高斯分布进行采样,得到一个 z 0 z_0
  3. ( z 0 , k q ( z 0 ) ) (z_0,kq(z_0)) 之间进行均匀采样,得到 μ 0 \mu_0
  4. 如果 μ 0 > p ~ ( z 0 ) \mu_0>\widetilde{p}(z_0) ,则拒绝该采样作为该分布的一个样本,反之则接受;
  5. 重复2-4直到接受足够多的样本。

Python代码实现:

def rejection_sampling(iter=1000):
    samples = []

    for i in range(iter):
        z = np.random.normal(50, 30)
        u = np.random.uniform(0, k*q(z))

        if u <= p(z):
            samples.append(z)

    return np.array(samples)

这里有两个点需要理解,第一个点是 z 0 z_0 的采样,我们为什么要找一个和复杂分布类似的简单分布?目的就是在采样 z 0 z_0 的时候取到复杂分布取值范围内的概率比较大,避免过多的无用计算;第二个点是 μ 0 \mu_0 的采样,也是为了说明为什么拒绝采样是可行的,当 p ~ ( z 0 ) \widetilde{p}(z_0) 较低时,决绝的概率较大,反之接受的概率较大,大致是符合 p ~ ( x ) \widetilde{p}(x) 的分布的。

这样,通过简单的高斯分布采样以及计算机可以实现的均匀采样,间接实现了复杂的分布的采样。

但是,这种方法在高维情况小的劣势也很明显,我们很难找到合适的 k q ( x ) kq(x) ,不合适的上界造成的结果就是采样的拒绝率会超高,浪费了大量的计算。

马尔科夫链及其平稳分布

马尔科夫链由一个状态空间下的初始状态概率 π = { π 0 , π 1 , . . . , π n } \pi=\{\pi_0, \pi_1, ..., \pi_n\} ,和状态转移矩阵 P P 组成, π i \pi_i 表示初始状态是状态 i i 的概率, P i j P_{ij} 表示状态 i i 转换为状态 j j 的概率,根据初始状态概率和状态转移矩阵,我们可以得到一个状态序列 X = { X 1 , X 2 , . . . , X n } X=\{X_1, X_2, ..., X_n\}
P ( X t + 1 = x X t , X t 1 , . . . ) = P ( X t + 1 = x X t ) P(X_{t+1}=x|X_t, X_{t-1}, ...)=P(X_{t+1}=x|X_t)
即当前状态的取值只与上一个状态有关,而跟之前所有的状态无关。

稳定分布

马尔科夫链的稳定分布说的是:对于遍历的马尔可夫链,当其转移次数足够多时,将会进入稳态分布π(x),即各状态的出现概率趋于稳定。换一种说法就是状态转移矩阵的n次方会收敛。进入稳定状态之后满足:
π t + 1 T = π t T P \pi^T_{t+1}=\pi^T_tP
π i j i P i j = j i π j P j i \pi_i\sum_{j\neq i}P_{ij}=\sum_{j\neq i}\pi_jP_{ji}

即每个状态流出的概率等于该状态流入的概率,该方程称为全局平衡方程。

细致平稳条件

对于全局平衡方程,我们再加一个更加严格的限制,使得任一个状态 i i 满足如下条件:
π i P i j = π j P j i \pi_iP_{ij}=\pi_jP_{ji}
即对于任意一对节点 ( i , j ) (i,j) i i j j 的概率等于 j j i i 的概率,上述方程称为细致平衡方程,我们可以很容易证明:如果一个分布满足细致平衡条件,那么它也满足全局平衡条件。

MCMC采样算法

对于给定的复杂分布 p ( x ) p(x) ,我们希望有一个更便捷的方式生成符合其分布的样本,由于马尔科夫链在多次迭代之后会收敛到一个稳定的分布,一个自然而然的想法是:我们可以构造一个马尔科夫链,使其最终达到的稳定分布是 p ( x ) p(x) ,那么我们从任何一个状态出发,根据状态转移矩阵 P P 进行转移,在得到状态序列 x 0 , x 1 , . . . , x n x_0, x_1, ..., x_n 之后,达到了稳定分布 p ( x ) p(x) ,那么接下来的 x n + 1 , x n + 2 , . . . x_{n+1}, x_{n+2},... 都是符合分布 p ( x ) p(x) 的样本。

这个想法在1953 年被Metropolis想到的,首次提出了基于马氏链的蒙特卡罗方法,即Metropolis算法,并在最早的计算机上编程实现。 Metropolis 算法是首个普适的采样方法,并启发了一系列MCMC方法。Metropolis 的这篇论文被收录在《统计学中的重大突破》中,Metropolis算法也被遴选为二十世纪的十个最重要的算法之一。

上面我们已经得到结论:马尔科夫链最终收敛的稳定分布依赖于其转移矩阵 P P ,那么我们应该怎么构造转移矩阵使得其最终的稳定分布是 p ( x ) p(x) 呢?

这时候就需要用到上面的细致平稳条件: π i P i j = π j P j i \pi_iP_{ij}=\pi_jP_{ji} ,当转移矩阵满足细致平稳条件的时候马尔科夫链即达到了平稳分布,但是我们随机构造的转移矩阵很显然不满足这个条件,为了使细致平稳条件成立,我们引入一个 α \alpha ,我们希望:

π i P i j α i j = π j P j i α j i \pi_iP_{ij}\alpha _{ij}=\pi_jP_{ji}\alpha_{ji}

那么我们如何给 α \alpha 赋值才能使得上式成立呢,一种简单的想法是令:

α i j = π j P j i α j i = π i P i j \alpha_{ij}=\pi_jP_{ji} \\ \alpha_{ji}=\pi_iP_{ij}

于是上式成立了,上式中引入的 α \alpha 称为接受率,可以理解为在原来马尔科夫链转移的基础上,以一定的概率接受或者拒绝这个转移,于是从i到j的转移概率变成了 α i j P i j \alpha_{ij}P_{ij} ,整理上述过程就得到了用于采样概率分布 p ( x ) p(x) 的算法。

上述过程存在一个小的问题,当 α i j \alpha_{ij} 较小的时候可能会导致拒绝大量的采样,这样采样过程中就容易原地踏步,使得马尔科夫链收敛到平稳分布的速度太慢。

假设 α i j = 0.1 ,   α j i = 0.2 \alpha_{ij}=0.1,\ \alpha_{ji}=0.2 ,此时满足细致平稳条件,即:

π i P i j 0.1 = π j P j i 0.2 \pi_iP_{ij}*0.1=\pi_jP_{ji}*0.2

上式两边同时扩大五倍等式仍然成立,也就是说我们按照比例提高接受率不会打破细致平稳条件,这启发我们可以将细致平稳条件中的 α i j ,   α j i \alpha_{ij},\ \alpha_{ji} 等比例方法,使得两者之中较大的那个放大为1,即:

α i j = m i n { π j P j i π i P i j , 1 } \alpha_{ij}=min\{\frac{\pi_jP_{ji}}{\pi_iP_{ij}}, 1\}

经过了上面的改造,我们就得到了最常见的Metropolis-Hastings采样算法。

Python代码实现:

def metropolis_hastings(p, iter=1000):
    x, y = 0., 0.
    samples = np.zeros((iter, 2))

    for i in range(iter):
        # 这里就相当于利用已有的转移矩阵P获得下一个状态
        x_star, y_star = np.array([x, y]) + np.random.normal(size=2)
        # 根据一定的概率来决定是否接受上面得到的转移状态
        if np.random.rand() < p(x_star, y_star) / p(x, y):
            x, y = x_star, y_star
        # 不管是不是接受上面的状态都不会舍弃这个样本
        # 这也是为什么如果接受率过低,采样会一直原地踏步的原因
        # 因为初始化的值是不符合要求的分布的,只有经过一段时间的转移才能慢慢靠近要求的分布
        samples[i] = np.array([x, y])

    return samples

Gibbs Sampling

对于高维的情形,由于接受率 a l p h a alpha 的存在,MCMC采样算法的效率不高,能否找到一个转移矩阵P使得接受率 α = 1 \alpha=1​ 呢?

重新定义新的细致平稳条件

细致平稳条件描述如下:
π i P i j = π j P j i \pi_iP_{ij}=\pi_jP_{ji}
从二维数据分布开始,假设 π ( x 1 , x 2 ) \pi(x_1,x_2) 是一个二维联合数据分布,现在有 A ( x 1 ( 1 ) , x 2 ( 1 ) ) , B ( x 1 ( 1 ) , x 2 ( 2 ) ) A(x_1^{(1)}, x_2^{(1)}), B(x_1^{(1)}, x_2^{(2)}) ,A、B的第一维度的取值是一样的,我们可以很容易得到:
π ( x 1 ( 1 ) , x 2 ( 1 ) ) π ( x 2 ( 2 ) x 1 ( 1 ) ) = π ( x 1 ( 1 ) ) π ( x 2 ( 1 ) x 1 ( 1 ) ) π ( x 2 ( 2 ) x 1 ( 1 ) ) π ( x 1 ( 1 ) , x 2 ( 2 ) ) π ( x 2 ( 1 ) x 1 ( 1 ) ) = π ( x 1 ( 1 ) ) π ( x 2 ( 2 ) x 1 ( 1 ) ) π ( x 2 ( 1 ) x 1 ( 1 ) ) \pi(x_1^{(1)}, x_2^{(1)}) \pi(x_2^{(2)}|x_1^{(1)})=\pi(x_1^{(1)})\pi(x_2^{(1)}|x_1^{(1)})\pi(x_2^{(2)}|x_1^{(1)})\\ \pi(x_1^{(1)}, x_2^{(2)}) \pi(x_2^{(1)}|x_1^{(1)})=\pi(x_1^{(1)})\pi(x_2^{(2)}|x_1^{(1)})\pi(x_2^{(1)}|x_1^{(1)})
两式的右边相等,因此我们有:
π ( x 1 ( 1 ) , x 2 ( 1 ) ) π ( x 2 ( 2 ) x 1 ( 1 ) ) = π ( x 1 ( 1 ) , x 2 ( 2 ) ) π ( x 2 ( 1 ) x 1 ( 1 ) ) \pi(x_1^{(1)}, x_2^{(1)}) \pi(x_2^{(2)}|x_1^{(1)})=\pi(x_1^{(1)}, x_2^{(2)}) \pi(x_2^{(1)}|x_1^{(1)})

也就是:
π ( A ) π ( x 2 ( 2 ) x 1 ( 1 ) ) = π ( B ) π ( x 2 ( 1 ) x 1 ( 1 ) ) \pi(A) \pi(x_2^{(2)}|x_1^{(1)})=\pi(B) \pi(x_2^{(1)}|x_1^{(1)})

观察上式,再观察细致平稳条件,我们发现在 x = x 1 ( 1 ) x=x_1^{(1)} 这条直线上,如果用 π ( x 2 ( 1 ) x 1 ( 1 ) ) \pi(x_2^{(1)}|x_1^{(1)}) 作为转移规律的话,是满足细致平稳条件的。

有了上面这个结论,我们就可以在对高维数据进行采样的时候,依次对每个维度进行转移、采样了。具体算法如下:

  1. 初始化状态初始值: x 1 ( 0 ) , x 2 ( 0 ) , . . . , x n ( 0 ) x_1^{(0)}, x_2^{(0)}, ..., x_n^{(0)}
  2. for t to n:
    1. 从条件概率分布 P ( x 1 x 2 ( t ) , . . . , x n ( t ) ) P(x_1|x_2^{(t)}, ..., x_n^{(t)}) 中采样得到 x 1 t + 1 x_1^{t+1}
    2. 从条件概率分布 P ( x 2 x 1 ( t + 1 ) , . . . , x n ( t ) ) P(x_2|x_1^{(t+1)}, ..., x_n^{(t)}) 中采样得到 x 2 t + 1 x_2^{t+1}
    3. 从条件概率分布 P ( x n x 1 ( t + 1 ) , x 2 ( t + 1 ) , . . . , x n ( t ) ) P(x_n|x_1^{(t+1)},x_2^{(t+1)}, ..., x_n^{(t)}) 中采样得到 x n t + 1 x_n^{t+1}
  3. finish

Python代码实现:

def p_x_given_y(y, mus, sigmas):
    mu = mus[0] + sigmas[1, 0] / sigmas[0, 0] * (y - mus[1])
    sigma = sigmas[0, 0] - sigmas[1, 0] / sigmas[1, 1] * sigmas[1, 0]
    return np.random.normal(mu, sigma)


def p_y_given_x(x, mus, sigmas):
    mu = mus[1] + sigmas[0, 1] / sigmas[1, 1] * (x - mus[0])
    sigma = sigmas[1, 1] - sigmas[0, 1] / sigmas[0, 0] * sigmas[0, 1]
    return np.random.normal(mu, sigma)


def gibbs_sampling(mus, sigmas, iter=10000):
    samples = np.zeros((iter, 2))
    y = np.random.rand() * 10

    for i in range(iter):
        x = p_x_given_y(y, mus, sigmas)
        y = p_y_given_x(x, mus, sigmas)
        samples[i, :] = [x, y]

    return samples

参考文献

1. 蒙特卡罗方法采样算法

2. 算法学习 | Metropolis-Hastings算法

3. 马尔可夫链蒙特卡罗算法

猜你喜欢

转载自blog.csdn.net/serryuer/article/details/89350102