GPflow 2 Upgrade Guide¶
This is a basic guide for people who have GPflow 1 code that needs to be upgraded to GPflow 2. Also see the Intro to GPflow with TensorFlow 2 notebook.
Kernel Input Dims¶
The input_dim
parameter has been removed from the Kernel
class’s initialiser. Therefore all calls to create a kernel must be changed to remove the input_dim
parameter.
For example:
-gpflow.kernels.SquaredExponential(1, variance=1.0, lengthscales=0.5)
+gpflow.kernels.SquaredExponential(variance=1.0, lengthscales=0.5)
Note: old code may still run without obvious errors against GPflow, since many kernels take an optional numerical value as their first parameter. You may not get the result you expect though!
Parameter and tf.Variable¶
The Parameter
class in GPflow 1 was a separate class from tf.Variable
. The params_as_tensors
decorator or the params_as_tensors_for
context manager were required to turn them into a tensor that could be consumed by TensorFlow operations.
In GPflow 2, Parameter
inherits from gpflow.Module
(a tf.Module
subclass) that wraps a tf.Variable
, and can directly be used in place of a tensor, so no such conversion is necessary.
References to params_as_tensors
and params_as_tensors_for
can simply be removed.
Parameter Assignment¶
In GPflow 2 the semantics of assigning values to parameters has changed. It is now necessary to use the Parameter.assign method rather than assigning values directly to parameters. For example:
# Initializations:
-likelihood.scale = 0.1
+likelihood.scale.assign(0.1)
In the above example, the old (GPflow 1) code would have assigned the value of likelihood.scale
to 0.1 (assuming that likelihood is a Parameterized
object and scale is a Parameter
), rather than replacing the scale
attribute with a Python float (which would be the “normal” Python behaviour). This maintains the properties of the parameter. For example, it remains trainable etc.
In GPflow 2, it is necessary to use the Parameter.assign
method explicitly to maintain the same behaviour, otherwise the parameter attribute will be replaced by an (untrainable) constant value.
To change other properties of the parameter (for example, to change transforms etc) you may need to replace the entire parameter object. See this notebook for further details.
Parameter trainable status¶
A parameter’s trainable
attribute cannot be set. Instead, use gpflow.set_trainable()
. E.g.:
-likelihood.trainable = False
+gpflow.set_trainable(likelihood, False)
SciPy Optimizer¶
Usage of GPflow’s Scipy optimizer has changed. It has been renamed from gpflow.train.ScipyOptimizer
to gpflow.optimizers.Scipy
and its minimize
method has changed in the following ways:
Instead of a GPflow model, the method now takes a zero-argument function that returns the loss to be minimised (most GPflow models provide a
model.training_loss
method for this use-case; gpflow.models.SVGP does not encapsulate data and provides amodel.training_loss_closure(data)
closure generating method instead), as well as the variables to be optimised (typicallymodel.trainable_variables
).The options (
disp
,maxiter
) must now be passed in a dictionary.
For example:
-optimizer = gpflow.train.ScipyOptimizer()
-optimizer.minimize(model, disp=True, maxiter=100)
+optimizer = gpflow.optimizers.Scipy()
+optimizer.minimize(
+ model.training_loss,
+ variables=model.trainable_variables,
+ options=dict(disp=True, maxiter=100),
+)
Any additional keyword arguments that are passed to the minimize
method are passed directly through to the SciPy optimizer’s minimize method.
Model Initialisers¶
In many cases the initialiser for the model will have changed. Typical changes include:
Instead of separate parameters for
X
andY
, some models now require a singledata
parameter containing a tuple of the X and Y data.The
kern
parameter has been renamed tokernel
.
For example, for the GPR
model:
-model = GPR(X, Y, kern=kernel)
+model = GPR(data=(X, Y), kernel=kernel)
Models that do not take a likelihood
argument because they hard-code a Gaussian likelihood (GPR, SGPR) now take a noise_variance
argument that sets the initial value of the likelihood variance.
SVGP Initialiser¶
The SVGP model’s initialiser no longer accepts X and Y data. Instead this data must be passed to the various computation methods of the model (elbo
, training_loss
etc).
In the Introduction to GPflow 2 notebook there is an example of how to use SVGP with optimisation using mini-batches of data.
In addition, SVGP’s Z
parameter has been removed. To pass-in inducing points use the inducing_variable
parameter. Also SVGP
’s feature
attribute has been renamed to inducing_variable
.
Autoflow¶
The @autoflow
decorator has been removed. Since eager execution is the default in TensorFlow 2 this is no longer necessary.
You may wish to consider wrapping functions that were previously wrapped in the @autoflow
decorator in the tf.function
decorator instead, to improve performance (but this is not necessary from a functionality point of view).
Use of tf.function¶
Wrapping compute-heavy operations such as calculating a model objective or even the optimizer steps (such as tf.optimizers.Adam().minimize()
) with tf.function
is crucial for efficient computation.
Note: you should ensure that functions wrapped in tf.function
are only passed tensors (not numpy arrays or other data structures, with the exception of a small number of bool or enum-style flags), or the decorator will re-compile the graph each time the function is passed new objects as its arguments. See the TensorFlow documentation on re-tracing for further details.
You can convert a numpy array to a tensor by using tf.constant
. For example: compiled_function(tf.constant(numpy_array))
.
Model Compilation¶
Models no longer need to be compiled before use. Remove all calls to the compile
method.
Sessions and Graphs¶
GPflow only supports eager execution, which is the default in TensorFlow 2. It does not support graph mode, which was the default execution mode in TensorFlow 1. Therefore all references to Sessions and Graphs should be removed. You should also remove references to the gpflow.reset_default_graph_and_session
function.
Warning: code that creates graphs (for example tf.Graph().as_default()
) will disable eager execution, which will not work well with GPflow 2. If you get errors like “’Tensor’ object has no attribute ‘numpy’” then you may not have removed all references to graphs and sessions in your code.
Defer Build¶
The defer_build
context manager has been removed. References to it can simply be removed.
Return Types from Auto-flowed Methods¶
GPflow methods that used the @autoflow
decorator, like for example predict_f
and predict_y
, will previously have returned NumPy Arrays. These now return TensorFlow tensors. In many cases these can be used like NumPy arrays (they can be passed directly to many of NumPy’s functions and even be plotted by matplotlib), but to actually turn them into numpy arrays you will need to call .numpy()
on them.
For example:
def _predict(self, features: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
mean, variance = self._model.predict_f(features)
- return mean, variance
+ return mean.numpy(), variance.numpy()
Parameter Values¶
In GPflow 1, Parameter.value
was a property that returned the numpy (np.ndarray
) representation of the value of the Parameter.
In GPflow 2, Parameter
behaves similar to TensorFlow’s tf.Variable
: Parameter.value()
is a method that returns a constant tf.Tensor with the current (constrained) value of the Parameter. To obtain the numpy representation, use the Parameter.numpy()
method:
For example:
-std_dev = np.sqrt(model.likelihood.variance.value)
+std_dev = np.sqrt(model.likelihood.variance.numpy())
Model Class¶
The Model
class has been removed. A suitable replacement, for those models that do not wish to inherit from GPModel
, may be BayesianModel
.
Periodic Base Kernel¶
The base kernel for the Periodic
kernel must now be specified explicitly. Previously the default was SquaredExponential
, so to maintain the same behaviour as before this must be passed-in to the Periodic
kernel’s initialiser (note that active_dims
is specified in the base kernel).
For example:
-Periodic(1, active_dims=[2])
+Periodic(SquaredExponential(active_dims=[2]))
Predict Full Covariance¶
The predict_f_full_cov
method has been removed from GPModel
. Instead, pass full_cov=True
to the predict_f
method.
For example:
-f_mean, f_cov = model.predict_f_full_cov(X)
+f_mean, f_cov = model.predict_f(X, full_cov=True)
Predictive (log)density¶
The predict_density
method of GPModels and Likelihoods has been renamed to predict_log_density
. (It always returned the predictive log-density, so no change in behaviour.)
Settings / Configuration¶
In GPflow 2, the gpflow.settings
module and the gpflowrc
file have been removed. Instead, there is gpflow.config
.
gpflow.settings.float_type
has changed to gpflow.default_float()
and gpflow.settings.int_type
has changed to gpflow.default_int()
. gpflow.settings.jitter
/gpflow.settings.numerics.jitter_level
has changed to gpflow.default_jitter()
.
These default settings can be changed using environment variables (GPFLOW_FLOAT
, GPFLOW_INT
, GPFLOW_JITTER
, etc.) or function calls (gpflow.config.set_default_float()
etc.). There is also a gpflow.config.as_context()
context manager for temporarily changing settings for only part of the code.
See the gpflow.config
API documentation for more details.
Data Types¶
In some cases TensorFlow will try to figure out an appropriate data type for certain variables. If Python floats have been used, TensorFlow may default these variables to float32
, which can cause incompatibilities with GPflow, which defaults to using float64
.
To resolve this you can use tf.constant
instead of a Python float, and explicitly specify the data type, e.g.
tf.constant(0.1, dtype=gpflow.default_float())
Transforms¶
These have been removed in favour of the tools in tensorflow_probability.bijectors
. See for example this Stackoverflow post.
GPflow 2 still provides the gpflow.utilities.triangular
alias for tfp.bijectors.FillTriangular
.
To constrain parameters to be positive, there is gpflow.utilities.positive
which is configurable to be either softplus or exp, with an optional shift to ensure a lower bound that is larger than zero. Note that the default lower bound used to be 1e-6
; by default, the lower bound if not specified explicitly is now 0.0
. Revert the previous behaviour using gpflow.config.set_default_positive_minimum(1e-6)
.
Stationary kernel subclasses¶
Most stationary kernels are actually isotropic-stationary kernels, and should now subclass from gpflow.kernels.IsotropicStationary
instead of gpflow.kernels.Stationary
. (The Cosine
kernel is an example of a non-isotropic stationary kernel that depends on the direction, not just the norm, of \(\mathbf{x} - \mathbf{x}'\).)
Likelihoods¶
We cleaned up the likelihood API. Likelihoods now explicitly define the expected number of outputs (observation_dim
) and latent functions (latent_dim
), and shape-checking is in place by default.
Most of the likelihoods simply broadcasted over outputs; these have now been grouped to subclass from gpflow.likelihoods.ScalarLikelihood
, and implementations have been moved to leading-underscore functions. ScalarLikelihood
subclasses need to implement at least _scalar_log_prob
(previously logp
), _conditional_mean
, and _conditional_variance
.
The likelihood log_prob
, predict_log_density
, and variational_expectations
methods now return a single value per data row; for ScalarLikelihood
subclasses this means these methods effectively sum over the observation dimension (multiple outputs for the same input).
Priors¶
Priors used to be defined on the unconstrained variable. The default has changed to the prior to be defined on the constrained parameter value; this can be changed by passing the prior_on
argument to gpflow.Parameter()
. See the MCMC notebook for more details.
Name Scoping¶
The name_scope
decorator does not exist in GPflow 2 anymore. Use TensorFlow’s `name_scope
<https://www.tensorflow.org/api_docs/python/tf/name_scope?version=stable>`__ context manager instead.
Model Persistence¶
Model persistence with gpflow.saver
has been removed in GPflow 2, in favour of TensorFlow 2’s checkpointing and model persistence using the SavedModel format.
There is currently a bug in saving GPflow models with TensorFlow’s model persistence (SavedModels). See https://github.com/GPflow/GPflow/issues/1127 for more details; a workaround is to replace all trainable parameters with constants using gpflow.utilities.freeze(model)
.
Checkpointing works fine.