From 135a9693817cfc10684971e8fd1b237ff66b61d3 Mon Sep 17 00:00:00 2001 From: Ramona Leenings Date: Wed, 27 Sep 2023 12:59:42 +0200 Subject: [PATCH 01/10] add LinearDiscriminantAnalysis to registry --- photonai/base/registry/PhotonCore.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/photonai/base/registry/PhotonCore.json b/photonai/base/registry/PhotonCore.json index ff2da2ef..54f00181 100644 --- a/photonai/base/registry/PhotonCore.json +++ b/photonai/base/registry/PhotonCore.json @@ -295,6 +295,10 @@ "sklearn.linear_model.LogisticRegression", "Estimator" ], + "LinearDiscriminantAnalysis": [ + "sklearn.discriminant_analysis.LinearDiscriminantAnalysis", + "Transformer" + ], "PassiveAggressiveClassifier":[ "sklearn.linear_model.PassiveAggressiveClassifier", "Estimator" From b960e4004c085e2de557233960e6dff334c4eea9 Mon Sep 17 00:00:00 2001 From: Ramona Leenings Date: Thu, 22 Feb 2024 14:34:57 +0100 Subject: [PATCH 02/10] store backmapped features instead of original ones in result tree root --- photonai/base/hyperpipe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/photonai/base/hyperpipe.py b/photonai/base/hyperpipe.py index 5c5493bd..ef4ca12b 100644 --- a/photonai/base/hyperpipe.py +++ b/photonai/base/hyperpipe.py @@ -939,7 +939,7 @@ def _finalize_optimization(self): if not feature_importances: logger.info("No feature importances available for {}!".format(self.optimum_pipe.elements[-1][0])) else: - self.results.best_config_feature_importances = feature_importances + # write backmapping file only if optimum_pipes inverse_transform works completely. # restriction: only a faulty inverse_transform is considered, missing ones are further ignored. @@ -953,6 +953,7 @@ def _finalize_optimization(self): # save backmapping self.results_handler.save_backmapping( filename='optimum_pipe_feature_importances_backmapped', backmapping=backmapping) + self.results.best_config_feature_importances = list(np.squeeze(backmapping)) else: logger.info('Could not save feature importance: backmapping NOT successful.') From 339d7d0b9adbf035455ec4df6d592059ce4e5d69 Mon Sep 17 00:00:00 2001 From: Ramona Leenings Date: Thu, 22 Feb 2024 15:28:17 +0100 Subject: [PATCH 03/10] reverse backmapped feature importances im tree --- photonai/base/hyperpipe.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/photonai/base/hyperpipe.py b/photonai/base/hyperpipe.py index ef4ca12b..0812972a 100644 --- a/photonai/base/hyperpipe.py +++ b/photonai/base/hyperpipe.py @@ -939,23 +939,22 @@ def _finalize_optimization(self): if not feature_importances: logger.info("No feature importances available for {}!".format(self.optimum_pipe.elements[-1][0])) else: - + self.results.best_config_feature_importances = feature_importances # write backmapping file only if optimum_pipes inverse_transform works completely. # restriction: only a faulty inverse_transform is considered, missing ones are further ignored. - with warnings.catch_warnings(record=True) as w: - # get backmapping - backmapping, _, _ = self.optimum_pipe.\ - inverse_transform(np.array(feature_importances).reshape(1, -1), None) - - if not any("The inverse transformation is not possible for" in s - for s in [e.message.args[0] for e in w]): - # save backmapping - self.results_handler.save_backmapping( - filename='optimum_pipe_feature_importances_backmapped', backmapping=backmapping) - self.results.best_config_feature_importances = list(np.squeeze(backmapping)) - else: - logger.info('Could not save feature importance: backmapping NOT successful.') + # with warnings.catch_warnings(record=True) as w: + # # get backmapping + # backmapping, _, _ = self.optimum_pipe.\ + # inverse_transform(np.array(feature_importances).reshape(1, -1), None) + # + # if not any("The inverse transformation is not possible for" in s + # for s in [e.message.args[0] for e in w]): + # # save backmapping + # self.results_handler.save_backmapping( + # filename='optimum_pipe_feature_importances_backmapped', backmapping=backmapping) + # else: + # logger.info('Could not save feature importance: backmapping NOT successful.') # save learning curves if self.cross_validation.learning_curves: From 4ca1459436c7f00fbd7f97bdfa9f83212cf8f9bb Mon Sep 17 00:00:00 2001 From: Ramona Leenings Date: Thu, 22 Feb 2024 16:18:32 +0100 Subject: [PATCH 04/10] removing logging output for backmapping --- photonai/base/hyperpipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/photonai/base/hyperpipe.py b/photonai/base/hyperpipe.py index 0812972a..ea913b02 100644 --- a/photonai/base/hyperpipe.py +++ b/photonai/base/hyperpipe.py @@ -933,7 +933,7 @@ def _finalize_optimization(self): logger.error(str(e)) # get feature importances of optimum pipe - logger.info("Mapping back feature importances...") + # logger.info("Mapping back feature importances...") feature_importances = self.optimum_pipe.feature_importances_ if not feature_importances: From cc2fb6398cc36de7fac8c9b06f0766aa52bce2c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:17:47 +0000 Subject: [PATCH 05/10] Bump scikit-learn from 1.1.3 to 1.5.2 Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.1.3 to 1.5.2. - [Release notes](https://github.com/scikit-learn/scikit-learn/releases) - [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.1.3...1.5.2) --- updated-dependencies: - dependency-name: scikit-learn dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- photonai/requirements.txt | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/photonai/requirements.txt b/photonai/requirements.txt index 30c8825d..769bcfc4 100644 --- a/photonai/requirements.txt +++ b/photonai/requirements.txt @@ -1,7 +1,7 @@ ###### Requirements with temporary Version Specifiers ###### numpy matplotlib -scikit-learn==1.1.3 +scikit-learn==1.5.2 pandas plotly imbalanced-learn diff --git a/requirements.txt b/requirements.txt index f6d6db70..31340106 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ numpy matplotlib -scikit-learn==1.3.0 +scikit-learn==1.5.2 pandas plotly imbalanced-learn==0.11.0 From c7d1e3fa71d560d6060df00ca0ac4f2af524f6e9 Mon Sep 17 00:00:00 2001 From: Jan Ernsting Date: Wed, 23 Oct 2024 14:34:25 +0200 Subject: [PATCH 06/10] Update requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 31340106..0b648199 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ matplotlib scikit-learn==1.5.2 pandas plotly -imbalanced-learn==0.11.0 +imbalanced-learn==0.12.4 pymodm scipy statsmodels @@ -14,4 +14,4 @@ dask>=2021.10.0 distributed scikit-optimize xlrd -pbr \ No newline at end of file +pbr From 91f5ad5ecd3d7684810b220f9017ede824ee0e43 Mon Sep 17 00:00:00 2001 From: Jan Ernsting Date: Wed, 23 Oct 2024 15:08:18 +0200 Subject: [PATCH 07/10] Fixed tests --- photonai/modelwrapper/keras_base_estimator.py | 4 ++-- photonai/modelwrapper/keras_base_models.py | 2 +- photonai/processing/metrics.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/photonai/modelwrapper/keras_base_estimator.py b/photonai/modelwrapper/keras_base_estimator.py index 70d6401f..08c37934 100644 --- a/photonai/modelwrapper/keras_base_estimator.py +++ b/photonai/modelwrapper/keras_base_estimator.py @@ -76,7 +76,7 @@ def save(self, filename): with open(filename + ".json", "w") as json_file: json_file.write(model_json) # serialize weights to HDF5 - self.model.save_weights(filename + ".h5") + self.model.save_weights(filename + ".weights.h5") def load(self, filename): # load json and create model @@ -86,6 +86,6 @@ def load(self, filename): loaded_model = keras.models.model_from_json(loaded_model_json) # load weights into new model - loaded_model.load_weights(filename + ".h5") + loaded_model.load_weights(filename + ".weights.h5") self.model = loaded_model self.init_weights = self.model.get_weights() diff --git a/photonai/modelwrapper/keras_base_models.py b/photonai/modelwrapper/keras_base_models.py index 9f7f026b..71d9b43d 100644 --- a/photonai/modelwrapper/keras_base_models.py +++ b/photonai/modelwrapper/keras_base_models.py @@ -245,7 +245,7 @@ def optimizer(self, value): if value.lower() not in __supported_optimizers__.keys(): raise ValueError("Optimizer is not supported by keras. Please use one of: "+str(__supported_optimizers__)) else: - self._optimizer = __supported_optimizers__[value.lower()](lr=self.learning_rate) + self._optimizer = __supported_optimizers__[value.lower()](learning_rate=self.learning_rate) @property def target_activation(self): diff --git a/photonai/processing/metrics.py b/photonai/processing/metrics.py index 5c4d8c3c..ed69fc12 100644 --- a/photonai/processing/metrics.py +++ b/photonai/processing/metrics.py @@ -107,9 +107,9 @@ def register_custom_metric(cls, metric: Union[Metric_Type, Tuple[str, Metric_Typ metric_obj = metric def metric_func(y_true, y_pred): - metric_obj.reset_states() + metric_obj.reset_state() metric_obj.update_state(y_true=y_true, y_pred=y_pred) - return float(cls.dynamic_keras_import.backend.eval(metric_obj.result())) + return float(metric_obj.result().numpy()) Scorer.CUSTOM_ELEMENT_DICTIONARY[metric_name] = metric_func elif callable(metric): From 92e933ed9a9fee998f255812ca227f9b822c5b7b Mon Sep 17 00:00:00 2001 From: Jan Ernsting Date: Wed, 23 Oct 2024 16:01:45 +0200 Subject: [PATCH 08/10] Fixed keras model load and save --- photonai/modelwrapper/keras_base_estimator.py | 27 ++++++++++++------- test/modelwrapper_tests/test_keras_basic.py | 23 +++++++++++----- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/photonai/modelwrapper/keras_base_estimator.py b/photonai/modelwrapper/keras_base_estimator.py index 08c37934..5c211217 100644 --- a/photonai/modelwrapper/keras_base_estimator.py +++ b/photonai/modelwrapper/keras_base_estimator.py @@ -1,4 +1,5 @@ import warnings +import os import tensorflow.keras as keras from sklearn.base import BaseEstimator @@ -72,20 +73,28 @@ def encode_targets(self, y): def save(self, filename): # serialize model to JSON + warnings.warn("Using json export for compatibility, will be deprecated in future.") model_json = self.model.to_json() with open(filename + ".json", "w") as json_file: - json_file.write(model_json) + json_file.write(model_json) # serialize weights to HDF5 self.model.save_weights(filename + ".weights.h5") + self.model.save(filename + ".keras") def load(self, filename): # load json and create model - json_file = open(filename + '.json', 'r') - loaded_model_json = json_file.read() - json_file.close() - loaded_model = keras.models.model_from_json(loaded_model_json) + if not os.path.exists(filename+'.keras'): + warnings.warn("Using json import for compatiblity, will be deprecated in future. " + "Please save your model to get a *.keras file") + json_file = open(filename + '.json', 'r') + loaded_model_json = json_file.read() + json_file.close() + loaded_model = keras.models.model_from_json(loaded_model_json) + + loaded_model.load_weights(filename + ".weights.h5") + self.model = loaded_model + self.init_weights = self.model.get_weights() + else: + # load weights into new model + self.model = keras.models.load_model(filename + '.keras') - # load weights into new model - loaded_model.load_weights(filename + ".weights.h5") - self.model = loaded_model - self.init_weights = self.model.get_weights() diff --git a/test/modelwrapper_tests/test_keras_basic.py b/test/modelwrapper_tests/test_keras_basic.py index b6d92f77..b84673e7 100644 --- a/test/modelwrapper_tests/test_keras_basic.py +++ b/test/modelwrapper_tests/test_keras_basic.py @@ -1,7 +1,7 @@ from sklearn.datasets import load_breast_cancer, load_diabetes import tensorflow as tf from tensorflow.keras.models import Sequential -from tensorflow.keras.layers import Dense, Dropout +from tensorflow.keras.layers import Dense, Dropout, Input, Activation import numpy as np import warnings import os @@ -16,7 +16,8 @@ def setUp(self): self.X, self.y = load_breast_cancer(return_X_y=True) self.model = Sequential() - self.model.add(Dense(3, input_dim=self.X.shape[1], activation='relu')) + self.model.add(Input(shape=[self.X.shape[1]])) + self.model.add(Dense(3, activation="relu")) self.model.add(Dropout(0.1)) self.model.add(Dense(2, activation='softmax')) self.model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) @@ -24,8 +25,8 @@ def setUp(self): self.estimator_type = KerasBaseClassifier inputs = tf.keras.Input(shape=(self.X.shape[1],)) - x = tf.keras.layers.Dense(4, activation=tf.nn.relu)(inputs) - outputs = tf.keras.layers.Dense(2, activation=tf.nn.softmax)(x) + x = tf.keras.layers.Dense(4, activation=tf.keras.activations.relu)(inputs) + outputs = tf.keras.layers.Dense(2, activation=tf.keras.activations.softmax)(x) self.tf_model = tf.keras.Model(inputs=inputs, outputs=outputs) self.tf_model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) @@ -57,10 +58,18 @@ def test_tf_model(self): estimator.save("keras_example_saved_model") - reload_estinator = self.estimator_type() - reload_estinator.load("keras_example_saved_model") + reload_estimator = self.estimator_type() + reload_estimator.load("keras_example_saved_model") + + np.testing.assert_array_almost_equal(estimator.predict(self.X), reload_estimator.predict(self.X), decimal=3) + + # remove novel keras file and test legacy import + os.remove("keras_example_saved_model.keras") + + reload_estimator_legacy = self.estimator_type() + reload_estimator_legacy.load("keras_example_saved_model") - np.testing.assert_array_almost_equal(estimator.predict(self.X), reload_estinator.predict(self.X), decimal=3) + np.testing.assert_array_almost_equal(estimator.predict(self.X), reload_estimator.predict(self.X), decimal=3) # remove saved keras files for fname in os.listdir("."): From 1fb1a1eb7f5d6e4d65f75c49a483957ec299e553 Mon Sep 17 00:00:00 2001 From: Jan Ernsting Date: Fri, 25 Oct 2024 12:29:21 +0200 Subject: [PATCH 09/10] Removed backmapping tests --- test/base_tests/test_hyperpipe.py | 14 ++++++------ test/processing_tests/test_results_handler.py | 22 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/test/base_tests/test_hyperpipe.py b/test/base_tests/test_hyperpipe.py index cdefbf5d..4c989956 100644 --- a/test/base_tests/test_hyperpipe.py +++ b/test/base_tests/test_hyperpipe.py @@ -700,14 +700,14 @@ def test_finalize_optimization(self): # save optimum model self.assert_best_model() - # backmapping + # backmapping - removed in 339d7d0 # because the pca is test disabled, we expect the number of features - self.assertEqual(len(self.hyperpipe.results.best_config_feature_importances[0]), self.__X.shape[1]) - backmapped_feature_importances = os.path.join(self.hyperpipe.output_settings.results_folder, - 'optimum_pipe_feature_importances_backmapped.csv') - self.assertTrue(os.path.isfile(backmapped_feature_importances)) - loaded_array = np.loadtxt(open(backmapped_feature_importances, 'rb'), delimiter=",") - self.assertEqual(loaded_array.shape[0], self.__X.shape[1]) + #self.assertEqual(len(self.hyperpipe.results.best_config_feature_importances[0]), self.__X.shape[1]) + #backmapped_feature_importances = os.path.join(self.hyperpipe.output_settings.results_folder, + # 'optimum_pipe_feature_importances_backmapped.csv') + #self.assertTrue(os.path.isfile(backmapped_feature_importances)) + #loaded_array = np.loadtxt(open(backmapped_feature_importances, 'rb'), delimiter=",") + #self.assertEqual(loaded_array.shape[0], self.__X.shape[1]) def assert_best_model(self): self.assertTrue(os.path.isfile(os.path.join(self.hyperpipe.output_settings.results_folder, diff --git a/test/processing_tests/test_results_handler.py b/test/processing_tests/test_results_handler.py index 1d94909e..e7edb483 100644 --- a/test/processing_tests/test_results_handler.py +++ b/test/processing_tests/test_results_handler.py @@ -100,24 +100,26 @@ def test_save_backmapping_weird_format(self): def test_save_backmapping_csv(self): """ Check dimension of feature backmapping equals input dimensions for less than 1000 features. + removed in 339d7d0 """ - backmapping = np.loadtxt(os.path.join(self.hyperpipe.output_settings.results_folder, - 'optimum_pipe_feature_importances_backmapped.csv'), delimiter=',') - self.assertEqual(np.shape(self.__X)[1], backmapping.size) + #backmapping = np.loadtxt(os.path.join(self.hyperpipe.output_settings.results_folder, + # 'optimum_pipe_feature_importances_backmapped.csv'), delimiter=',') + #self.assertEqual(np.shape(self.__X)[1], backmapping.size) def test_save_backmapping_npz(self): """ Check dimension of feature backmapping equals input dimensions for more than 1000 features. + removed in 339d7d0 """ # run another hyperpipe with more than 1000 features # use np.tile to copy features until at least 1000 features are reached - X = np.tile(self.__X, (1, 35)) - self.hyperpipe.fit(X, self.__y) - npzfile = np.load(os.path.join(self.hyperpipe.output_settings.results_folder, - 'optimum_pipe_feature_importances_backmapped.npz')) - self.assertEqual(len(npzfile.files), 1) - backmapping = npzfile[npzfile.files[0]] - self.assertEqual(np.shape(X)[1], backmapping.size) + #X = np.tile(self.__X, (1, 35)) + #self.hyperpipe.fit(X, self.__y) + #npzfile = np.load(os.path.join(self.hyperpipe.output_settings.results_folder, + # 'optimum_pipe_feature_importances_backmapped.npz')) + #self.assertEqual(len(npzfile.files), 1) + #backmapping = npzfile[npzfile.files[0]] + #self.assertEqual(np.shape(X)[1], backmapping.size) def test_save_backmapping_stack(self): # build hyperpipe with stack first From f1e54cbe3afb815b880808538a28c271c72eccaf Mon Sep 17 00:00:00 2001 From: Jan Ernsting Date: Fri, 25 Oct 2024 12:53:19 +0200 Subject: [PATCH 10/10] removed tmp dir usage --- examples/advanced/gpboost.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/advanced/gpboost.py b/examples/advanced/gpboost.py index 916807d7..3137d9e4 100644 --- a/examples/advanced/gpboost.py +++ b/examples/advanced/gpboost.py @@ -79,7 +79,7 @@ def get_mock_data(): X, y, clst = get_mock_data() # define project folder - project_folder = "/tmp/gpboost_debug" + project_folder = "./tmp/gpboost_debug" my_pipe = get_gpboost_pipe("Test_gpboost", project_folder, split="random") my_pipe.fit(X, y, clusters=clst)