python解zuobiaoxi方程_从马尔可夫链到蒙特卡洛-Metropolis方法(Python)

python解zuobiaoxi方程_从马尔可夫链到蒙特卡洛-Metropolis方法(Python)_第1张图片

这学期在上郭璐老师的《计算物理》,结合《An Introduction to Computational Physics》与网上一些资料,整理一下马尔科夫链与蒙特卡洛法相关笔记。

目录

1、蒙特卡洛基本思想与简单实例

1.1、基本思想与蒙特卡洛求圆周率

1.2、蒙特卡洛求简单定积分

2、马尔科夫链

2.1、马尔科夫链基本思想与转移矩阵

2.2、马尔科夫链收敛定理

3、马尔科夫链蒙特卡洛算法

3.1、马尔科夫链细致平衡条件

3.2、Metropolis采样算法与改进的Metropolis-Hastings算法

3.3、一般状态空间与转移核

4、Metropolis方法计算定积分Python实现

4.1、高斯分布函数积分

4.2、二次函数积分

5、Reference

1、蒙特卡洛基本思想与简单实例

蒙特卡洛法的基本思想可以分为2步:

首先建立一个合适的概率模型,构造一个随机变量X,使得要求解问题的解等于该随机变量的期望值。然后进行计算机模拟,通过大量的随机试验进行统计平均,得到随机变量X的期望值,即求出了问题的解。

此外,如果待求解问题本身遵循一定的概率分布规律,可以直接进行抽样试验,得到结果同样进行统计平均以求解问题,例如原子核衰变过程本身就是一个具有概率的问题。

1.1、基本思想与蒙特卡洛求圆周率

布丰投针实验是蒙特卡洛法的典型应用。对于

的正方形区域,其内画一个半径为
的扇形,然后开始均匀分布地投针扎点。当投出来的点足够多时,统计扇形内点与正方形区域内点数目比值(并乘4),便可以得到圆周率
的实验值。

使用Python描述如下:

import random
import matplotlib.pyplot as plt
import numpy as np
def pi(num):
    count = 0
    i = 1
    x_in = []
    y_in = []
    x_out = []
    y_out = []
    while i <= num:
        x = random.random()
        y = random.random()
        if x**2 + y**2 <= 1:
            count += 1
            x_in.append(x)
            y_in.append(y)
        else:
            x_out.append(x)
            y_out.append(y)
        i += 1
    plt.scatter(x_in, y_in, c='green', s=12)
    plt.scatter(x_out, y_out, c='blue', s=12)
    xlist = np.linspace(0,1,1000)
    ylist = np.sqrt(1-xlist**2)
    plt.plot(xlist, ylist, c='red')
    plt.title('Monte Carlo method for pi')
    plt.show()
    print('pi is ' + str(4.0*count/num))

pi(1000)

求得

值:
pi is 3.148

python解zuobiaoxi方程_从马尔可夫链到蒙特卡洛-Metropolis方法(Python)_第2张图片

1.2、蒙特卡洛求简单定积分

对于区间

内的积分
,可以把区间分成M份:
,于是积分值等于
。于是可以得到,这个积分值等价于在
中进行等权重取样,计算取样点的函数值并进行平均。如果M非常大,那么可以让
均匀分布在区间内,积分值:

分布的波动带来了误差:例如
如果在某个区域内分布很密集,在另一个区域分布很稀疏,那么计算结果会偏差很大。可以使用统计的标准差来估计随机抽样的可能误差:

对于最简单的二次函数积分

,其精确值为
,可以使用蒙特卡洛法对其进行求解:在区间
内产生1000个随机数,并计算这些随机数对应的函数值,最后其其平均得到期望值。
import random
def integral(num):
    sum = 0
    i = 1
    while i <= num:
        x = random.random()
        y = x**2
        sum += y
        i += 1
    print(sum/num)
integral(1000)

结果为:

0.33578569447979595

与解析值相符。

在这样的简单例子里,无法体现蒙特卡洛方法的优势,例如对于梯形积分法,其误差

而蒙特卡洛法的误差

蒙特卡洛法的误差高于梯形积分法。

但是在高维积分里,蒙特卡洛法的误差依然正比于

,但是随着维度的增高,例如对于
维积分,梯形积分法的误差

这时蒙特卡洛法具有很大的优势。

这里对二次函数的积分是简单的蒙特卡洛求定积分的例子,在许多实际问题中,这样的均匀随机抽样效果不一定好,往往需要构造特定的随机概率分布并进行抽样,这便需要马尔科夫链蒙特卡洛算法,应用比较广泛的有Metropolis-Hastings算法。

2、马尔科夫链

2.1、马尔科夫链基本思想与转移矩阵

如果随机游走中,任一阶段的行为不会被先前游走的历史所限制,这个区域里的每个点可以被多次访问,这种随机游走过程叫做马尔科夫过程,又叫马尔科夫链。可以理解为一个喝醉酒并且弄丢了眼镜的高度近视眼醉汉在街上闲逛:他高度近视所以只能看清周围很近的一段路,每往前走一步都是基于当前步的判断,又因为喝醉了所以他找不到回家的路,漫无目的地随机闲逛。

或者拿我自己举个例子。现在我有3门课的作业需要写:计算物理、微分几何、选修课论文。因为我水平很菜,所以每天都得写作业,但作业依然得写很多天才能写完。如果我今天写了一天计算物理作业,明天就不想写代码了,明天想写计算物理的概率就是0.2,想写选修课论文的概率是0.5,想写微分几何的概率是0.3。这样可以列一个表格,表示今天写的作业与明天想写的作业的关系:

python解zuobiaoxi方程_从马尔可夫链到蒙特卡洛-Metropolis方法(Python)_第3张图片

写成矩阵就是

如果今天写某一科作业的概率分布向量是

,表示各有0.3概率想写计算物理和微分几何,有0.4概率写选修课作业。那么明天写各科作业的概率为
,后天写各科作业概率为
,以此类推,即:
.

现在计算第n天写各门课作业的概率:

import numpy as np
i = 1
a=np.array([0.3,0.3,0.4])
b=np.array([[0.2,0.3,0.5],[0.4,0.25,0.35],[0.35,0.45,0.2]])
while i < 10:
    a = np.dot(a,b)
    print(a)
    i += 1

输出

[0.32  0.345 0.335]
[0.31925 0.333   0.34775]
[0.3187625 0.3355125 0.345725 ]
[0.31896125 0.33508313 0.34595562]
[0.31890997 0.33513919 0.34595084]
[0.31892046 0.33513567 0.34594387]
[0.31891871 0.3351348  0.34594649]
[0.31891893 0.33513523 0.34594583]
[0.31891892 0.33513511 0.34594596]

可以看到,从第5天开始,每天写每一门作业的概率基本变成定值。如果把初始的概率分布改为

,输出
[0.35  0.375 0.275]
[0.31625 0.3225  0.36125]
[0.3186875 0.3380625 0.34325  ]
[0.3191     0.33458437 0.34631562]
[0.31886422 0.33521812 0.34591766]
[0.31893127 0.33512674 0.34594198]
[0.31891665 0.33513496 0.34594839]
[0.31891925 0.33513551 0.34594524]
[0.31891889 0.33513501 0.3459461 ]

可以发现,无论初始概率分布如何,经过一定此时的迭代之后,总会给出一个确定的概率分布。既然最终的概率分布和初始

无关,那只能和转移矩阵
相关,现在计算
p^10: 
[[0.3189189  0.33513506 0.34594603]
 [0.31891895 0.33513515 0.3459459 ]
 [0.3189189  0.33513519 0.34594591]]
p^20: 
[[0.31891892 0.33513514 0.34594595]
 [0.31891892 0.33513514 0.34594595]
 [0.31891892 0.33513514 0.34594595]]
p^100: 
[[0.31891892 0.33513514 0.34594595]
 [0.31891892 0.33513514 0.34594595]
 [0.31891892 0.33513514 0.34594595]]
......

足够大时,转移矩阵的每一行都收敛到一个确定的概率分布,且该分布与初始概率值无关,仅由转移矩阵
决定。

2.2、马尔科夫链收敛定理

如果一个非周期马尔科夫链具有概率转移矩阵

,且任意两状态连通,则
存在且与初始概率分布无关,记为
,有:

1、

2、

3、

是方程
的唯一非负解。

其中

的每一行
称为马尔科夫链的稳定分布。

通俗地理解就是,无论初始给定何种概率分布,经过足够长的马尔科夫游走之后,总会得到一个由且仅由转移矩阵

决定的概率分布。这个定理是
马尔科夫链蒙特卡洛法的基石,它表明我们可以由任一概率分布(例如均匀分布)来产生任意的概率分布(例如高斯分布,见后文);如果我们能够生成任意概率分布的采样点,使用蒙特卡洛法便不存在障碍。

3、马尔科夫链蒙特卡洛算法

根据马尔科夫链收敛定理,最终收敛的概率分布由且仅由转移矩阵

决定,现在考虑它的反问题:如何得到目标概率分布函数相对应的转移矩阵?

1953年,Metropolis提出了著名的Metropolis采样算法,通过引入随机游走的接受率,利用细致平衡条件得到目标概率分布,该方案之后被Hastings改进,提高了采样率,称为Metropolis-Hastings算法。

3.1、马尔科夫链细致平衡条件

细致平衡条件:如果非周期马尔科夫链的转移矩阵

和分布
对所有的
满足

是马尔科夫链的平衡分布。

假设我们想得到的概率分布是

,现在找一个转移矩阵
,它可以把状态
转移到状态
,记为
。当然了,我们很难一次性猜出来对应于
的转移矩阵,因此这里的
不满足细致平衡条件:

为了让细致平衡条件成立,引入辅助的

,使得:

现在问题转化为:如何求出

?这便需要Metropolis算法。

3.2、Metropolis采样算法与Metropolis-Hastings采样算法

从对称的角度看,如果取

那么细致平衡条件一定是满足的。

至此,构造了对应于目标概率分布

的转移矩阵
,满足细致平衡条件。在此过程中,引入的
称为接受率,它表示在按照转移矩阵
从状态
游走到状态
的过程中,按照
的概率来接受该步游走,或者说按照
的概率拒绝该步游走。

这里的状态转移矩阵可以任意取,之后由

对游走进行选择,只要经过足够多的游走步数,就可以收敛到目标概率分布

至此,得到了Metropolis采样算法。

Metropolis算法的一个劣势在于,如果接受率很低,那么大多数的游走步数都被舍弃,会耗费大量计算资源。之后Hastings改进了该算法,提出了Metropolis-Hastings算法。

对于先前的细致平衡条件:

如果给等式两边都放大相同倍数,细致平衡等式依然成立,因此,可以分情况讨论:

a、如果

,可以取两边同时放大
倍,得到:

这样左边的

,右边

b、如果

,可以取两边同时放大
倍,得到:

这样左边的

,右边

由于最后的选择概率变为了

,综上,可以得到改进的接受率为:

此即为Metropolis-Hastings算法,其优势在于对接受率进行了放大,可以减少计算所需资源,提高效率。

一般而言,为了方便,选择的马尔科夫链转移矩阵是对称的,即

,这样接受率可以简化为:

3.3、一般状态空间与转移核

在一些情况下,状态空间是连续的,例如区间[0,1],其维度是无穷维,这种情况下似乎无法写出对应的转移矩阵;而且从由于有无穷多的态,从一个态到另一个态的转移矩阵元为

。查了一些资料,后来在stackexchange上找到了一些指导,或者可以换种理解方式:既然状态转移矩阵是可以任意取的,即使暂时取不出来,或者说无法显式地写出来,并不妨碍它可以是对称的,那么根据Metropolis-Hastings算法,转移矩阵由于对称而可以约分,直接可以写出采用率为:

有了采样率便可以开始采样直到收敛,状态转移矩阵形式上在不影响问题求解的情况下被绕过去了。

只有在状态空间是分立且有限的(或者可数的)情况下,才可以定义马尔科夫链的状态转移矩阵。状态空间是连续的情况称为一般状态空间(general state space),这种情况下其维度是无穷维,不再有转移矩阵,而是使用转移核(transtion kernel)来代替。对于一般状态空间的马尔科夫链,考虑使用改进的Metropolis-Hastings算法,以便于在没有准确定义转移函数的情况下产生目标平稳分布。

4、Metropolis方法计算定积分

4.1、高斯分布函数积分

高斯分布又称正态分布,其概率密度函数为:

,目标为产生
之间按高斯分布
的抽样点。

这是典型的一般状态空间的例子,因此考虑使用Metropolis-Hastings算法。马尔科夫链状态转移函数可以任意选取,为了方便,选取对称的转移函数,转移方式为

之间的均匀随机分布。为了保证马尔科夫链收敛,可以在迭代700步之后开始取样,一共抽取10000个抽样点。

Python代码如下:

import random
import numpy as np
from matplotlib import pyplot as plt

mu = 0.5
sigma = 0.1
skip = 700 # 收敛步数
num = 10000 # 采样点数

def Gaussian(x):
    return 1/(sigma*np.sqrt(2*np.pi)) * np.exp(-(x-mu)**2 /(2*sigma**2))
def M_H():
    x_0 = 0
    samples = []
    j = 1
    while len(samples) <= num:
        while True:
            x_1 = random.random()
            p_j = Gaussian(x_1)
            p_i = Gaussian(x_0)
            alpha = min(p_j / p_i, 1.0)
            r = random.random()
            if r <= alpha:
                x_0 = x_1
                if j >= skip:
                    samples.append(x_1)
                j += 1
                break
    return samples
norm_samples = M_H()
x = np.linspace(0, 1, len(norm_samples))
plt.plot(x, Gaussian(x), label='normal distribution')
plt.hist(norm_samples, 100, density=True, color='red', label='samples distribution')
plt.title('Metropolis-Hastings',fontsize=19)
plt.ylabel('pdf', fontsize=19)
plt.xlabel('sample', fontsize=19)
plt.legend()
plt.show()

结果如下:

python解zuobiaoxi方程_从马尔可夫链到蒙特卡洛-Metropolis方法(Python)_第4张图片

在得到目标概率分布的抽样点之后,积分

可以看作为常数函数对于高斯分布变量的期望值:

计算如下:

def g(x): # 常数函数
    return 1.0
sum = 0
n=len(norm_samples)
for sample in norm_samples:
    sum = sum + g(sample)
integral = sum / n
print(integral)

结果:

1.0

符合精确值。分析一下程序可以看到,这里的求和平均相当于n个1相加再除以n,结果应该严格等于1.0,但需要注意,这并不说明数值误差为0,因为高斯分布函数在

的积分才应该是严格为1,这里程序在
区间上得到1.0,恰恰说明存在数值误差。

4.2、二次函数积分

考虑定积分

,如果
函数值的方差很大,使用之前的简单蒙特卡洛法便会带来很大的计算误差。因此考虑将
拆分为两部分之乘积:
,其中
是一个相对方差比较小的函数,以便于减小误差;而
则看作是一个概率分布。

如果按照

的概率分布对自变量进行抽样,只需计算
在这些抽样点上的期望值便可以得到问题的解。

继续考虑前文的二次函数积分,

,即最简单的二次函数在区间
上的积分
,可以选取概率分布函数

对于这个概率分布,已知其归一化常数

积分可以改写为:

其中

首先产生对应于概率分布

的抽样点,Python代码如下:
import random
import numpy as np
from matplotlib import pyplot as plt

mu = 0.5
sigma = 0.1
skip = 700 # 收敛步数
num = 10000 # 采样点数
z = 0.46265167

def f(x):
    return 1/z *(np.exp(x**2)-1)
def g(x):
    return z*x**2/(np.exp(x**2)-1)

def M_H():
    x_0 = 0
    samples = []
    j = 1
    while len(samples) <= num:

        while True:
            x_1 = random.random()
            p_j = f(x_1)
            p_i = f(x_0)
            alpha = min(p_j / p_i, 1.0)
            r = random.random()
            if r <= alpha:
                x_0 = x_1
                if j >= skip:
                    samples.append(x_1)
                j += 1
                break

    return samples
norm_samples = M_H()

之后对函数

在抽样点上的函数值求平均:
n=len(norm_samples)
sum = 0
for sample in norm_samples:
    sum = sum + g(sample)
integral = sum / n
print(integral)

输出结果:

0.34778211403788584

与积分的精确值

相符。

5、Reference

1、郭璐,《计算物理》讲义

2、Tao Pang, An Introduction to Computational Physics.

3、stackexchange.com, How to generate the transition matrix of Markov Chain ...

4、靳志辉,LDA-math-MCMC 和 Gibbs Sampling

你可能感兴趣的:(python解zuobiaoxi方程_从马尔可夫链到蒙特卡洛-Metropolis方法(Python))