From 72e32fbd3afdc8b06563fbbb9c62b8311c5067b5 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 17 May 2024 14:55:44 +0530 Subject: [PATCH 01/32] Add DCNN --- aeon/networks/__init__.py | 2 + aeon/networks/_dcnn.py | 120 ++++++++++++++++++++++++ aeon/networks/utils.py | 193 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 aeon/networks/_dcnn.py create mode 100644 aeon/networks/utils.py diff --git a/aeon/networks/__init__.py b/aeon/networks/__init__.py index 946dc9bd01..0380534193 100644 --- a/aeon/networks/__init__.py +++ b/aeon/networks/__init__.py @@ -12,6 +12,7 @@ "AEFCNNetwork", "AEResNetNetwork", "LITENetwork", + "DCNNEncoderNetwork", ] from aeon.networks._ae_fcn import AEFCNNetwork from aeon.networks._ae_resnet import AEResNetNetwork @@ -24,3 +25,4 @@ from aeon.networks._resnet import ResNetNetwork from aeon.networks._tapnet import TapNetNetwork from aeon.networks.base import BaseDeepNetwork +from aeon.networks._dcnn import DCNNEncoderNetwork diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py new file mode 100644 index 0000000000..85b4b41824 --- /dev/null +++ b/aeon/networks/_dcnn.py @@ -0,0 +1,120 @@ +import numpy as np +import tensorflow as tf + +from aeon.networks.base import BaseDeepNetwork +from aeon.networks.utils import WeightNormalization + + +def DCNNLayer(inputs, num_filters, layer_num): + _dilation = 2**layer_num + _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) + x = WeightNormalization( + tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") + )(inputs) + x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) + x = WeightNormalization( + tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") + )(x) + x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) + return tf.keras.layers.Add([x, _add]) + + +class TripletLossTimeSeries(tf.keras.losses.Loss): + def __init__( + self, + encoder, + X, + anchor_length, + num_neg_samples, + negative_penalty, + fixed_time_dim=False, + reduction=tf.keras.losses.Reduction.AUTO, + ): + super().__init__(reduction) + + self.encoder = encoder + self.X = X + self.anchor_length = min(anchor_length, X.shape[1]) + self.num_neg_samples = num_neg_samples # For Negative Sampling (k) + self.negative_penalty = negative_penalty + self.fixed_time_dim = fixed_time_dim + + def call(self, y_true, y_pred): + """ + Parameters + ---------- + y_true: + Not to be used but required by Keras. + y_pred: + Predicted Embeddings by Encoder. + + Returns + ------- + float: + triplet loss between anchor, positive and negative. + """ + batch_size = tf.shape(y_pred)[0] + length = self.anchor_length + fixed_length = tf.shape(self.X)[1] + + # Extract embeddings for positive samples + positive_embeddings = y_pred[:, :length] + + # Generate negative samples + negative_indices = np.random.choice( + self.X.shape[0], self.num_neg_samples * batch_size + ) + negative_samples = tf.gather(self.X, negative_indices) + + negative_embeddings = [] + for i in range(self.num_neg_samples): + neg_sample = negative_samples[i * batch_size : (i + 1) * batch_size] + neg_embeddings = self.encoder(neg_sample) + neg_embeddings = neg_embeddings[:, :length] + if self.fixed_time_dim: + neg_embeddings = tf.pad( + neg_embeddings, [[0, 0], [0, fixed_length - length], [0, 0]] + ) + negative_embeddings.append(neg_embeddings) + negative_embeddings = tf.stack(negative_embeddings) + + if self.fixed_time_dim: + positive_embeddings = tf.pad( + positive_embeddings, [[0, 0], [0, fixed_length - length], [0, 0]] + ) + + # Compute positive loss + positive_similarity = tf.reduce_sum(y_pred * positive_embeddings, axis=-1) + positive_loss = -tf.reduce_mean(tf.math.log_sigmoid(positive_similarity)) + + # Compute negative loss + negative_loss = 0 + for neg_embeddings in negative_embeddings: + negative_similarity = tf.reduce_sum(y_pred * neg_embeddings, axis=-1) + negative_loss += -tf.reduce_mean(tf.math.log_sigmoid(-negative_similarity)) + + negative_loss *= self.negative_penalty / self.num_neg_samples + + loss = positive_loss + negative_loss + return loss + + +class DCNNEncoderNetwork(BaseDeepNetwork): + def __init__(self, latent_dim, kernel_size, num_filters, model_depth, loss_function): + super().__init__() + self.latent_dim = latent_dim + self.kernel_size = kernel_size + self.num_filters = num_filters + self.loss_functions = loss_function + self.model_depth = model_depth + + def build_network(self, input_shape): + input_layer = tf.keras.layers.Input(input_shape) + + x = input_layer + for i in range(1, self.model_depth + 1): + x = DCNNLayer(x, self.num_filters, i) + x = tf.keras.layers.GlobalMaxPool1D()(x) + output_layer = tf.keras.layers.Dense(self.latent_dim)(x) + + return tf.keras.Model(inputs=input_layer, outputs = output_layer) diff --git a/aeon/networks/utils.py b/aeon/networks/utils.py new file mode 100644 index 0000000000..1d057b5640 --- /dev/null +++ b/aeon/networks/utils.py @@ -0,0 +1,193 @@ +import logging +import tensorflow as tf +from typeguard import typechecked + +__all__ = ["WeightNormalization"] + + +@tf.keras.utils.register_keras_serializable(package="addons") +class WeightNormalization(tf.keras.layers.Wrapper): + """Wrapper function that performs weight normalization. + + Taken from: + https://github.com/tensorflow/addons/blob/master/tensorflow_addons/layers/wrappers.py + + Args: + layer: A `tf.keras.layers.Layer` instance. + data_init: If `True` use data dependent variable initialization. + Raises: + ValueError: If not initialized with a `Layer` instance. + ValueError: If `Layer` does not contain a `kernel` of weights. + NotImplementedError: If `data_init` is True and running graph execution. + """ + + @typechecked + def __init__(self, layer: tf.keras.layers, data_init: bool = True, **kwargs): + super().__init__(layer, **kwargs) + self.data_init = data_init + self._track_trackable(layer, name="layer") + self.is_rnn = isinstance(self.layer, tf.keras.layers.RNN) + + if self.data_init and self.is_rnn: + logging.warning( + "WeightNormalization: Using `data_init=True` with RNNs " + "is advised against by the paper. Use `data_init=False`." + ) + + def build(self, input_shape): + """Build `Layer`""" + input_shape = tf.TensorShape(input_shape) + self.input_spec = tf.keras.layers.InputSpec(shape=[None] + input_shape[1:]) + + if not self.layer.built: + self.layer.build(input_shape) + + kernel_layer = self.layer.cell if self.is_rnn else self.layer + + if not hasattr(kernel_layer, "kernel"): + raise ValueError( + "`WeightNormalization` must wrap a layer that" + " contains a `kernel` for weights" + ) + + if self.is_rnn: + kernel = kernel_layer.recurrent_kernel + else: + kernel = kernel_layer.kernel + + # The kernel's filter or unit dimension is -1 + self.layer_depth = int(kernel.shape[-1]) + self.kernel_norm_axes = list(range(kernel.shape.rank - 1)) + + self.g = self.add_weight( + name="g", + shape=(self.layer_depth,), + initializer="ones", + dtype=kernel.dtype, + trainable=True, + ) + self.v = kernel + + self._initialized = self.add_weight( + name="initialized", + shape=None, + initializer="zeros", + dtype=tf.dtypes.bool, + trainable=False, + ) + + if self.data_init: + # Used for data initialization in self._data_dep_init. + with tf.name_scope("data_dep_init"): + layer_config = tf.keras.layers.serialize(self.layer) + layer_config["config"]["trainable"] = False + self._naked_clone_layer = tf.keras.layers.deserialize(layer_config) + self._naked_clone_layer.build(input_shape) + self._naked_clone_layer.set_weights(self.layer.get_weights()) + if not self.is_rnn: + self._naked_clone_layer.activation = None + + self.built = True + + def call(self, inputs): + """Call `Layer`""" + + def _do_nothing(): + return tf.identity(self.g) + + def _update_weights(): + # Ensure we read `self.g` after _update_weights. + with tf.control_dependencies(self._initialize_weights(inputs)): + return tf.identity(self.g) + + g = tf.cond(self._initialized, _do_nothing, _update_weights) + + with tf.name_scope("compute_weights"): + # Replace kernel by normalized weight variable. + kernel = tf.nn.l2_normalize(self.v, axis=self.kernel_norm_axes) * g + + if self.is_rnn: + self.layer.cell.recurrent_kernel = kernel + update_kernel = tf.identity(self.layer.cell.recurrent_kernel) + else: + self.layer.kernel = kernel + update_kernel = tf.identity(self.layer.kernel) + + # Ensure we calculate result after updating kernel. + with tf.control_dependencies([update_kernel]): + outputs = self.layer(inputs) + return outputs + + def compute_output_shape(self, input_shape): + return tf.TensorShape(self.layer.compute_output_shape(input_shape).as_list()) + + def _initialize_weights(self, inputs): + """Initialize weight g. + + The initial value of g could either from the initial value in v, + or by the input value if self.data_init is True. + """ + with tf.control_dependencies( + [ + tf.debugging.assert_equal( # pylint: disable=bad-continuation + self._initialized, False, message="The layer has been initialized." + ) + ] + ): + if self.data_init: + assign_tensors = self._data_dep_init(inputs) + else: + assign_tensors = self._init_norm() + assign_tensors.append(self._initialized.assign(True)) + return assign_tensors + + def _init_norm(self): + """Set the weight g with the norm of the weight vector.""" + with tf.name_scope("init_norm"): + v_flat = tf.reshape(self.v, [-1, self.layer_depth]) + v_norm = tf.linalg.norm(v_flat, axis=0) + g_tensor = self.g.assign(tf.reshape(v_norm, (self.layer_depth,))) + return [g_tensor] + + def _data_dep_init(self, inputs): + """Data dependent initialization.""" + with tf.name_scope("data_dep_init"): + # Generate data dependent init values + x_init = self._naked_clone_layer(inputs) + data_norm_axes = list(range(x_init.shape.rank - 1)) + m_init, v_init = tf.nn.moments(x_init, data_norm_axes) + scale_init = 1.0 / tf.math.sqrt(v_init + 1e-10) + + # RNNs have fused kernels that are tiled + # Repeat scale_init to match the shape of fused kernel + # Note: This is only to support the operation, + # the paper advises against RNN+data_dep_init + if scale_init.shape[0] != self.g.shape[0]: + rep = int(self.g.shape[0] / scale_init.shape[0]) + scale_init = tf.tile(scale_init, [rep]) + + # Assign data dependent init values + g_tensor = self.g.assign(self.g * scale_init) + if hasattr(self.layer, "bias") and self.layer.bias is not None: + bias_tensor = self.layer.bias.assign(-m_init * scale_init) + return [g_tensor, bias_tensor] + else: + return [g_tensor] + + def get_config(self): + config = {"data_init": self.data_init} + base_config = super().get_config() + return {**base_config, **config} + + def remove(self): + kernel = tf.Variable( + tf.nn.l2_normalize(self.v, axis=self.kernel_norm_axes) * self.g, + name="recurrent_kernel" if self.is_rnn else "kernel", + ) + + if self.is_rnn: + self.layer.cell.recurrent_kernel = kernel + else: + self.layer.kernel = kernel + + return self.layer From d643b2662b1f192b2822ff2ca89fe7b31c3683b4 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 17 May 2024 19:48:07 +0530 Subject: [PATCH 02/32] remove triplet loss and move utils.py to utils/networks --- aeon/networks/_dcnn.py | 112 +++--------------- aeon/utils/networks/__init__.py | 5 + .../networks/weight_norm.py} | 3 - 3 files changed, 21 insertions(+), 99 deletions(-) create mode 100644 aeon/utils/networks/__init__.py rename aeon/{networks/utils.py => utils/networks/weight_norm.py} (99%) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 85b4b41824..423a9e6a83 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -1,102 +1,7 @@ -import numpy as np import tensorflow as tf from aeon.networks.base import BaseDeepNetwork -from aeon.networks.utils import WeightNormalization - - -def DCNNLayer(inputs, num_filters, layer_num): - _dilation = 2**layer_num - _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) - x = WeightNormalization( - tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") - )(inputs) - x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) - x = WeightNormalization( - tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") - )(x) - x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) - return tf.keras.layers.Add([x, _add]) - - -class TripletLossTimeSeries(tf.keras.losses.Loss): - def __init__( - self, - encoder, - X, - anchor_length, - num_neg_samples, - negative_penalty, - fixed_time_dim=False, - reduction=tf.keras.losses.Reduction.AUTO, - ): - super().__init__(reduction) - - self.encoder = encoder - self.X = X - self.anchor_length = min(anchor_length, X.shape[1]) - self.num_neg_samples = num_neg_samples # For Negative Sampling (k) - self.negative_penalty = negative_penalty - self.fixed_time_dim = fixed_time_dim - - def call(self, y_true, y_pred): - """ - Parameters - ---------- - y_true: - Not to be used but required by Keras. - y_pred: - Predicted Embeddings by Encoder. - - Returns - ------- - float: - triplet loss between anchor, positive and negative. - """ - batch_size = tf.shape(y_pred)[0] - length = self.anchor_length - fixed_length = tf.shape(self.X)[1] - - # Extract embeddings for positive samples - positive_embeddings = y_pred[:, :length] - - # Generate negative samples - negative_indices = np.random.choice( - self.X.shape[0], self.num_neg_samples * batch_size - ) - negative_samples = tf.gather(self.X, negative_indices) - - negative_embeddings = [] - for i in range(self.num_neg_samples): - neg_sample = negative_samples[i * batch_size : (i + 1) * batch_size] - neg_embeddings = self.encoder(neg_sample) - neg_embeddings = neg_embeddings[:, :length] - if self.fixed_time_dim: - neg_embeddings = tf.pad( - neg_embeddings, [[0, 0], [0, fixed_length - length], [0, 0]] - ) - negative_embeddings.append(neg_embeddings) - negative_embeddings = tf.stack(negative_embeddings) - - if self.fixed_time_dim: - positive_embeddings = tf.pad( - positive_embeddings, [[0, 0], [0, fixed_length - length], [0, 0]] - ) - - # Compute positive loss - positive_similarity = tf.reduce_sum(y_pred * positive_embeddings, axis=-1) - positive_loss = -tf.reduce_mean(tf.math.log_sigmoid(positive_similarity)) - - # Compute negative loss - negative_loss = 0 - for neg_embeddings in negative_embeddings: - negative_similarity = tf.reduce_sum(y_pred * neg_embeddings, axis=-1) - negative_loss += -tf.reduce_mean(tf.math.log_sigmoid(-negative_similarity)) - - negative_loss *= self.negative_penalty / self.num_neg_samples - - loss = positive_loss + negative_loss - return loss +from aeon.utils.networks import WeightNormalization class DCNNEncoderNetwork(BaseDeepNetwork): @@ -109,6 +14,20 @@ def __init__(self, latent_dim, kernel_size, num_filters, model_depth, loss_funct self.model_depth = model_depth def build_network(self, input_shape): + + def DCNNLayer(inputs, num_filters, layer_num): + _dilation = 2 ** layer_num + _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) + x = WeightNormalization( + tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") + )(inputs) + x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) + x = WeightNormalization( + tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") + )(x) + x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) + return tf.keras.layers.Add([x, _add]) + input_layer = tf.keras.layers.Input(input_shape) x = input_layer @@ -118,3 +37,4 @@ def build_network(self, input_shape): output_layer = tf.keras.layers.Dense(self.latent_dim)(x) return tf.keras.Model(inputs=input_layer, outputs = output_layer) + \ No newline at end of file diff --git a/aeon/utils/networks/__init__.py b/aeon/utils/networks/__init__.py new file mode 100644 index 0000000000..86065a7584 --- /dev/null +++ b/aeon/utils/networks/__init__.py @@ -0,0 +1,5 @@ +__all__ = [ + "WeightNormalization", +] + +from aeon.utils.networks.weight_norm import WeightNormalization \ No newline at end of file diff --git a/aeon/networks/utils.py b/aeon/utils/networks/weight_norm.py similarity index 99% rename from aeon/networks/utils.py rename to aeon/utils/networks/weight_norm.py index 1d057b5640..31aef1add7 100644 --- a/aeon/networks/utils.py +++ b/aeon/utils/networks/weight_norm.py @@ -2,9 +2,6 @@ import tensorflow as tf from typeguard import typechecked -__all__ = ["WeightNormalization"] - - @tf.keras.utils.register_keras_serializable(package="addons") class WeightNormalization(tf.keras.layers.Wrapper): """Wrapper function that performs weight normalization. From c1e24edcac86aa3f0ccdbdcc840811accc3bd12b Mon Sep 17 00:00:00 2001 From: aadya940 Date: Sat, 18 May 2024 00:24:16 +0530 Subject: [PATCH 03/32] Add docstring and minor changes dcnn network --- aeon/networks/_dcnn.py | 106 +++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 15 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 423a9e6a83..1011a0991d 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -5,36 +5,112 @@ class DCNNEncoderNetwork(BaseDeepNetwork): - def __init__(self, latent_dim, kernel_size, num_filters, model_depth, loss_function): + """Establish the network structure for a DCNN-Encoder. + + Dilated Convolutional Neural Network based Encoder + for low-rank embeddings. + + Parameters + ---------- + loss_function: object + Objective function to be used for weight optimization. + latent_space_dim: int, default=128 + Dimension of the encoder's latent space. + num_layers: int, default=4 + Number of convolution layers. + kernel_size: int, default=3 + Size of the 1D Convolutional Kernel. + num_filters: int, default=None + Number of filters used in convolution layers. + dilation_rate: list, default=None + The dilation rate for convolution. + activation: object, default=LeakyReLU + A Tensorflow activation function. + + References + ---------- + .. [1] Network originally defined in: + @article{franceschi2019unsupervised, + title={Unsupervised scalable representation learning for multivariate time series}, + author={Franceschi, Jean-Yves and Dieuleveut, Aymeric and Jaggi, Martin}, + journal={Advances in neural information processing systems}, + volume={32}, + year={2019} + } + """ + + def __init__( + self, + loss_function, + latent_space_dim=128, + num_layers=4, + kernel_size=3, + num_filters=None, + dilation_rate=None, + activation=tf.keras.layers.LeakyReLU, + ): super().__init__() - self.latent_dim = latent_dim + + self.latent_space_dim = latent_space_dim self.kernel_size = kernel_size self.num_filters = num_filters self.loss_functions = loss_function - self.model_depth = model_depth + self.model_depth = num_layers + self.dilation_rate = dilation_rate + self.activation = activation + + if self.num_filters is None: + self.num_filters = [32 * i for i in range(1, self.model_depth + 1)] + + if self.dilation_rate is None: + self.dilation_rate = [ + 2**layer_num for layer_num in range(1, self.model_depth + 1) + ] + else: + assert isinstance(self.dilation_rate, list) + assert len(self.dilation_rate) == self.model_depth def build_network(self, input_shape): - - def DCNNLayer(inputs, num_filters, layer_num): - _dilation = 2 ** layer_num + """Construct a network and return its input and output layers. + + Arguments + --------- + input_shape : tuple of shape = (n_timepoints (m), n_channels (d)) + The shape of the data fed into the input layer. + + Returns + ------- + encoder : a keras Model. + """ + def DCNNLayer(inputs, num_filters, dilation_rate): _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) x = WeightNormalization( - tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") + tf.keras.layers.Conv1D( + num_filters, + kernel_size=self.kernel_size, + dilation_rate=dilation_rate, + activation=self.activation, + padding="causal", + ) )(inputs) - x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) x = WeightNormalization( - tf.keras.layers.Conv1D(num_filters, dilation_rate=_dilation, padding="causal") + tf.keras.layers.Conv1D( + num_filters, + kernel_size=self.kernel_size, + dilation_rate=dilation_rate, + activation=self.activation, + padding="causal", + ) )(x) - x = tf.keras.layers.LeakyReLU(alpha=0.01)(x) return tf.keras.layers.Add([x, _add]) input_layer = tf.keras.layers.Input(input_shape) x = input_layer - for i in range(1, self.model_depth + 1): - x = DCNNLayer(x, self.num_filters, i) + for i in range(0, self.model_depth): + x = DCNNLayer(x, self.num_filters, self.dilation_rate[i]) x = tf.keras.layers.GlobalMaxPool1D()(x) - output_layer = tf.keras.layers.Dense(self.latent_dim)(x) + output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) - return tf.keras.Model(inputs=input_layer, outputs = output_layer) - \ No newline at end of file + encoder = tf.keras.Model(inputs=input_layer, outputs=output_layer) + return encoder From 02f387b8bda215efdbf5691625f49fe3e0fdf4b7 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Sat, 18 May 2024 13:12:28 +0530 Subject: [PATCH 04/32] minor fixes --- aeon/networks/__init__.py | 2 +- aeon/networks/_dcnn.py | 13 ++++++++++--- aeon/utils/networks/__init__.py | 5 +++-- aeon/utils/networks/weight_norm.py | 6 +++++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/aeon/networks/__init__.py b/aeon/networks/__init__.py index 0380534193..2db2793d37 100644 --- a/aeon/networks/__init__.py +++ b/aeon/networks/__init__.py @@ -17,6 +17,7 @@ from aeon.networks._ae_fcn import AEFCNNetwork from aeon.networks._ae_resnet import AEResNetNetwork from aeon.networks._cnn import CNNNetwork +from aeon.networks._dcnn import DCNNEncoderNetwork from aeon.networks._encoder import EncoderNetwork from aeon.networks._fcn import FCNNetwork from aeon.networks._inception import InceptionNetwork @@ -25,4 +26,3 @@ from aeon.networks._resnet import ResNetNetwork from aeon.networks._tapnet import TapNetNetwork from aeon.networks.base import BaseDeepNetwork -from aeon.networks._dcnn import DCNNEncoderNetwork diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 1011a0991d..683d5f1aa2 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -1,3 +1,7 @@ +"""Encoder using Dilated Convolutional Nerual Networks (DCNN).""" + +__maintainer__ = [] + import tensorflow as tf from aeon.networks.base import BaseDeepNetwork @@ -31,7 +35,8 @@ class DCNNEncoderNetwork(BaseDeepNetwork): ---------- .. [1] Network originally defined in: @article{franceschi2019unsupervised, - title={Unsupervised scalable representation learning for multivariate time series}, + title={Unsupervised scalable representation learning for multivariate time + series}, author={Franceschi, Jean-Yves and Dieuleveut, Aymeric and Jaggi, Martin}, journal={Advances in neural information processing systems}, volume={32}, @@ -82,6 +87,7 @@ def build_network(self, input_shape): ------- encoder : a keras Model. """ + def DCNNLayer(inputs, num_filters, dilation_rate): _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) x = WeightNormalization( @@ -102,8 +108,9 @@ def DCNNLayer(inputs, num_filters, dilation_rate): padding="causal", ) )(x) - return tf.keras.layers.Add([x, _add]) - + output = tf.keras.layers.Add()([x, _add]) + return output + input_layer = tf.keras.layers.Input(input_shape) x = input_layer diff --git a/aeon/utils/networks/__init__.py b/aeon/utils/networks/__init__.py index 86065a7584..003785833b 100644 --- a/aeon/utils/networks/__init__.py +++ b/aeon/utils/networks/__init__.py @@ -1,5 +1,6 @@ +"""Networks utility functions.""" + __all__ = [ "WeightNormalization", ] - -from aeon.utils.networks.weight_norm import WeightNormalization \ No newline at end of file +from aeon.utils.networks.weight_norm import WeightNormalization diff --git a/aeon/utils/networks/weight_norm.py b/aeon/utils/networks/weight_norm.py index 31aef1add7..9d7449cd98 100644 --- a/aeon/utils/networks/weight_norm.py +++ b/aeon/utils/networks/weight_norm.py @@ -1,7 +1,9 @@ import logging + import tensorflow as tf from typeguard import typechecked + @tf.keras.utils.register_keras_serializable(package="addons") class WeightNormalization(tf.keras.layers.Wrapper): """Wrapper function that performs weight normalization. @@ -12,7 +14,9 @@ class WeightNormalization(tf.keras.layers.Wrapper): Args: layer: A `tf.keras.layers.Layer` instance. data_init: If `True` use data dependent variable initialization. - Raises: + + Raises + ------ ValueError: If not initialized with a `Layer` instance. ValueError: If `Layer` does not contain a `kernel` of weights. NotImplementedError: If `data_init` is True and running graph execution. From 79fe9fb460df1664dc7c410474f64c22b88dc882 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 20 May 2024 20:32:26 +0530 Subject: [PATCH 05/32] Update DCNNEncoderNetwork --- aeon/networks/_dcnn.py | 28 ++--- aeon/utils/networks/__init__.py | 6 - aeon/utils/networks/weight_norm.py | 194 ----------------------------- 3 files changed, 8 insertions(+), 220 deletions(-) delete mode 100644 aeon/utils/networks/__init__.py delete mode 100644 aeon/utils/networks/weight_norm.py diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 683d5f1aa2..cb26cdabe8 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -5,8 +5,6 @@ import tensorflow as tf from aeon.networks.base import BaseDeepNetwork -from aeon.utils.networks import WeightNormalization - class DCNNEncoderNetwork(BaseDeepNetwork): """Establish the network structure for a DCNN-Encoder. @@ -16,8 +14,6 @@ class DCNNEncoderNetwork(BaseDeepNetwork): Parameters ---------- - loss_function: object - Objective function to be used for weight optimization. latent_space_dim: int, default=128 Dimension of the encoder's latent space. num_layers: int, default=4 @@ -28,8 +24,6 @@ class DCNNEncoderNetwork(BaseDeepNetwork): Number of filters used in convolution layers. dilation_rate: list, default=None The dilation rate for convolution. - activation: object, default=LeakyReLU - A Tensorflow activation function. References ---------- @@ -46,23 +40,19 @@ class DCNNEncoderNetwork(BaseDeepNetwork): def __init__( self, - loss_function, latent_space_dim=128, num_layers=4, kernel_size=3, num_filters=None, dilation_rate=None, - activation=tf.keras.layers.LeakyReLU, ): super().__init__() self.latent_space_dim = latent_space_dim self.kernel_size = kernel_size self.num_filters = num_filters - self.loss_functions = loss_function self.model_depth = num_layers self.dilation_rate = dilation_rate - self.activation = activation if self.num_filters is None: self.num_filters = [32 * i for i in range(1, self.model_depth + 1)] @@ -90,24 +80,22 @@ def build_network(self, input_shape): def DCNNLayer(inputs, num_filters, dilation_rate): _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) - x = WeightNormalization( - tf.keras.layers.Conv1D( + x = tf.keras.layers.Conv1D( num_filters, kernel_size=self.kernel_size, dilation_rate=dilation_rate, - activation=self.activation, padding="causal", - ) - )(inputs) - x = WeightNormalization( - tf.keras.layers.Conv1D( + kernel_regularizer="l2", + )(inputs) + x = tf.keras.layers.LeakyReLU()(x) + x = tf.keras.layers.Conv1D( num_filters, kernel_size=self.kernel_size, dilation_rate=dilation_rate, - activation=self.activation, padding="causal", - ) + kernel_regularizer="l2", )(x) + x = tf.keras.layers.LeakyReLU()(x) output = tf.keras.layers.Add()([x, _add]) return output @@ -115,7 +103,7 @@ def DCNNLayer(inputs, num_filters, dilation_rate): x = input_layer for i in range(0, self.model_depth): - x = DCNNLayer(x, self.num_filters, self.dilation_rate[i]) + x = DCNNLayer(x, self.num_filters[i], self.dilation_rate[i]) x = tf.keras.layers.GlobalMaxPool1D()(x) output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) diff --git a/aeon/utils/networks/__init__.py b/aeon/utils/networks/__init__.py deleted file mode 100644 index 003785833b..0000000000 --- a/aeon/utils/networks/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Networks utility functions.""" - -__all__ = [ - "WeightNormalization", -] -from aeon.utils.networks.weight_norm import WeightNormalization diff --git a/aeon/utils/networks/weight_norm.py b/aeon/utils/networks/weight_norm.py deleted file mode 100644 index 9d7449cd98..0000000000 --- a/aeon/utils/networks/weight_norm.py +++ /dev/null @@ -1,194 +0,0 @@ -import logging - -import tensorflow as tf -from typeguard import typechecked - - -@tf.keras.utils.register_keras_serializable(package="addons") -class WeightNormalization(tf.keras.layers.Wrapper): - """Wrapper function that performs weight normalization. - - Taken from: - https://github.com/tensorflow/addons/blob/master/tensorflow_addons/layers/wrappers.py - - Args: - layer: A `tf.keras.layers.Layer` instance. - data_init: If `True` use data dependent variable initialization. - - Raises - ------ - ValueError: If not initialized with a `Layer` instance. - ValueError: If `Layer` does not contain a `kernel` of weights. - NotImplementedError: If `data_init` is True and running graph execution. - """ - - @typechecked - def __init__(self, layer: tf.keras.layers, data_init: bool = True, **kwargs): - super().__init__(layer, **kwargs) - self.data_init = data_init - self._track_trackable(layer, name="layer") - self.is_rnn = isinstance(self.layer, tf.keras.layers.RNN) - - if self.data_init and self.is_rnn: - logging.warning( - "WeightNormalization: Using `data_init=True` with RNNs " - "is advised against by the paper. Use `data_init=False`." - ) - - def build(self, input_shape): - """Build `Layer`""" - input_shape = tf.TensorShape(input_shape) - self.input_spec = tf.keras.layers.InputSpec(shape=[None] + input_shape[1:]) - - if not self.layer.built: - self.layer.build(input_shape) - - kernel_layer = self.layer.cell if self.is_rnn else self.layer - - if not hasattr(kernel_layer, "kernel"): - raise ValueError( - "`WeightNormalization` must wrap a layer that" - " contains a `kernel` for weights" - ) - - if self.is_rnn: - kernel = kernel_layer.recurrent_kernel - else: - kernel = kernel_layer.kernel - - # The kernel's filter or unit dimension is -1 - self.layer_depth = int(kernel.shape[-1]) - self.kernel_norm_axes = list(range(kernel.shape.rank - 1)) - - self.g = self.add_weight( - name="g", - shape=(self.layer_depth,), - initializer="ones", - dtype=kernel.dtype, - trainable=True, - ) - self.v = kernel - - self._initialized = self.add_weight( - name="initialized", - shape=None, - initializer="zeros", - dtype=tf.dtypes.bool, - trainable=False, - ) - - if self.data_init: - # Used for data initialization in self._data_dep_init. - with tf.name_scope("data_dep_init"): - layer_config = tf.keras.layers.serialize(self.layer) - layer_config["config"]["trainable"] = False - self._naked_clone_layer = tf.keras.layers.deserialize(layer_config) - self._naked_clone_layer.build(input_shape) - self._naked_clone_layer.set_weights(self.layer.get_weights()) - if not self.is_rnn: - self._naked_clone_layer.activation = None - - self.built = True - - def call(self, inputs): - """Call `Layer`""" - - def _do_nothing(): - return tf.identity(self.g) - - def _update_weights(): - # Ensure we read `self.g` after _update_weights. - with tf.control_dependencies(self._initialize_weights(inputs)): - return tf.identity(self.g) - - g = tf.cond(self._initialized, _do_nothing, _update_weights) - - with tf.name_scope("compute_weights"): - # Replace kernel by normalized weight variable. - kernel = tf.nn.l2_normalize(self.v, axis=self.kernel_norm_axes) * g - - if self.is_rnn: - self.layer.cell.recurrent_kernel = kernel - update_kernel = tf.identity(self.layer.cell.recurrent_kernel) - else: - self.layer.kernel = kernel - update_kernel = tf.identity(self.layer.kernel) - - # Ensure we calculate result after updating kernel. - with tf.control_dependencies([update_kernel]): - outputs = self.layer(inputs) - return outputs - - def compute_output_shape(self, input_shape): - return tf.TensorShape(self.layer.compute_output_shape(input_shape).as_list()) - - def _initialize_weights(self, inputs): - """Initialize weight g. - - The initial value of g could either from the initial value in v, - or by the input value if self.data_init is True. - """ - with tf.control_dependencies( - [ - tf.debugging.assert_equal( # pylint: disable=bad-continuation - self._initialized, False, message="The layer has been initialized." - ) - ] - ): - if self.data_init: - assign_tensors = self._data_dep_init(inputs) - else: - assign_tensors = self._init_norm() - assign_tensors.append(self._initialized.assign(True)) - return assign_tensors - - def _init_norm(self): - """Set the weight g with the norm of the weight vector.""" - with tf.name_scope("init_norm"): - v_flat = tf.reshape(self.v, [-1, self.layer_depth]) - v_norm = tf.linalg.norm(v_flat, axis=0) - g_tensor = self.g.assign(tf.reshape(v_norm, (self.layer_depth,))) - return [g_tensor] - - def _data_dep_init(self, inputs): - """Data dependent initialization.""" - with tf.name_scope("data_dep_init"): - # Generate data dependent init values - x_init = self._naked_clone_layer(inputs) - data_norm_axes = list(range(x_init.shape.rank - 1)) - m_init, v_init = tf.nn.moments(x_init, data_norm_axes) - scale_init = 1.0 / tf.math.sqrt(v_init + 1e-10) - - # RNNs have fused kernels that are tiled - # Repeat scale_init to match the shape of fused kernel - # Note: This is only to support the operation, - # the paper advises against RNN+data_dep_init - if scale_init.shape[0] != self.g.shape[0]: - rep = int(self.g.shape[0] / scale_init.shape[0]) - scale_init = tf.tile(scale_init, [rep]) - - # Assign data dependent init values - g_tensor = self.g.assign(self.g * scale_init) - if hasattr(self.layer, "bias") and self.layer.bias is not None: - bias_tensor = self.layer.bias.assign(-m_init * scale_init) - return [g_tensor, bias_tensor] - else: - return [g_tensor] - - def get_config(self): - config = {"data_init": self.data_init} - base_config = super().get_config() - return {**base_config, **config} - - def remove(self): - kernel = tf.Variable( - tf.nn.l2_normalize(self.v, axis=self.kernel_norm_axes) * self.g, - name="recurrent_kernel" if self.is_rnn else "kernel", - ) - - if self.is_rnn: - self.layer.cell.recurrent_kernel = kernel - else: - self.layer.kernel = kernel - - return self.layer From 529e7b2b886cac9b22da1eb5a87d41cff4cbd02c Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 20 May 2024 20:37:37 +0530 Subject: [PATCH 06/32] add activation kwarg --- aeon/networks/_dcnn.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index cb26cdabe8..a3e68dd8ac 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -6,6 +6,7 @@ from aeon.networks.base import BaseDeepNetwork + class DCNNEncoderNetwork(BaseDeepNetwork): """Establish the network structure for a DCNN-Encoder. @@ -24,6 +25,8 @@ class DCNNEncoderNetwork(BaseDeepNetwork): Number of filters used in convolution layers. dilation_rate: list, default=None The dilation rate for convolution. + activation: str, default="relu" + The activation function used by convolution layers. References ---------- @@ -43,6 +46,7 @@ def __init__( latent_space_dim=128, num_layers=4, kernel_size=3, + activation="relu", num_filters=None, dilation_rate=None, ): @@ -53,6 +57,7 @@ def __init__( self.num_filters = num_filters self.model_depth = num_layers self.dilation_rate = dilation_rate + self.activation = activation if self.num_filters is None: self.num_filters = [32 * i for i in range(1, self.model_depth + 1)] @@ -78,22 +83,24 @@ def build_network(self, input_shape): encoder : a keras Model. """ - def DCNNLayer(inputs, num_filters, dilation_rate): + def DCNNLayer(inputs, num_filters, dilation_rate, activation): _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) x = tf.keras.layers.Conv1D( - num_filters, - kernel_size=self.kernel_size, - dilation_rate=dilation_rate, - padding="causal", - kernel_regularizer="l2", - )(inputs) + num_filters, + kernel_size=self.kernel_size, + dilation_rate=dilation_rate, + padding="causal", + kernel_regularizer="l2", + activation=activation, + )(inputs) x = tf.keras.layers.LeakyReLU()(x) x = tf.keras.layers.Conv1D( - num_filters, - kernel_size=self.kernel_size, - dilation_rate=dilation_rate, - padding="causal", - kernel_regularizer="l2", + num_filters, + kernel_size=self.kernel_size, + dilation_rate=dilation_rate, + padding="causal", + kernel_regularizer="l2", + activation=activation, )(x) x = tf.keras.layers.LeakyReLU()(x) output = tf.keras.layers.Add()([x, _add]) @@ -103,7 +110,12 @@ def DCNNLayer(inputs, num_filters, dilation_rate): x = input_layer for i in range(0, self.model_depth): - x = DCNNLayer(x, self.num_filters[i], self.dilation_rate[i]) + x = DCNNLayer( + x, + self.num_filters[i], + self.dilation_rate[i], + activation=self.activation, + ) x = tf.keras.layers.GlobalMaxPool1D()(x) output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) From 5ed6aeb99308068f5b6b676e8b8418b1be95ffac Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 20 May 2024 20:39:46 +0530 Subject: [PATCH 07/32] minor --- aeon/networks/_dcnn.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index a3e68dd8ac..ddc9664f50 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -93,7 +93,6 @@ def DCNNLayer(inputs, num_filters, dilation_rate, activation): kernel_regularizer="l2", activation=activation, )(inputs) - x = tf.keras.layers.LeakyReLU()(x) x = tf.keras.layers.Conv1D( num_filters, kernel_size=self.kernel_size, @@ -102,7 +101,6 @@ def DCNNLayer(inputs, num_filters, dilation_rate, activation): kernel_regularizer="l2", activation=activation, )(x) - x = tf.keras.layers.LeakyReLU()(x) output = tf.keras.layers.Add()([x, _add]) return output From 9c3f0b2e48ee7be1a18d96973f18218ee809ea71 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 27 May 2024 14:34:05 +0530 Subject: [PATCH 08/32] minor --- aeon/networks/_dcnn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index ddc9664f50..c982cdcf4e 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -2,8 +2,6 @@ __maintainer__ = [] -import tensorflow as tf - from aeon.networks.base import BaseDeepNetwork @@ -82,6 +80,7 @@ def build_network(self, input_shape): ------- encoder : a keras Model. """ + import tensorflow as tf def DCNNLayer(inputs, num_filters, dilation_rate, activation): _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) From 6c7ec5a8eea961656c4fe39465f712bd34b69560 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 27 May 2024 20:17:44 +0530 Subject: [PATCH 09/32] minor fixes --- aeon/networks/_dcnn.py | 107 ++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index c982cdcf4e..7a45701fc0 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -1,30 +1,30 @@ -"""Encoder using Dilated Convolutional Nerual Networks (DCNN).""" +"""Dilated Convolutional Nerual Networks (DCNN) Model.""" __maintainer__ = [] from aeon.networks.base import BaseDeepNetwork -class DCNNEncoderNetwork(BaseDeepNetwork): - """Establish the network structure for a DCNN-Encoder. +class DCNNNetwork(BaseDeepNetwork): + """Establish the network structure for a DCNN-Model. - Dilated Convolutional Neural Network based Encoder + Dilated Convolutional Neural Network based Model for low-rank embeddings. Parameters ---------- latent_space_dim: int, default=128 - Dimension of the encoder's latent space. + Dimension of the models's latent space. num_layers: int, default=4 Number of convolution layers. kernel_size: int, default=3 Size of the 1D Convolutional Kernel. + activation: str, default="relu" + The activation function used by convolution layers. num_filters: int, default=None Number of filters used in convolution layers. dilation_rate: list, default=None The dilation rate for convolution. - activation: str, default="relu" - The activation function used by convolution layers. References ---------- @@ -53,21 +53,10 @@ def __init__( self.latent_space_dim = latent_space_dim self.kernel_size = kernel_size self.num_filters = num_filters - self.model_depth = num_layers + self.num_layers = num_layers self.dilation_rate = dilation_rate self.activation = activation - if self.num_filters is None: - self.num_filters = [32 * i for i in range(1, self.model_depth + 1)] - - if self.dilation_rate is None: - self.dilation_rate = [ - 2**layer_num for layer_num in range(1, self.model_depth + 1) - ] - else: - assert isinstance(self.dilation_rate, list) - assert len(self.dilation_rate) == self.model_depth - def build_network(self, input_shape): """Construct a network and return its input and output layers. @@ -78,43 +67,71 @@ def build_network(self, input_shape): Returns ------- - encoder : a keras Model. + model : a keras Model. """ import tensorflow as tf - def DCNNLayer(inputs, num_filters, dilation_rate, activation): - _add = tf.keras.layers.Conv1D(num_filters, kernel_size=1)(inputs) - x = tf.keras.layers.Conv1D( - num_filters, - kernel_size=self.kernel_size, - dilation_rate=dilation_rate, - padding="causal", - kernel_regularizer="l2", - activation=activation, - )(inputs) - x = tf.keras.layers.Conv1D( - num_filters, - kernel_size=self.kernel_size, - dilation_rate=dilation_rate, - padding="causal", - kernel_regularizer="l2", - activation=activation, - )(x) - output = tf.keras.layers.Add()([x, _add]) - return output + if self.num_filters is None: + self.num_filters = [32 * i for i in range(1, self.num_layers + 1)] + elif isinstance(self.num_filters, list): + assert len(self.num_filters) == self.num_layers + + if self.dilation_rate is None: + self.dilation_rate = [ + 2**layer_num for layer_num in range(1, self.num_layers + 1) + ] + else: + assert isinstance(self.dilation_rate, list) + assert len(self.dilation_rate) == self.num_layers + + if isinstance(self.kernel_size, int): + self.kernel_size = [self.kernel_size for _ in range(self.num_layers)] + elif isinstance(self.kernel_size, list): + assert len(self.kernel_size) == self.num_layers + + if isinstance(self.activation, str): + self.activation = [self.activation for _ in range(self.num_layers)] + elif isinstance(self.kernel_size, list): + assert len(self.activation) == self.num_layers input_layer = tf.keras.layers.Input(input_shape) x = input_layer - for i in range(0, self.model_depth): - x = DCNNLayer( + for i in range(0, self.num_layers): + x = self._dcnn_layer( x, self.num_filters[i], self.dilation_rate[i], - activation=self.activation, + _activation=self.activation[i], + _kernel_size=self.kernel_size[i], ) x = tf.keras.layers.GlobalMaxPool1D()(x) output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) - encoder = tf.keras.Model(inputs=input_layer, outputs=output_layer) - return encoder + model = tf.keras.Model(inputs=input_layer, outputs=output_layer) + return model + + def _dcnn_layer( + self, _inputs, _num_filters, _dilation_rate, _activation, _kernel_size + ): + import tensorflow as tf + + _add = tf.keras.layers.Conv1D(_num_filters, kernel_size=1)(_inputs) + x = tf.keras.layers.Conv1D( + _num_filters, + kernel_size=_kernel_size, + dilation_rate=_dilation_rate, + padding="causal", + kernel_regularizer="l2", + activation=_activation, + )(_inputs) + x = tf.keras.layers.Conv1D( + _num_filters, + kernel_size=_kernel_size, + dilation_rate=_dilation_rate, + padding="causal", + kernel_regularizer="l2", + activation=_activation, + )(x) + output = tf.keras.layers.Add()([x, _add]) + return output From 780a775aaedf2c9011b0689320dabbfa8e0e9db2 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 27 May 2024 20:20:02 +0530 Subject: [PATCH 10/32] update class name --- aeon/networks/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aeon/networks/__init__.py b/aeon/networks/__init__.py index 2db2793d37..e041870a26 100644 --- a/aeon/networks/__init__.py +++ b/aeon/networks/__init__.py @@ -12,12 +12,12 @@ "AEFCNNetwork", "AEResNetNetwork", "LITENetwork", - "DCNNEncoderNetwork", + "DCNNNetwork", ] from aeon.networks._ae_fcn import AEFCNNetwork from aeon.networks._ae_resnet import AEResNetNetwork from aeon.networks._cnn import CNNNetwork -from aeon.networks._dcnn import DCNNEncoderNetwork +from aeon.networks._dcnn import DCNNNetwork from aeon.networks._encoder import EncoderNetwork from aeon.networks._fcn import FCNNetwork from aeon.networks._inception import InceptionNetwork From d24b0ed1070e8f490fe1300f89a010e13225ccec Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 31 May 2024 21:16:47 +0530 Subject: [PATCH 11/32] minor --- aeon/networks/_dcnn.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 7a45701fc0..60a7d65d20 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -72,26 +72,30 @@ def build_network(self, input_shape): import tensorflow as tf if self.num_filters is None: - self.num_filters = [32 * i for i in range(1, self.num_layers + 1)] + self._num_filters = [32 * i for i in range(1, self.num_layers + 1)] elif isinstance(self.num_filters, list): + self._num_filters = self.num_filters assert len(self.num_filters) == self.num_layers if self.dilation_rate is None: - self.dilation_rate = [ + self._dilation_rate = [ 2**layer_num for layer_num in range(1, self.num_layers + 1) ] else: + self._dilation_rate = self.dilation_rate assert isinstance(self.dilation_rate, list) assert len(self.dilation_rate) == self.num_layers if isinstance(self.kernel_size, int): self.kernel_size = [self.kernel_size for _ in range(self.num_layers)] elif isinstance(self.kernel_size, list): + self._kernel_size = self.kernel_size assert len(self.kernel_size) == self.num_layers if isinstance(self.activation, str): self.activation = [self.activation for _ in range(self.num_layers)] - elif isinstance(self.kernel_size, list): + elif isinstance(self.activation, list): + self._activation = self.activation assert len(self.activation) == self.num_layers input_layer = tf.keras.layers.Input(input_shape) @@ -100,10 +104,10 @@ def build_network(self, input_shape): for i in range(0, self.num_layers): x = self._dcnn_layer( x, - self.num_filters[i], - self.dilation_rate[i], - _activation=self.activation[i], - _kernel_size=self.kernel_size[i], + self._num_filters[i], + self._dilation_rate[i], + _activation=self._activation[i], + _kernel_size=self._kernel_size[i], ) x = tf.keras.layers.GlobalMaxPool1D()(x) output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) From 14f41c4eae7f1db841f09f90255d95a2543f10ed Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 31 May 2024 21:41:11 +0530 Subject: [PATCH 12/32] minor --- aeon/networks/_dcnn.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 60a7d65d20..945f38237e 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -81,6 +81,8 @@ def build_network(self, input_shape): self._dilation_rate = [ 2**layer_num for layer_num in range(1, self.num_layers + 1) ] + elif isinstance(self.dilation_rate, int): + self._dilation_rate = [self._dilation_rate for _ in range(self.num_layers)] else: self._dilation_rate = self.dilation_rate assert isinstance(self.dilation_rate, list) From d93dbd3f15d493ab42ac3bdba92e7c73685b5bbd Mon Sep 17 00:00:00 2001 From: aadya940 Date: Sat, 1 Jun 2024 16:26:13 +0530 Subject: [PATCH 13/32] Add temporal_latent_space kwarg --- aeon/networks/_dcnn.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 945f38237e..ab578d2542 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -47,6 +47,7 @@ def __init__( activation="relu", num_filters=None, dilation_rate=None, + temporal_latent_space=False, ): super().__init__() @@ -56,6 +57,7 @@ def __init__( self.num_layers = num_layers self.dilation_rate = dilation_rate self.activation = activation + self.temporal_latent_space = temporal_latent_space def build_network(self, input_shape): """Construct a network and return its input and output layers. @@ -111,8 +113,16 @@ def build_network(self, input_shape): _activation=self._activation[i], _kernel_size=self._kernel_size[i], ) - x = tf.keras.layers.GlobalMaxPool1D()(x) - output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) + + if not self.temporal_latent_space: + x = tf.keras.layers.GlobalMaxPool1D()(x) + output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) + + elif self.temporal_latent_space: + output_layer = tf.keras.layers.Conv1D( + filters=self.latent_space_dim, + kernel_size=1, + )(x) model = tf.keras.Model(inputs=input_layer, outputs=output_layer) return model From 8e98bd54ab03b9abcab7ab1c2671524ccb7ce072 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Sun, 2 Jun 2024 19:48:40 +0530 Subject: [PATCH 14/32] minor --- aeon/networks/_dcnn.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index ab578d2542..c970ba80c8 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -15,6 +15,8 @@ class DCNNNetwork(BaseDeepNetwork): ---------- latent_space_dim: int, default=128 Dimension of the models's latent space. + temporal_latent_space : bool, default = False + Flag to choose whether the latent space is an MTS or Euclidean space. num_layers: int, default=4 Number of convolution layers. kernel_size: int, default=3 From f66b3eaadef7fcca32ca3a8b6f7a268aa7df6985 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Sun, 2 Jun 2024 19:50:32 +0530 Subject: [PATCH 15/32] minor --- aeon/networks/_dcnn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index c970ba80c8..f425e300ed 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -44,12 +44,12 @@ class DCNNNetwork(BaseDeepNetwork): def __init__( self, latent_space_dim=128, + temporal_latent_space=False, num_layers=4, kernel_size=3, activation="relu", num_filters=None, dilation_rate=None, - temporal_latent_space=False, ): super().__init__() From dd1858182f0781bb9f71c56f72ba1be33e136b79 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 3 Jun 2024 15:31:12 +0530 Subject: [PATCH 16/32] Add test for DCNNNetwork --- aeon/networks/_dcnn.py | 6 ++-- aeon/networks/tests/test_dcnn.py | 50 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 aeon/networks/tests/test_dcnn.py diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index f425e300ed..a6ff512e3f 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -93,16 +93,16 @@ def build_network(self, input_shape): assert len(self.dilation_rate) == self.num_layers if isinstance(self.kernel_size, int): - self.kernel_size = [self.kernel_size for _ in range(self.num_layers)] + self._kernel_size = [self.kernel_size for _ in range(self.num_layers)] elif isinstance(self.kernel_size, list): self._kernel_size = self.kernel_size assert len(self.kernel_size) == self.num_layers if isinstance(self.activation, str): - self.activation = [self.activation for _ in range(self.num_layers)] + self._activation = [self.activation for _ in range(self.num_layers)] elif isinstance(self.activation, list): self._activation = self.activation - assert len(self.activation) == self.num_layers + assert len(self._activation) == self.num_layers input_layer = tf.keras.layers.Input(input_shape) diff --git a/aeon/networks/tests/test_dcnn.py b/aeon/networks/tests/test_dcnn.py new file mode 100644 index 0000000000..5c9638b908 --- /dev/null +++ b/aeon/networks/tests/test_dcnn.py @@ -0,0 +1,50 @@ +"""Tests for the AEDRNN Model.""" + +import random + +import pytest + +from aeon.networks import DCNNNetwork +from aeon.utils.validation._dependencies import _check_soft_dependencies + + +def pytest_generate_tests(): + """Parameter generation for test cases.""" + latent_space_dim_range = [32, 128, 256] + num_layers = range(1, 5) + temporal_latent_space_options = [True, False] + + test_params = [] + for latent_space_dim in latent_space_dim_range: + for n_layers_encoder in num_layers: + for temporal_latent_space in temporal_latent_space_options: + test_params.append( + dict( + latent_space_dim=latent_space_dim, + num_layers=n_layers_encoder, + dilation_rate=None, + activation=random.choice(["relu", "tanh"]), + num_filters=[ + random.choice([50, 25, 100]) + for _ in range(n_layers_encoder) + ], + temporal_latent_space=temporal_latent_space, + ) + ) + return test_params + + +params = pytest_generate_tests() + + +@pytest.mark.skipif( + not _check_soft_dependencies(["tensorflow"]), + severity="none", + reason="tensorflow soft dependency not present", +) +@pytest.mark.parametrize("params", params) +def test_aedrnnnetwork_init(params): + """Test whether AEDRNNNetwork initializes correctly for various parameters.""" + dcnnnet = DCNNNetwork(**params) + model = dcnnnet.build_network((1000, 5)) + assert model is not None From 2ef7816932fdb6510f14d1071c65c75d52fe4d5b Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 3 Jun 2024 15:57:26 +0530 Subject: [PATCH 17/32] minor --- aeon/networks/tests/test_dcnn.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aeon/networks/tests/test_dcnn.py b/aeon/networks/tests/test_dcnn.py index 5c9638b908..8c9a768374 100644 --- a/aeon/networks/tests/test_dcnn.py +++ b/aeon/networks/tests/test_dcnn.py @@ -38,9 +38,8 @@ def pytest_generate_tests(): @pytest.mark.skipif( - not _check_soft_dependencies(["tensorflow"]), - severity="none", - reason="tensorflow soft dependency not present", + not _check_soft_dependencies(["tensorflow"], severity="none"), + reason="tensorflow soft dependency not present.", ) @pytest.mark.parametrize("params", params) def test_aedrnnnetwork_init(params): From f3b6a57d6ba5de407b6f034713d0f5f6708749c6 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Tue, 4 Jun 2024 12:52:32 +0530 Subject: [PATCH 18/32] refactor test --- aeon/networks/tests/test_dcnn.py | 69 +++++++++++++++++--------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/aeon/networks/tests/test_dcnn.py b/aeon/networks/tests/test_dcnn.py index 8c9a768374..3b0a966bf9 100644 --- a/aeon/networks/tests/test_dcnn.py +++ b/aeon/networks/tests/test_dcnn.py @@ -1,4 +1,4 @@ -"""Tests for the AEDRNN Model.""" +"""Tests for the DCNN Model.""" import random @@ -8,42 +8,45 @@ from aeon.utils.validation._dependencies import _check_soft_dependencies -def pytest_generate_tests(): - """Parameter generation for test cases.""" - latent_space_dim_range = [32, 128, 256] - num_layers = range(1, 5) - temporal_latent_space_options = [True, False] - - test_params = [] - for latent_space_dim in latent_space_dim_range: - for n_layers_encoder in num_layers: - for temporal_latent_space in temporal_latent_space_options: - test_params.append( - dict( - latent_space_dim=latent_space_dim, - num_layers=n_layers_encoder, - dilation_rate=None, - activation=random.choice(["relu", "tanh"]), - num_filters=[ - random.choice([50, 25, 100]) - for _ in range(n_layers_encoder) - ], - temporal_latent_space=temporal_latent_space, - ) - ) - return test_params - - -params = pytest_generate_tests() +@pytest.mark.skipif( + not _check_soft_dependencies(["tensorflow"], severity="none"), + reason="Tensorflow soft dependency unavailable.", +) +@pytest.mark.parametrize( + "latent_space_dim,num_layers,temporal_latent_space", + [ + (32, 1, True), + (128, 2, False), + (256, 3, True), + (64, 4, False), + ], +) +def test_dcnnnetwork_init(latent_space_dim, num_layers, temporal_latent_space): + """Test whether DCNNNetwork initializes correctly for various parameters.""" + dcnnnet = DCNNNetwork( + latent_space_dim=latent_space_dim, + num_layers=num_layers, + temporal_latent_space=temporal_latent_space, + activation=random.choice(["relu", "tanh"]), + num_filters=[random.choice([50, 25, 100]) for _ in range(num_layers)], + ) + model = dcnnnet.build_network((1000, 5)) + assert model is not None @pytest.mark.skipif( not _check_soft_dependencies(["tensorflow"], severity="none"), - reason="tensorflow soft dependency not present.", + reason="Tensorflow soft dependency unavailable.", ) -@pytest.mark.parametrize("params", params) -def test_aedrnnnetwork_init(params): - """Test whether AEDRNNNetwork initializes correctly for various parameters.""" - dcnnnet = DCNNNetwork(**params) +@pytest.mark.parametrize("activation", ["relu", "tanh"]) +def test_dcnnnetwork_activations(activation): + """Test whether DCNNNetwork initializes correctly with different activations.""" + dcnnnet = DCNNNetwork( + latent_space_dim=64, + num_layers=2, + temporal_latent_space=True, + activation=activation, + num_filters=[50, 50], + ) model = dcnnnet.build_network((1000, 5)) assert model is not None From 802225047371202408ca75590343ea1daea0b206 Mon Sep 17 00:00:00 2001 From: Aadya Chinubhai <77720426+aadya940@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:39:53 +0530 Subject: [PATCH 19/32] Update test_dcnn.py --- aeon/networks/tests/test_dcnn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aeon/networks/tests/test_dcnn.py b/aeon/networks/tests/test_dcnn.py index 3b0a966bf9..3b23b3935b 100644 --- a/aeon/networks/tests/test_dcnn.py +++ b/aeon/networks/tests/test_dcnn.py @@ -48,5 +48,5 @@ def test_dcnnnetwork_activations(activation): activation=activation, num_filters=[50, 50], ) - model = dcnnnet.build_network((1000, 5)) + model = dcnnnet.build_network((150, 5)) assert model is not None From 0c92a3bb32b23f47571f4b7c5badb586ef775321 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 21 Jun 2024 19:51:44 +0530 Subject: [PATCH 20/32] update base class --- aeon/networks/_dcnn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index a6ff512e3f..563c4a9214 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -2,10 +2,10 @@ __maintainer__ = [] -from aeon.networks.base import BaseDeepNetwork +from aeon.networks.base import BaseDeepLearningNetwork -class DCNNNetwork(BaseDeepNetwork): +class DCNNNetwork(BaseDeepLearningNetwork): """Establish the network structure for a DCNN-Model. Dilated Convolutional Neural Network based Model From 29e7283aa3476950df10c64c0a7ab43e7de0d6f7 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Tue, 25 Jun 2024 21:08:34 +0530 Subject: [PATCH 21/32] fix bug --- aeon/networks/_dcnn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 563c4a9214..efa2adf931 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -126,8 +126,7 @@ def build_network(self, input_shape): kernel_size=1, )(x) - model = tf.keras.Model(inputs=input_layer, outputs=output_layer) - return model + return input_layer, output_layer def _dcnn_layer( self, _inputs, _num_filters, _dilation_rate, _activation, _kernel_size From 2740bc13ba25008d08419b19dd84acedab199f44 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Wed, 10 Jul 2024 14:56:07 +0530 Subject: [PATCH 22/32] remove temporal latent space kwarg --- aeon/networks/_dcnn.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index efa2adf931..bb26968e9d 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -15,8 +15,6 @@ class DCNNNetwork(BaseDeepLearningNetwork): ---------- latent_space_dim: int, default=128 Dimension of the models's latent space. - temporal_latent_space : bool, default = False - Flag to choose whether the latent space is an MTS or Euclidean space. num_layers: int, default=4 Number of convolution layers. kernel_size: int, default=3 @@ -44,7 +42,6 @@ class DCNNNetwork(BaseDeepLearningNetwork): def __init__( self, latent_space_dim=128, - temporal_latent_space=False, num_layers=4, kernel_size=3, activation="relu", @@ -59,7 +56,6 @@ def __init__( self.num_layers = num_layers self.dilation_rate = dilation_rate self.activation = activation - self.temporal_latent_space = temporal_latent_space def build_network(self, input_shape): """Construct a network and return its input and output layers. @@ -116,15 +112,8 @@ def build_network(self, input_shape): _kernel_size=self._kernel_size[i], ) - if not self.temporal_latent_space: - x = tf.keras.layers.GlobalMaxPool1D()(x) - output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) - - elif self.temporal_latent_space: - output_layer = tf.keras.layers.Conv1D( - filters=self.latent_space_dim, - kernel_size=1, - )(x) + x = tf.keras.layers.GlobalMaxPool1D()(x) + output_layer = tf.keras.layers.Dense(self.latent_space_dim)(x) return input_layer, output_layer From d458f18c1c2d3342f41d520dcd1e3ef5cd327fdd Mon Sep 17 00:00:00 2001 From: aadya940 Date: Wed, 10 Jul 2024 14:58:45 +0530 Subject: [PATCH 23/32] add _config --- aeon/networks/_dcnn.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index bb26968e9d..528a6d7df9 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -39,6 +39,12 @@ class DCNNNetwork(BaseDeepLearningNetwork): } """ + _config = { + "python_dependencies": ["tensorflow"], + "python_version": "<3.12", + "structure": "encoder", + } + def __init__( self, latent_space_dim=128, From af42c199c70900b3afe7a653c63e87333d149a0b Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 12 Jul 2024 12:23:29 +0530 Subject: [PATCH 24/32] remove tls from tests --- aeon/networks/tests/test_dcnn.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/aeon/networks/tests/test_dcnn.py b/aeon/networks/tests/test_dcnn.py index 3b23b3935b..dd4eb43801 100644 --- a/aeon/networks/tests/test_dcnn.py +++ b/aeon/networks/tests/test_dcnn.py @@ -13,20 +13,19 @@ reason="Tensorflow soft dependency unavailable.", ) @pytest.mark.parametrize( - "latent_space_dim,num_layers,temporal_latent_space", + "latent_space_dim,num_layers", [ - (32, 1, True), - (128, 2, False), - (256, 3, True), - (64, 4, False), + (32, 1), + (128, 2), + (256, 3), + (64, 4), ], ) -def test_dcnnnetwork_init(latent_space_dim, num_layers, temporal_latent_space): +def test_dcnnnetwork_init(latent_space_dim, num_layers): """Test whether DCNNNetwork initializes correctly for various parameters.""" dcnnnet = DCNNNetwork( latent_space_dim=latent_space_dim, num_layers=num_layers, - temporal_latent_space=temporal_latent_space, activation=random.choice(["relu", "tanh"]), num_filters=[random.choice([50, 25, 100]) for _ in range(num_layers)], ) @@ -44,7 +43,6 @@ def test_dcnnnetwork_activations(activation): dcnnnet = DCNNNetwork( latent_space_dim=64, num_layers=2, - temporal_latent_space=True, activation=activation, num_filters=[50, 50], ) From 09ff6066b13fe12f22cb14b8ca21b56d7d1af9d1 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Wed, 7 Aug 2024 20:32:39 +0530 Subject: [PATCH 25/32] enhance tests --- aeon/networks/tests/test_all_networks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aeon/networks/tests/test_all_networks.py b/aeon/networks/tests/test_all_networks.py index b5bcfc4b2e..3753cc9d46 100644 --- a/aeon/networks/tests/test_all_networks.py +++ b/aeon/networks/tests/test_all_networks.py @@ -128,7 +128,10 @@ def test_all_networks_params(network): elif network.__name__ in ["InceptionNetwork"]: attr = [attr] * my_network.depth else: - attr = [attr] * my_network.n_layers + if hasattr(my_network, "n_layers"): + attr = [attr] * my_network.n_layers + elif hasattr(my_network, "num_layers"): + attr = [attr] * my_network.num_layers params[attrname] = attr if params: From c8f4a24a908a8d3a0d8a9241f36efb20cf6dae3b Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 9 Aug 2024 12:48:11 +0530 Subject: [PATCH 26/32] num_layers -> n_layers --- aeon/networks/_dcnn.py | 26 ++++++++++++------------ aeon/networks/tests/test_all_networks.py | 5 +---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 528a6d7df9..aa880a5281 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -15,7 +15,7 @@ class DCNNNetwork(BaseDeepLearningNetwork): ---------- latent_space_dim: int, default=128 Dimension of the models's latent space. - num_layers: int, default=4 + n_layers: int, default=4 Number of convolution layers. kernel_size: int, default=3 Size of the 1D Convolutional Kernel. @@ -48,7 +48,7 @@ class DCNNNetwork(BaseDeepLearningNetwork): def __init__( self, latent_space_dim=128, - num_layers=4, + n_layers=4, kernel_size=3, activation="relu", num_filters=None, @@ -59,7 +59,7 @@ def __init__( self.latent_space_dim = latent_space_dim self.kernel_size = kernel_size self.num_filters = num_filters - self.num_layers = num_layers + self.n_layers = n_layers self.dilation_rate = dilation_rate self.activation = activation @@ -78,38 +78,38 @@ def build_network(self, input_shape): import tensorflow as tf if self.num_filters is None: - self._num_filters = [32 * i for i in range(1, self.num_layers + 1)] + self._num_filters = [32 * i for i in range(1, self.n_layers + 1)] elif isinstance(self.num_filters, list): self._num_filters = self.num_filters - assert len(self.num_filters) == self.num_layers + assert len(self.num_filters) == self.n_layers if self.dilation_rate is None: self._dilation_rate = [ - 2**layer_num for layer_num in range(1, self.num_layers + 1) + 2**layer_num for layer_num in range(1, self.n_layers + 1) ] elif isinstance(self.dilation_rate, int): - self._dilation_rate = [self._dilation_rate for _ in range(self.num_layers)] + self._dilation_rate = [self._dilation_rate for _ in range(self.n_layers)] else: self._dilation_rate = self.dilation_rate assert isinstance(self.dilation_rate, list) - assert len(self.dilation_rate) == self.num_layers + assert len(self.dilation_rate) == self.n_layers if isinstance(self.kernel_size, int): - self._kernel_size = [self.kernel_size for _ in range(self.num_layers)] + self._kernel_size = [self.kernel_size for _ in range(self.n_layers)] elif isinstance(self.kernel_size, list): self._kernel_size = self.kernel_size - assert len(self.kernel_size) == self.num_layers + assert len(self.kernel_size) == self.n_layers if isinstance(self.activation, str): - self._activation = [self.activation for _ in range(self.num_layers)] + self._activation = [self.activation for _ in range(self.n_layers)] elif isinstance(self.activation, list): self._activation = self.activation - assert len(self._activation) == self.num_layers + assert len(self._activation) == self.n_layers input_layer = tf.keras.layers.Input(input_shape) x = input_layer - for i in range(0, self.num_layers): + for i in range(0, self.n_layers): x = self._dcnn_layer( x, self._num_filters[i], diff --git a/aeon/networks/tests/test_all_networks.py b/aeon/networks/tests/test_all_networks.py index 3753cc9d46..b5bcfc4b2e 100644 --- a/aeon/networks/tests/test_all_networks.py +++ b/aeon/networks/tests/test_all_networks.py @@ -128,10 +128,7 @@ def test_all_networks_params(network): elif network.__name__ in ["InceptionNetwork"]: attr = [attr] * my_network.depth else: - if hasattr(my_network, "n_layers"): - attr = [attr] * my_network.n_layers - elif hasattr(my_network, "num_layers"): - attr = [attr] * my_network.num_layers + attr = [attr] * my_network.n_layers params[attrname] = attr if params: From 65f150cf8b11140662cbb2ffe18d583ae665426a Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 9 Aug 2024 13:12:19 +0530 Subject: [PATCH 27/32] num_layers => n_layers --- aeon/networks/tests/test_dcnn.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aeon/networks/tests/test_dcnn.py b/aeon/networks/tests/test_dcnn.py index dd4eb43801..8aa6caad41 100644 --- a/aeon/networks/tests/test_dcnn.py +++ b/aeon/networks/tests/test_dcnn.py @@ -13,7 +13,7 @@ reason="Tensorflow soft dependency unavailable.", ) @pytest.mark.parametrize( - "latent_space_dim,num_layers", + "latent_space_dim,n_layers", [ (32, 1), (128, 2), @@ -21,13 +21,13 @@ (64, 4), ], ) -def test_dcnnnetwork_init(latent_space_dim, num_layers): +def test_dcnnnetwork_init(latent_space_dim, n_layers): """Test whether DCNNNetwork initializes correctly for various parameters.""" dcnnnet = DCNNNetwork( latent_space_dim=latent_space_dim, - num_layers=num_layers, + n_layers=n_layers, activation=random.choice(["relu", "tanh"]), - num_filters=[random.choice([50, 25, 100]) for _ in range(num_layers)], + num_filters=[random.choice([50, 25, 100]) for _ in range(n_layers)], ) model = dcnnnet.build_network((1000, 5)) assert model is not None @@ -42,7 +42,7 @@ def test_dcnnnetwork_activations(activation): """Test whether DCNNNetwork initializes correctly with different activations.""" dcnnnet = DCNNNetwork( latent_space_dim=64, - num_layers=2, + n_layers=2, activation=activation, num_filters=[50, 50], ) From 995518afbee85e07b57d7d98fcb80ce0524d21aa Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 9 Aug 2024 13:37:29 +0530 Subject: [PATCH 28/32] typo --- aeon/networks/_dcnn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index aa880a5281..be237a19be 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -88,7 +88,7 @@ def build_network(self, input_shape): 2**layer_num for layer_num in range(1, self.n_layers + 1) ] elif isinstance(self.dilation_rate, int): - self._dilation_rate = [self._dilation_rate for _ in range(self.n_layers)] + self._dilation_rate = [self.dilation_rate for _ in range(self.n_layers)] else: self._dilation_rate = self.dilation_rate assert isinstance(self.dilation_rate, list) From 64f7a456034f5c026b9b7360160bdb8af9a7715b Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 12 Aug 2024 13:49:15 +0530 Subject: [PATCH 29/32] num => n --- aeon/networks/_dcnn.py | 26 +++++++++++++------------- aeon/networks/tests/test_dcnn.py | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index be237a19be..daebd2140b 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -21,7 +21,7 @@ class DCNNNetwork(BaseDeepLearningNetwork): Size of the 1D Convolutional Kernel. activation: str, default="relu" The activation function used by convolution layers. - num_filters: int, default=None + n_filters: int, default=None Number of filters used in convolution layers. dilation_rate: list, default=None The dilation rate for convolution. @@ -51,14 +51,14 @@ def __init__( n_layers=4, kernel_size=3, activation="relu", - num_filters=None, + n_filters=None, dilation_rate=None, ): super().__init__() self.latent_space_dim = latent_space_dim self.kernel_size = kernel_size - self.num_filters = num_filters + self.n_filters = n_filters self.n_layers = n_layers self.dilation_rate = dilation_rate self.activation = activation @@ -77,11 +77,11 @@ def build_network(self, input_shape): """ import tensorflow as tf - if self.num_filters is None: - self._num_filters = [32 * i for i in range(1, self.n_layers + 1)] - elif isinstance(self.num_filters, list): - self._num_filters = self.num_filters - assert len(self.num_filters) == self.n_layers + if self.n_filters is None: + self._n_filters = [32 * i for i in range(1, self.n_layers + 1)] + elif isinstance(self.n_filters, list): + self._n_filters = self.n_filters + assert len(self.n_filters) == self.n_layers if self.dilation_rate is None: self._dilation_rate = [ @@ -112,7 +112,7 @@ def build_network(self, input_shape): for i in range(0, self.n_layers): x = self._dcnn_layer( x, - self._num_filters[i], + self._n_filters[i], self._dilation_rate[i], _activation=self._activation[i], _kernel_size=self._kernel_size[i], @@ -124,13 +124,13 @@ def build_network(self, input_shape): return input_layer, output_layer def _dcnn_layer( - self, _inputs, _num_filters, _dilation_rate, _activation, _kernel_size + self, _inputs, _n_filters, _dilation_rate, _activation, _kernel_size ): import tensorflow as tf - _add = tf.keras.layers.Conv1D(_num_filters, kernel_size=1)(_inputs) + _add = tf.keras.layers.Conv1D(_n_filters, kernel_size=1)(_inputs) x = tf.keras.layers.Conv1D( - _num_filters, + _n_filters, kernel_size=_kernel_size, dilation_rate=_dilation_rate, padding="causal", @@ -138,7 +138,7 @@ def _dcnn_layer( activation=_activation, )(_inputs) x = tf.keras.layers.Conv1D( - _num_filters, + _n_filters, kernel_size=_kernel_size, dilation_rate=_dilation_rate, padding="causal", diff --git a/aeon/networks/tests/test_dcnn.py b/aeon/networks/tests/test_dcnn.py index 8aa6caad41..70ba35173d 100644 --- a/aeon/networks/tests/test_dcnn.py +++ b/aeon/networks/tests/test_dcnn.py @@ -27,7 +27,7 @@ def test_dcnnnetwork_init(latent_space_dim, n_layers): latent_space_dim=latent_space_dim, n_layers=n_layers, activation=random.choice(["relu", "tanh"]), - num_filters=[random.choice([50, 25, 100]) for _ in range(n_layers)], + n_filters=[random.choice([50, 25, 100]) for _ in range(n_layers)], ) model = dcnnnet.build_network((1000, 5)) assert model is not None @@ -44,7 +44,7 @@ def test_dcnnnetwork_activations(activation): latent_space_dim=64, n_layers=2, activation=activation, - num_filters=[50, 50], + n_filters=[50, 50], ) model = dcnnnet.build_network((150, 5)) assert model is not None From af933eeb011fbebf393dbd7ecc45a4531b92c316 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 16 Aug 2024 21:58:31 +0530 Subject: [PATCH 30/32] some fixes --- aeon/networks/_dcnn.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index daebd2140b..13387101f6 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -17,26 +17,24 @@ class DCNNNetwork(BaseDeepLearningNetwork): Dimension of the models's latent space. n_layers: int, default=4 Number of convolution layers. - kernel_size: int, default=3 - Size of the 1D Convolutional Kernel. - activation: str, default="relu" + kernel_size: Union[int, List[int]], default=3 + Size of the 1D Convolutional Kernel. Defaults + to a list of three's for `n_layers` elements. + activation: Union[str, List[str]], default="relu" The activation function used by convolution layers. - n_filters: int, default=None - Number of filters used in convolution layers. - dilation_rate: list, default=None - The dilation rate for convolution. + Defaults to a list of "relu" for `n_layers` elements. + n_filters: Union[int, List[int]], default=None + Number of filters used in convolution layers. Defaults + to a list of multiple's of 32 for `n_layers` elements. + dilation_rate: Union[int, List[int]], default=None + The dilation rate for convolution. Defaults to a list of + powers of 2 for `n_layers` elements. References ---------- - .. [1] Network originally defined in: - @article{franceschi2019unsupervised, - title={Unsupervised scalable representation learning for multivariate time - series}, - author={Franceschi, Jean-Yves and Dieuleveut, Aymeric and Jaggi, Martin}, - journal={Advances in neural information processing systems}, - volume={32}, - year={2019} - } + .. [1] Franceschi, J. Y., Dieuleveut, A., & Jaggi, M. (2019). + Unsupervised scalable representation learning for multivariate + time series. Advances in neural information processing systems, 32. """ _config = { @@ -66,8 +64,8 @@ def __init__( def build_network(self, input_shape): """Construct a network and return its input and output layers. - Arguments - --------- + Parameters + ---------- input_shape : tuple of shape = (n_timepoints (m), n_channels (d)) The shape of the data fed into the input layer. From e2e191cb06df2d9ed60b93310d3f46bfc32c9af8 Mon Sep 17 00:00:00 2001 From: aadya940 Date: Fri, 16 Aug 2024 22:31:26 +0530 Subject: [PATCH 31/32] Add logic to handle int for --- aeon/networks/_dcnn.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 13387101f6..36aa556333 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -77,6 +77,8 @@ def build_network(self, input_shape): if self.n_filters is None: self._n_filters = [32 * i for i in range(1, self.n_layers + 1)] + elif isinstance(self.n_filters, int): + self._n_filters = [self.n_filters for _ in range(self.n_layers)] elif isinstance(self.n_filters, list): self._n_filters = self.n_filters assert len(self.n_filters) == self.n_layers From 2dc858fa81141aaa87dc5637337598524cae122a Mon Sep 17 00:00:00 2001 From: aadya940 Date: Mon, 19 Aug 2024 13:51:39 +0530 Subject: [PATCH 32/32] Add padding param --- aeon/networks/_dcnn.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/aeon/networks/_dcnn.py b/aeon/networks/_dcnn.py index 36aa556333..243340c30e 100644 --- a/aeon/networks/_dcnn.py +++ b/aeon/networks/_dcnn.py @@ -29,6 +29,9 @@ class DCNNNetwork(BaseDeepLearningNetwork): dilation_rate: Union[int, List[int]], default=None The dilation rate for convolution. Defaults to a list of powers of 2 for `n_layers` elements. + padding: Union[str, List[str]], default="causal" + Padding to be used in each DCNN Layer. Defaults to a list + of causal paddings for `n_layers` elements. References ---------- @@ -51,6 +54,7 @@ def __init__( activation="relu", n_filters=None, dilation_rate=None, + padding="causal", ): super().__init__() @@ -60,6 +64,7 @@ def __init__( self.n_layers = n_layers self.dilation_rate = dilation_rate self.activation = activation + self.padding = padding def build_network(self, input_shape): """Construct a network and return its input and output layers. @@ -94,18 +99,30 @@ def build_network(self, input_shape): assert isinstance(self.dilation_rate, list) assert len(self.dilation_rate) == self.n_layers - if isinstance(self.kernel_size, int): + if self.kernel_size is None: + self._kernel_size = [3 for _ in range(self.n_layers)] + elif isinstance(self.kernel_size, int): self._kernel_size = [self.kernel_size for _ in range(self.n_layers)] elif isinstance(self.kernel_size, list): self._kernel_size = self.kernel_size assert len(self.kernel_size) == self.n_layers - if isinstance(self.activation, str): + if self.activation is None: + self._activation = ["relu" for _ in range(self.n_layers)] + elif isinstance(self.activation, str): self._activation = [self.activation for _ in range(self.n_layers)] elif isinstance(self.activation, list): self._activation = self.activation assert len(self._activation) == self.n_layers + if self.padding is None: + self._padding = ["causal" for _ in range(self.n_layers)] + elif isinstance(self.padding, str): + self._padding = [self.padding for _ in range(self.n_layers)] + elif isinstance(self.padding, list): + self._padding = self.padding + assert len(self._padding) == self.n_layers + input_layer = tf.keras.layers.Input(input_shape) x = input_layer @@ -116,6 +133,7 @@ def build_network(self, input_shape): self._dilation_rate[i], _activation=self._activation[i], _kernel_size=self._kernel_size[i], + _padding=self._padding[i], ) x = tf.keras.layers.GlobalMaxPool1D()(x) @@ -124,7 +142,7 @@ def build_network(self, input_shape): return input_layer, output_layer def _dcnn_layer( - self, _inputs, _n_filters, _dilation_rate, _activation, _kernel_size + self, _inputs, _n_filters, _dilation_rate, _activation, _kernel_size, _padding ): import tensorflow as tf @@ -133,9 +151,8 @@ def _dcnn_layer( _n_filters, kernel_size=_kernel_size, dilation_rate=_dilation_rate, - padding="causal", + padding=_padding, kernel_regularizer="l2", - activation=_activation, )(_inputs) x = tf.keras.layers.Conv1D( _n_filters, @@ -146,4 +163,5 @@ def _dcnn_layer( activation=_activation, )(x) output = tf.keras.layers.Add()([x, _add]) + output = tf.keras.layers.Activation(_activation)(output) return output