Skip to content

Commit dd98fe6

Browse files
Update GP Marginal and Latent notebooks to v5 (#549)
* Draft update of BNN notebook * Pre-commit fixes * Address reviewer comments * Debugging * Updated latent and marginal GP notebooks to v5 * Update examples/gaussian_processes/GP-Marginal.myst.md Co-authored-by: Oriol Abril-Pla <[email protected]> * Fixes to marginal and latent notebooks based on feedback * fixed authorship * Removed latent GP notebook from pre-commit exceptions * use extra dependencies table --------- Co-authored-by: Oriol Abril-Pla <[email protected]>
1 parent 8e84ef6 commit dd98fe6

File tree

5 files changed

+449
-558
lines changed

5 files changed

+449
-558
lines changed

.pre-commit-config.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ repos:
2020
(?x)^
2121
^examples/ode_models/ODE_with_manual_gradients\.ipynb$
2222
|examples/samplers/DEMetropolisZ_EfficiencyComparison\.ipynb$
23-
|examples/gaussian_processes/GP-Latent\.ipynb$
2423
|examples/gaussian_processes/GP-MaunaLoa2\.ipynb$
2524
|examples/samplers/MLDA_gravity_surveying\.ipynb$
2625
|examples/howto/sampling_callback\.ipynb$

examples/gaussian_processes/GP-Latent.ipynb

+176-326
Large diffs are not rendered by default.

examples/gaussian_processes/GP-Latent.myst.md

+12-5
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ jupytext:
55
format_name: myst
66
format_version: 0.13
77
kernelspec:
8-
display_name: pymc-dev
8+
display_name: Python 3 (ipykernel)
99
language: python
10-
name: pymc-dev
10+
name: python3
11+
myst:
12+
substitutions:
13+
extra_dependencies: jax numpyro
1114
---
1215

1316
(gp_latent)=
1417
# Gaussian Processes: Latent Variable Implementation
1518

16-
:::{post} Sept 28, 2022
19+
:::{post} June 6, 2023
1720
:tags: gaussian processes, time series
1821
:category: reference, intermediate
1922
:author: Bill Engels
@@ -80,6 +83,9 @@ with latent_gp_model:
8083

8184
The following is an example showing how to specify a simple model with a GP prior using the {class}`gp.Latent` class. We use a GP to generate the data so we can verify that the inference we perform is correct. Note that the likelihood is not normal, but IID Student-T. For a more efficient implementation when the likelihood is Gaussian, use {class}`gp.Marginal`.
8285

86+
:::{include} ../extra_installs.md
87+
:::
88+
8389
```{code-cell} ipython3
8490
import arviz as az
8591
import matplotlib.pyplot as plt
@@ -158,7 +164,7 @@ with pm.Model() as model:
158164
) # add one because student t is undefined for degrees of freedom less than one
159165
y_ = pm.StudentT("y", mu=f, lam=1.0 / sigma, nu=nu, observed=y)
160166
161-
idata = pm.sample(1000, tune=1000, chains=2, cores=1)
167+
idata = pm.sample(1000, tune=1000, chains=2, cores=2, nuts_sampler="numpyro")
162168
```
163169

164170
```{code-cell} ipython3
@@ -349,7 +355,7 @@ with pm.Model() as model:
349355
p = pm.Deterministic("p", pm.math.invlogit(f))
350356
y_ = pm.Bernoulli("y", p=p, observed=y)
351357
352-
idata = pm.sample(1000, chains=2, cores=1)
358+
idata = pm.sample(1000, chains=2, cores=2, nuts_sampler="numpyro")
353359
```
354360

355361
```{code-cell} ipython3
@@ -429,6 +435,7 @@ plt.legend(loc=(0.32, 0.65), frameon=True);
429435
* Created by [Bill Engels](https://github.com/bwengals) in 2017 ([pymc#1674](https://github.com/pymc-devs/pymc/pull/1674))
430436
* Reexecuted by [Colin Caroll](https://github.com/ColCarroll) in 2019 ([pymc#3397](https://github.com/pymc-devs/pymc/pull/3397))
431437
* Updated for V4 by Bill Engels in September 2022 ([pymc-examples#237](https://github.com/pymc-devs/pymc-examples/pull/237))
438+
* Updated for V5 by Chris Fonnesbeck in July 2023 ([pymc-examples#549](https://github.com/pymc-devs/pymc-examples/pull/549))
432439

433440
+++
434441

examples/gaussian_processes/GP-Marginal.ipynb

+201-188
Large diffs are not rendered by default.

examples/gaussian_processes/GP-Marginal.myst.md

+60-38
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,15 @@ kernelspec:
1010
name: python3
1111
---
1212

13+
(gp_marginal)=
1314
# Marginal Likelihood Implementation
1415

16+
:::{post} June 4, 2023
17+
:tags: gaussian processes, time series
18+
:category: reference, intermediate
19+
:author: Bill Engels, Chris Fonnesbeck
20+
:::
21+
1522
The `gp.Marginal` class implements the more common case of GP regression: the observed data are the sum of a GP and Gaussian noise. `gp.Marginal` has a `marginal_likelihood` method, a `conditional` method, and a `predict` method. Given a mean and covariance function, the function $f(x)$ is modeled as,
1623

1724
$$
@@ -68,19 +75,19 @@ with pm.Model() as marginal_gp_model:
6875

6976
# The scale of the white noise term can be provided,
7077
sigma = pm.HalfCauchy("sigma", beta=5)
71-
y_ = gp.marginal_likelihood("y", X=X, y=y, noise=sigma)
78+
y_ = gp.marginal_likelihood("y", X=X, y=y, sigma=sigma)
7279

7380
# OR a covariance function for the noise can be given
7481
# noise_l = pm.Gamma("noise_l", alpha=2, beta=2)
7582
# cov_func_noise = pm.gp.cov.Exponential(1, noise_l) + pm.gp.cov.WhiteNoise(sigma=0.1)
76-
# y_ = gp.marginal_likelihood("y", X=X, y=y, noise=cov_func_noise)
83+
# y_ = gp.marginal_likelihood("y", X=X, y=y, sigma=cov_func_noise)
7784
```
7885

7986
+++
8087

8188
## The `.conditional` distribution
8289

83-
The `.conditional` has an optional flag for `pred_noise`, which defaults to `False`. When `pred_noise=False`, the `conditional` method produces the predictive distribution for the underlying function represented by the GP. When `pred_noise=True`, the `conditional` method produces the predictive distribution for the GP plus noise. Using the same `gp` object defined above,
90+
The `.conditional` has an optional flag for `pred_noise`, which defaults to `False`. When `pred_sigma=False`, the `conditional` method produces the predictive distribution for the underlying function represented by the GP. When `pred_sigma=True`, the `conditional` method produces the predictive distribution for the GP plus noise. Using the same `gp` object defined above,
8491

8592
```python
8693
# vector of new X points we want to predict the function at
@@ -90,7 +97,7 @@ with marginal_gp_model:
9097
f_star = gp.conditional("f_star", Xnew=Xnew)
9198

9299
# or to predict the GP plus noise
93-
y_star = gp.conditional("y_star", Xnew=Xnew, pred_noise=True)
100+
y_star = gp.conditional("y_star", Xnew=Xnew, pred_sigma=True)
94101
```
95102
If using an additive GP model, the conditional distribution for individual components can be constructed by setting the optional argument `given`. For more information on building additive GPs, see the main documentation page. For an example, see the Mauna Loa CO$_2$ notebook.
96103

@@ -108,7 +115,7 @@ mu, cov = gp.predict(Xnew, point=trace[-1])
108115
mu, var = gp.predict(Xnew, point=trace[-1], diag=True)
109116

110117
# With noise included
111-
mu, var = gp.predict(Xnew, point=trace[-1], diag=True, pred_noise=True)
118+
mu, var = gp.predict(Xnew, point=trace[-1], diag=True, pred_sigma=True)
112119
```
113120

114121
+++
@@ -120,10 +127,11 @@ mu, var = gp.predict(Xnew, point=trace[-1], diag=True, pred_noise=True)
120127
jupyter:
121128
outputs_hidden: true
122129
---
130+
import arviz as az
123131
import matplotlib.pyplot as plt
124132
import numpy as np
125133
import pandas as pd
126-
import pymc3 as pm
134+
import pymc as pm
127135
import scipy as sp
128136
129137
%matplotlib inline
@@ -137,9 +145,9 @@ n = 100 # The number of data points
137145
X = np.linspace(0, 10, n)[:, None] # The inputs to the GP, they must be arranged as a column vector
138146
139147
# Define the true covariance function and its parameters
140-
ℓ_true = 1.0
141-
η_true = 3.0
142-
cov_func = η_true**2 * pm.gp.cov.Matern52(1, ℓ_true)
148+
ell_true = 1.0
149+
eta_true = 3.0
150+
cov_func = eta_true**2 * pm.gp.cov.Matern52(1, ell_true)
143151
144152
# A mean function that is zero everywhere
145153
mean_func = pm.gp.mean.Zero()
@@ -152,8 +160,8 @@ f_true = np.random.multivariate_normal(
152160
153161
# The observed data is the latent function plus a small amount of IID Gaussian noise
154162
# The standard deviation of the noise is `sigma`
155-
σ_true = 2.0
156-
y = f_true + σ_true * np.random.randn(n)
163+
sigma_true = 2.0
164+
y = f_true + sigma_true * np.random.randn(n)
157165
158166
## Plot the data and the unobserved latent function
159167
fig = plt.figure(figsize=(12, 5))
@@ -167,31 +175,26 @@ plt.legend();
167175

168176
```{code-cell} ipython3
169177
with pm.Model() as model:
170-
= pm.Gamma("", alpha=2, beta=1)
171-
η = pm.HalfCauchy("η", beta=5)
178+
ell = pm.Gamma("ell", alpha=2, beta=1)
179+
eta = pm.HalfCauchy("eta", beta=5)
172180
173-
cov = η**2 * pm.gp.cov.Matern52(1, )
181+
cov = eta**2 * pm.gp.cov.Matern52(1, ell)
174182
gp = pm.gp.Marginal(cov_func=cov)
175183
176-
σ = pm.HalfCauchy("σ", beta=5)
177-
y_ = gp.marginal_likelihood("y", X=X, y=y, noise=σ)
184+
sigma = pm.HalfCauchy("sigma", beta=5)
185+
y_ = gp.marginal_likelihood("y", X=X, y=y, sigma=sigma)
178186
179-
mp = pm.find_MAP()
187+
with model:
188+
marginal_post = pm.sample(500, tune=2000, nuts_sampler="numpyro", chains=1)
180189
```
181190

182191
```{code-cell} ipython3
183-
# collect the results into a pandas dataframe to display
184-
# "mp" stands for marginal posterior
185-
pd.DataFrame(
186-
{
187-
"Parameter": ["ℓ", "η", "σ"],
188-
"Value at MAP": [float(mp["ℓ"]), float(mp["η"]), float(mp["σ"])],
189-
"True value": [ℓ_true, η_true, σ_true],
190-
}
191-
)
192+
summary = az.summary(marginal_post, var_names=["ell", "eta", "sigma"], round_to=2, kind="stats")
193+
summary["True value"] = [ell_true, eta_true, sigma_true]
194+
summary
192195
```
193196

194-
The MAP values are close to their true values.
197+
The estimated values are close to their true values.
195198

196199
+++
197200

@@ -205,9 +208,10 @@ X_new = np.linspace(0, 20, 600)[:, None]
205208
with model:
206209
f_pred = gp.conditional("f_pred", X_new)
207210
208-
# To use the MAP values, you can just replace the trace with a length-1 list with `mp`
209211
with model:
210-
pred_samples = pm.sample_posterior_predictive([mp], vars=[f_pred], samples=2000)
212+
pred_samples = pm.sample_posterior_predictive(
213+
marginal_post.sel(draw=slice(0, 20)), var_names=["f_pred"]
214+
)
211215
```
212216

213217
```{code-cell} ipython3
@@ -216,9 +220,10 @@ fig = plt.figure(figsize=(12, 5))
216220
ax = fig.gca()
217221
218222
# plot the samples from the gp posterior with samples and shading
219-
from pymc3.gp.util import plot_gp_dist
223+
from pymc.gp.util import plot_gp_dist
220224
221-
plot_gp_dist(ax, pred_samples["f_pred"], X_new)
225+
f_pred_samples = az.extract(pred_samples, group="posterior_predictive", var_names=["f_pred"])
226+
plot_gp_dist(ax, samples=f_pred_samples.T, x=X_new)
222227
223228
# plot the data and the true latent function
224229
plt.plot(X, f_true, "dodgerblue", lw=3, label="True f")
@@ -238,19 +243,22 @@ The `conditional` method of `gp.Marginal` contains the flag `pred_noise` whose d
238243
```{code-cell} ipython3
239244
with model:
240245
y_pred = gp.conditional("y_pred", X_new, pred_noise=True)
241-
y_samples = pm.sample_posterior_predictive([mp], vars=[y_pred], samples=2000)
246+
y_samples = pm.sample_posterior_predictive(
247+
marginal_post.sel(draw=slice(0, 50)), var_names=["y_pred"]
248+
)
242249
```
243250

244251
```{code-cell} ipython3
245252
fig = plt.figure(figsize=(12, 5))
246253
ax = fig.gca()
247254
248255
# posterior predictive distribution
249-
plot_gp_dist(ax, y_samples["y_pred"], X_new, plot_samples=False, palette="bone_r")
256+
y_pred_samples = az.extract(y_samples, group="posterior_predictive", var_names=["y_pred"])
257+
plot_gp_dist(ax, y_pred_samples.T, X_new, plot_samples=False, palette="bone_r")
250258
251259
# overlay a scatter of one draw of random points from the
252260
# posterior predictive distribution
253-
plt.plot(X_new, y_samples["y_pred"][800, :].T, "co", ms=2, label="Predicted data")
261+
plt.plot(X_new, y_pred_samples.sel(sample=1), "co", ms=2, label="Predicted data")
254262
255263
# plot original data and true function
256264
plt.plot(X, y, "ok", ms=3, alpha=1.0, label="observed data")
@@ -268,18 +276,21 @@ Notice that the posterior predictive density is wider than the conditional distr
268276

269277
### Using `.predict`
270278

271-
We can use the `.predict` method to return the mean and variance given a particular `point`. Since we used `find_MAP` in this example, `predict` returns the same mean and covariance that the distribution of `.conditional` has.
279+
We can use the `.predict` method to return the mean and variance given a particular `point`.
272280

273281
```{code-cell} ipython3
274282
# predict
275-
mu, var = gp.predict(X_new, point=mp, diag=True)
283+
with model:
284+
mu, var = gp.predict(
285+
X_new, point=az.extract(marginal_post.posterior.sel(draw=[0])).squeeze(), diag=True
286+
)
276287
sd = np.sqrt(var)
277288
278289
# draw plot
279290
fig = plt.figure(figsize=(12, 5))
280291
ax = fig.gca()
281292
282-
# plot mean and intervals
293+
# plot mean and 2sigma intervals
283294
plt.plot(X_new, mu, "r", lw=2, label="mean and 2σ region")
284295
plt.plot(X_new, mu + 2 * sd, "r", lw=1)
285296
plt.plot(X_new, mu - 2 * sd, "r", lw=1)
@@ -291,10 +302,21 @@ plt.plot(X, f_true, "dodgerblue", lw=3, label="true f")
291302
292303
plt.xlabel("x")
293304
plt.ylim([-13, 13])
294-
plt.title("predictive mean and interval")
305+
plt.title("predictive mean and 2sigma interval")
295306
plt.legend();
296307
```
297308

309+
## Authors
310+
311+
* Created by [Bill Engels](https://github.com/bwengals) in 2017 ([pymc#1674](https://github.com/pymc-devs/pymc/pull/1674))
312+
* Reexecuted by [Colin Caroll](https://github.com/ColCarroll) in 2019 ([pymc#3397](https://github.com/pymc-devs/pymc/pull/3397))
313+
* Updated for V4 by Bill Engels in September 2022 ([pymc-examples#237](https://github.com/pymc-devs/pymc-examples/pull/237))
314+
* Updated for V5 by Chris Fonnesbeck in July 2023 ([pymc-examples#549](https://github.com/pymc-devs/pymc-examples/pull/549))
315+
316+
+++
317+
318+
## Watermark
319+
298320
```{code-cell} ipython3
299321
%load_ext watermark
300322
%watermark -n -u -v -iv -w

0 commit comments

Comments
 (0)