mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-07-14 16:32:15 +02:00
Basic framework for serializing GPy models
This commit is contained in:
parent
d529da3e6c
commit
e572bfb746
26 changed files with 828 additions and 64 deletions
|
|
@ -13,9 +13,9 @@ class Add(CombinationKernel):
|
|||
propagates gradients through.
|
||||
|
||||
This kernel will take over the active dims of it's subkernels passed in.
|
||||
|
||||
NOTE: The subkernels will be copies of the original kernels, to prevent
|
||||
unexpected behavior.
|
||||
|
||||
NOTE: The subkernels will be copies of the original kernels, to prevent
|
||||
unexpected behavior.
|
||||
"""
|
||||
def __init__(self, subkerns, name='sum'):
|
||||
_newkerns = []
|
||||
|
|
@ -26,7 +26,7 @@ class Add(CombinationKernel):
|
|||
_newkerns.append(part.copy())
|
||||
else:
|
||||
_newkerns.append(kern.copy())
|
||||
|
||||
|
||||
super(Add, self).__init__(_newkerns, name)
|
||||
self._exact_psicomp = self._check_exact_psicomp()
|
||||
|
||||
|
|
@ -43,6 +43,11 @@ class Add(CombinationKernel):
|
|||
else:
|
||||
return False
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(Add, self)._to_dict()
|
||||
input_dict["class"] = str("GPy.kern.Add")
|
||||
return input_dict
|
||||
|
||||
@Cache_this(limit=3, force_kwargs=['which_parts'])
|
||||
def K(self, X, X2=None, which_parts=None):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -60,6 +60,35 @@ class Kern(Parameterized):
|
|||
from .psi_comp import PSICOMP_GH
|
||||
self.psicomp = PSICOMP_GH()
|
||||
|
||||
def _to_dict(self):
|
||||
input_dict = {}
|
||||
input_dict["input_dim"] = self.input_dim
|
||||
if isinstance(self.active_dims, np.ndarray):
|
||||
input_dict["active_dims"] = self.active_dims.tolist()
|
||||
else:
|
||||
input_dict["active_dims"] = self.active_dims
|
||||
input_dict["name"] = self.name
|
||||
input_dict["useGPU"] = self.useGPU
|
||||
return input_dict
|
||||
|
||||
def to_dict(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def from_dict(input_dict):
|
||||
import copy
|
||||
input_dict = copy.deepcopy(input_dict)
|
||||
kernel_class = input_dict.pop('class')
|
||||
input_dict["name"] = str(input_dict["name"])
|
||||
import GPy
|
||||
kernel_class = eval(kernel_class)
|
||||
return kernel_class._from_dict(kernel_class, input_dict)
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
return kernel_class(**input_dict)
|
||||
|
||||
|
||||
def __setstate__(self, state):
|
||||
self._all_dims_active = np.arange(0, max(state['active_dims']) + 1)
|
||||
super(Kern, self).__setstate__(state)
|
||||
|
|
@ -342,6 +371,21 @@ class CombinationKernel(Kern):
|
|||
self.extra_dims = extra_dims
|
||||
self.link_parameters(*kernels)
|
||||
|
||||
def _to_dict(self):
|
||||
input_dict = super(CombinationKernel, self)._to_dict()
|
||||
input_dict["parts"] = {}
|
||||
for ii in range(len(self.parts)):
|
||||
input_dict["parts"][ii] = self.parts[ii].to_dict()
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
parts = input_dict.pop('parts', None)
|
||||
subkerns = []
|
||||
for pp in parts:
|
||||
subkerns.append(Kern.from_dict(parts[pp]))
|
||||
return kernel_class(subkerns)
|
||||
|
||||
@property
|
||||
def parts(self):
|
||||
return self.parameters
|
||||
|
|
|
|||
|
|
@ -51,6 +51,18 @@ class Linear(Kern):
|
|||
self.link_parameter(self.variances)
|
||||
self.psicomp = PSICOMP_Linear()
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(Linear, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.Linear"
|
||||
input_dict["variances"] = self.variances.values.tolist()
|
||||
input_dict["ARD"] = self.ARD
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
useGPU = input_dict.pop('useGPU', None)
|
||||
return Linear(**input_dict)
|
||||
|
||||
@Cache_this(limit=3)
|
||||
def K(self, X, X2=None):
|
||||
if self.ARD:
|
||||
|
|
@ -211,5 +223,3 @@ class LinearFull(Kern):
|
|||
def gradients_X_diag(self, dL_dKdiag, X):
|
||||
P = np.dot(self.W, self.W.T) + np.diag(self.kappa)
|
||||
return 2.*np.einsum('jk,i,ij->ik', P, dL_dKdiag, X)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -400,4 +400,3 @@ class PeriodicMatern52(Periodic):
|
|||
self.variance.gradient = np.sum(dK_dvar*dL_dK)
|
||||
self.lengthscale.gradient = np.sum(dK_dlen*dL_dK)
|
||||
self.period.gradient = np.sum(dK_dper*dL_dK)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ class Prod(CombinationKernel):
|
|||
kernels.insert(i, part)
|
||||
super(Prod, self).__init__(kernels, name)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(Prod, self)._to_dict()
|
||||
input_dict["class"] = str("GPy.kern.Prod")
|
||||
return input_dict
|
||||
|
||||
@Cache_this(limit=3, force_kwargs=['which_parts'])
|
||||
def K(self, X, X2=None, which_parts=None):
|
||||
if which_parts is None:
|
||||
|
|
@ -117,7 +122,7 @@ class Prod(CombinationKernel):
|
|||
part_param_num = len(p.param_array) # number of parameters in the part
|
||||
p.sde_update_gradient_full(gradients[part_start_param_index:(part_start_param_index+part_param_num)])
|
||||
part_start_param_index += part_param_num
|
||||
|
||||
|
||||
def sde(self):
|
||||
"""
|
||||
"""
|
||||
|
|
@ -131,88 +136,88 @@ class Prod(CombinationKernel):
|
|||
dQc = None
|
||||
dPinf = None
|
||||
dP0 = None
|
||||
|
||||
|
||||
# Assign models
|
||||
for p in self.parts:
|
||||
(Ft,Lt,Qct,Ht,P_inft, P0t, dFt,dQct,dP_inft,dP0t) = p.sde()
|
||||
|
||||
|
||||
# check derivative dimensions ->
|
||||
number_of_parameters = len(p.param_array)
|
||||
number_of_parameters = len(p.param_array)
|
||||
assert dFt.shape[2] == number_of_parameters, "Dynamic matrix derivative shape is wrong"
|
||||
assert dQct.shape[2] == number_of_parameters, "Diffusion matrix derivative shape is wrong"
|
||||
assert dP_inft.shape[2] == number_of_parameters, "Infinite covariance matrix derivative shape is wrong"
|
||||
# check derivative dimensions <-
|
||||
|
||||
|
||||
# exception for periodic kernel
|
||||
if (p.name == 'std_periodic'):
|
||||
Qct = P_inft
|
||||
dQct = dP_inft
|
||||
|
||||
Qct = P_inft
|
||||
dQct = dP_inft
|
||||
|
||||
dF = dkron(F,dF,Ft,dFt,'sum')
|
||||
dQc = dkron(Qc,dQc,Qct,dQct,'prod')
|
||||
dPinf = dkron(Pinf,dPinf,P_inft,dP_inft,'prod')
|
||||
dP0 = dkron(P0,dP0,P0t,dP0t,'prod')
|
||||
|
||||
|
||||
F = np.kron(F,np.eye(Ft.shape[0])) + np.kron(np.eye(F.shape[0]),Ft)
|
||||
L = np.kron(L,Lt)
|
||||
Qc = np.kron(Qc,Qct)
|
||||
Pinf = np.kron(Pinf,P_inft)
|
||||
P0 = np.kron(P0,P_inft)
|
||||
H = np.kron(H,Ht)
|
||||
|
||||
|
||||
return (F,L,Qc,H,Pinf,P0,dF,dQc,dPinf,dP0)
|
||||
|
||||
def dkron(A,dA,B,dB, operation='prod'):
|
||||
"""
|
||||
Function computes the derivative of Kronecker product A*B
|
||||
Function computes the derivative of Kronecker product A*B
|
||||
(or Kronecker sum A+B).
|
||||
|
||||
|
||||
Input:
|
||||
-----------------------
|
||||
|
||||
|
||||
A: 2D matrix
|
||||
Some matrix
|
||||
Some matrix
|
||||
dA: 3D (or 2D matrix)
|
||||
Derivarives of A
|
||||
B: 2D matrix
|
||||
Some matrix
|
||||
Some matrix
|
||||
dB: 3D (or 2D matrix)
|
||||
Derivarives of B
|
||||
|
||||
Derivarives of B
|
||||
|
||||
operation: str 'prod' or 'sum'
|
||||
Which operation is considered. If the operation is 'sum' it is assumed
|
||||
that A and are square matrices.s
|
||||
|
||||
|
||||
Output:
|
||||
dC: 3D matrix
|
||||
Derivative of Kronecker product A*B (or Kronecker sum A+B)
|
||||
"""
|
||||
|
||||
|
||||
if dA is None:
|
||||
dA_param_num = 0
|
||||
dA = np.zeros((A.shape[0], A.shape[1],1))
|
||||
else:
|
||||
dA_param_num = dA.shape[2]
|
||||
|
||||
|
||||
if dB is None:
|
||||
dB_param_num = 0
|
||||
dB = np.zeros((B.shape[0], B.shape[1],1))
|
||||
dB = np.zeros((B.shape[0], B.shape[1],1))
|
||||
else:
|
||||
dB_param_num = dB.shape[2]
|
||||
|
||||
# Space allocation for derivative matrix
|
||||
dC = np.zeros((A.shape[0]*B.shape[0], A.shape[1]*B.shape[1], dA_param_num + dB_param_num))
|
||||
|
||||
dC = np.zeros((A.shape[0]*B.shape[0], A.shape[1]*B.shape[1], dA_param_num + dB_param_num))
|
||||
|
||||
for k in range(dA_param_num):
|
||||
if operation == 'prod':
|
||||
dC[:,:,k] = np.kron(dA[:,:,k],B);
|
||||
else:
|
||||
dC[:,:,k] = np.kron(dA[:,:,k],np.eye( B.shape[0] ))
|
||||
|
||||
|
||||
for k in range(dB_param_num):
|
||||
if operation == 'prod':
|
||||
dC[:,:,dA_param_num+k] = np.kron(A,dB[:,:,k])
|
||||
else:
|
||||
dC[:,:,dA_param_num+k] = np.kron(np.eye( A.shape[0] ),dB[:,:,k])
|
||||
|
||||
|
||||
return dC
|
||||
|
|
|
|||
|
|
@ -31,6 +31,14 @@ class RBF(Stationary):
|
|||
self.inv_l = Param('inv_lengthscale',1./self.lengthscale**2, Logexp())
|
||||
self.link_parameter(self.inv_l)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(RBF, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.RBF"
|
||||
input_dict["inv_l"] = self.use_invLengthscale
|
||||
if input_dict["inv_l"] == True:
|
||||
input_dict["lengthscale"] = np.sqrt(1 / float(self.inv_l))
|
||||
return input_dict
|
||||
|
||||
def K_of_r(self, r):
|
||||
return self.variance * np.exp(-0.5 * r**2)
|
||||
|
||||
|
|
@ -42,7 +50,7 @@ class RBF(Stationary):
|
|||
|
||||
def dK2_drdr_diag(self):
|
||||
return -self.variance # as the diagonal of r is always filled with zeros
|
||||
|
||||
|
||||
def __getstate__(self):
|
||||
dc = super(RBF, self).__getstate__()
|
||||
if self.useGPU:
|
||||
|
|
|
|||
|
|
@ -93,6 +93,17 @@ class StdPeriodic(Kern):
|
|||
|
||||
self.link_parameters(self.variance, self.period, self.lengthscale)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(StdPeriodic, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.StdPeriodic"
|
||||
input_dict["variance"] = self.variance.values.tolist()
|
||||
input_dict["period"] = self.period.values.tolist()
|
||||
input_dict["lengthscale"] = self.lengthscale.values.tolist()
|
||||
input_dict["ARD1"] = self.ARD1
|
||||
input_dict["ARD2"] = self.ARD2
|
||||
return input_dict
|
||||
|
||||
|
||||
def parameters_changed(self):
|
||||
"""
|
||||
This functions deals as a callback for each optimization iteration.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ class Static(Kern):
|
|||
self.variance = Param('variance', variance, Logexp())
|
||||
self.link_parameters(self.variance)
|
||||
|
||||
def _to_dict(self):
|
||||
input_dict = super(Static, self)._to_dict()
|
||||
input_dict["variance"] = self.variance.values.tolist()
|
||||
return input_dict
|
||||
|
||||
def Kdiag(self, X):
|
||||
ret = np.empty((X.shape[0],), dtype=np.float64)
|
||||
ret[:] = self.variance
|
||||
|
|
@ -133,6 +138,16 @@ class Bias(Static):
|
|||
def __init__(self, input_dim, variance=1., active_dims=None, name='bias'):
|
||||
super(Bias, self).__init__(input_dim, variance, active_dims, name)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(Bias, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.Bias"
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
useGPU = input_dict.pop('useGPU', None)
|
||||
return Bias(**input_dict)
|
||||
|
||||
def K(self, X, X2=None):
|
||||
shape = (X.shape[0], X.shape[0] if X2 is None else X2.shape[0])
|
||||
return np.full(shape, self.variance, dtype=np.float64)
|
||||
|
|
@ -250,4 +265,3 @@ class Precomputed(Fixed):
|
|||
|
||||
def update_gradients_diag(self, dL_dKdiag, X):
|
||||
self.variance.gradient = np.einsum('i,ii', dL_dKdiag, self._index(X, None))
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,13 @@ class Stationary(Kern):
|
|||
assert self.variance.size==1
|
||||
self.link_parameters(self.variance, self.lengthscale)
|
||||
|
||||
def _to_dict(self):
|
||||
input_dict = super(Stationary, self)._to_dict()
|
||||
input_dict["variance"] = self.variance.values.tolist()
|
||||
input_dict["lengthscale"] = self.lengthscale.values.tolist()
|
||||
input_dict["ARD"] = self.ARD
|
||||
return input_dict
|
||||
|
||||
def K_of_r(self, r):
|
||||
raise NotImplementedError("implement the covariance function as a fn of r to use this class")
|
||||
|
||||
|
|
@ -351,6 +358,16 @@ class Exponential(Stationary):
|
|||
def dK_dr(self, r):
|
||||
return -self.K_of_r(r)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(Exponential, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.Exponential"
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
useGPU = input_dict.pop('useGPU', None)
|
||||
return Exponential(**input_dict)
|
||||
|
||||
# def sde(self):
|
||||
# """
|
||||
# Return the state space representation of the covariance.
|
||||
|
|
@ -399,6 +416,16 @@ class Matern32(Stationary):
|
|||
def __init__(self, input_dim, variance=1., lengthscale=None, ARD=False, active_dims=None, name='Mat32'):
|
||||
super(Matern32, self).__init__(input_dim, variance, lengthscale, ARD, active_dims, name)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(Matern32, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.Matern32"
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
useGPU = input_dict.pop('useGPU', None)
|
||||
return Matern32(**input_dict)
|
||||
|
||||
def K_of_r(self, r):
|
||||
return self.variance * (1. + np.sqrt(3.) * r) * np.exp(-np.sqrt(3.) * r)
|
||||
|
||||
|
|
@ -478,6 +505,16 @@ class Matern52(Stationary):
|
|||
def __init__(self, input_dim, variance=1., lengthscale=None, ARD=False, active_dims=None, name='Mat52'):
|
||||
super(Matern52, self).__init__(input_dim, variance, lengthscale, ARD, active_dims, name)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(Matern52, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.Matern52"
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
useGPU = input_dict.pop('useGPU', None)
|
||||
return Matern52(**input_dict)
|
||||
|
||||
def K_of_r(self, r):
|
||||
return self.variance*(1+np.sqrt(5.)*r+5./3*r**2)*np.exp(-np.sqrt(5.)*r)
|
||||
|
||||
|
|
@ -533,6 +570,16 @@ class ExpQuad(Stationary):
|
|||
def __init__(self, input_dim, variance=1., lengthscale=None, ARD=False, active_dims=None, name='ExpQuad'):
|
||||
super(ExpQuad, self).__init__(input_dim, variance, lengthscale, ARD, active_dims, name)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(ExpQuad, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.ExpQuad"
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
useGPU = input_dict.pop('useGPU', None)
|
||||
return ExpQuad(**input_dict)
|
||||
|
||||
def K_of_r(self, r):
|
||||
return self.variance * np.exp(-0.5 * r**2)
|
||||
|
||||
|
|
@ -566,6 +613,17 @@ class RatQuad(Stationary):
|
|||
self.power = Param('power', power, Logexp())
|
||||
self.link_parameters(self.power)
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(RatQuad, self)._to_dict()
|
||||
input_dict["class"] = "GPy.kern.RatQuad"
|
||||
input_dict["power"] = self.power.values.tolist()
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(kernel_class, input_dict):
|
||||
useGPU = input_dict.pop('useGPU', None)
|
||||
return RatQuad(**input_dict)
|
||||
|
||||
def K_of_r(self, r):
|
||||
r2 = np.square(r)
|
||||
# return self.variance*np.power(1. + r2/2., -self.power)
|
||||
|
|
@ -588,5 +646,3 @@ class RatQuad(Stationary):
|
|||
def update_gradients_diag(self, dL_dKdiag, X):
|
||||
super(RatQuad, self).update_gradients_diag(dL_dKdiag, X)
|
||||
self.power.gradient = 0.
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue