mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-05-07 19:12:40 +02:00
103 lines
3.7 KiB
Python
103 lines
3.7 KiB
Python
# Copyright (c) 2013, GPy authors (see AUTHORS.txt).
|
|
# Licensed under the BSD 3-clause license (see LICENSE.txt)
|
|
|
|
from kernpart import Kernpart
|
|
import numpy as np
|
|
from ...util.linalg import tdot
|
|
from ...core.mapping import Mapping
|
|
import GPy
|
|
|
|
class Hetero(Kernpart):
|
|
"""
|
|
TODO: Need to constrain the function outputs
|
|
positive (still thinking of best way of doing this!!! Yes, intend to use
|
|
transformations, but what's the *best* way). Currently just squaring output.
|
|
|
|
Heteroschedastic noise which depends on input location. See, for example,
|
|
this paper by Goldberg et al.
|
|
|
|
.. math::
|
|
|
|
k(x_i, x_j) = \delta_{i,j} \sigma^2(x_i)
|
|
|
|
where :math:`\sigma^2(x)` is a function giving the variance as a function of input space and :math:`\delta_{i,j}` is the Kronecker delta function.
|
|
|
|
The parameters are the parameters of \sigma^2(x) which is a
|
|
function that can be specified by the user, by default an
|
|
multi-layer peceptron is used.
|
|
|
|
:param input_dim: the number of input dimensions
|
|
:type input_dim: int
|
|
:param mapping: the mapping that gives the lengthscale across the input space (by default GPy.mappings.MLP is used with 20 hidden nodes).
|
|
:type mapping: GPy.core.Mapping
|
|
:rtype: Kernpart object
|
|
|
|
See this paper:
|
|
|
|
Goldberg, P. W. Williams, C. K. I. and Bishop,
|
|
C. M. (1998) Regression with Input-dependent Noise: a Gaussian
|
|
Process Treatment In Advances in Neural Information Processing
|
|
Systems, Volume 10, pp. 493-499. MIT Press
|
|
|
|
for a Gaussian process treatment of this problem.
|
|
|
|
"""
|
|
|
|
def __init__(self, input_dim, mapping=None, transform=None):
|
|
self.input_dim = input_dim
|
|
if not mapping:
|
|
mapping = GPy.mappings.MLP(output_dim=1, hidden_dim=20, input_dim=input_dim)
|
|
if not transform:
|
|
transform = GPy.core.transformations.logexp()
|
|
|
|
self.transform = transform
|
|
self.mapping = mapping
|
|
self.name='hetero'
|
|
self.num_params=self.mapping.num_params
|
|
self._set_params(self.mapping._get_params())
|
|
|
|
def _get_params(self):
|
|
return self.mapping._get_params()
|
|
|
|
def _set_params(self, x):
|
|
assert x.size == (self.num_params)
|
|
self.mapping._set_params(x)
|
|
|
|
def _get_param_names(self):
|
|
return self.mapping._get_param_names()
|
|
|
|
def K(self, X, X2, target):
|
|
"""Return covariance between X and X2."""
|
|
if (X2 is None) or (X2 is X):
|
|
target[np.diag_indices_from(target)] += self._Kdiag(X)
|
|
|
|
def Kdiag(self, X, target):
|
|
"""Compute the diagonal of the covariance matrix for X."""
|
|
target+=self._Kdiag(X)
|
|
|
|
def _Kdiag(self, X):
|
|
"""Helper function for computing the diagonal elements of the covariance."""
|
|
return self.mapping.f(X).flatten()**2
|
|
|
|
def _param_grad_helper(self, dL_dK, X, X2, target):
|
|
"""Derivative of the covariance with respect to the parameters."""
|
|
if (X2 is None) or (X2 is X):
|
|
dL_dKdiag = dL_dK.flat[::dL_dK.shape[0]+1]
|
|
self.dKdiag_dtheta(dL_dKdiag, X, target)
|
|
|
|
def dKdiag_dtheta(self, dL_dKdiag, X, target):
|
|
"""Gradient of diagonal of covariance with respect to parameters."""
|
|
target += 2.*self.mapping.df_dtheta(dL_dKdiag[:, None]*self.mapping.f(X), X)
|
|
|
|
def gradients_X(self, dL_dK, X, X2, target):
|
|
"""Derivative of the covariance matrix with respect to X."""
|
|
if X2==None or X2 is X:
|
|
dL_dKdiag = dL_dK.flat[::dL_dK.shape[0]+1]
|
|
self.dKdiag_dX(dL_dKdiag, X, target)
|
|
|
|
def dKdiag_dX(self, dL_dKdiag, X, target):
|
|
"""Gradient of diagonal of covariance with respect to X."""
|
|
target += 2.*self.mapping.df_dX(dL_dKdiag[:, None], X)*self.mapping.f(X)
|
|
|
|
|
|
|