Heteroscedastic Gaussian Regression
You are seeing the HTML output generated by Documenter.jl and Literate.jl from the Julia source file. The corresponding notebook can be viewed in nbviewer.
We load all the necessary packages
using AbstractGPs
using ApproximateGPs
using ArraysOfArrays
using AugmentedGPLikelihoods
using Distributions
using LinearAlgebra
using SplitApplyCombine
Plotting libraries
using Plots
We create some random data (sorted for plotting reasons)
N = 100
x = collect(range(-10, 10; length=N))
lik = HeteroscedasticGaussianLikelihood(InvScaledLogistic(3.0))
SplitApplyCombine.invert(x::ArrayOfSimilarArrays) = nestedview(flatview(x)')
kernel = 5.0 * with_lengthscale(SqExponentialKernel(), 2.0)
gp_f = GP(kernel)
μ₀g = -3.0
gp_g = GP(μ₀g, kernel)
f = rand(gp_f(x, 1e-6))
g = rand(gp_g(x, 1e-6))
py = lik(invert([f, g])) # invert turns [f, g] into [[f_1, g_1], ...]
y = rand(py);
We plot the sampled data
plt = plot(x, mean(py); ribbon=sqrt.(var(py)), label="y|f,g", lw=2.0)
scatter!(plt, x, y; msw=0.0, label="y")
plt2 = plot(x, [f, g]; label=["f" "g"], lw=2.0)
plot(plt, plt2)
CAVI Updates
We write our CAVI algorithmm
function u_posterior(fz, m, S)
return posterior(SparseVariationalApproximation(Centered(), fz, MvNormal(copy(m), S)))
end;
We can also optimize the likelihood parameter λ
function opt_lik(
lik::HeteroscedasticGaussianLikelihood,
(qf, qg)::AbstractVector{<:AbstractVector{<:Normal}},
y::AbstractVector{<:Real},
)
ψ = AugmentedGPLikelihoods.second_moment.(qf .- y) / 2
c = sqrt.(AugmentedGPLikelihoods.second_moment.(qg))
σ̃g = AugmentedGPLikelihoods.approx_expected_logistic.(-mean.(qg), c)
λ = max(length(y) / (2 * dot(ψ, 1 .- σ̃g)), lik.invlink.λ)
return HeteroscedasticGaussianLikelihood(InvScaledLogistic(λ))
end
function cavi!(fzs, x, y, ms, Ss, qΩ, lik; niter=10)
K = ApproximateGPs._chol_cov(fzs[1])
for _ in 1:niter
posts_u = u_posterior.(fzs, ms, Ss)
posts_fs = AbstractGPs.marginals.([p_u(x) for p_u in posts_u])
lik = opt_lik(lik, posts_fs, y)
aux_posterior!(qΩ, lik, y, invert(posts_fs))
ηs, Λs = expected_auglik_potential_and_precision(lik, qΩ, y, invert(posts_fs))
Ss .= inv.(Symmetric.(Ref(inv(K)) .+ Diagonal.(Λs)))
ms .= Ss .* (ηs .+ Ref(K) .\ mean.(fzs))
end
return lik
end
cavi! (generic function with 1 method)
Now we just initialize the variational parameters
ms = nestedview(zeros(N, nlatent(lik)))
Ss = [Matrix{Float64}(I(N)) for _ in 1:nlatent(lik)]
qΩ = init_aux_posterior(lik, N)
fzs = [gp_f(x, 1e-8), gp_g(x, 1e-8)];
x_te = -10:0.01:10
-10.0:0.01:10.0
We run CAVI for 3-4 iterations
new_lik = cavi!(fzs, x, y, ms, Ss, qΩ, lik; niter=20);
And visualize the obtained variational posterior
f_te = u_posterior(fzs[1], ms[1], Ss[1])(x_te)
g_te = u_posterior(fzs[2], ms[2], Ss[2])(x_te)
plot!(plt, x_te, mean(f_te); ribbon=sqrt.(new_lik.invlink.(mean(g_te))), label="y|q(f,g)")
plot!(plt2, f_te; color=1, linestye=:dash, alpha=0.5, label="q(f)")
plot!(plt2, g_te; color=2, linestyle=:dash, alpha=0.5, label="q(g)")
plot(plt, plt2)
ELBO
How can one compute the Augmented ELBO? Again AugmentedGPLikelihoods provides helper functions to not have to compute everything yourself
function aug_elbo(lik, u_post, x, y)
qf = marginals(u_post(x))
qΩ = aux_posterior(lik, y, qf)
return expected_logtilt(lik, qΩ, y, qf) - aux_kldivergence(lik, qΩ, y) -
kldivergence(u_post.approx.q, u_post.approx.fz)
end
aug_elbo (generic function with 1 method)
Gibbs Sampling
We create our Gibbs sampling algorithm (we could do something fancier with AbstractMCMC)
function gibbs_sample(fzs, fs, Ω; nsamples=200)
K = ApproximateGPs._chol_cov(fzs[1])
Σ = [zeros(N, N) for _ in 1:nlatent(lik)]
μ = [zeros(N) for _ in 1:nlatent(lik)]
return map(1:nsamples) do _
aux_sample!(Ω, lik, y, invert(fs))
Σ .=
inv.(
Symmetric.(
Ref(inv(K)) .+ Diagonal.(auglik_precision(lik, Ω, y, invert(fs)))
)
)
μ .= Σ .* (auglik_potential(lik, Ω, y, invert(fs)) .+ Ref(K) .\ mean.(fzs))
rand!.(MvNormal.(μ, Σ), fs) # this corresponds to f -> g
return copy(fs)
end
end;
We initialize our random variables
fs_init = nestedview(randn(N, nlatent(lik)))
Ω = init_aux_variables(lik, N);
Run the sampling for default number of iterations (200)
fs_samples = gibbs_sample(fzs, fs_init, Ω);
And visualize the samples overlapped to the variational posterior that we found earlier.
for fs in fs_samples
plot!(plt, x, fs[1]; color=3, alpha=0.07, label="")
for i in 1:nlatent(lik)
plot!(plt2, x, fs[i]; color=i, alpha=0.07, label="")
end
end
plt2
plot(plt, plt2)
Package and system information
Package information (click to expand)
Status `~/work/AugmentedGPLikelihoods.jl/AugmentedGPLikelihoods.jl/examples/heteroscedasticgaussian/Project.toml` [99985d1d] AbstractGPs v0.5.19 [298c2ebc] ApproximateGPs v0.4.5 [65a8f2f4] ArraysOfArrays v0.6.1 [4689c64d] AugmentedGPLikelihoods v0.4.18 `/home/runner/work/AugmentedGPLikelihoods.jl/AugmentedGPLikelihoods.jl#727b50a` [31c24e10] Distributions v0.25.102 [98b081ad] Literate v2.15.0 [91a5bcdd] Plots v1.39.0 [03a91e81] SplitApplyCombine v1.2.2To reproduce this notebook's package environment, you can download the full Manifest.toml.
System information (click to expand)
Julia Version 1.9.3 Commit bed2cd540a1 (2023-08-24 14:43 UTC) Build Info: Official https://julialang.org/ release Platform Info: OS: Linux (x86_64-linux-gnu) CPU: 2 × Intel(R) Xeon(R) Platinum 8272CL CPU @ 2.60GHz WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-14.0.6 (ORCJIT, skylake-avx512) Threads: 1 on 2 virtual cores Environment: JULIA_IMAGE_THREADS = 1 JULIA_PKG_SERVER_REGISTRY_PREFERENCE = eager JULIA_LOAD_PATH = :/home/runner/.julia/packages/JuliaGPsDocs/7M86H/src
This page was generated using Literate.jl.