разница между керасом и производительностью тензорного потока с помощью Adam Optimizer - PullRequest
0 голосов
/ 05 февраля 2020

Я пытаюсь преобразовать код Keras в тензор потока 1.14, и у меня возникают некоторые вопросы, когда я проверял распределение весов после 1 эпохи, используя набор данных игрушек, состоящий из пустых изображений.

Модель в основном U - Net но он использует частичные свертки для закрашивания неправильных отверстий. Когда я запускаю обе реализации с простыми данными, я получаю значительную разницу в выходных данных, поэтому я решил проверить каждый из весов. Хотя изначально они одинаковы, после одной эпохи основное различие проявляется в смещениях и гиперпараметрах периодической нормализации (бета и гамма). Как показано на следующих изображениях:

Частичный сверточный слой - смещение

Уровень нормализации партии - гамма

Уровень пакетной нормализации - бета

Вот краткое изложение соответствующих разделов обеих реализаций:

(1) Keras (реализация взята из здесь )

  • Слой частичной свертки

    from keras.utils import conv_utils
    from keras import backend as K
    from keras.engine import InputSpec
    from keras.layers import Conv2D
    from keras.initializers import glorot_uniform
    
    class PConv2D(Conv2D):
        def __init__(self, *args, n_channels=3, mono=False, **kwargs):
            super().__init__(*args, **kwargs)
            self.input_spec = [InputSpec(ndim=4), InputSpec(ndim=4)]
    
        def build(self, input_shape):
    
            if self.data_format == 'channels_first': channel_axis = 1
            else: channel_axis = -1
    
            if input_shape[0][channel_axis] is None: raise ValueError('The channel dimension of the inputs should be defined. Found `None`.')
    
            self.input_dim = input_shape[0][channel_axis]
    
            # Image kernel
            kernel_shape = self.kernel_size + (self.input_dim, self.filters)
            self.kernel = self.add_weight(shape=kernel_shape,
                                          initializer= glorot_uniform(seed=0),
                                          name='img_kernel',
                                          regularizer=self.kernel_regularizer,
                                          constraint=self.kernel_constraint)
            # Mask kernel
            self.kernel_mask = K.ones(shape=self.kernel_size + (self.input_dim, self.filters))
            # Calculate padding size to achieve zero-padding
            self.pconv_padding = (
        (int((self.kernel_size[0]-1)/2), int((self.kernel_size[0]-1)/2)),
        (int((self.kernel_size[0]-1)/2), int((self.kernel_size[0]-1)/2)),
        )
    
            # Window size - used for normalization
            self.window_size = self.kernel_size[0] * self.kernel_size[1]
    
            if self.use_bias:
                self.bias = self.add_weight(shape=(self.filters,),
                                    initializer=self.bias_initializer,
                                    name='bias',
                                    regularizer=self.bias_regularizer,
                                    constraint=self.bias_constraint)
            else:
                self.bias = None
    
            self.built = True
    
        def call(self, inputs, mask=None):
            # Both image and mask must be supplied
            if type(inputs) is not list or len(inputs) != 2:
                raise Exception('PartialConvolution2D must be called on a list of two tensors [img, mask]. Instead got: ' + str(inputs))
    
            # Padding done explicitly so that padding becomes part of the masked partial convolution
            images = K.spatial_2d_padding(inputs[0], self.pconv_padding, self.data_format)
            masks = K.spatial_2d_padding(inputs[1], self.pconv_padding, self.data_format)
    
            # Apply convolutions to mask
            mask_output = K.conv2d(masks, 
                                   self.kernel_mask, 
                                   strides=self.strides, 
                                   padding='valid', 
                                   data_format=self.data_format,
                                   dilation_rate=self.dilation_rate
                                   )
    
            # Apply convolutions to image
            img_output = K.conv2d((images*masks), self.kernel,
                                   strides=self.strides,
                                   padding='valid',
                                   data_format=self.data_format,
                                   dilation_rate=self.dilation_rate
                                  )
    
             # Calculate the mask ratio on each pixel in the output mask
             mask_ratio = self.window_size / (mask_output + 1e-8)
    
             # Clip output to be between 0 and 1
             mask_output = K.clip(mask_output, 0, 1)
    
             # Remove ratio values where there are holes
             mask_ratio = mask_ratio * mask_output
    
             # Normalize iamge output
             img_output = img_output * mask_ratio
    
             # Apply bias only to the image (if chosen to do so)
             if self.use_bias:
                 img_output = K.bias_add(img_output, self.bias,
                                         data_format=self.data_format)
    
             # Apply activations on the image
             if self.activation is not None:
                 img_output = self.activation(img_output)
    
             return [img_output, mask_output]
    
  • Интересующий сетевой блок

    def encoder_layer(img_in, mask_in, filters, kernel_size, bn=True):
        conv, mask = PConv2D(filters, kernel_size, strides=2, padding='same')([img_in, mask_in])
        if bn:
            conv = BatchNormalization(name='EncBN')(conv, training=train_bn)
            conv = Activation('relu')(conv)
    
        return conv, mask
    
  • Оптимизатор:

     optimizer = Adam(lr=0.0002)
    

(2) Tensorflow

  • Слой частичной свертки

     import tensorflow as tf
     import numpy as np
    
     def PConv2D(input, output_dim, kernel_size, stride, padding, use_bias=True):
         with tf.variable_scope('partial_conv'):
    
             _, h, w, input_dim = input[0].get_shape().as_list()
    
             # Window size - used for normalization
             window_size = kernel_size * kernel_size 
    
             images = input[0] 
             masks = input[1]  
    
             # Update mask
             update_mask = tf.layers.conv2d(inputs=masks,
                                   filters=output_dim,
                                   kernel_size=kernel_size,
                                   strides=(stride, stride),
                                   padding='same', #'valid'
                                   trainable=False,
                                   use_bias=False,
                                   kernel_initializer=tf.constant_initializer(1.0),
                                   name='up_mask')
    
             # Calculate the mask ratio on each pixel in the output mask
             mask_ratio = window_size / (update_mask + 1e-8)
    
             # Clip output to be between 0 and 1
             update_mask = tf.clip_by_value(update_mask, 0, 1)
             # Remove ratio values where there are holes
             mask_ratio = mask_ratio * update_mask
    
             # Inpaint image
    
             img_output = tf.layers.conv2d(inputs=images*masks, 
                                  filters=output_dim,
                                  kernel_size=kernel_size,
                                  strides=(stride, stride),
                                  padding='same', 
                                  use_bias=False,                                  
                            kernel_initializer=tf.contrib.layers.xavier_initializer(seed=0),
                                  name='img_out'
                                  )
    
             # Normalize image output
             img_output = img_output * mask_ratio
    
             # Apply bias only to the image (if chosen to do so)
             if use_bias:
                 bias = tf.get_variable("bias", [output_dim,], 
                 initializer=tf.constant_initializer(0.0))
                 img_output = tf.nn.bias_add(img_output, bias)
    
             return [img_output, update_mask]
    
  • Сеть блок интересов:

    def encoder_layer(img_in, mask_in, filters, kernel_size, name, training_bn, bn=True):
        with tf.variable_scope(name):
            conv, mask = PConv.PConv2D((img_in, mask_in), filters, kernel_size, 2, "same")
            if bn:
                conv = tf.layers.batch_normalization(inputs=conv,
                                            training=training_bn,
                                            trainable=True,
                                            fused=True,
                                            name='bn')
    
           conv = tf.nn.relu(conv, name='relu')
    
    
       return conv, mask
    
  • Оптимизатор:

    def optimize(loss, learning_rate=0.0002):
    
        global_step = tf.Variable(0, dtype=tf.int32, trainable=False, name='global_step')
    
        U_vars = [var for var in tf.trainable_variables() if 'UNET' in var.name]
    
        opt = tf.train.AdamOptimizer(learning_rate=learning_rate, epsilon=1e-07)
    
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
        train_opt = opt.minimize(loss, var_list=U_vars, global_step=global_step)
        train_opt = tf.group([train_opt, update_ops])
    
        return train_opt
    

Я проверил каждую часть своей реализации тензорного потока по сравнению с Keras Кроме того, функция потерь не должна быть проблемой. Я уже проверил ее поведение.

Что может испортить результаты? Я думаю, что это связано с моей реализацией, но я не могу понять, что это может быть.

Я буду очень благодарен, если кто-то может привести меня.

...