高斯过程#
GP 基础知识#
有时,模型中未知的参数或变量不是标量值或固定长度的向量,而是一个函数。高斯过程 (GP) 可以用作先验概率分布,其支撑集是连续函数空间。函数 \(f(x)\) 的 GP 先验通常写为:
函数值被建模为从多元正态分布中抽取的样本,该分布由均值函数 \(m(x)\) 和协方差函数 \(k(x, x')\) 参数化。由于多元正态分布的边缘化和条件化属性,高斯过程是作为函数先验的便捷选择。通常,边缘分布在 \(f(x)\) 上进行评估,在推理步骤中。然后,条件分布用于预测新点 \(x_*\) 处的函数值 \(f(x_*)\)。
\(f(x)\) 和 \(f(x_*)\) 的联合分布是多元正态分布:
从联合分布开始,得到 \(f(x)\) 的边缘分布,为 \(\text{N}(m(x),\, k(x, x'))\)。条件分布为:
注意
有关 GP 的更多信息,请查看 Rasmussen & Williams 的书籍 Gaussian Processes for Machine Learning,或 D. Mackay 的 这篇介绍。
PyMC 是一个非常适合使用完全贝叶斯高斯过程模型的环境。PyMC 中的 GP 具有清晰的语法和高度可组合性,并且包含许多预定义的协方差函数(或核函数)、均值函数和多种 GP 实现。GP 被视为可以在更大或分层模型中使用的分布,而不仅仅是作为独立的回归模型。
均值和协方差函数#
使用过 GPy 或 GPflow Python 包的用户会发现构造均值和协方差函数的语法有些熟悉。首次实例化时,均值和协方差函数会被参数化,但尚未给出其输入。协方差函数还必须提供输入矩阵的维度数量,以及索引这些维度中哪些维度是它们要操作的列表。这种设计的原因是为了可以构造协方差函数,这些函数是其他协方差函数的组合。
例如,要构造一个指数平方协方差函数,该函数对表示三个预测变量的三列矩阵的第二列和第三列进行操作:
ls = [2, 5] # the lengthscales
cov_func = pm.gp.cov.ExpQuad(input_dim=3, ls=ls, active_dims=[1, 2])
这里 ls
或长度尺度参数是二维的,允许第二维和第三维具有不同的长度尺度。我们必须指定 input_dim
(X
的总列数)和 active_dims
(协方差函数将作用于的列或维度)的原因是,cov_func
实际上尚未看到输入数据。active_dims
参数是可选的,默认值为输入矩阵的所有列。
PyMC 中的协方差函数密切遵循核函数的代数规则,这允许用户将协方差函数组合成新的函数,例如:
两个协方差函数的和也是协方差函数
cov_func = pm.gp.cov.ExpQuad(...) + pm.gp.cov.ExpQuad(...)
两个协方差函数的积也是协方差函数
cov_func = pm.gp.cov.ExpQuad(...) * pm.gp.cov.Periodic(...)
协方差函数与标量的积(或和)也是协方差函数
cov_func = eta**2 * pm.gp.cov.Matern32(...)
定义协方差函数后,它现在是一个可以通过调用 cov_func(x, x)
(或 mean_func(x)
)来评估的函数。由于 PyMC 构建在 PyTensor 之上,因此定义和试验非标准协方差和均值函数相对容易。有关更多信息,请查看关于协方差函数的 教程。
GP 实现#
PyMC 包含多种 GP 实现,包括边缘模型和潜在变量模型,以及一些快速近似。它们的使用都遵循类似的模式:首先,使用均值函数和协方差函数实例化 GP。然后,可以将 GP 对象加在一起,从而可以仔细地建模和分离函数特征。最后,在 GP 对象上调用 prior
、marginal_likelihood
或 conditional
方法之一,以实际构造表示函数先验的 PyMC 随机变量。
以 gp.Latent
为例,首先指定 GP 的语法是:
gp = pm.gp.Latent(mean_func, cov_func)
第一个参数是均值函数,第二个参数是协方差函数。我们已经创建了 GP 对象,但我们尚未明确它将作为哪个函数的先验,输入是什么,或者它将以哪些参数为条件。
注意
gp.Marginal
类和类似的类没有 prior
方法。相反,它们有一个 marginal_likelihood
方法,其使用方式类似,但具有额外的必需参数,例如观测数据、噪声或其他,具体取决于实现。请参阅笔记本示例。conditional
方法的工作方式类似。
调用 prior
方法将创建一个 PyMC 随机变量,该变量表示潜在函数 \(f(x) = \mathbf{f}\)
f = gp.prior("f", X)
f
是一个随机变量,可以在 PyMC 模型中像任何其他类型的随机变量一样使用。第一个参数是表示我们放置先验的函数的随机变量的名称。第二个参数是函数的输入,先验是关于该函数的,X
。输入通常是已知的并且存在于数据中,但它们也可以是 PyMC 随机变量。如果输入是 PyTensor 张量或 PyMC 随机变量,则需要给出 shape
。
通常在此时,对模型执行推理。conditional
方法创建任意 \(x_*\) 输入点 \(f(x_*)\) 处潜在函数的条件分布或预测分布。要构造条件分布,我们写:
f_star = gp.conditional("f_star", X_star)
加性 GP#
PyMC 中的 GP 实现的构造方式使其易于定义加性 GP 并从各个 GP 组件中采样。我们可以写:
gp1 = pm.gp.Marginal(mean_func1, cov_func1)
gp2 = pm.gp.Marginal(mean_func2, cov_func2)
gp3 = gp1 + gp2
GP 对象必须具有相同的类型,gp.Marginal
不能添加到 gp.Latent
。
考虑两个独立的 GP 分布函数,\(f_1(x) \sim \mathcal{GP}\left(m_1(x),\, k_1(x, x')\right)\) 和 \(f_2(x) \sim \mathcal{GP}\left( m_2(x),\, k_2(x, x')\right)\)。\(f_1,\, f_1^*,\, f_2,\, f_2^*,\, f_1 + f_2\) 和 \(f_1^* + f_2^*\) 的联合分布为:
使用联合分布来获得 \(f_1^*\) 的条件分布,并从中分解出 \(f_1 + f_2\) 的贡献,我们得到:
这些方程显示了如何将 GP 模型分解为各个组件,以了解每个组件如何贡献于数据。有关更多信息,请查看 David Duvenaud 的博士论文。
PyMC 中的 GP 对象会自动跟踪这些边缘分布。以下代码草图显示了如何定义 \(f_2^*\) 的条件分布。我们在示例中使用 gp.Marginal
,但同样的方法适用于其他实现。第一个代码块拟合 GP 先验。为了简洁起见,我们将 \(f_1 + f_2\) 表示为 \(f\):
with pm.Model() as model:
gp1 = pm.gp.Marginal(mean_func1, cov_func1)
gp2 = pm.gp.Marginal(mean_func2, cov_func2)
# gp represents f1 + f2.
gp = gp1 + gp2
f = gp.marginal_likelihood("f", X, y, sigma)
idata = pm.sample(1000)
要构造 gp1
或 gp2
的条件分布,我们还需要包含额外的参数 X
、y
和 sigma
:
with model:
# conditional distributions of f1 and f2
f1_star = gp1.conditional("f1_star", X_star,
given={"X": X, "y": y, "sigma": sigma, "gp": gp})
f2_star = gp2.conditional("f2_star", X_star,
given={"X": X, "y": y, "sigma": sigma, "gp": gp})
# conditional of f1 + f2, `given` not required
f_star = gp.conditional("f_star", X_star)
第二个代码块生成条件分布。请注意,\(f1\) 和 \(f2\) 的条件分布需要额外的参数,但 \(f\) 则不需要。这是因为当在 gp
上调用 .marginal_likelihood
时,这些参数会被缓存。
注意
在构造条件分布时,必须将额外的参数 X
、y
、sigma
和 gp
作为名为 given
的字典提供!
由于未调用 gp1
或 gp2
的边缘似然方法,因此它们的条件分布需要提供所需的输入。与先验相同,f_star
、f1_star
和 f2_star
是随机变量,现在可以像 PyMC 中的任何其他随机变量一样使用。
请查看 笔记本,以获取 PyMC 中 GP 功能用法的详细演示。