Skip to content

Commit 3288005

Browse files
authored
Update GP Kron notebook with PyMC version 4 (#455)
* Update GP Kron with PyMC v4 * Update links to gp classes and watermark * Update authors and watermark
1 parent 0ff69a3 commit 3288005

File tree

2 files changed

+381
-131
lines changed

2 files changed

+381
-131
lines changed

examples/gaussian_processes/GP-Kron.ipynb

Lines changed: 305 additions & 110 deletions
Large diffs are not rendered by default.

myst_nbs/gaussian_processes/GP-Kron.myst.md

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,23 @@ jupytext:
66
format_version: 0.13
77
jupytext_version: 1.13.7
88
kernelspec:
9-
display_name: gbi_env_py38
9+
display_name: Python 3 (ipykernel)
1010
language: python
11-
name: gbi_env_py38
11+
name: python3
1212
---
1313

14+
(GP-Kron)=
1415
# Kronecker Structured Covariances
1516

16-
PyMC3 contains implementations for models that have Kronecker structured covariances. This patterned structure enables Gaussian process models to work on much larger datasets. Kronecker structure can be exploited when
17+
:::{post} October, 2022
18+
:tags: gaussian process
19+
:category: intermediate
20+
:author: Bill Engels, Raul-ing Average, Christopher Krapu, Danh Phan
21+
:::
22+
23+
+++
24+
25+
PyMC contains implementations for models that have Kronecker structured covariances. This patterned structure enables Gaussian process models to work on much larger datasets. Kronecker structure can be exploited when
1726
- The dimension of the input data is two or greater ($\mathbf{x} \in \mathbb{R}^{d}\,, d \ge 2$)
1827
- The influence of the process across each dimension or set of dimensions is *separable*
1928
- The kernel can be written as a product over dimension, without cross terms:
@@ -28,17 +37,17 @@ $$
2837

2938
These implementations support the following property of Kronecker products to speed up calculations, $(\mathbf{K}_1 \otimes \mathbf{K}_2)^{-1} = \mathbf{K}_{1}^{-1} \otimes \mathbf{K}_{2}^{-1}$, the inverse of the sum is the sum of the inverses. If $K_1$ is $n \times n$ and $K_2$ is $m \times m$, then $\mathbf{K}_1 \otimes \mathbf{K}_2$ is $mn \times mn$. For $m$ and $n$ of even modest size, this inverse becomes impossible to do efficiently. Inverting two matrices, one $n \times n$ and another $m \times m$ is much easier.
3039

31-
This structure is common in spatiotemporal data. Given that there is Kronecker structure in the covariance matrix, this implementation is exact -- not an approximation to the full Gaussian process. PyMC3 contains two implementations that follow the same pattern as `gp.Marginal` and `gp.Latent`. For Kronecker structured covariances where the data likelihood is Gaussian, use `gp.MarginalKron`. For Kronecker structured covariances where the data likelihood is non-Gaussian, use `gp.LatentKron`.
40+
This structure is common in spatiotemporal data. Given that there is Kronecker structure in the covariance matrix, this implementation is exact -- not an approximation to the full Gaussian process. PyMC contains two implementations that follow the same pattern as {class}`gp.Marginal <pymc.gp.Marginal>` and {class}`gp.Latent <pymc.gp.Latent>`. For Kronecker structured covariances where the data likelihood is Gaussian, use {class}`gp.MarginalKron <pymc.gp.MarginalKron>`. For Kronecker structured covariances where the data likelihood is non-Gaussian, use {class}`gp.LatentKron <pymc.gp.LatentKron>`.
3241

33-
Our implementations follow [Saatchi's Thesis](http://mlg.eng.cam.ac.uk/pub/authors/#Saatci). `MarginalKron` follows "Algorithm 16" using the Eigendecomposition, and `LatentKron` follows "Algorithm 14", and uses the Cholesky decomposition.
42+
Our implementations follow [Saatchi's Thesis](http://mlg.eng.cam.ac.uk/pub/authors/#Saatci). `gp.MarginalKron` follows "Algorithm 16" using the Eigendecomposition, and `gp.LatentKron` follows "Algorithm 14", and uses the Cholesky decomposition.
3443

3544
+++
3645

3746
## Using `MarginalKron` for a 2D spatial problem
3847

39-
The following is a canonical example of the usage of `MarginalKron`. Like `Marginal`, this model assumes that the underlying GP is unobserved, but the sum of the GP and normally distributed noise are observed.
48+
The following is a canonical example of the usage of `gp.MarginalKron`. Like `gp.Marginal`, this model assumes that the underlying GP is unobserved, but the sum of the GP and normally distributed noise are observed.
4049

41-
For the simulated data set, we draw one sample from a Gaussian process with inputs in two dimensions whose covariance is Kronecker structured. Then we use `MarginalKron` to recover the unknown Gaussian process hyperparameters $\theta$ that were used to simulate the data.
50+
For the simulated data set, we draw one sample from a Gaussian process with inputs in two dimensions whose covariance is Kronecker structured. Then we use `gp.MarginalKron` to recover the unknown Gaussian process hyperparameters $\theta$ that were used to simulate the data.
4251

4352
+++
4453

@@ -50,16 +59,16 @@ We'll simulate a two dimensional data set and display it as a scatter plot whose
5059
import arviz as az
5160
import matplotlib as mpl
5261
import numpy as np
53-
import pymc3 as pm
54-
55-
from numpy.random import default_rng
62+
import pymc as pm
5663
5764
plt = mpl.pyplot
5865
%matplotlib inline
66+
%config InlineBackend.figure_format = 'retina'
5967
```
6068

6169
```{code-cell} ipython3
62-
rng = default_rng(827)
70+
RANDOM_SEED = 12345
71+
rng = np.random.default_rng(RANDOM_SEED)
6372
6473
# One dimensional column vectors of inputs
6574
n1, n2 = (50, 30)
@@ -147,7 +156,8 @@ x1new = np.linspace(5.1, 7.1, 20)
147156
x2new = np.linspace(-0.5, 3.5, 40)
148157
Xnew = pm.math.cartesian(x1new[:, None], x2new[:, None])
149158
150-
mu, var = gp.predict(Xnew, point=mp, diag=True)
159+
with model:
160+
mu, var = gp.predict(Xnew, point=mp, diag=True)
151161
```
152162

153163
```{code-cell} ipython3
@@ -163,11 +173,11 @@ plt.title("observed data 'y' (circles) with predicted mean (squares)");
163173

164174
## `LatentKron`
165175

166-
Like the `gp.Latent` implementation, the `LatentKron` implementation specifies a Kronecker structured GP regardless of context. **It can be used with any likelihood function, or can be used to model a variance or some other unobserved processes**. The syntax follows that of `gp.Latent` exactly.
176+
Like the `gp.Latent` implementation, the `gp.LatentKron` implementation specifies a Kronecker structured GP regardless of context. **It can be used with any likelihood function, or can be used to model a variance or some other unobserved processes**. The syntax follows that of `gp.Latent` exactly.
167177

168178
### Example 1
169179

170-
To compare with `MarginalLikelihood`, we use same example as before where the noise is normal, but the GP itself is not marginalized out. Instead, it is sampled directly using NUTS. It is very important to note that `LatentKron` does not require a Gaussian likelihood like `MarginalKron`; rather, any likelihood is admissible.
180+
To compare with `MarginalLikelihood`, we use same example as before where the noise is normal, but the GP itself is not marginalized out. Instead, it is sampled directly using NUTS. It is very important to note that `gp.LatentKron` does not require a Gaussian likelihood like `gp.MarginalKron`; rather, any likelihood is admissible.
171181

172182
```{code-cell} ipython3
173183
with pm.Model() as model:
@@ -204,7 +214,8 @@ az.plot_trace(
204214
tr,
205215
var_names=["ls1", "ls2", "eta", "sigma"],
206216
lines={"ls1": l1_true, "ls2": l2_true, "eta": eta_true, "sigma": sigma_true},
207-
);
217+
)
218+
plt.tight_layout()
208219
```
209220

210221
```{code-cell} ipython3
@@ -213,20 +224,43 @@ x2new = np.linspace(-0.5, 3.5, 40)
213224
Xnew = pm.math.cartesian(x1new[:, None], x2new[:, None])
214225
215226
with model:
216-
fnew = gp.conditional("fnew", Xnew)
227+
fnew = gp.conditional("fnew3", Xnew, jitter=1e-6)
228+
229+
with model:
230+
ppc = pm.sample_posterior_predictive(tr, var_names=["fnew3"])
231+
```
232+
233+
```{code-cell} ipython3
234+
x1new = np.linspace(5.1, 7.1, 20)[:, None]
235+
x2new = np.linspace(-0.5, 3.5, 40)[:, None]
236+
Xnew = pm.math.cartesian(x1new, x2new)
237+
x1new.shape, x2new.shape, Xnew.shape
238+
```
239+
240+
```{code-cell} ipython3
241+
with model:
242+
fnew = gp.conditional("fnew", Xnew, jitter=1e-6)
243+
```
217244

245+
```{code-cell} ipython3
218246
with model:
219-
ppc = pm.sample_posterior_predictive(tr, 200, var_names=["fnew"])
247+
ppc = pm.sample_posterior_predictive(tr, var_names=["fnew"])
220248
```
221249

222-
Below we show the original data set as colored circles, and the mean of the conditional samples as colored squares. The results closely follow those given by the `MarginalKron` implementation.
250+
Below we show the original data set as colored circles, and the mean of the conditional samples as colored squares. The results closely follow those given by the `gp.MarginalKron` implementation.
223251

224252
```{code-cell} ipython3
225253
fig = plt.figure(figsize=(14, 7))
226254
m = plt.scatter(X[:, 0], X[:, 1], s=30, c=y, marker="o", norm=norm, cmap=cmap)
227255
plt.colorbar(m)
228256
plt.scatter(
229-
Xnew[:, 0], Xnew[:, 1], s=30, c=np.mean(ppc["fnew"], axis=0), marker="s", norm=norm, cmap=cmap
257+
Xnew[:, 0],
258+
Xnew[:, 1],
259+
s=30,
260+
c=np.mean(ppc.posterior_predictive["fnew"].sel(chain=0), axis=0),
261+
marker="s",
262+
norm=norm,
263+
cmap=cmap,
230264
)
231265
plt.ylabel("x2"), plt.xlabel("x1")
232266
plt.title("observed data 'y' (circles) with mean of conditional, or predicted, samples (squares)");
@@ -241,11 +275,32 @@ axs = axs.ravel()
241275
for i, ax in enumerate(axs):
242276
ax.axis("off")
243277
ax.scatter(X[:, 0], X[:, 1], s=20, c=y, marker="o", norm=norm, cmap=cmap)
244-
ax.scatter(Xnew[:, 0], Xnew[:, 1], s=20, c=ppc["fnew"][i], marker="s", norm=norm, cmap=cmap)
278+
ax.scatter(
279+
Xnew[:, 0],
280+
Xnew[:, 1],
281+
s=20,
282+
c=ppc.posterior_predictive["fnew"].sel(chain=0)[i],
283+
marker="s",
284+
norm=norm,
285+
cmap=cmap,
286+
)
245287
ax.set_title(f"Sample {i+1}", fontsize=24)
246288
```
247289

290+
## Authors
291+
* Authored by [Bill Engels](https://github.com/bwengals), 2018
292+
* Updated by [Raul-ing Average](https://github.com/CloudChaoszero), March 2021
293+
* Updated by [Christopher Krapu](https://github.com/ckrapu), July 2021
294+
* Updated to PyMC 4.x by [Danh Phan](https://github.com/danhphan), November 2022
295+
296+
+++
297+
298+
## Watermark
299+
248300
```{code-cell} ipython3
249301
%load_ext watermark
250-
%watermark -n -u -v -iv -w
302+
%watermark -n -u -v -iv -w -p aesara,aeppl,xarray
251303
```
304+
305+
:::{include} ../page_footer.md
306+
:::

0 commit comments

Comments
 (0)