【贝叶斯分析⑦】高斯过程

贝叶斯框架下, 可以用高斯过程来估计一个函数 f : R→R. 对于每个xi, f(xi)可以用一个均值方差暂未知的高斯分布来建模。因为连续空间的xi可以有无限个,拟合一个函数的高斯过程其实一个无限维的多元高斯。实际中,不管是我们的给定数据{(x, y)},还是测试点{x*}的个数都是有限的。因此无论是高斯过程先验还是还是高斯过程后验都是有限维的。因为多元高斯分布的任意有限自己还是多远高斯分布,所以先验和后验也都可以都用高斯建模。

实践中,高斯过程先验(一个多元高斯)的均值通常统一设为0(因为我们通常对于需要拟合的函数取值并没有太多先验知识),而协方差矩阵则用核函数K(x,x')来建模(i.e., cov[yi, yj] = K(xi, xj) = η exp(-ρ * SED(xi, xj)) )。协方差描述的是,当x变化时,y是如何变化的.而核函数(高斯核)的特点是x,x'越近,返回值越大。用高斯核作协方差可以理解为一个小的x扰动会导致较小的y变化,一个大的x变化会导致较大的y变化。

此外,现实中观测值一定会受到噪声的影响,因此我们对观测值建模需要引入一项高斯白噪声:yi ~ f(xi) +  εi. ε ~ N(0, σ)。这样协方差函数就改写成 cov[yi, yj] = K(xi, xj) + σ δij.

注意高斯过程的先验建模中有参数θ = {η, ρ, σ}, 但为了强调高斯过程是一个非参方法,这些参数称为“超参”。这些参数将用贝叶斯推断来估计。

总结一下,我们有各种假设先验(θ的先验分布,f(x) ~ N(0, K(x, x'))),高斯似然 y | θ ~ N(0, K(x, x) + σ I), 然后根据y的观测值可以推断出超参的分布。预测就更简单了,高斯过程的一个优点就是后验分布可以直接求得解析解,要预测的点记为(x*, f(x*)), 后验分布可得 f(x*) | x, x*, y ~ N(μ*, Σ*), 其中 μ* = K(X*, X) (K(x, x) + σ I)^(-1) y, Σ* = K(x*, x*) - K(x*, x) (K(x, x) + σ I)^(-1) K(x, x*).详细推导可见Gaussian Processes for Machine Learning一书的Sec. 2.2, 此外可以记住的一个常见结论如下:

然后是代码实践,用20个带噪的正弦函数观测点来拟合函数:

#%matplotlib inline
import pymc3 as pm
import numpy as np
import theano.tensor as tt
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist


if __name__ == "__main__":
    np.random.seed(1)
    squared_distance = lambda x, y: cdist(x.reshape(-1,1), y.reshape(-1,1)) ** 2 #SED function

    N = 20         # number of training points.
    n = 100         # number of test points.
    
    np.random.seed(1)
    f = lambda x: np.sin(x).flatten()

    x = np.random.uniform(0, 10, size=N)
    y = np.random.normal(np.sin(x), np.sqrt(0.01))

    plt.plot(x, y, 'o')
    plt.xlabel('$x$', fontsize=16)
    plt.ylabel('$f(x)$', fontsize=16, rotation=0)
    
    with pm.Model() as GP:
        mu = np.zeros(N)
        eta = pm.HalfCauchy('eta', 0.1)
        rho = pm.HalfCauchy('rho', 1)
        sigma = pm.HalfCauchy('sigma', 1)
        
        D = squared_distance(x, x) #SED(x,x)
        
        K = tt.fill_diagonal(eta * pm.math.exp(-rho * D), eta + sigma) #(K(x, x) + σ I)
        
        obs = pm.MvNormal('obs', mu, cov=K, observed=y)
        
    
        test_points = np.linspace(0, 10, 100)
        D_pred = squared_distance(test_points, test_points) #SED(x*,x*)
        D_off_diag = squared_distance(x, test_points) #SED(x,x*) n * N
        
        K_oo = eta * pm.math.exp(-rho * D_pred) #K(x*,x*)
        K_o = eta * pm.math.exp(-rho * D_off_diag) #K(x,x*)

        inv_K = tt.nlinalg.matrix_inverse(K)
        
        mu_post = pm.Deterministic('mu_post', pm.math.dot(pm.math.dot(K_o.T, inv_K), y))
        SIGMA_post = pm.Deterministic('SIGMA_post', K_oo - pm.math.dot(pm.math.dot(K_o.T, inv_K), K_o))        

        step = pm.Metropolis()                
        start = pm.find_MAP()
        trace = pm.sample(1000, step = step, start=start, nchains = 1)        
        varnames = ['eta', 'rho', 'sigma']
        chain = trace[100:]
        pm.traceplot(chain, varnames)
        
        plt.figure()
        y_pred = [np.random.multivariate_normal(m, S) for m,S in zip(chain['mu_post'], chain['SIGMA_post'])]        
        for yp in y_pred:
            plt.plot(test_points, yp, 'r-', alpha=0.1)
        
        plt.plot(x, y, 'bo')
        plt.xlabel('$x$', fontsize=16)
        plt.ylabel('$f(x)$', fontsize=16, rotation=0)

输出:

发布了44 篇原创文章 · 获赞 16 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_32464407/article/details/81270903