我们来考虑一个简单的线性回归问题:
- 假设我们需要建模的函数为
- 我们有一些观察数据$ \bold x = [x_1, …,x_N]^T, \bold y=[y_1,…,y_N]^T, y_i=f(x_i)$
- 我们想预测函数在一些未观察点的值
采用高斯过程建模函数
高斯过程的核心思想是采用多元高斯分布建模函数。输入空间的每一个点都对应一个随机变量,所有这些随机变量的联合分布符合多元高斯分布。
那么,上面两句话是什么意思呢?我们从简单的二元高斯过程开始。假设输入对应的函数值满足二元高斯分布:
通常我们以3维的钟形概率密度函数的形式表示这个分布,这里我们不这样做,而是采样。我们采样10次,的值对应,的值对应,并用直线连接两点:
def plot_unit_gaussian_samples(D):
p = figure(plot_width=800, plot_height=500,
title='Samples from a unit {}D Gaussian'.format(D))
xs = np.linspace(0, 1, D)
for color in Category10[10]:
ys = np.random.multivariate_normal(np.zeros(D), np.eye(D))
p.line(xs, ys, line_width=1, color=color)
return p
show(plot_unit_gaussian_samples(2))
如上图,看起来我们采样了10个线性函数。如果我们采用20维高斯分布呢?
show(plot_unit_gaussian_samples(20))
曲线看起来更像是某种函数了,但是噪音太大了。我们思考一下我们想从样本中得到什么以及如何改进高斯分布使得曲线更平滑呢。
多元高斯分布有两个参数,平均值和协方差矩阵。如果我们改变平均值,我们只会改变取值的整体趋势,而不会改变锯齿形状。所以我们让高斯过程的均值保持为0。
我们想达到一种平滑的效果:如果两个点距离相近,则函数的取值页比较接近。
N维高斯分布的的协方差矩阵的维度为,第个元素为.也就是说是一个对称矩阵,保存了随机变量的所有协方差值。
使用核函数平滑函数
那么我们应该如何定义协方差函数呢?我们选择平方指数核:
当时函数取值为1,随远离而趋近于0。
因此为了达到平滑的效果,我们定义点对应的随机变量的协方差为. 越接近,的协方差值越高。
采用以上的协方差矩阵,我们再一次从20维高斯分布中采样:
def k(xs, ys, sigma=1, l=1):
"""Sqared Exponential kernel as above but designed to return the whole
covariance matrix - i.e. the pairwise covariance of the vectors xs & ys.
Also with two parameters which are discussed at the end."""
# Pairwise difference matrix.
dx = np.expand_dims(xs, 1) - np.expand_dims(ys, 0)
return (sigma ** 2) * np.exp(-((dx / l) ** 2) / 2)
def m(x):
"""The mean function. As discussed, we can let the mean always be zero."""
return np.zeros_like(x)
p = figure(plot_width=800, plot_height=500)
D = 20
xs = np.linspace(0, 1, D)
for color in Category10[10]:
ys = np.random.multivariate_normal(m(xs), k(xs, xs))
p.circle(xs, ys, size=3, color=color)
p.line(xs, ys, line_width=1, color=color)
show(p)
我们增加维度,看一下采样的结果:
n = 100
xs = np.linspace(-5, 5, n)
K = k(xs, xs)
mu = m(xs)
p = figure(plot_width=800, plot_height=500)
for color in Category10[5]:
ys = np.random.multivariate_normal(mu, K)
p.line(xs, ys, line_width=2, color=color)
show(p)
采用先验和观察进行预测
现在我们有了一个函数分布,如何使用训练数据去建模一个隐藏的函数并预测取值呢?
首先我们需要一些训练数据,为了得到训练数据,我们首先创建一个秘密的函数.
这里我们采用多项式函数:
当然我们也可以选择其他函数
# coefs[i] is the coefficient of x^i
coefs = [6, -2.5, -2.4, -0.1, 0.2, 0.03]
def f(x):
total = 0
for exp, coef in enumerate(coefs):
total += coef * (x ** exp)
return total
xs = np.linspace(-5.0, 3.5, 100)
ys = f(xs)
p = figure(plot_width=800, plot_height=400, x_axis_label='x',
y_axis_label='f(x)', title='The hidden function f(x)')
p.line(xs, ys, line_width=2)
show(p)
目前我们采用多元高斯分布建模:
其中
这是一个先验的分布,表示在未得到任何观察的情况下给定输入 输出的分布。
我们有观察数据输入为,输出$ \bold y = f(\bold x)\bold x_\bold y_ = f(\bold x_*)$.
x_obs = np.array([-4, -1.5, 0, 1.5, 2.5, 2.7])
y_obs = f(x_obs)
x_s = np.linspace(-8, 7, 80)
回忆高斯过程的定义,我们如此来建模的联合分布:
其中:.
但是这是对的建模,我们需要的是的分布,这里不做推导,直接给出结果:
其中
目前我们有了的后验概率分布。
K = k(x_obs, x_obs)
K_s = k(x_obs, x_s)
K_ss = k(x_s, x_s)
K_sTKinv = np.matmul(K_s.T, np.linalg.pinv(K))
mu_s = m(x_s) + np.matmul(K_sTKinv, y_obs - m(x_obs))
Sigma_s = K_ss - np.matmul(K_sTKinv, K_s)
我们可以采用这两个参数从条件概率分布中采样:
p = figure(plot_width=800, plot_height=600, y_range=(-7, 8))
y_true = f(x_s)
p.line(x_s, y_true, line_width=3, color='black', alpha=0.4,
line_dash='dashed', legend='True f(x)')
p.cross(x_obs, y_obs, size=20, legend='Training data')
stds = np.sqrt(Sigma_s.diagonal())
stds = np.sqrt(Sigma_s.diagonal())
err_xs = np.concatenate((x_s, np.flip(x_s, 0)))
err_ys = np.concatenate((mu_s + 2 * stds, np.flip(mu_s - 2 * stds, 0)))
p.patch(err_xs, err_ys, alpha=0.2, line_width=0, color='grey',
legend='Uncertainty')
for color in Category10[3]:
y_s = np.random.multivariate_normal(mu_s, Sigma_s)
p.line(x_s, y_s, line_width=1, color=color)
p.line(x_s, mu_s, line_width=3, color='blue', alpha=0.4, legend='Mean')
show(p)