Repository: aeon-toolkit/aeon
Pull request #2175 (2024-10-10 20:47:30)

[DEP] Regression coverage tests and remove deprecated CNN
part of #1518 

picking up a few lines of coverage by testing input variants handled correctly. 

Also removes the deprecated CNNRegressor, CNNClassifier and CNNNetwork as scheduled 

aeon-actions-bot[bot]: 
## Thank you for contributing to `aeon`

I have added the following labels to this PR based on the title: **[ $\\color{#FEF1BE}{\\textsf{enhancement}}$ ]**.
I would have added the following labels to this PR based on the changes made: **[ $\\color{#7E0206}{\\textsf{regression}}$ ]**, however some package labels are already present.

The [Checks](https://github.com/aeon-toolkit/aeon/pull/2175/checks) tab will show the status of our automated tests. You can click on individual test runs in the tab or "Details" in the panel below to see more information if there is a failure.

If our `pre-commit` code quality check fails, any trivial fixes will automatically be pushed to your PR unless it is a draft.

Don't hesitate to ask questions on the `aeon` [Slack](https://join.slack.com/t/aeon-toolkit/shared_invite/zt-22vwvut29-HDpCu~7VBUozyfL_8j3dLA) channel if you have any.

### PR CI actions

These checkboxes will add labels to enable/disable CI functionality for this PR. This may not take effect immediately, and a new commit may be required to run the new configuration.

- [ ] Run `pre-commit` checks for all files
- [ ] Run `mypy` typecheck tests
- [ ] Run all `pytest` tests and configurations
- [ ] Run all notebook example tests
- [ ] Run numba-disabled `codecov` tests
- [ ] Stop automatic `pre-commit` fixes (always disabled for drafts)
- [ ] Disable numba cache loading
- [ ] Push an empty commit to re-run CI checks
    
review-notebook-app[bot]: Check out this pull request on&nbsp; <a href="https://app.reviewnb.com/aeon-toolkit/aeon/pull/2175"><img align="absmiddle"  alt="ReviewNB" height="28" class="BotMessageButtonImage" src="https://raw.githubusercontent.com/ReviewNB/support/master/images/button_reviewnb.png"/></a> 

 See visual diffs & provide feedback on Jupyter Notebooks. 

---

 <i>Powered by <a href='https://www.reviewnb.com/?utm_source=gh'>ReviewNB</a></i>

Patch:
diff --git a/aeon/benchmarking/results_loaders.py b/aeon/benchmarking/results_loaders.py
index b510eecc..27634439 100644
--- a/aeon/benchmarking/results_loaders.py
+++ b/aeon/benchmarking/results_loaders.py
@@ -34,7 +34,13 @@ NAME_ALIASES = {
         "CIFClassifier",
         "CanonicalIntervalForestClassifier",
     },
-    "CNN": {"cnn", "CNNClassifier", "CNNRegressor"},
+    "CNN": {
+        "cnn",
+        "CNNClassifier",
+        "CNNRegressor",
+        "TimeCNNClassifier",
+        "TimeCNNRegressor",
+    },
     "Catch22": {"catch22", "Catch22Classifier"},
     "DrCIF": {"DrCIF", "DrCIFClassifier", "DrCIFRegressor"},
     "EE": {"ElasticEnsemble", "EEClassifier", "ElasticEnsembleClassifier"},
diff --git a/aeon/classification/deep_learning/__init__.py b/aeon/classification/deep_learning/__init__.py
index ba36690c..a7c1c73f 100644
--- a/aeon/classification/deep_learning/__init__.py
+++ b/aeon/classification/deep_learning/__init__.py
@@ -2,7 +2,6 @@
 
 __all__ = [
     "BaseDeepClassifier",
-    "CNNClassifier",
     "TimeCNNClassifier",
     "EncoderClassifier",
     "FCNClassifier",
@@ -14,7 +13,7 @@ __all__ = [
     "LITETimeClassifier",
     "IndividualLITEClassifier",
 ]
-from aeon.classification.deep_learning._cnn import CNNClassifier, TimeCNNClassifier
+from aeon.classification.deep_learning._cnn import TimeCNNClassifier
 from aeon.classification.deep_learning._encoder import EncoderClassifier
 from aeon.classification.deep_learning._fcn import FCNClassifier
 from aeon.classification.deep_learning._inception_time import (
diff --git a/aeon/classification/deep_learning/_cnn.py b/aeon/classification/deep_learning/_cnn.py
index 6f8a13a5..771e58d0 100644
--- a/aeon/classification/deep_learning/_cnn.py
+++ b/aeon/classification/deep_learning/_cnn.py
@@ -1,345 +1,17 @@
 """Time Convolutional Neural Network (CNN) classifier."""
 
 __maintainer__ = ["hadifawaz1999"]
-__all__ = ["CNNClassifier", "TimeCNNClassifier"]
+__all__ = ["TimeCNNClassifier"]
 
 import gc
 import os
 import time
 from copy import deepcopy
 
-from deprecated.sphinx import deprecated
 from sklearn.utils import check_random_state
 
 from aeon.classification.deep_learning.base import BaseDeepClassifier
-from aeon.networks import CNNNetwork, TimeCNNNetwork
-
-
-# TODO: remove v0.12.0
-@deprecated(
-    version="0.10.0",
-    reason="CNNClassifier has been renamed to TimeCNNClassifier"
-    "and will be removed in 0.12.0.",
-    category=FutureWarning,
-)
-class CNNClassifier(BaseDeepClassifier):
-    """
-    Time Convolutional Neural Network (CNN).
-
-    Adapted from the implementation used in [1]_.
-
-    Parameters
-    ----------
-    n_layers : int, default = 2
-        The number of convolution layers in the network.
-    kernel_size : int or list of int, default = 7
-        Kernel size of convolution layers, if not a list, the same kernel size
-        is used for all layer, len(list) should be n_layers.
-    n_filters : int or list of int, default = [6, 12]
-        Number of filters for each convolution layer, if not a list, the same n_filters
-        is used in all layers.
-    avg_pool_size : int or list of int, default = 3
-        The size of the average pooling layer, if not a list, the same
-        max pooling size is used for all convolution layer.
-    activation : str or list of str, default = "sigmoid"
-        Keras activation function used in the model for each layer, if not a list,
-        the same activation is used for all layers.
-    padding : str or list of str, default = 'valid'
-        The method of padding in convolution layers, if not a list, the same padding
-        used for all convolution layers.
-    strides : int or list of int, default = 1
-        The strides of kernels in the convolution and max pooling layers, if not a
-        list, the same strides are used for all layers.
-    dilation_rate : int or list of int, default = 1
-        The dilation rate of the convolution layers, if not a list, the same dilation
-        rate is used all over the network.
-    use_bias : bool or list of bool, default = True
-        Condition on whether to use bias values for convolution layers,
-        if not a list, the same condition is used for all layers.
-    random_state : int, RandomState instance or None, default=None
-        If `int`, random_state is the seed used by the random number generator;
-        If `RandomState` instance, random_state is the random number generator;
-        If `None`, the random number generator is the `RandomState` instance used
-        by `np.random`.
-        Seeded random number generation can only be guaranteed on CPU processing,
-        GPU processing will be non-deterministic.
-    n_epochs : int, default = 2000
-        The number of epochs to train the model.
-    batch_size : int, default = 16
-        The number of samples per gradient update.
-    verbose : boolean, default = False
-        Whether to output extra information.
-    loss : string, default = "mean_squared_error"
-        Fit parameter for the keras model.
-    optimizer : keras.optimizer, default = keras.optimizers.Adam()
-    metrics : list of strings, default = ["accuracy"]
-    callbacks : keras.callbacks, default = model_checkpoint
-        To save best model on training loss.
-    file_path : file_path for the best model
-        Only used if checkpoint is used as callback.
-    save_best_model : bool, default = False
-        Whether to save the best model, if the modelcheckpoint callback is used by
-        default, this condition, if True, will prevent the automatic deletion of the
-        best saved model from file and the user can choose the file name.
-    save_last_model : bool, default = False
-        Whether to save the last model, last epoch trained, using the base class method
-        save_last_model_to_file.
-    save_init_model : bool, default = False
-        Whether to save the initialization of the  model.
-    best_file_name : str, default = "best_model"
-        The name of the file of the best model, if save_best_model is set to False,
-        this parameter is discarded.
-    last_file_name : str, default = "last_model"
-        The name of the file of the last model, if save_last_model is set to False,
-        this parameter is discarded.
-    init_file_name : str, default = "init_model"
-        The name of the file of the init model, if save_init_model is set to False,
-        this parameter is discarded.
-
-    Notes
-    -----
-    Adapted from the implementation from Fawaz et. al
-    https://github.com/hfawaz/dl-4-tsc/blob/master/classifiers/cnn.py
-
-    References
-    ----------
-    .. [1] Zhao et. al, Convolutional neural networks for time series classification,
-    Journal of Systems Engineering and Electronics, 28(1):2017.
-
-    Examples
-    --------
-    >>> from aeon.classification.deep_learning import CNNClassifier
-    >>> from aeon.datasets import load_unit_test
-    >>> X_train, y_train = load_unit_test(split="train")
-    >>> X_test, y_test = load_unit_test(split="test")
-    >>> cnn = CNNClassifier(n_epochs=20, batch_size=4)  # doctest: +SKIP
-    >>> cnn.fit(X_train, y_train)  # doctest: +SKIP
-    CNNClassifier(...)
-    """
-
-    def __init__(
-        self,
-        n_layers=2,
-        kernel_size=7,
-        n_filters=None,
-        avg_pool_size=3,
-        activation="sigmoid",
-        padding="valid",
-        strides=1,
-        dilation_rate=1,
-        n_epochs=2000,
-        batch_size=16,
-        callbacks=None,
-        file_path="./",
-        save_best_model=False,
-        save_last_model=False,
-        save_init_model=False,
-        best_file_name="best_model",
-        last_file_name="last_model",
-        init_file_name="init_model",
-        verbose=False,
-        loss="mean_squared_error",
-        metrics=None,
-        random_state=None,
-        use_bias=True,
-        optimizer=None,
-    ):
-        self.n_layers = n_layers
-        self.kernel_size = kernel_size
-        self.n_filters = n_filters
-        self.padding = padding
-        self.strides = strides
-        self.dilation_rate = dilation_rate
-        self.avg_pool_size = avg_pool_size
-        self.activation = activation
-        self.use_bias = use_bias
-
-        self.n_epochs = n_epochs
-        self.callbacks = callbacks
-        self.file_path = file_path
-        self.save_best_model = save_best_model
-        self.save_last_model = save_last_model
-        self.save_init_model = save_init_model
-        self.best_file_name = best_file_name
-        self.init_file_name = init_file_name
-        self.verbose = verbose
-        self.loss = loss
-        self.metrics = metrics
-        self.optimizer = optimizer
-
-        self.history = None
-
-        super().__init__(
-            batch_size=batch_size,
-            random_state=random_state,
-            last_file_name=last_file_name,
-        )
-
-        self._network = CNNNetwork(
-            n_layers=self.n_layers,
-            kernel_size=self.kernel_size,
-            n_filters=self.n_filters,
-            avg_pool_size=self.avg_pool_size,
-            activation=self.activation,
-            padding=self.padding,
-            strides=self.strides,
-            dilation_rate=self.dilation_rate,
-            use_bias=self.use_bias,
-        )
-
-    def build_model(self, input_shape, n_classes, **kwargs):
-        """Construct a compiled, un-trained, keras model that is ready for training.
-
-        In aeon, time series are stored in numpy arrays of shape (d, m), where d
-        is the number of dimensions, m is the series length. Keras/tensorflow assume
-        data is in shape (m, d). This method also assumes (m, d). Transpose should
-        happen in fit.
-
-        Parameters
-        ----------
-        input_shape : tuple
-            The shape of the data fed into the input layer, should be (m, d)
-        n_classes : int
-            The number of classes, which becomes the size of the output layer
-
-        Returns
-        -------
-        output : a compiled Keras Model
-        """
-        import numpy as np
-        import tensorflow as tf
-
-        if self.metrics is None:
-            metrics = ["accuracy"]
-        else:
-            metrics = self.metrics
-
-        rng = check_random_state(self.random_state)
-        self.random_state_ = rng.randint(0, np.iinfo(np.int32).max)
-        tf.keras.utils.set_random_seed(self.random_state_)
-        input_layer, output_layer = self._network.build_network(input_shape, **kwargs)
-
-        output_layer = tf.keras.layers.Dense(
-            units=n_classes, activation=self.activation, use_bias=self.use_bias
-        )(output_layer)
-
-        self.optimizer_ = (
-            tf.keras.optimizers.Adam() if self.optimizer is None else self.optimizer
-        )
-
-        model = tf.keras.models.Model(inputs=input_layer, outputs=output_layer)
-        model.compile(
-            loss=self.loss,
-            optimizer=self.optimizer_,
-            metrics=metrics,
-        )
-
-        return model
-
-    def _fit(self, X, y):
-        """Fit the classifier on the training set (X, y).
-
-        Parameters
-        ----------
-        X : np.ndarray
-            The training input samples of shape (n_cases, n_channels, n_timepoints)
-        y : np.ndarray
-            The training data class labels of shape (n_cases,).
-
-
-        Returns
-        -------
-        self : object
-        """
-        import tensorflow as tf
-
-        y_onehot = self.convert_y_to_keras(y)
-        # Transpose to conform to Keras input style.
-        X = X.transpose(0, 2, 1)
-
-        self.input_shape = X.shape[1:]
-        self.training_model_ = self.build_model(self.input_shape, self.n_classes_)
-
-        if self.save_init_model:
-            self.training_model_.save(self.file_path + self.init_file_name + ".keras")
-
-        if self.verbose:
-            self.training_model_.summary()
-
-        self.file_name_ = (
-            self.best_file_name if self.save_best_model else str(time.time_ns())
-        )
-
-        if self.callbacks is None:
-            self.callbacks_ = [
-                tf.keras.callbacks.ModelCheckpoint(
-                    filepath=self.file_path + self.file_name_ + ".keras",
-                    monitor="loss",
-                    save_best_only=True,
-                ),
-            ]
-        else:
-            self.callbacks_ = self._get_model_checkpoint_callback(
-                callbacks=self.callbacks,
-                file_path=self.file_path,
-                file_name=self.file_name_,
-            )
-
-        self.history = self.training_model_.fit(
-            X,
-            y_onehot,
-            batch_size=self.batch_size,
-            epochs=self.n_epochs,
-            verbose=self.verbose,
-            callbacks=self.callbacks_,
-        )
-
-        try:
-            self.model_ = tf.keras.models.load_model(
-                self.file_path + self.file_name_ + ".keras", compile=False
-            )
-            if not self.save_best_model:
-                os.remove(self.file_path + self.file_name_ + ".keras")
-        except FileNotFoundError:
-            self.model_ = deepcopy(self.training_model_)
-
-        if self.save_last_model:
-            self.save_last_model_to_file(file_path=self.file_path)
-
-        gc.collect()
-        return self
-
-    @classmethod
-    def get_test_params(cls, parameter_set="default"):
-        """Return testing parameter settings for the estimator.
-
-        Parameters
-        ----------
-        parameter_set : str, default = "default"
-            Name of the set of test parameters to return, for use in tests. If no
-            special parameters are defined for a value, will return "default" set.
-            For classifiers, a "default" set of parameters should be provided for
-            general testing, and a "results_comparison" set for comparing against
-            previously recorded results if the general set does not produce suitable
-            probabilities to compare against.
-
-        Returns
-        -------
-        params : dict or list of dict, default = {}
-            Parameters to create testing instances of the class.
-            Each dict are parameters to construct an "interesting" test instance, i.e.,
-            `MyClass(**params)` or `MyClass(**params[i])` creates a valid test instance.
-            `create_test_instance` uses the first (or only) dictionary in `params`.
-        """
-        param1 = {
-            "n_epochs": 10,
-            "batch_size": 4,
-            "avg_pool_size": 4,
-        }
-
-        test_params = [param1]
-
-        return test_params
+from aeon.networks import TimeCNNNetwork
 
 
 class TimeCNNClassifier(BaseDeepClassifier):
diff --git a/aeon/networks/__init__.py b/aeon/networks/__init__.py
index dd7b90f8..30cc6b24 100644
--- a/aeon/networks/__init__.py
+++ b/aeon/networks/__init__.py
@@ -3,7 +3,6 @@
 __all__ = [
     "BaseDeepNetwork",
     "BaseDeepLearningNetwork",
-    "CNNNetwork",
     "TimeCNNNetwork",
     "EncoderNetwork",
     "FCNNetwork",
@@ -24,7 +23,7 @@ from aeon.networks._ae_bgru import AEBiGRUNetwork
 from aeon.networks._ae_drnn import AEDRNNNetwork
 from aeon.networks._ae_fcn import AEFCNNetwork
 from aeon.networks._ae_resnet import AEResNetNetwork
-from aeon.networks._cnn import CNNNetwork, TimeCNNNetwork
+from aeon.networks._cnn import TimeCNNNetwork
 from aeon.networks._encoder import EncoderNetwork
 from aeon.networks._fcn import FCNNetwork
 from aeon.networks._inception import InceptionNetwork
diff --git a/aeon/networks/_cnn.py b/aeon/networks/_cnn.py
index 04ad690b..3aae7288 100644
--- a/aeon/networks/_cnn.py
+++ b/aeon/networks/_cnn.py
@@ -2,222 +2,9 @@
 
 __maintainer__ = ["hadifawaz1999"]
 
-from deprecated.sphinx import deprecated
-
 from aeon.networks.base import BaseDeepLearningNetwork
 
 
-# TODO: remove v0.12.0
-@deprecated(
-    version="0.10.0",
-    reason="CNNNetwork has been renamed to TimeCNNNetwork"
-    "and will be removed in 0.12.0.",
-    category=FutureWarning,
-)
-class CNNNetwork(BaseDeepLearningNetwork):
-    """Establish the network structure for a CNN.
-
-    Adapted from the implementation used in [1]_.
-
-    Parameters
-    ----------
-    n_layers : int, default = 2
-        The number of convolution layers in the network.
-    kernel_size : int or list of int, default = 7
-        Kernel size of convolution layers, if not a list, the same kernel size is
-        used for all layer, len(list) should be n_layers.
-    n_filters : int or list of int, default = [6, 12]
-        Number of filters for each convolution layer, if not a list, the same
-        `n_filters` is used in all layers.
-    avg_pool_size : int or list of int, default = 3
-        The size of the average pooling layer, if not a list, the same max pooling
-        size is used for all convolution layer.
-    activation : str or list of str, default = "sigmoid"
-        Keras activation function used in the model for each layer, if not a list,
-        the same activation is used for all layers.
-    padding : str or list of str, default = "valid"
-        The method of padding in convolution layers, if not a list, the same padding
-        used for all convolution layers.
-    strides : int or list of int, default = 1
-        The strides of kernels in the convolution and max pooling layers, if not a list,
-        the same strides are used for all layers.
-    dilation_rate : int or list of int, default = 1
-        The dilation rate of the convolution layers, if not a list, the same dilation
-        rate is used all over the network.
-    use_bias : bool or list of bool, default = True
-        Condition on whether or not to use bias values for convolution layers, if not
-        a list, the same condition is used for all layers.
-
-    Notes
-    -----
-    Adapted from source code
-    https://github.com/hfawaz/dl-4-tsc/blob/master/classifiers/cnn.py
-
-    References
-    ----------
-    .. [1] Zhao et al. Convolutional neural networks for time series classification,
-    Journal of Systems Engineering and Electronics 28(1), 162--169, 2017
-    """
-
-    def __init__(
-        self,
-        n_layers=2,
-        kernel_size=7,
-        n_filters=None,
-        avg_pool_size=3,
-        activation="sigmoid",
-        padding="valid",
-        strides=1,
-        dilation_rate=1,
-        use_bias=True,
-    ):
-        self.n_layers = n_layers
-        self.n_filters = n_filters
-        self.kernel_size = kernel_size
-        self.avg_pool_size = avg_pool_size
-        self.activation = activation
-        self.padding = padding
-        self.strides = strides
-        self.dilation_rate = dilation_rate
-        self.use_bias = use_bias
-
-        super().__init__()
-
-    def build_network(self, input_shape, **kwargs):
-        """
-        Construct a network and return its input and output layers.
-
-        Parameters
-        ----------
-        input_shape : tuple
-            The shape of the data fed into the input layer.
-
-        Returns
-        -------
-        input_layer : a keras layer
-        output_layer : a keras layer
-        """
-        import tensorflow as tf
-
-        self._n_filters_ = [6, 12] if self.n_filters is None else self.n_filters
-
-        if isinstance(self.kernel_size, list):
-            if len(self.kernel_size) != self.n_layers:
-                raise ValueError(
-                    f"Number of kernels {len(self.kernel_size)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._kernel_size = self.kernel_size
-        else:
-            self._kernel_size = [self.kernel_size] * self.n_layers
-
-        if isinstance(self._n_filters_, list):
-            if len(self._n_filters_) != self.n_layers:
-                raise ValueError(
-                    f"Number of filters {len(self._n_filters_)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._n_filters = self._n_filters_
-        else:
-            self._n_filters = [self._n_filters_] * self.n_layers
-
-        if isinstance(self.avg_pool_size, list):
-            if len(self.avg_pool_size) != self.n_layers:
-                raise ValueError(
-                    f"Number of average pools {len(self.avg_pool_size)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._avg_pool_size = self.avg_pool_size
-        else:
-            self._avg_pool_size = [self.avg_pool_size] * self.n_layers
-
-        if isinstance(self.activation, list):
-            if len(self.activation) != self.n_layers:
-                raise ValueError(
-                    f"Number of activations {len(self.activation)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._activation = self.activation
-        else:
-            self._activation = [self.activation] * self.n_layers
-
-        if isinstance(self.padding, list):
-            if len(self.padding) != self.n_layers:
-                raise ValueError(
-                    f"Number of paddings {len(self.padding)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._padding = self.padding
-        else:
-            self._padding = [self.padding] * self.n_layers
-
-        if isinstance(self.strides, list):
-            if len(self.strides) != self.n_layers:
-                raise ValueError(
-                    f"Number of strides {len(self.strides)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._strides = self.strides
-        else:
-            self._strides = [self.strides] * self.n_layers
-
-        if isinstance(self.dilation_rate, list):
-            if len(self.dilation_rate) != self.n_layers:
-                raise ValueError(
-                    f"Number of dilations {len(self.dilation_rate)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._dilation_rate = self.dilation_rate
-        else:
-            self._dilation_rate = [self.dilation_rate] * self.n_layers
-
-        if isinstance(self.use_bias, list):
-            if len(self.use_bias) != self.n_layers:
-                raise ValueError(
-                    f"Number of biases {len(self.use_bias)} should be"
-                    f" the same as number of layers but is"
-                    f" not: {self.n_layers}"
-                )
-            self._use_bias = self.use_bias
-        else:
-            self._use_bias = [self.use_bias] * self.n_layers
-
-        input_layer = tf.keras.layers.Input(input_shape)
-
-        if input_shape[0] < 60:
-            self._padding = ["same"] * self.n_layers
-
-        x = input_layer
-
-        for i in range(self.n_layers):
-            conv = tf.keras.layers.Conv1D(
-                filters=self._n_filters[i],
-                kernel_size=self._kernel_size[i],
-                strides=self._strides[i],
-                padding=self._padding[i],
-                dilation_rate=self._dilation_rate[i],
-                activation=self._activation[i],
-                use_bias=self._use_bias[i],
-            )(x)
-
-            conv = tf.keras.layers.AveragePooling1D(pool_size=self._avg_pool_size[i])(
-                conv
-            )
-
-            x = conv
-
-        flatten_layer = tf.keras.layers.Flatten()(conv)
-
-        return input_layer, flatten_layer
-
-
 class TimeCNNNetwork(BaseDeepLearningNetwork):
     """Establish the network structure for a CNN.
 
diff --git a/aeon/regression/deep_learning/__init__.py b/aeon/regression/deep_learning/__init__.py
index eb69f1e0..0ea28dfe 100644
--- a/aeon/regression/deep_learning/__init__.py
+++ b/aeon/regression/deep_learning/__init__.py
@@ -2,7 +2,6 @@
 
 __all__ = [
     "BaseDeepRegressor",
-    "CNNRegressor",
     "TimeCNNRegressor",
     "FCNRegressor",
     "InceptionTimeRegressor",
@@ -15,7 +14,7 @@ __all__ = [
     "MLPRegressor",
 ]
 
-from aeon.regression.deep_learning._cnn import CNNRegressor, TimeCNNRegressor
+from aeon.regression.deep_learning._cnn import TimeCNNRegressor
 from aeon.regression.deep_learning._encoder import EncoderRegressor
 from aeon.regression.deep_learning._fcn import FCNRegressor
 from aeon.regression.deep_learning._inception_time import (
diff --git a/aeon/regression/deep_learning/_cnn.py b/aeon/regression/deep_learning/_cnn.py
index 988e823c..58f0f4a5 100644
--- a/aeon/regression/deep_learning/_cnn.py
+++ b/aeon/regression/deep_learning/_cnn.py
@@ -1,355 +1,19 @@
 """Time Convolutional Neural Network (TimeCNN) regressor."""
 
 __maintainer__ = ["hadifawaz1999"]
-__all__ = ["CNNRegressor", "TimeCNNRegressor"]
+__all__ = ["TimeCNNRegressor"]
 
 import gc
 import os
 import time
 from copy import deepcopy
 
-from deprecated.sphinx import deprecated
 from sklearn.utils import check_random_state
 
-from aeon.networks import CNNNetwork, TimeCNNNetwork
+from aeon.networks import TimeCNNNetwork
 from aeon.regression.deep_learning.base import BaseDeepRegressor
 
 
-# TODO: remove v0.12.0
-@deprecated(
-    version="0.10.0",
-    reason="CNNRegressor has been renamed to TimeCNNRegressor"
-    "and will be removed in 0.12.0.",
-    category=FutureWarning,
-)
-class CNNRegressor(BaseDeepRegressor):
-    """Time Series Convolutional Neural Network (CNN).
-
-    Adapted from the implementation used in [1]_.
-
-    Parameters
-    ----------
-    n_layers        : int, default = 2,
-        the number of convolution layers in the network
-    kernel_size    : int or list of int, default = 7,
-        kernel size of convolution layers, if not a list, the same kernel size
-        is used for all layer, len(list) should be n_layers
-    n_filters       : int or list of int, default = [6, 12],
-        number of filters for each convolution layer, if not a list, the same n_filters
-        is used in all layers.
-    avg_pool_size   : int or list of int, default = 3,
-        the size of the average pooling layer, if not a list, the same
-        max pooling size is used
-        for all convolution layer
-    output_activation   : str, default = "linear",
-        the output activation for the regressor
-    activation      : str or list of str, default = "sigmoid",
-        keras activation function used in the model for each layer,
-        if not a list, the same
-        activation is used for all layers
-    padding         : str or list of str, default = 'valid',
-        the method of padding in convolution layers, if not a list,
-        the same padding used
-        for all convolution layers
-    strides         : int or list of int, default = 1,
-        the strides of kernels in the convolution and max pooling layers,
-        if not a list, the same strides are used for all layers
-    dilation_rate   : int or list of int, default = 1,
-        the dilation rate of the convolution layers, if not a list,
-        the same dilation rate is used all over the network
-    use_bias        : bool or list of bool, default = True,
-        condition on whether or not to use bias values for convolution layers,
-        if not a list, the same condition is used for all layers
-    random_state : int, RandomState instance or None, default=None
-        If `int`, random_state is the seed used by the random number generator;
-        If `RandomState` instance, random_state is the random number generator;
-        If `None`, the random number generator is the `RandomState` instance used
-        by `np.random`.
-        Seeded random number generation can only be guaranteed on CPU processing,
-        GPU processing will be non-deterministic.
-    n_epochs       : int, default = 2000
-        the number of epochs to train the model
-    batch_size      : int, default = 16
-        the number of samples per gradient update.
-    verbose         : boolean, default = False
-        whether to output extra information
-    loss            : string, default="mean_squared_error"
-        fit parameter for the keras model
-    optimizer       : keras.optimizer, default=keras.optimizers.Adam(),
-    metrics         : str or list of str, default="mean_squared_error"
-        The evaluation metrics to use during training. If
-        a single string metric is provided, it will be
-        used as the only metric. If a list of metrics are
-        provided, all will be used for evaluation.
-    callbacks       : keras.callbacks, default=model_checkpoint to save best
-                      model on training loss
-    file_path       : file_path for the best model (if checkpoint is used as callback)
-    save_best_model     : bool, default = False
-        Whether or not to save the best model, if the
-        modelcheckpoint callback is used by default,
-        this condition, if True, will prevent the
-        automatic deletion of the best saved model from
-        file and the user can choose the file name
-    save_last_model     : bool, default = False
-        Whether or not to save the last model, last
-        epoch trained, using the base class method
-        save_last_model_to_file
-    save_init_model : bool, default = False
-        Whether to save the initialization of the  model.
-    best_file_name      : str, default = "best_model"
-        The name of the file of the best model, if
-        save_best_model is set to False, this parameter
-        is discarded
-    last_file_name      : str, default = "last_model"
-        The name of the file of the last model, if
-        save_last_model is set to False, this parameter
-        is discarded
-    init_file_name : str, default = "init_model"
-        The name of the file of the init model, if save_init_model is set to False,
-        this parameter is discarded.
-
-    Notes
-    -----
-    Adapted from the implementation from Fawaz et. al
-    https://github.com/hfawaz/dl-4-tsc/blob/master/classifiers/cnn.py
-
-    References
-    ----------
-    .. [1] Zhao et. al, Convolutional neural networks for time series classification,
-    Journal of Systems Engineering and Electronics, 28(1):2017.
-
-    Examples
-    --------
-    >>> from aeon.regression.deep_learning import CNNRegressor
-    >>> from aeon.testing.data_generation import make_example_3d_numpy
-    >>> X, y = make_example_3d_numpy(n_cases=10, n_channels=1, n_timepoints=12,
-    ...                              return_y=True, regression_target=True,
-    ...                              random_state=0)
-    >>> rgs = CNNRegressor(n_epochs=20, bacth_size=4) # doctest: +SKIP
-    >>> rgs.fit(X, y) # doctest: +SKIP
-    CNNRegressor(...)
-    """
-
-    def __init__(
-        self,
-        n_layers=2,
-        kernel_size=7,
-        n_filters=None,
-        avg_pool_size=3,
-        activation="sigmoid",
-        padding="valid",
-        strides=1,
-        dilation_rate=1,
-        n_epochs=2000,
-        batch_size=16,
-        callbacks=None,
-        file_path="./",
-        save_best_model=False,
-        save_last_model=False,
-        save_init_model=False,
-        best_file_name="best_model",
-        last_file_name="last_model",
-        init_file_name="init_model",
-        verbose=False,
-        loss="mse",
-        output_activation="linear",
-        metrics="mean_squared_error",
-        random_state=None,
-        use_bias=True,
-        optimizer=None,
-    ):
-        self.n_layers = n_layers
-        self.avg_pool_size = avg_pool_size
-        self.padding = padding
-        self.n_filters = n_filters
-        self.kernel_size = kernel_size
-        self.file_path = file_path
-        self.save_best_model = save_best_model
-        self.save_last_model = save_last_model
-        self.save_init_model = save_init_model
-        self.best_file_name = best_file_name
-        self.init_file_name = init_file_name
-        self.strides = strides
-        self.dilation_rate = dilation_rate
-        self.callbacks = callbacks
-        self.n_epochs = n_epochs
-        self.verbose = verbose
-        self.loss = loss
-        self.output_activation = output_activation
-        self.metrics = metrics
-        self.random_state = random_state
-        self.activation = activation
-        self.use_bias = use_bias
-        self.optimizer = optimizer
-
-        self.history = None
-
-        super().__init__(
-            batch_size=batch_size,
-            last_file_name=last_file_name,
-        )
-
-        self._network = CNNNetwork(
-            n_layers=self.n_layers,
-            kernel_size=self.kernel_size,
-            n_filters=self.n_filters,
-            avg_pool_size=self.avg_pool_size,
-            activation=self.activation,
-            padding=self.padding,
-            strides=self.strides,
-            dilation_rate=self.dilation_rate,
-            use_bias=self.use_bias,
-        )
-
-    def build_model(self, input_shape, **kwargs):
-        """Construct a compiled, un-trained, keras model that is ready for training.
-
-        In aeon, time series are stored in numpy arrays of shape (d,m), where d
-        is the number of dimensions, m is the series length. Keras/tensorflow assume
-        data is in shape (m,d). This method also assumes (m,d). Transpose should
-        happen in fit.
-
-        Parameters
-        ----------
-        input_shape : tuple
-            The shape of the data fed into the input layer, should be (m,d)
-
-        Returns
-        -------
-        output : a compiled Keras Model
-        """
-        import numpy as np
-        import tensorflow as tf
-        from tensorflow import keras
-
-        rng = check_random_state(self.random_state)
-        self.random_state_ = rng.randint(0, np.iinfo(np.int32).max)
-        tf.keras.utils.set_random_seed(self.random_state_)
-        input_layer, output_layer = self._network.build_network(input_shape, **kwargs)
-
-        output_layer = keras.layers.Dense(units=1, activation=self.output_activation)(
-            output_layer
-        )
-
-        self.optimizer_ = (
-            keras.optimizers.Adam() if self.optimizer is None else self.optimizer
-        )
-
-        model = keras.models.Model(inputs=input_layer, outputs=output_layer)
-
-        model.compile(
-            loss=self.loss,
-            optimizer=self.optimizer_,
-            metrics=self._metrics,
-        )
-        return model
-
-    def _fit(self, X, y):
-        """Fit the regressor on the training set (X, y).
-
-        Parameters
-        ----------
-        X : np.ndarray
-            The training input samples of shape (n_cases, n_channels, n_timepoints).
-        y : np.ndarray
-            The training data target values of shape (n_cases,).
-
-        Returns
-        -------
-        self : object
-        """
-        import tensorflow as tf
-
-        # Transpose to conform to Keras input style.
-        X = X.transpose(0, 2, 1)
-
-        if isinstance(self.metrics, str):
-            self._metrics = [self.metrics]
-        else:
-            self._metrics = self.metrics
-        self.input_shape = X.shape[1:]
-        self.training_model_ = self.build_model(self.input_shape)
-
-        if self.save_init_model:
-            self.training_model_.save(self.file_path + self.init_file_name + ".keras")
-
-        if self.verbose:
-            self.training_model_.summary()
-
-        self.file_name_ = (
-            self.best_file_name if self.save_best_model else str(time.time_ns())
-        )
-
-        if self.callbacks is None:
-            self.callbacks_ = [
-                tf.keras.callbacks.ModelCheckpoint(
-                    filepath=self.file_path + self.file_name_ + ".keras",
-                    monitor="loss",
-                    save_best_only=True,
-                ),
-            ]
-        else:
-            self.callbacks_ = self._get_model_checkpoint_callback(
-                callbacks=self.callbacks,
-                file_path=self.file_path,
-                file_name=self.file_name_,
-            )
-
-        self.history = self.training_model_.fit(
-            X,
-            y,
-            batch_size=self.batch_size,
-            epochs=self.n_epochs,
-            verbose=self.verbose,
-            callbacks=self.callbacks_,
-        )
-
-        try:
-            self.model_ = tf.keras.models.load_model(
-                self.file_path + self.file_name_ + ".keras", compile=False
-            )
-            if not self.save_best_model:
-                os.remove(self.file_path + self.file_name_ + ".keras")
-        except FileNotFoundError:
-            self.model_ = deepcopy(self.training_model_)
-
-        if self.save_last_model:
-            self.save_last_model_to_file(file_path=self.file_path)
-
-        gc.collect()
-        return self
-
-    @classmethod
-    def get_test_params(cls, parameter_set="default"):
-        """Return testing parameter settings for the estimator.
-
-        Parameters
-        ----------
-        parameter_set : str, default="default"
-            Name of the set of test parameters to return, for use in tests. If no
-            special parameters are defined for a value, will return `"default"` set.
-            For regressors, a "default" set of parameters should be provided for
-            general testing, and a "results_comparison" set for comparing against
-            previously recorded results if the general set does not produce suitable
-            probabilities to compare against.
-
-        Returns
-        -------
-        params : dict or list of dict, default={}
-            Parameters to create testing instances of the class.
-            Each dict are parameters to construct an "interesting" test instance, i.e.,
-            `MyClass(**params)` or `MyClass(**params[i])` creates a valid test instance.
-            `create_test_instance` uses the first (or only) dictionary in `params`.
-        """
-        param = {
-            "n_epochs": 10,
-            "batch_size": 4,
-            "avg_pool_size": 4,
-        }
-
-        return [param]
-
-
 class TimeCNNRegressor(BaseDeepRegressor):
     """Time Series Convolutional Neural Network (CNN).
 
diff --git a/aeon/transformations/series/_yeojohnson.py b/aeon/transformations/series/_yeojohnson.py
new file mode 100644
index 00000000..edc046bb
--- /dev/null
+++ b/aeon/transformations/series/_yeojohnson.py
@@ -0,0 +1,143 @@
+"""Implemenents Yeo-Johnson Transformation."""
+
+__maintainer__ = []
+__all__ = ["YeoJohnsonTransformer"]
+
+import numpy as np
+from numba import njit
+from scipy.stats import yeojohnson_normmax
+
+from aeon.transformations.series.base import BaseSeriesTransformer
+
+
+class YeoJohnsonTransformer(BaseSeriesTransformer):
+    r"""Yeo-Johnson power transform.
+
+    Yeo-Johnson transformation is related to the BoxCox transformation, and is
+    a power transformation that is used to make data more normally distributed
+    and stabilize its variance based on the hyperparameter lambda. [1]_
+
+    The YeoJohnsonTransformer solves for the lambda parameter used in the
+    Yeo-Johnson transformation using maximum likelihood estimation,
+    on input data provided to `fit`.
+
+    The Yeo-Johnson transformation is defined as :math:`\[
+        \phi(\lambda,y) =
+        \begin{cases}
+        log(y+1) & \text{if $\lambda=0, y\geq0$} \\
+        \frac{(y+1)^\lambda - 1}{\lambda} & \text{if $\lambda\neq0, y\geq0$} \\
+        -log(1-y) & \text{if $\lambda=2, y<0$} \\
+        -\frac{(1-y)^{2-\lambda}-1)}{2-\lambda} & \text{if $\lambda\neq2, y<0$}
+        \end{cases}
+    \]`.
+
+    Parameters
+    ----------
+    bounds : tuple
+        Lower and upper bounds used to restrict the feasible range
+        when solving for the value of lambda.
+    lambda_ : float
+        The Yeo-Johnson lambda parameter. If not supplied, it is solved for based
+        on the data provided in `fit`.
+
+    See Also
+    --------
+    aeon.transformations.boxcox.BoxCoxTransformer :
+        Transform input data by using the Box-Cox power transform. Used to
+        make data more normally distributed and stabilize its variance based
+        on the hyperparameter lambda.
+    aeon.transformations.boxcox.LogTransformer :
+        Transform input data using natural log. Can help normalize data and
+        compress variance of the series.
+    aeon.transformations.exponent.ExponentTransformer :
+        Transform input data by raising it to an exponent. Can help compress
+        variance of series if a fractional exponent is supplied.
+    aeon.transformations.exponent.SqrtTransformer :
+        Transform input data by taking its square root. Can help compress
+        variance of input series.
+
+    References
+    ----------
+    .. [1] Yeo and R.A. Johnson, “A New Family of Power Transformations to
+        Improve Normality or Symmetry”, Biometrika 87.4 (2000).
+
+    Examples
+    --------
+    >>> from aeon.transformations.series._yeojohnson import YeoJohnsonTransformer
+    >>> from aeon.datasets import load_airline
+    >>> y = load_airline()
+    >>> transformer = YeoJohnsonTransformer()
+    >>> y_hat = transformer.fit_transform(y)
+    """
+
+    # tag values specific to SeriesTransformers
+    _tags = {
+        "capability:univariate": True,
+        "capability:multivariate": False,
+    }
+
+    def __init__(self, lmbda=None, bounds=None):
+        self.bounds = bounds
+        self.lmbda = lmbda
+        super().__init__(axis=1)
+
+    def _fit(self, X, y=None):
+        """
+        Fit transformer to X and y.
+
+        private _fit containing the core logic, called from fit
+
+        Parameters
+        ----------
+        X : 2D np.ndarray (n x 1)
+            Data to be transformed
+        y : ignored argument for interface compatibility
+            Additional data, e.g., labels for transformation
+
+        Returns
+        -------
+        self: a fitted instance of the estimator
+        """
+        if self.lmbda is None:
+            X = X.flatten()
+            self._lambda = yeojohnson_normmax(X, brack=self.bounds)
+        else:
+            self._lambda = self.lmbda
+        return self
+
+    def _transform(self, X, y=None):
+        """Transform X and return a transformed version.
+
+        private _transform containing the core logic, called from transform
+
+        Parameters
+        ----------
+        X : 2D np.ndarray (1 x n_timepoints)
+            Data to be transformed
+        y : ignored argument for interface compatibility
+            Additional data, e.g., labels for transformation
+
+        Returns
+        -------
+        Xt : 2D np.ndarray
+            transformed version of X
+        """
+        return _yeo_johnson_transform(X, self._lambda)
+
+
+@njit(fastmath=True, cache=True)
+def _yeo_johnson_transform(X, lmbda):
+    X_shape = X.shape
+    Xt = X.flatten()
+    X_gte_0 = Xt >= 0
+    X_lt_0 = Xt < 0
+    if lmbda != 0:
+        Xt[X_gte_0] = (np.power(Xt[X_gte_0] + 1, lmbda) - 1) / lmbda
+    elif lmbda == 0:
+        Xt[X_gte_0] = np.log(Xt[X_gte_0] + 1)
+    if lmbda != 2:
+        Xt[X_lt_0] = -(np.power(-Xt[X_lt_0] + 1, 2 - lmbda) - 1) / (2 - lmbda)
+    elif lmbda == 2:
+        Xt[X_lt_0] = -np.log(-Xt[X_lt_0] + 1)
+    Xt = Xt.reshape(X_shape)
+    return Xt
diff --git a/docs/api_reference/classification.rst b/docs/api_reference/classification.rst
index 74946a69..03aaa29e 100644
--- a/docs/api_reference/classification.rst
+++ b/docs/api_reference/classification.rst
@@ -33,7 +33,6 @@ Deep learning
     :template: class.rst
 
     BaseDeepClassifier
-    CNNClassifier
     TimeCNNClassifier
     EncoderClassifier
     FCNClassifier
diff --git a/docs/api_reference/networks.rst b/docs/api_reference/networks.rst
index 65b8aefa..66741bcb 100644
--- a/docs/api_reference/networks.rst
+++ b/docs/api_reference/networks.rst
@@ -14,7 +14,6 @@ Deep learning networks
     :template: class.rst
 
     BaseDeepLearningNetwork
-    CNNNetwork
     TimeCNNNetwork
     EncoderNetwork
     FCNNetwork
diff --git a/docs/api_reference/regression.rst b/docs/api_reference/regression.rst
index bb0ff961..d775f320 100644
--- a/docs/api_reference/regression.rst
+++ b/docs/api_reference/regression.rst
@@ -52,7 +52,6 @@ Deep learning
     :template: class.rst
 
     BaseDeepRegressor
-    CNNRegressor
     TimeCNNRegressor
     EncoderRegressor
     FCNRegressor
diff --git a/docs/api_reference/transformations.rst b/docs/api_reference/transformations.rst
index d1c01552..d471923f 100644
--- a/docs/api_reference/transformations.rst
+++ b/docs/api_reference/transformations.rst
@@ -189,6 +189,7 @@ Series transforms
     StatsModelsPACF
     BKFilter
     BoxCoxTransformer
+    YeoJohnsonTransformer
     Dobin
     ScaledLogitSeriesTransformer
     SIVSeriesTransformer
diff --git a/examples/classification/deep_learning.ipynb b/examples/classification/deep_learning.ipynb
index 9be03a9d..d3400081 100644
--- a/examples/classification/deep_learning.ipynb
+++ b/examples/classification/deep_learning.ipynb
@@ -14,55 +14,6 @@
     "review of related deep learning work."
    ]
   },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "\n",
-    "A list of all deep learning classifiers"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[('CNNClassifier', aeon.classification.deep_learning._cnn.CNNClassifier),\n",
-       " ('EncoderClassifier',\n",
-       "  aeon.classification.deep_learning._encoder.EncoderClassifier),\n",
-       " ('FCNClassifier', aeon.classification.deep_learning._fcn.FCNClassifier),\n",
-       " ('InceptionTimeClassifier',\n",
-       "  aeon.classification.deep_learning._inception_time.InceptionTimeClassifier),\n",
-       " ('IndividualInceptionClassifier',\n",
-       "  aeon.classification.deep_learning._inception_time.IndividualInceptionClassifier),\n",
-       " ('IndividualLITEClassifier',\n",
-       "  aeon.classification.deep_learning._lite_time.IndividualLITEClassifier),\n",
-       " ('LITETimeClassifier',\n",
-       "  aeon.classification.deep_learning._lite_time.LITETimeClassifier),\n",
-       " ('MLPClassifier', aeon.classification.deep_learning._mlp.MLPClassifier),\n",
-       " ('ResNetClassifier',\n",
-       "  aeon.classification.deep_learning._resnet.ResNetClassifier),\n",
-       " ('TapNetClassifier',\n",
-       "  aeon.classification.deep_learning._tapnet.TapNetClassifier)]"
-      ]
-     },
-     "execution_count": 5,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "import warnings\n",
-    "\n",
-    "from aeon.registry import all_estimators\n",
-    "\n",
-    "warnings.filterwarnings(\"ignore\")\n",
-    "all_estimators(\"classifier\", filter_tags={\"algorithm_type\": \"deeplearning\"})"
-   ]
-  },
   {
    "cell_type": "markdown",
    "metadata": {
@@ -106,7 +57,7 @@
    "source": [
     "from sklearn.metrics import accuracy_score\n",
     "\n",
-    "from aeon.classification.deep_learning import CNNClassifier\n",
+    "from aeon.classification.deep_learning import TimeCNNClassifier\n",
     "from aeon.datasets import load_basic_motions  # multivariate dataset\n",
     "from aeon.datasets import load_italy_power_demand  # univariate dataset\n",
     "\n",
@@ -114,7 +65,7 @@
     "italy_test, italy_test_labels = load_italy_power_demand(split=\"test\")\n",
     "motions, motions_labels = load_basic_motions(split=\"train\")\n",
     "motions_test, motions_test_labels = load_basic_motions(split=\"train\")\n",
-    "cnn = CNNClassifier()\n",
+    "cnn = TimeCNNClassifier()\n",
     "cnn.fit(italy, italy_labels)\n",
     "y_pred = cnn.predict(italy_test)\n",
     "accuracy_score(italy_test_labels, y_pred)"
@@ -167,7 +118,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 2,
    "metadata": {
     "collapsed": false
    },
@@ -176,7 +127,6 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "('CNNClassifier', <class 'aeon.classification.deep_learning._cnn.CNNClassifier'>)\n",
       "('EncoderClassifier', <class 'aeon.classification.deep_learning._encoder.EncoderClassifier'>)\n",
       "('FCNClassifier', <class 'aeon.classification.deep_learning._fcn.FCNClassifier'>)\n",
       "('InceptionTimeClassifier', <class 'aeon.classification.deep_learning._inception_time.InceptionTimeClassifier'>)\n",
@@ -185,7 +135,8 @@
       "('LITETimeClassifier', <class 'aeon.classification.deep_learning._lite_time.LITETimeClassifier'>)\n",
       "('MLPClassifier', <class 'aeon.classification.deep_learning._mlp.MLPClassifier'>)\n",
       "('ResNetClassifier', <class 'aeon.classification.deep_learning._resnet.ResNetClassifier'>)\n",
-      "('TapNetClassifier', <class 'aeon.classification.deep_learning._tapnet.TapNetClassifier'>)\n"
+      "('TapNetClassifier', <class 'aeon.classification.deep_learning._tapnet.TapNetClassifier'>)\n",
+      "('TimeCNNClassifier', <class 'aeon.classification.deep_learning._cnn.TimeCNNClassifier'>)\n"
      ]
     }
    ],
@@ -228,6 +179,7 @@
     "names.remove(\"MLP\")\n",
     "names.remove(\"TimeCNN\")\n",
     "names.remove(\"TapNet\")  # Multivariate only\n",
+    "names.add(\"CNN\")  # Multivariate only\n",
     "\n",
     "\n",
     "results, present_names = get_estimator_results_as_array(\n",
diff --git a/examples/classification/distance_based.ipynb b/examples/classification/distance_based.ipynb
index 0def97cd..ac3ac857 100644
--- a/examples/classification/distance_based.ipynb
+++ b/examples/classification/distance_based.ipynb
@@ -293,11 +293,11 @@
    "source": [
     "### Proximity Forest\n",
     "\n",
-    "Proximity Forest [2] is a distance-based ensemble of decision trees. \n",
-    "It is the current state-of-the-art distance-based classifier that \n",
-    "creates an ensemble of decision trees, where the splits are based \n",
-    "on the similarity between time series measured using various \n",
-    "parameterised distance measures. The current algorithm is \n",
+    "Proximity Forest [2] is a distance-based ensemble of decision trees.\n",
+    "It is the current state-of-the-art distance-based classifier that\n",
+    "creates an ensemble of decision trees, where the splits are based\n",
+    "on the similarity between time series measured using various\n",
+    "parameterised distance measures. The current algorithm is\n",
     "implemented to work for univariate, equal-length time-series data."
    ]
   },


Test patch:
diff --git a/aeon/regression/deep_learning/tests/test_timecnn.py b/aeon/regression/deep_learning/tests/test_timecnn.py
new file mode 100644
index 00000000..57f27667
--- /dev/null
+++ b/aeon/regression/deep_learning/tests/test_timecnn.py
@@ -0,0 +1,18 @@
+"""Test for TimeCNN regressor."""
+
+import pytest
+
+from aeon.regression.deep_learning import TimeCNNRegressor
+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_timecnn_inputs():
+    """Test parameter inputs correctly handled."""
+    X, y = make_example_3d_numpy(n_cases=3, n_channels=1, n_timepoints=10)
+    cnn = TimeCNNRegressor(metrics=["mean_squared_error"], callbacks=None)
+    cnn.fit(X, y)
diff --git a/aeon/regression/interval_based/tests/__init__.py b/aeon/regression/interval_based/tests/__init__.py
new file mode 100644
index 00000000..4be71f9f
--- /dev/null
+++ b/aeon/regression/interval_based/tests/__init__.py
@@ -0,0 +1,1 @@
+"""Test interval-based regression models."""
diff --git a/aeon/regression/interval_based/tests/test_cif.py b/aeon/regression/interval_based/tests/test_cif.py
new file mode 100644
index 00000000..83deef37
--- /dev/null
+++ b/aeon/regression/interval_based/tests/test_cif.py
@@ -0,0 +1,15 @@
+"""Test DrCIF regressor."""
+
+from aeon.regression.interval_based import CanonicalIntervalForestRegressor
+
+
+def test_cif():
+    """Test with catch22 enabled."""
+    dr = CanonicalIntervalForestRegressor(use_pycatch22=True)
+    d = dr.get_tag("python_dependencies")
+    assert d == "pycatch22"
+    paras = CanonicalIntervalForestRegressor.get_test_params(
+        parameter_set="contracting"
+    )
+    assert paras["time_limit_in_minutes"] == 5
+    assert paras["att_subsample_size"] == 2
diff --git a/aeon/regression/interval_based/tests/test_dr_cif.py b/aeon/regression/interval_based/tests/test_dr_cif.py
new file mode 100644
index 00000000..571c10b1
--- /dev/null
+++ b/aeon/regression/interval_based/tests/test_dr_cif.py
@@ -0,0 +1,13 @@
+"""Test DrCIF regressor."""
+
+from aeon.regression.interval_based import DrCIFRegressor
+
+
+def test_dr_cif():
+    """Test with pyfftw enabled."""
+    dr = DrCIFRegressor(use_pyfftw=True)
+    d = dr.get_tag("python_dependencies")
+    assert d[0] == "pyfftw"
+    paras = DrCIFRegressor.get_test_params(parameter_set="contracting")
+    assert paras["time_limit_in_minutes"] == 5
+    assert paras["att_subsample_size"] == 2
diff --git a/aeon/regression/interval_based/tests/test_interval_forest.py b/aeon/regression/interval_based/tests/test_interval_forest.py
new file mode 100644
index 00000000..55651660
--- /dev/null
+++ b/aeon/regression/interval_based/tests/test_interval_forest.py
@@ -0,0 +1,10 @@
+"""Test Inter regressor."""
+
+from aeon.regression.interval_based import IntervalForestRegressor
+
+
+def test_cif():
+    """Test with IntervalForestRegressor contracting."""
+    paras = IntervalForestRegressor.get_test_params(parameter_set="contracting")
+    assert paras["time_limit_in_minutes"] == 5
+    assert paras["n_intervals"] == 2
diff --git a/aeon/regression/interval_based/tests/test_rand_interval.py b/aeon/regression/interval_based/tests/test_rand_interval.py
new file mode 100644
index 00000000..1dbe60a5
--- /dev/null
+++ b/aeon/regression/interval_based/tests/test_rand_interval.py
@@ -0,0 +1,18 @@
+"""Test interval-based regression models."""
+
+from sklearn.ensemble import RandomForestRegressor
+
+from aeon.regression.interval_based import RandomIntervalRegressor
+from aeon.testing.data_generation import make_example_3d_numpy
+
+
+def test_cif():
+    """Test with IntervalForestRegressor contracting."""
+    cls = RandomIntervalRegressor(
+        n_jobs=1,
+        n_intervals=5,
+        estimator=RandomForestRegressor(n_estimators=10, n_jobs=2),
+    )
+    X, y = make_example_3d_numpy()
+    cls.fit(X, y)
+    assert cls._estimator.n_jobs == 1
diff --git a/aeon/regression/interval_based/tests/test_rise.py b/aeon/regression/interval_based/tests/test_rise.py
new file mode 100644
index 00000000..7e89f127
--- /dev/null
+++ b/aeon/regression/interval_based/tests/test_rise.py
@@ -0,0 +1,15 @@
+"""Test DrCIF regressor."""
+
+from aeon.regression.interval_based import RandomIntervalSpectralEnsembleRegressor
+
+
+def test_rise():
+    """Test with pyfftw enabled."""
+    dr = RandomIntervalSpectralEnsembleRegressor(use_pyfftw=True)
+    d = dr.get_tag("python_dependencies")
+    assert d == "pyfftw"
+    paras = RandomIntervalSpectralEnsembleRegressor.get_test_params(
+        parameter_set="contracting"
+    )
+    assert paras["time_limit_in_minutes"] == 5
+    assert paras["contract_max_n_estimators"] == 2
diff --git a/aeon/transformations/series/tests/test_yeojohnson.py b/aeon/transformations/series/tests/test_yeojohnson.py
new file mode 100644
index 00000000..ddbf3e2f
--- /dev/null
+++ b/aeon/transformations/series/tests/test_yeojohnson.py
@@ -0,0 +1,21 @@
+"""Tests for YeoJohnsonTransformer."""
+
+__maintainer__ = []
+__all__ = []
+
+import numpy as np
+from scipy.stats import yeojohnson
+
+from aeon.datasets import load_airline
+from aeon.transformations.series._yeojohnson import YeoJohnsonTransformer
+
+
+def test_yeojohnson_against_scipy():
+    y = load_airline()
+
+    t = YeoJohnsonTransformer()
+    actual = t.fit_transform(y)
+
+    excepted, expected_lambda = yeojohnson(y.values)
+    np.testing.assert_almost_equal(actual, excepted, decimal=12)
+    assert t._lambda == expected_lambda