From 8ba7422f6c97cba87abeb94ebd2752ccb206bfc0 Mon Sep 17 00:00:00 2001 From: ned Date: Wed, 21 Aug 2024 14:07:14 +0200 Subject: [PATCH 1/5] Test all probabilistic layers as model output layer --- tests/test_models.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 16d87c5..3052b06 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,4 +1,5 @@ import itertools +from inspect import getmembers, isclass import numpy as np import pytest @@ -6,7 +7,10 @@ from keras.engine.functional import Functional from numpy.testing import assert_array_equal -from mlpp_lib import models +from mlpp_lib import models, probabilistic_layers + + +PROB_LAYERS = [obj[0] for obj in getmembers(probabilistic_layers, isclass)] FCN_OPTIONS = dict( @@ -17,7 +21,7 @@ dropout=[None, 0.1, [0.1, 0.0]], mc_dropout=[True, False], out_bias_init=["zeros", np.array([0.2]), np.array([0.2, 2.1])], - probabilistic_layer=[None, "IndependentNormal", "MultivariateNormalTriL"], + probabilistic_layer=[None] + PROB_LAYERS, skip_connection=[False, True], ) From c067f22f8e224008dd5fad5079d6db6fb9db9436 Mon Sep 17 00:00:00 2001 From: ned Date: Wed, 21 Aug 2024 14:08:36 +0200 Subject: [PATCH 2/5] Test model output --- tests/test_models.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_models.py b/tests/test_models.py index 3052b06..6b7dcde 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -38,6 +38,24 @@ ] +def _test_model(model): + moodel_is_keras = ( + str(type(model)).endswith("keras.engine.sequential.Sequential'>") + or str(type(model)).endswith("keras.models.Sequential'>") + or str(type(model)).endswith("keras.engine.training.Model'>") + or isinstance(model, tf.keras.Model) + ) + assert moodel_is_keras + assert len(model.layers[-1]._inbound_nodes) > 0 + model_output = model.layers[-1].output + assert not isinstance( + model_output, list + ), "The model output must be a single tensor!" + assert ( + len(model_output.shape) < 3 + ), "The model output must be a vector or a single value!" + + def _test_prediction(model, scenario_kwargs, dummy_input, output_size): pred = model(dummy_input) assert pred.shape == (32, output_size) @@ -82,6 +100,7 @@ def test_fully_connected_network(scenario_kwargs): ) assert isinstance(model, Functional) + _test_model(model) _test_prediction(model, scenario_kwargs, dummy_input, output_size) @@ -114,6 +133,7 @@ def test_fully_connected_multibranch_network(scenario_kwargs): ) assert isinstance(model, Functional) + _test_model(model) _test_prediction(model, scenario_kwargs, dummy_input, output_size) @@ -139,4 +159,5 @@ def test_deep_cross_network(scenario_kwargs): model = models.deep_cross_network(input_shape, output_size, **scenario_kwargs) assert isinstance(model, Functional) + _test_model(model) _test_prediction(model, scenario_kwargs, dummy_input, output_size) From dd735a6256668f748699ec534d3eba33e621aebc Mon Sep 17 00:00:00 2001 From: ned Date: Wed, 21 Aug 2024 14:20:27 +0200 Subject: [PATCH 3/5] Fix output for built-in prob layers --- mlpp_lib/probabilistic_layers.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/mlpp_lib/probabilistic_layers.py b/mlpp_lib/probabilistic_layers.py index 807afb7..01bca9e 100644 --- a/mlpp_lib/probabilistic_layers.py +++ b/mlpp_lib/probabilistic_layers.py @@ -13,7 +13,7 @@ ) -# these work out of the box +# these almost work out of the box from tensorflow_probability.python.layers import ( IndependentNormal, IndependentLogistic, @@ -21,6 +21,33 @@ IndependentPoisson, ) +@tf.keras.saving.register_keras_serializable() +class IndependentNormal(IndependentNormal): + @property + def output(self): # this is necessary to use the layer within shap + return super().output[0] + + +@tf.keras.saving.register_keras_serializable() +class IndependentLogistic(IndependentLogistic): + @property + def output(self): + return super().output[0] + + +@tf.keras.saving.register_keras_serializable() +class IndependentBernoulli(IndependentBernoulli): + @property + def output(self): + return super().output[0] + + +@tf.keras.saving.register_keras_serializable() +class IndependentPoisson(IndependentPoisson): + @property + def output(self): + return super().output[0] + @tf.keras.saving.register_keras_serializable() class IndependentBeta(tfpl.DistributionLambda): From caf436562ba6238d745150775492d853bba44996 Mon Sep 17 00:00:00 2001 From: ned Date: Wed, 21 Aug 2024 14:24:43 +0200 Subject: [PATCH 4/5] Small refactoring --- tests/test_models.py | 9 ++------- tests/test_probabilistic_layers.py | 7 +++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 6b7dcde..0eb6258 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -10,9 +10,6 @@ from mlpp_lib import models, probabilistic_layers -PROB_LAYERS = [obj[0] for obj in getmembers(probabilistic_layers, isclass)] - - FCN_OPTIONS = dict( input_shape=[(5,)], output_size=[1, 2], @@ -21,7 +18,7 @@ dropout=[None, 0.1, [0.1, 0.0]], mc_dropout=[True, False], out_bias_init=["zeros", np.array([0.2]), np.array([0.2, 2.1])], - probabilistic_layer=[None] + PROB_LAYERS, + probabilistic_layer=[None] + ["IndependentNormal", "IndependentGamma"], skip_connection=[False, True], ) @@ -46,6 +43,7 @@ def _test_model(model): or isinstance(model, tf.keras.Model) ) assert moodel_is_keras + assert isinstance(model, Functional) assert len(model.layers[-1]._inbound_nodes) > 0 model_output = model.layers[-1].output assert not isinstance( @@ -98,7 +96,6 @@ def test_fully_connected_network(scenario_kwargs): model = models.fully_connected_network( input_shape, output_size, **scenario_kwargs ) - assert isinstance(model, Functional) _test_model(model) _test_prediction(model, scenario_kwargs, dummy_input, output_size) @@ -131,7 +128,6 @@ def test_fully_connected_multibranch_network(scenario_kwargs): model = models.fully_connected_multibranch_network( input_shape, output_size, **scenario_kwargs ) - assert isinstance(model, Functional) _test_model(model) _test_prediction(model, scenario_kwargs, dummy_input, output_size) @@ -157,7 +153,6 @@ def test_deep_cross_network(scenario_kwargs): else: model = models.deep_cross_network(input_shape, output_size, **scenario_kwargs) - assert isinstance(model, Functional) _test_model(model) _test_prediction(model, scenario_kwargs, dummy_input, output_size) diff --git a/tests/test_probabilistic_layers.py b/tests/test_probabilistic_layers.py index ec9d4f9..939e5f9 100644 --- a/tests/test_probabilistic_layers.py +++ b/tests/test_probabilistic_layers.py @@ -54,6 +54,13 @@ def test_probabilistic_model(layer): encoder.summary() encoder.compile() assert isinstance(encoder, tf.keras.Sequential) + model_output = encoder.layers[-1].output + assert not isinstance( + model_output, list + ), "The model output must be a single tensor!" + assert ( + len(model_output.shape) < 3 + ), "The model output must be a vector or a single value!" @pytest.mark.parametrize("layer", LAYERS) From ad5b13aee9671ed0f30e629f9c3c5fa9c1972c74 Mon Sep 17 00:00:00 2001 From: ned Date: Wed, 21 Aug 2024 14:25:56 +0200 Subject: [PATCH 5/5] Cleanup imports --- tests/test_models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_models.py b/tests/test_models.py index 0eb6258..60d0bb6 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,4 @@ import itertools -from inspect import getmembers, isclass import numpy as np import pytest @@ -7,7 +6,7 @@ from keras.engine.functional import Functional from numpy.testing import assert_array_equal -from mlpp_lib import models, probabilistic_layers +from mlpp_lib import models FCN_OPTIONS = dict(