├── complex_networks_keras_tf1 ├── __init__.py ├── layers │ ├── __init__.py │ ├── utils.py │ ├── fft.py │ ├── activations.py │ ├── stft.py │ ├── dense.py │ ├── init.py │ ├── norm.py │ ├── pool.py │ └── bn.py ├── models │ ├── __init__.py │ ├── densenet_blocks_1d.py │ ├── densenet_blocks_2d.py │ ├── resnet_blocks_1d.py │ ├── resnet_blocks_2d.py │ ├── resnet_blocks_3d.py │ ├── resnet_models_1d.py │ ├── resnet_models_2d.py │ └── densenet_models_1d.py └── sync.ffs_db ├── LICENSE └── README.md /complex_networks_keras_tf1/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/sync.ffs_db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QinggangSUN/keras_complex_valued_networks/HEAD/complex_networks_keras_tf1/sync.ffs_db -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SUN Qinggang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # keras_complex_valued_networks 2 | Complex valued convolutional nerual networks using keras-gpu 2.2.4 with tensorflow-gpu 1.12.0 backend. 3 | Including ResNet architecture for classification. 4 | 5 | # Reference 6 | Please cite the original work as: 7 | 8 | [Deep Complex Networks](https://github.com/ChihebTrabelsi/deep_complex_networks) 9 | @ARTICLE {, 10 | author = "Chiheb Trabelsi, Olexa Bilaniuk, Ying Zhang, Dmitriy Serdyuk, Sandeep Subramanian, João Felipe Santos, Soroush Mehri, Negar Rostamzadeh, Yoshua Bengio, Christopher J Pal", 11 | title = "Deep Complex Networks", 12 | journal = "arXiv preprint arXiv:1705.09792", 13 | year = "2017" 14 | } 15 | 16 | [Complex-Valued Neural Networks in Keras with Tensorflow](https://github.com/zengjie617789/keras-complex) 17 | @misc{dramsch2019complex, 18 | title = {Complex-Valued Neural Networks in Keras with Tensorflow}, 19 | url = {https://figshare.com/articles/Complex-Valued_Neural_Networks_in_Keras_with_Tensorflow/9783773/1}, 20 | DOI = {10.6084/m9.figshare.9783773}, 21 | publisher = {figshare}, 22 | author = {Dramsch, Jesper S{\"o}ren and Contributors}, 23 | year = {2019} 24 | } 25 | 26 | [ResNet](https://arxiv.org/abs/1512.03385) 27 | 28 | [Keras-ResNet](https://github.com/broadinstitute/keras-resnet) 29 | 30 | [DenseNet](https://arxiv.org/pdf/1608.06993v3.pdf) 31 | 32 | [Dense Net in Keras](https://github.com/titu1994/DenseNet) 33 | 34 | # Related works 35 | [Deep Complex Networks (ICLR 2018)](https://arxiv.org/abs/1705.09792) 36 | [On Complex Valued Convolutional Neural Networks](https://arxiv.org/abs/1602.09046) 37 | [Spectral Representations for Convolutional Neural Networks](https://arxiv.org/abs/1506.03767) 38 | [Complex-Valued_Networks](https://github.com/wangyifan1027/Complex-Valued_Networks) 39 | [Deep Complex Networks](https://github.com/Doyosae/Deep_Complex_Networks) 40 | [Complex-Valued MRI Reconstruction - Unrolled Architecture](https://github.com/MRSRL/complex-networks-release) 41 | [Complex ResNet Aided DoA Estimation for Near-Field MIMO Systems](https://arxiv.org/abs/2007.10590) 42 | Li W., Xie W., Wang Z. (2020) Complex-Valued Densely Connected Convolutional Networks. In: Zeng J., Jing W., Song X., Lu Z. (eds) Data Science. ICPCSEE 2020. Communications in Computer and Information Science, vol 1257. Springer, Singapore. https://doi.org/10.1007/978-981-15-7981-3_21 43 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Authors: Dmitriy Serdyuk, Olexa Bilaniuk, Chiheb Trabelsi 6 | 7 | import keras.backend as K 8 | from keras.layers import Layer, Lambda 9 | 10 | # 11 | # GetReal/GetImag Lambda layer Implementation 12 | # 13 | 14 | 15 | def get_realpart(x): 16 | image_format = K.image_data_format() 17 | ndim = K.ndim(x) 18 | input_shape = K.shape(x) 19 | 20 | if (image_format == 'channels_first' and ndim != 3) or ndim == 2: 21 | input_dim = input_shape[1] // 2 22 | return x[:, :input_dim] 23 | 24 | input_dim = input_shape[-1] // 2 25 | if ndim == 3: 26 | return x[:, :, :input_dim] 27 | elif ndim == 4: 28 | return x[:, :, :, :input_dim] 29 | elif ndim == 5: 30 | return x[:, :, :, :, :input_dim] 31 | 32 | 33 | def get_imagpart(x): 34 | image_format = K.image_data_format() 35 | ndim = K.ndim(x) 36 | input_shape = K.shape(x) 37 | 38 | if (image_format == 'channels_first' and ndim != 3) or ndim == 2: 39 | input_dim = input_shape[1] // 2 40 | return x[:, input_dim:] 41 | 42 | input_dim = input_shape[-1] // 2 43 | if ndim == 3: 44 | return x[:, :, input_dim:] 45 | elif ndim == 4: 46 | return x[:, :, :, input_dim:] 47 | elif ndim == 5: 48 | return x[:, :, :, :, input_dim:] 49 | 50 | 51 | def get_abs(x): 52 | real = get_realpart(x) 53 | imag = get_imagpart(x) 54 | 55 | return K.sqrt(real * real + imag * imag) 56 | 57 | 58 | def getpart_output_shape(input_shape): 59 | returned_shape = list(input_shape[:]) 60 | image_format = K.image_data_format() 61 | ndim = len(returned_shape) 62 | 63 | if (image_format == 'channels_first' and ndim != 3) or ndim == 2: 64 | axis = 1 65 | else: 66 | axis = -1 67 | 68 | returned_shape[axis] = returned_shape[axis] // 2 69 | 70 | return tuple(returned_shape) 71 | 72 | 73 | class GetReal(Layer): 74 | def call(self, inputs): 75 | return get_realpart(inputs) 76 | def compute_output_shape(self, input_shape): 77 | return getpart_output_shape(input_shape) 78 | class GetImag(Layer): 79 | def call(self, inputs): 80 | return get_imagpart(inputs) 81 | def compute_output_shape(self, input_shape): 82 | return getpart_output_shape(input_shape) 83 | class GetAbs(Layer): 84 | def call(self, inputs): 85 | return get_abs(inputs) 86 | def compute_output_shape(self, input_shape): 87 | return getpart_output_shape(input_shape) 88 | 89 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/fft.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Authors: Olexa Bilaniuk 6 | # 7 | 8 | import keras.backend as KB 9 | import keras.engine as KE 10 | import keras.layers as KL 11 | import keras.optimizers as KO 12 | import tensorflow as tf 13 | import numpy as np 14 | 15 | if tf.__version__ <= '1.12.0': 16 | from tensorflow.spectral import fft, rfft, ifft 17 | else: 18 | from tensorflow.signal import fft, rfft, ifft 19 | 20 | # 21 | # FFT functions: 22 | # 23 | # fft(): Batched 1-D FFT (Input: (Batch, TimeSamples)) 24 | # ifft(): Batched 1-D IFFT (Input: (Batch, FreqSamples)) 25 | # fft2(): Batched 2-D FFT (Input: (Batch, TimeSamplesH, TimeSamplesW)) 26 | # ifft2(): Batched 2-D IFFT (Input: (Batch, FreqSamplesH, FreqSamplesW)) 27 | # 28 | 29 | def fft_func(z): 30 | B = z.shape[0]//2 31 | L = z.shape[1] 32 | C = tf.Variable(np.asarray([[[1,-1]]], dtype=np.float32)) 33 | Zr, Zi = rfft(z[:B]), rfft(z[B:]) 34 | isOdd = tf.equal(L%2, 1) 35 | Zr = tf.cond(isOdd, tf.concat([Zr, C*Zr[:,1: ][:,::-1]], axis=1), 36 | tf.concat([Zr, C*Zr[:,1:-1][:,::-1]], axis=1)) 37 | Zi = tf.cond(isOdd, tf.concat([Zi, C*Zi[:,1: ][:,::-1]], axis=1), 38 | tf.concat([Zi, C*Zi[:,1:-1][:,::-1]], axis=1)) 39 | Zi = (C*Zi)[:,:,::-1] # Zi * i 40 | Z = Zr+Zi 41 | return tf.concat([Z[:,:,0], Z[:,:,1]], axis=0) 42 | 43 | def ifft_func(z): 44 | B = z.shape[0]//2 45 | real, imag = z[:B], z[B:] 46 | ifft_complex = ifft(tf.complex(real, imag)) 47 | ifft_real = tf.math.real(ifft_complex) 48 | ifft_imag = tf.math.imag(ifft_complex) 49 | return tf.concat([ifft_real, ifft_imag], axis=0) 50 | 51 | def fft2_func(x): 52 | tt = x 53 | tt = KB.reshape(tt, (x.shape[0] *x.shape[1], x.shape[2])) 54 | tf = fft_func(tt) 55 | tf = KB.reshape(tf, (x.shape[0], x.shape[1], x.shape[2])) 56 | tf = KB.permute_dimensions(tf, (0, 2, 1)) 57 | tf = KB.reshape(tf, (x.shape[0] *x.shape[2], x.shape[1])) 58 | ff = fft_func(tf) 59 | ff = KB.reshape(ff, (x.shape[0], x.shape[2], x.shape[1])) 60 | ff = KB.permute_dimensions(ff, (0, 2, 1)) 61 | return ff 62 | 63 | def ifft2_func(x): 64 | ff = x 65 | ff = KB.permute_dimensions(ff, (0, 2, 1)) 66 | ff = KB.reshape(ff, (x.shape[0] *x.shape[2], x.shape[1])) 67 | tf = ifft_func(ff) 68 | tf = KB.reshape(tf, (x.shape[0], x.shape[2], x.shape[1])) 69 | tf = KB.permute_dimensions(tf, (0, 2, 1)) 70 | tf = KB.reshape(tf, (x.shape[0] *x.shape[1], x.shape[2])) 71 | tt = ifft_func(tf) 72 | tt = KB.reshape(tt, (x.shape[0], x.shape[1], x.shape[2])) 73 | return tt 74 | 75 | # 76 | # FFT Layers: 77 | # 78 | # FFT: Batched 1-D FFT (Input: (Batch, FeatureMaps, TimeSamples)) 79 | # IFFT: Batched 1-D IFFT (Input: (Batch, FeatureMaps, FreqSamples)) 80 | # FFT2: Batched 2-D FFT (Input: (Batch, FeatureMaps, TimeSamplesH, TimeSamplesW)) 81 | # IFFT2: Batched 2-D IFFT (Input: (Batch, FeatureMaps, FreqSamplesH, FreqSamplesW)) 82 | # 83 | 84 | class FFT(KL.Layer): 85 | def call(self, x, mask=None): 86 | a = KB.permute_dimensions(x, (1,0,2)) 87 | a = KB.reshape(a, (x.shape[1] *x.shape[0], x.shape[2])) 88 | a = fft_func(a) 89 | a = KB.reshape(a, (x.shape[1], x.shape[0], x.shape[2])) 90 | return KB.permute_dimensions(a, (1,0,2)) 91 | class IFFT(KL.Layer): 92 | def call(self, x, mask=None): 93 | a = KB.permute_dimensions(x, (1,0,2)) 94 | a = KB.reshape(a, (x.shape[1] *x.shape[0], x.shape[2])) 95 | a = ifft_func(a) 96 | a = KB.reshape(a, (x.shape[1], x.shape[0], x.shape[2])) 97 | return KB.permute_dimensions(a, (1,0,2)) 98 | class FFT2(KL.Layer): 99 | def call(self, x, mask=None): 100 | a = KB.permute_dimensions(x, (1,0,2,3)) 101 | a = KB.reshape(a, (x.shape[1] *x.shape[0], x.shape[2], x.shape[3])) 102 | a = fft2_func(a) 103 | a = KB.reshape(a, (x.shape[1], x.shape[0], x.shape[2], x.shape[3])) 104 | return KB.permute_dimensions(a, (1,0,2,3)) 105 | class IFFT2(KL.Layer): 106 | def call(self, x, mask=None): 107 | a = KB.permute_dimensions(x, (1,0,2,3)) 108 | a = KB.reshape(a, (x.shape[1] *x.shape[0], x.shape[2], x.shape[3])) 109 | a = ifft2_func(a) 110 | a = KB.reshape(a, (x.shape[1], x.shape[0], x.shape[2], x.shape[3])) 111 | return KB.permute_dimensions(a, (1,0,2,3)) 112 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/activations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Authors: Qinggang Sun 5 | # 6 | # Reference: 7 | # Authors: Russell Doyosae 8 | # https://github.com/Doyosae/Deep_Complex_Networks 9 | 10 | import numpy as np 11 | import keras 12 | import keras.backend as K 13 | import tensorflow as tf 14 | 15 | 16 | def complex_flatten (real, imag): 17 | 18 | real = keras.layers.Flatten()(real) 19 | imag = keras.layers.Flatten()(imag) 20 | 21 | return real, imag 22 | 23 | 24 | def CReLU (real, imag): 25 | real = keras.layers.ReLU()(real) 26 | imag = keras.layers.ReLU()(imag) 27 | 28 | return real, imag 29 | 30 | 31 | def zReLU (real, imag): 32 | 33 | real = keras.layers.ReLU()(real) 34 | imag = keras.layers.ReLU()(imag) 35 | 36 | real_flag = tf.cast(tf.cast(real, tf.bool), tf.float32) 37 | imag_flag = tf.cast(tf.cast(imag, tf.bool), tf.float32) 38 | 39 | flag = real_flag * imag_flag 40 | 41 | real = tf.math.multiply(real, flag) 42 | imag = tf.math.multiply(imag, flag) 43 | 44 | return real, imag 45 | 46 | 47 | def modReLU (real, imag): 48 | 49 | norm = tf.abs(tf.complex(real, imag)) 50 | bias = tf.Variable(np.zeros([norm.get_shape()[-1]]), trainable = True, dtype=tf.float32) 51 | relu = tf.nn.relu(norm + bias) 52 | 53 | real = tf.math.multiply(relu / norm + (1e+5), real) 54 | imag = tf.math.multiply(relu / norm + (1e+5), imag) 55 | 56 | return real, imag 57 | 58 | 59 | def CLeaky_ReLU (real, imag): 60 | 61 | real = tf.nn.leaky_relu(real) 62 | imag = tf.nn.leaky_relu(imag) 63 | 64 | return real, imag 65 | 66 | 67 | def complex_tanh (real, imag): 68 | 69 | real = tf.nn.tanh(real) 70 | imag = tf.nn.tanh(imag) 71 | 72 | return real, imag 73 | 74 | 75 | def complex_softmax (real, imag): 76 | 77 | magnitude = tf.abs(tf.complex(real, imag)) 78 | magnitude = keras.layers.Softmax()(magnitude) 79 | 80 | return complex_to_real_imag(magnitude) 81 | 82 | def complex_real_sigmoid (real, imag): 83 | 84 | magnitude = tf.abs(tf.complex(real, imag)) 85 | magnitude = keras.activations.sigmoid(magnitude) 86 | 87 | return complex_to_real_imag(magnitude) 88 | 89 | def complex_to_real_imag(inputs): 90 | channel_axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 91 | input_dim = K.int_shape(inputs)[channel_axis] // 2 92 | rank = K.ndim(inputs) 93 | if channel_axis == 1: 94 | if rank == 2: 95 | real = inputs[:, :input_dim] 96 | imag = inputs[:, input_dim:] 97 | elif rank == 3: 98 | real = inputs[:, :input_dim, :] 99 | imag = inputs[:, input_dim:, :] 100 | elif rank == 4: 101 | real = inputs[:, :input_dim, :, :] 102 | imag = inputs[:, input_dim:, :, :] 103 | elif rank == 5: 104 | real = inputs[:, :input_dim, :, :, :] 105 | imag = inputs[:, input_dim:, :, :, :] 106 | else: # channels_axis == -1 107 | if rank == 2: 108 | real = inputs[:, :input_dim] 109 | imag = inputs[:, input_dim:] 110 | elif rank == 3: 111 | real = inputs[:, :, :input_dim] 112 | imag = inputs[:, :, input_dim:] 113 | elif rank == 4: 114 | real = inputs[:, :, :, :input_dim] 115 | imag = inputs[:, :, :, input_dim:] 116 | elif rank == 5: 117 | real = inputs[:, :, :, :, :input_dim] 118 | imag = inputs[:, :, :, :, input_dim:] 119 | 120 | return real, imag 121 | 122 | def real_imag_to_complex(real, imag): 123 | channel_axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 124 | output = keras.layers.concatenate([real, imag], axis=channel_axis) 125 | return output 126 | 127 | _activation_dict = {'crelu':CReLU, 128 | 'zrelu':zReLU, 129 | 'mrelu':modReLU, 130 | 'clrelu':CLeaky_ReLU, 131 | 'ctanh':complex_tanh, 132 | 'csoftmax':complex_softmax, 133 | 'crsigmoid':complex_real_sigmoid} 134 | 135 | def activation(inputs, key, input_form='complex'): 136 | if input_form == 'complex': # ((,...,) + (input_dim*2=n_channels,)) 137 | inputs = complex_to_real_imag(inputs) 138 | 139 | real, imag = inputs 140 | real, imag = _activation_dict[key](real, imag) 141 | output = real_imag_to_complex(real, imag) 142 | return output 143 | 144 | def layer_activation(inputs, activation_key, input_form='complex', name=None): 145 | """A keras Lambda layer as activation layer.""" 146 | outputs = keras.layers.Lambda( 147 | lambda inputs: (activation(inputs, activation_key, input_form)), name=name)(inputs) 148 | return outputs 149 | 150 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/densenet_blocks_1d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular one-dimensional complex valued DenseNet blocks.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Somshubra Majumdar. DenseNet 9 | # https://github.com/titu1994/DenseNet 10 | 11 | # pylint:disable=too-many-arguments, invalid-name, unused-variable 12 | 13 | import keras.layers 14 | import keras.regularizers 15 | 16 | from ..layers.activations import layer_activation 17 | from ..layers.bn import ComplexBatchNormalization 18 | from ..layers.conv import ComplexConv1D 19 | # from ..layers.conv import conv1d_transpose 20 | from ..layers.pool import ComplexAveragePooling1D 21 | 22 | 23 | def conv1d_block(inputs, nb_filter, activation='crelu', bottleneck=False, dropout_rate=None, weight_decay=1e-4): 24 | ''' Apply BatchNorm, Relu, 3x1 Conv1D, optional bottleneck block and dropout 25 | Args: 26 | inputs: keras tensor, complex valued inputs 27 | nb_filter: int, number of filters 28 | activation: char, activation function after the conventional layer 29 | bottleneck: bool, add bottleneck block 30 | dropout_rate: float, dropout rate 31 | weight_decay: float, weight decay factor 32 | Returns: keras tensor, with batch_norm, relu and convolution1d added (optional bottleneck) 33 | ''' 34 | concat_axis = 1 if keras.backend.image_data_format() == 'channels_first' else -1 35 | 36 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(inputs) 37 | 38 | x_complex = layer_activation(x_complex, activation) 39 | 40 | if bottleneck: 41 | inter_channel = nb_filter * 4 # Obtained from https://github.com/liuzhuang13/DenseNet/blob/master/densenet.lua 42 | 43 | x_complex = ComplexConv1D(inter_channel, 1, use_bias=False, spectral_parametrization=False, 44 | padding='same', kernel_regularizer=keras.regularizers.l2(weight_decay))(x_complex) 45 | 46 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(x_complex) 47 | 48 | x_complex = layer_activation(x_complex, activation) 49 | 50 | x_complex = ComplexConv1D(inter_channel, 3, use_bias=False, spectral_parametrization=False, 51 | padding='same')(x_complex) 52 | 53 | if dropout_rate: 54 | x_complex = keras.layers.Dropout(dropout_rate)(x_complex) 55 | 56 | return x_complex 57 | 58 | 59 | def dense1d_block(x_complex, nb_layers, nb_filter, growth_rate, activation='crelu', bottleneck=False, 60 | dropout_rate=None, weight_decay=1e-4, grow_nb_filters=True, return_concat_list=False): 61 | ''' Build a dense_block where the output of each conv_block is fed to subsequent ones 62 | Args: 63 | x_complex: keras tensor, inputs 64 | nb_layers: int, the number of layers of conv_block to append to the model. 65 | nb_filter: int, number of filters 66 | growth_rate: float, growth rate 67 | activation: char, activation function after the conventional layer 68 | bottleneck: bool, bottleneck block 69 | dropout_rate: float, dropout rate 70 | weight_decay: float, weight decay factor 71 | grow_nb_filters: bool, flag to decide to allow number of filters to grow 72 | return_concat_list: bool, return the list of feature maps along with the actual output 73 | Returns: keras tensor, with nb_layers of conv_block appended 74 | ''' 75 | concat_axis = 1 if keras.backend.image_data_format() == 'channels_first' else -1 76 | 77 | x_list = [x_complex] 78 | 79 | for i in range(nb_layers): 80 | cb = conv1d_block(x_complex, growth_rate, activation, bottleneck, dropout_rate, weight_decay) 81 | x_list.append(cb) 82 | 83 | x_complex = keras.layers.concatenate([x_complex, cb], axis=concat_axis) 84 | 85 | if grow_nb_filters: 86 | nb_filter += growth_rate 87 | 88 | if return_concat_list: 89 | return x_complex, nb_filter, x_list 90 | return x_complex, nb_filter 91 | 92 | 93 | def transition1d_block(inputs, nb_filter, activation='crelu', compression=1.0, weight_decay=1e-4): 94 | ''' Apply BatchNorm, Relu 1x1, Conv1D, optional compression, dropout and Maxpooling1D 95 | Args: 96 | inputs: keras tensor 97 | nb_filter: number of filters 98 | activation: char, activation function after the conventional layer 99 | compression: calculated as 1 - reduction. Reduces the number of feature maps 100 | in the transition block. 101 | dropout_rate: dropout rate 102 | weight_decay: weight decay factor 103 | Returns: keras tensor, after applying batch_norm, relu-conv, dropout, maxpool 104 | ''' 105 | concat_axis = 1 if keras.backend.image_data_format() == 'channels_first' else -1 106 | 107 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(inputs) 108 | 109 | x_complex = layer_activation(x_complex, activation) 110 | 111 | x_complex = ComplexConv1D(int(nb_filter * compression), 1, use_bias=False, spectral_parametrization=False, 112 | padding='same', kernel_regularizer=keras.regularizers.l2(weight_decay))(x_complex) 113 | 114 | x_complex = ComplexAveragePooling1D(2, strides=2)(x_complex) 115 | 116 | return x_complex 117 | 118 | 119 | def transition1d_up_block(inputs, nb_filters, way='deconv', weight_decay=1e-4): 120 | ''' SubpixelConvolutional Upscaling (factor = 2) 121 | Args: 122 | inputs: keras tensor 123 | nb_filters: number of layers 124 | way: can be 'upsampling', 'subpixel', 'deconv'. Determines type of upsampling performed 125 | weight_decay: weight decay factor 126 | Returns: keras tensor, after applying upsampling operation. 127 | ''' 128 | 129 | if way == 'upsampling': 130 | x_complex = keras.layers.UpSampling1D()(inputs) 131 | else: 132 | # x = keras.layers.Conv1DTranspose(nb_filters, 3, activation='relu', padding='same', strides=2, 133 | # kernel_initializer='he_normal', 134 | # kernel_regularizer=keras.regularizers.l2(weight_decay))(inputs) 135 | 136 | # x_complex = conv1d_transpose(inputs, nb_filters, 3, strides=2) 137 | 138 | x_complex = ComplexConv1D(nb_filters, 3, strides=2, padding='same', activation='crelu', 139 | transposed=True, 140 | kernel_initializer='he_normal', 141 | kernel_regularizer=keras.regularizers.l2(weight_decay))(inputs) 142 | 143 | return x_complex 144 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/densenet_blocks_2d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular two-dimensional complex valued DenseNet blocks.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Somshubra Majumdar. DenseNet 9 | # https://github.com/titu1994/DenseNet 10 | 11 | # pylint:disable=too-many-arguments, invalid-name, unused-variable 12 | 13 | import keras.layers 14 | import keras.regularizers 15 | 16 | from ..layers.activations import layer_activation 17 | from ..layers.bn import ComplexBatchNormalization 18 | from ..layers.conv import ComplexConv2D 19 | # from ..layers.conv import conv2d_transpose 20 | from ..layers.pool import ComplexAveragePooling2D 21 | 22 | 23 | def conv2d_block(inputs, nb_filter, activation='crelu', bottleneck=False, dropout_rate=None, weight_decay=1e-4): 24 | ''' Apply BatchNorm, Relu, 3x3 Conv2D, optional bottleneck block and dropout 25 | Args: 26 | inputs: keras tensor, complex valued inputs 27 | nb_filter: int, number of filters 28 | activation: char, activation function after the conventional layer 29 | bottleneck: bool, add bottleneck block 30 | dropout_rate: float, dropout rate 31 | weight_decay: float, weight decay factor 32 | Returns: keras tensor, with batch_norm, relu and convolution2d added (optional bottleneck) 33 | ''' 34 | concat_axis = 1 if keras.backend.image_data_format() == 'channels_first' else -1 35 | 36 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(inputs) 37 | 38 | x_complex = layer_activation(x_complex, activation) 39 | 40 | if bottleneck: 41 | inter_channel = nb_filter * 4 # Obtained from https://github.com/liuzhuang13/DenseNet/blob/master/densenet.lua 42 | 43 | x_complex = ComplexConv2D(inter_channel, (1, 1), use_bias=False, spectral_parametrization=False, 44 | padding='same', kernel_regularizer=keras.regularizers.l2(weight_decay))(x_complex) 45 | 46 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(x_complex) 47 | 48 | x_complex = layer_activation(x_complex, activation) 49 | 50 | x_complex = ComplexConv2D(inter_channel, (3, 3), use_bias=False, spectral_parametrization=False, 51 | padding='same')(x_complex) 52 | 53 | if dropout_rate: 54 | x_complex = keras.layers.Dropout(dropout_rate)(x_complex) 55 | 56 | return x_complex 57 | 58 | 59 | def dense2d_block(x_complex, nb_layers, nb_filter, growth_rate, activation='crelu', bottleneck=False, 60 | dropout_rate=None, weight_decay=1e-4, grow_nb_filters=True, return_concat_list=False): 61 | ''' Build a dense_block where the output of each conv_block is fed to subsequent ones 62 | Args: 63 | x_complex: keras tensor, inputs 64 | nb_layers: int, the number of layers of conv_block to append to the model. 65 | nb_filter: int, number of filters 66 | growth_rate: float, growth rate 67 | activation: char, activation function after the conventional layer 68 | bottleneck: bool, bottleneck block 69 | dropout_rate: float, dropout rate 70 | weight_decay: float, weight decay factor 71 | grow_nb_filters: bool, flag to decide to allow number of filters to grow 72 | return_concat_list: bool, return the list of feature maps along with the actual output 73 | Returns: keras tensor, with nb_layers of conv_block appended 74 | ''' 75 | concat_axis = 1 if keras.backend.image_data_format() == 'channels_first' else -1 76 | 77 | x_list = [x_complex] 78 | 79 | for i in range(nb_layers): 80 | cb = conv2d_block(x_complex, growth_rate, activation, bottleneck, dropout_rate, weight_decay) 81 | x_list.append(cb) 82 | 83 | x_complex = keras.layers.concatenate([x_complex, cb], axis=concat_axis) 84 | 85 | if grow_nb_filters: 86 | nb_filter += growth_rate 87 | 88 | if return_concat_list: 89 | return x_complex, nb_filter, x_list 90 | return x_complex, nb_filter 91 | 92 | 93 | def transition2d_block(inputs, nb_filter, activation='crelu', compression=1.0, weight_decay=1e-4): 94 | ''' Apply BatchNorm, Relu 1x1, Conv2D, optional compression, dropout and Maxpooling2D 95 | Args: 96 | inputs: keras tensor 97 | nb_filter: number of filters 98 | activation: char, activation function after the conventional layer 99 | compression: calculated as 1 - reduction. Reduces the number of feature maps 100 | in the transition block. 101 | dropout_rate: dropout rate 102 | weight_decay: weight decay factor 103 | Returns: keras tensor, after applying batch_norm, relu-conv, dropout, maxpool 104 | ''' 105 | concat_axis = 1 if keras.backend.image_data_format() == 'channels_first' else -1 106 | 107 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(inputs) 108 | 109 | x_complex = layer_activation(x_complex, activation) 110 | 111 | x_complex = ComplexConv2D(int(nb_filter * compression), (1, 1), use_bias=False, spectral_parametrization=False, 112 | padding='same', kernel_regularizer=keras.regularizers.l2(weight_decay))(x_complex) 113 | 114 | x_complex = ComplexAveragePooling2D((2, 2), strides=(2, 2))(x_complex) 115 | 116 | return x_complex 117 | 118 | 119 | def transition2d_up_block(inputs, nb_filters, way='deconv', weight_decay=1e-4): 120 | ''' SubpixelConvolutional Upscaling (factor = 2) 121 | Args: 122 | inputs: keras tensor 123 | nb_filters: number of layers 124 | way: can be 'upsampling', 'subpixel', 'deconv'. Determines type of upsampling performed 125 | weight_decay: weight decay factor 126 | Returns: keras tensor, after applying upsampling operation. 127 | ''' 128 | 129 | if way == 'upsampling': 130 | x_complex = keras.layers.UpSampling2D()(inputs) 131 | else: 132 | # x = keras.layers.Conv2DTranspose(nb_filters, (3, 3), activation='relu', padding='same', strides=(2, 2), 133 | # kernel_initializer='he_normal', 134 | # kernel_regularizer=keras.regularizers.l2(weight_decay))(inputs) 135 | 136 | # x_complex = conv2d_transpose(inputs, nb_filters, (3, 3), strides=(2, 2)) 137 | 138 | x_complex = ComplexConv2D(nb_filters, (3, 3), strides=(2, 2), padding='same', activation='crelu', 139 | transposed=True, 140 | kernel_initializer='he_normal', 141 | kernel_regularizer=keras.regularizers.l2(weight_decay))(inputs) 142 | 143 | return x_complex 144 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/resnet_blocks_1d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular one-dimensional complex valued residual blocks.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Allen Goodman, Allen Goodman, Claire McQuin, Hans Gaiser, et al. keras-resnet 9 | # https://github.com/broadinstitute/keras-resnet 10 | 11 | # pylint:disable=too-many-arguments, invalid-name, unused-argument 12 | 13 | import keras.layers 14 | import keras.regularizers 15 | 16 | from ..layers.activations import layer_activation 17 | from ..layers.bn import ComplexBatchNormalization 18 | from ..layers.conv import ComplexConv1D 19 | 20 | 21 | def basic_1d(filters, 22 | stage=0, 23 | block=0, 24 | kernel_size=3, 25 | numerical_name=False, 26 | stride=None, 27 | activation='crelu', 28 | **kwargs, 29 | ): 30 | """ 31 | A one-dimensional basic block. 32 | 33 | :param filters: int, the output’s feature space 34 | 35 | :param stage: int, representing the stage of this block (starting from 0) 36 | 37 | :param block: int, representing this block (starting from 0) 38 | 39 | :param kernel_size: int or tuple/list of 2 integers, size of the kernel 40 | 41 | :param numerical_name: bool, if true, uses numbers to represent blocks instead of chars (ResNet{18, 34}) 42 | 43 | :param stride: int, representing the stride used in the shortcut and the first conv layer, 44 | default derives stride from block id 45 | 46 | :param activation: str, the activation of convolution layer in residual blocks 47 | 48 | Usage: 49 | 50 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import basic_1d 51 | 52 | >>> basic_1d(64) 53 | """ 54 | if stride is None: 55 | if block != 0 or stage == 0: 56 | stride = 1 57 | else: 58 | stride = 2 59 | 60 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 61 | 62 | if block > 0 and numerical_name: 63 | block_char = f'b{block}' 64 | else: 65 | block_char = chr(ord('a') + block) 66 | 67 | stage_char = str(stage + 2) 68 | 69 | def f(inputs, **kwargs): 70 | """Method for block.""" 71 | outputs = keras.layers.ZeroPadding1D(padding=1, name=f'padding{stage_char}{block_char}_branch2a')(inputs) 72 | 73 | outputs = ComplexConv1D(filters, kernel_size, strides=stride, use_bias=False, spectral_parametrization=False, 74 | name=f'res{stage_char}{block_char}_branch2a', **kwargs)(outputs) 75 | 76 | outputs = ComplexBatchNormalization( 77 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2a')(outputs) 78 | 79 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2a_{activation}') 80 | 81 | outputs = keras.layers.ZeroPadding1D(padding=1, name=f'padding{stage_char}{block_char}_branch2b')(outputs) 82 | 83 | outputs = ComplexConv1D(filters, kernel_size, use_bias=False, spectral_parametrization=False, 84 | name=f'res{stage_char}{block_char}_branch2b', **kwargs)(outputs) 85 | 86 | outputs = ComplexBatchNormalization( 87 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2b')(outputs) 88 | 89 | if block == 0: 90 | shortcut = ComplexConv1D(filters, 1, strides=stride, use_bias=False, spectral_parametrization=False, 91 | name=f'res{stage_char}{block_char}_branch1', **kwargs)(inputs) 92 | 93 | shortcut = ComplexBatchNormalization( 94 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch1')(shortcut) 95 | else: 96 | shortcut = inputs 97 | 98 | outputs = keras.layers.add([outputs, shortcut], name=f'res{stage_char}{block_char}') 99 | 100 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_{activation}') 101 | 102 | return outputs 103 | 104 | return f 105 | 106 | 107 | def bottleneck_1d(filters, 108 | stage=0, 109 | block=0, 110 | kernel_size=3, 111 | numerical_name=False, 112 | stride=None, 113 | activation='crelu', 114 | **kwargs, 115 | ): 116 | """ 117 | A one-dimensional bottleneck block. 118 | 119 | :param filters: int, the output’s feature space 120 | 121 | :param stage: int, representing the stage of this block (starting from 0) 122 | 123 | :param block: int, representing this block (starting from 0) 124 | 125 | :param kernel_size: int or tuple/list of 2 integers, size of the kernel 126 | 127 | :param numerical_name: bool, if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 128 | 129 | :param stride: int, representing the stride used in the shortcut and the first conv layer, 130 | default derives stride from block id 131 | 132 | :param activation: str, the activation of convolution layer in residual blocks 133 | 134 | Usage: 135 | 136 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import bottleneck_1d 137 | 138 | >>> bottleneck_1d(64) 139 | """ 140 | 141 | if stride is None: 142 | if block != 0 or stage == 0: 143 | stride = 1 144 | else: 145 | stride = 2 146 | 147 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 148 | 149 | if block > 0 and numerical_name: 150 | block_char = f'b{block}' 151 | else: 152 | block_char = chr(ord('a') + block) 153 | 154 | stage_char = str(stage + 2) 155 | 156 | def f(inputs, **kwargs): 157 | """Method for block.""" 158 | outputs = ComplexConv1D(filters, 1, strides=stride, use_bias=False, spectral_parametrization=False, 159 | name=f'res{stage_char}{block_char}_branch2a', **kwargs)(inputs) 160 | 161 | outputs = ComplexBatchNormalization( 162 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2a')(outputs) 163 | 164 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2a_{activation}') 165 | 166 | outputs = keras.layers.ZeroPadding1D(padding=1, name=f'padding{stage_char}{block_char}_branch2b')(outputs) 167 | 168 | outputs = ComplexConv1D(filters, kernel_size, use_bias=False, spectral_parametrization=False, 169 | name=f'res{stage_char}{block_char}_branch2b', **kwargs)(outputs) 170 | 171 | outputs = ComplexBatchNormalization( 172 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2b')(outputs) 173 | 174 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2b_{activation}') 175 | 176 | outputs = ComplexConv1D(filters*4, 1, strides=1, use_bias=False, spectral_parametrization=False, 177 | name=f'res{stage_char}{block_char}_branch2c', **kwargs)(outputs) 178 | 179 | outputs = ComplexBatchNormalization( 180 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2c')(outputs) 181 | 182 | if block == 0: 183 | shortcut = ComplexConv1D(filters*4, 1, strides=stride, use_bias=False, spectral_parametrization=False, 184 | name=f'res{stage_char}{block_char}_branch1', **kwargs)(inputs) 185 | 186 | shortcut = ComplexBatchNormalization( 187 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch1')(shortcut) 188 | else: 189 | shortcut = inputs 190 | 191 | outputs = keras.layers.add([outputs, shortcut], name=f'res{stage_char}{block_char}') 192 | 193 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_{activation}') 194 | 195 | return outputs 196 | 197 | return f 198 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/resnet_blocks_2d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular two-dimensional complex valued residual blocks.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Allen Goodman, Allen Goodman, Claire McQuin, Hans Gaiser, et al. keras-resnet 9 | # https://github.com/broadinstitute/keras-resnet 10 | 11 | # pylint:disable=too-many-arguments, invalid-name, unused-argument 12 | 13 | import keras.layers 14 | import keras.regularizers 15 | 16 | from ..layers.activations import layer_activation 17 | from ..layers.bn import ComplexBatchNormalization 18 | from ..layers.conv import ComplexConv2D 19 | 20 | 21 | def basic_2d(filters, 22 | stage=0, 23 | block=0, 24 | kernel_size=3, 25 | numerical_name=False, 26 | stride=None, 27 | activation='crelu', 28 | **kwargs, 29 | ): 30 | """ 31 | A two-dimensional basic block. 32 | 33 | :param filters: int, the output’s feature space 34 | 35 | :param stage: int, representing the stage of this block (starting from 0) 36 | 37 | :param block: int, representing this block (starting from 0) 38 | 39 | :param kernel_size: int or tuple/list of 2 integers, size of the kernel 40 | 41 | :param numerical_name: bool, if true, uses numbers to represent blocks instead of chars (ResNet{18, 34}) 42 | 43 | :param stride: int, representing the stride used in the shortcut and the first conv layer, 44 | default derives stride from block id 45 | 46 | :param activation: str, the activation of convolution layer in residual blocks 47 | 48 | Usage: 49 | 50 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import basic_2d 51 | 52 | >>> basic_2d(64) 53 | """ 54 | if stride is None: 55 | if block != 0 or stage == 0: 56 | stride = 1 57 | else: 58 | stride = 2 59 | 60 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 61 | 62 | if block > 0 and numerical_name: 63 | block_char = f'b{block}' 64 | else: 65 | block_char = chr(ord('a') + block) 66 | 67 | stage_char = str(stage + 2) 68 | 69 | def f(inputs, **kwargs): 70 | """Method for block.""" 71 | outputs = keras.layers.ZeroPadding2D(padding=1, name=f'padding{stage_char}{block_char}_branch2a')(inputs) 72 | 73 | outputs = ComplexConv2D(filters, kernel_size, strides=stride, use_bias=False, spectral_parametrization=False, 74 | name=f'res{stage_char}{block_char}_branch2a', **kwargs)(outputs) 75 | 76 | outputs = ComplexBatchNormalization( 77 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2a')(outputs) 78 | 79 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2a_{activation}') 80 | 81 | outputs = keras.layers.ZeroPadding2D(padding=1, name=f'padding{stage_char}{block_char}_branch2b')(outputs) 82 | 83 | outputs = ComplexConv2D(filters, kernel_size, use_bias=False, spectral_parametrization=False, 84 | name=f'res{stage_char}{block_char}_branch2b', **kwargs)(outputs) 85 | 86 | outputs = ComplexBatchNormalization( 87 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2b')(outputs) 88 | 89 | if block == 0: 90 | shortcut = ComplexConv2D(filters, (1, 1), strides=stride, use_bias=False, spectral_parametrization=False, 91 | name=f'res{stage_char}{block_char}_branch1', **kwargs)(inputs) 92 | 93 | shortcut = ComplexBatchNormalization( 94 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch1')(shortcut) 95 | else: 96 | shortcut = inputs 97 | 98 | outputs = keras.layers.add([outputs, shortcut], name=f'res{stage_char}{block_char}') 99 | 100 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_{activation}') 101 | 102 | return outputs 103 | 104 | return f 105 | 106 | 107 | def bottleneck_2d(filters, 108 | stage=0, 109 | block=0, 110 | kernel_size=3, 111 | numerical_name=False, 112 | stride=None, 113 | activation='crelu', 114 | **kwargs, 115 | ): 116 | """ 117 | A two-dimensional bottleneck block. 118 | 119 | :param filters: int, the output’s feature space 120 | 121 | :param stage: int, representing the stage of this block (starting from 0) 122 | 123 | :param block: int, representing this block (starting from 0) 124 | 125 | :param kernel_size: int or tuple/list of 2 integers, size of the kernel 126 | 127 | :param numerical_name: bool, if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 128 | 129 | :param stride: int, representing the stride used in the shortcut and the first conv layer, 130 | default derives stride from block id 131 | 132 | :param activation: str, the activation of convolution layer in residual blocks 133 | 134 | Usage: 135 | 136 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import bottleneck_2d 137 | 138 | >>> bottleneck_2d(64) 139 | """ 140 | 141 | if stride is None: 142 | if block != 0 or stage == 0: 143 | stride = 1 144 | else: 145 | stride = 2 146 | 147 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 148 | 149 | if block > 0 and numerical_name: 150 | block_char = f'b{block}' 151 | else: 152 | block_char = chr(ord('a') + block) 153 | 154 | stage_char = str(stage + 2) 155 | 156 | def f(inputs, **kwargs): 157 | """Method for block.""" 158 | outputs = ComplexConv2D(filters, 1, strides=stride, use_bias=False, spectral_parametrization=False, 159 | name=f'res{stage_char}{block_char}_branch2a', **kwargs)(inputs) 160 | 161 | outputs = ComplexBatchNormalization( 162 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2a')(outputs) 163 | 164 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2a_{activation}') 165 | 166 | outputs = keras.layers.ZeroPadding2D(padding=1, name=f'padding{stage_char}{block_char}_branch2b')(outputs) 167 | 168 | outputs = ComplexConv2D(filters, kernel_size, use_bias=False, spectral_parametrization=False, 169 | name=f'res{stage_char}{block_char}_branch2b', **kwargs)(outputs) 170 | 171 | outputs = ComplexBatchNormalization( 172 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2b')(outputs) 173 | 174 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2b_{activation}') 175 | 176 | outputs = ComplexConv2D(filters*4, 1, strides=(1, 1), use_bias=False, spectral_parametrization=False, 177 | name=f'res{stage_char}{block_char}_branch2c', **kwargs)(outputs) 178 | 179 | outputs = ComplexBatchNormalization( 180 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2c')(outputs) 181 | 182 | if block == 0: 183 | shortcut = ComplexConv2D(filters*4, (1, 1), strides=stride, use_bias=False, spectral_parametrization=False, 184 | name=f'res{stage_char}{block_char}_branch1', **kwargs)(inputs) 185 | 186 | shortcut = ComplexBatchNormalization( 187 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch1')(shortcut) 188 | else: 189 | shortcut = inputs 190 | 191 | outputs = keras.layers.add([outputs, shortcut], name=f'res{stage_char}{block_char}') 192 | 193 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_{activation}') 194 | 195 | return outputs 196 | 197 | return f 198 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/resnet_blocks_3d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular two-dimensional complex valued residual blocks.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Allen Goodman, Allen Goodman, Claire McQuin, Hans Gaiser, et al. keras-resnet 9 | # https://github.com/broadinstitute/keras-resnet 10 | 11 | # pylint:disable=too-many-arguments, invalid-name, unused-argument 12 | 13 | import keras.layers 14 | import keras.regularizers 15 | 16 | from ..layers.activations import layer_activation 17 | from ..layers.bn import ComplexBatchNormalization 18 | from ..layers.conv import ComplexConv3D 19 | 20 | 21 | def basic_3d(filters, 22 | stage=0, 23 | block=0, 24 | kernel_size=3, 25 | numerical_name=False, 26 | stride=None, 27 | activation='crelu', 28 | **kwargs, 29 | ): 30 | """ 31 | A two-dimensional basic block. 32 | 33 | :param filters: int, the output’s feature space 34 | 35 | :param stage: int, representing the stage of this block (starting from 0) 36 | 37 | :param block: int, representing this block (starting from 0) 38 | 39 | :param kernel_size: int or tuple/list of 2 integers, size of the kernel 40 | 41 | :param numerical_name: bool, if true, uses numbers to represent blocks instead of chars (ResNet{18, 34}) 42 | 43 | :param stride: int, representing the stride used in the shortcut and the first conv layer, 44 | default derives stride from block id 45 | 46 | :param activation: str, the activation of convolution layer in residual blocks 47 | 48 | Usage: 49 | 50 | >>> from complex_networks_keras_tf1.models.resnet_models_3d import basic_3d 51 | 52 | >>> basic_3d(64) 53 | """ 54 | if stride is None: 55 | if block != 0 or stage == 0: 56 | stride = 1 57 | else: 58 | stride = 2 59 | 60 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 61 | 62 | if block > 0 and numerical_name: 63 | block_char = f'b{block}' 64 | else: 65 | block_char = chr(ord('a') + block) 66 | 67 | stage_char = str(stage + 2) 68 | 69 | def f(inputs, **kwargs): 70 | """Method for block.""" 71 | outputs = keras.layers.ZeroPadding3D(padding=1, name=f'padding{stage_char}{block_char}_branch2a')(inputs) 72 | 73 | outputs = ComplexConv3D(filters, kernel_size, strides=stride, use_bias=False, spectral_parametrization=False, 74 | name=f'res{stage_char}{block_char}_branch2a', **kwargs)(outputs) 75 | 76 | outputs = ComplexBatchNormalization( 77 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2a')(outputs) 78 | 79 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2a_{activation}') 80 | 81 | outputs = keras.layers.ZeroPadding3D(padding=1, name=f'padding{stage_char}{block_char}_branch2b')(outputs) 82 | 83 | outputs = ComplexConv3D(filters, kernel_size, use_bias=False, spectral_parametrization=False, 84 | name=f'res{stage_char}{block_char}_branch2b', **kwargs)(outputs) 85 | 86 | outputs = ComplexBatchNormalization( 87 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2b')(outputs) 88 | 89 | if block == 0: 90 | shortcut = ComplexConv3D(filters, (1, 1), strides=stride, use_bias=False, spectral_parametrization=False, 91 | name=f'res{stage_char}{block_char}_branch1', **kwargs)(inputs) 92 | 93 | shortcut = ComplexBatchNormalization( 94 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch1')(shortcut) 95 | else: 96 | shortcut = inputs 97 | 98 | outputs = keras.layers.add([outputs, shortcut], name=f'res{stage_char}{block_char}') 99 | 100 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_{activation}') 101 | 102 | return outputs 103 | 104 | return f 105 | 106 | 107 | def bottleneck_3d(filters, 108 | stage=0, 109 | block=0, 110 | kernel_size=3, 111 | numerical_name=False, 112 | stride=None, 113 | activation='crelu', 114 | **kwargs, 115 | ): 116 | """ 117 | A two-dimensional bottleneck block. 118 | 119 | :param filters: int, the output’s feature space 120 | 121 | :param stage: int, representing the stage of this block (starting from 0) 122 | 123 | :param block: int, representing this block (starting from 0) 124 | 125 | :param kernel_size: int or tuple/list of 2 integers, size of the kernel 126 | 127 | :param numerical_name: bool, if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 128 | 129 | :param stride: int, representing the stride used in the shortcut and the first conv layer, 130 | default derives stride from block id 131 | 132 | :param activation: str, the activation of convolution layer in residual blocks 133 | 134 | Usage: 135 | 136 | >>> from complex_networks_keras_tf1.models.resnet_models_3d import bottleneck_3d 137 | 138 | >>> bottleneck_3d(64) 139 | """ 140 | 141 | if stride is None: 142 | if block != 0 or stage == 0: 143 | stride = 1 144 | else: 145 | stride = 2 146 | 147 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 148 | 149 | if block > 0 and numerical_name: 150 | block_char = f'b{block}' 151 | else: 152 | block_char = chr(ord('a') + block) 153 | 154 | stage_char = str(stage + 2) 155 | 156 | def f(inputs, **kwargs): 157 | """Method for block.""" 158 | outputs = ComplexConv3D(filters, 1, strides=stride, use_bias=False, spectral_parametrization=False, 159 | name=f'res{stage_char}{block_char}_branch2a', **kwargs)(inputs) 160 | 161 | outputs = ComplexBatchNormalization( 162 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2a')(outputs) 163 | 164 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2a_{activation}') 165 | 166 | outputs = keras.layers.ZeroPadding3D(padding=1, name=f'padding{stage_char}{block_char}_branch2b')(outputs) 167 | 168 | outputs = ComplexConv3D(filters, kernel_size, use_bias=False, spectral_parametrization=False, 169 | name=f'res{stage_char}{block_char}_branch2b', **kwargs)(outputs) 170 | 171 | outputs = ComplexBatchNormalization( 172 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2b')(outputs) 173 | 174 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_branch2b_{activation}') 175 | 176 | outputs = ComplexConv3D(filters*4, 1, strides=(1, 1), use_bias=False, spectral_parametrization=False, 177 | name=f'res{stage_char}{block_char}_branch2c', **kwargs)(outputs) 178 | 179 | outputs = ComplexBatchNormalization( 180 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch2c')(outputs) 181 | 182 | if block == 0: 183 | shortcut = ComplexConv3D(filters*4, (1, 1), strides=stride, use_bias=False, spectral_parametrization=False, 184 | name=f'res{stage_char}{block_char}_branch1', **kwargs)(inputs) 185 | 186 | shortcut = ComplexBatchNormalization( 187 | axis=axis, epsilon=1e-5, name=f'bn{stage_char}{block_char}_branch1')(shortcut) 188 | else: 189 | shortcut = inputs 190 | 191 | outputs = keras.layers.add([outputs, shortcut], name=f'res{stage_char}{block_char}') 192 | 193 | outputs = layer_activation(outputs, activation, name=f'res{stage_char}{block_char}_{activation}') 194 | 195 | return outputs 196 | 197 | return f 198 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/stft.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Authors: Russell Doyosae 6 | # https://github.com/Doyosae/Deep_Complex_Networks 7 | # 8 | 9 | 10 | import scipy.signal 11 | import numpy as np 12 | import tensorflow as tf 13 | import keras.backend as K 14 | from keras.layers import Conv1D, Lambda, Conv2DTranspose 15 | """ 16 | STFT_network's INPUT : [batch_size, time_step (signal length), channel == 1] 17 | STFT_network's OUTPUT : return real, imag 18 | _, signal = scipy.io.wavfile.read("./test_speech/speech.wav") 19 | sound = np.reshape(signal, (1, -1, 1)) 20 | print(sound.shape) 21 | (1, 16384, 1) 22 | 23 | real : [batch_size, time_step, frequency_bin] 24 | imag : [batch_size, time_step, frequency_bin] 25 | 26 | STFT Network's outputs is transformation of time_step, frequency_bin transpose 27 | So, 28 | spec = tf.transpose(tf.complex(real, imag), perm = [0, 2, 1]) 29 | show_spectogram (spec, shape = (width, height)) 30 | 31 | 1. If you want to be part of the Convolution Network, 32 | real, imag <= tf.reshape in real or image (batch_size, time_step, Frequency_bin, 1)) form 33 | If you want to give [batch_size, time_step, Frequency_bin] like [batch_size, Frequency_bin, time_step, 1]... 34 | real, imag <= tf.transpose(real or imag, perm = [0, 2, 1, 3]) == tf.resape(real or imag, (batch_size, time_step, Frequency_bin, 1)) 35 | 36 | 37 | 2. Without an intermediate network, 38 | Input of ISTFT_network: STFT_network can be continuously received. 39 | Output of ISTFT_network : return signal [batch_size, time_step (signal length), channel] 40 | real : [batch_size, time_step, Frequency_bin] 41 | imag : [batch_size, time_step, Frequency_bin] 42 | 43 | 3. Basic Options 44 | length = 1024, over_lapping = 256, padding = "same" 45 | And the layer's trainable option is based on "False". 46 | Do not learn. DFT kernel is broken 47 | """ 48 | 49 | 50 | 'Short Time Fourier Transform via Neural Network' 51 | class STFT_network (tf.keras.layers.Layer): 52 | 53 | 54 | def __init__ (self, window_length = 1024, over_lapping = 256, padding = "same"): 55 | 56 | super(STFT_network, self).__init__() 57 | 58 | self.window_length = window_length 59 | self.frequency_bin = int(self.window_length/2 + 1) 60 | self.over_lapping = over_lapping 61 | self.padding = padding 62 | 63 | self.fourier_basis = np.fft.fft(np.eye(self.window_length)) 64 | self.discrete_fourier_transform_window = scipy.signal.hann(self.window_length, sym = False) 65 | self.discrete_fourier_transform_window = self.discrete_fourier_transform_window.reshape((1, -1)) 66 | 67 | self.kernel = np.multiply(self.fourier_basis, self.discrete_fourier_transform_window) 68 | del (self.fourier_basis) 69 | 70 | self.kernel = self.kernel[:self.frequency_bin, :] 71 | 72 | self.real_kernel_init = np.real(self.kernel) 73 | self.imag_kernel_init = np.imag(self.kernel) 74 | 75 | self.real_kernel_init = self.real_kernel_init.T 76 | self.imag_kernel_init = self.imag_kernel_init.T 77 | self.real_kernel_init = self.real_kernel_init[:, None, :] 78 | self.imag_kernel_init = self.imag_kernel_init[:, None, :] 79 | 80 | 81 | def build (self, inputs_shape): 82 | ''' 83 | self.frequency_bin : integer 84 | self.window_length : integer 85 | self.strides : same as over_lapping number 86 | kernel_initializer : 3D Tensor [width = window_length, height = 1, output size : self.frequency_bin] 87 | ''' 88 | self.real_fourier_convolution = Conv1D(filters = self.frequency_bin, 89 | kernel_size = self.window_length, 90 | strides = self.over_lapping, 91 | padding = self.padding, 92 | kernel_initializer = tf.keras.initializers.Constant(value = self.real_kernel_init), 93 | trainable = False) 94 | 95 | self.imag_foruier_convolution = Conv1D(filters = self.frequency_bin, 96 | kernel_size = self.window_length, 97 | strides = self.over_lapping, 98 | padding = self.padding, 99 | kernel_initializer = tf.keras.initializers.Constant(value = self.imag_kernel_init), 100 | trainable = False) 101 | 102 | super(STFT_network, self).build(inputs_shape) 103 | 104 | 105 | def call (self, input_signal): 106 | 107 | 'inputs_signal : 3D Tensor [batch_size, signal_length, channel_number]' 108 | real = self.real_fourier_convolution(input_signal) 109 | imag = self.imag_foruier_convolution(input_signal) 110 | 111 | return real, imag 112 | 113 | 114 | 'Inverse Short Time Fourier Transform via Neural Network' 115 | class ISTFT_network (tf.keras.layers.Layer): 116 | 117 | 118 | def __init__ (self, window_length = 1024, over_lapping = 256, padding = "same"): 119 | 120 | super(ISTFT_network, self).__init__() 121 | 122 | self.window_length = window_length 123 | self.over_lapping = over_lapping 124 | 125 | self.cut_off = int(self.window_length / 2 + 1) 126 | self.kernel_size = window_length 127 | self.strides = over_lapping 128 | self.padding = padding 129 | 130 | self.window_coefficient = scipy.signal.get_window("hanning", self.window_length) 131 | self.inverse_window = self.inverse_stft_window(self.window_coefficient, self.over_lapping) 132 | 133 | 'Inverse Fourier Transform Kernel' 134 | self.fourier_basis = np.fft.fft(np.eye(self.window_length)) 135 | self.fourier_basis = np.vstack([np.real(self.fourier_basis[:self.cut_off, :]), np.imag(self.fourier_basis[:self.cut_off, :])]) 136 | 137 | self.inverse_basis = self.inverse_window * np.linalg.pinv(self.fourier_basis).T[ :, None, None, ] 138 | self.inverse_basis = self.inverse_basis.T 139 | 140 | 141 | def inverse_stft_window (self, window, hop_length): 142 | 143 | 'Ceiling Division' 144 | window_length = len(window) 145 | denom = window ** 2 146 | overlaps = -(-window_length // hop_length) 147 | denom = np.pad(denom, (0, overlaps * hop_length - window_length), 'constant') 148 | denom = np.reshape(denom, (overlaps, hop_length)).sum(0) 149 | denom = np.tile(denom, (overlaps, 1)).reshape(overlaps * hop_length) 150 | 151 | return window / denom[:window_length] 152 | 153 | 154 | def build (self, inputs_shape): 155 | 156 | self.expand_dims_lambda = Lambda(lambda x: K.expand_dims(x, axis = 2)) 157 | self.Conv2DTranspose = Conv2DTranspose(filters = 1, 158 | kernel_size = (self.kernel_size, 1), 159 | strides = (self.strides, 1), 160 | padding = self.padding, 161 | kernel_initializer = tf.keras.initializers.Constant(self.inverse_basis), 162 | trainable = False) 163 | self.squeeze_dims_lambda = Lambda(lambda x: K.squeeze(x, axis = 2)) 164 | 165 | super(ISTFT_network, self).build(inputs_shape) 166 | 167 | 168 | def call (self, real, imag): 169 | ''' 170 | inputs_signal : 3D Tensor [batch_size, time_step (signal length), channel_number (Frequency bin)] 171 | To do Inverse Short Time Fourier Transform... 172 | The size of the input tensor shall come in the form of the batch size, the length of the signal, and the channel form of the signal. 173 | The input modalities of the Spectogram [deployment size, time step, frequency bin] are. 174 | 175 | Concat it at Axis = 2. 176 | If you perform expand_dims again at Axis = 2, [Deployment size. Time step, 1, frequency bin. 177 | 178 | In other words, an image in the form of Time Step x 1 is as good as a frequency bin channel. 179 | 180 | If you do Conv 2D Transpose and look at the output, 181 | [batch size, signal length, 1, 1] It's a form. 182 | 183 | Reduce size with skew to form [batch size, signal length, 1]. 184 | ''' 185 | input_tensor = tf.concat([real, imag], axis = 2) 186 | outputs = self.expand_dims_lambda(input_tensor) 187 | outputs = self.Conv2DTranspose(outputs) 188 | outputs = self.squeeze_dims_lambda(outputs) 189 | 190 | return outputs -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/dense.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Authors: Chiheb Trabelsi 6 | # 7 | 8 | import sys; sys.path.append('.') 9 | from keras import backend as K 10 | from keras import initializers, regularizers, constraints 11 | from keras.layers import Layer, InputSpec 12 | import numpy as np 13 | from numpy.random import RandomState 14 | from .activations import activation as activation_func 15 | 16 | class ComplexDense(Layer): 17 | """Regular complex densely-connected NN layer. 18 | `Dense` implements the operation: 19 | `real_preact = dot(real_input, real_kernel) - dot(imag_input, imag_kernel)` 20 | `imag_preact = dot(real_input, imag_kernel) + dot(imag_input, real_kernel)` 21 | `output = activation(K.concatenate([real_preact, imag_preact]) + bias)` 22 | where `activation` is the element-wise activation function 23 | passed as the `activation` argument, `kernel` is a weights matrix 24 | created by the layer, and `bias` is a bias vector created by the layer 25 | (only applicable if `use_bias` is `True`). 26 | Note: if the input to the layer has a rank greater than 2, then 27 | AN ERROR MESSAGE IS PRINTED. 28 | # Arguments 29 | units: Positive integer, dimensionality of each of the real part 30 | and the imaginary part. It is actualy the number of complex units. 31 | activation: Activation function to use 32 | (see keras.activations). 33 | If you don't specify anything, no activation is applied 34 | (ie. "linear" activation: `a(x) = x`). 35 | use_bias: Boolean, whether the layer uses a bias vector. 36 | kernel_initializer: Initializer for the complex `kernel` weights matrix. 37 | By default it is 'complex'. 38 | and the usual initializers could also be used. 39 | (see keras.initializers and init.py). 40 | bias_initializer: Initializer for the bias vector 41 | (see keras.initializers). 42 | kernel_regularizer: Regularizer function applied to 43 | the `kernel` weights matrix 44 | (see keras.regularizers). 45 | bias_regularizer: Regularizer function applied to the bias vector 46 | (see keras.regularizers). 47 | activity_regularizer: Regularizer function applied to 48 | the output of the layer (its "activation"). 49 | (see keras.regularizers). 50 | kernel_constraint: Constraint function applied to the kernel matrix 51 | (see keras.constraints). 52 | bias_constraint: Constraint function applied to the bias vector 53 | (see keras.constraints). 54 | # Input shape 55 | a 2D input with shape `(batch_size, input_dim)`. 56 | # Output shape 57 | For a 2D input with shape `(batch_size, input_dim)`, 58 | the output would have shape `(batch_size, units)`. 59 | """ 60 | 61 | def __init__(self, units, 62 | activation=None, 63 | use_bias=True, 64 | init_criterion='he', 65 | kernel_initializer='complex', 66 | bias_initializer='zeros', 67 | kernel_regularizer=None, 68 | bias_regularizer=None, 69 | activity_regularizer=None, 70 | kernel_constraint=None, 71 | bias_constraint=None, 72 | seed=None, 73 | **kwargs): 74 | if 'input_shape' not in kwargs and 'input_dim' in kwargs: 75 | kwargs['input_shape'] = (kwargs.pop('input_dim'),) 76 | super(ComplexDense, self).__init__(**kwargs) 77 | self.units = units 78 | self.activation = activation # key of activation 79 | self.use_bias = use_bias 80 | self.init_criterion = init_criterion 81 | if kernel_initializer in {'complex'}: 82 | self.kernel_initializer = kernel_initializer 83 | else: 84 | self.kernel_initializer = initializers.get(kernel_initializer) 85 | self.bias_initializer = initializers.get(bias_initializer) 86 | self.kernel_regularizer = regularizers.get(kernel_regularizer) 87 | self.bias_regularizer = regularizers.get(bias_regularizer) 88 | self.activity_regularizer = regularizers.get(activity_regularizer) 89 | self.kernel_constraint = constraints.get(kernel_constraint) 90 | self.bias_constraint = constraints.get(bias_constraint) 91 | if seed is None: 92 | self.seed = np.random.randint(1, 10e6) 93 | else: 94 | self.seed = seed 95 | self.input_spec = InputSpec(ndim=2) 96 | self.supports_masking = True 97 | 98 | def build(self, input_shape): 99 | assert len(input_shape) == 2 100 | assert input_shape[-1] % 2 == 0 101 | input_dim = input_shape[-1] // 2 102 | data_format = K.image_data_format() 103 | kernel_shape = (input_dim, self.units) 104 | fan_in, fan_out = initializers._compute_fans( 105 | kernel_shape, 106 | data_format=data_format 107 | ) 108 | if self.init_criterion == 'he': 109 | s = np.sqrt(1. / fan_in) 110 | elif self.init_criterion == 'glorot': 111 | s = np.sqrt(1. / (fan_in + fan_out)) 112 | rng = RandomState(seed=self.seed) 113 | 114 | # Equivalent initialization using amplitude phase representation: 115 | """modulus = rng.rayleigh(scale=s, size=kernel_shape) 116 | phase = rng.uniform(low=-np.pi, high=np.pi, size=kernel_shape) 117 | def init_w_real(shape, dtype=None): 118 | return modulus * K.cos(phase) 119 | def init_w_imag(shape, dtype=None): 120 | return modulus * K.sin(phase)""" 121 | 122 | # Initialization using euclidean representation: 123 | def init_w_real(shape, dtype=None): 124 | return rng.normal( 125 | size=kernel_shape, 126 | loc=0, 127 | scale=s, 128 | ).astype(dtype) 129 | def init_w_imag(shape, dtype=None): 130 | return rng.normal( 131 | size=kernel_shape, 132 | loc=0, 133 | scale=s 134 | ).astype(dtype) 135 | if self.kernel_initializer in {'complex'}: 136 | real_init = init_w_real 137 | imag_init = init_w_imag 138 | else: 139 | real_init = self.kernel_initializer 140 | imag_init = self.kernel_initializer 141 | 142 | self.real_kernel = self.add_weight( 143 | shape=kernel_shape, 144 | initializer=real_init, 145 | name='real_kernel', 146 | regularizer=self.kernel_regularizer, 147 | constraint=self.kernel_constraint 148 | ) 149 | self.imag_kernel = self.add_weight( 150 | shape=kernel_shape, 151 | initializer=imag_init, 152 | name='imag_kernel', 153 | regularizer=self.kernel_regularizer, 154 | constraint=self.kernel_constraint 155 | ) 156 | 157 | if self.use_bias: 158 | self.bias = self.add_weight( 159 | shape=(2 * self.units,), 160 | initializer=self.bias_initializer, 161 | name='bias', 162 | regularizer=self.bias_regularizer, 163 | constraint=self.bias_constraint 164 | ) 165 | else: 166 | self.bias = None 167 | 168 | self.input_spec = InputSpec(ndim=2, axes={-1: 2 * input_dim}) 169 | self.built = True 170 | 171 | def call(self, inputs): 172 | # input_shape = K.int_shape(inputs) 173 | # input_dim = input_shape[-1] // 2 174 | # real_input = inputs[:, :input_dim] 175 | # imag_input = inputs[:, input_dim:] 176 | 177 | cat_kernels_4_real = K.concatenate( 178 | [self.real_kernel, -self.imag_kernel], 179 | axis=-1 180 | ) 181 | cat_kernels_4_imag = K.concatenate( 182 | [self.imag_kernel, self.real_kernel], 183 | axis=-1 184 | ) 185 | cat_kernels_4_complex = K.concatenate( 186 | [cat_kernels_4_real, cat_kernels_4_imag], 187 | axis=0 188 | ) 189 | 190 | output = K.dot(inputs, cat_kernels_4_complex) 191 | 192 | if self.use_bias: 193 | output = K.bias_add(output, self.bias) 194 | if self.activation is not None: 195 | output = activation_func(output, self.activation) 196 | 197 | return output 198 | 199 | def compute_output_shape(self, input_shape): 200 | assert input_shape and len(input_shape) == 2 201 | assert input_shape[-1] 202 | output_shape = list(input_shape) 203 | output_shape[-1] = 2 * self.units 204 | return tuple(output_shape) 205 | 206 | def get_config(self): 207 | if self.kernel_initializer in {'complex'}: 208 | ki = self.kernel_initializer 209 | else: 210 | ki = initializers.serialize(self.kernel_initializer) 211 | config = { 212 | 'units': self.units, 213 | 'activation': self.activation, 214 | 'use_bias': self.use_bias, 215 | 'init_criterion': self.init_criterion, 216 | 'kernel_initializer': ki, 217 | 'bias_initializer': initializers.serialize(self.bias_initializer), 218 | 'kernel_regularizer': regularizers.serialize(self.kernel_regularizer), 219 | 'bias_regularizer': regularizers.serialize(self.bias_regularizer), 220 | 'activity_regularizer': regularizers.serialize(self.activity_regularizer), 221 | 'kernel_constraint': constraints.serialize(self.kernel_constraint), 222 | 'bias_constraint': constraints.serialize(self.bias_constraint), 223 | 'seed': self.seed, 224 | } 225 | base_config = super(ComplexDense, self).get_config() 226 | return dict(list(base_config.items()) + list(config.items())) 227 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/init.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Authors: Chiheb Trabelsi 6 | 7 | import numpy as np 8 | from numpy.random import RandomState 9 | import keras.backend as K 10 | from keras import initializers 11 | from keras.initializers import Initializer 12 | from keras.utils.generic_utils import (serialize_keras_object, 13 | deserialize_keras_object) 14 | 15 | 16 | class IndependentFilters(Initializer): 17 | # This initialization constructs real-valued kernels 18 | # that are independent as much as possible from each other 19 | # while respecting either the He or the Glorot criterion. 20 | def __init__(self, kernel_size, input_dim, 21 | weight_dim, nb_filters=None, 22 | criterion='glorot', seed=None): 23 | 24 | # `weight_dim` is used as a parameter for sanity check 25 | # as we should not pass an integer as kernel_size when 26 | # the weight dimension is >= 2. 27 | # nb_filters == 0 if weights are not convolutional (matrix instead of filters) 28 | # then in such a case, weight_dim = 2. 29 | # (in case of 2D input): 30 | # nb_filters == None and len(kernel_size) == 2 and_weight_dim == 2 31 | # conv1D: len(kernel_size) == 1 and weight_dim == 1 32 | # conv2D: len(kernel_size) == 2 and weight_dim == 2 33 | # conv3d: len(kernel_size) == 3 and weight_dim == 3 34 | 35 | assert len(kernel_size) == weight_dim and weight_dim in {0, 1, 2, 3} 36 | self.nb_filters = nb_filters 37 | self.kernel_size = kernel_size 38 | self.input_dim = input_dim 39 | self.weight_dim = weight_dim 40 | self.criterion = criterion 41 | self.seed = 1337 if seed is None else seed 42 | 43 | def __call__(self, shape, dtype=None): 44 | 45 | if self.nb_filters is not None: 46 | num_rows = self.nb_filters * self.input_dim 47 | num_cols = np.prod(self.kernel_size) 48 | else: 49 | # in case it is the kernel is a matrix and not a filter 50 | # which is the case of 2D input (No feature maps). 51 | num_rows = self.input_dim 52 | num_cols = self.kernel_size[-1] 53 | 54 | flat_shape = (num_rows, num_cols) 55 | rng = RandomState(self.seed) 56 | x = rng.uniform(size=flat_shape) 57 | u, _, v = np.linalg.svd(x) 58 | orthogonal_x = np.dot(u, np.dot(np.eye(num_rows, num_cols), v.T)) 59 | if self.nb_filters is not None: 60 | independent_filters = np.reshape(orthogonal_x, (num_rows,) + tuple(self.kernel_size)) 61 | fan_in, fan_out = initializers._compute_fans( 62 | tuple(self.kernel_size) + (self.input_dim, self.nb_filters) 63 | ) 64 | else: 65 | independent_filters = orthogonal_x 66 | fan_in, fan_out = (self.input_dim, self.kernel_size[-1]) 67 | 68 | if self.criterion == 'glorot': 69 | desired_var = 2. / (fan_in + fan_out) 70 | elif self.criterion == 'he': 71 | desired_var = 2. / fan_in 72 | else: 73 | raise ValueError('Invalid criterion: ' + self.criterion) 74 | 75 | multip_constant = np.sqrt (desired_var / np.var(independent_filters)) 76 | scaled_indep = multip_constant * independent_filters 77 | 78 | if self.weight_dim == 2 and self.nb_filters is None: 79 | weight_real = scaled_real 80 | weight_imag = scaled_imag 81 | else: 82 | kernel_shape = tuple(self.kernel_size) + (self.input_dim, self.nb_filters) 83 | if self.weight_dim == 1: 84 | transpose_shape = (1, 0) 85 | elif self.weight_dim == 2 and self.nb_filters is not None: 86 | transpose_shape = (1, 2, 0) 87 | elif self.weight_dim == 3 and self.nb_filters is not None: 88 | transpose_shape = (1, 2, 3, 0) 89 | weight = np.transpose(scaled_indep, transpose_shape) 90 | weight = np.reshape(weight, kernel_shape) 91 | 92 | return weight 93 | 94 | def get_config(self): 95 | return {'nb_filters': self.nb_filters, 96 | 'kernel_size': self.kernel_size, 97 | 'input_dim': self.input_dim, 98 | 'weight_dim': self.weight_dim, 99 | 'criterion': self.criterion, 100 | 'seed': self.seed} 101 | 102 | 103 | class ComplexIndependentFilters(Initializer): 104 | # This initialization constructs complex-valued kernels 105 | # that are independent as much as possible from each other 106 | # while respecting either the He or the Glorot criterion. 107 | def __init__(self, kernel_size, input_dim, 108 | weight_dim, nb_filters=None, 109 | criterion='glorot', seed=None): 110 | 111 | # `weight_dim` is used as a parameter for sanity check 112 | # as we should not pass an integer as kernel_size when 113 | # the weight dimension is >= 2. 114 | # nb_filters == 0 if weights are not convolutional (matrix instead of filters) 115 | # then in such a case, weight_dim = 2. 116 | # (in case of 2D input): 117 | # nb_filters == None and len(kernel_size) == 2 and_weight_dim == 2 118 | # conv1D: len(kernel_size) == 1 and weight_dim == 1 119 | # conv2D: len(kernel_size) == 2 and weight_dim == 2 120 | # conv3d: len(kernel_size) == 3 and weight_dim == 3 121 | 122 | assert len(kernel_size) == weight_dim and weight_dim in {0, 1, 2, 3} 123 | self.nb_filters = nb_filters 124 | self.kernel_size = kernel_size 125 | self.input_dim = input_dim 126 | self.weight_dim = weight_dim 127 | self.criterion = criterion 128 | self.seed = 1337 if seed is None else seed 129 | 130 | def __call__(self, shape, dtype=None): 131 | 132 | if self.nb_filters is not None: 133 | num_rows = self.nb_filters * self.input_dim 134 | num_cols = np.prod(self.kernel_size) 135 | else: 136 | # in case it is the kernel is a matrix and not a filter 137 | # which is the case of 2D input (No feature maps). 138 | num_rows = self.input_dim 139 | num_cols = self.kernel_size[-1] 140 | 141 | flat_shape = (int(num_rows), int(num_cols)) 142 | rng = RandomState(self.seed) 143 | r = rng.uniform(size=flat_shape) 144 | i = rng.uniform(size=flat_shape) 145 | z = r + 1j * i 146 | u, _, v = np.linalg.svd(z) 147 | unitary_z = np.dot(u, np.dot(np.eye(int(num_rows), int(num_cols)), np.conjugate(v).T)) 148 | real_unitary = unitary_z.real 149 | imag_unitary = unitary_z.imag 150 | if self.nb_filters is not None: 151 | indep_real = np.reshape(real_unitary, (num_rows,) + tuple(self.kernel_size)) 152 | indep_imag = np.reshape(imag_unitary, (num_rows,) + tuple(self.kernel_size)) 153 | fan_in, fan_out = initializers._compute_fans( 154 | tuple(self.kernel_size) + (int(self.input_dim), self.nb_filters) 155 | ) 156 | else: 157 | indep_real = real_unitary 158 | indep_imag = imag_unitary 159 | fan_in, fan_out = (int(self.input_dim), self.kernel_size[-1]) 160 | 161 | if self.criterion == 'glorot': 162 | desired_var = 1. / (fan_in + fan_out) 163 | elif self.criterion == 'he': 164 | desired_var = 1. / (fan_in) 165 | else: 166 | raise ValueError('Invalid criterion: ' + self.criterion) 167 | 168 | multip_real = np.sqrt(desired_var / np.var(indep_real)) 169 | multip_imag = np.sqrt(desired_var / np.var(indep_imag)) 170 | scaled_real = multip_real * indep_real 171 | scaled_imag = multip_imag * indep_imag 172 | 173 | if self.weight_dim == 2 and self.nb_filters is None: 174 | weight_real = scaled_real 175 | weight_imag = scaled_imag 176 | else: 177 | kernel_shape = tuple(self.kernel_size) + (int(self.input_dim), self.nb_filters) 178 | if self.weight_dim == 1: 179 | transpose_shape = (1, 0) 180 | elif self.weight_dim == 2 and self.nb_filters is not None: 181 | transpose_shape = (1, 2, 0) 182 | elif self.weight_dim == 3 and self.nb_filters is not None: 183 | transpose_shape = (1, 2, 3, 0) 184 | 185 | weight_real = np.transpose(scaled_real, transpose_shape) 186 | weight_imag = np.transpose(scaled_imag, transpose_shape) 187 | weight_real = np.reshape(weight_real, kernel_shape) 188 | weight_imag = np.reshape(weight_imag, kernel_shape) 189 | weight = np.concatenate([weight_real, weight_imag], axis=-1) 190 | 191 | return weight 192 | 193 | def get_config(self): 194 | return {'nb_filters': self.nb_filters, 195 | 'kernel_size': self.kernel_size, 196 | 'input_dim': self.input_dim, 197 | 'weight_dim': self.weight_dim, 198 | 'criterion': self.criterion, 199 | 'seed': self.seed} 200 | 201 | 202 | class ComplexInit(Initializer): 203 | # The standard complex initialization using 204 | # either the He or the Glorot criterion. 205 | def __init__(self, kernel_size, input_dim, 206 | weight_dim, nb_filters=None, 207 | criterion='glorot', seed=None): 208 | 209 | # `weight_dim` is used as a parameter for sanity check 210 | # as we should not pass an integer as kernel_size when 211 | # the weight dimension is >= 2. 212 | # nb_filters == 0 if weights are not convolutional (matrix instead of filters) 213 | # then in such a case, weight_dim = 2. 214 | # (in case of 2D input): 215 | # nb_filters == None and len(kernel_size) == 2 and_weight_dim == 2 216 | # conv1D: len(kernel_size) == 1 and weight_dim == 1 217 | # conv2D: len(kernel_size) == 2 and weight_dim == 2 218 | # conv3d: len(kernel_size) == 3 and weight_dim == 3 219 | 220 | assert len(kernel_size) == weight_dim and weight_dim in {0, 1, 2, 3} 221 | self.nb_filters = nb_filters 222 | self.kernel_size = kernel_size 223 | self.input_dim = input_dim 224 | self.weight_dim = weight_dim 225 | self.criterion = criterion 226 | self.seed = 1337 if seed is None else seed 227 | 228 | def __call__(self, shape, dtype=None): 229 | 230 | if self.nb_filters is not None: 231 | kernel_shape = shape 232 | # kernel_shape = tuple(self.kernel_size) + (int(self.input_dim), 233 | # self.nb_filters) 234 | else: 235 | kernel_shape = (int(self.input_dim), self.kernel_size[-1]) 236 | 237 | fan_in, fan_out = initializers._compute_fans( 238 | # tuple(self.kernel_size) + (self.input_dim, self.nb_filters) 239 | kernel_shape 240 | ) 241 | 242 | if self.criterion == 'glorot': 243 | s = 1. / (fan_in + fan_out) 244 | elif self.criterion == 'he': 245 | # s = 1. / fan_in 246 | s = 1. / (fan_in+.001) 247 | else: 248 | raise ValueError('Invalid criterion: ' + self.criterion) 249 | rng = RandomState(self.seed) 250 | modulus = rng.rayleigh(scale=s, size=kernel_shape) 251 | phase = rng.uniform(low=-np.pi, high=np.pi, size=kernel_shape) 252 | weight_real = modulus * np.cos(phase) 253 | weight_imag = modulus * np.sin(phase) 254 | weight = np.concatenate([weight_real, weight_imag], axis=-1) 255 | 256 | return weight 257 | 258 | 259 | class SqrtInit(Initializer): 260 | def __call__(self, shape, dtype=None): 261 | return K.constant(1 / K.sqrt(2), shape=shape, dtype=dtype) 262 | 263 | 264 | # Aliases: 265 | sqrt_init = SqrtInit 266 | independent_filters = IndependentFilters 267 | complex_init = ComplexInit -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/norm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 5 | # Authors: Chiheb Trabelsi 6 | 7 | # 8 | # Implementation of Layer Normalization and Complex Layer Normalization 9 | # 10 | 11 | import numpy as np 12 | from keras.layers import Layer, InputSpec 13 | from keras import initializers, regularizers, constraints 14 | import keras.backend as K 15 | from .bn import ComplexBN as complex_normalization 16 | from .bn import sqrt_init 17 | 18 | def layernorm(x, axis, epsilon, gamma, beta): 19 | # assert self.built, 'Layer must be built before being called' 20 | input_shape = K.shape(x) 21 | reduction_axes = list(range(K.ndim(x))) 22 | del reduction_axes[axis] 23 | del reduction_axes[0] 24 | broadcast_shape = [1] * K.ndim(x) 25 | broadcast_shape[axis] = input_shape[axis] 26 | broadcast_shape[0] = K.shape(x)[0] 27 | 28 | # Perform normalization: centering and reduction 29 | 30 | mean = K.mean(x, axis=reduction_axes) 31 | broadcast_mean = K.reshape(mean, broadcast_shape) 32 | x_centred = x - broadcast_mean 33 | variance = K.mean(x_centred ** 2, axis=reduction_axes) + epsilon 34 | broadcast_variance = K.reshape(variance, broadcast_shape) 35 | 36 | x_normed = x_centred / K.sqrt(broadcast_variance) 37 | 38 | # Perform scaling and shifting 39 | 40 | broadcast_shape_params = [1] * K.ndim(x) 41 | broadcast_shape_params[axis] = K.shape(x)[axis] 42 | broadcast_gamma = K.reshape(gamma, broadcast_shape_params) 43 | broadcast_beta = K.reshape(beta, broadcast_shape_params) 44 | 45 | x_LN = broadcast_gamma * x_normed + broadcast_beta 46 | 47 | return x_LN 48 | 49 | class LayerNormalization(Layer): 50 | 51 | def __init__(self, 52 | epsilon=1e-4, 53 | axis=-1, 54 | beta_init='zeros', 55 | gamma_init='ones', 56 | gamma_regularizer=None, 57 | beta_regularizer=None, 58 | **kwargs): 59 | 60 | self.supports_masking = True 61 | self.beta_init = initializers.get(beta_init) 62 | self.gamma_init = initializers.get(gamma_init) 63 | self.epsilon = epsilon 64 | self.axis = axis 65 | self.gamma_regularizer = regularizers.get(gamma_regularizer) 66 | self.beta_regularizer = regularizers.get(beta_regularizer) 67 | 68 | super(LayerNormalization, self).__init__(**kwargs) 69 | 70 | def build(self, input_shape): 71 | self.input_spec = InputSpec(ndim=len(input_shape), 72 | axes={self.axis: input_shape[self.axis]}) 73 | shape = (input_shape[self.axis],) 74 | 75 | self.gamma = self.add_weight(shape, 76 | initializer=self.gamma_init, 77 | regularizer=self.gamma_regularizer, 78 | name='{}_gamma'.format(self.name)) 79 | self.beta = self.add_weight(shape, 80 | initializer=self.beta_init, 81 | regularizer=self.beta_regularizer, 82 | name='{}_beta'.format(self.name)) 83 | 84 | self.built = True 85 | 86 | def call(self, x, mask=None): 87 | assert self.built, 'Layer must be built before being called' 88 | return layernorm(x, self.axis, self.epsilon, self.gamma, self.beta) 89 | 90 | def get_config(self): 91 | config = {'epsilon': self.epsilon, 92 | 'axis': self.axis, 93 | 'gamma_regularizer': self.gamma_regularizer.get_config() if self.gamma_regularizer else None, 94 | 'beta_regularizer': self.beta_regularizer.get_config() if self.beta_regularizer else None 95 | } 96 | base_config = super(LayerNormalization, self).get_config() 97 | return dict(list(base_config.items()) + list(config.items())) 98 | 99 | 100 | class ComplexLayerNorm(Layer): 101 | def __init__(self, 102 | epsilon=1e-4, 103 | axis=-1, 104 | center=True, 105 | scale=True, 106 | beta_initializer='zeros', 107 | gamma_diag_initializer=sqrt_init, 108 | gamma_off_initializer='zeros', 109 | beta_regularizer=None, 110 | gamma_diag_regularizer=None, 111 | gamma_off_regularizer=None, 112 | beta_constraint=None, 113 | gamma_diag_constraint=None, 114 | gamma_off_constraint=None, 115 | **kwargs): 116 | 117 | self.supports_masking = True 118 | self.epsilon = epsilon 119 | self.axis = axis 120 | self.center = center 121 | self.scale = scale 122 | self.beta_initializer = initializers.get(beta_initializer) 123 | self.gamma_diag_initializer = initializers.get(gamma_diag_initializer) 124 | self.gamma_off_initializer = initializers.get(gamma_off_initializer) 125 | self.beta_regularizer = regularizers.get(beta_regularizer) 126 | self.gamma_diag_regularizer = regularizers.get(gamma_diag_regularizer) 127 | self.gamma_off_regularizer = regularizers.get(gamma_off_regularizer) 128 | self.beta_constraint = constraints.get(beta_constraint) 129 | self.gamma_diag_constraint = constraints.get(gamma_diag_constraint) 130 | self.gamma_off_constraint = constraints.get(gamma_off_constraint) 131 | super(ComplexLayerNorm, self).__init__(**kwargs) 132 | 133 | def build(self, input_shape): 134 | 135 | ndim = len(input_shape) 136 | dim = input_shape[self.axis] 137 | if dim is None: 138 | raise ValueError('Axis ' + str(self.axis) + ' of ' 139 | 'input tensor should have a defined dimension ' 140 | 'but the layer received an input with shape ' + 141 | str(input_shape) + '.') 142 | self.input_spec = InputSpec(ndim=len(input_shape), 143 | axes={self.axis: dim}) 144 | 145 | gamma_shape = (input_shape[self.axis] // 2,) 146 | if self.scale: 147 | self.gamma_rr = self.add_weight( 148 | shape=gamma_shape, 149 | name='gamma_rr', 150 | initializer=self.gamma_diag_initializer, 151 | regularizer=self.gamma_diag_regularizer, 152 | constraint=self.gamma_diag_constraint 153 | ) 154 | self.gamma_ii = self.add_weight( 155 | shape=gamma_shape, 156 | name='gamma_ii', 157 | initializer=self.gamma_diag_initializer, 158 | regularizer=self.gamma_diag_regularizer, 159 | constraint=self.gamma_diag_constraint 160 | ) 161 | self.gamma_ri = self.add_weight( 162 | shape=gamma_shape, 163 | name='gamma_ri', 164 | initializer=self.gamma_off_initializer, 165 | regularizer=self.gamma_off_regularizer, 166 | constraint=self.gamma_off_constraint 167 | ) 168 | else: 169 | self.gamma_rr = None 170 | self.gamma_ii = None 171 | self.gamma_ri = None 172 | 173 | if self.center: 174 | self.beta = self.add_weight(shape=(input_shape[self.axis],), 175 | name='beta', 176 | initializer=self.beta_initializer, 177 | regularizer=self.beta_regularizer, 178 | constraint=self.beta_constraint) 179 | else: 180 | self.beta = None 181 | 182 | self.built = True 183 | 184 | def call(self, inputs): 185 | input_shape = K.shape(inputs) 186 | ndim = K.ndim(inputs) 187 | reduction_axes = list(range(ndim)) 188 | del reduction_axes[self.axis] 189 | del reduction_axes[0] 190 | input_dim = input_shape[self.axis] // 2 191 | mu = K.mean(inputs, axis=reduction_axes) 192 | broadcast_mu_shape = [1] * ndim 193 | broadcast_mu_shape[self.axis] = input_shape[self.axis] 194 | broadcast_mu_shape[0] = K.shape(inputs)[0] 195 | broadcast_mu = K.reshape(mu, broadcast_mu_shape) 196 | if self.center: 197 | input_centred = inputs - broadcast_mu 198 | else: 199 | input_centred = inputs 200 | centred_squared = input_centred ** 2 201 | if (self.axis == 1 and ndim != 3) or ndim == 2: 202 | centred_squared_real = centred_squared[:, :input_dim] 203 | centred_squared_imag = centred_squared[:, input_dim:] 204 | centred_real = input_centred[:, :input_dim] 205 | centred_imag = input_centred[:, input_dim:] 206 | elif ndim == 3: 207 | centred_squared_real = centred_squared[:, :, :input_dim] 208 | centred_squared_imag = centred_squared[:, :, input_dim:] 209 | centred_real = input_centred[:, :, :input_dim] 210 | centred_imag = input_centred[:, :, input_dim:] 211 | elif self.axis == -1 and ndim == 4: 212 | centred_squared_real = centred_squared[:, :, :, :input_dim] 213 | centred_squared_imag = centred_squared[:, :, :, input_dim:] 214 | centred_real = input_centred[:, :, :, :input_dim] 215 | centred_imag = input_centred[:, :, :, input_dim:] 216 | elif self.axis == -1 and ndim == 5: 217 | centred_squared_real = centred_squared[:, :, :, :, :input_dim] 218 | centred_squared_imag = centred_squared[:, :, :, :, input_dim:] 219 | centred_real = input_centred[:, :, :, :, :input_dim] 220 | centred_imag = input_centred[:, :, :, :, input_dim:] 221 | else: 222 | raise ValueError( 223 | 'Incorrect Layernorm combination of axis and dimensions. axis should be either 1 or -1. ' 224 | 'axis: ' + str(self.axis) + '; ndim: ' + str(ndim) + '.' 225 | ) 226 | if self.scale: 227 | Vrr = K.mean( 228 | centred_squared_real, 229 | axis=reduction_axes 230 | ) + self.epsilon 231 | Vii = K.mean( 232 | centred_squared_imag, 233 | axis=reduction_axes 234 | ) + self.epsilon 235 | # Vri contains the real and imaginary covariance for each feature map. 236 | Vri = K.mean( 237 | centred_real * centred_imag, 238 | axis=reduction_axes, 239 | ) 240 | elif self.center: 241 | Vrr = None 242 | Vii = None 243 | Vri = None 244 | else: 245 | raise ValueError('Error. Both scale and center in batchnorm are set to False.') 246 | 247 | return complex_normalization( 248 | input_centred, Vrr, Vii, Vri, 249 | self.beta, self.gamma_rr, self.gamma_ri, 250 | self.gamma_ii, self.scale, self.center, 251 | layernorm=True, axis=self.axis 252 | ) 253 | 254 | def get_config(self): 255 | config = { 256 | 'axis': self.axis, 257 | 'epsilon': self.epsilon, 258 | 'center': self.center, 259 | 'scale': self.scale, 260 | 'beta_initializer': initializers.serialize(self.beta_initializer), 261 | 'gamma_diag_initializer': initializers.serialize(self.gamma_diag_initializer), 262 | 'gamma_off_initializer': initializers.serialize(self.gamma_off_initializer), 263 | 'beta_regularizer': regularizers.serialize(self.beta_regularizer), 264 | 'gamma_diag_regularizer': regularizers.serialize(self.gamma_diag_regularizer), 265 | 'gamma_off_regularizer': regularizers.serialize(self.gamma_off_regularizer), 266 | 'beta_constraint': constraints.serialize(self.beta_constraint), 267 | 'gamma_diag_constraint': constraints.serialize(self.gamma_diag_constraint), 268 | 'gamma_off_constraint': constraints.serialize(self.gamma_off_constraint), 269 | } 270 | base_config = super(ComplexLayerNorm, self).get_config() 271 | return dict(list(base_config.items()) + list(config.items())) 272 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/pool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Complex valued pooling layer.""" 5 | 6 | # Authors: Qinggang Sun 7 | # 8 | # Reference: 9 | # Authors: Olexa Bilaniuk 10 | # https://github.com/ChihebTrabelsi/deep_complex_networks 11 | # 12 | # Authors: Dramsch Jesper 13 | # https://github.com/zengjie617789/keras-complex 14 | # 15 | 16 | # pylint:disable=too-many-arguments, invalid-name, bad-whitespace, inconsistent-return-statements 17 | # pylint:disable=no-else-return, arguments-differ, redefined-outer-name, abstract-method, c-extension-no-member 18 | 19 | import keras.backend as KB 20 | import keras.engine as KE 21 | import keras.layers as KL 22 | import numpy as np 23 | from .activations import complex_to_real_imag, real_imag_to_complex 24 | 25 | # 26 | # Spectral Pooling Layer 27 | # 28 | 29 | class SpectralPooling1D(KL.Layer): 30 | """SpectralPooling1D""" 31 | def __init__(self, topf=None, gamma=None, **kwargs): 32 | super(SpectralPooling1D, self).__init__(**kwargs) 33 | if topf: 34 | self.topf = (int(topf[0]),) 35 | self.topf = (self.topf[0]//2,) 36 | elif gamma: 37 | self.gamma = (float(gamma[0]),) 38 | self.gamma = (self.gamma[0]/2,) 39 | else: 40 | raise RuntimeError("Must provide either topf= or gamma= !") 41 | 42 | def call(self, x, mask=None): 43 | xshape = KB.int_shape(x) 44 | if hasattr(self, "topf"): 45 | topf = self.topf 46 | else: 47 | if KB.image_data_format() == "channels_first": 48 | topf = (int(self.gamma[0]*xshape[2]),) 49 | else: 50 | topf = (int(self.gamma[0]*xshape[1]),) 51 | 52 | if KB.image_data_format() == "channels_first": 53 | if topf[0] > 0 and xshape[2] >= 2*topf[0]: 54 | mask = [1]*(topf[0] ) +\ 55 | [0]*(xshape[2] - 2*topf[0]) +\ 56 | [1]*(topf[0] ) 57 | mask = [[mask]] 58 | mask = np.asarray(mask, dtype=KB.floatx()).transpose((0,1,2)) 59 | mask = KB.constant(mask) 60 | x *= mask 61 | else: 62 | if topf[0] > 0 and xshape[1] >= 2*topf[0]: 63 | mask = [1]*(topf[0] ) +\ 64 | [0]*(xshape[1] - 2*topf[0]) +\ 65 | [1]*(topf[0] ) 66 | mask = [[mask]] 67 | mask = np.asarray(mask, dtype=KB.floatx()).transpose((0,2,1)) 68 | mask = KB.constant(mask) 69 | x *= mask 70 | 71 | return x 72 | 73 | class SpectralPooling2D(KL.Layer): 74 | """SpectralPooling2D""" 75 | def __init__(self, topf=None, gamma=None, **kwargs): 76 | super(SpectralPooling2D, self).__init__(**kwargs) 77 | if topf: 78 | self.topf = (int (topf[0]), int (topf[1])) 79 | self.topf = (self.topf[0]//2, self.topf[1]//2) 80 | elif gamma: 81 | self.gamma = (float(gamma[0]), float(gamma[1])) 82 | self.gamma = (self.gamma[0]/2, self.gamma[1]/2) 83 | else: 84 | raise RuntimeError("Must provide either topf= or gamma= !") 85 | 86 | def call(self, x, mask=None): 87 | xshape = KB.int_shape(x) 88 | if hasattr(self, "topf"): 89 | topf = self.topf 90 | else: 91 | if KB.image_data_format() == "channels_first": 92 | topf = (int(self.gamma[0]*xshape[2]), int(self.gamma[1]*xshape[3])) 93 | else: 94 | topf = (int(self.gamma[0]*xshape[1]), int(self.gamma[1]*xshape[2])) 95 | 96 | if KB.image_data_format() == "channels_first": 97 | if topf[0] > 0 and xshape[2] >= 2*topf[0]: 98 | mask = [1]*(topf[0] ) +\ 99 | [0]*(xshape[2] - 2*topf[0]) +\ 100 | [1]*(topf[0] ) 101 | mask = [[[mask]]] 102 | mask = np.asarray(mask, dtype=KB.floatx()).transpose((0,1,3,2)) 103 | mask = KB.constant(mask) 104 | x *= mask 105 | if topf[1] > 0 and xshape[3] >= 2*topf[1]: 106 | mask = [1]*(topf[1] ) +\ 107 | [0]*(xshape[3] - 2*topf[1]) +\ 108 | [1]*(topf[1] ) 109 | mask = [[[mask]]] 110 | mask = np.asarray(mask, dtype=KB.floatx()).transpose((0,1,2,3)) 111 | mask = KB.constant(mask) 112 | x *= mask 113 | else: 114 | if topf[0] > 0 and xshape[1] >= 2*topf[0]: 115 | mask = [1]*(topf[0] ) +\ 116 | [0]*(xshape[1] - 2*topf[0]) +\ 117 | [1]*(topf[0] ) 118 | mask = [[[mask]]] 119 | mask = np.asarray(mask, dtype=KB.floatx()).transpose((0,3,1,2)) 120 | mask = KB.constant(mask) 121 | x *= mask 122 | if topf[1] > 0 and xshape[2] >= 2*topf[1]: 123 | mask = [1]*(topf[1] ) +\ 124 | [0]*(xshape[2] - 2*topf[1]) +\ 125 | [1]*(topf[1] ) 126 | mask = [[[mask]]] 127 | mask = np.asarray(mask, dtype=KB.floatx()).transpose((0,1,3,2)) 128 | mask = KB.constant(mask) 129 | x *= mask 130 | 131 | return x 132 | 133 | 134 | class _ComplexPooling(KL.Layer): 135 | """Abstract class for different complex pooling layers. 136 | """ 137 | 138 | def __init__(self, pool_size, strides, padding, data_format, **kwargs): 139 | super(_ComplexPooling, self).__init__(**kwargs) 140 | self.pool_size = pool_size 141 | self.strides = strides 142 | self.padding = padding 143 | self.data_format = data_format 144 | 145 | def _pooling_function(self, inputs, pool_size, strides, 146 | padding, data_format): 147 | raise NotImplementedError 148 | 149 | def call(self, inputs): 150 | output = self._pooling_function(inputs=inputs, 151 | pool_size=self.pool_size, 152 | strides=self.strides, 153 | padding=self.padding, 154 | data_format=self.data_format) 155 | return output 156 | 157 | def get_config(self): 158 | config = {'pool_size': self.pool_size, 159 | 'padding': self.padding, 160 | 'strides': self.strides, 161 | 'data_format': self.data_format} 162 | base_config = super(_ComplexPooling, self).get_config() 163 | return dict(list(base_config.items()) + list(config.items())) 164 | 165 | class _ComplexPooling1D(_ComplexPooling): 166 | """Abstract class for different complex pooling 1D layers. 167 | """ 168 | 169 | def __init__(self, pool_size=2, strides=None, 170 | padding='valid', data_format='channels_last', **kwargs): 171 | super(_ComplexPooling1D, self).__init__(pool_size, strides, padding, 172 | data_format, **kwargs) 173 | if strides is None: 174 | strides = pool_size 175 | self.pool_size = KL.conv_utils.normalize_tuple(pool_size, 1, 'pool_size') 176 | self.strides = KL.conv_utils.normalize_tuple(strides, 1, 'strides') 177 | self.padding = KL.conv_utils.normalize_padding(padding) 178 | self.data_format = KB.normalize_data_format(data_format) 179 | self.input_spec = KE.base_layer.InputSpec(ndim=3) 180 | 181 | def compute_output_shape(self, input_shape): 182 | if self.data_format == 'channels_first': 183 | steps = input_shape[2] 184 | features = input_shape[1] 185 | else: 186 | steps = input_shape[1] 187 | features = input_shape[2] 188 | length = KL.conv_utils.conv_output_length(steps, 189 | self.pool_size[0], 190 | self.padding, 191 | self.strides[0]) 192 | if self.data_format == 'channels_first': 193 | return (input_shape[0], features, length) 194 | else: 195 | return (input_shape[0], length, features) 196 | 197 | def call(self, inputs): 198 | output = self._pooling_function(inputs=inputs, 199 | pool_size=self.pool_size, 200 | strides=self.strides, 201 | padding=self.padding, 202 | data_format=self.data_format) 203 | return output 204 | 205 | def get_config(self): 206 | config = {'pool_size': self.pool_size, 207 | 'padding': self.padding, 208 | 'strides': self.strides, 209 | 'data_format': self.data_format} 210 | base_config = super(_ComplexPooling1D, self).get_config() 211 | return dict(list(base_config.items()) + list(config.items())) 212 | 213 | class _ComplexPooling2D(_ComplexPooling): 214 | """Abstract class for different complex pooling 1D layers. 215 | """ 216 | 217 | def __init__(self, pool_size=(2, 2), strides=None, padding='valid', 218 | data_format=None, **kwargs): 219 | super(_ComplexPooling2D, self).__init__(pool_size, strides, padding, 220 | data_format, **kwargs) 221 | if strides is None: 222 | strides = pool_size 223 | self.pool_size = KL.conv_utils.normalize_tuple(pool_size, 2, 'pool_size') 224 | self.strides = KL.conv_utils.normalize_tuple(strides, 2, 'strides') 225 | self.padding = KL.conv_utils.normalize_padding(padding) 226 | self.data_format = KB.normalize_data_format(data_format) 227 | self.input_spec = KE.base_layer.InputSpec(ndim=4) 228 | 229 | def compute_output_shape(self, input_shape): 230 | if self.data_format == 'channels_first': 231 | rows = input_shape[2] 232 | cols = input_shape[3] 233 | elif self.data_format == 'channels_last': 234 | rows = input_shape[1] 235 | cols = input_shape[2] 236 | rows = KL.conv_utils.conv_output_length(rows, self.pool_size[0], 237 | self.padding, self.strides[0]) 238 | cols = KL.conv_utils.conv_output_length(cols, self.pool_size[1], 239 | self.padding, self.strides[1]) 240 | if self.data_format == 'channels_first': 241 | return (input_shape[0], input_shape[1], rows, cols) 242 | elif self.data_format == 'channels_last': 243 | return (input_shape[0], rows, cols, input_shape[3]) 244 | 245 | def call(self, inputs): 246 | output = self._pooling_function(inputs=inputs, 247 | pool_size=self.pool_size, 248 | strides=self.strides, 249 | padding=self.padding, 250 | data_format=self.data_format) 251 | return output 252 | 253 | def get_config(self): 254 | config = {'pool_size': self.pool_size, 255 | 'padding': self.padding, 256 | 'strides': self.strides, 257 | 'data_format': self.data_format} 258 | base_config = super(_ComplexPooling2D, self).get_config() 259 | return dict(list(base_config.items()) + list(config.items())) 260 | 261 | class _ComplexPooling3D(_ComplexPooling): 262 | """Abstract class for different complex pooling 1D layers. 263 | """ 264 | 265 | def __init__(self, pool_size=(2, 2, 2), strides=None, padding='valid', 266 | data_format=None, **kwargs): 267 | super(_ComplexPooling3D, self).__init__(pool_size, strides, padding, 268 | data_format, **kwargs) 269 | if strides is None: 270 | strides = pool_size 271 | self.pool_size = KL.conv_utils.normalize_tuple(pool_size, 3, 'pool_size') 272 | self.strides = KL.conv_utils.normalize_tuple(strides, 3, 'strides') 273 | self.padding = KL.conv_utils.normalize_padding(padding) 274 | self.data_format = KB.normalize_data_format(data_format) 275 | self.input_spec = KE.base_layer.InputSpec(ndim=5) 276 | 277 | def compute_output_shape(self, input_shape): 278 | if self.data_format == 'channels_first': 279 | len_dim1 = input_shape[2] 280 | len_dim2 = input_shape[3] 281 | len_dim3 = input_shape[4] 282 | elif self.data_format == 'channels_last': 283 | len_dim1 = input_shape[1] 284 | len_dim2 = input_shape[2] 285 | len_dim3 = input_shape[3] 286 | len_dim1 = KL.conv_utils.conv_output_length(len_dim1, self.pool_size[0], 287 | self.padding, self.strides[0]) 288 | len_dim2 = KL.conv_utils.conv_output_length(len_dim2, self.pool_size[1], 289 | self.padding, self.strides[1]) 290 | len_dim3 = KL.conv_utils.conv_output_length(len_dim3, self.pool_size[2], 291 | self.padding, self.strides[2]) 292 | if self.data_format == 'channels_first': 293 | return (input_shape[0], 294 | input_shape[1], 295 | len_dim1, len_dim2, len_dim3) 296 | elif self.data_format == 'channels_last': 297 | return (input_shape[0], 298 | len_dim1, len_dim2, len_dim3, 299 | input_shape[4]) 300 | 301 | def call(self, inputs): 302 | output = self._pooling_function(inputs=inputs, 303 | pool_size=self.pool_size, 304 | strides=self.strides, 305 | padding=self.padding, 306 | data_format=self.data_format) 307 | return output 308 | 309 | def get_config(self): 310 | config = {'pool_size': self.pool_size, 311 | 'padding': self.padding, 312 | 'strides': self.strides, 313 | 'data_format': self.data_format} 314 | base_config = super(_ComplexPooling3D, self).get_config() 315 | return dict(list(base_config.items()) + list(config.items())) 316 | 317 | class ComplexMaxPooling1D(_ComplexPooling1D): 318 | """ComplexMaxPooling1D""" 319 | def __init__(self, pool_size=(2,), strides=(1,), padding='same', data_format='channels_last', **kwargs): 320 | super(ComplexMaxPooling1D, self).__init__(pool_size, strides, padding, 321 | data_format, **kwargs) 322 | 323 | def _pooling_function(self, inputs, pool_size, strides, 324 | padding, data_format): 325 | input_real, input_imag = complex_to_real_imag(inputs) 326 | real_outputs = KL.MaxPooling1D(pool_size, strides, padding)(input_real) 327 | imag_outputs = KL.MaxPooling1D(pool_size, strides, padding)(input_imag) 328 | outputs = real_imag_to_complex(real_outputs, imag_outputs) 329 | return outputs 330 | 331 | class ComplexMaxPooling2D(_ComplexPooling2D): 332 | """ComplexMaxPooling2D""" 333 | def __init__(self, pool_size=(2, 2), strides=(1, 1), padding='same', data_format='channels_last', **kwargs): 334 | super(ComplexMaxPooling2D, self).__init__(pool_size, strides, padding, 335 | data_format, **kwargs) 336 | 337 | def _pooling_function(self, inputs, pool_size, strides, 338 | padding, data_format): 339 | input_real, input_imag = complex_to_real_imag(inputs) 340 | real_outputs = KL.MaxPooling2D(pool_size, strides, padding)(input_real) 341 | imag_outputs = KL.MaxPooling2D(pool_size, strides, padding)(input_imag) 342 | outputs = real_imag_to_complex(real_outputs, imag_outputs) 343 | return outputs 344 | 345 | class ComplexMaxPooling3D(_ComplexPooling3D): 346 | """ComplexMaxPooling3D""" 347 | def __init__(self, pool_size=(2, 2, 2), strides=(1, 1, 1), padding='same', data_format='channels_last', **kwargs): 348 | super(ComplexMaxPooling3D, self).__init__(pool_size, strides, padding, 349 | data_format, **kwargs) 350 | 351 | def _pooling_function(self, inputs, pool_size, strides, 352 | padding, data_format): 353 | input_real, input_imag = complex_to_real_imag(inputs) 354 | real_outputs = KL.MaxPooling3D(pool_size, strides, padding)(input_real) 355 | imag_outputs = KL.MaxPooling3D(pool_size, strides, padding)(input_imag) 356 | outputs = real_imag_to_complex(real_outputs, imag_outputs) 357 | return outputs 358 | 359 | class ComplexAveragePooling1D(_ComplexPooling1D): 360 | """ComplexAveragePooling1D""" 361 | def __init__(self, pool_size=(2,), strides=(1,), padding='same', data_format='channels_last', **kwargs): 362 | super(ComplexAveragePooling1D, self).__init__(pool_size, strides, padding, 363 | data_format, **kwargs) 364 | 365 | def _pooling_function(self, inputs, pool_size, strides, 366 | padding, data_format): 367 | input_real, input_imag = complex_to_real_imag(inputs) 368 | real_outputs = KL.AveragePooling1D(pool_size, strides, padding)(input_real) 369 | imag_outputs = KL.AveragePooling1D(pool_size, strides, padding)(input_imag) 370 | outputs = real_imag_to_complex(real_outputs, imag_outputs) 371 | return outputs 372 | 373 | class ComplexAveragePooling2D(_ComplexPooling2D): 374 | """ComplexAveragePooling2D""" 375 | def __init__(self, pool_size=(2, 2), strides=(1, 1), padding='same', data_format='channels_last', **kwargs): 376 | super(ComplexAveragePooling2D, self).__init__(pool_size, strides, padding, 377 | data_format, **kwargs) 378 | 379 | def _pooling_function(self, inputs, pool_size, strides, 380 | padding, data_format): 381 | input_real, input_imag = complex_to_real_imag(inputs) 382 | real_outputs = KL.AveragePooling2D(pool_size, strides, padding)(input_real) 383 | imag_outputs = KL.AveragePooling2D(pool_size, strides, padding)(input_imag) 384 | outputs = real_imag_to_complex(real_outputs, imag_outputs) 385 | return outputs 386 | 387 | class ComplexAveragePooling3D(_ComplexPooling3D): 388 | """ComplexAveragePooling3D""" 389 | def __init__(self, pool_size=(2, 2, 2), strides=(1, 1, 1), padding='same', data_format='channels_last', **kwargs): 390 | super(ComplexAveragePooling3D, self).__init__(pool_size, strides, padding, 391 | data_format, **kwargs) 392 | 393 | def _pooling_function(self, inputs, pool_size, strides, 394 | padding, data_format): 395 | input_real, input_imag = complex_to_real_imag(inputs) 396 | real_outputs = KL.AveragePooling3D(pool_size, strides, padding)(input_real) 397 | imag_outputs = KL.AveragePooling3D(pool_size, strides, padding)(input_imag) 398 | outputs = real_imag_to_complex(real_outputs, imag_outputs) 399 | return outputs 400 | 401 | if __name__ == "__main__": 402 | import __main__ as SP 403 | # inputs = KL.Input(shape=(128, 128, 2)) 404 | # x = SP.ComplexMaxPooling2D()(inputs) 405 | # print(x) 406 | # print('x.int_shape', KB.int_shape(x)) 407 | # 408 | # inputs = KL.Input(shape=(128, 128, 2)) 409 | # x = SP.ComplexMaxPooling2D(pool_size=(2, 2), strides=(2, 2))(inputs) 410 | # print(x) 411 | # print('x.int_shape', KB.int_shape(x)) 412 | 413 | 414 | inputs = KL.Input(shape=(128, 128, 2)) 415 | x = SP.SpectralPooling2D(gamma=[0.15, 0.15])(inputs) 416 | print(x) 417 | print('x.int_shape', KB.int_shape(x)) 418 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/resnet_models_1d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular one-dimensional complex valued ResNet models.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Allen Goodman, Allen Goodman, Claire McQuin, Hans Gaiser, et al. keras-resnet 9 | # https://github.com/broadinstitute/keras-resnet 10 | 11 | # pylint:disable=dangerous-default-value, keyword-arg-before-vararg, too-many-arguments, too-many-locals 12 | # pylint:disable=invalid-name, too-many-branches 13 | 14 | import keras 15 | import keras.backend as K 16 | from ..layers.activations import layer_activation 17 | from ..layers.conv import ComplexConv1D 18 | from ..layers.dense import ComplexDense 19 | from ..layers.bn import ComplexBatchNormalization 20 | from ..layers.pool import SpectralPooling1D, ComplexMaxPooling1D, ComplexAveragePooling1D 21 | from .resnet_blocks_1d import basic_1d, bottleneck_1d 22 | 23 | class ResNet1D(keras.Model): 24 | """ 25 | Constructs a `keras.models.Model` object using the given block count. 26 | 27 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 28 | 29 | :param num_blocks: list, number of residual blocks 30 | 31 | :param block_func: method, the network’s residual architecture 32 | 33 | :param conv_activation: str, the activation of convolution layer in residual blocks 34 | 35 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 36 | 37 | :param pooling_func: list, the type of pooling layers in network 38 | 39 | :param include_top: bool, if true, includes classification layers 40 | 41 | :param classes: int, number of classes to classify (include_top must be true) 42 | 43 | :param output_activation: int, activation of the output Dense layer of the classifer 44 | 45 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 46 | output (if `include_top=True`) 47 | 48 | Usage: 49 | >>> from .resnet_blocks_1d import block_func 50 | >>> from keras.layers import Input 51 | >>> num_blocks = [2, 2, 2, 2] 52 | >>> block_func = block_func 53 | >>> id1, id2, od = 128, 128, 3 54 | >>> inputs = Input(shape=(id1, id2, 2)) 55 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import ResNet1D 56 | >>> model = ResNet1D(inputs, classes=od, 57 | pooling_func=['max','global_average'], 58 | output_activation='sigmoid') 59 | >>> print(model.summary()) 60 | """ 61 | 62 | 63 | def __init__(self, 64 | inputs, 65 | num_blocks, 66 | block_func, 67 | activation='crelu', 68 | n_filters=64, 69 | pooling_func=['max', 'global_average'], 70 | include_top=True, 71 | classes=1000, 72 | numerical_names=None, 73 | output_activation=None, 74 | *args, 75 | **kwargs 76 | ): 77 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 78 | 79 | if numerical_names is None: 80 | numerical_names = [True] * len(num_blocks) 81 | 82 | x = keras.layers.ZeroPadding1D(padding=3, name="padding_conv1")(inputs) 83 | 84 | x_complex = ComplexConv1D(n_filters, 7, strides=2, padding='same', use_bias=False, 85 | spectral_parametrization=False, name='conv1')(inputs) 86 | 87 | x_complex = ComplexBatchNormalization(axis=axis, epsilon=1e-5, name='bn_conv1')(x_complex) 88 | 89 | x_complex = layer_activation(x_complex, activation, name=f'conv1_{activation}') 90 | 91 | if pooling_func[0] == 'max': 92 | x_complex = ComplexMaxPooling1D(pool_size=3, strides=2, padding='same', name='pool1')(x_complex) 93 | elif pooling_func[0] == 'average': 94 | x_complex = ComplexAveragePooling1D(pool_size=3, strides=2, padding='same', name='pool1')(x_complex) 95 | 96 | outputs = [] 97 | 98 | for stage_id, iterations in enumerate(num_blocks): 99 | for block_id in range(iterations): 100 | x_complex = block_func( 101 | n_filters, 102 | stage_id, 103 | block_id, 104 | numerical_name=(block_id > 0 and numerical_names[stage_id]), 105 | activation=activation 106 | )(x_complex) 107 | 108 | n_filters *= 2 109 | 110 | outputs.append(x_complex) 111 | 112 | if include_top: 113 | assert classes > 0 114 | if pooling_func[1] == 'global_average': 115 | x_complex = keras.layers.GlobalAveragePooling1D(name="pool5")(x_complex) 116 | elif pooling_func[1] == 'complex_average': 117 | x_complex = ComplexAveragePooling1D(name='pool5')(x_complex) 118 | elif pooling_func[1] == 'complex_max': 119 | x_complex = ComplexMaxPooling1D(name='pool5')(x_complex) 120 | elif pooling_func[1] == 'spectral_average': 121 | x_complex = SpectralPooling1D(gamma=[0.25, 0.25], name='pool5')(x_complex) 122 | 123 | if output_activation is None: 124 | output_activation = 'softmax' 125 | 126 | if K.ndim(x_complex) > 2: 127 | x_complex = keras.layers.Flatten()(x_complex) 128 | 129 | if output_activation.startswith('complex_'): 130 | output_activation = output_activation[len('complex_'):] 131 | x = ComplexDense(classes, activation=output_activation, name=f'fc{classes}')(x_complex) 132 | else: 133 | x = keras.layers.Dense(classes, activation=output_activation, name=f'fc{classes}')(x_complex) 134 | 135 | super(ResNet1D, self).__init__(inputs=inputs, outputs=x, *args, **kwargs) 136 | else: 137 | # Else output each stages features 138 | super(ResNet1D, self).__init__(inputs=inputs, outputs=outputs, *args, **kwargs) 139 | 140 | class ResNet1D18(ResNet1D): 141 | """ 142 | Constructs a `keras.models.Model` according to the ResNet18 specifications. 143 | 144 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 145 | 146 | :param num_blocks: list, number of residual blocks 147 | 148 | :param block_func: method, the network’s residual architecture 149 | 150 | :param conv_activation: str, the activation of convolution layer in residual blocks 151 | 152 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 153 | 154 | :param pooling_func: list, the type of pooling layers in network 155 | 156 | :param include_top: bool, if true, includes classification layers 157 | 158 | :param classes: int, number of classes to classify (include_top must be true) 159 | 160 | :param output_activation: int, activation of the output Dense layer of the classifer 161 | 162 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 163 | output (if `include_top=True`) 164 | 165 | Usage: 166 | 167 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import ResNet1D18 168 | >>> from keras.layers import Input 169 | >>> id1, id2, od = 128, 128, 3 170 | >>> inputs = Input(shape=(id1, id2, 2)) 171 | >>> model_resnet = ResNet1D18(inputs, classes=od, 172 | pooling_func=['max','global_average'], 173 | output_activation='sigmoid') 174 | >>> print(model_resnet.summary()) 175 | """ 176 | 177 | 178 | def __init__(self, 179 | inputs, 180 | num_blocks=None, 181 | block_func=basic_1d, 182 | conv_activation='crelu', 183 | n_filters=64, 184 | pooling_func=['max', 'global_average'], 185 | include_top=True, 186 | classes=1000, 187 | numerical_names=None, 188 | output_activation='sigmoid', 189 | *args, 190 | **kwargs 191 | ): 192 | 193 | if num_blocks is None: 194 | num_blocks = [2, 2, 2, 2] 195 | 196 | super(ResNet1D18, self).__init__( 197 | inputs, 198 | num_blocks, 199 | block_func, 200 | conv_activation, 201 | n_filters, 202 | pooling_func, 203 | include_top, 204 | classes, 205 | numerical_names, 206 | output_activation, 207 | *args, 208 | **kwargs 209 | ) 210 | 211 | class ResNet1D34(ResNet1D): 212 | """ 213 | Constructs a `keras.models.Model` according to the ResNet34 specifications. 214 | 215 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 216 | 217 | :param num_blocks: list, number of residual blocks 218 | 219 | :param block_func: method, the network’s residual architecture 220 | 221 | :param conv_activation: str, the activation of convolution layer in residual blocks 222 | 223 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 224 | 225 | :param pooling_func: list, the type of pooling layers in network 226 | 227 | :param include_top: bool, if true, includes classification layers 228 | 229 | :param classes: int, number of classes to classify (include_top must be true) 230 | 231 | :param output_activation: int, activation of the output Dense layer of the classifer 232 | 233 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 234 | output (if `include_top=True`) 235 | 236 | Usage: 237 | 238 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import ResNet1D34 239 | >>> from keras.layers import Input 240 | >>> id1, id2, od = 128, 128, 3 241 | >>> inputs = Input(shape=(id1, id2, 2)) 242 | >>> model_resnet = ResNet1D34(inputs, classes=od, 243 | pooling_func=['max','global_average'], 244 | output_activation='sigmoid') 245 | >>> print(model_resnet.summary()) 246 | """ 247 | 248 | 249 | def __init__(self, 250 | inputs, 251 | num_blocks=None, 252 | block_func=basic_1d, 253 | conv_activation='crelu', 254 | n_filters=64, 255 | pooling_func=['max', 'global_average'], 256 | include_top=True, 257 | classes=1000, 258 | numerical_names=None, 259 | output_activation='sigmoid', 260 | *args, 261 | **kwargs 262 | ): 263 | 264 | if num_blocks is None: 265 | num_blocks = [3, 4, 6, 3] 266 | 267 | super(ResNet1D34, self).__init__( 268 | inputs, 269 | num_blocks, 270 | block_func, 271 | conv_activation, 272 | n_filters, 273 | pooling_func, 274 | include_top, 275 | classes, 276 | numerical_names, 277 | output_activation, 278 | *args, 279 | **kwargs 280 | ) 281 | 282 | class ResNet1D50(ResNet1D): 283 | """ 284 | Constructs a `keras.models.Model` according to the ResNet50 specifications. 285 | 286 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 287 | 288 | :param num_blocks: list, number of residual blocks 289 | 290 | :param block_func: method, the network’s residual architecture 291 | 292 | :param conv_activation: str, the activation of convolution layer in residual blocks 293 | 294 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 295 | 296 | :param pooling_func: list, the type of pooling layers in network 297 | 298 | :param include_top: bool, if true, includes classification layers 299 | 300 | :param classes: int, number of classes to classify (include_top must be true) 301 | 302 | :param output_activation: int, activation of the output Dense layer of the classifer 303 | 304 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 305 | output (if `include_top=True`) 306 | 307 | Usage: 308 | 309 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import ResNet1D50 310 | >>> from keras.layers import Input 311 | >>> id1, id2, od = 128, 128, 3 312 | >>> inputs = Input(shape=(id1, id2, 2)) 313 | >>> model_resnet = ResNet1D50(inputs, classes=od, 314 | pooling_func=['max','global_average'], 315 | output_activation='sigmoid') 316 | >>> print(model_resnet.summary()) 317 | """ 318 | 319 | 320 | def __init__(self, 321 | inputs, 322 | num_blocks=None, 323 | block_func=bottleneck_1d, 324 | conv_activation='crelu', 325 | n_filters=64, 326 | pooling_func=['max', 'global_average'], 327 | include_top=True, 328 | classes=1000, 329 | numerical_names=None, 330 | output_activation='sigmoid', 331 | *args, 332 | **kwargs 333 | ): 334 | 335 | if num_blocks is None: 336 | num_blocks = [3, 4, 6, 3] 337 | 338 | numerical_names = [False, False, False, False] 339 | 340 | super(ResNet1D50, self).__init__( 341 | inputs, 342 | num_blocks, 343 | block_func, 344 | conv_activation, 345 | n_filters, 346 | pooling_func, 347 | include_top, 348 | classes, 349 | numerical_names, 350 | output_activation, 351 | *args, 352 | **kwargs 353 | ) 354 | 355 | class ResNet1D101(ResNet1D): 356 | """ 357 | Constructs a `keras.models.Model` according to the ResNet101 specifications. 358 | 359 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 360 | 361 | :param num_blocks: list, number of residual blocks 362 | 363 | :param block_func: method, the network’s residual architecture 364 | 365 | :param conv_activation: str, the activation of convolution layer in residual blocks 366 | 367 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 368 | 369 | :param pooling_func: list, the type of pooling layers in network 370 | 371 | :param include_top: bool, if true, includes classification layers 372 | 373 | :param classes: int, number of classes to classify (include_top must be true) 374 | 375 | :param output_activation: int, activation of the output Dense layer of the classifer 376 | 377 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 378 | output (if `include_top=True`) 379 | 380 | Usage: 381 | 382 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import ResNet1D101 383 | >>> from keras.layers import Input 384 | >>> id1, id2, od = 128, 128, 3 385 | >>> inputs = Input(shape=(id1, id2, 2)) 386 | >>> model_resnet = ResNet1D101(inputs, classes=od, 387 | pooling_func=['max','global_average'], 388 | output_activation='sigmoid') 389 | >>> print(model_resnet.summary()) 390 | """ 391 | 392 | 393 | def __init__( 394 | self, 395 | inputs, 396 | num_blocks=None, 397 | block_func=bottleneck_1d, 398 | conv_activation='crelu', 399 | n_filters=64, 400 | pooling_func=['max', 'global_average'], 401 | include_top=True, 402 | classes=1000, 403 | numerical_names=None, 404 | output_activation='sigmoid', 405 | *args, 406 | **kwargs): 407 | 408 | if num_blocks is None: 409 | num_blocks = [3, 4, 23, 3] 410 | 411 | numerical_names = [False, True, True, False] 412 | 413 | super(ResNet1D101, self).__init__( 414 | inputs, 415 | num_blocks, 416 | block_func, 417 | conv_activation, 418 | n_filters, 419 | pooling_func, 420 | include_top, 421 | classes, 422 | numerical_names, 423 | output_activation, 424 | *args, 425 | **kwargs 426 | ) 427 | 428 | class ResNet1D152(ResNet1D): 429 | """ 430 | Constructs a `keras.models.Model` according to the ResNet152 specifications. 431 | 432 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 433 | 434 | :param num_blocks: list, number of residual blocks 435 | 436 | :param block_func: method, the network’s residual architecture 437 | 438 | :param conv_activation: str, the activation of convolution layer in residual blocks 439 | 440 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 441 | 442 | :param pooling_func: list, the type of pooling layers in network 443 | 444 | :param include_top: bool, if true, includes classification layers 445 | 446 | :param classes: int, number of classes to classify (include_top must be true) 447 | 448 | :param output_activation: int, activation of the output Dense layer of the classifer 449 | 450 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 451 | output (if `include_top=True`) 452 | 453 | Usage: 454 | 455 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import ResNet1D152 456 | >>> from keras.layers import Input 457 | >>> id1, id2, od = 128, 128, 3 458 | >>> inputs = Input(shape=(id1, id2, 2)) 459 | >>> model_resnet = ResNet1D152(inputs, classes=od, 460 | pooling_func=['max','global_average'], 461 | output_activation='sigmoid') 462 | >>> print(model_resnet.summary()) 463 | """ 464 | 465 | 466 | def __init__( 467 | self, 468 | inputs, 469 | num_blocks=None, 470 | block_func=bottleneck_1d, 471 | conv_activation='crelu', 472 | n_filters=64, 473 | pooling_func=['max', 'global_average'], 474 | include_top=True, 475 | classes=1000, 476 | numerical_names=None, 477 | output_activation='sigmoid', 478 | *args, 479 | **kwargs): 480 | 481 | if num_blocks is None: 482 | num_blocks = [3, 8, 36, 3] 483 | 484 | numerical_names = [False, True, True, False] 485 | 486 | super(ResNet1D152, self).__init__( 487 | inputs, 488 | num_blocks, 489 | block_func, 490 | conv_activation, 491 | n_filters, 492 | pooling_func, 493 | include_top, 494 | classes, 495 | numerical_names, 496 | output_activation, 497 | *args, 498 | **kwargs 499 | ) 500 | 501 | class ResNet1D200(ResNet1D): 502 | """ 503 | Constructs a `keras.models.Model` according to the ResNet200 specifications. 504 | 505 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 506 | 507 | :param num_blocks: list, number of residual blocks 508 | 509 | :param block_func: method, the network’s residual architecture 510 | 511 | :param conv_activation: str, the activation of convolution layer in residual blocks 512 | 513 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 514 | 515 | :param pooling_func: list, the type of pooling layers in network 516 | 517 | :param include_top: bool, if true, includes classification layers 518 | 519 | :param classes: int, number of classes to classify (include_top must be true) 520 | 521 | :param output_activation: int, activation of the output Dense layer of the classifer 522 | 523 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 524 | output (if `include_top=True`) 525 | 526 | Usage: 527 | 528 | >>> from complex_networks_keras_tf1.models.resnet_models_1d import ResNet1D200 529 | >>> from keras.layers import Input 530 | >>> id1, id2, od = 128, 128, 3 531 | >>> inputs = Input(shape=(id1, id2, 2)) 532 | >>> model_resnet = ResNet1D200(inputs, classes=od, 533 | pooling_func=['max','global_average'], 534 | output_activation='sigmoid') 535 | >>> print(model_resnet.summary()) 536 | """ 537 | 538 | 539 | def __init__( 540 | self, 541 | inputs, 542 | num_blocks=None, 543 | block_func=bottleneck_1d, 544 | conv_activation='crelu', 545 | n_filters=64, 546 | pooling_func=['max', 'global_average'], 547 | include_top=True, 548 | classes=1000, 549 | numerical_names=None, 550 | output_activation='sigmoid', 551 | *args, 552 | **kwargs): 553 | 554 | if num_blocks is None: 555 | num_blocks = [3, 24, 36, 3] 556 | 557 | numerical_names = [False, True, True, False] 558 | 559 | super(ResNet1D200, self).__init__( 560 | inputs, 561 | num_blocks, 562 | block_func, 563 | conv_activation, 564 | n_filters, 565 | pooling_func, 566 | include_top, 567 | classes, 568 | numerical_names, 569 | output_activation, 570 | *args, 571 | **kwargs 572 | ) 573 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/resnet_models_2d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular one-dimensional complex valued ResNet models.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Allen Goodman, Allen Goodman, Claire McQuin, Hans Gaiser, et al. keras-resnet 9 | # https://github.com/broadinstitute/keras-resnet 10 | 11 | # pylint:disable=dangerous-default-value, keyword-arg-before-vararg, too-many-arguments, too-many-locals 12 | # pylint:disable=invalid-name, too-many-branches 13 | 14 | import keras 15 | import keras.backend as K 16 | from ..layers.activations import layer_activation 17 | from ..layers.conv import ComplexConv2D 18 | from ..layers.dense import ComplexDense 19 | from ..layers.bn import ComplexBatchNormalization 20 | from ..layers.pool import SpectralPooling2D, ComplexMaxPooling2D, ComplexAveragePooling2D 21 | from .resnet_blocks_2d import basic_2d, bottleneck_2d 22 | 23 | class ResNet2D(keras.Model): 24 | """ 25 | Constructs a `keras.models.Model` object using the given block count. 26 | 27 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 28 | 29 | :param num_blocks: list, number of residual blocks 30 | 31 | :param block_func: method, the network’s residual architecture 32 | 33 | :param conv_activation: str, the activation of convolution layer in residual blocks 34 | 35 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 36 | 37 | :param pooling_func: list, the type of pooling layers in network 38 | 39 | :param include_top: bool, if true, includes classification layers 40 | 41 | :param classes: int, number of classes to classify (include_top must be true) 42 | 43 | :param output_activation: int, activation of the output Dense layer of the classifer 44 | 45 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 46 | output (if `include_top=True`) 47 | 48 | Usage: 49 | >>> from .resnet_blocks_2d import block_func 50 | >>> from keras.layers import Input 51 | >>> num_blocks = [2, 2, 2, 2] 52 | >>> block_func = block_func 53 | >>> id1, id2, od = 128, 128, 3 54 | >>> inputs = Input(shape=(id1, id2, 2)) 55 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import ResNet2D 56 | >>> model = ResNet2D(inputs, classes=od, 57 | pooling_func=['max','global_average'], 58 | output_activation='sigmoid') 59 | >>> print(model.summary()) 60 | """ 61 | def __init__(self, 62 | inputs, 63 | num_blocks, 64 | block_func, 65 | activation='crelu', 66 | n_filters=64, 67 | pooling_func=['max', 'global_average'], 68 | include_top=True, 69 | classes=1000, 70 | numerical_names=None, 71 | output_activation=None, 72 | *args, 73 | **kwargs 74 | ): 75 | axis = -1 if keras.backend.image_data_format() == "channels_last" else 1 76 | 77 | if numerical_names is None: 78 | numerical_names = [True] * len(num_blocks) 79 | 80 | x_complex = ComplexConv2D(n_filters, 7, strides=(2, 2), padding='same', use_bias=False, 81 | spectral_parametrization=False, name='conv1')(inputs) 82 | 83 | x_complex = ComplexBatchNormalization(axis=axis, epsilon=1e-5, name='bn_conv1')(x_complex) 84 | 85 | x_complex = layer_activation(x_complex, activation, name=f'conv1_{activation}') 86 | 87 | if pooling_func[0] == 'max': 88 | x_complex = ComplexMaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding='same', name='pool1')(x_complex) 89 | elif pooling_func[0] == 'average': 90 | x_complex = ComplexAveragePooling2D(pool_size=(3, 3), strides=(2, 2), padding='same', 91 | name='pool1')(x_complex) 92 | 93 | outputs = [] 94 | 95 | for stage_id, iterations in enumerate(num_blocks): 96 | for block_id in range(iterations): 97 | x_complex = block_func( 98 | n_filters, 99 | stage_id, 100 | block_id, 101 | numerical_name=(block_id > 0 and numerical_names[stage_id]), 102 | activation=activation 103 | )(x_complex) 104 | 105 | n_filters *= 2 106 | 107 | outputs.append(x_complex) 108 | 109 | if include_top: 110 | assert classes > 0 111 | if pooling_func[1] == 'global_average': 112 | x_complex = keras.layers.GlobalAveragePooling2D(name="pool5")(x_complex) 113 | elif pooling_func[1] == 'complex_average': 114 | x_complex = ComplexAveragePooling2D(name='pool5')(x_complex) 115 | elif pooling_func[1] == 'complex_max': 116 | x_complex = ComplexMaxPooling2D(name='pool5')(x_complex) 117 | elif pooling_func[1] == 'spectral_average': 118 | x_complex = SpectralPooling2D(gamma=[0.25, 0.25], name='pool5')(x_complex) 119 | 120 | if output_activation is None: 121 | output_activation = 'softmax' 122 | 123 | if K.ndim(x_complex) > 2: 124 | x_complex = keras.layers.Flatten()(x_complex) 125 | 126 | if output_activation.startswith('complex_'): 127 | output_activation = output_activation[len('complex_'):] 128 | x = ComplexDense(classes, activation=output_activation, name=f'fc{classes}')(x_complex) 129 | else: 130 | x = keras.layers.Dense(classes, activation=output_activation, name=f'fc{classes}')(x_complex) 131 | 132 | super(ResNet2D, self).__init__(inputs=inputs, outputs=x, *args, **kwargs) 133 | else: 134 | # Else output each stages features 135 | super(ResNet2D, self).__init__(inputs=inputs, outputs=outputs, *args, **kwargs) 136 | 137 | class ResNet2D18(ResNet2D): 138 | """ 139 | Constructs a `keras.models.Model` according to the ResNet18 specifications. 140 | 141 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 142 | 143 | :param num_blocks: list, number of residual blocks 144 | 145 | :param block_func: method, the network’s residual architecture 146 | 147 | :param conv_activation: str, the activation of convolution layer in residual blocks 148 | 149 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 150 | 151 | :param pooling_func: list, the type of pooling layers in network 152 | 153 | :param include_top: bool, if true, includes classification layers 154 | 155 | :param classes: int, number of classes to classify (include_top must be true) 156 | 157 | :param output_activation: int, activation of the output Dense layer of the classifer 158 | 159 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 160 | output (if `include_top=True`) 161 | 162 | Usage: 163 | 164 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import ResNet2D18 165 | >>> from keras.layers import Input 166 | >>> id1, id2, od = 128, 128, 3 167 | >>> inputs = Input(shape=(id1, id2, 2)) 168 | >>> model_resnet = ResNet2D18(inputs, classes=od, 169 | pooling_func=['max','global_average'], 170 | output_activation='sigmoid') 171 | >>> print(model_resnet.summary()) 172 | """ 173 | 174 | 175 | def __init__(self, 176 | inputs, 177 | num_blocks=None, 178 | block_func=basic_2d, 179 | conv_activation='crelu', 180 | n_filters=64, 181 | pooling_func=['max', 'global_average'], 182 | include_top=True, 183 | classes=1000, 184 | numerical_names=None, 185 | output_activation='sigmoid', 186 | *args, 187 | **kwargs 188 | ): 189 | 190 | if num_blocks is None: 191 | num_blocks = [2, 2, 2, 2] 192 | 193 | super(ResNet2D18, self).__init__( 194 | inputs, 195 | num_blocks, 196 | block_func, 197 | conv_activation, 198 | n_filters, 199 | pooling_func, 200 | include_top, 201 | classes, 202 | numerical_names, 203 | output_activation, 204 | *args, 205 | **kwargs 206 | ) 207 | 208 | class ResNet2D34(ResNet2D): 209 | """ 210 | Constructs a `keras.models.Model` according to the ResNet34 specifications. 211 | 212 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 213 | 214 | :param num_blocks: list, number of residual blocks 215 | 216 | :param block_func: method, the network’s residual architecture 217 | 218 | :param conv_activation: str, the activation of convolution layer in residual blocks 219 | 220 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 221 | 222 | :param pooling_func: list, the type of pooling layers in network 223 | 224 | :param include_top: bool, if true, includes classification layers 225 | 226 | :param classes: int, number of classes to classify (include_top must be true) 227 | 228 | :param output_activation: int, activation of the output Dense layer of the classifer 229 | 230 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 231 | output (if `include_top=True`) 232 | 233 | Usage: 234 | 235 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import ResNet2D34 236 | >>> from keras.layers import Input 237 | >>> id1, id2, od = 128, 128, 3 238 | >>> inputs = Input(shape=(id1, id2, 2)) 239 | >>> model_resnet = ResNet2D34(inputs, classes=od, 240 | pooling_func=['max','global_average'], 241 | output_activation='sigmoid') 242 | >>> print(model_resnet.summary()) 243 | """ 244 | 245 | 246 | def __init__(self, 247 | inputs, 248 | num_blocks=None, 249 | block_func=basic_2d, 250 | conv_activation='crelu', 251 | n_filters=64, 252 | pooling_func=['max', 'global_average'], 253 | include_top=True, 254 | classes=1000, 255 | numerical_names=None, 256 | output_activation='sigmoid', 257 | *args, 258 | **kwargs 259 | ): 260 | 261 | if num_blocks is None: 262 | num_blocks = [3, 4, 6, 3] 263 | 264 | super(ResNet2D34, self).__init__( 265 | inputs, 266 | num_blocks, 267 | block_func, 268 | conv_activation, 269 | n_filters, 270 | pooling_func, 271 | include_top, 272 | classes, 273 | numerical_names, 274 | output_activation, 275 | *args, 276 | **kwargs 277 | ) 278 | 279 | class ResNet2D50(ResNet2D): 280 | """ 281 | Constructs a `keras.models.Model` according to the ResNet50 specifications. 282 | 283 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 284 | 285 | :param num_blocks: list, number of residual blocks 286 | 287 | :param block_func: method, the network’s residual architecture 288 | 289 | :param conv_activation: str, the activation of convolution layer in residual blocks 290 | 291 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 292 | 293 | :param pooling_func: list, the type of pooling layers in network 294 | 295 | :param include_top: bool, if true, includes classification layers 296 | 297 | :param classes: int, number of classes to classify (include_top must be true) 298 | 299 | :param output_activation: int, activation of the output Dense layer of the classifer 300 | 301 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 302 | output (if `include_top=True`) 303 | 304 | Usage: 305 | 306 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import ResNet2D50 307 | >>> from keras.layers import Input 308 | >>> id1, id2, od = 128, 128, 3 309 | >>> inputs = Input(shape=(id1, id2, 2)) 310 | >>> model_resnet = ResNet2D50(inputs, classes=od, 311 | pooling_func=['max','global_average'], 312 | output_activation='sigmoid') 313 | >>> print(model_resnet.summary()) 314 | """ 315 | 316 | 317 | def __init__(self, 318 | inputs, 319 | num_blocks=None, 320 | block_func=bottleneck_2d, 321 | conv_activation='crelu', 322 | n_filters=64, 323 | pooling_func=['max', 'global_average'], 324 | include_top=True, 325 | classes=1000, 326 | numerical_names=None, 327 | output_activation='sigmoid', 328 | *args, 329 | **kwargs 330 | ): 331 | 332 | if num_blocks is None: 333 | num_blocks = [3, 4, 6, 3] 334 | 335 | numerical_names = [False, False, False, False] 336 | 337 | super(ResNet2D50, self).__init__( 338 | inputs, 339 | num_blocks, 340 | block_func, 341 | conv_activation, 342 | n_filters, 343 | pooling_func, 344 | include_top, 345 | classes, 346 | numerical_names, 347 | output_activation, 348 | *args, 349 | **kwargs 350 | ) 351 | 352 | class ResNet2D101(ResNet2D): 353 | """ 354 | Constructs a `keras.models.Model` according to the ResNet101 specifications. 355 | 356 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 357 | 358 | :param num_blocks: list, number of residual blocks 359 | 360 | :param block_func: method, the network’s residual architecture 361 | 362 | :param conv_activation: str, the activation of convolution layer in residual blocks 363 | 364 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 365 | 366 | :param pooling_func: list, the type of pooling layers in network 367 | 368 | :param include_top: bool, if true, includes classification layers 369 | 370 | :param classes: int, number of classes to classify (include_top must be true) 371 | 372 | :param output_activation: int, activation of the output Dense layer of the classifer 373 | 374 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 375 | output (if `include_top=True`) 376 | 377 | Usage: 378 | 379 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import ResNet2D101 380 | >>> from keras.layers import Input 381 | >>> id1, id2, od = 128, 128, 3 382 | >>> inputs = Input(shape=(id1, id2, 2)) 383 | >>> model_resnet = ResNet2D101(inputs, classes=od, 384 | pooling_func=['max','global_average'], 385 | output_activation='sigmoid') 386 | >>> print(model_resnet.summary()) 387 | """ 388 | 389 | 390 | def __init__(self, 391 | inputs, 392 | num_blocks=None, 393 | block_func=bottleneck_2d, 394 | conv_activation='crelu', 395 | n_filters=64, 396 | pooling_func=['max', 'global_average'], 397 | include_top=True, 398 | classes=1000, 399 | numerical_names=None, 400 | output_activation='sigmoid', 401 | *args, 402 | **kwargs 403 | ): 404 | 405 | if num_blocks is None: 406 | num_blocks = [3, 4, 23, 3] 407 | 408 | numerical_names = [False, True, True, False] 409 | 410 | super(ResNet2D101, self).__init__( 411 | inputs, 412 | num_blocks, 413 | block_func, 414 | conv_activation, 415 | n_filters, 416 | pooling_func, 417 | include_top, 418 | classes, 419 | numerical_names, 420 | output_activation, 421 | *args, 422 | **kwargs 423 | ) 424 | 425 | class ResNet2D152(ResNet2D): 426 | """ 427 | Constructs a `keras.models.Model` according to the ResNet152 specifications. 428 | 429 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 430 | 431 | :param num_blocks: list, number of residual blocks 432 | 433 | :param block_func: method, the network’s residual architecture 434 | 435 | :param conv_activation: str, the activation of convolution layer in residual blocks 436 | 437 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 438 | 439 | :param pooling_func: list, the type of pooling layers in network 440 | 441 | :param include_top: bool, if true, includes classification layers 442 | 443 | :param classes: int, number of classes to classify (include_top must be true) 444 | 445 | :param output_activation: int, activation of the output Dense layer of the classifer 446 | 447 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 448 | output (if `include_top=True`) 449 | 450 | Usage: 451 | 452 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import ResNet2D152 453 | >>> from keras.layers import Input 454 | >>> id1, id2, od = 128, 128, 3 455 | >>> inputs = Input(shape=(id1, id2, 2)) 456 | >>> model_resnet = ResNet2D152(inputs, classes=od, 457 | pooling_func=['max','global_average'], 458 | output_activation='sigmoid') 459 | >>> print(model_resnet.summary()) 460 | """ 461 | 462 | 463 | def __init__(self, 464 | inputs, 465 | num_blocks=None, 466 | block_func=bottleneck_2d, 467 | conv_activation='crelu', 468 | n_filters=64, 469 | pooling_func=['max', 'global_average'], 470 | include_top=True, 471 | classes=1000, 472 | numerical_names=None, 473 | output_activation='sigmoid', 474 | *args, 475 | **kwargs 476 | ): 477 | 478 | if num_blocks is None: 479 | num_blocks = [3, 8, 36, 3] 480 | 481 | numerical_names = [False, True, True, False] 482 | 483 | super(ResNet2D152, self).__init__( 484 | inputs, 485 | num_blocks, 486 | block_func, 487 | conv_activation, 488 | n_filters, 489 | pooling_func, 490 | include_top, 491 | classes, 492 | numerical_names, 493 | output_activation, 494 | *args, 495 | **kwargs 496 | ) 497 | 498 | class ResNet2D200(ResNet2D): 499 | """ 500 | Constructs a `keras.models.Model` according to the ResNet200 specifications. 501 | 502 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 503 | 504 | :param num_blocks: list, number of residual blocks 505 | 506 | :param block_func: method, the network’s residual architecture 507 | 508 | :param conv_activation: str, the activation of convolution layer in residual blocks 509 | 510 | :param n_filters: int, the number of filters of the first convolution layer in residual blocks 511 | 512 | :param pooling_func: list, the type of pooling layers in network 513 | 514 | :param include_top: bool, if true, includes classification layers 515 | 516 | :param classes: int, number of classes to classify (include_top must be true) 517 | 518 | :param output_activation: int, activation of the output Dense layer of the classifer 519 | 520 | :return model: ResNet model with encoding output (if `include_top=False`) or classification 521 | output (if `include_top=True`) 522 | 523 | Usage: 524 | 525 | >>> from complex_networks_keras_tf1.models.resnet_models_2d import ResNet2D200 526 | >>> from keras.layers import Input 527 | >>> id1, id2, od = 128, 128, 3 528 | >>> inputs = Input(shape=(id1, id2, 2)) 529 | >>> model_resnet = ResNet2D200(inputs, classes=od, 530 | pooling_func=['max','global_average'], 531 | output_activation='sigmoid') 532 | >>> print(model_resnet.summary()) 533 | """ 534 | 535 | 536 | def __init__(self, 537 | inputs, 538 | num_blocks=None, 539 | block_func=bottleneck_2d, 540 | conv_activation='crelu', 541 | n_filters=64, 542 | pooling_func=['max', 'global_average'], 543 | include_top=True, 544 | classes=1000, 545 | numerical_names=None, 546 | output_activation='sigmoid', 547 | *args, 548 | **kwargs): 549 | 550 | if num_blocks is None: 551 | num_blocks = [3, 24, 36, 3] 552 | 553 | numerical_names = [False, True, True, False] 554 | 555 | super(ResNet2D200, self).__init__( 556 | inputs, 557 | num_blocks, 558 | block_func, 559 | conv_activation, 560 | n_filters, 561 | pooling_func, 562 | include_top, 563 | classes, 564 | numerical_names, 565 | output_activation, 566 | *args, 567 | **kwargs 568 | ) 569 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/layers/bn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Complex valued batch normalization.""" 5 | 6 | # 7 | # Authors: Chiheb Trabelsi, Olexa Bilaniuk 8 | # 9 | # Note: The implementation of complex Batchnorm is based on 10 | # the Keras implementation of batch Normalization 11 | # available here: 12 | # https://github.com/fchollet/keras/blob/master/keras/layers/normalization.py 13 | 14 | # pylint: disable=bad-whitespace, invalid-name, too-many-arguments, too-many-locals, no-else-return, line-too-long 15 | # pylint: disable=too-many-instance-attributes, too-many-branches, attribute-defined-outside-init, arguments-differ 16 | # flake8: noqa 17 | 18 | import numpy as np 19 | from keras.layers import Layer, InputSpec 20 | from keras import initializers, regularizers, constraints 21 | import keras.backend as K 22 | 23 | 24 | def sqrt_init(shape): 25 | """sqrt_init""" 26 | value = (1 / np.sqrt(2)) * K.ones(shape) 27 | return value 28 | 29 | 30 | def sanitizedInitGet(init): 31 | """sanitizedInitGet""" 32 | if init in ["sqrt_init"]: 33 | return sqrt_init 34 | else: 35 | return initializers.get(init) 36 | 37 | 38 | def sanitizedInitSer(init): 39 | """sanitizedInitSer""" 40 | if init in [sqrt_init]: 41 | return "sqrt_init" 42 | else: 43 | return initializers.serialize(init) 44 | 45 | 46 | def complex_standardization(input_centred, Vrr, Vii, Vri, 47 | layernorm=False, axis=-1): 48 | """Complex Standardization of input 49 | 50 | Arguments: 51 | input_centred -- Input Tensor 52 | Vrr -- Real component of covariance matrix V 53 | Vii -- Imaginary component of covariance matrix V 54 | Vri -- Non-diagonal component of covariance matrix V 55 | 56 | Keyword Arguments: 57 | layernorm {bool} -- Normalization (default: {False}) 58 | axis {int} -- Axis for Standardization (default: {-1}) 59 | 60 | Raises: 61 | ValueError: Mismatched dimensoins 62 | 63 | Returns: 64 | Complex standardized input 65 | """ 66 | 67 | ndim = K.ndim(input_centred) 68 | input_dim = K.shape(input_centred)[axis] // 2 69 | variances_broadcast = [1] * ndim 70 | variances_broadcast[axis] = input_dim 71 | if layernorm: 72 | variances_broadcast[0] = K.shape(input_centred)[0] 73 | 74 | # We require the covariance matrix's inverse square root. That first 75 | # requires square rooting, followed by inversion (I do this in that order 76 | # because during the computation of square root we compute the determinant 77 | # we'll need for inversion as well). 78 | 79 | # tau = Vrr + Vii = Trace. Guaranteed >= 0 because SPD 80 | tau = Vrr + Vii 81 | # delta = (Vrr * Vii) - (Vri ** 2) = Determinant. Guaranteed >= 0 because 82 | # SPD 83 | delta = (Vrr * Vii) - (Vri ** 2) 84 | 85 | s = K.sqrt(delta) # Determinant of square root matrix 86 | t = K.sqrt(tau + 2 * s) 87 | 88 | # The square root matrix could now be explicitly formed as 89 | # [ Vrr+s Vri ] 90 | # (1/t) [ Vir Vii+s ] 91 | # https://en.wikipedia.org/wiki/Square_root_of_a_2_by_2_matrix 92 | # but we don't need to do this immediately since we can also simultaneously 93 | # invert. We can do this because we've already computed the determinant of 94 | # the square root matrix, and can thus invert it using the analytical 95 | # solution for 2x2 matrices 96 | # [ A B ] [ D -B ] 97 | # inv( [ C D ] ) = (1/det) [ -C A ] 98 | # http://mathworld.wolfram.com/MatrixInverse.html 99 | # Thus giving us 100 | # [ Vii+s -Vri ] 101 | # (1/s)(1/t)[ -Vir Vrr+s ] 102 | # So we proceed as follows: 103 | 104 | inverse_st = 1.0 / (s * t) 105 | Wrr = (Vii + s) * inverse_st 106 | Wii = (Vrr + s) * inverse_st 107 | Wri = -Vri * inverse_st 108 | 109 | # And we have computed the inverse square root matrix W = sqrt(V)! 110 | # Normalization. We multiply, x_normalized = W.x. 111 | 112 | # The returned result will be a complex standardized input 113 | # where the real and imaginary parts are obtained as follows: 114 | # x_real_normed = Wrr * x_real_centred + Wri * x_imag_centred 115 | # x_imag_normed = Wri * x_real_centred + Wii * x_imag_centred 116 | 117 | broadcast_Wrr = K.reshape(Wrr, variances_broadcast) 118 | broadcast_Wri = K.reshape(Wri, variances_broadcast) 119 | broadcast_Wii = K.reshape(Wii, variances_broadcast) 120 | 121 | cat_W_4_real = K.concatenate([broadcast_Wrr, broadcast_Wii], axis=axis) 122 | cat_W_4_imag = K.concatenate([broadcast_Wri, broadcast_Wri], axis=axis) 123 | 124 | if (axis == 1 and ndim != 3) or ndim == 2: 125 | centred_real = input_centred[:, :input_dim] 126 | centred_imag = input_centred[:, input_dim:] 127 | elif ndim == 3: 128 | centred_real = input_centred[:, :, :input_dim] 129 | centred_imag = input_centred[:, :, input_dim:] 130 | elif axis == -1 and ndim == 4: 131 | centred_real = input_centred[:, :, :, :input_dim] 132 | centred_imag = input_centred[:, :, :, input_dim:] 133 | elif axis == -1 and ndim == 5: 134 | centred_real = input_centred[:, :, :, :, :input_dim] 135 | centred_imag = input_centred[:, :, :, :, input_dim:] 136 | else: 137 | raise ValueError( 138 | 'Incorrect Batchnorm combination of axis and dimensions. axis ' 139 | 'should be either 1 or -1. ' 140 | 'axis: ' + str(axis) + '; ndim: ' + str(ndim) + '.' 141 | ) 142 | rolled_input = K.concatenate([centred_imag, centred_real], axis=axis) 143 | 144 | output = cat_W_4_real * input_centred + cat_W_4_imag * rolled_input 145 | 146 | # Wrr * x_real_centered | Wii * x_imag_centered 147 | # + Wri * x_imag_centered | Wri * x_real_centered 148 | # ----------------------------------------------- 149 | # = output 150 | 151 | return output 152 | 153 | 154 | def ComplexBN(input_centred, Vrr, Vii, Vri, beta, 155 | gamma_rr, gamma_ri, gamma_ii, scale=True, 156 | center=True, layernorm=False, axis=-1): 157 | """Complex Batch Normalization 158 | 159 | Arguments: 160 | input_centred -- input data 161 | Vrr -- Real component of covariance matrix V 162 | Vii -- Imaginary component of covariance matrix V 163 | Vri -- Non-diagonal component of covariance matrix V 164 | beta -- Lernable shift parameter beta 165 | gamma_rr -- Scaling parameter gamma - rr component of 2x2 matrix 166 | gamma_ri -- Scaling parameter gamma - ri component of 2x2 matrix 167 | gamma_ii -- Scaling parameter gamma - ii component of 2x2 matrix 168 | 169 | Keyword Arguments: 170 | scale {bool} {bool} -- Standardization of input (default: {True}) 171 | center {bool} -- Mean-shift correction (default: {True}) 172 | layernorm {bool} -- Normalization (default: {False}) 173 | axis {int} -- Axis for Standardization (default: {-1}) 174 | 175 | Raises: 176 | ValueError: Dimonsional mismatch 177 | 178 | Returns: 179 | Batch-Normalized Input 180 | """ 181 | 182 | ndim = K.ndim(input_centred) 183 | input_dim = K.shape(input_centred)[axis] // 2 184 | if scale: 185 | gamma_broadcast_shape = [1] * ndim 186 | gamma_broadcast_shape[axis] = input_dim 187 | if center: 188 | broadcast_beta_shape = [1] * ndim 189 | broadcast_beta_shape[axis] = input_dim * 2 190 | 191 | if scale: 192 | standardized_output = complex_standardization( 193 | input_centred, Vrr, Vii, Vri, 194 | layernorm, 195 | axis=axis 196 | ) 197 | 198 | # Now we perform th scaling and Shifting of the normalized x using 199 | # the scaling parameter 200 | # [ gamma_rr gamma_ri ] 201 | # Gamma = [ gamma_ri gamma_ii ] 202 | # and the shifting parameter 203 | # Beta = [beta_real beta_imag].T 204 | # where: 205 | # x_real_BN = gamma_rr * x_real_normed + amma_ri * x_imag_normed + beta_real 206 | # x_imag_BN = gamma_ri * x_real_normed + gamma_ii * x_imag_normed + beta_imag 207 | 208 | broadcast_gamma_rr = K.reshape(gamma_rr, gamma_broadcast_shape) 209 | broadcast_gamma_ri = K.reshape(gamma_ri, gamma_broadcast_shape) 210 | broadcast_gamma_ii = K.reshape(gamma_ii, gamma_broadcast_shape) 211 | 212 | cat_gamma_4_real = K.concatenate([broadcast_gamma_rr, broadcast_gamma_ii], axis=axis) 213 | cat_gamma_4_imag = K.concatenate([broadcast_gamma_ri, broadcast_gamma_ri], axis=axis) 214 | if (axis == 1 and ndim != 3) or ndim == 2: 215 | centred_real = standardized_output[:, :input_dim] 216 | centred_imag = standardized_output[:, input_dim:] 217 | elif ndim == 3: 218 | centred_real = standardized_output[:, :, :input_dim] 219 | centred_imag = standardized_output[:, :, input_dim:] 220 | elif axis == -1 and ndim == 4: 221 | centred_real = standardized_output[:, :, :, :input_dim] 222 | centred_imag = standardized_output[:, :, :, input_dim:] 223 | elif axis == -1 and ndim == 5: 224 | centred_real = standardized_output[:, :, :, :, :input_dim] 225 | centred_imag = standardized_output[:, :, :, :, input_dim:] 226 | else: 227 | raise ValueError( 228 | 'Incorrect Batchnorm combination of axis and dimensions. axis' 229 | ' should be either 1 or -1. ' 230 | 'axis: ' + str(axis) + '; ndim: ' + str(ndim) + '.' 231 | ) 232 | rolled_standardized_output = K.concatenate([centred_imag, centred_real], axis=axis) 233 | if center: 234 | broadcast_beta = K.reshape(beta, broadcast_beta_shape) 235 | return cat_gamma_4_real * standardized_output + cat_gamma_4_imag * rolled_standardized_output + broadcast_beta 236 | else: 237 | return cat_gamma_4_real * standardized_output + cat_gamma_4_imag * rolled_standardized_output 238 | else: 239 | if center: 240 | broadcast_beta = K.reshape(beta, broadcast_beta_shape) 241 | return input_centred + broadcast_beta 242 | else: 243 | return input_centred 244 | 245 | 246 | class ComplexBatchNormalization(Layer): 247 | """Complex version of the real domain 248 | Batch normalization layer (Ioffe and Szegedy, 2014). 249 | Normalize the activations of the previous complex layer at each batch, 250 | i.e. applies a transformation that maintains the mean of a complex unit 251 | close to the null vector, the 2 by 2 covariance matrix of a complex unit close to identity 252 | and the 2 by 2 relation matrix, also called pseudo-covariance, close to the 253 | null matrix. 254 | # Arguments 255 | axis: Integer, the axis that should be normalized 256 | (typically the features axis). 257 | For instance, after a `Conv2D` layer with 258 | `data_format="channels_first"`, 259 | set `axis=2` in `ComplexBatchNormalization`. 260 | momentum: Momentum for the moving statistics related to the real and 261 | imaginary parts. 262 | epsilon: Small float added to each of the variances related to the 263 | real and imaginary parts in order to avoid dividing by zero. 264 | center: If True, add offset of `beta` to complex normalized tensor. 265 | If False, `beta` is ignored. 266 | (beta is formed by real_beta and imag_beta) 267 | scale: If True, multiply by the `gamma` matrix. 268 | If False, `gamma` is not used. 269 | beta_initializer: Initializer for the real_beta and the imag_beta weight. 270 | gamma_diag_initializer: Initializer for the diagonal elements of the gamma matrix. 271 | which are the variances of the real part and the imaginary part. 272 | gamma_off_initializer: Initializer for the off-diagonal elements of the gamma matrix. 273 | moving_mean_initializer: Initializer for the moving means. 274 | moving_variance_initializer: Initializer for the moving variances. 275 | moving_covariance_initializer: Initializer for the moving covariance of 276 | the real and imaginary parts. 277 | beta_regularizer: Optional regularizer for the beta weights. 278 | gamma_regularizer: Optional regularizer for the gamma weights. 279 | beta_constraint: Optional constraint for the beta weights. 280 | gamma_constraint: Optional constraint for the gamma weights. 281 | # Input shape 282 | Arbitrary. Use the keyword argument `input_shape` 283 | (tuple of integers, does not include the samples axis) 284 | when using this layer as the first layer in a model. 285 | # Output shape 286 | Same shape as input. 287 | # References 288 | - [Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift](https://arxiv.org/abs/1502.03167) 289 | """ 290 | 291 | def __init__(self, 292 | axis=-1, 293 | momentum=0.9, 294 | epsilon=1e-4, 295 | center=True, 296 | scale=True, 297 | beta_initializer='zeros', 298 | gamma_diag_initializer='sqrt_init', 299 | gamma_off_initializer='zeros', 300 | moving_mean_initializer='zeros', 301 | moving_variance_initializer='sqrt_init', 302 | moving_covariance_initializer='zeros', 303 | beta_regularizer=None, 304 | gamma_diag_regularizer=None, 305 | gamma_off_regularizer=None, 306 | beta_constraint=None, 307 | gamma_diag_constraint=None, 308 | gamma_off_constraint=None, 309 | **kwargs): 310 | super(ComplexBatchNormalization, self).__init__(**kwargs) 311 | self.supports_masking = True 312 | self.axis = axis 313 | self.momentum = momentum 314 | self.epsilon = epsilon 315 | self.center = center 316 | self.scale = scale 317 | self.beta_initializer = sanitizedInitGet(beta_initializer) 318 | self.gamma_diag_initializer = sanitizedInitGet(gamma_diag_initializer) 319 | self.gamma_off_initializer = sanitizedInitGet(gamma_off_initializer) 320 | self.moving_mean_initializer = sanitizedInitGet(moving_mean_initializer) 321 | self.moving_variance_initializer = sanitizedInitGet(moving_variance_initializer) 322 | self.moving_covariance_initializer = sanitizedInitGet(moving_covariance_initializer) 323 | self.beta_regularizer = regularizers.get(beta_regularizer) 324 | self.gamma_diag_regularizer = regularizers.get(gamma_diag_regularizer) 325 | self.gamma_off_regularizer = regularizers.get(gamma_off_regularizer) 326 | self.beta_constraint = constraints .get(beta_constraint) 327 | self.gamma_diag_constraint = constraints .get(gamma_diag_constraint) 328 | self.gamma_off_constraint = constraints .get(gamma_off_constraint) 329 | 330 | def build(self, input_shape): 331 | dim = input_shape[self.axis] 332 | if dim is None: 333 | raise ValueError('Axis ' + str(self.axis) + ' of ' 334 | 'input tensor should have a defined dimension ' 335 | 'but the layer received an input with shape ' + 336 | str(input_shape) + '.') 337 | self.input_spec = InputSpec(ndim=len(input_shape), 338 | axes={self.axis: dim}) 339 | 340 | param_shape = (input_shape[self.axis] // 2,) 341 | 342 | if self.scale: 343 | self.gamma_rr = self.add_weight(shape=param_shape, 344 | name='gamma_rr', 345 | initializer=self.gamma_diag_initializer, 346 | regularizer=self.gamma_diag_regularizer, 347 | constraint=self.gamma_diag_constraint) 348 | self.gamma_ii = self.add_weight(shape=param_shape, 349 | name='gamma_ii', 350 | initializer=self.gamma_diag_initializer, 351 | regularizer=self.gamma_diag_regularizer, 352 | constraint=self.gamma_diag_constraint) 353 | self.gamma_ri = self.add_weight(shape=param_shape, 354 | name='gamma_ri', 355 | initializer=self.gamma_off_initializer, 356 | regularizer=self.gamma_off_regularizer, 357 | constraint=self.gamma_off_constraint) 358 | self.moving_Vrr = self.add_weight(shape=param_shape, 359 | initializer=self.moving_variance_initializer, 360 | name='moving_Vrr', 361 | trainable=False) 362 | self.moving_Vii = self.add_weight(shape=param_shape, 363 | initializer=self.moving_variance_initializer, 364 | name='moving_Vii', 365 | trainable=False) 366 | self.moving_Vri = self.add_weight(shape=param_shape, 367 | initializer=self.moving_covariance_initializer, 368 | name='moving_Vri', 369 | trainable=False) 370 | else: 371 | self.gamma_rr = None 372 | self.gamma_ii = None 373 | self.gamma_ri = None 374 | self.moving_Vrr = None 375 | self.moving_Vii = None 376 | self.moving_Vri = None 377 | 378 | if self.center: 379 | self.beta = self.add_weight(shape=(input_shape[self.axis],), 380 | name='beta', 381 | initializer=self.beta_initializer, 382 | regularizer=self.beta_regularizer, 383 | constraint=self.beta_constraint) 384 | self.moving_mean = self.add_weight(shape=(input_shape[self.axis],), 385 | initializer=self.moving_mean_initializer, 386 | name='moving_mean', 387 | trainable=False) 388 | else: 389 | self.beta = None 390 | self.moving_mean = None 391 | 392 | self.built = True 393 | 394 | def call(self, inputs, training=None): 395 | input_shape = K.int_shape(inputs) 396 | ndim = len(input_shape) 397 | reduction_axes = list(range(ndim)) 398 | del reduction_axes[self.axis] 399 | input_dim = input_shape[self.axis] // 2 400 | mu = K.mean(inputs, axis=reduction_axes) 401 | broadcast_mu_shape = [1] * len(input_shape) 402 | broadcast_mu_shape[self.axis] = input_shape[self.axis] 403 | broadcast_mu = K.reshape(mu, broadcast_mu_shape) 404 | if self.center: 405 | input_centred = inputs - broadcast_mu 406 | else: 407 | input_centred = inputs 408 | centred_squared = input_centred ** 2 409 | if (self.axis == 1 and ndim != 3) or ndim == 2: 410 | centred_squared_real = centred_squared[:, :input_dim] 411 | centred_squared_imag = centred_squared[:, input_dim:] 412 | centred_real = input_centred[:, :input_dim] 413 | centred_imag = input_centred[:, input_dim:] 414 | elif ndim == 3: 415 | centred_squared_real = centred_squared[:, :, :input_dim] 416 | centred_squared_imag = centred_squared[:, :, input_dim:] 417 | centred_real = input_centred[:, :, :input_dim] 418 | centred_imag = input_centred[:, :, input_dim:] 419 | elif self.axis == -1 and ndim == 4: 420 | centred_squared_real = centred_squared[:, :, :, :input_dim] 421 | centred_squared_imag = centred_squared[:, :, :, input_dim:] 422 | centred_real = input_centred[:, :, :, :input_dim] 423 | centred_imag = input_centred[:, :, :, input_dim:] 424 | elif self.axis == -1 and ndim == 5: 425 | centred_squared_real = centred_squared[:, :, :, :, :input_dim] 426 | centred_squared_imag = centred_squared[:, :, :, :, input_dim:] 427 | centred_real = input_centred[:, :, :, :, :input_dim] 428 | centred_imag = input_centred[:, :, :, :, input_dim:] 429 | else: 430 | raise ValueError( 431 | 'Incorrect Batchnorm combination of axis and dimensions. axis should be either 1 or -1. ' 432 | 'axis: ' + str(self.axis) + '; ndim: ' + str(ndim) + '.' 433 | ) 434 | if self.scale: 435 | Vrr = K.mean( 436 | centred_squared_real, 437 | axis=reduction_axes 438 | ) + self.epsilon 439 | Vii = K.mean( 440 | centred_squared_imag, 441 | axis=reduction_axes 442 | ) + self.epsilon 443 | # Vri contains the real and imaginary covariance for each feature map. 444 | Vri = K.mean( 445 | centred_real * centred_imag, 446 | axis=reduction_axes, 447 | ) 448 | elif self.center: 449 | Vrr = None 450 | Vii = None 451 | Vri = None 452 | else: 453 | raise ValueError('Error. Both scale and center in batchnorm are set to False.') 454 | 455 | input_bn = ComplexBN( 456 | input_centred, Vrr, Vii, Vri, 457 | self.beta, self.gamma_rr, self.gamma_ri, 458 | self.gamma_ii, self.scale, self.center, 459 | axis=self.axis 460 | ) 461 | if training in {0, False}: 462 | return input_bn 463 | else: 464 | update_list = [] 465 | if self.center: 466 | update_list.append(K.moving_average_update(self.moving_mean, mu, self.momentum)) 467 | if self.scale: 468 | update_list.append(K.moving_average_update(self.moving_Vrr, Vrr, self.momentum)) 469 | update_list.append(K.moving_average_update(self.moving_Vii, Vii, self.momentum)) 470 | update_list.append(K.moving_average_update(self.moving_Vri, Vri, self.momentum)) 471 | self.add_update(update_list, inputs) 472 | 473 | def normalize_inference(): 474 | """normalize_inference""" 475 | if self.center: 476 | inference_centred = inputs - K.reshape(self.moving_mean, broadcast_mu_shape) 477 | else: 478 | inference_centred = inputs 479 | return ComplexBN( 480 | inference_centred, self.moving_Vrr, self.moving_Vii, 481 | self.moving_Vri, self.beta, self.gamma_rr, self.gamma_ri, 482 | self.gamma_ii, self.scale, self.center, axis=self.axis 483 | ) 484 | 485 | # Pick the normalized form corresponding to the training phase. 486 | return K.in_train_phase(input_bn, 487 | normalize_inference, 488 | training=training) 489 | 490 | def get_config(self): 491 | config = { 492 | 'axis': self.axis, 493 | 'momentum': self.momentum, 494 | 'epsilon': self.epsilon, 495 | 'center': self.center, 496 | 'scale': self.scale, 497 | 'beta_initializer': sanitizedInitSer(self.beta_initializer), 498 | 'gamma_diag_initializer': sanitizedInitSer(self.gamma_diag_initializer), 499 | 'gamma_off_initializer': sanitizedInitSer(self.gamma_off_initializer), 500 | 'moving_mean_initializer': sanitizedInitSer(self.moving_mean_initializer), 501 | 'moving_variance_initializer': sanitizedInitSer(self.moving_variance_initializer), 502 | 'moving_covariance_initializer': sanitizedInitSer(self.moving_covariance_initializer), 503 | 'beta_regularizer': regularizers.serialize(self.beta_regularizer), 504 | 'gamma_diag_regularizer': regularizers.serialize(self.gamma_diag_regularizer), 505 | 'gamma_off_regularizer': regularizers.serialize(self.gamma_off_regularizer), 506 | 'beta_constraint': constraints .serialize(self.beta_constraint), 507 | 'gamma_diag_constraint': constraints .serialize(self.gamma_diag_constraint), 508 | 'gamma_off_constraint': constraints .serialize(self.gamma_off_constraint), 509 | } 510 | base_config = super(ComplexBatchNormalization, self).get_config() 511 | return dict(list(base_config.items()) + list(config.items())) 512 | -------------------------------------------------------------------------------- /complex_networks_keras_tf1/models/densenet_models_1d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """This module implements a number of popular one-dimensional complex valued DenseNet models.""" 4 | 5 | # Authors: Qinggang Sun 6 | # 7 | # Reference: 8 | # Somshubra Majumdar. DenseNet 9 | # https://github.com/titu1994/DenseNet 10 | 11 | # pylint: disable=too-many-locals, dangerous-default-value, keyword-arg-before-vararg, too-many-arguments, invalid-name 12 | # pylint: disable=too-many-branches, too-many-statements 13 | 14 | import keras.backend as K 15 | import keras.layers 16 | import keras.regularizers 17 | 18 | from .densenet_blocks_1d import dense1d_block, transition1d_block 19 | from ..layers.activations import layer_activation 20 | from ..layers.bn import ComplexBatchNormalization 21 | from ..layers.conv import ComplexConv1D 22 | from ..layers.dense import ComplexDense 23 | # from ..layers.conv import conv1d_transpose 24 | from ..layers.pool import ComplexMaxPooling1D, ComplexAveragePooling1D, SpectralPooling1D 25 | 26 | 27 | class DenseNet1D(keras.Model): 28 | """Instantiate the DenseNet architecture, constructs a `keras.models.Model` object using the given block count. 29 | 30 | Args: 31 | inputs (keras tensor): e.g. an instance of `keras.layers.Input` 32 | depth (int, optional): number of layers in the DenseNet. Defaults to None. 33 | nb_dense_block (int, optional): number of dense blocks to add to end. Defaults to 4. 34 | growth_rate (int, optional): number of filters to add per dense block. Defaults to 12. 35 | nb_filter (int, optional): initial number of filters. -1 indicates initial 36 | number of filters is 2 * growth_rate. Defaults to -1. 37 | nb_layers_per_block (list, optional): number of layers in each dense block. 38 | Can be a -1, positive integer or a list. 39 | If -1, calculates nb_layer_per_block from the network depth. 40 | If positive integer, a set number of layers per dense block. 41 | If list, nb_layer is used as provided. Note that list size must 42 | be (nb_dense_block + 1). Defaults to -1. 43 | bottleneck (bool, optional): flag to add bottleneck blocks in between dense blocks. Defaults to False. 44 | reduction (float, optional): reduction factor of transition blocks. 45 | Note : reduction value is inverted to compute compression. Defaults to 0.0. 46 | dropout_rate (float, optional): dropout rate. Defaults to 0.0. 47 | weight_decay (float, optional): weight decay rate. Defaults to 1e-4. 48 | subsample_initial_block (bool, optional): Set to True to subsample the initial convolution and 49 | add a Pool1D layer before the dense blocks are added. Defaults to False. 50 | activation_conv (str, optional): Type of activation of conv layers in the blocks. Defaults to 'crelu'. 51 | pooling_func (list, optional): Type of pooling layers. Defaults to ['max', 'global_average']. 52 | include_top (bool, optional): whether to include the fully-connected 53 | layer at the top of the network. Defaults to False. 54 | classes (int, optional): number of classes to classify, only to be specified if `include_top` is True. 55 | Defaults to None. 56 | output_activation (str, optional): Type of activation at the top layer. Defaults to None. 57 | """ 58 | 59 | def __init__(self, 60 | inputs, 61 | depth=None, 62 | nb_dense_block=4, 63 | growth_rate=12, 64 | nb_filter=-1, 65 | nb_layers_per_block=-1, 66 | bottleneck=False, 67 | reduction=0.0, 68 | dropout_rate=0.0, 69 | weight_decay=1e-4, 70 | subsample_initial_block=False, 71 | activation_conv='crelu', 72 | pooling_func=['max', 'global_average'], 73 | include_top=False, 74 | classes=None, 75 | output_activation=None, 76 | *args, 77 | **kwargs 78 | ): 79 | concat_axis = -1 if K.image_data_format() == "channels_last" else 1 80 | 81 | if reduction != 0.0: 82 | assert reduction <= 1.0 and reduction > 0.0, 'reduction value must lie between 0.0 and 1.0' 83 | 84 | # layers in each dense block 85 | if isinstance(nb_layers_per_block, (list, tuple)): 86 | nb_layers = list(nb_layers_per_block) # Convert tuple to list 87 | 88 | assert len(nb_layers) == (nb_dense_block), 'If list, nb_layer is used as provided. ' \ 89 | 'Note that list size must be (nb_dense_block)' 90 | final_nb_layer = nb_layers[-1] 91 | nb_layers = nb_layers[:-1] 92 | else: 93 | if nb_layers_per_block == -1: 94 | assert (depth - 4) % 3 == 0, 'Depth must be 3 N + 4 if nb_layers_per_block == -1' 95 | count = int((depth - 4) / 3) 96 | 97 | if bottleneck: 98 | count = count // 2 99 | 100 | nb_layers = [count for _ in range(nb_dense_block)] 101 | final_nb_layer = count 102 | else: 103 | final_nb_layer = nb_layers_per_block 104 | nb_layers = [nb_layers_per_block] * nb_dense_block 105 | 106 | # compute initial nb_filter if -1, else accept users initial nb_filter 107 | if nb_filter <= 0: 108 | nb_filter = 2 * growth_rate 109 | 110 | # compute compression factor 111 | compression = 1.0 - reduction 112 | 113 | # Initial convolution 114 | if subsample_initial_block: 115 | initial_kernel = 7 116 | initial_strides = 2 117 | else: 118 | initial_kernel = 3 119 | initial_strides = 1 120 | 121 | x_complex = ComplexConv1D(nb_filter, initial_kernel, strides=initial_strides, 122 | padding='same', use_bias=False, 123 | spectral_parametrization=False, 124 | kernel_regularizer=keras.regularizers.l2(weight_decay))(inputs) 125 | 126 | if subsample_initial_block: 127 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(x_complex) 128 | x_complex = layer_activation(x_complex, activation_conv) 129 | if pooling_func[0] == 'max': 130 | x_complex = ComplexMaxPooling1D(pool_size=3, strides=2, padding='same')(x_complex) 131 | elif pooling_func[0] == 'average': 132 | x_complex = ComplexAveragePooling1D(pool_size=3, strides=2, padding='same')(x_complex) 133 | 134 | # Add dense blocks 135 | for block_idx in range(nb_dense_block - 1): 136 | x_complex, nb_filter = dense1d_block(x_complex, nb_layers[block_idx], nb_filter, growth_rate, 137 | activation=activation_conv, bottleneck=bottleneck, 138 | dropout_rate=dropout_rate, weight_decay=weight_decay) 139 | 140 | # add transition_block 141 | x_complex = transition1d_block(x_complex, nb_filter, activation=activation_conv, 142 | compression=compression, weight_decay=weight_decay) 143 | nb_filter = int(nb_filter * compression) 144 | 145 | # The last dense_block does not have a transition_block 146 | x_complex, nb_filter = dense1d_block(x_complex, final_nb_layer, nb_filter, growth_rate, 147 | activation=activation_conv, bottleneck=bottleneck, 148 | dropout_rate=dropout_rate, weight_decay=weight_decay) 149 | 150 | x_complex = ComplexBatchNormalization(axis=concat_axis, epsilon=1.1e-5)(x_complex) 151 | x_complex = layer_activation(x_complex, activation_conv) 152 | 153 | if include_top: 154 | assert classes > 0 155 | if pooling_func[1] == 'global_average': 156 | x_complex = keras.layers.GlobalAveragePooling1D(name="pool5")(x_complex) 157 | elif pooling_func[1] == 'complex_average': 158 | x_complex = ComplexAveragePooling1D(name='pool5')(x_complex) 159 | elif pooling_func[1] == 'complex_max': 160 | x_complex = ComplexMaxPooling1D(name='pool5')(x_complex) 161 | elif pooling_func[1] == 'spectral_average': 162 | x_complex = SpectralPooling1D(gamma=[0.25, 0.25], name='pool5')(x_complex) 163 | 164 | if output_activation is None: 165 | output_activation = 'softmax' 166 | 167 | if K.ndim(x_complex) > 2: 168 | x_complex = keras.layers.Flatten()(x_complex) 169 | 170 | if output_activation.startswith('complex_'): 171 | output_activation = output_activation[len('complex_'):] 172 | x = ComplexDense(classes, activation=output_activation)(x_complex) 173 | else: 174 | x = keras.layers.Dense(classes, activation=output_activation)(x_complex) 175 | else: 176 | x = x_complex 177 | 178 | super(DenseNet1D, self).__init__(inputs=inputs, outputs=x, *args, **kwargs) 179 | 180 | 181 | class DenseNet1D121(DenseNet1D): 182 | """Constructs a `keras.models.Model` according to the DenseNet1D121 specifications. 183 | 184 | Args: 185 | inputs (keras tensor): e.g. an instance of `keras.layers.Input`. 186 | depth (int, optional): number of layers in the DenseNet. Defaults to 121. 187 | nb_dense_block (int, optional): number of dense blocks to add to end. Defaults to 4. 188 | growth_rate (int, optional): number of filters to add per dense block. Defaults to 32. 189 | nb_filter (int, optional): initial number of filters. -1 indicates initial 190 | number of filters is 2 * growth_rate. Defaults to 64. 191 | nb_layers_per_block (list, optional): number of layers in each dense block. 192 | Can be a -1, positive integer or a list. 193 | If -1, calculates nb_layer_per_block from the network depth. 194 | If positive integer, a set number of layers per dense block. 195 | If list, nb_layer is used as provided. Note that list size must 196 | be (nb_dense_block + 1). Defaults to [6, 12, 24, 16]. 197 | bottleneck (bool, optional): flag to add bottleneck blocks in between dense blocks. Defaults to True. 198 | reduction (float, optional): reduction factor of transition blocks. 199 | Note : reduction value is inverted to compute compression. Defaults to 0.5. 200 | dropout_rate (float, optional): dropout rate. Defaults to 0.0. 201 | weight_decay (float, optional): weight decay rate. Defaults to 1e-4. 202 | subsample_initial_block (bool, optional): Set to True to subsample the initial convolution and 203 | add a Pool1D layer before the dense blocks are added. Defaults to True. 204 | activation_conv (str, optional): Type of activation of conv layers in the blocks. Defaults to 'crelu'. 205 | pooling_func (list, optional): Type of pooling layers. Defaults to ['max', 'global_average']. 206 | include_top (bool, optional): whether to include the fully-connected 207 | layer at the top of the network. Defaults to True. 208 | classes (int, optional): number of classes to classify, only to be specified if `include_top` is True. 209 | Defaults to 10. 210 | output_activation (str, optional): Type of activation at the top layer. Defaults to 'softmax'. 211 | Usage: 212 | >>> from complex_networks_keras_tf1.models.densenet_models_1d import DenseNet1D121 213 | >>> from keras.layers import Input 214 | >>> id1, od = 128, 3 215 | >>> inputs = Input(shape=(id1, 2)) 216 | >>> model_densenet = DenseNet1D121( 217 | inputs, 218 | depthh=121, 219 | nb_dense_block=4, 220 | growth_rate=13, 221 | nb_filter=64, 222 | nb_layers_per_block=[6, 12, 24, 16], 223 | bottleneck=True, 224 | reduction=0.5, 225 | dropout_rate=0.0, 226 | weight_decay=1e-4, 227 | subsample_initial_block=True, 228 | classes=od, 229 | output_activation='sigmoid') 230 | >>> print(model_densenet.summary()) 231 | """ 232 | 233 | def __init__(self, 234 | inputs, 235 | depth=121, 236 | nb_dense_block=4, 237 | growth_rate=32, 238 | nb_filter=64, 239 | nb_layers_per_block=[6, 12, 24, 16], 240 | bottleneck=True, 241 | reduction=0.5, 242 | dropout_rate=0.0, 243 | weight_decay=1e-4, 244 | subsample_initial_block=True, 245 | activation_conv='crelu', 246 | pooling_func=['max', 'global_average'], 247 | include_top=True, 248 | classes=10, 249 | output_activation='softmax', 250 | *args, 251 | **kwargs 252 | ): 253 | super(DenseNet1D121, self).__init__( 254 | inputs, 255 | depth, 256 | nb_dense_block, 257 | growth_rate, 258 | nb_filter, 259 | nb_layers_per_block, 260 | bottleneck, 261 | reduction, 262 | dropout_rate, 263 | weight_decay, 264 | subsample_initial_block, 265 | activation_conv, 266 | pooling_func, 267 | include_top, 268 | classes, 269 | output_activation, 270 | *args, 271 | **kwargs 272 | ) 273 | 274 | 275 | class DenseNet1D169(DenseNet1D): 276 | """Constructs a `keras.models.Model` according to the DenseNet1D169 specifications. 277 | 278 | Args: 279 | inputs (keras tensor): e.g. an instance of `keras.layers.Input`. 280 | depth (int, optional): number of layers in the DenseNet. Defaults to 169. 281 | nb_dense_block (int, optional): number of dense blocks to add to end. Defaults to 4. 282 | growth_rate (int, optional): number of filters to add per dense block. Defaults to 32. 283 | nb_filter (int, optional): initial number of filters. -1 indicates initial 284 | number of filters is 2 * growth_rate. Defaults to 64. 285 | nb_layers_per_block (list, optional): number of layers in each dense block. 286 | Can be a -1, positive integer or a list. 287 | If -1, calculates nb_layer_per_block from the network depth. 288 | If positive integer, a set number of layers per dense block. 289 | If list, nb_layer is used as provided. Note that list size must 290 | be (nb_dense_block + 1). Defaults to [6, 12, 24, 16]. 291 | bottleneck (bool, optional): flag to add bottleneck blocks in between dense blocks. Defaults to True. 292 | reduction (float, optional): reduction factor of transition blocks. 293 | Note : reduction value is inverted to compute compression. Defaults to 0.5. 294 | dropout_rate (float, optional): dropout rate. Defaults to 0.0. 295 | weight_decay (float, optional): weight decay rate. Defaults to 1e-4. 296 | subsample_initial_block (bool, optional): Set to True to subsample the initial convolution and 297 | add a Pool1D layer before the dense blocks are added. Defaults to True. 298 | activation_conv (str, optional): Type of activation of conv layers in the blocks. Defaults to 'crelu'. 299 | pooling_func (list, optional): Type of pooling layers. Defaults to ['max', 'global_average']. 300 | include_top (bool, optional): whether to include the fully-connected 301 | layer at the top of the network. Defaults to True. 302 | classes (int, optional): number of classes to classify, only to be specified if `include_top` is True. 303 | Defaults to 10. 304 | output_activation (str, optional): Type of activation at the top layer. Defaults to 'softmax'. 305 | Usage: 306 | >>> from complex_networks_keras_tf1.models.densenet_models_1d import DenseNet1D169 307 | >>> from keras.layers import Input 308 | >>> id1, od = 128, 3 309 | >>> inputs = Input(shape=(id1, 2)) 310 | >>> model_densenet = DenseNet1D169( 311 | inputs, 312 | depthh=169, 313 | nb_dense_block=4, 314 | growth_rate=13, 315 | nb_filter=64, 316 | nb_layers_per_block=[6, 12, 32, 32], 317 | bottleneck=True, 318 | reduction=0.5, 319 | dropout_rate=0.0, 320 | weight_decay=1e-4, 321 | subsample_initial_block=True, 322 | classes=od, 323 | output_activation='sigmoid') 324 | >>> print(model_densenet.summary()) 325 | """ 326 | 327 | def __init__(self, 328 | inputs, 329 | depth=169, 330 | nb_dense_block=4, 331 | growth_rate=32, 332 | nb_filter=64, 333 | nb_layers_per_block=[6, 12, 32, 32], 334 | bottleneck=True, 335 | reduction=0.5, 336 | dropout_rate=0.0, 337 | weight_decay=1e-4, 338 | subsample_initial_block=True, 339 | activation_conv='crelu', 340 | pooling_func=['max', 'global_average'], 341 | include_top=True, 342 | classes=10, 343 | output_activation='softmax', 344 | *args, 345 | **kwargs 346 | ): 347 | super(DenseNet1D169, self).__init__( 348 | inputs, 349 | depth, 350 | nb_dense_block, 351 | growth_rate, 352 | nb_filter, 353 | nb_layers_per_block, 354 | bottleneck, 355 | reduction, 356 | dropout_rate, 357 | weight_decay, 358 | subsample_initial_block, 359 | activation_conv, 360 | pooling_func, 361 | include_top, 362 | classes, 363 | output_activation, 364 | *args, 365 | **kwargs 366 | ) 367 | 368 | 369 | class DenseNet1D201(DenseNet1D): 370 | """Constructs a `keras.models.Model` according to the DenseNet1D201 specifications. 371 | 372 | Args: 373 | inputs (keras tensor): e.g. an instance of `keras.layers.Input`. 374 | depth (int, optional): number of layers in the DenseNet. Defaults to 201. 375 | nb_dense_block (int, optional): number of dense blocks to add to end. Defaults to 4. 376 | growth_rate (int, optional): number of filters to add per dense block. Defaults to 32. 377 | nb_filter (int, optional): initial number of filters. -1 indicates initial 378 | number of filters is 2 * growth_rate. Defaults to 64. 379 | nb_layers_per_block (list, optional): number of layers in each dense block. 380 | Can be a -1, positive integer or a list. 381 | If -1, calculates nb_layer_per_block from the network depth. 382 | If positive integer, a set number of layers per dense block. 383 | If list, nb_layer is used as provided. Note that list size must 384 | be (nb_dense_block + 1). Defaults to [6, 12, 24, 16]. 385 | bottleneck (bool, optional): flag to add bottleneck blocks in between dense blocks. Defaults to True. 386 | reduction (float, optional): reduction factor of transition blocks. 387 | Note : reduction value is inverted to compute compression. Defaults to 0.5. 388 | dropout_rate (float, optional): dropout rate. Defaults to 0.0. 389 | weight_decay (float, optional): weight decay rate. Defaults to 1e-4. 390 | subsample_initial_block (bool, optional): Set to True to subsample the initial convolution and 391 | add a Pool1D layer before the dense blocks are added. Defaults to True. 392 | activation_conv (str, optional): Type of activation of conv layers in the blocks. Defaults to 'crelu'. 393 | pooling_func (list, optional): Type of pooling layers. Defaults to ['max', 'global_average']. 394 | include_top (bool, optional): whether to include the fully-connected 395 | layer at the top of the network. Defaults to True. 396 | classes (int, optional): number of classes to classify, only to be specified if `include_top` is True. 397 | Defaults to 10. 398 | output_activation (str, optional): Type of activation at the top layer. Defaults to 'softmax'. 399 | Usage: 400 | >>> from complex_networks_keras_tf1.models.densenet_models_1d import DenseNet1D201 401 | >>> from keras.layers import Input 402 | >>> id1, od = 128, 3 403 | >>> inputs = Input(shape=(id1, 2)) 404 | >>> model_densenet = DenseNet1D201( 405 | inputs, 406 | depthh=201, 407 | nb_dense_block=4, 408 | growth_rate=13, 409 | nb_filter=64, 410 | nb_layers_per_block=[6, 12, 48, 32], 411 | bottleneck=True, 412 | reduction=0.5, 413 | dropout_rate=0.0, 414 | weight_decay=1e-4, 415 | subsample_initial_block=True, 416 | classes=od, 417 | output_activation='sigmoid') 418 | >>> print(model_densenet.summary()) 419 | """ 420 | 421 | def __init__(self, 422 | inputs, 423 | depth=201, 424 | nb_dense_block=4, 425 | growth_rate=32, 426 | nb_filter=64, 427 | nb_layers_per_block=[6, 12, 48, 32], 428 | bottleneck=True, 429 | reduction=0.5, 430 | dropout_rate=0.0, 431 | weight_decay=1e-4, 432 | subsample_initial_block=True, 433 | activation_conv='crelu', 434 | pooling_func=['max', 'global_average'], 435 | include_top=True, 436 | classes=10, 437 | output_activation='softmax', 438 | *args, 439 | **kwargs 440 | ): 441 | super(DenseNet1D201, self).__init__( 442 | inputs, 443 | depth, 444 | nb_dense_block, 445 | growth_rate, 446 | nb_filter, 447 | nb_layers_per_block, 448 | bottleneck, 449 | reduction, 450 | dropout_rate, 451 | weight_decay, 452 | subsample_initial_block, 453 | activation_conv, 454 | pooling_func, 455 | include_top, 456 | classes, 457 | output_activation, 458 | *args, 459 | **kwargs 460 | ) 461 | 462 | 463 | class DenseNet1D264(DenseNet1D): 464 | """Constructs a `keras.models.Model` according to the DenseNet1D264 specifications. 465 | 466 | Args: 467 | inputs (keras tensor): e.g. an instance of `keras.layers.Input`. 468 | depth (int, optional): number of layers in the DenseNet. Defaults to 264. 469 | nb_dense_block (int, optional): number of dense blocks to add to end. Defaults to 4. 470 | growth_rate (int, optional): number of filters to add per dense block. Defaults to 32. 471 | nb_filter (int, optional): initial number of filters. -1 indicates initial 472 | number of filters is 2 * growth_rate. Defaults to 64. 473 | nb_layers_per_block (list, optional): number of layers in each dense block. 474 | Can be a -1, positive integer or a list. 475 | If -1, calculates nb_layer_per_block from the network depth. 476 | If positive integer, a set number of layers per dense block. 477 | If list, nb_layer is used as provided. Note that list size must 478 | be (nb_dense_block + 1). Defaults to [6, 12, 24, 16]. 479 | bottleneck (bool, optional): flag to add bottleneck blocks in between dense blocks. Defaults to True. 480 | reduction (float, optional): reduction factor of transition blocks. 481 | Note : reduction value is inverted to compute compression. Defaults to 0.5. 482 | dropout_rate (float, optional): dropout rate. Defaults to 0.0. 483 | weight_decay (float, optional): weight decay rate. Defaults to 1e-4. 484 | subsample_initial_block (bool, optional): Set to True to subsample the initial convolution and 485 | add a Pool1D layer before the dense blocks are added. Defaults to True. 486 | activation_conv (str, optional): Type of activation of conv layers in the blocks. Defaults to 'crelu'. 487 | pooling_func (list, optional): Type of pooling layers. Defaults to ['max', 'global_average']. 488 | include_top (bool, optional): whether to include the fully-connected 489 | layer at the top of the network. Defaults to True. 490 | classes (int, optional): number of classes to classify, only to be specified if `include_top` is True. 491 | Defaults to 10. 492 | output_activation (str, optional): Type of activation at the top layer. Defaults to 'softmax'. 493 | Usage: 494 | >>> from complex_networks_keras_tf1.models.densenet_models_1d import DenseNet1D264 495 | >>> from keras.layers import Input 496 | >>> id1, od = 128, 3 497 | >>> inputs = Input(shape=(id1, 2)) 498 | >>> model_densenet = DenseNet1D264( 499 | inputs, 500 | depthh=264, 501 | nb_dense_block=4, 502 | growth_rate=13, 503 | nb_filter=64, 504 | nb_layers_per_block=[6, 12, 64, 48], 505 | bottleneck=True, 506 | reduction=0.5, 507 | dropout_rate=0.0, 508 | weight_decay=1e-4, 509 | subsample_initial_block=True, 510 | classes=od, 511 | output_activation='sigmoid') 512 | >>> print(model_densenet.summary()) 513 | """ 514 | 515 | def __init__(self, 516 | inputs, 517 | depth=264, 518 | nb_dense_block=4, 519 | growth_rate=32, 520 | nb_filter=64, 521 | nb_layers_per_block=[6, 12, 64, 48], 522 | bottleneck=True, 523 | reduction=0.5, 524 | dropout_rate=0.0, 525 | weight_decay=1e-4, 526 | subsample_initial_block=True, 527 | activation_conv='crelu', 528 | pooling_func=['max', 'global_average'], 529 | include_top=True, 530 | classes=10, 531 | output_activation='softmax', 532 | *args, 533 | **kwargs 534 | ): 535 | super(DenseNet1D264, self).__init__( 536 | inputs, 537 | depth, 538 | nb_dense_block, 539 | growth_rate, 540 | nb_filter, 541 | nb_layers_per_block, 542 | bottleneck, 543 | reduction, 544 | dropout_rate, 545 | weight_decay, 546 | subsample_initial_block, 547 | activation_conv, 548 | pooling_func, 549 | include_top, 550 | classes, 551 | output_activation, 552 | *args, 553 | **kwargs 554 | ) 555 | 556 | 557 | class DenseNet1D161(DenseNet1D): 558 | """Constructs a `keras.models.Model` according to the DenseNet1D161 specifications. 559 | 560 | Args: 561 | inputs (keras tensor): e.g. an instance of `keras.layers.Input`. 562 | depth (int, optional): number of layers in the DenseNet. Defaults to 161. 563 | nb_dense_block (int, optional): number of dense blocks to add to end. Defaults to 4. 564 | growth_rate (int, optional): number of filters to add per dense block. Defaults to 32. 565 | nb_filter (int, optional): initial number of filters. -1 indicates initial 566 | number of filters is 2 * growth_rate. Defaults to 64. 567 | nb_layers_per_block (list, optional): number of layers in each dense block. 568 | Can be a -1, positive integer or a list. 569 | If -1, calculates nb_layer_per_block from the network depth. 570 | If positive integer, a set number of layers per dense block. 571 | If list, nb_layer is used as provided. Note that list size must 572 | be (nb_dense_block + 1). Defaults to [6, 12, 24, 16]. 573 | bottleneck (bool, optional): flag to add bottleneck blocks in between dense blocks. Defaults to True. 574 | reduction (float, optional): reduction factor of transition blocks. 575 | Note : reduction value is inverted to compute compression. Defaults to 0.5. 576 | dropout_rate (float, optional): dropout rate. Defaults to 0.0. 577 | weight_decay (float, optional): weight decay rate. Defaults to 1e-4. 578 | subsample_initial_block (bool, optional): Set to True to subsample the initial convolution and 579 | add a Pool1D layer before the dense blocks are added. Defaults to True. 580 | activation_conv (str, optional): Type of activation of conv layers in the blocks. Defaults to 'crelu'. 581 | pooling_func (list, optional): Type of pooling layers. Defaults to ['max', 'global_average']. 582 | include_top (bool, optional): whether to include the fully-connected 583 | layer at the top of the network. Defaults to True. 584 | classes (int, optional): number of classes to classify, only to be specified if `include_top` is True. 585 | Defaults to 10. 586 | output_activation (str, optional): Type of activation at the top layer. Defaults to 'softmax'. 587 | Usage: 588 | >>> from complex_networks_keras_tf1.models.densenet_models_1d import DenseNet1D161 589 | >>> from keras.layers import Input 590 | >>> id1, od = 128, 3 591 | >>> inputs = Input(shape=(id1, 2)) 592 | >>> model_densenet = DenseNet1D161( 593 | inputs, 594 | depthh=161, 595 | nb_dense_block=4, 596 | growth_rate=13, 597 | nb_filter=64, 598 | nb_layers_per_block=[6, 12, 36, 24], 599 | bottleneck=True, 600 | reduction=0.5, 601 | dropout_rate=0.0, 602 | weight_decay=1e-4, 603 | subsample_initial_block=True, 604 | classes=od, 605 | output_activation='sigmoid') 606 | >>> print(model_densenet.summary()) 607 | """ 608 | 609 | def __init__(self, 610 | inputs, 611 | depth=161, 612 | nb_dense_block=4, 613 | growth_rate=48, 614 | nb_filter=96, 615 | nb_layers_per_block=[6, 12, 36, 24], 616 | bottleneck=True, 617 | reduction=0.5, 618 | dropout_rate=0.0, 619 | weight_decay=1e-4, 620 | subsample_initial_block=True, 621 | activation_conv='crelu', 622 | pooling_func=['max', 'global_average'], 623 | include_top=True, 624 | classes=10, 625 | output_activation='softmax', 626 | *args, 627 | **kwargs 628 | ): 629 | super(DenseNet1D161, self).__init__( 630 | inputs, 631 | depth, 632 | nb_dense_block, 633 | growth_rate, 634 | nb_filter, 635 | nb_layers_per_block, 636 | bottleneck, 637 | reduction, 638 | dropout_rate, 639 | weight_decay, 640 | subsample_initial_block, 641 | activation_conv, 642 | pooling_func, 643 | include_top, 644 | classes, 645 | output_activation, 646 | *args, 647 | **kwargs 648 | ) 649 | --------------------------------------------------------------------------------