zoukankan      html  css  js  c++  java
  • 采样

    随机采样

    采样是根据某种分布去生成一些数据点。最基本的假设是认为我们可以获得服从均匀分布的随机数,再根据均匀分布生成复杂分布的采样。对于离散分布的采样,可以把概率分布向量看作一个区间段,然后判断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轴做转移

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

    Python代码:

    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()
    

  • 相关阅读:
    排序算法(2)-冒泡排序及优化
    [转]Java泛型详解
    [转]Java泛型详解
    02-机器学习_(knn分类算法与应用)
    01-机器学习_(python数据类型详解)
    流量运营项目说明
    数据仓库星型模型vs雪花模型
    数据仓库命名规范
    数据仓库建模
    数据仓库
  • 原文地址:https://www.cnblogs.com/kexinxin/p/9858530.html
Copyright © 2011-2022 走看看