diff --git a/GPy/core/gp_base.py b/GPy/core/gp_base.py index 083f9980..7cf62e69 100644 --- a/GPy/core/gp_base.py +++ b/GPy/core/gp_base.py @@ -418,3 +418,18 @@ class GPBase(Model): index = np.ones((X.shape[0],1))*output return np.hstack((X,index)) + + def log_predictive_density(self, x_test, y_test): + """ + Calculation of the log predictive density + + .. math: + p(y_{*}|D) = p(y_{*}|f_{*})p(f_{*}|\mu_{*}\\sigma^{2}_{*}) + + :param x_test: test observations (x_{*}) + :type x_test: (Nx1) array + :param y_test: test observations (y_{*}) + :type y_test: (Nx1) array + """ + mu_star, var_star = self._raw_predict(x_test) + return self.likelihood.log_predictive_density(y_test, mu_star, var_star) diff --git a/GPy/likelihoods/ep.py b/GPy/likelihoods/ep.py index cfa00500..32575813 100644 --- a/GPy/likelihoods/ep.py +++ b/GPy/likelihoods/ep.py @@ -54,6 +54,22 @@ class EP(likelihood): raise NotImplementedError, "Cannot make correlated predictions with an EP likelihood" return self.noise_model.predictive_values(mu,var) + def log_predictive_density(self, y_test, mu_star, var_star): + """ + Calculation of the log predictive density + + .. math: + p(y_{*}|D) = p(y_{*}|f_{*})p(f_{*}|\mu_{*}\\sigma^{2}_{*}) + + :param y_test: test observations (y_{*}) + :type y_test: (Nx1) array + :param mu_star: predictive mean of gaussian p(f_{*}|mu_{*}, var_{*}) + :type mu_star: (Nx1) array + :param var_star: predictive variance of gaussian p(f_{*}|mu_{*}, var_{*}) + :type var_star: (Nx1) array + """ + return self.noise_model.log_predictive_density(y_test, mu_star, var_star) + def _get_params(self): #return np.zeros(0) return self.noise_model._get_params() diff --git a/GPy/likelihoods/gaussian.py b/GPy/likelihoods/gaussian.py index 8b9ac776..85c028b4 100644 --- a/GPy/likelihoods/gaussian.py +++ b/GPy/likelihoods/gaussian.py @@ -90,5 +90,25 @@ class Gaussian(likelihood): _95pc = mean + 2.*np.sqrt(true_var) return mean, true_var, _5pc, _95pc + def log_predictive_density(self, y_test, mu_star, var_star): + """ + Calculation of the log predictive density + + .. math: + p(y_{*}|D) = p(y_{*}|f_{*})p(f_{*}|\mu_{*}\\sigma^{2}_{*}) + + :param y_test: test observations (y_{*}) + :type y_test: (Nx1) array + :param mu_star: predictive mean of gaussian p(f_{*}|mu_{*}, var_{*}) + :type mu_star: (Nx1) array + :param var_star: predictive variance of gaussian p(f_{*}|mu_{*}, var_{*}) + :type var_star: (Nx1) array + + .. Note: + Works as if each test point was provided individually, i.e. not full_cov + """ + y_rescaled = (y_test - self._offset)/self._scale + return -0.5*np.log(2*np.pi) -0.5*np.log(var_star + self._variance) -0.5*(np.square(y_rescaled - mu_star))/(var_star + self._variance) + def _gradients(self, partial): return np.sum(partial) diff --git a/GPy/likelihoods/laplace.py b/GPy/likelihoods/laplace.py index 05b4ff02..047d7f74 100644 --- a/GPy/likelihoods/laplace.py +++ b/GPy/likelihoods/laplace.py @@ -73,6 +73,22 @@ class Laplace(likelihood): with an Laplace likelihood") return self.noise_model.predictive_values(mu, var) + def log_predictive_density(self, y_test, mu_star, var_star): + """ + Calculation of the log predictive density + + .. math: + p(y_{*}|D) = p(y_{*}|f_{*})p(f_{*}|\mu_{*}\\sigma^{2}_{*}) + + :param y_test: test observations (y_{*}) + :type y_test: (Nx1) array + :param mu_star: predictive mean of gaussian p(f_{*}|mu_{*}, var_{*}) + :type mu_star: (Nx1) array + :param var_star: predictive variance of gaussian p(f_{*}|mu_{*}, var_{*}) + :type var_star: (Nx1) array + """ + return self.noise_model.log_predictive_density(y_test, mu_star, var_star) + def _get_params(self): return np.asarray(self.noise_model._get_params()) diff --git a/GPy/likelihoods/likelihood.py b/GPy/likelihoods/likelihood.py index a86eaac6..5e7c8c68 100644 --- a/GPy/likelihoods/likelihood.py +++ b/GPy/likelihoods/likelihood.py @@ -51,3 +51,19 @@ class likelihood(Parameterized): def predictive_values(self, mu, var): raise NotImplementedError + + def log_predictive_density(self, y_test, mu_star, var_star): + """ + Calculation of the predictive density + + .. math: + p(y_{*}|D) = p(y_{*}|f_{*})p(f_{*}|\mu_{*}\\sigma^{2}_{*}) + + :param y_test: test observations (y_{*}) + :type y_test: (Nx1) array + :param mu_star: predictive mean of gaussian p(f_{*}|mu_{*}, var_{*}) + :type mu_star: (Nx1) array + :param var_star: predictive variance of gaussian p(f_{*}|mu_{*}, var_{*}) + :type var_star: (Nx1) array + """ + raise NotImplementedError diff --git a/GPy/likelihoods/noise_models/noise_distributions.py b/GPy/likelihoods/noise_models/noise_distributions.py index 59465a5b..3cd46013 100644 --- a/GPy/likelihoods/noise_models/noise_distributions.py +++ b/GPy/likelihoods/noise_models/noise_distributions.py @@ -62,6 +62,34 @@ class NoiseDistribution(object): """ raise NotImplementedError + def log_predictive_density(self, y_test, mu_star, var_star): + """ + Calculation of the log predictive density + + .. math: + p(y_{*}|D) = p(y_{*}|f_{*})p(f_{*}|\mu_{*}\\sigma^{2}_{*}) + + :param y_test: test observations (y_{*}) + :type y_test: (Nx1) array + :param mu_star: predictive mean of gaussian p(f_{*}|mu_{*}, var_{*}) + :type mu_star: (Nx1) array + :param var_star: predictive variance of gaussian p(f_{*}|mu_{*}, var_{*}) + :type var_star: (Nx1) array + """ + assert y_test.shape==mu_star.shape + assert y_test.shape==var_star.shape + assert y_test.shape[1] == 1 + def integral_generator(y, m, v): + """Generate a function which can be integrated to give p(Y*|Y) = int p(Y*|f*)p(f*|Y) df*""" + def f(f_star): + return self.pdf(f_star, y)*np.exp(-(1./(2*v))*np.square(m-f_star)) + return f + + scaled_p_ystar, accuracy = zip(*[quad(integral_generator(y, m, v), -np.inf, np.inf) for y, m, v in zip(y_test.flatten(), mu_star.flatten(), var_star.flatten())]) + scaled_p_ystar = np.array(scaled_p_ystar).reshape(-1,1) + p_ystar = scaled_p_ystar/np.sqrt(2*np.pi*var_star) + return np.log(p_ystar) + def _moments_match_numerical(self,obs,tau,v): """ Calculation of moments using quadrature