2014-11-21 11:52:28 +00:00
|
|
|
# Copyright (c) 2012-2014, GPy authors (see AUTHORS.txt).
|
2013-12-04 12:02:30 +00:00
|
|
|
# Licensed under the BSD 3-clause license (see LICENSE.txt)
|
|
|
|
|
|
2015-02-27 17:39:15 +00:00
|
|
|
from .posterior import Posterior
|
2013-12-05 15:09:31 -05:00
|
|
|
from ...util.linalg import pdinv, dpotrs, tdot
|
2014-03-18 12:28:46 +00:00
|
|
|
from ...util import diag
|
2013-12-05 15:09:31 -05:00
|
|
|
import numpy as np
|
2014-05-15 14:06:00 +01:00
|
|
|
from . import LatentFunctionInference
|
2013-12-04 17:58:02 +00:00
|
|
|
log_2_pi = np.log(2*np.pi)
|
2013-12-04 12:02:30 +00:00
|
|
|
|
|
|
|
|
|
2014-05-15 14:06:00 +01:00
|
|
|
class ExactGaussianInference(LatentFunctionInference):
|
2013-12-04 17:58:02 +00:00
|
|
|
"""
|
|
|
|
|
An object for inference when the likelihood is Gaussian.
|
|
|
|
|
|
|
|
|
|
The function self.inference returns a Posterior object, which summarizes
|
|
|
|
|
the posterior.
|
|
|
|
|
|
|
|
|
|
For efficiency, we sometimes work with the cholesky of Y*Y.T. To save repeatedly recomputing this, we cache it.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self):
|
2013-12-05 15:09:31 -05:00
|
|
|
pass#self._YYTfactor_cache = caching.cache()
|
2013-12-04 17:58:02 +00:00
|
|
|
|
2013-12-05 15:09:31 -05:00
|
|
|
def get_YYTfactor(self, Y):
|
2013-12-04 17:58:02 +00:00
|
|
|
"""
|
2014-01-24 11:03:17 +00:00
|
|
|
find a matrix L which satisfies LL^T = YY^T.
|
2013-12-04 17:58:02 +00:00
|
|
|
|
2014-01-24 14:06:16 +00:00
|
|
|
Note that L may have fewer columns than Y, else L=Y.
|
2013-12-04 17:58:02 +00:00
|
|
|
"""
|
|
|
|
|
N, D = Y.shape
|
|
|
|
|
if (N>D):
|
|
|
|
|
return Y
|
|
|
|
|
else:
|
2014-01-24 11:03:17 +00:00
|
|
|
#if Y in self.cache, return self.Cache[Y], else store Y in cache and return L.
|
2014-04-09 12:22:46 +01:00
|
|
|
#print "WARNING: N>D of Y, we need caching of L, such that L*L^T = Y, returning Y still!"
|
2014-03-28 12:11:14 +00:00
|
|
|
return Y
|
2013-12-04 17:58:02 +00:00
|
|
|
|
2015-03-23 14:47:49 +00:00
|
|
|
def inference(self, kern, X, likelihood, Y, mean_function=None, Y_metadata=None):
|
2013-12-04 17:58:02 +00:00
|
|
|
"""
|
|
|
|
|
Returns a Posterior class containing essential quantities of the posterior
|
|
|
|
|
"""
|
2015-03-23 14:47:49 +00:00
|
|
|
|
2015-03-23 15:48:20 +00:00
|
|
|
if mean_function is None:
|
|
|
|
|
m = 0
|
|
|
|
|
else:
|
|
|
|
|
m = mean_function.f(X)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
YYT_factor = self.get_YYTfactor(Y-m)
|
2013-12-04 17:58:02 +00:00
|
|
|
|
2014-01-24 14:06:16 +00:00
|
|
|
K = kern.K(X)
|
|
|
|
|
|
2014-03-18 12:28:46 +00:00
|
|
|
Ky = K.copy()
|
2015-04-24 08:59:30 +02:00
|
|
|
diag.add(Ky, likelihood.gaussian_variance(Y_metadata)+1e-8)
|
2014-03-18 12:28:46 +00:00
|
|
|
Wi, LW, LWi, W_logdet = pdinv(Ky)
|
2013-12-04 17:58:02 +00:00
|
|
|
|
|
|
|
|
alpha, _ = dpotrs(LW, YYT_factor, lower=1)
|
|
|
|
|
|
2014-01-24 14:06:16 +00:00
|
|
|
log_marginal = 0.5*(-Y.size * log_2_pi - Y.shape[1] * W_logdet - np.sum(alpha * YYT_factor))
|
2014-03-13 14:42:03 +00:00
|
|
|
|
2013-12-04 17:58:02 +00:00
|
|
|
dL_dK = 0.5 * (tdot(alpha) - Y.shape[1] * Wi)
|
|
|
|
|
|
2014-03-17 10:33:58 +00:00
|
|
|
dL_dthetaL = likelihood.exact_inference_gradients(np.diag(dL_dK),Y_metadata)
|
2014-03-13 14:42:03 +00:00
|
|
|
|
2015-03-26 08:48:56 +00:00
|
|
|
return Posterior(woodbury_chol=LW, woodbury_vector=alpha, K=K), log_marginal, {'dL_dK':dL_dK, 'dL_dthetaL':dL_dthetaL, 'dL_dm':alpha}
|
2015-04-14 14:17:08 +01:00
|
|
|
|
|
|
|
|
def LOO(self, kern, X, Y, likelihood, posterior, Y_metadata=None, K=None):
|
|
|
|
|
"""
|
|
|
|
|
Leave one out error as found in
|
|
|
|
|
"Bayesian leave-one-out cross-validation approximations for Gaussian latent variable models"
|
|
|
|
|
Vehtari et al. 2014.
|
|
|
|
|
"""
|
|
|
|
|
g = posterior.woodbury_vector
|
|
|
|
|
c = posterior.woodbury_inv
|
|
|
|
|
c_diag = np.diag(c)[:, None]
|
|
|
|
|
neg_log_marginal_LOO = 0.5*np.log(2*np.pi) - 0.5*np.log(c_diag) + 0.5*(g**2)/c_diag
|
|
|
|
|
#believe from Predictive Approaches for Choosing Hyperparameters in Gaussian Processes
|
|
|
|
|
#this is the negative marginal LOO
|
|
|
|
|
return -neg_log_marginal_LOO
|