diff --git a/docs/requirements_docs.txt b/docs/requirements_docs.txt index 0fb01800898f70ff541083567c289bb4f307009f..772efd78abe7713a5a4ce94a47c0515fc4276c2a 100644 --- a/docs/requirements_docs.txt +++ b/docs/requirements_docs.txt @@ -1,4 +1,5 @@ -sphinx==3.0.1 +sphinx==3.0.3 sphinx-autoapi==1.3.0 sphinx-rtd-theme==0.4.3 -recommonmark==0.6.0 \ No newline at end of file +recommonmark==0.6.0 +sphinx-autodoc-typehints==1.10.3 \ No newline at end of file diff --git a/src/model_modules/advanced_paddings.py b/src/model_modules/advanced_paddings.py index d9e55c78fb6c78bbe219c820078c46a235627897..f2fd4de91e84b1407f54c5ea156ad34f2d46acff 100644 --- a/src/model_modules/advanced_paddings.py +++ b/src/model_modules/advanced_paddings.py @@ -1,30 +1,35 @@ +"""Collection of customised padding layers.""" + __author__ = 'Felix Kleinert' __date__ = '2020-03-02' -import tensorflow as tf -import numpy as np -import keras.backend as K -from keras.layers.convolutional import _ZeroPadding +from typing import Union, Tuple + +import numpy as np +import tensorflow as tf +from keras.backend.common import normalize_data_format from keras.layers import ZeroPadding2D +from keras.layers.convolutional import _ZeroPadding from keras.legacy import interfaces from keras.utils import conv_utils from keras.utils.generic_utils import transpose_shape -from keras.backend.common import normalize_data_format class PadUtils: - """ - Helper class for advanced paddings - """ + """Helper class for advanced padding.""" @staticmethod - def get_padding_for_same(kernel_size, strides=1): + def get_padding_for_same(kernel_size: Tuple[int], strides: int = 1) -> Tuple[int]: """ - This methods calculates the padding size to keep input and output dimensions equal for a given kernel size - (STRIDES HAVE TO BE EQUAL TO ONE!) - :param kernel_size: - :return: + Calculate padding size to keep input and output dimensions equal for a given kernel size. + + .. hint:: custom paddings are currently only implemented for strides = 1 + + :param kernel_size: size of padding kernel size + :param strides: number of strides (default 1, currently only strides=1 supported) + + :return: padding size """ if strides != 1: raise NotImplementedError("Strides other than 1 not implemented!") @@ -40,15 +45,15 @@ class PadUtils: if all(k % 2 == 1 for k in ks): # (d & 0x1 for d in ks): pad = ((ks - 1) / 2).astype(np.int64) # convert numpy int to base int - pad = [np.asscalar(v) for v in pad] + pad = [int(v.item()) for v in pad] return tuple(pad) - # return tuple(PadUtils.check_padding_format(pad)) else: raise NotImplementedError(f"even kernel size not implemented. Got {kernel_size}") @staticmethod def spatial_2d_padding(padding=((1, 1), (1, 1)), data_format=None): - """Pads the 2nd and 3rd dimensions of a 4D tensor. + """ + Pad the 2nd and 3rd dimensions of a 4D tensor. # Arguments x: Tensor or variable. @@ -75,6 +80,7 @@ class PadUtils: @staticmethod def check_padding_format(padding): + """Check padding format (int, 1D or 2D, >0).""" if isinstance(padding, int): normalized_padding = ((padding, padding), (padding, padding)) elif hasattr(padding, '__len__'): @@ -89,16 +95,18 @@ class PadUtils: raise ValueError(f'`padding[{idx_pad}]` should have one or two elements. ' f'Found: {padding[idx_pad]}') if not all(isinstance(sub_k, int) for sub_k in padding[idx_pad]): - raise ValueError(f'`padding[{idx_pad}]` should have one or two elements of type int. ' + raise ValueError(f'`padding[{idx_pad}]` should have one or two elements of type int. ' f"Found:{padding[idx_pad]} of type {[type(sub_k) for sub_k in padding[idx_pad]]}") height_padding = conv_utils.normalize_tuple(padding[0], 2, '1st entry of padding') if not all(k >= 0 for k in height_padding): - raise ValueError(f"The `1st entry of padding` argument must be >= 0. Received: {padding[0]} of type {type(padding[0])}") + raise ValueError( + f"The `1st entry of padding` argument must be >= 0. Received: {padding[0]} of type {type(padding[0])}") width_padding = conv_utils.normalize_tuple(padding[1], 2, '2nd entry of padding') if not all(k >= 0 for k in width_padding): - raise ValueError(f"The `2nd entry of padding` argument must be >= 0. Received: {padding[1]} of type {type(padding[1])}") + raise ValueError( + f"The `2nd entry of padding` argument must be >= 0. Received: {padding[1]} of type {type(padding[1])}") normalized_padding = (height_padding, width_padding) else: raise ValueError('`padding` should be either an int, ' @@ -112,9 +120,10 @@ class PadUtils: class ReflectionPadding2D(_ZeroPadding): """ - Reflection padding layer for 2D input. This custum padding layer is built on keras' zero padding layers. Doc is copy - pasted from the original functions/methods: + Reflection padding layer for 2D input. + This custom padding layer is built on keras' zero padding layers. Doc is copy and pasted from the original + functions/methods: This layer can add rows and columns of reflected values at the top, bottom, left and right side of an image like tensor. @@ -129,7 +138,7 @@ class ReflectionPadding2D(_ZeroPadding): - '# Arguments + # Arguments padding: int, or tuple of 2 ints, or tuple of 2 tuples of 2 ints. - If int: the same symmetric padding is applied to height and width. @@ -172,21 +181,24 @@ class ReflectionPadding2D(_ZeroPadding): padding=(1, 1), data_format=None, **kwargs): + """Initialise ReflectionPadding2D.""" normalized_padding = PadUtils.check_padding_format(padding=padding) super(ReflectionPadding2D, self).__init__(normalized_padding, data_format, **kwargs) def call(self, inputs, mask=None): + """Call ReflectionPadding2D.""" pattern = PadUtils.spatial_2d_padding(padding=self.padding, data_format=self.data_format) return tf.pad(inputs, pattern, 'REFLECT') class SymmetricPadding2D(_ZeroPadding): """ - Symmetric padding layer for 2D input. This custom padding layer is built on keras' zero padding layers. Doc is copy - pasted from the original functions/methods: + Symmetric padding layer for 2D input. + This custom padding layer is built on keras' zero padding layers. Doc is copy pasted from the original + functions/methods: This layer can add rows and columns of symmetric values at the top, bottom, left and right side of an image like tensor. @@ -243,39 +255,57 @@ class SymmetricPadding2D(_ZeroPadding): padding=(1, 1), data_format=None, **kwargs): + """Initialise SymmetricPadding2D.""" normalized_padding = PadUtils.check_padding_format(padding=padding) super(SymmetricPadding2D, self).__init__(normalized_padding, data_format, **kwargs) def call(self, inputs, mask=None): + """Call SymmetricPadding2D.""" pattern = PadUtils.spatial_2d_padding(padding=self.padding, data_format=self.data_format) return tf.pad(inputs, pattern, 'SYMMETRIC') class Padding2D: - ''' - This class combines the implemented padding methods. You can call this method by defining a specific padding type. - The __call__ method will return the corresponding Padding layer. - ''' + """ + Combine all implemented padding methods. + + You can call this method by defining a specific padding type. The __call__ method will return the corresponding + Padding layer. + + .. code-block:: python + + input_x = ... # your input data + kernel_size = (5, 1) + padding_size = PadUtils.get_padding_for_same(kernel_size) + + tower = layers.Conv2D(...)(input_x) + tower = layers.Activation(...)(tower) + tower = Padding2D('ZeroPad2D')(padding=padding_size, name=f'Custom_Pad')(tower) + + Padding type can either be set by a string or directly by using an instance of a valid padding class. + """ allowed_paddings = { **dict.fromkeys(("RefPad2D", "ReflectionPadding2D"), ReflectionPadding2D), **dict.fromkeys(("SymPad2D", "SymmetricPadding2D"), SymmetricPadding2D), **dict.fromkeys(("ZeroPad2D", "ZeroPadding2D"), ZeroPadding2D) } + padding_type = Union[ReflectionPadding2D, SymmetricPadding2D, ZeroPadding2D] - def __init__(self, padding_type): + def __init__(self, padding_type: Union[str, padding_type]): + """Set padding type.""" self.padding_type = padding_type def _check_and_get_padding(self): if isinstance(self.padding_type, str): try: pad2d = self.allowed_paddings[self.padding_type] - except KeyError as einfo: + except KeyError as e: raise NotImplementedError( - f"`{einfo}' is not implemented as padding. " - "Use one of those: i) `RefPad2D', ii) `SymPad2D', iii) `ZeroPad2D'") + f"`{e}' is not implemented as padding. Use one of those: i) `RefPad2D', ii) `SymPad2D', " + f"iii) `ZeroPad2D'") else: if self.padding_type in self.allowed_paddings.values(): pad2d = self.padding_type @@ -286,6 +316,7 @@ class Padding2D: return pad2d def __call__(self, *args, **kwargs): + """Call padding.""" return self._check_and_get_padding()(*args, **kwargs) @@ -318,5 +349,3 @@ if __name__ == '__main__': model.compile('adam', loss='mse') model.summary() model.fit(x, y, epochs=10) - -