text: import tensorflow as tf
  from keras.applications.resnet50 import ResNet50
  from keras.layers import Input, GlobalAveragePooling2D, GlobalMaxPooling2D, AveragePooling2D, ZeroPadding2D, add, concatenate, Dense, Dropout, Activation, Flatten, GlobalAveragePooling2D, Conv2D, MaxPooling2D, BatchNormalization
  from keras.models import Model, Sequential
  from keras import backend as K
  import numpy as np
  import utils
  
  
  def weighted_binary_crossentropy(y_true, y_pred, weight=1.):
      y_true = K.clip(y_true, K.epsilon(), 1)
      y_pred = K.clip(y_pred, K.epsilon(), 1)
      logloss = -(y_true * K.log(y_pred) * weight + (1 - y_true) * K.log(1 - y_pred))
      return K.mean(logloss, axis=-1)
  
  
  def print_params(model):
      trainable_count = int(np.sum([K.count_params(p) for p in set(model.trainable_weights)]))
      non_trainable_count = int(np.sum([K.count_params(p) for p in set(model.non_trainable_weights)]))
  
      print('Total params: {:,}'.format(trainable_count + non_trainable_count))
      print('Trainable params: {:,}'.format(trainable_count))
      print('Non-trainable params: {:,}'.format(non_trainable_count))
  
  
  def compile_model(model, soft_sigmoid=True):
  
      model.compile(optimizer='adam', loss=['categorical_crossentropy'], metrics=['accuracy'])
      return model
  
  keras_layer_names = []
  
  
  def rgb_create_model(n_classes, model_name='resnet50', freeze_all=True, conv_fusion=False):
      # TODO Make this multi-GPU
      with tf.device('/gpu:0'):
          if model_name == "resnet50":
              # base_model = ResNet50(include_top=False, weights='imagenet', pooling=None, input_shape=(224, 224, 3))
              base_model = resnet50_rgb(input_shape=(224, 224, 3), pooling=None)
          elif model_name == "inceptionv3":
              # NOTE Weights upon model building are imagenet but kinetics weights are loaded, see rgb.py
              base_model = InceptionV3_TSN(include_top=False, weights='imagenet', input_shape=(224, 224, 3), pooling=None)
  
          if conv_fusion is True:
              x = base_model.layers[-2].output
              x = Conv2D(64, (2, 2), name='ConvFusion')(x)
          else:
              x = base_model.output
          x = GlobalAveragePooling2D()(x)
          x = Dropout(0.75)(x)
          x = Dense(1024, activation='relu', kernel_initializer='he_uniform')(x)
  
          # Freeze layers
          if freeze_all is True:
              for layer in base_model.layers:
                  layer.trainable = False
          else:
              if model_name == "resnet50":  # I want to train the 37 last layers to train my last conv section as well as the FC's at the end
                  # Layers in the model :179
                  # 179-37 = 142
                  for layer in base_model.layers:
                      layer.trainable = True
  #                for layer in base_model.layers[100:]:
  #                    layer.trainable = True
  #
          model = None
          pred = Dense(n_classes, activation='softmax', name='pred')(x)
          model = Model(inputs=base_model.input, output=pred)
          print_params(model)
          # print model.summary()
          print("Total number of layers in base model: " + str(len(base_model.layers)))
          print("Total number of layers in model: " + str(len(model.layers)))
      return model, keras_layer_names
  
  
  def conv2d_bn(x, filters, num_row, num_col, padding='same', strides=(1, 1), name=None):
      if name is not None:
          bn_name = name + '_bn'
          conv_name = name + '_conv'
      else:
          bn_name = None
          conv_name = None
  
      bn_axis = 3
      # NOTE use_bias had to be set to True here to match TSN
      x = Conv2D(filters, (num_row, num_col), strides=strides, padding=padding, use_bias=True, name=conv_name)(x)
      keras_layer_names.append(x.name.split("/")[0])
      # NOTE scale had to be set to True here to match TSN
      x = BatchNormalization(axis=bn_axis, scale=True, name=bn_name)(x)
      keras_layer_names.append(x.name.split("/")[0])
      x = Activation('relu', name=name)(x)
      return x
  
  
  def InceptionV3_TSN(include_top=True, weights='imagenet', input_tensor=None, input_shape=None, pooling=None, classes=1000):
  
      # Determine proper input shape
  
      img_input = Input(shape=input_shape)
      channel_axis = 3
      x = conv2d_bn(img_input, 32, 3, 3, strides=(2, 2), padding='valid')
      x = conv2d_bn(x, 32, 3, 3, padding='valid')
      x = conv2d_bn(x, 64, 3, 3)
      x = MaxPooling2D((3, 3), strides=(2, 2))(x)
  
      x = conv2d_bn(x, 80, 1, 1, padding='valid')
      x = conv2d_bn(x, 192, 3, 3, padding='valid')
      x = MaxPooling2D((3, 3), strides=(2, 2))(x)
  
      # mixed 0, 1, 2: 35 x 35 x 256
      branch1x1 = conv2d_bn(x, 64, 1, 1)
  
      branch5x5 = conv2d_bn(x, 48, 1, 1)
      branch5x5 = conv2d_bn(branch5x5, 64, 5, 5)
  
      branch3x3dbl = conv2d_bn(x, 64, 1, 1)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3)
  
      branch_pool = AveragePooling2D((3, 3), strides=(1, 1), padding='same')(x)
      branch_pool = conv2d_bn(branch_pool, 32, 1, 1)
      x = concatenate([branch1x1, branch5x5, branch3x3dbl, branch_pool], axis=channel_axis, name='mixed0')
  
      # mixed 1: 35 x 35 x 256
      branch1x1 = conv2d_bn(x, 64, 1, 1)
  
      branch5x5 = conv2d_bn(x, 48, 1, 1)
      branch5x5 = conv2d_bn(branch5x5, 64, 5, 5)
  
      branch3x3dbl = conv2d_bn(x, 64, 1, 1)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3)
  
      branch_pool = AveragePooling2D((3, 3), strides=(1, 1), padding='same')(x)
      branch_pool = conv2d_bn(branch_pool, 64, 1, 1)
      x = concatenate([branch1x1, branch5x5, branch3x3dbl, branch_pool], axis=channel_axis, name='mixed1')
  
      # mixed 2: 35 x 35 x 256
      branch1x1 = conv2d_bn(x, 64, 1, 1)
  
      branch5x5 = conv2d_bn(x, 48, 1, 1)
      branch5x5 = conv2d_bn(branch5x5, 64, 5, 5)
  
      branch3x3dbl = conv2d_bn(x, 64, 1, 1)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3)
  
      branch_pool = AveragePooling2D((3, 3), strides=(1, 1), padding='same')(x)
      branch_pool = conv2d_bn(branch_pool, 64, 1, 1)
      x = concatenate([branch1x1, branch5x5, branch3x3dbl, branch_pool], axis=channel_axis, name='mixed2')
  
      # mixed 3: 17 x 17 x 768
      branch3x3 = conv2d_bn(x, 384, 3, 3, strides=(2, 2), padding='valid')
  
      branch3x3dbl = conv2d_bn(x, 64, 1, 1)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3)
      branch3x3dbl = conv2d_bn(branch3x3dbl, 96, 3, 3, strides=(2, 2), padding='valid')
  
      branch_pool = MaxPooling2D((3, 3), strides=(2, 2))(x)
      x = concatenate([branch3x3, branch3x3dbl, branch_pool], axis=channel_axis, name='mixed3')
  
      # mixed 4: 17 x 17 x 768
      branch1x1 = conv2d_bn(x, 192, 1, 1)
  
      branch7x7 = conv2d_bn(x, 128, 1, 1)
      branch7x7 = conv2d_bn(branch7x7, 128, 1, 7)
      branch7x7 = conv2d_bn(branch7x7, 192, 7, 1)
  
      branch7x7dbl = conv2d_bn(x, 128, 1, 1)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 128, 7, 1)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 128, 1, 7)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 128, 7, 1)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 192, 1, 7)
  
      branch_pool = AveragePooling2D((3, 3), strides=(1, 1), padding='same')(x)
      branch_pool = conv2d_bn(branch_pool, 192, 1, 1)
      x = concatenate([branch1x1, branch7x7, branch7x7dbl, branch_pool], axis=channel_axis, name='mixed4')
  
      # mixed 5, 6: 17 x 17 x 768
      for i in range(2):
          branch1x1 = conv2d_bn(x, 192, 1, 1)
  
          branch7x7 = conv2d_bn(x, 160, 1, 1)
          branch7x7 = conv2d_bn(branch7x7, 160, 1, 7)
          branch7x7 = conv2d_bn(branch7x7, 192, 7, 1)
  
          branch7x7dbl = conv2d_bn(x, 160, 1, 1)
          branch7x7dbl = conv2d_bn(branch7x7dbl, 160, 7, 1)
          branch7x7dbl = conv2d_bn(branch7x7dbl, 160, 1, 7)
          branch7x7dbl = conv2d_bn(branch7x7dbl, 160, 7, 1)
          branch7x7dbl = conv2d_bn(branch7x7dbl, 192, 1, 7)
  
          branch_pool = AveragePooling2D((3, 3), strides=(1, 1), padding='same')(x)
          branch_pool = conv2d_bn(branch_pool, 192, 1, 1)
          x = concatenate([branch1x1, branch7x7, branch7x7dbl, branch_pool], axis=channel_axis, name='mixed' + str(5 + i))
  
      # mixed 7: 17 x 17 x 768
      branch1x1 = conv2d_bn(x, 192, 1, 1)
  
      branch7x7 = conv2d_bn(x, 192, 1, 1)
      branch7x7 = conv2d_bn(branch7x7, 192, 1, 7)
      branch7x7 = conv2d_bn(branch7x7, 192, 7, 1)
  
      branch7x7dbl = conv2d_bn(x, 192, 1, 1)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 192, 7, 1)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 192, 1, 7)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 192, 7, 1)
      branch7x7dbl = conv2d_bn(branch7x7dbl, 192, 1, 7)
  
      branch_pool = AveragePooling2D((3, 3), strides=(1, 1), padding='same')(x)
      branch_pool = conv2d_bn(branch_pool, 192, 1, 1)
      x = concatenate([branch1x1, branch7x7, branch7x7dbl, branch_pool], axis=channel_axis, name='mixed7')
  
      # mixed 8: 8 x 8 x 1280
      branch3x3 = conv2d_bn(x, 192, 1, 1)
      branch3x3 = conv2d_bn(branch3x3, 320, 3, 3, strides=(2, 2), padding='valid')
  
      branch7x7x3 = conv2d_bn(x, 192, 1, 1)
      branch7x7x3 = conv2d_bn(branch7x7x3, 192, 1, 7)
      branch7x7x3 = conv2d_bn(branch7x7x3, 192, 7, 1)
      branch7x7x3 = conv2d_bn(branch7x7x3, 192, 3, 3, strides=(2, 2), padding='valid')
  
      branch_pool = MaxPooling2D((3, 3), strides=(2, 2))(x)
      x = concatenate([branch3x3, branch7x7x3, branch_pool], axis=channel_axis, name='mixed8')
  
      # mixed 9, 10: 8 x 8 x 2048
      for i in range(2):
          branch1x1 = conv2d_bn(x, 320, 1, 1)
  
          branch3x3 = conv2d_bn(x, 384, 1, 1)
          branch3x3_1 = conv2d_bn(branch3x3, 384, 1, 3)
          branch3x3_2 = conv2d_bn(branch3x3, 384, 3, 1)
          branch3x3 = concatenate([branch3x3_1, branch3x3_2], axis=channel_axis, name='mixed9_' + str(i))
  
          branch3x3dbl = conv2d_bn(x, 448, 1, 1)
          branch3x3dbl = conv2d_bn(branch3x3dbl, 384, 3, 3)
          branch3x3dbl_1 = conv2d_bn(branch3x3dbl, 384, 1, 3)
          branch3x3dbl_2 = conv2d_bn(branch3x3dbl, 384, 3, 1)
          branch3x3dbl = concatenate([branch3x3dbl_1, branch3x3dbl_2], axis=channel_axis)
  
          branch_pool = AveragePooling2D((3, 3), strides=(1, 1), padding='same')(x)
          branch_pool = conv2d_bn(branch_pool, 192, 1, 1)
          x = concatenate([branch1x1, branch3x3, branch3x3dbl, branch_pool], axis=channel_axis, name='mixed' + str(9 + i))
      if include_top:
          # Classification block
          x = GlobalAveragePooling2D(name='avg_pool')(x)
          x = Dense(classes, activation='softmax', name='predictions')(x)
      else:
          if pooling == 'avg':
              x = GlobalAveragePooling2D()(x)
          elif pooling == 'max':
              x = layers.GlobalMaxPooling2D()(x)
  
      model = Model(img_input, x, name='inception_v3')
  
      return model
  
  
  def identity_block(input_tensor, kernel_size, filters, stage, block):
      """The identity block is the block that has no conv layer at shortcut.
      # Arguments
          input_tensor: input tensor
          kernel_size: default 3, the kernel size of middle conv layer at main path
          filters: list of integers, the filters of 3 conv layer at main path
          stage: integer, current stage label, used for generating layer names
          block: 'a','b'..., current block label, used for generating layer names
      # Returns
          Output tensor for the block.
      """
      filters1, filters2, filters3 = filters
      bn_axis = 3
  
      conv_name_base = 'res' + str(stage) + block + '_branch'
      bn_name_base = 'bn' + str(stage) + block + '_branch'
  
      x = Conv2D(filters1, (1, 1), name=conv_name_base + '2a')(input_tensor)
      x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2a')(x)
      x = Activation('relu')(x)
  
      x = Conv2D(filters2, kernel_size,
                 padding='same', name=conv_name_base + '2b')(x)
      x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2b')(x)
      x = Activation('relu')(x)
  
      x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x)
      x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2c')(x)
  
      x = add([x, input_tensor])
      x = Activation('relu')(x)
      return x
  
  
  def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2)):
      """A block that has a conv layer at shortcut.
      # Arguments
          input_tensor: input tensor
          kernel_size: default 3, the kernel ...
  