天天看点

采样

随机采样

采样是根据某种分布去生成一些数据点。最基本的假设是认为我们可以获得服从均匀分布的随机数,再根据均匀分布生成复杂分布的采样。对于离散分布的采样,可以把概率分布向量看作一个区间段,然后判断u落在哪个区间段内。对于比较复杂的分布比如正态分布我们可以通过Box-Muller算法,实现对高斯分布的采样。

采样

其他几个著名的连续分布,包括指数分布,Gamma分布,t分布,F分布,Beta分布,Dirichlet分布都可以通过类似的数学变换得到。

蒙特卡洛估计

从一个分布中采样可以做很多有用的事情,包括求边缘分布,MAP推断,以及计算如下形式的期望

采样
通过蒙特卡洛方法可以得到该期望的近似解
采样
其中
采样
是从分布p中采样得到的

拒绝采样

采样

举例:比如要计算圆的面积可以在矩形中均匀的撒点,统计落在圆中的点的个数处于总的点的个数,就是圆的面积与矩形面积的比值。

采样

假设我们已经可以抽样高斯分布q(x)(如Box–Muller_transform 算法),我们按照一定的方法拒绝某些样本,达到接近p(x)分布的目的。

拒绝采样时蒙特卡洛中的一种特殊情况。计算p(x=x'):我们可以将这个概率写成如下形式

采样

接着采用蒙特卡洛近似

重要采样

拒绝采样会浪费大量的样本,一个更好的方法是通过重要采样进行采样。

采样

本来需要从p分布中进行采样,由于种种原因(p分布不好采样等)我们不愿意从p分布中采样,而是从q分布中采样,需要在f中乘上权重因子w(x)

采样

对于p(x=x')可以通过如下的计算

采样

Python代码

import matplotlib

import numpy as np

import matplotlib.pyplot as plt

import seaborn

def qsample():

return np.random.rand() * 4.

def p(x):

return 0.3 * np.exp(-(x - 0.3) ** 2) + 0.7 * np.exp(-(x - 2.) ** 2 / 0.3)

def q(x):

return 4.0

def importance(nsamples):

samples = np.zeros(nsamples, dtype=float)

w = np.zeros(nsamples, dtype=float)

for i in range(nsamples):

samples[i] = qsample()

w[i] = p(samples[i]) / q(samples[i])

return samples, w

def sample_discrete(vec):

u = np.random.rand()

start = 0

for i, num in enumerate(vec):

if u > start:

start += num

else:

return i - 1

return i

def importance_sampling(nsamples):

samples, w = importance(nsamples)

# print samples

final_samples = np.zeros(nsamples, dtype=float)

w = w / w.sum()

# print w

for j in range(nsamples):

final_samples[j] = samples[sample_discrete(w)]

return final_samples

x = np.arange(0, 4, 0.01)

x2 = np.arange(-0.5, 4.5, 0.1)

realdata = 0.3 * np.exp(-(x - 0.3) ** 2) + 0.7 * np.exp(-(x - 2.) ** 2 / 0.3)

box = np.ones(len(x2)) * 0.8

box[:5] = 0

box[-5:] = 0

plt.plot(x, realdata, 'g', lw=6)

plt.plot(x2, box, 'r--', lw=6)

# samples,w = importance(5000)

final_samples = importance_sampling(5000)

plt.hist(final_samples, normed=1, fc='c')

plt.show()

采样

MCMC(Markov Chain Monte Carlo)

MCMC采样算法

从马尔科夫链中对某个分布进行采样,采样的结果通过蒙特卡洛方法来对期望进行估计。

采样

一个直观的理解是:收集了可能来自需要观测山的大量地点,我们就可以近似的构造出这座山出来。

从当前地点开始,移动到一个靠近你的新地点。如果该地点可能来自需要观测的山,那么移动到该地点,如果不是待在原处。通过大量的迭代,返回所有可能的地点。

    MCMC适合使用在高维的空间中,会提高采样的效率。

细致平稳条件

采样
采样

假设我们已经有一个转移矩阵为Q马氏链(q(i,j))表示从状态i转移到状态j的概率,通常情况下

采样

也就是细致平稳条件不成立,所以p(x)不太可能是这个马氏链的平稳分布。为此需要引入

采样
采样

所以有

采样
采样
采样

Metropolis-Hastings采样算法

以上MCMC算法已经可以工作了,但是它有一个小的问题:马氏链Q在转移的过程中可能偏小,这样采样过程中马氏链容易原地踏步。因此需要提高接受率。

假设

采样

此时满足细致平稳条件,于是

采样

上式两边扩大5倍,我们改写为

采样

我们提高了接收率,而细致平稳条件没有打破。这启发我们可以把细致平稳条件的同比例放大,使得两数中最大的一个放大到1,这样就提高了采样中的跳转接受率。

采样
采样

Python代码:

# The Metropolis-Hastings algorithm
from pylab import *
from numpy import *

def p(x):
    mu1 = 3
mu2 = 10
v1 = 10
v2 = 3
return 0.3*exp(-(x-mu1)**2/v1) + 0.7* exp(-(x-mu2)**2/v2)
#    return -x**2

def q(x):
    mu = 5
sigma = 10
return exp(-(x-mu)**2/(sigma**2))

stepsize = 0.5
x = arange(-10,20,stepsize)
px = zeros(shape(x))
for i in range(len(x)):
    px[i] = p(x[i])
N = 5000

# independence chain
u = random.rand(N)
mu = 5
sigma = 10
y = zeros(N)
y[0] = random.normal(mu,sigma)
for i in range(N-1):
    ynew = random.normal(mu,sigma)
    alpha = min(1,p(ynew)*q(y[i])/(p(y[i])*q(ynew)))
if u[i] < alpha:
        y[i+1] = ynew
else:
        y[i+1] = y[i]

# random walk chain
u2 = random.rand(N)
sigma = 10
y2 = zeros(N)
y2[0] = random.normal(0,sigma)
for i in range(N-1):
    y2new = y2[i] + random.normal(0,sigma)
    alpha = min(1,p(y2new)/p(y2[i]))
if u2[i] < alpha:
        y2[i+1] = y2new
else:
        y2[i+1] = y2[i]

figure(1)
nbins = 30
hist(y, bins = x)
plot(x, px*N/sum(px), color='g', linewidth=2)
plot(x, q(x)*N/sum(px), color='r', linewidth=2)

figure(2)
nbins = 30
hist(y2, bins = x)
plot(x, px*N/sum(px), color='g', linewidth=2)

show()
           
采样
采样

该代码用到一个比较特殊的马尔科夫过程:当一个随机变量,其增量的变化服从于正态分布时,我们说,这个随机过程叫做维纳过程(Wiener process)——或者一个更出名的名字:布朗运动过程

设x(t)是一个独立增量过程,即随机过程x(t)的下一阶段增量的概率分布独立于其他任意阶段的概率分布

X(t)是一个关于t的连续函数;

X(t)的增量服从关于时间t的正态分布,即

采样

Gibbs采样

由于Metropolis-Hastings采样的通常小于1。所以Gibbs进一步提高到1。

考虑二维的情形,假设有一个概率分布p(x,y),考虑x坐标相同的两个点A(x1,y1),B(x1,y2)

采样

我们发现

采样

所以得到

采样

采样

基于以上等式,我们发现,在x=x1这条平行于y轴的直线上,如果使用条件分布p(y|x1)做为任何两点之间的转移概率,那么任何两个点之间的转移满足细致平稳条件。

于是我们可以如下构造平面上任意两点之间的状态转移概率矩阵Q

采样

有了如上的转移矩阵Q,我们很容易验证对平面上任意两点X,Y,满足细致平稳条件

采样
采样

以上采样过程中,如图所示,马氏链的转移只是轮换的沿着坐标轴x轴和y轴做转移

采样

很容易将算法扩展到高维的情况中

采样
def pXgivenY(y,m1,m2,s1,s2):
           
return np.random.normal(m1 + (y-m2)/s2,s1)
           
def pYgivenX(x,m1,m2,s1,s2):
           
return np.random.normal(m2 + (x-m1)/s1,s2)
           
def gibbs(N=5000):
           
k=20
           
x0 = np.zeros(N,dtype=float)
           
m1 = 10
           
m2 = 20
           
s1 = 2
           
s2 = 3
           
for i in range(N):
           
y = np.random.rand(1)
           
# 每次采样需要迭代 k 次
           
for j in range(k):
           
x = pXgivenY(y,m1,m2,s1,s2)
           
y = pYgivenX(x,m1,m2,s1,s2)
           
x0[i] = x
           
return x0
           
def f(x):
           
"""目标分布"""
           
return np.exp(-(x-10)**2/10)
           
# 画图
           
N=10000
           
s=gibbs(N)
           
x1 = np.arange(0,17,1)
           
plt.hist(s,bins=x1,fc='c')
           
x1 = np.ar
           
ange(0,17, 0.1)
           
px1 = np.zeros(len(x1))
           
for i in range(len(x1)):
           
px1[i] = f(x1[i])
           
plt.plot(x1, px1*N*10/sum(px1), color='r',linewidth=3)
           
plt.show()