[ ]:
%load_ext autoreload
%autoreload 2
P-value Function Diagnostics¶
INTRO & SETTINGS¶
The p-values calibration method fits a monotonic probabilistic classifier that estimates the rejection probability \(P(T \le \tau \mid \theta)\) – i.e. a p-value/CDF function of the test statistic, conditional on \(\theta\). Notebooks 2 and 4 use this fitted function to build confidence sets, but never ask how good the fit itself is.
This notebook is about that second question: diagnosing the fit of the p-value function, independent of the confidence sets built from it. We reuse the same model as notebook 2 (Posterior + calibration_method='p-values' on GaussianMean) so the diagnostics here are directly comparable to that notebook’s construction.
[ ]:
# SETTINGS
LIKELIHOOD_COV = 0.01
PRIOR_LOC = 0
PRIOR_COV = 0.1
PARAM_DIM = 2
DATA_DIM = 2
BATCH_SIZE = 1
PARAM_SPACE_BOUNDS = {'low': -1.5, 'high': 1.5}
PARAM_GRID_SIZE = 1_000
CONFIDENCE_LEVEL = 0.90
B = 20_000
B_PRIME = 10_000
MONTE_CARLO_SIZE = 2_000 # MC draws per grid point for the diagnostics themselves
SIMULATE¶
[ ]:
import torch
from lf2i.simulator.gaussian import GaussianMean
gm = GaussianMean(
likelihood_cov=LIKELIHOOD_COV,
prior='gaussian',
prior_kwargs={'loc': PRIOR_LOC, 'cov': PRIOR_COV},
poi_space_bounds=PARAM_SPACE_BOUNDS,
poi_grid_size=PARAM_GRID_SIZE,
poi_dim=PARAM_DIM,
data_dim=DATA_DIM,
batch_size=BATCH_SIZE,
)
FIT THE P-VALUE FUNCTION¶
[ ]:
from lf2i.inference import LF2I
from lf2i.test_statistics import Posterior
from sbi.inference import SNPE
posterior_ts = Posterior(poi_dim=PARAM_DIM, estimator=SNPE())
lf2i = LF2I(test_statistic=posterior_ts)
x_obs = gm(param=torch.tensor([[0.5, -0.3]])).reshape(1, DATA_DIM)
_ = lf2i.inference(
x=x_obs,
evaluation_grid=gm.poi_grid,
confidence_level=CONFIDENCE_LEVEL,
calibration_method='p-values',
calibration_model='nn',
simulator=gm,
b=B,
b_prime=B_PRIME,
)
DIAGNOSE THE FIT¶
For each \(\theta\) on the evaluation grid, monte_carlo_pvalue_diagnostics draws fresh Monte Carlo samples, evaluates the test statistic, and compares the fitted p-value/CDF model’s predictions to the empirical CDF at that \(\theta\) – returning, per grid point, a normalized CRPS (0 = perfect fit, 1 = no better than ignoring \(\theta\) and using the marginal CDF) and pinball losses at a range of quantile levels.
[ ]:
from lf2i.diagnostics.monte_carlo_methods import monte_carlo_pvalue_diagnostics
evaluation_grid_out, estimation_errors = monte_carlo_pvalue_diagnostics(
test_statistic=posterior_ts,
calibration_model=lf2i.calibration_model,
simulator=gm,
evaluation_grid=gm.poi_grid,
monte_carlo_size=MONTE_CARLO_SIZE,
)
list(estimation_errors.keys())
Calibration score heatmap¶
[ ]:
from lf2i.plot.calibration_diagnostics import calibration_score_plot
calibration_score_plot(
parameters=gm.poi_grid.numpy(),
scores=estimation_errors['crps'],
score_label='CRPS (normalized)',
param_dim=PARAM_DIM,
title='Normalized CRPS of the fitted p-value function across the parameter grid',
)
CDF comparison at a single theta¶
[ ]:
from lf2i.plot.calibration_diagnostics import plot_cdf_comparison
plot_cdf_comparison(
test_statistic=posterior_ts,
calib_model=lf2i.calibration_model[f'{CONFIDENCE_LEVEL:.2f}'],
theta_eval=torch.tensor([[0.5, -0.3]]),
simulator=gm,
monte_carlo_size=MONTE_CARLO_SIZE,
title='Fitted vs. empirical CDF at theta = (0.5, -0.3)',
)
Combined diagnostic panel¶
[ ]:
from lf2i.plot.calibration_diagnostics import calibration_cdf_panel
calibration_cdf_panel(
evaluation_grid=gm.poi_grid,
estimation_errors=estimation_errors,
test_statistic=posterior_ts,
calibration_model=lf2i.calibration_model,
simulator=gm,
param_dim=PARAM_DIM,
score_key='crps',
monte_carlo_size=MONTE_CARLO_SIZE,
)