diff --git a/mlair/keras_legacy/__init__.py b/mlair/keras_legacy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mlair/keras_legacy/conv_utils.py b/mlair/keras_legacy/conv_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..c5ee50e3f260fdf41f90c58654f82cfb8b35dfe8 --- /dev/null +++ b/mlair/keras_legacy/conv_utils.py @@ -0,0 +1,180 @@ +"""Utilities used in convolutional layers. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +from tensorflow.keras import backend as K + + +def normalize_tuple(value, n, name): + """Transforms a single int or iterable of ints into an int tuple. + + # Arguments + value: The value to validate and convert. Could be an int, or any iterable + of ints. + n: The size of the tuple to be returned. + name: The name of the argument being validated, e.g. `strides` or + `kernel_size`. This is only used to format error messages. + + # Returns + A tuple of n integers. + + # Raises + ValueError: If something else than an int/long or iterable thereof was + passed. + """ + if isinstance(value, int): + return (value,) * n + else: + try: + value_tuple = tuple(value) + except TypeError: + raise ValueError('The `' + name + '` argument must be a tuple of ' + + str(n) + ' integers. Received: ' + str(value)) + if len(value_tuple) != n: + raise ValueError('The `' + name + '` argument must be a tuple of ' + + str(n) + ' integers. Received: ' + str(value)) + for single_value in value_tuple: + try: + int(single_value) + except ValueError: + raise ValueError('The `' + name + '` argument must be a tuple of ' + + str(n) + ' integers. Received: ' + str(value) + ' ' + 'including element ' + str(single_value) + ' of ' + 'type ' + str(type(single_value))) + return value_tuple + + +def normalize_padding(value): + padding = value.lower() + allowed = {'valid', 'same', 'causal'} + if K.backend() == 'theano': + allowed.add('full') + if padding not in allowed: + raise ValueError('The `padding` argument must be one of "valid", "same" ' + '(or "causal" for Conv1D). Received: ' + str(padding)) + return padding + + +def convert_kernel(kernel): + """Converts a Numpy kernel matrix from Theano format to TensorFlow format. + + Also works reciprocally, since the transformation is its own inverse. + + # Arguments + kernel: Numpy array (3D, 4D or 5D). + + # Returns + The converted kernel. + + # Raises + ValueError: in case of invalid kernel shape or invalid data_format. + """ + kernel = np.asarray(kernel) + if not 3 <= kernel.ndim <= 5: + raise ValueError('Invalid kernel shape:', kernel.shape) + slices = [slice(None, None, -1) for _ in range(kernel.ndim)] + no_flip = (slice(None, None), slice(None, None)) + slices[-2:] = no_flip + return np.copy(kernel[slices]) + + +def conv_output_length(input_length, filter_size, + padding, stride, dilation=1): + """Determines output length of a convolution given input length. + + # Arguments + input_length: integer. + filter_size: integer. + padding: one of `"same"`, `"valid"`, `"full"`. + stride: integer. + dilation: dilation rate, integer. + + # Returns + The output length (integer). + """ + if input_length is None: + return None + assert padding in {'same', 'valid', 'full', 'causal'} + dilated_filter_size = filter_size + (filter_size - 1) * (dilation - 1) + if padding == 'same': + output_length = input_length + elif padding == 'valid': + output_length = input_length - dilated_filter_size + 1 + elif padding == 'causal': + output_length = input_length + elif padding == 'full': + output_length = input_length + dilated_filter_size - 1 + return (output_length + stride - 1) // stride + + +def conv_input_length(output_length, filter_size, padding, stride): + """Determines input length of a convolution given output length. + + # Arguments + output_length: integer. + filter_size: integer. + padding: one of `"same"`, `"valid"`, `"full"`. + stride: integer. + + # Returns + The input length (integer). + """ + if output_length is None: + return None + assert padding in {'same', 'valid', 'full'} + if padding == 'same': + pad = filter_size // 2 + elif padding == 'valid': + pad = 0 + elif padding == 'full': + pad = filter_size - 1 + return (output_length - 1) * stride - 2 * pad + filter_size + + +def deconv_length(dim_size, stride_size, kernel_size, padding, + output_padding, dilation=1): + """Determines output length of a transposed convolution given input length. + + # Arguments + dim_size: Integer, the input length. + stride_size: Integer, the stride along the dimension of `dim_size`. + kernel_size: Integer, the kernel size along the dimension of + `dim_size`. + padding: One of `"same"`, `"valid"`, `"full"`. + output_padding: Integer, amount of padding along the output dimension, + Can be set to `None` in which case the output length is inferred. + dilation: dilation rate, integer. + + # Returns + The output length (integer). + """ + assert padding in {'same', 'valid', 'full'} + if dim_size is None: + return None + + # Get the dilated kernel size + kernel_size = kernel_size + (kernel_size - 1) * (dilation - 1) + + # Infer length if output padding is None, else compute the exact length + if output_padding is None: + if padding == 'valid': + dim_size = dim_size * stride_size + max(kernel_size - stride_size, 0) + elif padding == 'full': + dim_size = dim_size * stride_size - (stride_size + kernel_size - 2) + elif padding == 'same': + dim_size = dim_size * stride_size + else: + if padding == 'same': + pad = kernel_size // 2 + elif padding == 'valid': + pad = 0 + elif padding == 'full': + pad = kernel_size - 1 + + dim_size = ((dim_size - 1) * stride_size + kernel_size - 2 * pad + + output_padding) + + return dim_size diff --git a/mlair/keras_legacy/interfaces.py b/mlair/keras_legacy/interfaces.py new file mode 100644 index 0000000000000000000000000000000000000000..45a0e310cda87df3b3af238dc83405878b0d4746 --- /dev/null +++ b/mlair/keras_legacy/interfaces.py @@ -0,0 +1,668 @@ +"""Interface converters for Keras 1 support in Keras 2. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import six +import warnings +import functools +import numpy as np + + +def generate_legacy_interface(allowed_positional_args=None, + conversions=None, + preprocessor=None, + value_conversions=None, + object_type='class'): + if allowed_positional_args is None: + check_positional_args = False + else: + check_positional_args = True + allowed_positional_args = allowed_positional_args or [] + conversions = conversions or [] + value_conversions = value_conversions or [] + + def legacy_support(func): + @six.wraps(func) + def wrapper(*args, **kwargs): + if object_type == 'class': + object_name = args[0].__class__.__name__ + else: + object_name = func.__name__ + if preprocessor: + args, kwargs, converted = preprocessor(args, kwargs) + else: + converted = [] + if check_positional_args: + if len(args) > len(allowed_positional_args) + 1: + raise TypeError('`' + object_name + + '` can accept only ' + + str(len(allowed_positional_args)) + + ' positional arguments ' + + str(tuple(allowed_positional_args)) + + ', but you passed the following ' + 'positional arguments: ' + + str(list(args[1:]))) + for key in value_conversions: + if key in kwargs: + old_value = kwargs[key] + if old_value in value_conversions[key]: + kwargs[key] = value_conversions[key][old_value] + for old_name, new_name in conversions: + if old_name in kwargs: + value = kwargs.pop(old_name) + if new_name in kwargs: + raise_duplicate_arg_error(old_name, new_name) + kwargs[new_name] = value + converted.append((new_name, old_name)) + if converted: + signature = '`' + object_name + '(' + for i, value in enumerate(args[1:]): + if isinstance(value, six.string_types): + signature += '"' + value + '"' + else: + if isinstance(value, np.ndarray): + str_val = 'array' + else: + str_val = str(value) + if len(str_val) > 10: + str_val = str_val[:10] + '...' + signature += str_val + if i < len(args[1:]) - 1 or kwargs: + signature += ', ' + for i, (name, value) in enumerate(kwargs.items()): + signature += name + '=' + if isinstance(value, six.string_types): + signature += '"' + value + '"' + else: + if isinstance(value, np.ndarray): + str_val = 'array' + else: + str_val = str(value) + if len(str_val) > 10: + str_val = str_val[:10] + '...' + signature += str_val + if i < len(kwargs) - 1: + signature += ', ' + signature += ')`' + warnings.warn('Update your `' + object_name + '` call to the ' + + 'Keras 2 API: ' + signature, stacklevel=2) + return func(*args, **kwargs) + wrapper._original_function = func + return wrapper + return legacy_support + + +generate_legacy_method_interface = functools.partial(generate_legacy_interface, + object_type='method') + + +def raise_duplicate_arg_error(old_arg, new_arg): + raise TypeError('For the `' + new_arg + '` argument, ' + 'the layer received both ' + 'the legacy keyword argument ' + '`' + old_arg + '` and the Keras 2 keyword argument ' + '`' + new_arg + '`. Stick to the latter!') + + +legacy_dense_support = generate_legacy_interface( + allowed_positional_args=['units'], + conversions=[('output_dim', 'units'), + ('init', 'kernel_initializer'), + ('W_regularizer', 'kernel_regularizer'), + ('b_regularizer', 'bias_regularizer'), + ('W_constraint', 'kernel_constraint'), + ('b_constraint', 'bias_constraint'), + ('bias', 'use_bias')]) + +legacy_dropout_support = generate_legacy_interface( + allowed_positional_args=['rate', 'noise_shape', 'seed'], + conversions=[('p', 'rate')]) + + +def embedding_kwargs_preprocessor(args, kwargs): + converted = [] + if 'dropout' in kwargs: + kwargs.pop('dropout') + warnings.warn('The `dropout` argument is no longer support in `Embedding`. ' + 'You can apply a `keras.layers.SpatialDropout1D` layer ' + 'right after the `Embedding` layer to get the same behavior.', + stacklevel=3) + return args, kwargs, converted + +legacy_embedding_support = generate_legacy_interface( + allowed_positional_args=['input_dim', 'output_dim'], + conversions=[('init', 'embeddings_initializer'), + ('W_regularizer', 'embeddings_regularizer'), + ('W_constraint', 'embeddings_constraint')], + preprocessor=embedding_kwargs_preprocessor) + +legacy_pooling1d_support = generate_legacy_interface( + allowed_positional_args=['pool_size', 'strides', 'padding'], + conversions=[('pool_length', 'pool_size'), + ('stride', 'strides'), + ('border_mode', 'padding')]) + +legacy_prelu_support = generate_legacy_interface( + allowed_positional_args=['alpha_initializer'], + conversions=[('init', 'alpha_initializer')]) + + +legacy_gaussiannoise_support = generate_legacy_interface( + allowed_positional_args=['stddev'], + conversions=[('sigma', 'stddev')]) + + +def recurrent_args_preprocessor(args, kwargs): + converted = [] + if 'forget_bias_init' in kwargs: + if kwargs['forget_bias_init'] == 'one': + kwargs.pop('forget_bias_init') + kwargs['unit_forget_bias'] = True + converted.append(('forget_bias_init', 'unit_forget_bias')) + else: + kwargs.pop('forget_bias_init') + warnings.warn('The `forget_bias_init` argument ' + 'has been ignored. Use `unit_forget_bias=True` ' + 'instead to initialize with ones.', stacklevel=3) + if 'input_dim' in kwargs: + input_length = kwargs.pop('input_length', None) + input_dim = kwargs.pop('input_dim') + input_shape = (input_length, input_dim) + kwargs['input_shape'] = input_shape + converted.append(('input_dim', 'input_shape')) + warnings.warn('The `input_dim` and `input_length` arguments ' + 'in recurrent layers are deprecated. ' + 'Use `input_shape` instead.', stacklevel=3) + return args, kwargs, converted + +legacy_recurrent_support = generate_legacy_interface( + allowed_positional_args=['units'], + conversions=[('output_dim', 'units'), + ('init', 'kernel_initializer'), + ('inner_init', 'recurrent_initializer'), + ('inner_activation', 'recurrent_activation'), + ('W_regularizer', 'kernel_regularizer'), + ('b_regularizer', 'bias_regularizer'), + ('U_regularizer', 'recurrent_regularizer'), + ('dropout_W', 'dropout'), + ('dropout_U', 'recurrent_dropout'), + ('consume_less', 'implementation')], + value_conversions={'consume_less': {'cpu': 0, + 'mem': 1, + 'gpu': 2}}, + preprocessor=recurrent_args_preprocessor) + +legacy_gaussiandropout_support = generate_legacy_interface( + allowed_positional_args=['rate'], + conversions=[('p', 'rate')]) + +legacy_pooling2d_support = generate_legacy_interface( + allowed_positional_args=['pool_size', 'strides', 'padding'], + conversions=[('border_mode', 'padding'), + ('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_pooling3d_support = generate_legacy_interface( + allowed_positional_args=['pool_size', 'strides', 'padding'], + conversions=[('border_mode', 'padding'), + ('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_global_pooling_support = generate_legacy_interface( + conversions=[('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_upsampling1d_support = generate_legacy_interface( + allowed_positional_args=['size'], + conversions=[('length', 'size')]) + +legacy_upsampling2d_support = generate_legacy_interface( + allowed_positional_args=['size'], + conversions=[('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_upsampling3d_support = generate_legacy_interface( + allowed_positional_args=['size'], + conversions=[('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + + +def conv1d_args_preprocessor(args, kwargs): + converted = [] + if 'input_dim' in kwargs: + if 'input_length' in kwargs: + length = kwargs.pop('input_length') + else: + length = None + input_shape = (length, kwargs.pop('input_dim')) + kwargs['input_shape'] = input_shape + converted.append(('input_shape', 'input_dim')) + return args, kwargs, converted + +legacy_conv1d_support = generate_legacy_interface( + allowed_positional_args=['filters', 'kernel_size'], + conversions=[('nb_filter', 'filters'), + ('filter_length', 'kernel_size'), + ('subsample_length', 'strides'), + ('border_mode', 'padding'), + ('init', 'kernel_initializer'), + ('W_regularizer', 'kernel_regularizer'), + ('b_regularizer', 'bias_regularizer'), + ('W_constraint', 'kernel_constraint'), + ('b_constraint', 'bias_constraint'), + ('bias', 'use_bias')], + preprocessor=conv1d_args_preprocessor) + + +def conv2d_args_preprocessor(args, kwargs): + converted = [] + if len(args) > 4: + raise TypeError('Layer can receive at most 3 positional arguments.') + elif len(args) == 4: + if isinstance(args[2], int) and isinstance(args[3], int): + new_keywords = ['padding', 'strides', 'data_format'] + for kwd in new_keywords: + if kwd in kwargs: + raise ValueError( + 'It seems that you are using the Keras 2 ' + 'and you are passing both `kernel_size` and `strides` ' + 'as integer positional arguments. For safety reasons, ' + 'this is disallowed. Pass `strides` ' + 'as a keyword argument instead.') + kernel_size = (args[2], args[3]) + args = [args[0], args[1], kernel_size] + converted.append(('kernel_size', 'nb_row/nb_col')) + elif len(args) == 3 and isinstance(args[2], int): + if 'nb_col' in kwargs: + kernel_size = (args[2], kwargs.pop('nb_col')) + args = [args[0], args[1], kernel_size] + converted.append(('kernel_size', 'nb_row/nb_col')) + elif len(args) == 2: + if 'nb_row' in kwargs and 'nb_col' in kwargs: + kernel_size = (kwargs.pop('nb_row'), kwargs.pop('nb_col')) + args = [args[0], args[1], kernel_size] + converted.append(('kernel_size', 'nb_row/nb_col')) + elif len(args) == 1: + if 'nb_row' in kwargs and 'nb_col' in kwargs: + kernel_size = (kwargs.pop('nb_row'), kwargs.pop('nb_col')) + kwargs['kernel_size'] = kernel_size + converted.append(('kernel_size', 'nb_row/nb_col')) + return args, kwargs, converted + +legacy_conv2d_support = generate_legacy_interface( + allowed_positional_args=['filters', 'kernel_size'], + conversions=[('nb_filter', 'filters'), + ('subsample', 'strides'), + ('border_mode', 'padding'), + ('dim_ordering', 'data_format'), + ('init', 'kernel_initializer'), + ('W_regularizer', 'kernel_regularizer'), + ('b_regularizer', 'bias_regularizer'), + ('W_constraint', 'kernel_constraint'), + ('b_constraint', 'bias_constraint'), + ('bias', 'use_bias')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}, + preprocessor=conv2d_args_preprocessor) + + +def separable_conv2d_args_preprocessor(args, kwargs): + converted = [] + if 'init' in kwargs: + init = kwargs.pop('init') + kwargs['depthwise_initializer'] = init + kwargs['pointwise_initializer'] = init + converted.append(('init', 'depthwise_initializer/pointwise_initializer')) + args, kwargs, _converted = conv2d_args_preprocessor(args, kwargs) + return args, kwargs, converted + _converted + +legacy_separable_conv2d_support = generate_legacy_interface( + allowed_positional_args=['filters', 'kernel_size'], + conversions=[('nb_filter', 'filters'), + ('subsample', 'strides'), + ('border_mode', 'padding'), + ('dim_ordering', 'data_format'), + ('b_regularizer', 'bias_regularizer'), + ('b_constraint', 'bias_constraint'), + ('bias', 'use_bias')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}, + preprocessor=separable_conv2d_args_preprocessor) + + +def deconv2d_args_preprocessor(args, kwargs): + converted = [] + if len(args) == 5: + if isinstance(args[4], tuple): + args = args[:-1] + converted.append(('output_shape', None)) + if 'output_shape' in kwargs: + kwargs.pop('output_shape') + converted.append(('output_shape', None)) + args, kwargs, _converted = conv2d_args_preprocessor(args, kwargs) + return args, kwargs, converted + _converted + +legacy_deconv2d_support = generate_legacy_interface( + allowed_positional_args=['filters', 'kernel_size'], + conversions=[('nb_filter', 'filters'), + ('subsample', 'strides'), + ('border_mode', 'padding'), + ('dim_ordering', 'data_format'), + ('init', 'kernel_initializer'), + ('W_regularizer', 'kernel_regularizer'), + ('b_regularizer', 'bias_regularizer'), + ('W_constraint', 'kernel_constraint'), + ('b_constraint', 'bias_constraint'), + ('bias', 'use_bias')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}, + preprocessor=deconv2d_args_preprocessor) + + +def conv3d_args_preprocessor(args, kwargs): + converted = [] + if len(args) > 5: + raise TypeError('Layer can receive at most 4 positional arguments.') + if len(args) == 5: + if all([isinstance(x, int) for x in args[2:5]]): + kernel_size = (args[2], args[3], args[4]) + args = [args[0], args[1], kernel_size] + converted.append(('kernel_size', 'kernel_dim*')) + elif len(args) == 4 and isinstance(args[3], int): + if isinstance(args[2], int) and isinstance(args[3], int): + new_keywords = ['padding', 'strides', 'data_format'] + for kwd in new_keywords: + if kwd in kwargs: + raise ValueError( + 'It seems that you are using the Keras 2 ' + 'and you are passing both `kernel_size` and `strides` ' + 'as integer positional arguments. For safety reasons, ' + 'this is disallowed. Pass `strides` ' + 'as a keyword argument instead.') + if 'kernel_dim3' in kwargs: + kernel_size = (args[2], args[3], kwargs.pop('kernel_dim3')) + args = [args[0], args[1], kernel_size] + converted.append(('kernel_size', 'kernel_dim*')) + elif len(args) == 3: + if all([x in kwargs for x in ['kernel_dim2', 'kernel_dim3']]): + kernel_size = (args[2], + kwargs.pop('kernel_dim2'), + kwargs.pop('kernel_dim3')) + args = [args[0], args[1], kernel_size] + converted.append(('kernel_size', 'kernel_dim*')) + elif len(args) == 2: + if all([x in kwargs for x in ['kernel_dim1', 'kernel_dim2', 'kernel_dim3']]): + kernel_size = (kwargs.pop('kernel_dim1'), + kwargs.pop('kernel_dim2'), + kwargs.pop('kernel_dim3')) + args = [args[0], args[1], kernel_size] + converted.append(('kernel_size', 'kernel_dim*')) + elif len(args) == 1: + if all([x in kwargs for x in ['kernel_dim1', 'kernel_dim2', 'kernel_dim3']]): + kernel_size = (kwargs.pop('kernel_dim1'), + kwargs.pop('kernel_dim2'), + kwargs.pop('kernel_dim3')) + kwargs['kernel_size'] = kernel_size + converted.append(('kernel_size', 'nb_row/nb_col')) + return args, kwargs, converted + +legacy_conv3d_support = generate_legacy_interface( + allowed_positional_args=['filters', 'kernel_size'], + conversions=[('nb_filter', 'filters'), + ('subsample', 'strides'), + ('border_mode', 'padding'), + ('dim_ordering', 'data_format'), + ('init', 'kernel_initializer'), + ('W_regularizer', 'kernel_regularizer'), + ('b_regularizer', 'bias_regularizer'), + ('W_constraint', 'kernel_constraint'), + ('b_constraint', 'bias_constraint'), + ('bias', 'use_bias')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}, + preprocessor=conv3d_args_preprocessor) + + +def batchnorm_args_preprocessor(args, kwargs): + converted = [] + if len(args) > 1: + raise TypeError('The `BatchNormalization` layer ' + 'does not accept positional arguments. ' + 'Use keyword arguments instead.') + if 'mode' in kwargs: + value = kwargs.pop('mode') + if value != 0: + raise TypeError('The `mode` argument of `BatchNormalization` ' + 'no longer exists. `mode=1` and `mode=2` ' + 'are no longer supported.') + converted.append(('mode', None)) + return args, kwargs, converted + + +def convlstm2d_args_preprocessor(args, kwargs): + converted = [] + if 'forget_bias_init' in kwargs: + value = kwargs.pop('forget_bias_init') + if value == 'one': + kwargs['unit_forget_bias'] = True + converted.append(('forget_bias_init', 'unit_forget_bias')) + else: + warnings.warn('The `forget_bias_init` argument ' + 'has been ignored. Use `unit_forget_bias=True` ' + 'instead to initialize with ones.', stacklevel=3) + args, kwargs, _converted = conv2d_args_preprocessor(args, kwargs) + return args, kwargs, converted + _converted + +legacy_convlstm2d_support = generate_legacy_interface( + allowed_positional_args=['filters', 'kernel_size'], + conversions=[('nb_filter', 'filters'), + ('subsample', 'strides'), + ('border_mode', 'padding'), + ('dim_ordering', 'data_format'), + ('init', 'kernel_initializer'), + ('inner_init', 'recurrent_initializer'), + ('W_regularizer', 'kernel_regularizer'), + ('U_regularizer', 'recurrent_regularizer'), + ('b_regularizer', 'bias_regularizer'), + ('inner_activation', 'recurrent_activation'), + ('dropout_W', 'dropout'), + ('dropout_U', 'recurrent_dropout'), + ('bias', 'use_bias')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}, + preprocessor=convlstm2d_args_preprocessor) + +legacy_batchnorm_support = generate_legacy_interface( + allowed_positional_args=[], + conversions=[('beta_init', 'beta_initializer'), + ('gamma_init', 'gamma_initializer')], + preprocessor=batchnorm_args_preprocessor) + + +def zeropadding2d_args_preprocessor(args, kwargs): + converted = [] + if 'padding' in kwargs and isinstance(kwargs['padding'], dict): + if set(kwargs['padding'].keys()) <= {'top_pad', 'bottom_pad', + 'left_pad', 'right_pad'}: + top_pad = kwargs['padding'].get('top_pad', 0) + bottom_pad = kwargs['padding'].get('bottom_pad', 0) + left_pad = kwargs['padding'].get('left_pad', 0) + right_pad = kwargs['padding'].get('right_pad', 0) + kwargs['padding'] = ((top_pad, bottom_pad), (left_pad, right_pad)) + warnings.warn('The `padding` argument in the Keras 2 API no longer' + 'accepts dict types. You can now input argument as: ' + '`padding=(top_pad, bottom_pad, left_pad, right_pad)`.', + stacklevel=3) + elif len(args) == 2 and isinstance(args[1], dict): + if set(args[1].keys()) <= {'top_pad', 'bottom_pad', + 'left_pad', 'right_pad'}: + top_pad = args[1].get('top_pad', 0) + bottom_pad = args[1].get('bottom_pad', 0) + left_pad = args[1].get('left_pad', 0) + right_pad = args[1].get('right_pad', 0) + args = (args[0], ((top_pad, bottom_pad), (left_pad, right_pad))) + warnings.warn('The `padding` argument in the Keras 2 API no longer' + 'accepts dict types. You can now input argument as: ' + '`padding=((top_pad, bottom_pad), (left_pad, right_pad))`', + stacklevel=3) + return args, kwargs, converted + +legacy_zeropadding2d_support = generate_legacy_interface( + allowed_positional_args=['padding'], + conversions=[('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}, + preprocessor=zeropadding2d_args_preprocessor) + +legacy_zeropadding3d_support = generate_legacy_interface( + allowed_positional_args=['padding'], + conversions=[('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_cropping2d_support = generate_legacy_interface( + allowed_positional_args=['cropping'], + conversions=[('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_cropping3d_support = generate_legacy_interface( + allowed_positional_args=['cropping'], + conversions=[('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_spatialdropout1d_support = generate_legacy_interface( + allowed_positional_args=['rate'], + conversions=[('p', 'rate')]) + +legacy_spatialdropoutNd_support = generate_legacy_interface( + allowed_positional_args=['rate'], + conversions=[('p', 'rate'), + ('dim_ordering', 'data_format')], + value_conversions={'dim_ordering': {'tf': 'channels_last', + 'th': 'channels_first', + 'default': None}}) + +legacy_lambda_support = generate_legacy_interface( + allowed_positional_args=['function', 'output_shape']) + + +# Model methods + +def generator_methods_args_preprocessor(args, kwargs): + converted = [] + if len(args) < 3: + if 'samples_per_epoch' in kwargs: + samples_per_epoch = kwargs.pop('samples_per_epoch') + if len(args) > 1: + generator = args[1] + else: + generator = kwargs['generator'] + if hasattr(generator, 'batch_size'): + kwargs['steps_per_epoch'] = samples_per_epoch // generator.batch_size + else: + kwargs['steps_per_epoch'] = samples_per_epoch + converted.append(('samples_per_epoch', 'steps_per_epoch')) + + keras1_args = {'samples_per_epoch', 'val_samples', + 'nb_epoch', 'nb_val_samples', 'nb_worker'} + if keras1_args.intersection(kwargs.keys()): + warnings.warn('The semantics of the Keras 2 argument ' + '`steps_per_epoch` is not the same as the ' + 'Keras 1 argument `samples_per_epoch`. ' + '`steps_per_epoch` is the number of batches ' + 'to draw from the generator at each epoch. ' + 'Basically steps_per_epoch = samples_per_epoch/batch_size. ' + 'Similarly `nb_val_samples`->`validation_steps` and ' + '`val_samples`->`steps` arguments have changed. ' + 'Update your method calls accordingly.', stacklevel=3) + + return args, kwargs, converted + + +legacy_generator_methods_support = generate_legacy_method_interface( + allowed_positional_args=['generator', 'steps_per_epoch', 'epochs'], + conversions=[('samples_per_epoch', 'steps_per_epoch'), + ('val_samples', 'steps'), + ('nb_epoch', 'epochs'), + ('nb_val_samples', 'validation_steps'), + ('nb_worker', 'workers'), + ('pickle_safe', 'use_multiprocessing'), + ('max_q_size', 'max_queue_size')], + preprocessor=generator_methods_args_preprocessor) + + +legacy_model_constructor_support = generate_legacy_interface( + allowed_positional_args=None, + conversions=[('input', 'inputs'), + ('output', 'outputs')]) + +legacy_input_support = generate_legacy_interface( + allowed_positional_args=None, + conversions=[('input_dtype', 'dtype')]) + + +def add_weight_args_preprocessing(args, kwargs): + if len(args) > 1: + if isinstance(args[1], (tuple, list)): + kwargs['shape'] = args[1] + args = (args[0],) + args[2:] + if len(args) > 1: + if isinstance(args[1], six.string_types): + kwargs['name'] = args[1] + args = (args[0],) + args[2:] + return args, kwargs, [] + + +legacy_add_weight_support = generate_legacy_interface( + allowed_positional_args=['name', 'shape'], + preprocessor=add_weight_args_preprocessing) + + +def get_updates_arg_preprocessing(args, kwargs): + # Old interface: (params, constraints, loss) + # New interface: (loss, params) + if len(args) > 4: + raise TypeError('`get_update` call received more arguments ' + 'than expected.') + elif len(args) == 4: + # Assuming old interface. + opt, params, _, loss = args + kwargs['loss'] = loss + kwargs['params'] = params + return [opt], kwargs, [] + elif len(args) == 3: + if isinstance(args[1], (list, tuple)): + assert isinstance(args[2], dict) + assert 'loss' in kwargs + opt, params, _ = args + kwargs['params'] = params + return [opt], kwargs, [] + return args, kwargs, [] + +legacy_get_updates_support = generate_legacy_interface( + allowed_positional_args=None, + conversions=[], + preprocessor=get_updates_arg_preprocessing) diff --git a/mlair/model_modules/advanced_paddings.py b/mlair/model_modules/advanced_paddings.py index e30002e73ada07933bf0339d96ffaa433cf23d83..b0f6c9c25dfd4eacb65e5cb59a267296a7f1ab72 100644 --- a/mlair/model_modules/advanced_paddings.py +++ b/mlair/model_modules/advanced_paddings.py @@ -8,12 +8,88 @@ from typing import Union, Tuple import numpy as np import tensorflow as tf -from tensorflow.keras.backend.common import normalize_data_format +# from tensorflow.keras.backend.common import normalize_data_format from tensorflow.keras.layers import ZeroPadding2D -from tensorflow.keras.layers.convolutional import _ZeroPadding -from tensorflow.keras.legacy import interfaces -from tensorflow.keras.utils import conv_utils -from tensorflow.keras.utils.generic_utils import transpose_shape +# from tensorflow.keras.layers.convolutional import _ZeroPadding +from tensorflow.keras.layers import Layer +# from tensorflow.keras.legacy import interfaces +from mlair.keras_legacy import interfaces +# from tensorflow.keras.utils import conv_utils +from mlair.keras_legacy import conv_utils +# from tensorflow.keras.utils.generic_utils import transpose_shape +# from mlair.keras_legacy.generic_utils import transpose_shape + + + +def transpose_shape(shape, target_format, spatial_axes): + """Converts a tuple or a list to the correct `data_format`. + It does so by switching the positions of its elements. + # Arguments + shape: Tuple or list, often representing shape, + corresponding to `'channels_last'`. + target_format: A string, either `'channels_first'` or `'channels_last'`. + spatial_axes: A tuple of integers. + Correspond to the indexes of the spatial axes. + For example, if you pass a shape + representing (batch_size, timesteps, rows, cols, channels), + then `spatial_axes=(2, 3)`. + # Returns + A tuple or list, with the elements permuted according + to `target_format`. + # Example + ```python + >>> # from keras.utils.generic_utils import transpose_shape + >>> transpose_shape((16, 128, 128, 32),'channels_first', spatial_axes=(1, 2)) + (16, 32, 128, 128) + >>> transpose_shape((16, 128, 128, 32), 'channels_last', spatial_axes=(1, 2)) + (16, 128, 128, 32) + >>> transpose_shape((128, 128, 32), 'channels_first', spatial_axes=(0, 1)) + (32, 128, 128) + ``` + # Raises + ValueError: if `value` or the global `data_format` invalid. + """ + if target_format == 'channels_first': + new_values = shape[:spatial_axes[0]] + new_values += (shape[-1],) + new_values += tuple(shape[x] for x in spatial_axes) + + if isinstance(shape, list): + return list(new_values) + return new_values + elif target_format == 'channels_last': + return shape + else: + raise ValueError('The `data_format` argument must be one of ' + '"channels_first", "channels_last". Received: ' + + str(target_format)) + + +def normalize_data_format(value): + """Checks that the value correspond to a valid data format. + # Arguments + value: String or None. `'channels_first'` or `'channels_last'`. + # Returns + A string, either `'channels_first'` or `'channels_last'` + # Example + ```python + >>> from tensorflow.keras import backend as K + >>> K.normalize_data_format(None) + 'channels_first' + >>> K.normalize_data_format('channels_last') + 'channels_last' + ``` + # Raises + ValueError: if `value` or the global `data_format` invalid. + """ + if value is None: + value = 'channels_last' + data_format = value.lower() + if data_format not in {'channels_first', 'channels_last'}: + raise ValueError('The `data_format` argument must be one of ' + '"channels_first", "channels_last". Received: ' + + str(value)) + return data_format class PadUtils: @@ -118,6 +194,51 @@ class PadUtils: return normalized_padding +class _ZeroPadding(Layer): + """Abstract nD ZeroPadding layer (private, used as implementation base). + # Arguments + padding: Tuple of tuples of two ints. Can be a tuple of ints when + rank is 1. + data_format: A string, + one of `"channels_last"` or `"channels_first"`. + The ordering of the dimensions in the inputs. + `"channels_last"` corresponds to inputs with shape + `(batch, ..., channels)` while `"channels_first"` corresponds to + inputs with shape `(batch, channels, ...)`. + It defaults to the `image_data_format` value found in your + Keras config file at `~/.keras/keras.json`. + If you never set it, then it will be "channels_last". + """ + def __init__(self, padding, data_format=None, **kwargs): + # self.rank is 1 for ZeroPadding1D, 2 for ZeroPadding2D. + self.rank = len(padding) + self.padding = padding + self.data_format = K.normalize_data_format(data_format) + self.input_spec = InputSpec(ndim=self.rank + 2) + super(_ZeroPadding, self).__init__(**kwargs) + + def call(self, inputs): + raise NotImplementedError + + def compute_output_shape(self, input_shape): + padding_all_dims = ((0, 0),) + self.padding + ((0, 0),) + spatial_axes = list(range(1, 1 + self.rank)) + padding_all_dims = transpose_shape(padding_all_dims, + self.data_format, + spatial_axes) + output_shape = list(input_shape) + for dim in range(len(output_shape)): + if output_shape[dim] is not None: + output_shape[dim] += sum(padding_all_dims[dim]) + return tuple(output_shape) + + def get_config(self): + config = {'padding': self.padding, + 'data_format': self.data_format} + base_config = super(_ZeroPadding, self).get_config() + return dict(list(base_config.items()) + list(config.items())) + + class ReflectionPadding2D(_ZeroPadding): """ Reflection padding layer for 2D input. @@ -321,8 +442,8 @@ class Padding2D: if __name__ == '__main__': - from keras.models import Model - from keras.layers import Conv2D, Flatten, Dense, Input + from tensorflow.keras.models import Model + from tensorflow.keras.layers import Conv2D, Flatten, Dense, Input kernel_1 = (3, 3) kernel_2 = (5, 5) diff --git a/mlair/model_modules/convolutional_networks.py b/mlair/model_modules/convolutional_networks.py index 294e31aff582fbdcb7643863790f51363389d8c3..be047eb7a1c92cbb8847328c157c874bfeca93ca 100644 --- a/mlair/model_modules/convolutional_networks.py +++ b/mlair/model_modules/convolutional_networks.py @@ -21,7 +21,7 @@ class CNN(AbstractModelClass): _initializer = {"tanh": "glorot_uniform", "sigmoid": "glorot_uniform", "linear": "glorot_uniform", "relu": keras.initializers.he_normal(), "selu": keras.initializers.lecun_normal(), "prelu": keras.initializers.he_normal()} - _optimizer = {"adam": keras.optimizers.adam, "sgd": keras.optimizers.SGD} + _optimizer = {"adam": keras.optimizers.Adam, "sgd": keras.optimizers.SGD} _regularizer = {"l1": keras.regularizers.l1, "l2": keras.regularizers.l2, "l1_l2": keras.regularizers.l1_l2} _requirements = ["lr", "beta_1", "beta_2", "epsilon", "decay", "amsgrad", "momentum", "nesterov", "l1", "l2"] _dropout = {"selu": keras.layers.AlphaDropout} diff --git a/run.py b/run.py index 49537c8fe595984114cf80bf250bda7d71de4f67..1d607acfc8a789080a38da9ee546e516be845797 100644 --- a/run.py +++ b/run.py @@ -3,7 +3,7 @@ __date__ = '2020-06-29' import argparse from mlair.workflows import DefaultWorkflow -from mlair.model_modules.model_class import MyLittleModelHourly as chosen_model +# from mlair.model_modules.recurrent_networks import RNN as chosen_model from mlair.helpers import remove_items from mlair.configuration.defaults import DEFAULT_PLOT_LIST import os @@ -29,7 +29,7 @@ def main(parser_args): stations=["DEBW013", "DEBW087", "DEBW107", "DEBW076"], train_model=False, create_new_model=True, network="UBA", evaluate_bootstraps=False, # plot_list=["PlotCompetitiveSkillScore"], - competitors=["test_model", "test_model2"], model=chosen_model, + competitors=["test_model", "test_model2"], # model=chosen_model, competitor_path=os.path.join(os.getcwd(), "data", "comp_test"), **parser_args.__dict__, start_script=__file__) workflow.run()