mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-05-09 20:12:38 +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
|
|
@ -41,6 +41,26 @@ class LatentFunctionInference(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def _to_dict(self):
|
||||
input_dict = {}
|
||||
return input_dict
|
||||
|
||||
def to_dict(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def from_dict(input_dict):
|
||||
import copy
|
||||
input_dict = copy.deepcopy(input_dict)
|
||||
inference_class = input_dict.pop('class')
|
||||
import GPy
|
||||
inference_class = eval(inference_class)
|
||||
return inference_class._from_dict(inference_class, input_dict)
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(inference_class, input_dict):
|
||||
return inference_class(**input_dict)
|
||||
|
||||
class InferenceMethodList(LatentFunctionInference, list):
|
||||
|
||||
def on_optimization_start(self):
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ class ExactGaussianInference(LatentFunctionInference):
|
|||
def __init__(self):
|
||||
pass#self._YYTfactor_cache = caching.cache()
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(ExactGaussianInference, self)._to_dict()
|
||||
input_dict["class"] = "GPy.inference.latent_function_inference.exact_gaussian_inference.ExactGaussianInference"
|
||||
return input_dict
|
||||
|
||||
def inference(self, kern, X, likelihood, Y, mean_function=None, Y_metadata=None, K=None, variance=None, Z_tilde=None):
|
||||
"""
|
||||
Returns a Posterior class containing essential quantities of the posterior
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from . import ExactGaussianInference, VarDTC
|
|||
from ...util import diag
|
||||
from .posterior import PosteriorEP as Posterior
|
||||
from ...likelihoods import Gaussian
|
||||
from . import LatentFunctionInference
|
||||
|
||||
log_2_pi = np.log(2*np.pi)
|
||||
|
||||
|
|
@ -26,6 +27,14 @@ class cavityParams(object):
|
|||
def _update_i(self, eta, ga_approx, post_params, i):
|
||||
self.tau[i] = 1./post_params.Sigma_diag[i] - eta*ga_approx.tau[i]
|
||||
self.v[i] = post_params.mu[i]/post_params.Sigma_diag[i] - eta*ga_approx.v[i]
|
||||
def to_dict(self):
|
||||
return {"tau": self.tau.tolist(), "v": self.v.tolist()}
|
||||
@staticmethod
|
||||
def from_dict(input_dict):
|
||||
c = cavityParams(len(input_dict["tau"]))
|
||||
c.tau = np.array(input_dict["tau"])
|
||||
c.v = np.array(input_dict["v"])
|
||||
return c
|
||||
|
||||
|
||||
class gaussianApproximation(object):
|
||||
|
|
@ -48,6 +57,11 @@ class gaussianApproximation(object):
|
|||
self.v[i] += delta_v
|
||||
|
||||
return (delta_tau, delta_v)
|
||||
def to_dict(self):
|
||||
return {"tau": self.tau.tolist(), "v": self.v.tolist()}
|
||||
@staticmethod
|
||||
def from_dict(input_dict):
|
||||
return gaussianApproximation(np.array(input_dict["v"]), np.array(input_dict["tau"]))
|
||||
|
||||
|
||||
class posteriorParamsBase(object):
|
||||
|
|
@ -71,6 +85,20 @@ class posteriorParams(posteriorParamsBase):
|
|||
ci = delta_tau/(1.+ delta_tau*self.Sigma_diag[i])
|
||||
DSYR(self.Sigma, self.Sigma[:,i].copy(), -ci)
|
||||
self.mu = np.dot(self.Sigma, ga_approx.v)
|
||||
def to_dict(self):
|
||||
#TODO: Implement a more memory efficient variant
|
||||
if self.L is None:
|
||||
return { "mu": self.mu.tolist(), "Sigma": self.Sigma.tolist()}
|
||||
else:
|
||||
return { "mu": self.mu.tolist(), "Sigma": self.Sigma.tolist(), "L": self.L.tolist()}
|
||||
@staticmethod
|
||||
def from_dict(input_dict):
|
||||
if "L" in input_dict:
|
||||
return posteriorParams(np.array(input_dict["mu"]), np.array(input_dict["Sigma"]), np.array(input_dict["L"]))
|
||||
else:
|
||||
return posteriorParams(np.array(input_dict["mu"]), np.array(input_dict["Sigma"]))
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _recompute(K, ga_approx):
|
||||
|
|
@ -112,7 +140,7 @@ class posteriorParamsDTC(posteriorParamsBase):
|
|||
return posteriorParamsDTC(mu, Sigma_diag), LLT
|
||||
|
||||
class EPBase(object):
|
||||
def __init__(self, epsilon=1e-6, eta=1., delta=1., always_reset=False, max_iters=np.inf, ep_mode="alternated", parallel_updates=False):
|
||||
def __init__(self, epsilon=1e-6, eta=1., delta=1., always_reset=False, max_iters=np.inf, ep_mode="alternated", parallel_updates=False, loading=False):
|
||||
"""
|
||||
The expectation-propagation algorithm.
|
||||
For nomenclature see Rasmussen & Williams 2006.
|
||||
|
|
@ -128,6 +156,7 @@ class EPBase(object):
|
|||
:max_iters: int
|
||||
:ep_mode: string. It can be "nested" (EP is run every time the Hyperparameters change) or "alternated" (It runs EP at the beginning and then optimize the Hyperparameters).
|
||||
:parallel_updates: boolean. If true, updates of the parameters of the sites in parallel
|
||||
:loading: boolean. If True, prevents the EP parameters to change. Hack used when loading a serialized model
|
||||
"""
|
||||
super(EPBase, self).__init__()
|
||||
|
||||
|
|
@ -135,6 +164,8 @@ class EPBase(object):
|
|||
self.epsilon, self.eta, self.delta, self.max_iters = epsilon, eta, delta, max_iters
|
||||
self.ep_mode = ep_mode
|
||||
self.parallel_updates = parallel_updates
|
||||
#FIXME: Hack for serialiation. If True, prevents the EP parameters to change when loading a serialized model
|
||||
self.loading = loading
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
|
|
@ -161,9 +192,21 @@ class EPBase(object):
|
|||
def __getstate__(self):
|
||||
return [super(EPBase, self).__getstate__() , [self.epsilon, self.eta, self.delta]]
|
||||
|
||||
def _to_dict(self):
|
||||
input_dict = super(EPBase, self)._to_dict()
|
||||
input_dict["epsilon"]=self.epsilon
|
||||
input_dict["eta"]=self.eta
|
||||
input_dict["delta"]=self.delta
|
||||
input_dict["always_reset"]=self.always_reset
|
||||
input_dict["max_iters"]=self.max_iters
|
||||
input_dict["ep_mode"]=self.ep_mode
|
||||
input_dict["parallel_updates"]=self.parallel_updates
|
||||
input_dict["loading"]=True
|
||||
return input_dict
|
||||
|
||||
class EP(EPBase, ExactGaussianInference):
|
||||
def inference(self, kern, X, likelihood, Y, mean_function=None, Y_metadata=None, precision=None, K=None):
|
||||
if self.always_reset:
|
||||
if self.always_reset and not self.loading:
|
||||
self.reset()
|
||||
|
||||
num_data, output_dim = Y.shape
|
||||
|
|
@ -172,11 +215,11 @@ class EP(EPBase, ExactGaussianInference):
|
|||
if K is None:
|
||||
K = kern.K(X)
|
||||
|
||||
if self.ep_mode=="nested":
|
||||
if self.ep_mode=="nested" and not self.loading:
|
||||
#Force EP at each step of the optimization
|
||||
self._ep_approximation = None
|
||||
post_params, ga_approx, cav_params, log_Z_tilde = self._ep_approximation = self.expectation_propagation(K, Y, likelihood, Y_metadata)
|
||||
elif self.ep_mode=="alternated":
|
||||
elif self.ep_mode=="alternated" or self.loading:
|
||||
if getattr(self, '_ep_approximation', None) is None:
|
||||
#if we don't yet have the results of runnign EP, run EP and store the computed factors in self._ep_approximation
|
||||
post_params, ga_approx, cav_params, log_Z_tilde = self._ep_approximation = self.expectation_propagation(K, Y, likelihood, Y_metadata)
|
||||
|
|
@ -186,6 +229,7 @@ class EP(EPBase, ExactGaussianInference):
|
|||
else:
|
||||
raise ValueError("ep_mode value not valid")
|
||||
|
||||
self.loading = False
|
||||
return self._inference(Y, K, ga_approx, cav_params, likelihood, Y_metadata=Y_metadata, Z_tilde=log_Z_tilde)
|
||||
|
||||
def expectation_propagation(self, K, Y, likelihood, Y_metadata):
|
||||
|
|
@ -297,6 +341,36 @@ class EP(EPBase, ExactGaussianInference):
|
|||
dL_dthetaL = likelihood.ep_gradients(Y, cav_params.tau, cav_params.v, np.diag(dL_dK), Y_metadata=Y_metadata, quad_mode='gh')
|
||||
return Posterior(woodbury_inv=Wi, woodbury_vector=alpha, K=K), log_marginal, {'dL_dK':dL_dK, 'dL_dthetaL':dL_dthetaL, 'dL_dm':alpha}
|
||||
|
||||
def to_dict(self):
|
||||
input_dict = super(EP, self)._to_dict()
|
||||
input_dict["class"] = "GPy.inference.latent_function_inference.expectation_propagation.EP"
|
||||
if self.ga_approx_old is not None:
|
||||
input_dict["ga_approx_old"] = self.ga_approx_old.to_dict()
|
||||
if self._ep_approximation is not None:
|
||||
input_dict["_ep_approximation"] = {}
|
||||
input_dict["_ep_approximation"]["post_params"] = self._ep_approximation[0].to_dict()
|
||||
input_dict["_ep_approximation"]["ga_approx"] = self._ep_approximation[1].to_dict()
|
||||
input_dict["_ep_approximation"]["cav_params"] = self._ep_approximation[2].to_dict()
|
||||
input_dict["_ep_approximation"]["log_Z_tilde"] = self._ep_approximation[3].tolist()
|
||||
|
||||
return input_dict
|
||||
|
||||
@staticmethod
|
||||
def _from_dict(inference_class, input_dict):
|
||||
ga_approx_old = input_dict.pop('ga_approx_old', None)
|
||||
if ga_approx_old is not None:
|
||||
ga_approx_old = gaussianApproximation.from_dict(ga_approx_old)
|
||||
_ep_approximation_dict = input_dict.pop('_ep_approximation', None)
|
||||
_ep_approximation = []
|
||||
if _ep_approximation is not None:
|
||||
_ep_approximation.append(posteriorParams.from_dict(_ep_approximation_dict["post_params"]))
|
||||
_ep_approximation.append(gaussianApproximation.from_dict(_ep_approximation_dict["ga_approx"]))
|
||||
_ep_approximation.append(cavityParams.from_dict(_ep_approximation_dict["cav_params"]))
|
||||
_ep_approximation.append(np.array(_ep_approximation_dict["log_Z_tilde"]))
|
||||
ee = EP(**input_dict)
|
||||
ee.ga_approx_old = ga_approx_old
|
||||
ee._ep_approximation = _ep_approximation
|
||||
return ee
|
||||
|
||||
class EPDTC(EPBase, VarDTC):
|
||||
def inference(self, kern, X, Z, likelihood, Y, mean_function=None, Y_metadata=None, Lm=None, dL_dKmm=None, psi0=None, psi1=None, psi2=None):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue