Marginal Gaussian with y=None vs Latent Gaussian (original) (raw)

In a time series model with regressors, I would like to model the trend with a Gaussian process. I see that I can describe the model either with a Marginal Gaussian process (with y=None and a small noise) or a Latent Gaussian process. Both descriptions give the same results, though the number of divergent samples is higher in the second case. What is the recommended way to do it? I post below both models:

import numpy as np
import pymc as pm

# Simulated data
np.random.seed(42)
n = 50
t = np.linspace(0, 1, n)[:, None]       # Time
X1 = np.random.rand(n, 1)               # Regressor 1
X2 = np.random.rand(n, 1)               # Regressor 2

# True model: linear in X + nonlinear trend
true_trend = 0.5 * t + 0.3 * np.sin(5 * t)
y = (
    true_trend.ravel()
    + 1.5 * X1.ravel()
    - 2.0 * X2.ravel()
    + 0.05 * np.random.randn(n)
)

# PyMC model
with pm.Model() as model:
    # GP hyperpriors
    ls = pm.Gamma("lengthscale", alpha=2, beta=10)
    eta = pm.HalfNormal("eta", sigma=1.0)
    slope = pm.Normal("slope", mu=0.0, sigma=1.0)

    # GP kernel: Linear + ExpQuad
    cov = (
        pm.gp.cov.Linear(1, c=slope)
        + eta**2 * pm.gp.cov.ExpQuad(1, ls=ls)
    )
    gp = pm.gp.Marginal(cov_func=cov, mean_func=pm.gp.mean.Zero())

    # Linear regression weights
    a1 = pm.Normal("a1", mu=0.0, sigma=2.0)
    a2 = pm.Normal("a2", mu=0.0, sigma=2.0)

    # Observation noise
    sigma = pm.HalfNormal("sigma", sigma=0.1)

    # Linear component
    lin_term = a1 * X1.ravel() + a2 * X2.ravel()

    # GP trend component
    gp_trend = gp.marginal_likelihood("gp_trend", X=t, y=None, sigma=0.0001)

    # Total mean = GP trend + linear predictors
    mu = gp_trend + lin_term

    # Final observed likelihood
    y_obs = pm.Normal("y_obs", mu=mu, sigma=sigma, observed=y)
    trace = pm.sample(chains=5, cores=6, target_accept=0.95, draws=3000, tune=4000, nuts_sampler="nutpie", random_seed=42)
import numpy as np
import pymc as pm

# Simulated data

np.random.seed(42)
n = 50
t = np.linspace(0, 1, n)[:, None]       # Time
X1 = np.random.rand(n, 1)               # Regressor 1
X2 = np.random.rand(n, 1)               # Regressor 2

# True model: linear in X + nonlinear trend
true_trend = 0.5 * t + 0.3 * np.sin(5 * t)
y = (
    true_trend.ravel()
    + 1.5 * X1.ravel()
    - 2.0 * X2.ravel()
    + 0.05 * np.random.randn(n)
)

# PyMC model
with pm.Model() as model:
    # GP hyperpriors
    ls = pm.Gamma("lengthscale", alpha=2, beta=10)
    eta = pm.HalfNormal("eta", sigma=1.0)
    slope = pm.Normal("slope", mu=0.0, sigma=1.0)

    # GP kernel: Linear + ExpQuad
    cov = (
        pm.gp.cov.Linear(1, c=slope)
        + eta**2 * pm.gp.cov.ExpQuad(1, ls=ls)
    )
    gp = pm.gp.Latent(cov_func=cov, mean_func=pm.gp.mean.Zero())

    # Place a GP prior over the function f.
    f = gp.prior("f", X=t)

    # Linear regression weights
    a1 = pm.Normal("a1", mu=0.0, sigma=2.0)
    a2 = pm.Normal("a2", mu=0.0, sigma=2.0)

    # Observation noise
    sigma = pm.HalfNormal("sigma", sigma=0.1)

    # Linear component
    lin_term = a1 * X1.ravel() + a2 * X2.ravel()

    # Total mean = GP trend + linear predictors
    mu = f + lin_term

    # Final observed likelihood
    y_obs = pm.Normal("y_obs", mu=mu, sigma=sigma, observed=y)
    trace = pm.sample(chains=5, cores=6, target_accept=0.95, draws=3000, tune=4000, nuts_sampler="nutpie", random_seed=42)