Repository: sktime/sktime

Issue:
[BUG] `CNNRegressor` missing essential initializing parameters
**Describe the bug**
<!--
A clear and concise description of what the bug is.
-->
`CNNRegressor` misses parameters like `optimizer` in its constructor. There might be more things which don't coincide with `CNNClassifier` which should be added but since I'm not sure what all things might be missing, I'm opening this issue to bring it to notice.


Comments:

Patch:
diff --git a/sktime/annotation/hmm_learn/base.py b/sktime/annotation/hmm_learn/base.py
index e688bdd16..c4b6fb0da 100644
--- a/sktime/annotation/hmm_learn/base.py
+++ b/sktime/annotation/hmm_learn/base.py
@@ -22,7 +22,7 @@ __all__ = ["BaseHMMLearn"]
 class BaseHMMLearn(BaseSeriesAnnotator):
     """Base class for all HMM wrappers, handles required overlap between packages."""
 
-    __tags = {
+    _tags = {
         "univariate-only": True,
         "fit_is_empty": True,
         "python_dependencies": "hmmlearn",
diff --git a/sktime/regression/deep_learning/cnn.py b/sktime/regression/deep_learning/cnn.py
index 37d7020af..d7773ef7c 100644
--- a/sktime/regression/deep_learning/cnn.py
+++ b/sktime/regression/deep_learning/cnn.py
@@ -1,9 +1,11 @@
 # -*- coding: utf-8 -*-
 """Time Convolutional Neural Network (CNN) for regression."""
 
-__author__ = ["AurumnPegasus"]
+__author__ = ["AurumnPegasus", "achieveordie"]
 __all__ = ["CNNRegressor"]
 
+from sklearn.utils import check_random_state
+
 from sktime.networks.cnn import CNNNetwork
 from sktime.regression.deep_learning.base import BaseDeepRegressor
 from sktime.utils.validation._dependencies import _check_dl_dependencies
@@ -34,7 +36,12 @@ class CNNRegressor(BaseDeepRegressor):
         whether to output extra information
     loss            : string, default="mean_squared_error"
         fit parameter for the keras model
-    optimizer       : keras.optimizer, default=keras.optimizers.Adam(),
+    activation      : keras.activations or string, default `linear`
+        function to use in the output layer.
+    optimizer       : keras.optimizers or string, default `None`.
+        when `None`, internally uses `keras.optimizers.Adam(0.01)`
+    use_bias        : bool, default=True
+        whether to use bias in the output layer.
     metrics         : list of strings, default=["accuracy"],
 
     Notes
@@ -58,7 +65,10 @@ class CNNRegressor(BaseDeepRegressor):
         verbose=False,
         loss="mean_squared_error",
         metrics=None,
-        random_seed=0,
+        random_state=0,
+        activation="linear",
+        use_bias=True,
+        optimizer=None,
     ):
         _check_dl_dependencies(severity="error")
         super(CNNRegressor, self).__init__(
@@ -73,7 +83,11 @@ class CNNRegressor(BaseDeepRegressor):
         self.verbose = verbose
         self.loss = loss
         self.metrics = metrics
-        self.random_seed = random_seed
+        self.random_state = random_state
+        self.activation = activation
+        self.use_bias = use_bias
+        self.optimizer = optimizer
+        self.history = None
         self._network = CNNNetwork()
 
     def build_model(self, input_shape, **kwargs):
@@ -96,7 +110,7 @@ class CNNRegressor(BaseDeepRegressor):
         import tensorflow as tf
         from tensorflow import keras
 
-        tf.random.set_seed(self.random_seed)
+        tf.random.set_seed(self.random_state)
 
         if self.metrics is None:
             metrics = ["accuracy"]
@@ -105,13 +119,23 @@ class CNNRegressor(BaseDeepRegressor):
 
         input_layer, output_layer = self._network.build_network(input_shape, **kwargs)
 
-        output_layer = keras.layers.Dense(units=1, activation="sigmoid")(output_layer)
+        output_layer = keras.layers.Dense(
+            units=1,
+            activation=self.activation,
+            use_bias=self.use_bias,
+        )(output_layer)
+
+        self.optimizer_ = (
+            keras.optimizers.Adam(learning_rate=0.01)
+            if self.optimizer is None
+            else self.optimizer
+        )
 
         model = keras.models.Model(inputs=input_layer, outputs=output_layer)
 
         model.compile(
             loss=self.loss,
-            optimizer=keras.optimizers.Adam(),
+            optimizer=self.optimizer_,
             metrics=metrics,
         )
         return model
@@ -136,6 +160,7 @@ class CNNRegressor(BaseDeepRegressor):
         # Transpose to conform to Keras input style.
         X = X.transpose(0, 2, 1)
 
+        check_random_state(self.random_state)
         self.input_shape = X.shape[1:]
         self.model_ = self.build_model(self.input_shape)
         if self.verbose:
diff --git a/sktime/transformations/hierarchical/aggregate.py b/sktime/transformations/hierarchical/aggregate.py
index 9e32e6482..79fb305e2 100644
--- a/sktime/transformations/hierarchical/aggregate.py
+++ b/sktime/transformations/hierarchical/aggregate.py
@@ -147,7 +147,7 @@ class Aggregator(BaseTransformer):
                 df_out.reset_index(level=-1).loc[new_index].set_index(nm, append=True)
             ).rename_axis(X.index.names, axis=0)
 
-        df_out.sort_index(inplace=True)
+        df_out = df_out.sort_index()
 
         return df_out
 
diff --git a/sktime/utils/sampling.py b/sktime/utils/sampling.py
index de6f7dcab..bfe8581cf 100644
--- a/sktime/utils/sampling.py
+++ b/sktime/utils/sampling.py
@@ -26,7 +26,7 @@ def stratified_resample(X_train, y_train, X_test, y_test, random_state):
     X_test : pd.DataFrame
         test data attributes in sktime pandas format.
     y_test : np.array
-        test data class labes as np array.
+        test data class labels as np array.
     random_state : int
         seed to enable reproducable resamples
     Returns
@@ -71,12 +71,9 @@ def stratified_resample(X_train, y_train, X_test, y_test, random_state):
         X_test = pd.concat([X_test, test_instances])
         y_train = np.concatenate([y_train, train_labels], axis=None)
         y_test = np.concatenate([y_test, test_labels], axis=None)
-    # get the counts of the new train and test resample
-    unique_train_new, counts_train_new = np.unique(y_train, return_counts=True)
-    unique_test_new, counts_test_new = np.unique(y_test, return_counts=True)
-    # make sure they match the original distribution of data
-    assert list(counts_train_new) == list(counts_train)
-    assert list(counts_test_new) == list(counts_test)
+    # reset indexes to conform to sktime format.
+    X_train = X_train.reset_index(drop=True)
+    X_test = X_test.reset_index(drop=True)
     return X_train, y_train, X_test, y_test
 
 


Test patch:
diff --git a/sktime/benchmarking/tests/test_experiments.py b/sktime/benchmarking/tests/test_experiments.py
index 968078fac..e065cdd0a 100644
--- a/sktime/benchmarking/tests/test_experiments.py
+++ b/sktime/benchmarking/tests/test_experiments.py
@@ -65,25 +65,3 @@ def test_run_classification_experiment(tmp_path):
     # remove files
     test_path.unlink()
     train_path.unlink()
-
-
-# def test_load_and_run_clustering_experiment():
-#     """Test loading, running and saving.
-#
-#     Currently it just checks that the files have been created, then deletes them.
-#     Later it can be enhanced to check the results can be loaded.
-#     """
-#     load_and_run_clustering_experiment(
-#         overwrite=True,
-#         problem_path="../../datasets/data/",
-#         results_path="../Temp/",
-#         cls_name="kmeans",
-#         dataset="UnitTest",
-#         resample_id=0,
-#         train_file=True,
-#     )
-#     assert os.path.isfile("../Temp/kmeans/Predictions/UnitTest/testResample0.csv")
-#     assert os.path.isfile("../Temp/kmeans/Predictions/UnitTest/trainResample0.csv")
-#     os.remove("../Temp/kmeans/Predictions/UnitTest/testResample0.csv")
-#     os.remove("../Temp/kmeans/Predictions/UnitTest/trainResample0.csv")
-#
diff --git a/sktime/utils/tests/test_sampling.py b/sktime/utils/tests/test_sampling.py
index 3d17cca37..85fca8bfb 100644
--- a/sktime/utils/tests/test_sampling.py
+++ b/sktime/utils/tests/test_sampling.py
@@ -1,10 +1,13 @@
 # -*- coding: utf-8 -*-
 """Testing sampling utilities."""
 
+import numpy as np
 import pytest
 
+from sktime.datasets import load_unit_test
+from sktime.datatypes import check_is_scitype
 from sktime.utils._testing.deep_equals import deep_equals
-from sktime.utils.sampling import random_partition
+from sktime.utils.sampling import random_partition, stratified_resample
 
 NK_FIXTURES = [(10, 3), (15, 5), (19, 6), (3, 1), (1, 2)]
 SEED_FIXTURES = [42, 0, 100, -5]
@@ -42,3 +45,23 @@ def test_seed(n, k, seed):
     part2 = random_partition(n, k, seed)
 
     assert deep_equals(part, part2)
+
+
+def test_stratified_resample():
+    """Test resampling returns valid data structure and maintains class distribution."""
+    trainX, trainy = load_unit_test(split="TRAIN")
+    testX, testy = load_unit_test(split="TEST")
+    new_trainX, new_trainy, new_testX, new_testy = stratified_resample(
+        trainX, trainy, testX, testy, 0
+    )
+
+    valid_train = check_is_scitype(new_trainX, scitype="Panel")
+    valid_test = check_is_scitype(new_testX, scitype="Panel")
+    assert valid_test and valid_train
+    # count class occurrences
+    unique_train, counts_train = np.unique(trainy, return_counts=True)
+    unique_test, counts_test = np.unique(testy, return_counts=True)
+    unique_train_new, counts_train_new = np.unique(new_trainy, return_counts=True)
+    unique_test_new, counts_test_new = np.unique(new_testy, return_counts=True)
+    assert list(counts_train_new) == list(counts_train)
+    assert list(counts_test_new) == list(counts_test)