AutoGP API
The purpose of this package is to automatically infer Gaussian process (GP) models of time series data, hence the name AutoGP.
At a high level, a Gaussian process $\{X(t) \mid t \in T \}$ is a family of random variables indexed by some set $T$. For time series data, the index set $T = \mathbb{R}$ is typically (a subset) of the real numbers. For any list of $n$ time points $[t_1, \dots, t_n]$, the prior distribution of the random vector $[X(t_1), \dots, X(t_n)]$ is a multivariate Gaussian,
\[\begin{aligned} \begin{bmatrix} X(t_1)\\ \vdots \\ X(t_n)\\ \end{bmatrix} \sim \mathrm{MultivariteNormal} \left( \begin{bmatrix} m(t_1)\\ \vdots \\ m(t_n)\\ \end{bmatrix}, \begin{bmatrix} k_\theta(t_1, t_1) & \dots & k_\theta(t_1, t_n) \\ \vdots & \ddots & \vdots \\ k_\theta(t_n, t_1) & \dots & k_\theta(t_n, t_n) \\ \end{bmatrix} \right). \end{aligned}\]
In the equation above $m : T \to \mathbb{R}$ is the mean function and $k_\theta: T \times T \to \mathbb{R}_{\ge 0}$ is the covariance (kernel) function parameterized by $\theta$. We typically assume (without loss of generality) that $m(t) = 0$ for all $t$ and focus our modeling efforts on the covariance kernel $k_\theta$. The structure of the kernel dictates the qualitative properties of $X$, such as the existence of linear trends, seasonal components, changepoints, etc.; refer to the kernel cookbook for an overview of these concepts.
Gaussian processes can be used as priors in Bayesian nonparametric models of time series data $[Y(t_1), \dots, Y(t_n)]$ as follows. We assume that each $Y(t_i) = g(X(t_i), \epsilon(t_i))$ for some known function $g$ and noise process $\epsilon(t_i)$. A common choice is to let $Y$ be a copy of $X$ corrupted with i.i.d. Gaussian innovations, which gives $Y(t_i) = X(t_i) + \epsilon(t_i)$ where $\epsilon(t_i) \sim \mathrm{Normal}(0, \eta)$ for some variance $\eta > 0$.
Writing out the full model
\[\begin{aligned} k &\sim \mathrm{PCFG} &&\textrm{(prior over covariance kernel)} \\ [\theta_1, \dots, \theta_{d(k)}] &\sim p_{k} && \textrm{(parameter prior)}\\ \eta &\sim p_{\eta} &&\textrm{(noise prior)} \\ [X(t_1), \dots, X(t_n)] &\sim \mathrm{MultivariteNormal}(\mathbf{0}, [k_\theta(t_i,t_j)]_{i,j=1}^n) &&\textrm{(Gaussian process)} \\ Y(t_i) &\sim \mathrm{Normal}(X(t_i), \eta), i=1,\dots,n &&\textrm{(noisy observations)} \end{aligned}\]
where PCFG denotes a probabilistic-context free grammar that defines a language of covariance kernel expressions $k$,
\[\begin{aligned} B &:= \texttt{Linear} \mid \texttt{Periodic} \mid \texttt{GammaExponential} \mid \dots \\ \oplus &:= \texttt{+} \mid \texttt{*} \mid \texttt{ChangePoint} \\ k &:= B \mid \texttt{(}k_1 \oplus k_2\texttt{)}. \end{aligned}\]
and $d(k)$ is the number of parameters in expression $k$. Given data $\{(t_i,y_i)\}_{i=1}^n$, AutoGP infers likely values of the symbolic structure of the covariance kernel $k$, the kernel parameters $\theta$, and the observation noise variance $\eta$.
The ability to automatically synthesize covariance kernels in AutoGP is in contrast to existing Gaussian process libraries such as GaussianProcess.jl
sklearn.gaussian_process
, GPy
, and GPyTorch
, which all require users to manually specify $k$.
After model fitting, users can query the models to generate forecasts, compute probabilities, and inspect qualitative structure.
AutoGP
— ModuleMain module.
Exports
Model Initialization
AutoGP.GPModel
— Typestruct GPModel
A GPModel
contains covariance kernel structures and parameters for modeling data.
Fields
pf_state::Gen.ParticleFilterState
: Internal particle set.config::GP.GPConfig=GP.GPConfig()
: User-specific customization, refer toGP.GPConfig
.ds::IndexType
: Observed time points.y::Vector{<:Real}
: Observed time series values.ds_transform::Transforms.LinearTransform
: Transformation of time to direct space.y_transform::Transforms.LinearTransform
: Transformation of observations to direct space.
Constructors
model = GPModel(
ds::IndexType,
y::Vector{<:Real};
n_particles::Integer=8,
config::GP.GPConfig=GP.GPConfig())
See also
To perform learning given the data, refer to
AutoGP.num_particles
— Functionnum_particles(model::GPModel)
Return the number of particles.
AutoGP.seed!
— Functionseed!(seed)
Set the random seed of the global random number generator.
AutoGP.IndexType
— TypeIndexType = Union{Vector{<:Real}, Vector{<:Dates.TimeType}}
Permitted Julia types for Gaussian process time points. Real
numbers are ingested directly, treated as time points.. Instances of Dates.TimeType
are converted to numeric time points by using Dates.datetime2unix
.
End-to-End Model Fitting
AutoGP.fit_smc!
— Functionfit_smc!(
model::GPModel;
schedule::Vector{<:Integer},
n_mcmc::Int,
n_hmc::Int,
shuffle::Bool=true,
biased::Bool=false,
adaptive_resampling::Bool=true,
adaptive_rejuvenation::Bool=false,
hmc_config::Dict=Dict(),
verbose::Bool=false,
check::Bool=false,
callback_fn::Function=(; kwargs...) -> nothing)
Infer the structure and parameters of an appropriate Gaussian process covariance kernel for modeling the observed data. Inference is performed using sequential Monte Carlo.
Arguments
model::GPModel
: Instance of theGPModel
to use.schedule::Vector{<:Integer}
: Schedule for incorporating data for SMC, refer toSchedule
.n_mcmc::Int
: Number of involutive MCMC rejuvenation steps.n_hmc::Int
: Number of HMC steps per accepted involutive MCMC step.biased::Bool
: Whether to bias the proposal to produce "short" structures.shuffle::Bool=true
: Whether to shuffle indexesds
or incorporate data in the given order.adaptive_resampling::Bool=true
: Iftrue
resamples based on ESS threshold, else at each step.adaptive_rejuvenation::Bool=false
: Iftrue
rejuvenates only if resampled, else at each step.hmc_config::Dict
: Configuration for HMC inference on numeric parameters. Allowable keys are:n_exit::Integer=1
: Number of successive rejections after which HMC loop is terminated.L_param::Integer=10
Number of leapfrog steps for kernel parameters.L_noise::Integer=10
: Number of leapfrog steps for noise parameter.eps_param::Float64=0.02
: Step size for kernel parameters.eps_noise::Float64=0.02
: Step size for noise parameter.
verbose::Bool=false
: Report progress to stdout.check::Bool=false
: Perform dynamic correctness checks during inference.config::GP.GPConfig=GP.GPConfig()
: User-specific customization, refer toGP.GPConfig
.callback_fn
: A callback for monitoring inference, must be generated byAutoGP.Callbacks.make_smc_callback
.
AutoGP.fit_mcmc!
— Functionfit_mcmc!(
model::GPModel;
n_mcmc::Integer,
n_hmc::Integer,
biased::Bool=false,
verbose::Bool=false,
check::Bool=false,
callback_fn::Function=(; kwargs...) -> nothing)
Perform n_mcmc
steps of involutive MCMC on the structure, with n_hmc
steps of Hamiltonian Monte Carlo sampling on the parameters per accepted involutive MCMC move.
A callback_fn
can be provided to monitor the progress each MCMC step for which at least one particle (i.e, chain) accepted a transition. Its signature must contain a single varargs specifier, which will be populated with keys :model
, :step
, :elapsed
.
The callback_fn
imposes a roughly 2x runtime overhead as compared to the equivalent mcmc_structure!
method, because parallel execution must be synchronized across the particles to invoke the callback at each step. The :elapsed
variable provided to the callback function will still reflect an accurate estimate of the inference runtime without this overhead. If no callback is required, use mcmc_structure!
instead.
AutoGP.fit_greedy!
— Functionfunction fit_greedy!(
model::GPModel;
max_depth::Integer=model.config.max_depth,
verbose::Bool=false,
check::Bool=false,
callback_fn::Function=(; kwargs...) -> nothing)
Infer the structure and parameters of an appropriate Gaussian process covariance kernel for modeling the observed data. Inference is performed using greedy search, as described in Algorithm 2 of Kim and Teh, 2018. It is an error if max_depth
is not a finite positive number.
A callback_fn
can be provided to monitor the search progress at each stage. Its signature must contain a single varargs specifier, which will be populated with keys :model
, :step
, :elapsed
at each step of the greedy search.
Incremental Model Fitting
AutoGP.add_data!
— Functionadd_data!(model::GPModel, ds::IndexType, y::Vector{<:Real})
Incorporate new observations (ds, y)
into model
.
AutoGP.maybe_resample!
— Functionmaybe_resample!(model::GPModel, ess_threshold::Real)
Resample the particle collection in model
if ESS is below ess_threshold
. Setting ess_threshold = AutoGP.num_particles(model) + 1
will ensure that resampling always takes place, since the ESS is upper bounded by the number of particles.
AutoGP.mcmc_structure!
— Functionmcmc_structure!(model::GPModel, n_mcmc::Integer, n_hmc::Integer;
biased::Bool=false, verbose::Bool=false, check::Bool=false)
Perform n_mcmc
steps of involutive MCMC on the structure, with n_hmc
steps of Hamiltonian Monte Carlo sampling on the parameters per accepted involutive MCMC move.
AutoGP.mcmc_parameters!
— Functionmcmc_parameters!(model::GPModel, n_hmc::Integer; verbose::Bool=false, check::Bool=false)
Perform n_hmc
steps of Hamiltonian Monte Carlo sampling on the parameters.
Model Querying
AutoGP.predict
— Functionpredictions = predict(
model::GPModel,
ds::IndexType;
quantiles::Vector{Float64}=Float64[],
noise_pred::Union{Nothing,Float64}=nothing)
Return predictions for new index point ds
, and optionally quantiles corresponding to the provided quantiles
(numbers between 0 and 1, inclusive). By default, the noise_pred
of the new data is equal to the inferred noise
of the observed data within each particle in model
; using noise_pred=0.
returns the posterior distribution over the noiseless function values.
The returned DataFrames.DataFrame
has columns ["ds", "particle", "weight", "y_mean"]
, as well as any additional columns for the requested quantiles
.
Example
julia> ds = [Dates.Date(2020,1,1), Dates.Date(2020,1,2)] # Dates to query
julia> GPModel.predict(model, ds; quantiles=[.025, 0.975])
16×6 DataFrame
Row │ ds particle weight y_0.025 y_0.975 y_mean
│ Date Int64 Float64 Float64 Float64 Float64
─────┼────────────────────────────────────────────────────────────────────
1 │ 2020-01-01 1 4.97761e-22 -13510.0 14070.6 280.299
2 │ 2020-01-02 1 4.97761e-22 -13511.0 14071.6 280.299
3 │ 2020-01-01 2 0.279887 4504.73 8211.43 6358.08
4 │ 2020-01-02 2 0.279887 4448.06 8154.3 6301.18
5 │ 2020-01-01 3 0.0748059 -43638.6 65083.0 10722.2
6 │ 2020-01-02 3 0.0748059 -43662.0 65074.7 10706.4
7 │ 2020-01-01 4 0.60809 -17582.2 30762.4 6590.06
8 │ 2020-01-02 4 0.60809 -17588.0 30771.5 6591.78
AutoGP.predict_proba
— Functionfunction predict_proba(model::GPModel, ds::IndexType, y::Vector{<:Real})
Compute predictive probability of data y
at time points ds
under model
.
Example
julia> ds = [Dates.Date(2020,1,1), Dates.Date(2020,1,2)] # Dates to query
julia> y = [0.1, .0.5] # values to query
julia> GPModel.predict(model, ds, y)
7×3 DataFrame
Row │ particle weight logp
│ Int64 Float64 Float64
─────┼─────────────────────────────────
1 │ 1 0.0287155 -64.2388
2 │ 2 0.0437349 -59.7672
3 │ 3 0.576247 -62.6499
4 │ 4 0.00164846 -59.5311
5 │ 5 0.215255 -61.066
6 │ 6 0.134198 -64.4041
7 │ 7 0.000201078 -68.462
AutoGP.predict_mvn
— Functiondist = predict_mvn(model::GPModel, ds::IndexType; noise_pred::Union{Nothing,Float64}=nothing)
Return an instance of Distributions.MixtureModel
representing the overall posterior predictive distribution for data at index points ds
. By default, the noise_pred
of the new data is equal to the inferred noise
of the observed data within each particle in model
; using noise_pred=0.
returns the posterior distribution over the noiseless function values.
The returned dist
has precisely num_particles
(model)
components, each of type Distributions.MvNormal
, with weights particle_weights
(model)
. These objects can be retrieved using Distributions.components
and Distributions.probs
, respectively.
AutoGP.predict_quantile
— Function(x::Vector, success::Bool) = predict_quantile(
model::GPModel, ds::IndexType, q::Real;
noise_pred::Union{Nothing,Float64}=nothing, tol=1e-5, max_iter=1e6)
Evaluates the inverse cumulative distribution function (CDF) of the multivariate Gaussian mixture model returned by predict_mvn
at q
(between 0 and 1, exclusive) separately for each dimension. The returned vector x
has the same length as the index points ds
.
Note
The inverse CDF is numerically estimated using a binary search algorithm. The keyword arguments tol
and max_iter
correspond to the desired absolute tolerance of the estimate and the maximum number of binary search iterations, respectively. The returned Boolean variable success
indicates whether the returned value x
has been located to within the specified error tolerance.
See also
AutoGP.log_marginal_likelihood_estimate
— Functionlog_marginal_likelihood_estimate(model::GPModel)
Return estimate of marginal likelihood of data (in log space).
AutoGP.particle_weights
— Functionparticle_weights(model::GPModel)
Return vector of normalized particle weights.
AutoGP.effective_sample_size
— Functioneffective_sample_size(model::GPModel)
Return effective sample size (ESS) of weighted particle collection.
AutoGP.covariance_kernels
— Functioncovariance_kernels(model::GPModel)
Return Gaussian process covariance kernels in model
.
AutoGP.observation_noise_variances
— Functionobservation_noise_variances(model::GPModel)
Return list of observation noise variances for each particle in model
.
AutoGP.decompose
— Functionfunction decompose(model::GPModel)
Decompose each particle within model
into its constituent kernels. Supposing that num_particles
(model)
equals $k$, the return value models::Vector{GPModel}
of decompose
is a length-$k$ vector of GPModel
instances.
Therefore, models[i]
is a GPModel
that represents the decomposition of particle i
in model
into its constituent kernels. Each particle in models[i]
corresponds to a kernel fragment in the covariance for particle i
of model
(i.e., one particle for each GP.Node
in the covariance kernel).
The weights of models[i]
are arbitrary and have no meaningful value.
This function is particularly useful for visualizing the individual time series structures that make up each particle of model
.