From 7113d4b25446676dfa11d5ed9d6fbd675dd475e8 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Tue, 9 Feb 2021 17:33:39 +0000 Subject: [PATCH 1/5] Adding plot_kernel() function to utils --- GCEm/utils.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/GCEm/utils.py b/GCEm/utils.py index 0cd628c..e38246f 100644 --- a/GCEm/utils.py +++ b/GCEm/utils.py @@ -2,6 +2,97 @@ import tensorflow as tf from tqdm import tqdm +def kernel_plot(kernels, kernel_op='add'): + """ Function for plotting kernel decomposition """ + import gpflow + from operator import add, mul + from functools import reduce + import matplotlib.pyplot as plt + + assert isinstance(kernels, list), "Input argument `kernels` must be a list of strings." + assert np.all([type(_)==str for _ in kernels]), "Input argument `kernels` must be a list of strings." + + kernel_dict = { + "RBF": gpflow.kernels.RBF(), + "Linear": gpflow.kernels.Linear(), + "Polynomial": gpflow.kernels.Polynomial(), + "Bias": gpflow.kernels.Bias(), + "White": gpflow.kernels.White(), + "Cosine": gpflow.kernels.Cosine(), + "Exponential": gpflow.kernels.Exponential(), + "Matern12": gpflow.kernels.Matern12(), + "Matern32": gpflow.kernels.Matern32(), + "Matern52": gpflow.kernels.Matern52(), + } + + operator_dict = { + 'add': add, + 'mul': mul + } + + if kernel_op not in operator_dict.keys(): + raise ValueError("Invalid operator: {}. Use either 'add' or 'mul'.".format(kernel_op)) + + for k in kernels: + try: + K_Class = kernel_dict[k] + except KeyError: + raise ValueError("Invalid Kernel: {}. Please choose from one of: {}".format(k, kernel_dict.keys())) + + + + # Plotting function + def plotkernelsample(k, ax, xmin=-3, xmax=3): + xx = np.linspace(xmin, xmax, 100)[:, None] + K = k(xx) + ax.plot(xx, np.random.multivariate_normal(np.zeros(100), K, 3).T) + ax.set_title(k.__class__.__name__) + + # Set up figure + if len(kernels)>=2: + ncols = 3 + else: + ncols = int(len(kernels)+1) + + nrows = int(np.ceil(len(kernels)/3) + 1) + + # Pad end of kernel list with 0 and 1 + # So it matches axes shape (nrows*ncols) + kernel_save = kernels*1 + kernels.extend([1] * (1)) + kernels.extend([0] * (nrows*ncols - len(kernels))) + + # Plot + fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(12, 6), dpi=100, sharex=True, sharey=True) + + for k_idx, k in enumerate(kernels): + if k==0: + # Redundant axes + axes.flatten()[k_idx].axis('off') + continue + + elif k==1: + # Sum/Product axis + k_combined = reduce(operator_dict[kernel_op], (kernel_dict[_] for _ in kernel_save)) + plotkernelsample(k_combined, axes.flatten()[k_idx]) + else: + K_class = kernel_dict[k] + plotkernelsample(K_class, axes.flatten()[k_idx]) + + for ax in axes.flatten()[0:len(kernel_save)+1]: + xmin, xmax = ax.get_xlim() + ymin, ymax = ax.get_ylim() + ax.hlines(0, xmin-0.1, xmax+0.1, 'k', '--', zorder=-10, lw=0.5) + ax.vlines(0, ymin-0.1, ymax+0.1, 'k', '--', zorder=-10, lw=0.5) + ax.set_xlim(xmin, xmax) + ax.set_ylim(ymin, ymax) + + _ = axes[0, 0].set_ylim(-3, 3) + fig.tight_layout() + + return fig, axes + + def add_121_line(ax): import numpy as np From 258ceb7c165f6313a6af46be125b2bccb37f4184 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Wed, 10 Feb 2021 10:35:46 +0000 Subject: [PATCH 2/5] updated with adaptive number of subplots --- GCEm/utils.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/GCEm/utils.py b/GCEm/utils.py index e38246f..3f40ed7 100644 --- a/GCEm/utils.py +++ b/GCEm/utils.py @@ -30,8 +30,9 @@ def kernel_plot(kernels, kernel_op='add'): 'mul': mul } - if kernel_op not in operator_dict.keys(): - raise ValueError("Invalid operator: {}. Use either 'add' or 'mul'.".format(kernel_op)) + if kernel_op is not None: + if kernel_op not in operator_dict.keys(): + raise ValueError("Invalid operator: {}. Use either 'add' or 'mul'.".format(kernel_op)) for k in kernels: try: @@ -45,25 +46,35 @@ def kernel_plot(kernels, kernel_op='add'): def plotkernelsample(k, ax, xmin=-3, xmax=3): xx = np.linspace(xmin, xmax, 100)[:, None] K = k(xx) - ax.plot(xx, np.random.multivariate_normal(np.zeros(100), K, 3).T) + ax.plot(xx, np.random.multivariate_normal(np.zeros(100), K, 5).T) ax.set_title(k.__class__.__name__) # Set up figure - if len(kernels)>=2: - ncols = 3 + if kernel_op is not None: + if len(kernels)>=2: + ncols = 3 + else: + ncols = int(len(kernels)+1) else: - ncols = int(len(kernels)+1) - - nrows = int(np.ceil(len(kernels)/3) + 1) + if len(kernels)>=3: + ncols = 3 + else: + ncols = int(len(kernels)) + + if kernel_op is not None: + nrows = int(np.ceil((len(kernels)+1)/3)) + else: + nrows = int(np.ceil(len(kernels)/3)) # Pad end of kernel list with 0 and 1 # So it matches axes shape (nrows*ncols) kernel_save = kernels*1 - kernels.extend([1] * (1)) + if kernel_op is not None: + kernels.extend([1] * (1)) kernels.extend([0] * (nrows*ncols - len(kernels))) # Plot - fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(12, 6), dpi=100, sharex=True, sharey=True) + fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(6*nrows, 3*nrows), dpi=100, sharex=True, sharey=True) for k_idx, k in enumerate(kernels): if k==0: @@ -79,7 +90,12 @@ def plotkernelsample(k, ax, xmin=-3, xmax=3): K_class = kernel_dict[k] plotkernelsample(K_class, axes.flatten()[k_idx]) - for ax in axes.flatten()[0:len(kernel_save)+1]: + if kernel_op is not None: + extra = 1 + else: + extra = 0 + + for ax in axes.flatten()[0:len(kernel_save)+extra]: xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() ax.hlines(0, xmin-0.1, xmax+0.1, 'k', '--', zorder=-10, lw=0.5) @@ -87,7 +103,7 @@ def plotkernelsample(k, ax, xmin=-3, xmax=3): ax.set_xlim(xmin, xmax) ax.set_ylim(ymin, ymax) - _ = axes[0, 0].set_ylim(-3, 3) + _ = axes.flatten()[0].set_ylim(-3, 3) fig.tight_layout() return fig, axes From d6801623d60c3b40e12ec133fb3b76f8db6ee04d Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Wed, 10 Feb 2021 10:44:30 +0000 Subject: [PATCH 3/5] Updating default kernel_plot args --- GCEm/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GCEm/utils.py b/GCEm/utils.py index 3f40ed7..032d26e 100644 --- a/GCEm/utils.py +++ b/GCEm/utils.py @@ -2,7 +2,7 @@ import tensorflow as tf from tqdm import tqdm -def kernel_plot(kernels, kernel_op='add'): +def kernel_plot(kernels, kernel_op=None): """ Function for plotting kernel decomposition """ import gpflow from operator import add, mul From c37faca598e97bc3308c582ab0eec708202db854 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Wed, 10 Feb 2021 10:53:43 +0000 Subject: [PATCH 4/5] Clarifying ValueError in _get_gpflow_kernel in init --- GCEm/__init__.py | 2 +- GCEm/utils.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/GCEm/__init__.py b/GCEm/__init__.py index be5b61e..bfcdfa2 100644 --- a/GCEm/__init__.py +++ b/GCEm/__init__.py @@ -141,7 +141,7 @@ def init_kernel(k): try: K_Class = kernel_dict[k] except KeyError: - raise ValueError("Invalid Kernel: {}. Please choose from one of: {}".format(k, kernel_dict)) + raise ValueError("Invalid Kernel: {}. Please choose from one of: {}".format(k, kernel_dict.keys())) if issubclass(K_Class, gpflow.kernels.Static): # This covers e.g. White return K_Class(active_dims=active_dims) diff --git a/GCEm/utils.py b/GCEm/utils.py index 032d26e..bf8ea64 100644 --- a/GCEm/utils.py +++ b/GCEm/utils.py @@ -9,6 +9,8 @@ def kernel_plot(kernels, kernel_op=None): from functools import reduce import matplotlib.pyplot as plt + from GCEm.__init__ import _get_gpflow_kernel + assert isinstance(kernels, list), "Input argument `kernels` must be a list of strings." assert np.all([type(_)==str for _ in kernels]), "Input argument `kernels` must be a list of strings." From 04a409389b680c12d0684ac075d47593324d7899 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Wed, 10 Feb 2021 11:08:00 +0000 Subject: [PATCH 5/5] Slight changes to _get_gpflow_kernel, now used in kernel_plot --- GCEm/__init__.py | 9 +++++++-- GCEm/utils.py | 41 ++++++++++------------------------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/GCEm/__init__.py b/GCEm/__init__.py index bfcdfa2..dd4c152 100644 --- a/GCEm/__init__.py +++ b/GCEm/__init__.py @@ -89,7 +89,7 @@ def gp_model(training_params, training_data, data_processors=None, return Emulator(model, training_params, data, name=name, gpu=gpu) -def _get_gpflow_kernel(names, n_params, active_dims=None, operator='add'): +def _get_gpflow_kernel(names, n_params, active_dims=None, operator='add', return_individual=False): """ Helper function for creating a single GPFlow kernel from a combination of kernel names. @@ -103,6 +103,8 @@ def _get_gpflow_kernel(names, n_params, active_dims=None, operator='add'): The active dimensions to allow the kernel to fit operator: {'add', 'mul'} The operator to use to combine the kernels + return_individual: bool + Whether to return a list of the individually initialized kernels Returns ------- @@ -154,7 +156,10 @@ def init_kernel(k): else: raise ValueError("Unexpected Kernel type: {}".format(K_Class)) # This shouldn't happen... - return reduce(operator_dict[operator], (init_kernel(k) for k in names)) + if return_individual: + return reduce(operator_dict[operator], (init_kernel(k) for k in names)), [init_kernel(k) for k in names] + else: + return reduce(operator_dict[operator], (init_kernel(k) for k in names)) def cnn_model(training_params, training_data, data_processors=None, diff --git a/GCEm/utils.py b/GCEm/utils.py index bf8ea64..211e8f1 100644 --- a/GCEm/utils.py +++ b/GCEm/utils.py @@ -13,36 +13,16 @@ def kernel_plot(kernels, kernel_op=None): assert isinstance(kernels, list), "Input argument `kernels` must be a list of strings." assert np.all([type(_)==str for _ in kernels]), "Input argument `kernels` must be a list of strings." - - kernel_dict = { - "RBF": gpflow.kernels.RBF(), - "Linear": gpflow.kernels.Linear(), - "Polynomial": gpflow.kernels.Polynomial(), - "Bias": gpflow.kernels.Bias(), - "White": gpflow.kernels.White(), - "Cosine": gpflow.kernels.Cosine(), - "Exponential": gpflow.kernels.Exponential(), - "Matern12": gpflow.kernels.Matern12(), - "Matern32": gpflow.kernels.Matern32(), - "Matern52": gpflow.kernels.Matern52(), - } - - operator_dict = { - 'add': add, - 'mul': mul - } - if kernel_op is not None: - if kernel_op not in operator_dict.keys(): - raise ValueError("Invalid operator: {}. Use either 'add' or 'mul'.".format(kernel_op)) + # Initialize kernels for plotting + kernel_list = _get_gpflow_kernel(names=kernels, n_params=1, + active_dims=None, operator='add', + return_individual=True)[1] - for k in kernels: - try: - K_Class = kernel_dict[k] - except KeyError: - raise ValueError("Invalid Kernel: {}. Please choose from one of: {}".format(k, kernel_dict.keys())) - - + if kernel_op is not None: + kernel_list.append(_get_gpflow_kernel(names=kernels, n_params=1, + active_dims=None, operator=kernel_op, + return_individual=True)[0]) # Plotting function def plotkernelsample(k, ax, xmin=-3, xmax=3): @@ -86,10 +66,9 @@ def plotkernelsample(k, ax, xmin=-3, xmax=3): elif k==1: # Sum/Product axis - k_combined = reduce(operator_dict[kernel_op], (kernel_dict[_] for _ in kernel_save)) - plotkernelsample(k_combined, axes.flatten()[k_idx]) + plotkernelsample(kernel_list[-1], axes.flatten()[k_idx]) else: - K_class = kernel_dict[k] + K_class = kernel_list[k_idx] plotkernelsample(K_class, axes.flatten()[k_idx]) if kernel_op is not None: