Part II: Gaussian Processes - Implementation and Practice
The Complete Emulation Workflow | Module 7: The Learnable Universe | ASTR 596
“In theory, theory and practice are the same. In practice, they are not.”
Attributed to Yogi Berra (and many others)
Overview¶
Prerequisites: Complete Part I: GP Theory before proceeding. You should understand:
What a Gaussian Process is (distribution over functions)
Gaussian conditioning (how predictions work)
Kernels (encoding smoothness and structure)
When to use GPs vs other methods
In Part II, you’ll learn:
The complete emulation workflow from simulations to predictions
How to train GPs by optimizing marginal likelihood
Numerical implementation techniques in JAX
Validation and diagnostics (calibration, OOD detection)
How to use emulators for scientific applications
Quick Recap: What You Learned in Part I¶
The Core Ideas¶
Gaussian Processes are:
Probability distributions over functions f: \mathbb{R}^D \to \mathbb{R
Specified by mean function and kernel
A Bayesian approach to function learning with built-in uncertainty
The Prediction Formulas:
Given training data and GP prior :
where:
: kernel matrix with
: kernel vector with
: observation noise variance
The Key Kernels:
Squared Exponential (SE): (infinitely smooth)
Matérn-5/2: Twice differentiable, more realistic smoothness
ARD (Automatic Relevance Determination): Per-dimension lengthscales
When to Use GPs:
✅ Low-dimensional inputs ()
✅ Limited training data ()
✅ Smooth physics
✅ Uncertainty quantification critical
✅ Interpretability matters
Now let’s build one!
The Emulation Workflow: From Simulations to Predictions¶
Now that you understand GPs conceptually, let’s walk through the complete workflow you’ll implement in Part 2.
Step 1: Generate Training Data¶
Goal: Sample initial condition space intelligently to get informative training set.
Naive approach: Random sampling
Draw uniformly from allowed ranges
Problem: Might cluster samples, miss important regions
Better approach: Latin Hypercube Sampling (LHS)
Stratified sampling ensuring coverage in each dimension
Maximizes “space-filling” property
Standard in experimental design
Best approach: Active learning (advanced)
Start with LHS
Iteratively add simulations where GP is most uncertain
Adaptively refine emulator
For your project: Provided dataset uses LHS. You’ll add 50 more simulations where you choose (tests if you can identify gaps).
Step 2: Train the GP¶
Goal: Find hyperparameters that best explain data.
Approach: Maximize marginal likelihood (evidence):
Three terms:
Data fit: (how well does GP explain data?)
Complexity penalty: (Occam’s razor—prefer simpler explanations)
Normalization: (constant, doesn’t affect optimization)
Interpretation (Occam’s Razor):
Small → Very flexible GP (can fit any data) → High complexity penalty
Large → Very rigid GP (smooth functions only) → Low complexity penalty, but poor data fit
Marginal likelihood automatically trades off fit vs complexity
No manual regularization needed! You still optimize hyperparameters by maximizing the marginal likelihood, but this automatic Occam’s razor avoids the manual tuning of regularization parameters common in neural networks
Optimization:
Use gradient-based optimizer (L-BFGS, Adam)
JAX computes gradients automatically:
jax.grad(log_marginal_likelihood)Often use log-space for hyperparameters (ensures positivity):
Initialization heuristics (guess starting point):
: Typical distance between data points, or fraction of input range (e.g., 0.1 × data range)
: Variance of outputs
: Estimate from simulation variability or set to small value (1% of signal variance)
Step 3: Make Predictions¶
Goal: Given new initial conditions , predict bound fraction with uncertainty .
Algorithm (from conditioning formulas):
Compute kernel vectors:
(training-test covariances)
(test-test covariance, usually 1)
Compute predictive mean:
Note: We typically standardize training targets to zero mean () during training, then de-standardize predictions for reporting. This simplifies computation while preserving generality.
Compute predictive variance:
Efficient implementation (precompute during training):
Step 1: Cholesky factorization
where is lower-triangular (Cholesky factor).
Step 2: Precompute regression weights Solve , then solve to get:
Step 3: Fast prediction (whitened form).
For any test point :
Why this works:
Never compute explicitly (numerically unstable!)
Stable: Cholesky preserves positive-definiteness
Computational complexity summary:
| Operation | Complexity | When Needed | Your Project () |
|---|---|---|---|
| Training (one-time) | Cholesky + hyperparameter optimization | Fast for on modern CPUs/GPUs (seconds to minutes for optimization) | |
| Marginal predictions | Mean/variance at independent points (typical) | ~seconds for | |
| Joint covariance | Full predictive covariance for test points via triangular solves (rarely needed) | Use batches: | |
| Joint sampling | Drawing correlated samples for visualization | Only for small |
Key insight: Marginal predictions scale linearly with → You can predict at millions of test points efficiently!
For your project: Training is instant (), predictions are fast. Focus on getting the kernel right, not optimizing speed.
Numerical stability:
Never compute explicitly!
Use Cholesky factorization + triangular solves (stable)
Add small “jitter” 10-6 to diagonal if matrix near-singular
Keep shapes static under
jitPre-allocate arrays inside compiled functions
Avoid dynamic shapes (e.g., no
n = X.shape[0]that varies between calls)
Standardize inputs and outputs
Z-score normalize each input dimension:
(X - mean) / stdFor outputs spanning orders of magnitude, normalize or log-transform
Store normalization constants, denormalize predictions when reporting
Use log-space for positive hyperparameters (with bounds)
Ensures positivity: , optimize over
Gradients often better behaved in log-space
Apply to lengthscales, variances, noise
Tip for robustness: Bound the log-space parameters to avoid extreme values:
Lengthscale: (roughly input range) — prevents learning scales far outside data
Signal variance: — prevents excessively small or large signal
Noise variance: — prevents noise from dominating or disappearing
Implement bounded optimization via
optax.clip_by_global_normor reparameterize to constrained domain (e.g., sigmoid)
Multiple optimizer restarts
Marginal likelihood is non-convex (many local optima)
Try 5-10 random initializations, keep best
Use heuristic initialization (see below) as starting point
Precision considerations
Enable 64-bit in JAX: Set environment variable
JAX_ENABLE_X64=Trueor usejax.config.update("jax_enable_x64", True)at startDefault: Use
float64for training and hyperparameter optimizationSpeed optimization (optional): After validating is well-conditioned, test
float32for predictionsMatérn-5/2 + float32: Often stable for with proper jitter (Matérn kernels numerically better-behaved than SE)
SE kernel + float32: More prone to numerical issues near singularities (requires higher jitter or float64)
Monitor condition number:
cond(K_y) = max(eig)/min(eig)should be for float64, for float32Your project: Stick with float64 for (no performance penalty, guaranteed stability)
Batch predictions for memory safety
For , predict in batches:
M_batch ~ 10^4Use
vmap(vectorized map) for vectorization:jax.vmap(predict_fn)(X_test_batch)
Deduplicate training inputs
Problem: Near-identical training points cause to be nearly singular
Check: Compute pairwise distances: for all
Threshold: Flag duplicates if distance (numerical precision limit)
Causes: Accidentally running same simulation twice, numerical roundoff in parameter sampling
Symptom: Cholesky fails with “matrix not positive definite”
Fix: Remove exact duplicates, or add more jitter to diagonal
💡 Debugging breadcrumb: If Cholesky fails...
Increase jitter: Try
1e-5or1e-4(scale-aware formula above)Deduplicate inputs: Check for near-identical training points (distance <
1e-8)Standardize features: Z-score normalize all inputs before training
Try Matérn-5/2 instead of SE (less prone to near-singularity)
Check condition number:
jnp.linalg.cond(K_y)should be for float64
### Step 4: Validate the Emulator
**Goal**: Check that GP predictions are accurate and well-calibrated.
**Metrics**:
1. **Predictive accuracy**:
- RMSE: $\sqrt{\frac{1}{M} \sum_{i=1}^M (y_i - \mu(\mathbf{x}_i))^2}$ (how close are predictions?)
- $R^2$ coefficient: fraction of variance explained
2. **Uncertainty calibration**:
- **Coverage**: What fraction of test points fall within predicted 1-$\sigma$ bands?
- Should be 68% if calibrated (for Gaussian residuals)
- **Empirical coverage < nominal** (e.g., 60% within 1σ vs expected 68%) → **Overconfident** (error bars too narrow)
- **Empirical coverage > nominal** (e.g., 80% within 1σ) → **Underconfident** (error bars too wide)
- **Calibration plot**: Plot predicted std vs actual error, should align
3. **Negative log-likelihood (NLL)** on test set:
$$\text{NLL} = -\frac{1}{M} \sum_{i=1}^M \log \mathcal{N}\big(y_i \mid \mu(\mathbf{x}_i), \sigma_*^2(\mathbf{x}_i) + \sigma_n^2\big)$$
**Note**: Use **total predictive variance** $\sigma_*^2(\mathbf{x}_i) + \sigma_n^2$ for observed targets. For latent-function scoring, use epistemic variance $\sigma_*^2(\mathbf{x}_i)$ only.
- Jointly measures accuracy + calibration
- Lower is better
**Diagnostics**:
- **Residual plots**: $(y_i - \mu(\mathbf{x}_i))$ vs $\mathbf{x}_i$ (should be random, no patterns)
- **QQ plots** (quantile–quantile plots): Normalized residuals $(y_i - \mu(\mathbf{x}_i)) / \sigma(\mathbf{x}_i)$ should be $\mathcal{N}(0,1)$
- **Learned hyperparameters**: Do lengthscales make physical sense?
- **Out-of-distribution (OOD) detection**: Flag test inputs with low training similarity:
$$\text{OOD if } \max_i k(\mathbf{x}_*, \mathbf{x}_i) / \sigma_f^2 < \text{threshold}$$
**Note**: The threshold (e.g., 0.05) should be validated on your data; it depends on learned hyperparameters ($\sigma_f^2$, $\ell$). Cross-validate by checking whether OOD-flagged points have poor predictive accuracy.
**Interpretation**: Point $\mathbf{x}_*$ has weak correlation with ALL training points → extrapolation!
**Action items**:
- Annotate predictions as **low-trust** in plots (different color/marker)
- Report OOD fraction in test set as quality metric
- Consider running additional simulations in OOD regions before making scientific claims
- **Rule**: Never publish conclusions based solely on OOD predictions without validation
**For your project**: If >10% of test set is OOD, your training data doesn't cover parameter space adequately.
**Cross-validation**:
- Leave-one-out: Train on $N-1$ points, predict left-out point, repeat
- $k$-fold: Split data into $k$ chunks, train on $k-1$, test on 1, repeat
- Checks: Does GP generalize? Or just memorizing training data?
### Step 5: Use the Emulator for Science
**Now the payoff! You have a fast, accurate, uncertainty-aware emulator. Use it for:**
**Application 1: Parameter Space Exploration.**
- Evaluate $\mu(\mathbf{x})$ on dense grid (millions of points, takes seconds)
- Visualize: Which combinations of $(Q, N, a)$ lead to survival vs dissolution?
- Identify interesting regions for further study
**Application 2: Optimization.**
- Find $\mathbf{x}^* = \arg\max_{\mathbf{x}} \mu(\mathbf{x})$ (e.g., maximize cluster lifetime)
- Use gradient-based optimizer (JAX gives gradients!)
- Or: Bayesian optimization (sample where acquisition function is high)
**Application 3: Bayesian Parameter Inference.**
- Observe real cluster (e.g., Pleiades): $(R_{\rm core, obs}, \sigma_{v, obs}, M_{\rm obs})$
- Likelihood: $p(\text{data} | \mathbf{x}) = \mathcal{N}(\text{data} | \mu_{\rm emu}(\mathbf{x}), \sigma^2_{\rm emu}(\mathbf{x}))$
- Run MCMC (you know how!) using emulator for likelihood
- Infer: What initial conditions produced observed cluster?
:::{admonition} Critical: Using Emulator in MCMC Likelihoods
:class: danger
**Common mistake**: Double-counting or forgetting emulator uncertainty in inference!
When using your emulator inside MCMC, the total observational uncertainty is:
$$
\boxed{\Sigma_{\text{total}}(\mathbf{x}) = \Sigma_{\text{obs}} + \sigma_*^2(\mathbf{x})}
$$
where:
- $\Sigma_{\text{obs}}$: Measurement uncertainty from observations (telescope noise, etc.)
- $\sigma_*^2(\mathbf{x})$: Emulator epistemic uncertainty (from GP)
**The likelihood is**:
$$
p(\mathbf{y}_{\text{obs}} | \mathbf{x}) = \mathcal{N}(\mathbf{y}_{\text{obs}} | \mu_{\text{emu}}(\mathbf{x}), \Sigma_{\text{total}}(\mathbf{x}))
$$
**NOT**: $\mathcal{N}(\mathbf{y}_{\text{obs}} | \mu_{\text{emu}}(\mathbf{x}), \Sigma_{\text{obs}})$ ❌ (ignores emulator error!)
**NOT**: $\mathcal{N}(\mathbf{y}_{\text{obs}} | \mu_{\text{emu}}(\mathbf{x}), \sigma_*^2(\mathbf{x}))$ ❌ (ignores observation noise!)
**Why this matters**:
- Ignoring emulator uncertainty → overconfident posteriors (claims too much precision)
- Double-counting → underconfident posteriors (claims too little)
- Correct accounting → honest uncertainty quantification
**In practice**:
```python
# Emulator prediction
mu_emu, var_emu = gp.predict(theta) # var_emu = sigma_*^2
# Total variance
var_total = var_obs + var_emu # Add in quadrature
# Likelihood
log_likelihood = -0.5 * (y_obs - mu_emu)**2 / var_total - 0.5 * jnp.log(2 * jnp.pi * var_total)
```
**Connection to Module 5**: This is Bayesian error propagation—combining uncertainties from multiple sources (observations + model).Application 4: Experimental Design.
GP uncertainty tells you: “I’m uncertain here, run more simulations!”
Active learning: Iteratively add simulations to maximize information gain
Exploration vs Exploitation: Greedy variance sampling (below) maximizes exploration by filling uncertain regions, but may miss important physical boundaries. More sophisticated methods (expected improvement, mutual information) balance exploration with exploitation (reducing uncertainty where predictions matter most). For your project, simple variance sampling works well; for research applications, consider hybrid strategies that weight uncertainty by physical relevance.
Greedy variance sampling: Pick (easiest to implement, purely exploratory)
Batch-greedy: Pick points by iterating predictive updates or approximating with farthest-point in ARD-scaled space
Minimize computational cost to achieve target accuracy
🎓 What We’ve Learned: Implementation Summary¶
The Complete Workflow¶
Generate training data (LHS sampling)
Train GP (maximize marginal likelihood)
Make predictions (Gaussian conditioning)
Validate (coverage, calibration)
Use for science (exploration, inference, optimization)
Connection to Everything You’ve Learned¶
Module 1 (Statistics):
GPs extend multivariate Gaussians to infinite dimensions
Central Limit Theorem: Why Gaussian assumptions are reasonable
Moments: GP mean and covariance are first two moments
Module 2-3 (Stellar Systems):
N-body simulations define functions
Emulation lets you explore phase space without full dynamics
Lengthscales relate to characteristic scales (dynamical time, relaxation time)
Module 4 (Radiative Transfer):
Monte Carlo RT is expensive (like N-body)
Could emulate emergent spectrum vs stellar parameters
Same workflow applies!
Module 5 (Bayesian Inference):
GPs are Bayesian: Prior (kernel) + Likelihood (data) = Posterior (predictions)
MCMC becomes tractable with emulator replacing expensive likelihood
Marginal likelihood for hyperparameters = evidence for model selection
Project 5 (JAX):
Automatic differentiation gives gradients for optimization
JIT compilation makes GP fast
Vectorization (
vmap) enables efficient prediction at many test pointsGPU acceleration makes feasible
Next Lecture (Neural Networks):
NNs are alternative emulators (different tradeoffs)
Can combine: Deep kernel learning, neural processes
Compare empirically: Which is better for your problem?
The Philosophical Point¶
Machine learning is not magic. It’s principled probabilistic inference with mathematical foundations.
GPs exemplify this:
Every component has clear interpretation (kernel, hyperparameters, predictions, uncertainty)
Connections to statistics, physics, computation
Uncertainty quantification is first-class citizen (not afterthought)
Hyperparameters have physical meaning (not just numbers to tune)
When you implement a GP, you’re not using a black box—you’re applying a century of mathematics (Kolmogorov, Wiener, Rasmussen) to solve a 21st-century astrophysics problem.
This is computational astrophysics in 2025!
🔮 Your Final Project¶
Next Lecture (Part 2: Implementation):
Build GP regression from scratch in JAX
Apply to N-body emulation (live demo)
Hyperparameter optimization (gradients via autodiff)
Validation and diagnostics
Comparison to neural networks (preview)
Your Final Project (Weeks 1-3):
Week 1: Core Emulation.
Fit GP to provided training data
Predict bound fraction and core radius
Validate uncertainty calibration
Deliverable: Working GP emulator
Week 2: Neural Network Comparison.
Build NN emulator (MLP in JAX)
Compare: Accuracy, uncertainty, computational cost
Analyze: When is each better?
Deliverable: Comparative analysis
Week 3: Extension.
Choose one:
Time series emulation (predict full trajectories)
Active learning (adaptive simulation placement)
Multi-fidelity (combine cheap + expensive sims)
Physics-informed constraints
Deliverable: Extended emulator + documentation
Success Criteria:
Emulator predicts well ( on test set)
Uncertainty is calibrated (68% coverage at 1-)
Can explain: When to use GP vs NN?
Code is modular (easy to swap kernels, add data)
Documentation is research-grade (hand to collaborator, they can use it)
📚 Preparation for Next Lecture¶
Before Part 2, you should:
Review these notes: Make sure you understand every conceptual checkpoint
Mathematical preparation: Can you derive the GP predictive formulas? Try it on paper.
Computational thinking:
How would you implement in JAX?
How to compute for training points efficiently?
What’s the shape of all the matrices involved?
Read (optional but recommended):
Rasmussen & Williams, Gaussian Processes for Machine Learning, Ch. 2.1-2.3
MacKay, Information Theory, Inference, and Learning Algorithms, Ch. 45
Physical intuition:
For your N-body sims, which parameter do you expect matters most for bound fraction?
Sketch what you think the emulator function looks like: holding others fixed
What should lengthscales be approximately? (Guess based on parameter ranges)
Deliverables checklist (for your final project report):
Report RMSE, , NLL (with total variance ), and 1σ/2σ coverage
Include one residual plot and one QQ plot (quantile–quantile)
Include an OOD similarity histogram (flag extrapolation regions)
Discuss ARD lengthscales physically: After training, list and write one sentence per parameter interpreting physics:
Example: “ small → bound fraction highly sensitive to virial ratio”
Example: “ large → bound fraction insensitive to particle number for ”
Example: “ moderate → cluster scale directly affects tidal stripping timescale”
Questions: Write down anything confusing. We’ll address in Part 2!
📖 Glossary of Key Terms¶
ARD (Automatic Relevance Determination): Kernel with per-dimension lengthscales , allowing the GP to learn which input dimensions matter most. Small → high sensitivity to dimension , large → low sensitivity (GP effectively ignores that dimension).
LHS (Latin Hypercube Sampling): Stratified sampling method ensuring uniform coverage in each dimension separately. Better than random sampling for space-filling experimental designs. Widely used for training surrogate models.
OOD (Out-of-Distribution): Test points far from training data, where the emulator extrapolates and uncertainty is high. Detect via kernel similarity: (e.g., 0.05; validate empirically for your problem as threshold depends on learned hyperparameters).
ICM/LMC (Intrinsic Coregionalization Model / Linear Model of Coregionalization): Multi-output GP methods that model correlations between outputs via cross-covariance functions. Beyond scope of this course; see Álvarez et al. (2012) for details.
Epistemic Uncertainty: Uncertainty about the latent function (reducible with more training data). From GP: . Also called “model uncertainty” or “knowledge uncertainty.”
Aleatoric Uncertainty: Irreducible noise in observations (measurement error, simulation stochasticity). From likelihood: . Also called “data uncertainty.”
Marginal Likelihood (Evidence): , the probability of observing training data given hyperparameters, integrating over all possible functions. Used for Type-II maximum likelihood hyperparameter optimization. Implements Occam’s razor automatically.
Kernel (Covariance Function): , measures correlation between function values at inputs and . Encodes smoothness assumptions and prior beliefs about function structure.
Lengthscale (): Characteristic distance in input space over which function values remain correlated. Small → rapid variation (wiggly functions), large → smooth variation (slowly varying functions). Units: same as input dimensions.
Signal Variance (): Prior variance of function values (overall amplitude of variation). Units: output.
Noise Variance (): Observation noise level (aleatoric uncertainty). Units: output.
Cholesky Factorization: Decomposition where is lower-triangular. Numerically stable way to solve linear systems and compute determinants. Essential for GP implementation.
Jitter: Small constant added to kernel matrix diagonal to ensure numerical stability: . Prevents Cholesky failures from near-singular matrices. Typical values: (scale-aware).
SE (Squared Exponential) Kernel: Also called RBF (Radial Basis Function). Assumes infinitely smooth functions. Often too smooth for real physics.
Matérn Kernel: Family of kernels with tunable smoothness . Matérn-5/2 is recommended default (twice differentiable, realistic for physics).
PSD (Positive Semi-Definite): Property required for valid kernels. Ensures covariance matrices have non-negative eigenvalues (variances can’t be negative).
MCMC (Markov Chain Monte Carlo): Sampling method for complex posterior distributions (from Module 5). GPs enable MCMC-based inference by providing fast, differentiable emulators of expensive simulators.
JAX: Just-In-Time compiled array computing library for Python with automatic differentiation. Makes GP implementation fast and enables gradient-based hyperparameter optimization.
JIT (Just-In-Time Compilation): Compilation technique that speeds up Python code to near-C performance. Use @jit decorator in JAX.
vmap (Vectorized Map): JAX function for automatic vectorization. Maps a function over batch dimension efficiently (avoids Python loops).
Now you’re ready to implement! 🚀
“The best way to understand a Gaussian Process is to implement one.” — Every computational scientist ever