From 9ae88d80d93f224200c771091d9464e2f1e6a856 Mon Sep 17 00:00:00 2001 From: Ramana-Raja Date: Fri, 2 May 2025 15:33:06 +0530 Subject: [PATCH 1/4] added load_model function and test cases for it --- .../deep_learning/_inception_time.py | 31 +++++++++++ aeon/regression/deep_learning/_lite_time.py | 31 +++++++++++ .../tests/test_inception_time.py | 51 +++++++++++++++++++ .../deep_learning/tests/test_lite_time.py | 51 +++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 aeon/regression/deep_learning/tests/test_inception_time.py create mode 100644 aeon/regression/deep_learning/tests/test_lite_time.py diff --git a/aeon/regression/deep_learning/_inception_time.py b/aeon/regression/deep_learning/_inception_time.py index 96e8a38362..c22a5db222 100644 --- a/aeon/regression/deep_learning/_inception_time.py +++ b/aeon/regression/deep_learning/_inception_time.py @@ -336,6 +336,37 @@ def _predict(self, X) -> np.ndarray: return ypreds + @classmethod + def load_model(cls, model_paths): + """ + Load pre-trained regressors instead of fitting. + + This enables full use of the estimator's functionality such as predict. + + Parameters + ---------- + model_paths : list of str (list of paths including the model names and extension) + List of file paths to the saved .keras models of each regressor. + + Returns + ------- + InceptionTimeRegressor + An instance of InceptionTimeRegressor with the pre-trained models loaded. + """ + assert isinstance(model_paths, list), "model_paths should be a list of paths to the models" + + regressor = cls(n_regressors=len(model_paths)) + regressor.regressors_ = [] + + for i, path in enumerate(model_paths): + # Create a dummy model just to load into + ind_regressor = IndividualInceptionRegressor() + ind_regressor.load_model(path) + regressor.regressors_.append(ind_regressor) + + regressor.is_fitted = True + return regressor + @classmethod def _get_test_params(cls, parameter_set="default"): """Return testing parameter settings for the estimator. diff --git a/aeon/regression/deep_learning/_lite_time.py b/aeon/regression/deep_learning/_lite_time.py index 9af8bbaf4e..8b8dab5114 100644 --- a/aeon/regression/deep_learning/_lite_time.py +++ b/aeon/regression/deep_learning/_lite_time.py @@ -262,6 +262,37 @@ def _predict(self, X) -> np.ndarray: return vals + @classmethod + def load_model(cls, model_paths): + """ + Load pre-trained regressors instead of fitting. + + This enables full use of the estimator's functionality such as predict. + + Parameters + ---------- + model_paths : list of str (list of paths including the model names and extension) + List of file paths to the saved .keras models of each regressor. + + Returns + ------- + LITETimeRegressor + An instance of LITETimeRegressor with the pre-trained models loaded. + """ + assert isinstance(model_paths, list), "model_paths should be a list of paths to the models" + + regressor = cls(n_regressors=len(model_paths)) + regressor.regressors_ = [] + + for i, path in enumerate(model_paths): + # Create a dummy model just to load into + ind_regressor = IndividualLITERegressor() + ind_regressor.load_model(path) + regressor.regressors_.append(ind_regressor) + + regressor.is_fitted = True + return regressor + @classmethod def _get_test_params(cls, parameter_set="default"): """Return testing parameter settings for the estimator. diff --git a/aeon/regression/deep_learning/tests/test_inception_time.py b/aeon/regression/deep_learning/tests/test_inception_time.py new file mode 100644 index 0000000000..e186fd5793 --- /dev/null +++ b/aeon/regression/deep_learning/tests/test_inception_time.py @@ -0,0 +1,51 @@ +"""Tests for save/load functionality of InceptionTimeRegressor.""" + +import glob +import os +import tempfile + +import numpy as np +import pytest + +from aeon.regression.deep_learning import InceptionTimeRegressor +from aeon.testing.data_generation import make_example_3d_numpy +from aeon.utils.validation._dependencies import _check_soft_dependencies + + +@pytest.mark.skipif( + not _check_soft_dependencies("tensorflow", severity="none"), + reason="skip test if required soft dependency not available", +) +def test_save_load_inceptiontime_regressor(): + """Test saving and loading for InceptionTimeRegressor.""" + with tempfile.TemporaryDirectory() as temp: + temp_dir = os.path.join(temp, "") + + X, y = make_example_3d_numpy( + n_cases=10, n_channels=1, n_timepoints=12, return_y=True, regression_target=True + ) + + model = InceptionTimeRegressor( + n_epochs=1, + random_state=42, + save_best_model=True, + file_path=temp_dir, + n_regressors=1, + ) + model.fit(X, y) + + y_pred_orig = model.predict(X) + + model_files = glob.glob(os.path.join(temp_dir, f"{model.best_file_name}*.keras")) + + loaded_model = InceptionTimeRegressor.load_model( + model_paths=model_files + ) + + assert isinstance(loaded_model, InceptionTimeRegressor) + + preds = loaded_model.predict(X) + assert isinstance(preds, np.ndarray) + + assert len(preds) == len(y) + np.testing.assert_array_almost_equal(preds, y_pred_orig, decimal=4) diff --git a/aeon/regression/deep_learning/tests/test_lite_time.py b/aeon/regression/deep_learning/tests/test_lite_time.py new file mode 100644 index 0000000000..1a51cb1efc --- /dev/null +++ b/aeon/regression/deep_learning/tests/test_lite_time.py @@ -0,0 +1,51 @@ +"""Tests for save/load functionality of LITETimeRegressor.""" + +import glob +import os +import tempfile + +import numpy as np +import pytest + +from aeon.regression.deep_learning import LITETimeRegressor +from aeon.testing.data_generation import make_example_3d_numpy +from aeon.utils.validation._dependencies import _check_soft_dependencies + + +@pytest.mark.skipif( + not _check_soft_dependencies("tensorflow", severity="none"), + reason="skip test if required soft dependency not available", +) +def test_save_load_litetimeregressor_regressor(): + """Test saving and loading for LITETimeRegressor.""" + with tempfile.TemporaryDirectory() as temp: + temp_dir = os.path.join(temp, "") + + X, y = make_example_3d_numpy( + n_cases=10, n_channels=1, n_timepoints=12, return_y=True, regression_target=True + ) + + model = LITETimeRegressor( + n_epochs=1, + random_state=42, + save_best_model=True, + file_path=temp_dir, + n_regressors=1, + ) + model.fit(X, y) + + y_pred_orig = model.predict(X) + + model_files = glob.glob(os.path.join(temp_dir, f"{model.best_file_name}*.keras")) + + loaded_model = LITETimeRegressor.load_model( + model_paths=model_files + ) + + assert isinstance(loaded_model, LITETimeRegressor) + + preds = loaded_model.predict(X) + assert isinstance(preds, np.ndarray) + + assert len(preds) == len(y) + np.testing.assert_array_almost_equal(preds, y_pred_orig, decimal=4) From 6e5a1e15b037bf83a5c8242bef6dffd3c66b2735 Mon Sep 17 00:00:00 2001 From: Ramana-Raja <83065061+Ramana-Raja@users.noreply.github.com> Date: Fri, 2 May 2025 10:07:45 +0000 Subject: [PATCH 2/4] Automatic `pre-commit` fixes --- aeon/regression/deep_learning/_inception_time.py | 4 +++- aeon/regression/deep_learning/_lite_time.py | 4 +++- .../deep_learning/tests/test_inception_time.py | 14 +++++++++----- .../deep_learning/tests/test_lite_time.py | 14 +++++++++----- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/aeon/regression/deep_learning/_inception_time.py b/aeon/regression/deep_learning/_inception_time.py index c22a5db222..819952619e 100644 --- a/aeon/regression/deep_learning/_inception_time.py +++ b/aeon/regression/deep_learning/_inception_time.py @@ -353,7 +353,9 @@ def load_model(cls, model_paths): InceptionTimeRegressor An instance of InceptionTimeRegressor with the pre-trained models loaded. """ - assert isinstance(model_paths, list), "model_paths should be a list of paths to the models" + assert isinstance( + model_paths, list + ), "model_paths should be a list of paths to the models" regressor = cls(n_regressors=len(model_paths)) regressor.regressors_ = [] diff --git a/aeon/regression/deep_learning/_lite_time.py b/aeon/regression/deep_learning/_lite_time.py index 8b8dab5114..64eea24a1b 100644 --- a/aeon/regression/deep_learning/_lite_time.py +++ b/aeon/regression/deep_learning/_lite_time.py @@ -279,7 +279,9 @@ def load_model(cls, model_paths): LITETimeRegressor An instance of LITETimeRegressor with the pre-trained models loaded. """ - assert isinstance(model_paths, list), "model_paths should be a list of paths to the models" + assert isinstance( + model_paths, list + ), "model_paths should be a list of paths to the models" regressor = cls(n_regressors=len(model_paths)) regressor.regressors_ = [] diff --git a/aeon/regression/deep_learning/tests/test_inception_time.py b/aeon/regression/deep_learning/tests/test_inception_time.py index e186fd5793..9d88cde70b 100644 --- a/aeon/regression/deep_learning/tests/test_inception_time.py +++ b/aeon/regression/deep_learning/tests/test_inception_time.py @@ -22,7 +22,11 @@ def test_save_load_inceptiontime_regressor(): temp_dir = os.path.join(temp, "") X, y = make_example_3d_numpy( - n_cases=10, n_channels=1, n_timepoints=12, return_y=True, regression_target=True + n_cases=10, + n_channels=1, + n_timepoints=12, + return_y=True, + regression_target=True, ) model = InceptionTimeRegressor( @@ -36,12 +40,12 @@ def test_save_load_inceptiontime_regressor(): y_pred_orig = model.predict(X) - model_files = glob.glob(os.path.join(temp_dir, f"{model.best_file_name}*.keras")) - - loaded_model = InceptionTimeRegressor.load_model( - model_paths=model_files + model_files = glob.glob( + os.path.join(temp_dir, f"{model.best_file_name}*.keras") ) + loaded_model = InceptionTimeRegressor.load_model(model_paths=model_files) + assert isinstance(loaded_model, InceptionTimeRegressor) preds = loaded_model.predict(X) diff --git a/aeon/regression/deep_learning/tests/test_lite_time.py b/aeon/regression/deep_learning/tests/test_lite_time.py index 1a51cb1efc..554c647ea8 100644 --- a/aeon/regression/deep_learning/tests/test_lite_time.py +++ b/aeon/regression/deep_learning/tests/test_lite_time.py @@ -22,7 +22,11 @@ def test_save_load_litetimeregressor_regressor(): temp_dir = os.path.join(temp, "") X, y = make_example_3d_numpy( - n_cases=10, n_channels=1, n_timepoints=12, return_y=True, regression_target=True + n_cases=10, + n_channels=1, + n_timepoints=12, + return_y=True, + regression_target=True, ) model = LITETimeRegressor( @@ -36,12 +40,12 @@ def test_save_load_litetimeregressor_regressor(): y_pred_orig = model.predict(X) - model_files = glob.glob(os.path.join(temp_dir, f"{model.best_file_name}*.keras")) - - loaded_model = LITETimeRegressor.load_model( - model_paths=model_files + model_files = glob.glob( + os.path.join(temp_dir, f"{model.best_file_name}*.keras") ) + loaded_model = LITETimeRegressor.load_model(model_paths=model_files) + assert isinstance(loaded_model, LITETimeRegressor) preds = loaded_model.predict(X) From d9c5abeecd4257f831da8a7f4f4fcfe6924849e9 Mon Sep 17 00:00:00 2001 From: Ramana-Raja Date: Fri, 2 May 2025 15:38:08 +0530 Subject: [PATCH 3/4] some docs improvement --- aeon/regression/deep_learning/_inception_time.py | 1 - aeon/regression/deep_learning/_lite_time.py | 1 - 2 files changed, 2 deletions(-) diff --git a/aeon/regression/deep_learning/_inception_time.py b/aeon/regression/deep_learning/_inception_time.py index c22a5db222..e3197c9511 100644 --- a/aeon/regression/deep_learning/_inception_time.py +++ b/aeon/regression/deep_learning/_inception_time.py @@ -359,7 +359,6 @@ def load_model(cls, model_paths): regressor.regressors_ = [] for i, path in enumerate(model_paths): - # Create a dummy model just to load into ind_regressor = IndividualInceptionRegressor() ind_regressor.load_model(path) regressor.regressors_.append(ind_regressor) diff --git a/aeon/regression/deep_learning/_lite_time.py b/aeon/regression/deep_learning/_lite_time.py index 8b8dab5114..7a3f9d031c 100644 --- a/aeon/regression/deep_learning/_lite_time.py +++ b/aeon/regression/deep_learning/_lite_time.py @@ -285,7 +285,6 @@ def load_model(cls, model_paths): regressor.regressors_ = [] for i, path in enumerate(model_paths): - # Create a dummy model just to load into ind_regressor = IndividualLITERegressor() ind_regressor.load_model(path) regressor.regressors_.append(ind_regressor) From d0fa998f8abdbd0dad2f2c5cc57ff36785fa3ed5 Mon Sep 17 00:00:00 2001 From: Ramana-Raja Date: Fri, 2 May 2025 15:43:28 +0530 Subject: [PATCH 4/4] some improvements --- aeon/regression/deep_learning/_inception_time.py | 5 +++-- aeon/regression/deep_learning/_lite_time.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/aeon/regression/deep_learning/_inception_time.py b/aeon/regression/deep_learning/_inception_time.py index c8810c0487..d29967ae09 100644 --- a/aeon/regression/deep_learning/_inception_time.py +++ b/aeon/regression/deep_learning/_inception_time.py @@ -345,7 +345,8 @@ def load_model(cls, model_paths): Parameters ---------- - model_paths : list of str (list of paths including the model names and extension) + model_paths : list of str (list of paths including the + model names and extension) List of file paths to the saved .keras models of each regressor. Returns @@ -360,7 +361,7 @@ def load_model(cls, model_paths): regressor = cls(n_regressors=len(model_paths)) regressor.regressors_ = [] - for i, path in enumerate(model_paths): + for path in model_paths: ind_regressor = IndividualInceptionRegressor() ind_regressor.load_model(path) regressor.regressors_.append(ind_regressor) diff --git a/aeon/regression/deep_learning/_lite_time.py b/aeon/regression/deep_learning/_lite_time.py index c164af5089..7750094019 100644 --- a/aeon/regression/deep_learning/_lite_time.py +++ b/aeon/regression/deep_learning/_lite_time.py @@ -271,7 +271,8 @@ def load_model(cls, model_paths): Parameters ---------- - model_paths : list of str (list of paths including the model names and extension) + model_paths : list of str (list of paths including the + model names and extension) List of file paths to the saved .keras models of each regressor. Returns @@ -286,7 +287,7 @@ def load_model(cls, model_paths): regressor = cls(n_regressors=len(model_paths)) regressor.regressors_ = [] - for i, path in enumerate(model_paths): + for path in model_paths: ind_regressor = IndividualLITERegressor() ind_regressor.load_model(path) regressor.regressors_.append(ind_regressor)