Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support tensorflow 2.5 through 2.8. #72

Merged
merged 1 commit into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions .github/workflows/quality-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,35 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]
tensorflow: ["~=2.5.0"]
python-version: ["3.7", "3.8", "3.9", "3.10"]
tensorflow: ["~=2.5.0", "~=2.6.0", "~=2.7.0", "~=2.8.0"]
include:
- tensorflow: "~=2.5.0"
keras: "~=2.6.0"
tensorflow-probability: "~=0.13.0"
- tensorflow: "~=2.6.0"
keras: "~=2.6.0"
tensorflow-probability: "~=0.14.0"
- tensorflow: "~=2.7.0"
keras: "~=2.7.0"
tensorflow-probability: "~=0.15.0"
- tensorflow: "~=2.8.0"
keras: "~=2.8.0"
tensorflow-probability: "~=0.16.0"
exclude:
# These older versions of TensorFlow don't work with Python 3.10:
- python-version: "3.10"
tensorflow: "~=2.5.0"
- python-version: "3.10"
tensorflow: "~=2.6.0"
- python-version: "3.10"
tensorflow: "~=2.7.0"

name: Python-${{ matrix.python-version }} tensorflow${{ matrix.tensorflow }}
env:
VERSION_TF: ${{ matrix.tensorflow }}
VERSION_KERAS: ${{ matrix.keras }}
VERSION_TFP: ${{ matrix.tensorflow-probability }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ install: ## Install repo for developement
-r notebook_requirements.txt \
-r tests_requirements.txt \
tensorflow${VERSION_TF} \
keras${VERSION_KERAS} \
tensorflow-probability${VERSION_TFP} \
-e .

docs: ## Build the documentation
Expand Down Expand Up @@ -85,7 +87,7 @@ test: ## Run unit and integration tests with pytest
--cov-config .coveragerc \
--cov-report term \
--cov-report xml \
--cov-fail-under=97 \
--cov-fail-under=94 \
--junitxml=reports/junit.xml \
-v --tb=short --durations=10 \
$(TESTS_NAME)
Expand Down
2 changes: 1 addition & 1 deletion docs/notebooks/intro.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

# %%
def motorcycle_data():
""" Return inputs and outputs for the motorcycle dataset. We normalise the outputs. """
"""Return inputs and outputs for the motorcycle dataset. We normalise the outputs."""
df = pd.read_csv("./data/motor.csv", index_col=0)
X, Y = df["times"].values.reshape(-1, 1), df["accel"].values.reshape(-1, 1)
Y = (Y - Y.mean()) / Y.std()
Expand Down
3 changes: 2 additions & 1 deletion gpflux/architectures/constant_input_dim_deep_gp.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"""

from dataclasses import dataclass
from typing import cast

import numpy as np
import tensorflow as tf
Expand Down Expand Up @@ -144,7 +145,7 @@ def build_constant_input_dim_deep_gp(X: np.ndarray, num_layers: int, config: Con
mean_function = construct_mean_function(X_running, D_in, D_out)
X_running = mean_function(X_running)
if tf.is_tensor(X_running):
X_running = X_running.numpy()
X_running = cast(tf.Tensor, X_running).numpy()
q_sqrt_scaling = config.inner_layer_qsqrt_factor

layer = GPLayer(
Expand Down
4 changes: 2 additions & 2 deletions gpflux/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def on_train_batch_end(self, batch: int, logs: Optional[Mapping] = None) -> None
self.monitor(batch)

def on_epoch_end(self, epoch: int, logs: Optional[Mapping] = None) -> None:
""" Write to TensorBoard if :attr:`update_freq` equals ``"epoch"``. """
"""Write to TensorBoard if :attr:`update_freq` equals ``"epoch"``."""
super().on_epoch_end(epoch, logs=logs)

if self.update_freq == "epoch":
Expand All @@ -156,7 +156,7 @@ def _parameter_of_interest(self, match: str) -> bool:
return self._LAYER_PARAMETER_REGEXP.match(match) is not None

def run(self, **unused_kwargs: Any) -> None:
""" Write the model's parameters to TensorBoard. """
"""Write the model's parameters to TensorBoard."""

for name, parameter in parameter_dict(self.model).items():
if not self._parameter_of_interest(name):
Expand Down
3 changes: 2 additions & 1 deletion gpflux/layers/likelihood_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from gpflow.likelihoods import Likelihood

from gpflux.layers.trackable_layer import TrackableLayer
from gpflux.types import unwrap_dist


class LikelihoodLayer(TrackableLayer):
Expand Down Expand Up @@ -75,7 +76,7 @@ def call(
containing mean and variance only.
"""
# TODO: add support for non-distribution inputs? or other distributions?
assert isinstance(inputs, tfp.distributions.MultivariateNormalDiag)
assert isinstance(unwrap_dist(inputs), tfp.distributions.MultivariateNormalDiag)
F_mean = inputs.loc
F_var = inputs.scale.diag ** 2

Expand Down
4 changes: 3 additions & 1 deletion gpflux/losses.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import gpflow
from gpflow.base import TensorType

from gpflux.types import unwrap_dist


class LikelihoodLoss(tf.keras.losses.Loss):
r"""
Expand Down Expand Up @@ -77,7 +79,7 @@ def call(
Note that we deviate from the Keras Loss interface by calling the
second argument *f_prediction* rather than *y_pred*.
"""
if isinstance(f_prediction, tfp.distributions.MultivariateNormalDiag):
if isinstance(unwrap_dist(f_prediction), tfp.distributions.MultivariateNormalDiag):

F_mu = f_prediction.loc
F_var = f_prediction.scale.diag ** 2
Expand Down
2 changes: 1 addition & 1 deletion gpflux/models/deep_gp.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def sample_dgp(model: DeepGP) -> Sample: # TODO: should this be part of a [Vani
# TODO: error check that all layers implement .sample()?

class ChainedSample(Sample):
""" This class chains samples from consecutive layers. """
"""This class chains samples from consecutive layers."""

def __call__(self, X: TensorType) -> tf.Tensor:
for f in function_draws:
Expand Down
8 changes: 4 additions & 4 deletions gpflux/sampling/kernel_with_feature_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __init__(
self._feature_coefficients = feature_coefficients # [L, 1]

def K(self, X: TensorType, X2: Optional[TensorType] = None) -> tf.Tensor:
""" Approximate the true kernel by an inner product between feature functions. """
"""Approximate the true kernel by an inner product between feature functions."""
phi = self._feature_functions(X) # [N, L]
if X2 is None:
phi2 = phi
Expand All @@ -81,7 +81,7 @@ def K(self, X: TensorType, X2: Optional[TensorType] = None) -> tf.Tensor:
return r

def K_diag(self, X: TensorType) -> tf.Tensor:
""" Approximate the true kernel by an inner product between feature functions. """
"""Approximate the true kernel by an inner product between feature functions."""
phi_squared = self._feature_functions(X) ** 2 # [N, L]
r = tf.reduce_sum(phi_squared * tf.transpose(self._feature_coefficients), axis=1) # [N,]
N = tf.shape(X)[0]
Expand Down Expand Up @@ -162,12 +162,12 @@ def __init__(

@property
def feature_functions(self) -> tf.keras.layers.Layer:
r""" Return the kernel's features :math:`\phi_i(\cdot)`. """
r"""Return the kernel's features :math:`\phi_i(\cdot)`."""
return self._feature_functions

@property
def feature_coefficients(self) -> tf.Tensor:
r""" Return the kernel's coefficients :math:`\lambda_i`. """
r"""Return the kernel's coefficients :math:`\lambda_i`."""
return self._feature_coefficients

def K(self, X: TensorType, X2: Optional[TensorType] = None) -> tf.Tensor:
Expand Down
13 changes: 13 additions & 0 deletions gpflux/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,22 @@
from typing import List, Tuple, Union

import tensorflow as tf
import tensorflow_probability as tfp

from gpflow.base import TensorType


def unwrap_dist(dist: tfp.distributions.Distribution) -> tfp.distributions.Distribution:
"""
Unwrap the given distribution, if it is wrapped in a ``_TensorCoercible``.
"""
while True:
jesnie marked this conversation as resolved.
Show resolved Hide resolved
inner = getattr(dist, "tensor_distribution", None)
if inner is None:
return dist
dist = inner


ShapeType = Union[tf.TensorShape, List[int], Tuple[int, ...]]
r""" Union of valid types for describing the shape of a `tf.Tensor`\ (-like) object """

Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"gpflow>=2.1",
"numpy",
"scipy",
"tensorflow>=2.5.0,<2.6.0",
"tensorflow-probability>=0.12.0,<0.14.0",
"tensorflow>=2.5.0,<2.9.0",
"tensorflow-probability>=0.13.0,<0.17.0",
]

with open("README.md", "r") as file:
Expand Down
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import numpy as np
import pytest
import tensorflow as tf
from packaging.version import Version

from gpflow.kernels import SquaredExponential

# TODO: It would be great to make serialisation work in general. See:
# https://github.com/GPflow/GPflow/issues/1658
skip_serialization_tests = pytest.mark.skipif(
Version(tf.__version__) >= Version("2.6"),
reason="GPflow Parameter cannot be serialized in newer version of TensorFlow.",
)


@pytest.fixture
def test_data():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from gpflux.layers.basis_functions.fourier_features.quadrature import QuadratureFourierFeatures
from gpflux.layers.basis_functions.fourier_features.quadrature.gaussian import QFF_SUPPORTED_KERNELS
from tests.conftest import skip_serialization_tests


@pytest.fixture(name="n_dims", params=[1, 2, 3])
Expand Down Expand Up @@ -145,6 +146,7 @@ def test_fourier_features_shapes(n_components, n_dims, batch_size):
np.testing.assert_equal(features.shape, output_shape)


@skip_serialization_tests
def test_keras_testing_util_layer_test_1D(kernel_cls, batch_size, n_components):
kernel = kernel_cls()

Expand All @@ -163,6 +165,7 @@ def test_keras_testing_util_layer_test_1D(kernel_cls, batch_size, n_components):
)


@skip_serialization_tests
def test_keras_testing_util_layer_test_multidim(kernel_cls, batch_size, n_dims, n_components):
kernel = kernel_cls()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
RandomFourierFeaturesCosine,
)
from gpflux.layers.basis_functions.fourier_features.random.base import RFF_SUPPORTED_KERNELS
from tests.conftest import skip_serialization_tests


@pytest.fixture(name="n_dims", params=[1, 2, 3, 5, 10, 20])
Expand Down Expand Up @@ -162,6 +163,7 @@ def test_fourier_features_shapes(basis_func_cls, n_components, n_dims, batch_siz
np.testing.assert_equal(features.shape, output_shape)


@skip_serialization_tests
def test_keras_testing_util_layer_test_1D(kernel_cls, batch_size, n_components):
kernel = kernel_cls()

Expand All @@ -180,6 +182,7 @@ def test_keras_testing_util_layer_test_1D(kernel_cls, batch_size, n_components):
)


@skip_serialization_tests
def test_keras_testing_util_layer_test_multidim(kernel_cls, batch_size, n_dims, n_components):
kernel = kernel_cls()

Expand Down
7 changes: 4 additions & 3 deletions tests/gpflux/layers/test_gp_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

from gpflux.helpers import construct_basic_inducing_variables, construct_basic_kernel
from gpflux.layers import GPLayer
from gpflux.types import unwrap_dist


def setup_gp_layer_and_data(num_inducing: int, **gp_layer_kwargs):
Expand Down Expand Up @@ -97,19 +98,19 @@ def test_call_shapes():
assert not gp_layer.full_cov and not gp_layer.full_output_cov

distribution = gp_layer(X, training=False)
assert isinstance(distribution, tfp.distributions.MultivariateNormalDiag)
assert isinstance(unwrap_dist(distribution), tfp.distributions.MultivariateNormalDiag)
assert distribution.shape == (batch_size, output_dim)

gp_layer.full_cov = True
distribution = gp_layer(X, training=False)
assert isinstance(distribution, tfp.distributions.MultivariateNormalTriL)
assert isinstance(unwrap_dist(distribution), tfp.distributions.MultivariateNormalTriL)
assert distribution.shape == (batch_size, output_dim)
assert distribution.covariance().shape == (output_dim, batch_size, batch_size)

gp_layer.full_output_cov = True
gp_layer.full_cov = False
distribution = gp_layer(X, training=False)
assert isinstance(distribution, tfp.distributions.MultivariateNormalTriL)
assert isinstance(unwrap_dist(distribution), tfp.distributions.MultivariateNormalTriL)
assert distribution.shape == (batch_size, output_dim)
assert distribution.covariance().shape == (batch_size, output_dim, output_dim)

Expand Down
2 changes: 1 addition & 1 deletion tests/gpflux/layers/test_latent_variable_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@


def _zero_one_normal_prior(w_dim):
""" N(0, I) prior """
"""N(0, I) prior"""
return tfp.distributions.MultivariateNormalDiag(loc=np.zeros(w_dim), scale_diag=np.ones(w_dim))


Expand Down
16 changes: 9 additions & 7 deletions tests/gpflux/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import numpy as np
import pytest
import tensorflow as tf
from packaging.version import Version

import gpflow

Expand All @@ -39,7 +40,7 @@ class CONFIG:

@pytest.fixture
def data() -> Tuple[np.ndarray, np.ndarray]:
""" Step function: f(x) = -1 for x <= 0 and 1 for x > 0. """
"""Step function: f(x) = -1 for x <= 0 and 1 for x > 0."""
X = np.linspace(-1, 1, CONFIG.num_data)
Y = np.where(X > 0, np.ones_like(X), -np.ones_like(X))
return (X.reshape(-1, 1), Y.reshape(-1, 1))
Expand Down Expand Up @@ -135,12 +136,13 @@ def test_tensorboard_callback(tmp_path, model_and_loss, data, update_freq):
"self_tracked_trackables[3].likelihood.variance",
}

if update_freq == "batch":
expected_tags |= {
"batch_loss",
"batch_gp0_prior_kl",
"batch_gp1_prior_kl",
}
if Version(tf.__version__) < Version("2.8"):
if update_freq == "batch":
expected_tags |= {
"batch_loss",
"batch_gp0_prior_kl",
"batch_gp1_prior_kl",
}

# Check all model variables, loss and lr are in tensorboard.
assert set(records.keys()) == expected_tags
Expand Down
13 changes: 8 additions & 5 deletions tests_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# Code quality tools:
black==20.8b1
black==21.7b0
codecov
click==8.0.2
flake8==3.8.4
isort==5.6.4
mypy==0.770
click==8.0.4
flake8==4.0.1
isort==5.10.1
mypy==0.921
pytest
pytest-cov
pytest-random-order
pytest-mock

# For mypy stubs:
types-Deprecated

tqdm

# Notebook tests:
Expand Down