ottertune/server/analysis/gpr/gpr_models.py

171 lines
5.0 KiB
Python

#
# OtterTune - analysis/gpr_models.py
#
# Copyright (c) 2017-18, Carnegie Mellon University Database Group
#
# Author: Dana Van Aken
import copy
import json
import os
import gpflow
import numpy as np
import tensorflow as tf
from .gprc import GPRC
class BaseModel(object):
# Min/max bounds for the kernel lengthscales
_LENGTHSCALE_BOUNDS = (0.1, 10.)
# Keys for each kernel's hyperparameters
_KERNEL_HP_KEYS = []
# The key for the likelihood parameter
_LIKELIHOOD_HP_KEY = 'GPRC/likelihood/variance'
def __init__(self, X, y, hyperparameters=None, optimize_hyperparameters=False,
learning_rate=0.001, maxiter=5000, **kwargs):
# Store model kwargs
self._model_kwargs = {
'hyperparameters': hyperparameters,
'optimize_hyperparameters': optimize_hyperparameters,
'learning_rate': learning_rate,
'maxiter': maxiter,
}
# Store kernel kwargs
kernel_kwargs = self._get_kernel_kwargs(X_dim=X.shape[1], **kwargs)
if hyperparameters is not None:
self._assign_kernel_hyperparams(hyperparameters, kernel_kwargs)
self._kernel_kwargs = copy.deepcopy(kernel_kwargs)
# Build the kernels and the model
with gpflow.defer_build():
k = self._build_kernel(kernel_kwargs, optimize_hyperparameters=optimize_hyperparameters, **kwargs)
m = GPRC(X, y, kern=k)
if hyperparameters is not None and self._LIKELIHOOD_HP_KEY in hyperparameters:
m.likelihood.variance = hyperparameters[self._LIKELIHOOD_HP_KEY]
m.compile()
# If enabled, optimize the hyperparameters
if optimize_hyperparameters:
opt = gpflow.train.AdamOptimizer(learning_rate)
opt.minimize(m, maxiter=maxiter)
self.model = m
def _get_kernel_kwargs(self, **kwargs):
return []
def _build_kernel(self, kernel_kwargs, **kwargs):
return None
def get_hyperparameters(self):
return {k: float(v) if v.ndim == 0 else v.tolist()
for k, v in self.model.read_values().items()}
def get_model_parameters(self):
return {
'model_params': copy.deepcopy(self._model_kwargs),
'kernel_params': copy.deepcopy(self._kernel_kwargs)
}
def _assign_kernel_hyperparams(self, hyperparams, kernel_kwargs):
for i, kernel_keys in enumerate(self._KERNEL_HP_KEYS):
for key in kernel_keys:
if key in hyperparams:
argname = key.rsplit('/', 1)[-1]
kernel_kwargs[i][argname] = hyperparams[key]
@staticmethod
def load_hyperparameters(path, hp_idx=0):
with open(path, 'r') as f:
hyperparams = json.load(f)['hyperparameters']
if isinstance(hyperparams, list):
assert hp_idx >= 0, 'hp_idx: {} (expected >= 0)'.format(hp_idx)
if hp_idx >= len(hyperparams):
hp_idx = -1
hyperparams = hyperparams[hp_idx]
return hyperparams
class BasicGP(BaseModel):
_KERNEL_HP_KEYS = [
[
'GPRC/kern/kernels/0/variance',
'GPRC/kern/kernels/0/lengthscales',
]
]
def _get_kernel_kwargs(self, **kwargs):
X_dim = kwargs.pop('X_dim')
return [
{
'input_dim': X_dim,
'ARD': False
}
]
def _build_kernel(self, kernel_kwargs, **kwargs):
k = gpflow.kernels.Matern12(lengthscales=2, **kernel_kwargs[0])
if kwargs.pop('optimize_hyperparameters'):
k.lengthscales.transform = gpflow.transforms.Logistic(
*self._LENGTHSCALE_BOUNDS)
return k
class ExpWhiteGP(BaseModel):
_KERNEL_HP_KEYS = [
[
'GPRC/kern/kernels/0/variance',
'GPRC/kern/kernels/0/lengthscales',
],
[
'GPRC/kern/kernels/1/variance',
],
]
def _get_kernel_kwargs(self, **kwargs):
X_dim = kwargs.pop('X_dim')
return [
{
'input_dim': X_dim,
'ARD': False
},
{
'input_dim': X_dim,
},
]
def _build_kernel(self, kernel_kwargs, **kwargs):
k0 = gpflow.kernels.Exponential(**kernel_kwargs[0])
k1 = gpflow.kernels.White(**kernel_kwargs[1])
if kwargs.pop('optimize_hyperparameters'):
k0.lengthscales.transform = gpflow.transforms.Logistic(
*self._LENGTHSCALE_BOUNDS)
k = k0 + k1
return k
_MODEL_MAP = {
'BasicGP': BasicGP,
'ExpWhiteGP': ExpWhiteGP,
}
def create_model(model_name, **kwargs):
# Update tensorflow session settings to enable GPU sharing
gpflow.settings.session.update(gpu_options=tf.GPUOptions(allow_growth=True))
check_valid(model_name)
return _MODEL_MAP[model_name](**kwargs)
def check_valid(model_name):
if model_name not in _MODEL_MAP:
raise ValueError('Invalid GPR model name: {}'.format(model_name))