MCMC算法

一. MCMC算法定义

    MCMC是Markov chain Monte Carlo的缩写,即马尔可夫链蒙特卡罗方法。MCMC是一组利用马尔可夫链从随机分布中取样的算法。生成的马氏链即是对于目标分布的近似估计。

常见算法:

  • Metropolis-Hastings算法
  • Gibbs取样算法
  • Hamiltonian Monte Carlo算法(效率最高,但不常用)
    在统计推断和贝叶斯推断中,常用的是前两种算法。

二、Metropolis算法

假设随机变量X服从一个概率密度函数P。

  1. 初始化X,即给定随机变量一个起始点 X o l d X_{old} Xold
  2. 生成随机的跳跃 Δ X \Delta X ΔX~ N ( 0 , σ ) N(0,\sigma) N(0,σ) X n e w = X o l d + Δ X X_{new} = X_{old}+\Delta X Xnew=Xold+ΔX
  3. 计算随机变量移动到提议的点的概率
    P m o v i n g = m i n ( 1 , X n e w X o l d ) P_{moving} = min(1, \frac{X_{new}} {X_{old}}) Pmoving=min(1,XoldXnew)
  4. 生成一个随机数 C C C~ U ( 0 , 1 ) U(0,1) U(0,1),如果 C < P m o v i n g C < P_{moving} C<Pmoving 则接受跳跃,随机变量取新的值 X n e w X_{new} Xnew;反之则任取原来的值。
  5. 重复步骤2~4,并记录下随机变量所有的取值,知道蒙特卡洛模拟生成的随机游走足以代表目标分布。

利用Gamma分布对Metropolis算法进行测试

# Metropolis算法
import numpy
from scipy.special import gamma
import scipy.stats
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def MCMC(P,X0,chain,space):
    '''
    P - 随机变量X服从的概率密度函数
    X0 - MCMC的起始值
    chain - MCMC的长度
    space - 随机变量的取值范围,如[0,inf]
    '''
    
    if not callable(P):
        raise Exception('P必须是函数')
        
    X_current = X0
    X = [X_current]
    
    while True:
        Delta_X = scipy.stats.norm(loc=0,scale=2).rvs()
        X_proposed = X_current + Delta_X
        
        if X_proposed < space[0] or X_proposed > space[1]:
            p_moving = 0
        elif P(X_current) == 0:
            p_moving = 1
        else:
            p_moving = min(1,P(X_proposed)/P(X_current))
            
        if scipy.stats.uniform().rvs() <= p_moving:  #新生成的服从0,1均匀分布的值小于等于p_moving
            X.append(X_proposed)
            X_current = X_proposed
        else:
            X.append(X_current)
            
        if len(X) >= chain:
            break
        
    return numpy.array(X)
# 测试Metropolis算法:生成服从Gamma分布的马氏链
def GammaDist(x,k,theta):
    '''定义Gamma分布'''
    return 1/(gamma(k)*theta**k) * x**(k-1) * numpy.exp(-x/theta)

# 得到参数k=2,theta=2的分布
gammadist = lambda x: GammaDist(x,2,2)

X = MCMC(gammadist,2,100,[0,numpy.inf])   #最终结果:有MCMC算法生成的马氏链

这里生成的X就是使用MCMC算法生成的近似服从Gamma分布,是对目标分布的近似估计。下面通过动画演示这一过程。

# 用一个动画来演示生成的马氏链
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
fig.suptitle('MCMC Process')


def anima_chain(index):
    ax1.clear()

    ax1.plot(X[:index + 1], 'r-')
    # ax1.set_xlim([1,1000])
    ax1.set_xlabel('chain')  # 第多少步
    ax2.set_label('X')  # 生成的马氏链

    # ax1.set_title('MCMC Process')


def anima_density(index):
    ax2.clear()

    x = numpy.arange(0., 12, .1)
    ax2.plot(x, gammadist(x), 'k-')
    if index <= 10:
        y = numpy.zeros(len(x))
        ax2.plot(x, y)
    else:
        density = scipy.stats.gaussian_kde(X[:index - 1])
        ax2.plot(x, density(x), 'r-')

    ax2.set_xlim([0.,12])
    ax2.set_ylim([0,.5])
    ax2.set_xlabel('X')
    ax2.set_ylabel('density')

    # ax2.set_title('MCMC density estimate')

ani_chain = animation.FuncAnimation(fig, anima_chain, interval=1)
ani_density = animation.FuncAnimation(fig, anima_density, interval=1)


def main():
    plt.show()

if __name__ == '__main__':
    main()

MCMC算法_第1张图片

  • 左侧是MCMC算法每一次迭代生成的随机数得到的马氏链,不断地取下一个点;
  • 右边是根据模拟出来的chain来近似得到的概率密度曲线(红色曲线);
  • MCMC模拟出来的分布要近似于目标分布Gamma分布。
  • 生成1000个数后得到的马氏链,可以近似的拟合我们的真实分布Gamma(2,2)。如果我们将链拉的更长,那就这个生成的马氏链几乎可以完美的拟合真实分布。
  • 后续过程中,会不断根据两个信息(概率密度函数的信息-也就是每个采样点之间相关关系的信息,以及随机游走的信息)来更新马氏链,最终很好的拟合目标分布。

你可能感兴趣的:(算法,算法,python,MCMC)