Merge branch 'params' of github.com:SheffieldML/GPy into params

This commit is contained in:
James Hensman 2014-03-21 15:23:52 +00:00
commit 044e714ad4
4 changed files with 109 additions and 16 deletions

View file

@ -74,25 +74,27 @@ class GP(Model):
def _raw_predict(self, _Xnew, full_cov=False): def _raw_predict(self, _Xnew, full_cov=False):
""" """
Internal helper function for making predictions, does not account For making predictions, does not account for normalization or likelihood
for normalization or likelihood
full_cov is a boolean which defines whether the full covariance matrix full_cov is a boolean which defines whether the full covariance matrix
of the prediction is computed. If full_cov is False (default), only the of the prediction is computed. If full_cov is False (default), only the
diagonal of the covariance is returned. diagonal of the covariance is returned.
$$
p(f*|X*, X, Y) = \int^{\inf}_{\inf} p(f*|f,X*)p(f|X,Y) df
= N(f*| K_{x*x}(K_{xx} + \Sigma)^{-1}Y, K_{x*x*} - K_{xx*}(K_{xx} + \Sigma)^{-1}K_{xx*}
\Sigma := \texttt{Likelihood.variance / Approximate likelihood covariance}
$$
""" """
Kx = self.kern.K(_Xnew, self.X).T Kx = self.kern.K(_Xnew, self.X).T
#LiKx, _ = dtrtrs(self.posterior.woodbury_chol, np.asfortranarray(Kx), lower=1)
WiKx = np.dot(self.posterior.woodbury_inv, Kx) WiKx = np.dot(self.posterior.woodbury_inv, Kx)
mu = np.dot(Kx.T, self.posterior.woodbury_vector) mu = np.dot(Kx.T, self.posterior.woodbury_vector)
if full_cov: if full_cov:
Kxx = self.kern.K(_Xnew) Kxx = self.kern.K(_Xnew)
#var = Kxx - tdot(LiKx.T) var = Kxx - np.dot(Kx.T, WiKx)
var = np.dot(Kx.T, WiKx)
else: else:
Kxx = self.kern.Kdiag(_Xnew) Kxx = self.kern.Kdiag(_Xnew)
#var = Kxx - np.sum(LiKx*LiKx, 0)
var = Kxx - np.sum(WiKx*Kx, 0) var = Kxx - np.sum(WiKx*Kx, 0)
var = var.reshape(-1, 1) var = var.reshape(-1, 1)

View file

@ -88,8 +88,9 @@ class SparseGP(GP):
mu = np.dot(Kx.T, self.posterior.woodbury_vector) mu = np.dot(Kx.T, self.posterior.woodbury_vector)
if full_cov: if full_cov:
Kxx = self.kern.K(Xnew) Kxx = self.kern.K(Xnew)
#var = Kxx - mdot(Kx.T, self.posterior.woodbury_inv, Kx) var = Kxx - np.dot(Kx.T, np.dot(self.posterior.woodbury_inv, Kx))
var = Kxx - np.tensordot(np.dot(np.atleast_3d(self.posterior.woodbury_inv).T, Kx).T, Kx, [1,0]).swapaxes(1,2) #var = Kxx[:,:,None] - np.tensordot(np.dot(np.atleast_3d(self.posterior.woodbury_inv).T, Kx).T, Kx, [1,0]).swapaxes(1,2)
var = var.squeeze()
else: else:
Kxx = self.kern.Kdiag(Xnew) Kxx = self.kern.Kdiag(Xnew)
var = (Kxx - np.sum(np.dot(np.atleast_3d(self.posterior.woodbury_inv).T, Kx) * Kx[None,:,:], 1)).T var = (Kxx - np.sum(np.dot(np.atleast_3d(self.posterior.woodbury_inv).T, Kx) * Kx[None,:,:], 1)).T

View file

@ -73,20 +73,37 @@ class Posterior(object):
@property @property
def mean(self): def mean(self):
"""
Posterior mean
$$
K_{xx}v
v := \texttt{Woodbury vector}
$$
"""
if self._mean is None: if self._mean is None:
self._mean = np.dot(self._K, self.woodbury_vector) self._mean = np.dot(self._K, self.woodbury_vector)
return self._mean return self._mean
@property @property
def covariance(self): def covariance(self):
"""
Posterior covariance
$$
K_{xx} - K_{xx}W_{xx}^{-1}K_{xx}
W_{xx} := \texttt{Woodbury inv}
$$
"""
if self._covariance is None: if self._covariance is None:
#LiK, _ = dtrtrs(self.woodbury_chol, self._K, lower=1) #LiK, _ = dtrtrs(self.woodbury_chol, self._K, lower=1)
self._covariance = np.tensordot(np.dot(np.atleast_3d(self.woodbury_inv).T, self._K), self._K, [1,0]).T self._covariance = self._K[:, :, None] - np.tensordot(np.dot(np.atleast_3d(self.woodbury_inv).T, self._K), self._K, [1,0]).T
#self._covariance = self._K - self._K.dot(self.woodbury_inv).dot(self._K) #old_covariance = self._K - self._K.dot(self.woodbury_inv).dot(self._K)
return self._covariance.squeeze() return self._covariance.squeeze()
@property @property
def precision(self): def precision(self):
"""
Inverse of posterior covariance
"""
if self._precision is None: if self._precision is None:
cov = np.atleast_3d(self.covariance) cov = np.atleast_3d(self.covariance)
self._precision = np.zeros(cov.shape) # if one covariance per dimension self._precision = np.zeros(cov.shape) # if one covariance per dimension
@ -96,8 +113,15 @@ class Posterior(object):
@property @property
def woodbury_chol(self): def woodbury_chol(self):
"""
return $L_{W}$ where L is the lower triangular Cholesky decomposition of the Woodbury matrix
$$
L_{W}L_{W}^{\top} = W^{-1}
W^{-1} := \texttt{Woodbury inv}
$$
"""
if self._woodbury_chol is None: if self._woodbury_chol is None:
#compute woodbury chol from #compute woodbury chol from
if self._woodbury_inv is not None: if self._woodbury_inv is not None:
winv = np.atleast_3d(self._woodbury_inv) winv = np.atleast_3d(self._woodbury_inv)
self._woodbury_chol = np.zeros(winv.shape) self._woodbury_chol = np.zeros(winv.shape)
@ -121,6 +145,13 @@ class Posterior(object):
@property @property
def woodbury_inv(self): def woodbury_inv(self):
"""
The inverse of the woodbury matrix, in the gaussian likelihood case it is defined as
$$
(K_{xx} + \Sigma_{xx})^{-1}
\Sigma_{xx} := \texttt{Likelihood.variance / Approximate likelihood covariance}
$$
"""
if self._woodbury_inv is None: if self._woodbury_inv is None:
self._woodbury_inv, _ = dpotri(self.woodbury_chol, lower=1) self._woodbury_inv, _ = dpotri(self.woodbury_chol, lower=1)
#self._woodbury_inv, _ = dpotrs(self.woodbury_chol, np.eye(self.woodbury_chol.shape[0]), lower=1) #self._woodbury_inv, _ = dpotrs(self.woodbury_chol, np.eye(self.woodbury_chol.shape[0]), lower=1)
@ -129,17 +160,22 @@ class Posterior(object):
@property @property
def woodbury_vector(self): def woodbury_vector(self):
"""
Woodbury vector in the gaussian likelihood case only is defined as
$$
(K_{xx} + \Sigma)^{-1}Y
\Sigma := \texttt{Likelihood.variance / Approximate likelihood covariance}
$$
"""
if self._woodbury_vector is None: if self._woodbury_vector is None:
self._woodbury_vector, _ = dpotrs(self.K_chol, self.mean) self._woodbury_vector, _ = dpotrs(self.K_chol, self.mean)
return self._woodbury_vector return self._woodbury_vector
@property @property
def K_chol(self): def K_chol(self):
"""
Cholesky of the prior covariance K
"""
if self._K_chol is None: if self._K_chol is None:
self._K_chol = jitchol(self._K) self._K_chol = jitchol(self._K)
return self._K_chol return self._K_chol

View file

@ -6,6 +6,60 @@ import unittest
import numpy as np import numpy as np
import GPy import GPy
class MiscTests(unittest.TestCase):
def setUp(self):
self.N = 20
self.N_new = 50
self.D = 1
self.X = np.random.uniform(-3., 3., (self.N, 1))
self.Y = np.sin(self.X) + np.random.randn(self.N, self.D) * 0.05
self.X_new = np.random.uniform(-3., 3., (self.N_new, 1))
def test_raw_predict(self):
k = GPy.kern.RBF(1)
m = GPy.models.GPRegression(self.X, self.Y, kernel=k)
m.randomize()
Kinv = np.linalg.pinv(k.K(self.X) + np.eye(self.N)*m.Gaussian_noise.variance)
K_hat = k.K(self.X_new) - k.K(self.X_new, self.X).dot(Kinv).dot(k.K(self.X, self.X_new))
mu_hat = k.K(self.X_new, self.X).dot(Kinv).dot(self.Y)
mu, covar = m._raw_predict(self.X_new, full_cov=True)
self.assertEquals(mu.shape, (self.N_new, self.D))
self.assertEquals(covar.shape, (self.N_new, self.N_new))
np.testing.assert_almost_equal(K_hat, covar)
np.testing.assert_almost_equal(mu_hat, mu)
mu, var = m._raw_predict(self.X_new)
self.assertEquals(mu.shape, (self.N_new, self.D))
self.assertEquals(var.shape, (self.N_new, 1))
np.testing.assert_almost_equal(np.diag(K_hat)[:, None], var)
np.testing.assert_almost_equal(mu_hat, mu)
def test_sparse_raw_predict(self):
k = GPy.kern.RBF(1)
m = GPy.models.SparseGPRegression(self.X, self.Y, kernel=k)
m.randomize()
Z = m.Z[:]
X = self.X[:]
#Not easy to check if woodbury_inv is correct in itself as it requires a large derivation and expression
Kinv = m.posterior.woodbury_inv
K_hat = k.K(self.X_new) - k.K(self.X_new, Z).dot(Kinv).dot(k.K(Z, self.X_new))
mu, covar = m._raw_predict(self.X_new, full_cov=True)
self.assertEquals(mu.shape, (self.N_new, self.D))
self.assertEquals(covar.shape, (self.N_new, self.N_new))
np.testing.assert_almost_equal(K_hat, covar)
#np.testing.assert_almost_equal(mu_hat, mu)
mu, var = m._raw_predict(self.X_new)
self.assertEquals(mu.shape, (self.N_new, self.D))
self.assertEquals(var.shape, (self.N_new, 1))
np.testing.assert_almost_equal(np.diag(K_hat)[:, None], var)
#np.testing.assert_almost_equal(mu_hat, mu)
class GradientTests(unittest.TestCase): class GradientTests(unittest.TestCase):
def setUp(self): def setUp(self):
###################################### ######################################