Skip to content

Commit 52620eb

Browse files
authored
Merge branch 'main' into fix-dataset-root-3088
2 parents 5116a3b + ff28266 commit 52620eb

File tree

19 files changed

+358
-103
lines changed

19 files changed

+358
-103
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ repos:
2727
args: [ "--create", "--python-folders", "aeon" ]
2828

2929
- repo: https://github.com/astral-sh/ruff-pre-commit
30-
rev: v0.14.4
30+
rev: v0.14.5
3131
hooks:
3232
- id: ruff
3333
args: [ "--fix"]
@@ -53,7 +53,7 @@ repos:
5353
args: [ "--max-line-length=88", "--extend-ignore=E203" ]
5454

5555
- repo: https://github.com/psf/black
56-
rev: 25.9.0
56+
rev: 25.11.0
5757
hooks:
5858
- id: black
5959
language_version: python3

aeon/anomaly_detection/series/distance_based/_rockad.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class ROCKAD(BaseSeriesAnomalyDetector):
8585
>>> detector.fit(X_train)
8686
ROCKAD(...)
8787
>>> detector.predict(X_test)
88-
array([0. , 0.00554713, 0.0699094 , 0.22881059, 0.32382585,
88+
array([0. , 0.00554713, 0.06990941, 0.22881059, 0.32382585,
8989
0.43652154, 0.43652154, 0.43652154, 0.43652154, 0.43652154,
9090
0.43652154, 0.43652154, 0.43652154, 0.43652154, 0.43652154,
9191
0.52382585, 0.65200875, 0.80313368, 0.85194345, 1. ])

aeon/classification/deep_learning/_inception_time.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""InceptionTime and Inception classifiers."""
22

3+
from __future__ import annotations
4+
35
__maintainer__ = ["hadifawaz1999"]
46
__all__ = ["InceptionTimeClassifier"]
57

@@ -251,7 +253,7 @@ def __init__(
251253

252254
super().__init__()
253255

254-
def _fit(self, X, y):
256+
def _fit(self, X: np.ndarray, y: np.ndarray) -> InceptionTimeClassifier:
255257
"""Fit the ensemble of IndividualInceptionClassifier models.
256258
257259
Parameters
@@ -307,7 +309,7 @@ def _fit(self, X, y):
307309

308310
return self
309311

310-
def _predict(self, X) -> np.ndarray:
312+
def _predict(self, X: np.ndarray) -> np.ndarray:
311313
"""Predict the labels of the test set using InceptionTime.
312314
313315
Parameters
@@ -328,7 +330,7 @@ def _predict(self, X) -> np.ndarray:
328330
]
329331
)
330332

331-
def _predict_proba(self, X) -> np.ndarray:
333+
def _predict_proba(self, X: np.ndarray) -> np.ndarray:
332334
"""Predict the proba of labels of the test set using InceptionTime.
333335
334336
Parameters
@@ -351,24 +353,30 @@ def _predict_proba(self, X) -> np.ndarray:
351353
return probs
352354

353355
@classmethod
354-
def load_model(self, model_path, classes):
355-
"""Load pre-trained classifiers instead of fitting.
356+
def load_model(
357+
self, model_path: list[str], classes: np.ndarray
358+
) -> InceptionTimeClassifier:
359+
"""Load pre-trained keras models from disk instead of fitting.
356360
361+
Pretrained models should be saved using "save_best_model"
362+
or "save_last_model" boolean parameter.
357363
When calling this function, all functionalities can be used
358-
such as predict, predict_proba, etc. with the loaded models.
364+
such as predict, predict_proba etc. with the loaded model.
359365
360366
Parameters
361367
----------
362368
model_path : list of str (list of paths including the model names and extension)
363-
The directory where the models will be saved including the model
364-
names with a ".keras" extension.
365-
classes : np.ndarray
369+
The complete path (including file name and '.keras' extension)
370+
from which the pre-trained model's weights and configuration
371+
are loaded.
372+
classes : np.ndarray
366373
The set of unique classes the pre-trained loaded model is trained
367374
to predict during the classification task.
375+
Example: model_path="path/to/file/best_model.keras"
368376
369377
Returns
370378
-------
371-
None
379+
InceptionTimeClassifier
372380
"""
373381
assert (
374382
type(model_path) is list

aeon/classification/deep_learning/_lite_time.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""LITETime and LITE classifiers."""
22

3+
from __future__ import annotations
4+
35
__maintainer__ = ["hadifawaz1999"]
46
__all__ = ["LITETimeClassifier"]
57

@@ -193,7 +195,7 @@ def __init__(
193195

194196
super().__init__()
195197

196-
def _fit(self, X, y):
198+
def _fit(self, X: np.ndarray, y: np.ndarray) -> LITETimeClassifier:
197199
"""Fit the ensemble of IndividualLITEClassifier models.
198200
199201
Parameters
@@ -239,7 +241,7 @@ def _fit(self, X, y):
239241

240242
return self
241243

242-
def _predict(self, X) -> np.ndarray:
244+
def _predict(self, X: np.ndarray) -> np.ndarray:
243245
"""Predict the labels of the test set using LITETime.
244246
245247
Parameters
@@ -260,7 +262,7 @@ def _predict(self, X) -> np.ndarray:
260262
]
261263
)
262264

263-
def _predict_proba(self, X) -> np.ndarray:
265+
def _predict_proba(self, X: np.ndarray) -> np.ndarray:
264266
"""Predict the proba of labels of the test set using LITETime.
265267
266268
Parameters
@@ -283,24 +285,30 @@ def _predict_proba(self, X) -> np.ndarray:
283285
return probs
284286

285287
@classmethod
286-
def load_model(self, model_path, classes):
287-
"""Load pre-trained classifiers instead of fitting.
288+
def load_model(
289+
self, model_path: list[str], classes: np.ndarray
290+
) -> LITETimeClassifier:
291+
"""Load pre-trained keras models from disk instead of fitting.
288292
293+
Pretrained models should be saved using "save_best_model"
294+
or "save_last_model" boolean parameter.
289295
When calling this function, all functionalities can be used
290-
such as predict, predict_proba, etc. with the loaded models.
296+
such as predict, predict_proba etc. with the loaded model.
291297
292298
Parameters
293299
----------
294300
model_path : list of str (list of paths including the model names and extension)
295-
The director where the models will be saved including the model
296-
names with a ".keras" extension.
297-
classes : np.ndarray
301+
The complete path (including file name and '.keras' extension)
302+
from which the pre-trained model's weights and configuration
303+
are loaded.
304+
classes : np.ndarray
298305
The set of unique classes the pre-trained loaded model is trained
299306
to predict during the classification task.
307+
Example: model_path="path/to/file/best_model.keras"
300308
301309
Returns
302310
-------
303-
None
311+
LITETimeClassifier
304312
"""
305313
assert (
306314
type(model_path) is list

aeon/classification/deep_learning/base.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class name: BaseDeepClassifier
2222
because we can generalise tags, _predict and _predict_proba
2323
"""
2424

25+
from __future__ import annotations
26+
2527
__maintainer__ = ["hadifawaz1999"]
2628
__all__ = ["BaseDeepClassifier"]
2729

@@ -178,17 +180,20 @@ def save_last_model_to_file(self, file_path="./"):
178180
"""
179181
self.model_.save(file_path + self.last_file_name + ".keras")
180182

181-
def load_model(self, model_path, classes):
183+
def load_model(self, model_path: str, classes: np.ndarray) -> None:
182184
"""Load a pre-trained keras model instead of fitting.
183185
186+
Pretrained model should be saved using "save_last_model" or
187+
"save_best_model" boolean parameter.
184188
When calling this function, all functionalities can be used
185189
such as predict, predict_proba etc. with the loaded model.
186190
187191
Parameters
188192
----------
189193
model_path : str (path including model name and extension)
190-
The directory where the model will be saved including the model
191-
name with a ".keras" extension.
194+
The complete path (including file name and '.keras' extension)
195+
from which the pre-trained model's weights and configuration
196+
are loaded.
192197
Example: model_path="path/to/file/best_model.keras"
193198
classes : np.ndarray
194199
The set of unique classes the pre-trained loaded model is trained

aeon/clustering/averaging/_ba_petitjean.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def petitjean_barycenter_average(
186186
return barycenter
187187

188188

189-
@njit(cache=True, fastmath=True)
189+
@njit(cache=True, fastmath=True, parallel=True)
190190
def _ba_one_iter_petitjean(
191191
barycenter: np.ndarray,
192192
X: np.ndarray,

aeon/clustering/averaging/_kasba_average.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def _kasba_refine_one_iter(
248248
for i in shuffled_indices:
249249
curr_ts = X[i]
250250
curr_alignment, _ = _get_alignment_path(
251-
center=barycenter,
251+
center=barycenter_copy,
252252
ts=curr_ts,
253253
distance=distance,
254254
window=window,

aeon/clustering/averaging/tests/test_kasba.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ def test_kasba_ba_expected():
6363
],
6464
)
6565
def test_kasba_ba_uni(distance, init_barycenter):
66-
"""Test kasba dba functionality."""
66+
"""Test kasba ba functionality."""
6767
distance = distance[0]
68-
X_train_uni = make_example_3d_numpy(10, 1, 10, random_state=1, return_y=False)
68+
X_train_uni = make_example_3d_numpy(20, 1, 10, random_state=1, return_y=False)
6969

7070
params = {
7171
"window": 0.2,
@@ -92,7 +92,6 @@ def test_kasba_ba_uni(distance, init_barycenter):
9292
assert average_ts_uni.shape == X_train_uni[0].shape
9393
assert np.allclose(average_ts_uni, call_directly_average_ts_uni)
9494

95-
# EDR and shape_dtw with random values don't update the barycenter so skipping
9695
if distance not in ["shape_dtw", "edr"]:
9796
# Test not just returning the init barycenter
9897
assert not np.array_equal(average_ts_uni, init_barycenter)
@@ -144,7 +143,11 @@ def test_kasba_distance_params(distance):
144143
"""Test kasba with various distance parameters."""
145144
distance_params = distance[1]
146145
distance = distance[0]
147-
X_train_uni = make_example_3d_numpy(10, 1, 10, random_state=1, return_y=False)
146+
if distance == "soft_dtw":
147+
# Skip for now and add back when soft-dtw refactored
148+
return
149+
150+
X_train_uni = make_example_3d_numpy(20, 1, 10, random_state=1, return_y=False)
148151

149152
for key in distance_params:
150153
curr_param = {key: distance_params[key]}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Tests for loading Keras models in deep autoencoder clusterers."""
2+
3+
import tempfile
4+
from pathlib import Path
5+
6+
import numpy as np
7+
import pytest
8+
9+
from aeon.clustering.deep_learning._ae_abgru import AEAttentionBiGRUClusterer
10+
from aeon.clustering.deep_learning._ae_bgru import AEBiGRUClusterer
11+
from aeon.clustering.deep_learning._ae_dcnn import AEDCNNClusterer
12+
from aeon.clustering.deep_learning._ae_drnn import AEDRNNClusterer
13+
from aeon.clustering.deep_learning._ae_fcn import AEFCNClusterer
14+
from aeon.clustering.deep_learning._ae_resnet import AEResNetClusterer
15+
from aeon.utils.validation._dependencies import _check_soft_dependencies
16+
17+
ALL_DEEP_CLUSTERERS = [
18+
AEAttentionBiGRUClusterer,
19+
AEBiGRUClusterer,
20+
AEDCNNClusterer,
21+
AEDRNNClusterer,
22+
AEFCNClusterer,
23+
AEResNetClusterer,
24+
]
25+
26+
27+
@pytest.mark.skipif(
28+
not _check_soft_dependencies("tensorflow", severity="none"),
29+
reason="TensorFlow not installed.",
30+
)
31+
@pytest.mark.parametrize("cls", ALL_DEEP_CLUSTERERS)
32+
def test_deep_clusterer_load_model(cls):
33+
"""Test that all deep autoencoder clusterers load saved Keras models correctly."""
34+
X = np.random.randn(4, 10, 1).astype(np.float32)
35+
params = cls._get_test_params()[0]
36+
params["n_epochs"] = 1
37+
params["save_best_model"] = True
38+
39+
with tempfile.TemporaryDirectory() as tmp:
40+
params["file_path"] = tmp + "/"
41+
model = cls(**params)
42+
model.fit(X)
43+
trained_estimator = model._estimator
44+
saved = list(Path(tmp).glob("*.keras"))
45+
assert saved, f"No .keras file saved for {cls.__name__}"
46+
model_path = str(saved[0])
47+
loaded = cls(**params)
48+
loaded.load_model(model_path, trained_estimator)
49+
assert loaded.model_ is not None, f"Loaded model_ is None for {cls.__name__}"
50+
assert hasattr(loaded.model_, "predict"), f"Invalid model_ for {cls.__name__}"
51+
preds = loaded.predict(X)
52+
assert preds.shape[0] == X.shape[0], f"Predict failed for {cls.__name__}"

aeon/clustering/tests/test_k_shape.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
from aeon.datasets import load_basic_motions
88
from aeon.utils.validation._dependencies import _check_estimator_deps
99

10-
expected_results = [2, 2, 2, 0, 0]
10+
expected_results = [1, 1, 1, 2, 2]
1111

12-
inertia = 0.5645477840468736
12+
inertia = 0.5753409783429747
1313

14-
expected_iters = 2
14+
expected_iters = 4
1515

16-
expected_labels = [0, 2, 1, 1, 1]
16+
expected_labels = [2, 1, 0, 2, 2]
1717

1818

1919
@pytest.mark.skipif(

0 commit comments

Comments
 (0)