本文搬运于个人博客,欢迎点击这里查看原博文。

高斯过程 Gaussian Processes 是概率论和数理统计中随机过程的一种,是多元高斯分布的扩展,被应用于机器学习、信号处理等领域。本文对高斯过程进行公式推导、原理阐述、可视化以及代码实现,介绍了以高斯过程为基础的高斯过程回归 Gaussian Process Regression 基本原理、超参优化、高维输入等问题。

目录一元高斯分布

多元高斯分布

无限元高斯分布?

核函数(协方差函数)

高斯过程可视化

高斯过程回归实现

超参数优化

多维输入

高斯过程回归的优缺点

一元高斯分布

我们从最简单最常见的一元高斯分布开始,其概率密度函数为

其中

分别表示均值和方差,这个概率密度函数曲线画出来就是我们熟悉的钟形曲线,均值和方差唯一地决定了曲线的形状。

多元高斯分布

从一元高斯分布推广到多元高斯分布,假设各维度之间相互独立

其中

分别是第 1 维、第二维... 的均值和方差。对上式向量和矩阵表示上式,令

代入公式(2)得到

其中

是均值向量,

为协方差矩阵,由于我们假设了各维度直接相互独立,因此

是一个对角矩阵。在各维度变量相关时,上式的形式仍然一致,但此时协方差矩阵

不再是对角矩阵,只具备半正定和对称的性质。上式通常也简写为

无限元高斯分布?

在多元高斯分布的基础上考虑进一步扩展,假设有无限多维呢?用一个例子来展示这个扩展的过程(来源:MLSS 2012: J. Cunningham - Gaussian Processes for Machine Learning),假设我们在周一到周四每天的 7:00 测试了 4 次心率,如下图中 4 个点,可能的高斯分布如图所示(高瘦的那条)。这是一个一元高斯分布,只有每天 7: 00 的心率这个维度。

现在考虑不仅在每天的 7: 00 测心率(横轴),在 8:00 时也进行测量(纵轴),这个时候变成两个维度(二元高斯分布),如下图所示

更进一步,如果我们在每天的无数个时间点都进行测量,则变成了下图的情况。注意下图中把测量时间作为横轴,则每个颜色的一条线代表一个(无限个时间点的测量)无限维的采样。当对每次对无限维进行采样得到无限多个点时,其实可以理解为我们采样得到了一个函数。

当从函数的视角去看待采样,理解了每次采样无限维相当于采样一个函数之后,原本的概率密度函数不再是点的分布 ,而变成了函数的分布。这个无限元高斯分布即称为高斯过程。高斯过程正式地定义为:对于所有

都服从多元高斯分布,则称

是一个高斯过程,表示为

这里

表示均值函数(Mean function),返回各个维度的均值;

为协方差函数 Covariance Function(也叫核函数 Kernel Function)返回两个向量各个维度之间的协方差矩阵。一个高斯过程为一个均值函数和协方差函数唯一地定义,并且一个高斯过程的有限维度的子集都服从一个多元高斯分布(为了方便理解,可以想象二元高斯分布两个维度各自都服从一个高斯分布)。

核函数(协方差函数)

核函数是一个高斯过程的核心,核函数决定了一个高斯过程的性质。核函数在高斯过程中起生成一个协方差矩阵(相关系数矩阵)来衡量任意两个点之间的“距离”。不同的核函数有不同的衡量方法,得到的高斯过程的性质也不一样。最常用的一个核函数为高斯核函数,也成为径向基函数 RBF。其基本形式如下。其中

是高斯核的超参数。

高斯核函数的 python 实现如下

import numpy as np

def gaussian_kernel(x1, x2, l=1.0, sigma_f=1.0):

"""Easy to understand but inefficient."""

m, n = x1.shape[0], x2.shape[0]

dist_matrix = np.zeros((m, n), dtype=float)

for i in range(m):

for j in range(n):

dist_matrix[i][j] = np.sum((x1[i] - x2[j]) ** 2)

return sigma_f ** 2 * np.exp(- 0.5 / l ** 2 * dist_matrix)

def gaussian_kernel_vectorization(x1, x2, l=1.0, sigma_f=1.0):

"""More efficient approach."""

dist_matrix = np.sum(x1**2, 1).reshape(-1, 1) + np.sum(x2**2, 1) - 2 * np.dot(x1, x2.T)

return sigma_f ** 2 * np.exp(-0.5 / l ** 2 * dist_matrix)

x = np.array([700, 800, 1029]).reshape(-1, 1)

print(gaussian_kernel_vectorization(x, x, l=500, sigma=10))

输出的向量

与自身的协方差矩阵为

[[100. 98.02 80.53]

[ 98.02 100. 90.04]

[ 80.53 90.04 100. ]]

高斯过程可视化

下图是高斯过程的可视化,其中蓝线是高斯过程的均值,浅蓝色区域 95% 置信区间(由协方差矩阵的对角线得到),每条虚线代表一个函数采样(这里用了 100 维模拟连续无限维)。左上角第一幅图是高斯过程的先验(这里用了零均值作为先验),后面几幅图展示了当观测到新的数据点的时候,高斯过程如何更新自身的均值函数和协方差函数。

接下来我们用公式推导上图的过程。将高斯过程的先验表示为

,对应左上角第一幅图,如果现在我们观测到一些数据

,并且假设

服从联合高斯分布

其中

,则有

上述式子表明了给定数据

之后函数的分布

仍然是一个高斯过程,具体的推导可见 Gaussian Processes for Machine Learning。这个式子可以看出一些有趣的性质,均值

实际上是观测点

的一个线性函数,协方差项

的第一部分是我们的先验的协方差,减掉的后面的那一项实际上表示了观测到数据后函数分布不确定性的减少,如果第二项非常接近于 0,说明观测数据后我们的不确定性几乎不变,反之如果第二项非常大,则说明不确定性降低了很多。

上式其实就是高斯过程回归的基本公式,首先有一个高斯过程先验分布,观测到一些数据(机器学习中的训练数据),基于先验和一定的假设(联合高斯分布)计算得到高斯过程后验分布的均值和协方差。

简单高斯过程回归实现

考虑代码实现一个高斯过程回归,API 接口风格采用 sciki-learn fit-predict 风格。由于高斯过程回归是一种非参数化 (non-parametric)的模型,每次的 inference 都需要利用所有的训练数据进行计算得到结果,因此并没有一个显式的训练模型参数的过程,所以 fit 方法只需要将训练数据保存下来,实际的 inference 在 predict 方法中进行。Python 代码如下

from scipy.optimize import minimize

class GPR:

def __init__(self, optimize=True):

self.is_fit = False

self.train_X, self.train_y = None, None

self.params = {"l": 0.5, "sigma_f": 0.2}

self.optimize = optimize

def fit(self, X, y):

# store train data

self.train_X = np.asarray(X)

self.train_y = np.asarray(y)

self.is_fit = True

def predict(self, X):

if not self.is_fit:

print("GPR Model not fit yet.")

return

X = np.asarray(X)

Kff = self.kernel(self.train_X, self.train_X) # (N, N)

Kyy = self.kernel(X, X) # (k, k)

Kfy = self.kernel(self.train_X, X) # (N, k)

Kff_inv = np.linalg.inv(Kff + 1e-8 * np.eye(len(self.train_X))) # (N, N)

mu = Kfy.T.dot(Kff_inv).dot(self.train_y)

cov = Kyy - Kfy.T.dot(Kff_inv).dot(Kfy)

return mu, cov

def kernel(self, x1, x2):

dist_matrix = np.sum(x1**2, 1).reshape(-1, 1) + np.sum(x2**2, 1) - 2 * np.dot(x1, x2.T)

return self.params["sigma_f"] ** 2 * np.exp(-0.5 / self.params["l"] ** 2 * dist_matrix)

def y(x, noise_sigma=0.0):

x = np.asarray(x)

y = np.cos(x) + np.random.normal(0, noise_sigma, size=x.shape)

return y.tolist()

train_X = np.array([3, 1, 4, 5, 9]).reshape(-1, 1)

train_y = y(train_X, noise_sigma=1e-4)

test_X = np.arange(0, 10, 0.1).reshape(-1, 1)

gpr = GPR()

gpr.fit(train_X, train_y)

mu, cov = gpr.predict(test_X)

test_y = mu.ravel()

uncertainty = 1.96 * np.sqrt(np.diag(cov))

plt.figure()

plt.title("l=%.2fsigma_f=%.2f" % (gpr.params["l"], gpr.params["sigma_f"]))

plt.fill_between(test_X.ravel(), test_y + uncertainty, test_y - uncertainty, alpha=0.1)

plt.plot(test_X, test_y, label="predict")

plt.scatter(train_X, train_y, label="train", c="red", marker="x")

plt.legend()

结果如下图,红点是训练数据,蓝线是预测值,浅蓝色区域是 95% 置信区间。真实的函数是一个 cosine 函数,可以看到在训练数据点较为密集的地方,模型预测的不确定性较低,而在训练数据点比较稀疏的区域,模型预测不确定性较高。

超参数优化

上文提到高斯过程是一种非参数模型,没有训练模型参数的过程,一旦核函数、训练数据给定,则模型就被唯一地确定下来。但是核函数本身是有参数的,比如高斯核的参数

,我们称为这种参数为模型的超参数(类似于 k-NN 模型中 k 的取值)。

核函数本质上决定了样本点相似性的度量方法,进行影响到了整个函数的概率分布的形状。上面的高斯过程回归的例子中使用了

的超参数,我们可以选取不同的超参数看看回归出来的效果。

从上图可以看出,

越大函数更加平滑,同时训练数据点之间的预测方差更小,反之

越小则函数倾向于更加“曲折”,训练数据点之间的预测方差更大;

则直接控制方差大小,

越大方差越大,反之亦然。

如何选择最优的核函数参数

呢?答案是最大化在这两个超参数下

出现的概率,通过最大化边缘对数似然(Marginal Log-likelihood)来找到最优的参数,边缘对数似然表示为

具体的实现中,我们在 fit 方法中增加超参数优化这部分的代码,最小化负边缘对数似然。

from scipy.optimize import minimize

class GPR:

def __init__(self, optimize=True):

self.is_fit = False

self.train_X, self.train_y = None, None

self.params = {"l": 0.5, "sigma_f": 0.2}

self.optimize = optimize

def fit(self, X, y):

# store train data

self.train_X = np.asarray(X)

self.train_y = np.asarray(y)

# hyper parameters optimization

def negative_log_likelihood_loss(params):

self.params["l"], self.params["sigma_f"] = params[0], params[1]

Kyy = self.kernel(self.train_X, self.train_X) + 1e-8 * np.eye(len(self.train_X))

loss = 0.5 * self.train_y.T.dot(np.linalg.inv(Kyy)).dot(self.train_y) + 0.5 * np.linalg.slogdet(Kyy)[1] + 0.5 * len(self.train_X) * np.log(2 * np.pi)

return loss.ravel()

if self.optimize:

res = minimize(negative_log_likelihood_loss, [self.params["l"], self.params["sigma_f"]],

bounds=((1e-4, 1e4), (1e-4, 1e4)),

method='L-BFGS-B')

self.params["l"], self.params["sigma_f"] = res.x[0], res.x[1]

self.is_fit = True

将训练、优化得到的超参数、预测结果可视化如下图,可以看到最优的

这里用 scikit-learn 的 GaussianProcessRegressor 接口进行对比

from sklearn.gaussian_process import GaussianProcessRegressor

from sklearn.gaussian_process.kernels import ConstantKernel, RBF

# fit GPR

kernel = ConstantKernel(constant_value=0.2, constant_value_bounds=(1e-4, 1e4)) * RBF(length_scale=0.5, length_scale_bounds=(1e-4, 1e4))

gpr = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=2)

gpr.fit(train_X, train_y)

mu, cov = gpr.predict(test_X, return_cov=True)

test_y = mu.ravel()

uncertainty = 1.96 * np.sqrt(np.diag(cov))

# plotting

plt.figure()

plt.title("l=%.1fsigma_f=%.1f" % (gpr.kernel_.k2.length_scale, gpr.kernel_.k1.constant_value))

plt.fill_between(test_X.ravel(), test_y + uncertainty, test_y - uncertainty, alpha=0.1)

plt.plot(test_X, test_y, label="predict")

plt.scatter(train_X, train_y, label="train", c="red", marker="x")

plt.legend()

得到结果为

,这个与我们实现的优化得到的超参数有一点点不同,可能是实现的细节有所不同导致。

多维输入

我们上面讨论的训练数据都是一维的,高斯过程直接可以扩展于多维输入的情况,直接将输入维度增加即可。

def y_2d(x, noise_sigma=0.0):

x = np.asarray(x)

y = np.sin(0.5 * np.linalg.norm(x, axis=1))

y += np.random.normal(0, noise_sigma, size=y.shape)

return y

train_X = np.random.uniform(-4, 4, (100, 2)).tolist()

train_y = y_2d(train_X, noise_sigma=1e-4)

test_d1 = np.arange(-5, 5, 0.2)

test_d2 = np.arange(-5, 5, 0.2)

test_d1, test_d2 = np.meshgrid(test_d1, test_d2)

test_X = [[d1, d2] for d1, d2 in zip(test_d1.ravel(), test_d2.ravel())]

gpr = GPR(optimize=True)

gpr.fit(train_X, train_y)

mu, cov = gpr.predict(test_X)

z = mu.reshape(test_d1.shape)

fig = plt.figure(figsize=(7, 5))

ax = Axes3D(fig)

ax.plot_surface(test_d1, test_d2, z, cmap=cm.coolwarm, linewidth=0, alpha=0.2, antialiased=False)

ax.scatter(np.asarray(train_X)[:,0], np.asarray(train_X)[:,1], train_y, c=train_y, cmap=cm.coolwarm)

ax.contourf(test_d1, test_d2, z, zdir='z', offset=0, cmap=cm.coolwarm, alpha=0.6)

ax.set_title("l=%.2fsigma_f=%.2f" % (gpr.params["l"], gpr.params["sigma_f"]))

下面是一个二维输入数据的高斯过程回归,左图是没有经过超参优化的拟合效果,右图是经过超参优化的拟合效果。

以上相关的代码放在 toys/GP 。

高斯过程回归的优缺点优点(采用 RBF 作为协方差函数)具有平滑性质,能够拟合非线性数据

高斯过程回归天然支持得到模型关于预测的不确定性(置信区间),直接输出关于预测点值的概率分布

通过最大化边缘似然这一简洁的方式,高斯过程回归可以在不需要交叉验证的情况下给出比较好的正则化效果

缺点高斯过程是一个非参数模型,每次的 inference 都需要对所有的数据点进行(矩阵求逆)。对于没有经过任何优化的高斯过程回归,n 个样本点时间复杂度大概是

,空间复杂度是

,在数据量大的时候高斯过程变得 intractable

高斯过程回归中,先验是一个高斯过程,likelihood 也是高斯的,因此得到的后验仍是高斯过程。在 likelihood 不服从高斯分布的问题中(如分类),需要对得到的后验进行 approximate 使其仍为高斯过程

RBF 是最常用的协方差函数,但在实际中通常需要根据问题和数据的性质选择恰当的协方差函数

参考资料

python 高斯过程_高斯过程 Gaussian Processes 原理、可视化及代码实现相关推荐

  1. python 高斯过程_高斯过程

    主要参考这两篇的内容 http://kingfengji.com/?p=44​ http://kastnerkyle.github.io/posts/introduction-to-gaussian- ...

  2. java版 高斯过程_高斯过程scikit-learn - 异常

    我想使用高斯过程来解决回归任务 . 我的数据如下:每个X向量的长度为37,每个Y向量的长度为8 . 我正在使用 Python 中的 sklearn 包,但尝试使用高斯进程会导致 Exception : ...

  3. ++代码实现 感知机的原理_常见排序算法原理及JS代码实现

    来源:SegmentFault 思否社区 作者:Warren 本文只是将作者学习的过程以及算法理解进行简单的分享,提供多一个角度的理解说明,或许让你的困惑能得以解决(代码或说明若有问题,欢迎留言.联系 ...

  4. 哲学家就餐问题python解决_关于哲学家就餐问题的分析代码.

    ①总体思路: 都去拿左边的筷子,并且最后一个人不能去拿筷子(防止大家都拿了左边的筷子,没有右边的筷子,导致死锁了),解决死锁问题的办法就是同时只允许四位哲学家同时拿起同一边的筷子,这样就能保证一定会有 ...

  5. 高斯过程(Gaussian Processes)原理

    高斯过程(Gaussian Processes, GP)是概率论和数理统计中随机过程的一种,是多元高斯分布的扩展,被应用于机器学习.信号处理等领域.博主在阅读了数篇文章和博客后才算是基本搞懂了GP的原 ...

  6. 1.7. 高斯过程(Gaussian Processes)

    针对机器学习的高斯过程(Gaussian Processes for Machine Learning,即 GPML) 是一个通用的监督学习方法,主要被设计用来解决 回归 问题. 它也可以扩展为 概率 ...

  7. 论文笔记 Inference in Deep Gaussian Processes using Stochastic Gradient Hamiltonia使用随机梯度哈密顿量蒙特卡罗推理深度高斯过程

    使用随机梯度哈密顿量蒙特卡罗推理深度高斯过程 0.摘要 深度高斯过程 (DGP) 是高斯过程的层次概括,它将经过良好校准的不确定性估计与多层模型的高度灵活性相结合. 这些模型的最大挑战之一是精确推断是 ...

  8. python 靶心_手把手教你使用Python实战反欺诈模型|原理+代码

    原标题:手把手教你使用Python实战反欺诈模型|原理+代码 作者 | 萝卜 来源 | 早起Python(ID: zaoqi-python) 本文将基于不平衡数据,使用Python进行 反欺诈模型数据 ...

  9. python异常处理_汇总三大python异常处理、自定义异常、断言原理与用法分析

    本文实例讲述了python异常处理.自定义异常.断言原理与用法.分享给大家供大家参考,具体如下: 什么是异常: 当程序遭遇某些非正常问题的时候就会抛出异常:比如int()只能处理能转化成int的对象, ...

最新文章

  1. HDU1874(Dijstra算法)
  2. 日本奥委会主席否认为争取奥运会主办权行贿
  3. 计算机基础教育德育教学,【家庭教育论文】计算机基础教学的德育教育(共2650字)...
  4. 【转】构建Android平台Google Map应用
  5. Python 之 Python2 和 Python3 的区别
  6. 交流适配器行业调研报告 - 市场现状分析与发展前景预测
  7. Windows下能PING通网关不能打开网页解决方法
  8. 深度学习花书-4.4 约束优化
  9. android根据银行卡卡号判断银行
  10. 全国一级计算机考什么,全国计算机等级考试一级考什么
  11. FFmpeg命令行工具学习(二):播放媒体文件的工具ffplay
  12. C语言ALG什么文件,alg.exe进程是什么
  13. windows10 右下角任务栏 隐藏图标
  14. Java Swing窗体JFrame之设置窗体图标
  15. SaaS后台管理系统
  16. PHP做大转盘抽奖的思路,jQuery实现幸运大转盘(php抽奖程序)抽奖程序
  17. Chapter 22 UDP and TCP 第二十二章UDP和TCP协议作业
  18. post_thumbnail_html,WordPress 文章特色图片(Post Thumbnail)详细介绍和使用
  19. 理清JS中的深拷贝与浅拷贝
  20. 斐波那契回调线怎么画_自动绘制斐波那契回调线的指标

热门文章

  1. docker安装mysql5.7并且配置my.conf
  2. 推荐一个找paper和code的网址
  3. 图像处理之模糊集合原理
  4. 计算机主板电池没电了 会怎么样,主板电池没电了会怎么样_主板电池没电引起罕见故障...
  5. leetcode:514. 自由之路
  6. Linux下生成HTTPS证书申请与颁发方法
  7. 三天打渔,两天晒网Python
  8. C# 计算DataTime的4种时间差(相差天数、相差小时、相差分钟、相差秒)
  9. 中专计算机应用基础知识点,中专计算机应用基础
  10. Cocos Creator 3.x 动态加载 龙骨动画