Skip to content

Metamodels

Linear Basis Function Models

Linear basis function models are a simple class of metamodels that express the predicted output as a linear combination of basis functions evaluated for the input variables defined as

y(x)=i=1nβiφi(x),

where φi(x) are nonlinear basis functions that map the input space into an intermediate feature space and β represents the adjustable weights. Some commonly used basis functions are introduced next.

Monomial Basis

Monomial basis functions are defined as the powers, or products of powers in the multivariate case, of the input variables with a total degree of less than or equal to d. For example, the monomial basis in two variables of degree d=3 in graded reverse lexicographic order is given by

φ(x)=[1,x2,x1,x22,x1x2,x12,x23,x1x22,x12x2,x13]

The construction of this MonomialBasis is presented next.

julia
φ = MonomialBasis(2, 3)
MonomialBasis(2, 3, Monomials.Monomial[1, x2, x1, x2², x1x2, x1², x2³, x1x2², x1²x2, x1³])

By default the MonomialBasis includes the constant (zero degree) term. This behaviour can be changed by passing the include_zero=false keyword.

Radial Basis

A radial basis function (RBF) is a real-valued function φ(||xc||) that depends on the distance between the input x and a fixed point c, called a center. These functions are multivariate but reduce to scalar functions of the radius r=||xc||, hence the name radial basis function. The distance used is most commonly the euclidian norm. UncertaintyQuantification provides two types of RBFs.

Gaussian

φ(r)=exp(ϵr)

here, ϵ is a shape parameter. Gaussian radial basis functions are exposed through the GaussianRadialBasis struct.

Polyharmonic

φ(r)={rkwithk=1,3,5,,rkln(r)withk=2,4,6,

Note, that polyharmonic radial basis functions do not require a shape parameter. These RBs can be constructed using the PolyharmonicRadialBasis type.

Least Squares

Despite the nonlinearity of the basis functions, the model remains linear in its parameters, which allows efficient estimation of the weights using ordinary least squares. Given m observations (xi,yi), for i=1,,m we construct the design matrix Φ where each row contains the evaluated basis functions for one input:

Φij=φj(xi), for i=1,,n and j=1,,m

The optimal weight vector is found by minimizing the sum-of-squares error

E(β)=i=1m(y(xi,β)yi)2

yielding the closed-form solution via

β=(ΦΦ)1Φy,

where y is the output vector.

Example

Consider the function

y(x)=xcos(x),

where xU(5,5)

We use HaltonSampling to sample 150 data points from the input variable, evaluate the model, and fit a LinearBasisFunctionModel using a MonomialBasis of degree d=9.

julia
x = RandomVariable(Uniform(-5, 5), :x)
y = Model(
        df -> df.x .* cos.(df.x),
        :y,
    )
data = sample(x, HaltonSampling(150))
evaluate!(y, data)
lbfm = LinearBasisFunctionModel(data, :y, MonomialBasis(1, 9))
ResponseSurface(MonomialBasis(1, 9, Monomials.Monomial[1, x1, x1², x1³, x1⁴, x1⁵, x1⁶, x1⁷, x1⁸, x1⁹]), [0.00018241593646902075, 0.9878644609495059, -0.00018150970456987044, -0.48925985169682223, 4.747249404303626e-5, 0.038987490233917725, -3.913393749589379e-6, -0.0011115728398402001, 9.668870671278248e-8, 1.1603496934147144e-5], [:x], :y)

A plot comparing the resulting model to the data points is presented next.

Response Surface

A linear basis function model constructed from a MonomialBasis is also known as a polynomial Response Surface [ DocumenterCitations.CitationSiteNode("khuriResponseSurfaceMethodology2010-cite-1")

]. For this reason we provide a convenient alias ResponseSurface. Using this alias the previous example can be adapted as follows.

julia
rs = ResponseSurface(data, :y, 9)
ResponseSurface(MonomialBasis(1, 9, Monomials.Monomial[1, x1, x1², x1³, x1⁴, x1⁵, x1⁶, x1⁷, x1⁸, x1⁹]), [0.00018241593646902075, 0.9878644609495059, -0.00018150970456987044, -0.48925985169682223, 4.747249404303626e-5, 0.038987490233917725, -3.913393749589379e-6, -0.0011115728398402001, 9.668870671278248e-8, 1.1603496934147144e-5], [:x], :y)

Design Of Experiments

Several experimental designs have been developed to efficiently estimate ResponseSurface models [ DocumenterCitations.CitationSiteNode("khuriResponseSurfaceMethodology2010-cite-2")

]. Although designed for response surface methodology these designs can be used to fit any metamodel. However, for more complex models we suggest using Quasi Monte Carlo sampling schemes instead.

The designs implemented in UncertaintyQuantification are TwoLevelFactorial, FullFactorial, FractionalFactorial, CentralComposite, BoxBehnken, and PlackettBurman.

Interval Predictor Model

An interval predictor model (IPM)[ DocumenterCitations.CitationSiteNode("crespoIntervalPredictorModels2016-cite-1")

] is a function that returns an interval instead of a precise value for the dependent variable given as

Iy(x,P)={y=pTφ(x),pP},

where φ(x) is an arbitrary basis and the uncertainty set P is defined as

P={p:ppp}.

Using the defining vertices of P p and p the IPM results as

Iy(x,P)=[y(x,p,p),y(x,p,p)],

with

y(x,p,p)=pT(φ(x)|φ(x)|2)+pT(φ(x)+|φ(x)|2)

and

y(x,p,p)=pT(φ(x)+|φ(x)|2)+pT(φ(x)|φ(x)|2).

Here, y and y are the lower and upper bounds of the IPM.

The distance between the lower and upper bound given by

δy(x,p,p)=(pp)T|φ(x)|

is known as the spread of the IPM. The optimal defining vertices for a given data set are found by minimizing the average spread such that all data points fall into the IPM by solving the following convex constrained optimization problem.

{p,p}=argmaxu,v{Ex[δy(x,u,v]:y(xi,v,u)yiy(xi,v,u),uv}

Example

Consider the function

y(x)=x2cos(x)sin(3x)exp(x2)xcos(x2)+xg,

where xU(5.5,5.5 and gN(0,1). We generate a data sequence of N=150 points and fit an IntervalPredictorModel using a MonomialBasis of sixth degree.

julia
x = RandomVariable(Uniform(-5.5, 5.5), :x)
data = sample(x, HaltonSampling(150))

m = Model(
        df ->
            df.x .^ 2 .* cos.(df.x) .- sin.(3 * df.x) .* exp.(-df.x .^ 2) .- df.x .-
            cos.(df.x .^ 2) .+ df.x .* randn(size(df, 1)),
        :y,
    )

evaluate!(m, data)

b = MonomialBasis(1,6)
ipm = IntervalPredictorModel(data, :y, b)
IntervalPredictorModel{MonomialBasis}(MonomialBasis(1, 6, Monomials.Monomial[1, x1, x1², x1³, x1⁴, x1⁵, x1⁶]), [-1.2720546724495774, -2.547509011220345, -1.8441520363249984, 0.03628854681653243, 0.04630398819605291, -6.102061289813858e-5, 0.0013326512447875813], [6.780543527846371, -0.5173681859439141, -1.8441520363249966, 0.036288546816527135, 0.04630398819605069, -6.102061289878519e-5, 0.0013326512447874406], [:x], :y, 150)

The following figure presents the bounds of the resulting IPM and the corresponding least squares solution. Note, that the least squares solution is not guaranteed to be between the bounds of the IPM.

IPM reliability

The reliability of the IPM, that is the probability that and unobserved data point (x,y) will fall in the interval Iy(x,P) can be assessed using the reliability function. The function reliability(ipm, ϵ) returns the confidence parameter β. Then, the reliability of the IPM is no less than 1ϵ with confidence 1β.

julia
1 - reliability(ipm, 0.1548)
0.9898502075787752

Reliability analysis

As the IPM is an imprecise model, it can only be applied in a reliability analysis using the DoubleLoop or RandomSlicing. For more information, see Imprecise Reliability Analysis.

Gaussian Process Regression

Theoretical Background

A Gaussian Process (GP) is a collection of random variables, any finite subset of which has a joint Gaussian distribution. It is fully specified by a mean function m(x) and a covariance (kernel) function k(x,x). In GP regression, we aim to model an unknown function f(x). Before observing any data, we assume that the function f(x) is distributed according to a GP:

f(x)GP(m(x),k(x,x)).

This prior GP specifies that any finite collection of function values follows a multivariate normal distribution.

To define a prior GP we use AbstractGPs.jl for the GP interface and mean function, and KernelFunctions.jl for the definition of a covariance kernel. Below, we construct a simple prior GP with a constant zero mean function and a scaled squared exponential kernel:

julia
using UncertaintyQuantification

kernel = SqExponentialKernel()  ScaleTransform(3.0)

Note that the definition of a prior GP is handled by UncertaintyQuantification if no prior GP is specified. The construction of a GaussianProcess is flexible. Mean functions, kernels and many other parameters can be specified later directly in the constructor of the GaussianProcess.

Posterior Gaussian Process

The posterior GP represents the distribution of functions after incorporating observed data. We denote the observation data as:

D={(x^i,f^i)i=1,,N},

where f^i=f(x^i) in the noise-free observation case, and f^i=f(x^i)+ei in the noisy case, with independent noise terms eiN(0,σe2). Let X^=[x^1,,x^N] denote the collection of observation data locations. The corresponding mean vector and covariance matrix are:

μ(X^)=[m(x^1),,m(x^N)],K(X^,X^) with entries Kij=k(x^i,x^j).

For a new input location x we are interested at the unknown function value f=f(x). By the definition of a GP, the joint distribution of observed outputs f^i and the unknown f is multivariate Gaussian:

[f^f]=N([μ(X^)m(x)],[K(X^,X^)K(X^,x)K(x,X^)K(x,x)]),

where:

  • K(X^,X^) is the covariance matrix with entries Kij=k(x^i,x^j),

  • K(X^,x) is the covariance matrix with entries Ki1=k(x^i,x),

  • and (x,x) is the variance at the unknown input location.

We can then obtain the posterior distribution of f from the properties of multivariate Gaussian distributions (see, e.g. Appendix A.2 in [ DocumenterCitations.CitationSiteNode("rasmussen2005gaussian-cite-1")

]), by conditioning the joint Gaussian on the observed outputs f^i:

fX^,f^,xN(μ(x),Σ(x)),

with

μ(x)=m(x)+K(x,X^)K(X^,X^)1(f^μ(X^)),Σ(x)=K(x,x)K(x,X^)K(X^,X^)1K(X^,x).

In the noisy observation case, the covariance between training points is adjusted by adding the noise variance:

K(X^,X^)K(X^,X^)+σe2I.

The computation of the posterior predictive distribution generalizes straightforwardly to multiple input locations, providing both the posterior mean, which can serve as a regression estimate of the unknown function, and the posterior variances, which quantify the uncertainty at each point. Because the posterior is multivariate Gaussian, one can also sample function realizations at specified locations to visualize possible functions consistent with the observed data.

To construct a posterior GP, we need to define training data in form of a DataFrame. Constructing a GaussianProcess model will then automatically compute the posterior GP to predict requested the modeled output y and by default it will also optimize the hyperparameters. If this is not desired, the input learn_hyperparameters=false can be set.

The following creates a standard GP with mean function ConstMean(), kernel SqExponentialKernel(), and directly optimizes the hyperparameters. Note that while ConstMean(0.0) and ZeroMean() provide the same zero-mean prior GP, using ConstMean() also allows for optimization of the mean. We also equip the GP with small observation noise σ2, which has implications on the numerical stability and allows the GP to handle imprecise data. The noise can also be optimized as part of the hyperparameter optimization, but it is not optimized by default. To specify different mean functions and/or kernels, either construct a GP manually beforehand, or use them as inputs.

julia
x = collect(range(0, 10, 10))
y = sin.(x) + 0.3 * cos.(2 .* x)
df = DataFrame(x = x, y = y)

mean_fct = ConstMean(0.0)
kernel = SqExponentialKernel()  ScaleTransform(3.0)

gp_prior = GP(mean_fct, kernel)

σ² = 1e-5

# these are equivalent
gp_model = GaussianProcess(gp_prior, df, :y; σ²=σ²)
gp_model = GaussianProcess(df, :y; σ²=σ², mean_fct=mean_fct, kernel=kernel)
# providing the input learn_noise=true also optimizes the data noise

Now we can use our GP model to predict at new input locations x_test:

julia
x_test = collect(range(0, 5, 500))
prediction = DataFrame(:x => x_test)

evaluate!(gp_model, prediction; mode=:mean_and_var)


plot!(
    x_test, prediction_mean, ribbon=2 .* prediction_std,
    color=:grey, alpha=0.5, label="Confidence band"

Hyperparameter optimization

GP models typically contain hyperparameters in their mean functions m(x;θm) and covariance kernel functions k(x,x;θk). The observation noise variance σe2 is also considered a hyperparameter related to the kernel. The choice of hyperparameters strongly affects the quality of the posterior GP.

A common approach to selecting hyperparameters is maximum likelihood estimation (MLE) (see, e.g. [ DocumenterCitations.CitationSiteNode("rasmussen2005gaussian-cite-2")

]), where we maximize the likelihood of observing the training data D under the chosen GP prior.

The marginal likelihood of the observed training outputs f^ is:

p(f^X^,θm,θk,σe2)=N(f^μθm(X^),Kθk(X^,X^)+σe2I),

where μθm(X^) and Kθk(X^,X^) denote the parameter dependent versions of the previously defined quantities.

For numerical reasons, the logarithm of the marginal likelihood is typically used. Maximizing the log marginal likelihood with respect to the hyperparameters then yields the parameters that best explain the observed data. After obtaining the optimal hyperparameters, the posterior GP can be constructed as described above.

UncertaintyQuantification.jl provides a default optimizer for the hyperparameters based on the MaximumLikelihoodEstimation constructor.

optimizer::AbstractHyperparameterOptimization=MaximumLikelihoodEstimation(Optim.LBFGS(), Optim.Options(; iterations=100, show_trace=false))

If other options are desired, a different optimizer can be constructed based on Optim.jl. The script below shows the difference between an optimized and unoptimized GP.

julia
using Optim

optimization = MaximumLikelihoodEstimation(
                Optim.LBFGS(),
                Optim.Options(; iterations=10, show_trace=false)
            )

gp_model = GaussianProcess(df, :y;
                           σ²=σ²,
                           mean_fct=mean_fct,
                           kernel=kernel,
                           optimizer=optimization
                           )

gp_model_unoptimized = GaussianProcess(df, :y;
                            σ²=σ²,
                            mean_fct=mean_fct,
                            kernel=kernel,
                            learn_hyperparameters=false
                           )

prediction = DataFrame(:x => x_test)
prediction_unopt = DataFrame(:x => x_test)
evaluate!(gp_model, prediction; mode=:mean_and_var)
evaluate!(gp_model_unoptimized, prediction_unopt; mode=:mean_and_var)

Internally, MaximumLikelihoodEstimation() defaults to using LBFGS optimizer that performs 100 optimization steps with standard optimization hyperparameters as defined Optim.jl. Note that any other first-order optimizer supported by Optim.jl, along with its corresponding hyperparameters, can also be used when constructing MaximumLikelihoodEstimation.

During optimization, GP hyperparameters θm,θk and σe2 are automatically extracted and updated.

We support the automatic extraction of hyperparameters from mean functions provided by AbstractGPs.jl, with the exception of:

  • Custom mean functions CustomMean. These are defined with a custom function that itself could depend on hyperparameters. These additional hyperparameters are ignored in the optimization.

Kernel functions are defined with the kernels and transformations provided by KernelFunctions.jl. For similar reasons as with CustomMean, we do not extract potential function hyperparameters from the following kernels or transforms:

  • Transforms defined with custom functions FunctionTransform,

  • The GibbsKernel, which models a kernel lengthscale parameter with the help of a function.

Further, GP models containing the following kernels are not supported for hyperparameter optimization currently:

  • Multi-output kernels MOKernel,

  • Neural kernel networks [NeuralKernelNetwork].

Adaptive Gaussian Process Regression

Fitting a good GP surrogate can require many expensive model evaluations if the initial experimental design is chosen naively. Adaptive (or active learning) Gaussian process regression instead starts from a small initial design and iteratively enriches the training data: at each iteration a set of candidate points is sampled from the input space, an acquisition function (also called a learning function) scores every candidate, the most promising candidate is evaluated with the true (expensive) model, and the GP is refitted with the enlarged training set. This is repeated for a fixed number of iterations, or until the acquisition function's own convergence criterion is met.

The AdaptiveGaussianProcess function drives this loop. It first constructs (or accepts) an initial GaussianProcess, then calls evaluate! on the supplied model for each newly selected point.

julia
x = RandomVariable(Uniform(-10, 10), :x1)
model = Model(df -> sin.(df.x1) .* df.x1 .^ 2, :y)

mean_f = ConstMean(0.0)
kernel = Matern52Kernel()
gp_prior = GP(mean_f, kernel)

n_design_points = 10
n_added_points = 5

adaptive_gp = AdaptiveGaussianProcess(
    gp_prior,
    x,
    model,
    :y,
    MaximumVariance(),
    n_added_points,
    n_design_points,
)

As with GaussianProcess, the initial n_design_points are sampled with an experimental_design (LatinHypercubeSampling by default), while the n_added_points adaptively selected candidates are drawn from candidate_sampling, a Monte Carlo sampling scheme (MonteCarlo(100_000) by default). Hyperparameters can be re-optimized after every added point via learn_hyperparameters (default true).

The resulting adaptive_gp is a regular GaussianProcess and can be evaluated as usual:

julia
using DataFrames
using Plots

test_data = DataFrame(x1 = -10:0.1:10)
evaluate!(adaptive_gp, test_data; mode = :mean_and_var)
evaluate!(model, test_data)

p = plot(test_data.x1, test_data.y_mean; ribbon = 2 .* sqrt.(test_data.y_var), label = "GP mean ± 2σ", xlabel = "x₁", ylabel = "y", color = :blue, alpha = 0.5)
plot!(p, test_data.x1, test_data.y; label = "True function", color = :red, linestyle = :dash)
scatter!(p, adaptive_gp.training_data.x1[1:n_design_points], adaptive_gp.training_data.y[1:n_design_points]; label = "Initial design", color = :black)
scatter!(p, adaptive_gp.training_data.x1[(n_design_points + 1):end], adaptive_gp.training_data.y[(n_design_points + 1):end]; label = "Adaptively added")

Acquisition Functions

The acquisition function determines which candidate point is added next, and therefore what the adaptive scheme optimizes for. Given the posterior mean μ(x) and posterior standard deviation σ(x) of the current GP, the next point x+ is chosen from the sampled candidates xXc by maximizing (or minimizing) a criterion a(x). For a review of various acquisition functions we refer to [ DocumenterCitations.CitationSiteNode("fuhg2021state-cite-1")

].

UncertaintyQuantification.jl provides the following acquisition functions to adapatively refine GP regression models; we separate them by there main area of application:

General active learning

Goal: improve the global fit of the GP

x+=argmaxxXc σ2(x).
  • MaximinDistance is a space-filling criterion that adds the candidate farthest (in input space) from every existing training point x^iX^,
x+=argmaxxXc minixx^i.
  • ExpectedImprovementForGlobalFit (EIGF) trades off the local discrepancy to the nearest training observation f^i(x) (with i(x)=argminixx^i) against the posterior variance,
x+=argmaxxXc [μ(x)f^i(x)]2+σ2(x).

Bayesian optimization

Goal: refine the global minimum f^best=minif^i

EI(x)=(f^bestμ(x)ξ)Φ(z)+σ(x)ϕ(z),z=f^bestμ(x)ξσ(x),x+=argmaxxXc EI(x),

where Φ and ϕ are the standard normal cdf and pdf, respectively (EI(x)=0 if σ(x)=0).

PI(x)=Φ(z),z=f^bestμ(x)ξσ(x),x+=argmaxxXc PI(x).
  • UpperConfidenceBound with exploration weight κ minimizes a lower confidence bound (for a minimization objective),
x+=argminxXc μ(x)κσ(x).

Reliability analysis

Goal: refine the limit-state surface g(x)=τ (typically, τ=0):

  • DeviationNumber, the U-function used in AK-MCS, adds the point closest to the limit state relative to its uncertainty,
x+=argminxXc U(x),U(x)=|μ(x)τ|σ(x).
  • ExpectedFeasibility (EFF) integrates the probability that the true response lies within an ϵ-band ϵ(x)=epsilon_factorσ(x) around the limit state,
EFF(x)=(μ(x)τ)[2Φ(z)Φ(z)Φ(z+)]σ(x)[2ϕ(z)ϕ(z)ϕ(z+)]+ϵ(x)[Φ(z+)Φ(z)],z=τμ(x)σ(x),z=τϵ(x)μ(x)σ(x),z+=τ+ϵ(x)μ(x)σ(x),x+=argmaxxXc EFF(x).