def DarknetDepthwiseConv2D(*args, **kwargs): """Wrapper to set Darknet parameters for YoloDepthwiseConv2D.""" #darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)} #darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same' darknet_conv_kwargs = {'padding': 'valid' if kwargs.get('strides')==(2,2) else 'same'} darknet_conv_kwargs.update(kwargs) return YoloDepthwiseConv2D(*args, **darknet_conv_kwargs)
def cheap_operations(x, output_filters, kernel_size, strides=(1,1), padding='same', act=True, use_bias=False, name=None): x = YoloDepthwiseConv2D(kernel_size=kernel_size, strides=strides, padding=padding, use_bias=use_bias, name=name+'_0')(x) x = CustomBatchNormalization(name=name+'_1')(x) x = ReLU(name=name+'_relu')(x) if act else x return x
def GhostBottleneck(input_x, mid_chs, out_chs, dw_kernel_size=3, stride=(1,1), se_ratio=0., name=None): '''ghostnet bottleneck w/optional se''' has_se = se_ratio is not None and se_ratio > 0. #1st ghost bottleneck x = GhostModule(input_x, mid_chs, act=True, name=name+'_ghost1') #depth_with convolution if stride[0] > 1: x = YoloDepthwiseConv2D(kernel_size=dw_kernel_size, strides=stride, padding='same', use_bias=False, name=name+'_conv_dw')(x) x = CustomBatchNormalization(name=name+'_bn_dw')(x) #Squeeze_and_excitation if has_se: x = SqueezeExcite(x, se_ratio=se_ratio, name=name+'_se') #2nd ghost bottleneck x = GhostModule(x, out_chs, act=False, name=name+'_ghost2') #short cut if (input_x.shape[-1] == out_chs and stride[0] == 1): sc = input_x else: name1 = name + '_shortcut' sc = YoloDepthwiseConv2D(kernel_size=dw_kernel_size, strides=stride, padding='same', use_bias=False, name=name1+'_0')(input_x) sc = CustomBatchNormalization(name=name1+'_1')(sc) sc = YoloConv2D(filters=out_chs, kernel_size=1, strides=(1,1), padding='valid', use_bias=False, name=name1+'_2')(sc) sc = CustomBatchNormalization(name=name1+'_3')(sc) x = Add(name=name+'_add')([x, sc]) return x
def _inverted_res_block(inputs, expansion, stride, alpha, filters, block_id): channel_axis = 1 if K.image_data_format() == 'channels_first' else -1 in_channels = K.int_shape(inputs)[channel_axis] pointwise_conv_filters = int(filters * alpha) pointwise_filters = _make_divisible(pointwise_conv_filters, 8) x = inputs prefix = 'block_{}_'.format(block_id) if block_id: # Expand x = YoloConv2D(expansion * in_channels, kernel_size=1, padding='same', use_bias=False, activation=None, name=prefix + 'expand')(x) x = CustomBatchNormalization(axis=channel_axis, epsilon=1e-3, momentum=0.999, name=prefix + 'expand_BN')(x) x = ReLU(6., name=prefix + 'expand_relu')(x) else: prefix = 'expanded_conv_' # Depthwise if stride == 2: x = ZeroPadding2D(padding=correct_pad(K, x, 3), name=prefix + 'pad')(x) x = YoloDepthwiseConv2D(kernel_size=3, strides=stride, activation=None, use_bias=False, padding='same' if stride == 1 else 'valid', name=prefix + 'depthwise')(x) x = CustomBatchNormalization(axis=channel_axis, epsilon=1e-3, momentum=0.999, name=prefix + 'depthwise_BN')(x) x = ReLU(6., name=prefix + 'depthwise_relu')(x) # Project x = YoloConv2D(pointwise_filters, kernel_size=1, padding='same', use_bias=False, activation=None, name=prefix + 'project')(x) x = CustomBatchNormalization(axis=channel_axis, epsilon=1e-3, momentum=0.999, name=prefix + 'project_BN')(x) if in_channels == pointwise_filters and stride == 1: return Add(name=prefix + 'add')([inputs, x]) return x
def Depthwise_Conv2D_BN_Leaky(kernel_size=(3, 3), block_id_str=None): """Depthwise Convolution2D.""" if not block_id_str: block_id_str = str(K.get_uid()) return compose( YoloDepthwiseConv2D(kernel_size, padding='same', name='conv_dw_' + block_id_str), CustomBatchNormalization(name='conv_dw_%s_bn' % block_id_str), LeakyReLU(alpha=0.1, name='conv_dw_%s_leaky_relu' % block_id_str))
def Depthwise_Separable_Conv2D_BN_Leaky(filters, kernel_size=(3, 3), block_id_str=None): """Depthwise Separable Convolution2D.""" if not block_id_str: block_id_str = str(K.get_uid()) return compose( YoloDepthwiseConv2D(kernel_size, padding='same', name='conv_dw_' + block_id_str), CustomBatchNormalization(name='conv_dw_%s_bn' % block_id_str), LeakyReLU(alpha=0.1, name='conv_dw_%s_leaky_relu' % block_id_str), YoloConv2D(filters, (1,1), padding='same', use_bias=False, strides=(1, 1), name='conv_pw_%s' % block_id_str), CustomBatchNormalization(name='conv_pw_%s_bn' % block_id_str), LeakyReLU(alpha=0.1, name='conv_pw_%s_leaky_relu' % block_id_str))
def _inverted_res_block(x, expansion, filters, kernel_size, stride, se_ratio, activation, block_id): channel_axis = 1 if K.image_data_format() == 'channels_first' else -1 shortcut = x prefix = 'expanded_conv/' infilters = K.int_shape(x)[channel_axis] if block_id: # Expand prefix = 'expanded_conv_{}/'.format(block_id) x = YoloConv2D(_depth(infilters * expansion), kernel_size=1, padding='same', use_bias=False, name=prefix + 'expand')(x) x = CustomBatchNormalization(axis=channel_axis, epsilon=1e-3, momentum=0.999, name=prefix + 'expand/BatchNorm')(x) x = Activation(activation)(x) if stride == 2: x = ZeroPadding2D(padding=correct_pad(K, x, kernel_size), name=prefix + 'depthwise/pad')(x) x = YoloDepthwiseConv2D(kernel_size, strides=stride, padding='same' if stride == 1 else 'valid', use_bias=False, name=prefix + 'depthwise/Conv')(x) x = CustomBatchNormalization(axis=channel_axis, epsilon=1e-3, momentum=0.999, name=prefix + 'depthwise/BatchNorm')(x) x = Activation(activation)(x) if se_ratio: x = _se_block(x, _depth(infilters * expansion), se_ratio, prefix) x = YoloConv2D(filters, kernel_size=1, padding='same', use_bias=False, name=prefix + 'project')(x) x = CustomBatchNormalization(axis=channel_axis, epsilon=1e-3, momentum=0.999, name=prefix + 'project/BatchNorm')(x) if stride == 1 and infilters == filters: x = Add(name=prefix + 'Add')([shortcut, x]) return x
def _ep_block(inputs, filters, stride, expansion, block_id): #in_channels = backend.int_shape(inputs)[-1] in_channels = inputs.shape.as_list()[-1] pointwise_conv_filters = int(filters) x = inputs prefix = 'ep_block_{}_'.format(block_id) # Expand x = YoloConv2D(int(expansion * in_channels), kernel_size=1, padding='same', use_bias=False, activation=None, name=prefix + 'expand')(x) x = CustomBatchNormalization(epsilon=1e-3, momentum=0.999, name=prefix + 'expand_BN')(x) x = ReLU(6., name=prefix + 'expand_relu')(x) # Depthwise if stride == 2: x = ZeroPadding2D(padding=correct_pad(K, x, 3), name=prefix + 'pad')(x) x = YoloDepthwiseConv2D(kernel_size=3, strides=stride, activation=None, use_bias=False, padding='same' if stride == 1 else 'valid', name=prefix + 'depthwise')(x) x = CustomBatchNormalization(epsilon=1e-3, momentum=0.999, name=prefix + 'depthwise_BN')(x) x = ReLU(6., name=prefix + 'depthwise_relu')(x) # Project x = YoloConv2D(pointwise_conv_filters, kernel_size=1, padding='same', use_bias=False, activation=None, name=prefix + 'project')(x) x = CustomBatchNormalization(epsilon=1e-3, momentum=0.999, name=prefix + 'project_BN')(x) if in_channels == pointwise_conv_filters and stride == 1: return Add(name=prefix + 'add')([inputs, x]) return x
def _shuffle_unit(inputs, in_channels, out_channels, groups, bottleneck_ratio, strides=2, stage=1, block=1): """ creates a shuffleunit Parameters ---------- inputs: Input tensor of with `channels_last` data format in_channels: number of input channels out_channels: number of output channels strides: An integer or tuple/list of 2 integers, specifying the strides of the convolution along the width and height. groups: int(1) number of groups per channel bottleneck_ratio: float bottleneck ratio implies the ratio of bottleneck channels to output channels. For example, bottleneck ratio = 1 : 4 means the output feature map is 4 times the width of the bottleneck feature map. stage: int(1) stage number block: int(1) block number Returns ------- """ if K.image_data_format() == 'channels_last': bn_axis = -1 else: bn_axis = 1 prefix = 'stage%d/block%d' % (stage, block) #if strides >= 2: #out_channels -= in_channels # default: 1/4 of the output channel of a ShuffleNet Unit bottleneck_channels = int(out_channels * bottleneck_ratio) groups = (1 if stage == 2 and block == 1 else groups) x = _group_conv(inputs, in_channels, out_channels=bottleneck_channels, groups=(1 if stage == 2 and block == 1 else groups), name='%s/1x1_gconv_1' % prefix) x = CustomBatchNormalization(axis=bn_axis, name='%s/bn_gconv_1' % prefix)(x) x = Activation('relu', name='%s/relu_gconv_1' % prefix)(x) x = Lambda(channel_shuffle, arguments={'groups': groups}, name='%s/channel_shuffle' % prefix)(x) x = YoloDepthwiseConv2D(kernel_size=(3, 3), padding="same", use_bias=False, strides=strides, name='%s/1x1_dwconv_1' % prefix)(x) x = CustomBatchNormalization(axis=bn_axis, name='%s/bn_dwconv_1' % prefix)(x) x = _group_conv( x, bottleneck_channels, out_channels=out_channels if strides == 1 else out_channels - in_channels, groups=groups, name='%s/1x1_gconv_2' % prefix) x = CustomBatchNormalization(axis=bn_axis, name='%s/bn_gconv_2' % prefix)(x) if strides < 2: ret = Add(name='%s/add' % prefix)([x, inputs]) else: avg = AveragePooling2D(pool_size=3, strides=2, padding='same', name='%s/avg_pool' % prefix)(inputs) ret = Concatenate(bn_axis, name='%s/concat' % prefix)([x, avg]) ret = Activation('relu', name='%s/relu_out' % prefix)(ret) return ret
def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha, depth_multiplier=1, strides=(1, 1), block_id=1): """Adds a depthwise convolution block. A depthwise convolution block consists of a depthwise conv, batch normalization, relu6, pointwise convolution, batch normalization and relu6 activation. # Arguments inputs: Input tensor of shape `(rows, cols, channels)` (with `channels_last` data format) or (channels, rows, cols) (with `channels_first` data format). pointwise_conv_filters: Integer, the dimensionality of the output space (i.e. the number of output filters in the pointwise convolution). alpha: controls the width of the network. - If `alpha` < 1.0, proportionally decreases the number of filters in each layer. - If `alpha` > 1.0, proportionally increases the number of filters in each layer. - If `alpha` = 1, default number of filters from the paper are used at each layer. depth_multiplier: The number of depthwise convolution output channels for each input channel. The total number of depthwise convolution output channels will be equal to `filters_in * depth_multiplier`. strides: An integer or tuple/list of 2 integers, specifying the strides of the convolution along the width and height. Can be a single integer to specify the same value for all spatial dimensions. Specifying any stride value != 1 is incompatible with specifying any `dilation_rate` value != 1. block_id: Integer, a unique identification designating the block number. # Input shape 4D tensor with shape: `(batch, channels, rows, cols)` if data_format='channels_first' or 4D tensor with shape: `(batch, rows, cols, channels)` if data_format='channels_last'. # Output shape 4D tensor with shape: `(batch, filters, new_rows, new_cols)` if data_format='channels_first' or 4D tensor with shape: `(batch, new_rows, new_cols, filters)` if data_format='channels_last'. `rows` and `cols` values might have changed due to stride. # Returns Output tensor of block. """ channel_axis = 1 if K.image_data_format() == 'channels_first' else -1 pointwise_conv_filters = int(pointwise_conv_filters * alpha) if strides == (1, 1): x = inputs else: x = ZeroPadding2D(((0, 1), (0, 1)), name='conv_pad_%d' % block_id)(inputs) x = YoloDepthwiseConv2D((3, 3), padding='same' if strides == (1, 1) else 'valid', depth_multiplier=depth_multiplier, strides=strides, use_bias=False, name='conv_dw_%d' % block_id)(x) x = CustomBatchNormalization(axis=channel_axis, name='conv_dw_%d_bn' % block_id)(x) x = ReLU(6., name='conv_dw_%d_relu' % block_id)(x) x = YoloConv2D(pointwise_conv_filters, (1, 1), padding='same', use_bias=False, strides=(1, 1), name='conv_pw_%d' % block_id)(x) x = CustomBatchNormalization(axis=channel_axis, name='conv_pw_%d_bn' % block_id)(x) return ReLU(6., name='conv_pw_%d_relu' % block_id)(x)
def block(inputs, activation_fn=swish, drop_rate=0., name='', filters_in=32, filters_out=16, kernel_size=3, strides=1, expand_ratio=1, se_ratio=0., id_skip=True): """A mobile inverted residual block. # Arguments inputs: input tensor. activation_fn: activation function. drop_rate: float between 0 and 1, fraction of the input units to drop. name: string, block label. filters_in: integer, the number of input filters. filters_out: integer, the number of output filters. kernel_size: integer, the dimension of the convolution window. strides: integer, the stride of the convolution. expand_ratio: integer, scaling coefficient for the input filters. se_ratio: float between 0 and 1, fraction to squeeze the input filters. id_skip: boolean. # Returns output tensor for the block. """ bn_axis = 3 if K.image_data_format() == 'channels_last' else 1 # Expansion phase filters = filters_in * expand_ratio if expand_ratio != 1: x = YoloConv2D(filters, 1, padding='same', use_bias=False, kernel_initializer=CONV_KERNEL_INITIALIZER, name=name + 'expand_conv')(inputs) x = CustomBatchNormalization(axis=bn_axis, name=name + 'expand_bn')(x) x = Activation(activation_fn, name=name + 'expand_activation')(x) else: x = inputs # Depthwise Convolution if strides == 2: x = ZeroPadding2D(padding=correct_pad(K, x, kernel_size), name=name + 'dwconv_pad')(x) conv_pad = 'valid' else: conv_pad = 'same' x = YoloDepthwiseConv2D(kernel_size, strides=strides, padding=conv_pad, use_bias=False, depthwise_initializer=CONV_KERNEL_INITIALIZER, name=name + 'dwconv')(x) x = CustomBatchNormalization(axis=bn_axis, name=name + 'bn')(x) x = Activation(activation_fn, name=name + 'activation')(x) # Squeeze and Excitation phase if 0 < se_ratio <= 1: filters_se = max(1, int(filters_in * se_ratio)) se = GlobalAveragePooling2D(name=name + 'se_squeeze')(x) se = Reshape((1, 1, filters), name=name + 'se_reshape')(se) se = YoloConv2D(filters_se, 1, padding='same', activation=activation_fn, kernel_initializer=CONV_KERNEL_INITIALIZER, name=name + 'se_reduce')(se) se = YoloConv2D(filters, 1, padding='same', activation='sigmoid', kernel_initializer=CONV_KERNEL_INITIALIZER, name=name + 'se_expand')(se) if K.backend() == 'theano': # For the Theano backend, we have to explicitly make # the excitation weights broadcastable. se = Lambda( lambda x: K.pattern_broadcast(x, [True, True, True, False]), output_shape=lambda input_shape: input_shape, name=name + 'se_broadcast')(se) x = multiply([x, se], name=name + 'se_excite') # Output phase x = YoloConv2D(filters_out, 1, padding='same', use_bias=False, kernel_initializer=CONV_KERNEL_INITIALIZER, name=name + 'project_conv')(x) x = CustomBatchNormalization(axis=bn_axis, name=name + 'project_bn')(x) if (id_skip is True and strides == 1 and filters_in == filters_out): if drop_rate > 0: if tf2.enabled(): x = Dropout(drop_rate, noise_shape=(None, 1, 1, 1), name=name + 'drop')(x) else: x = Dropout( drop_rate, #noise_shape=(None, 1, 1, 1), name=name + 'drop')(x) x = add([x, inputs], name=name + 'add') return x
def shuffle_unit(inputs, out_channels, bottleneck_ratio, strides=2, stage=1, block=1): if K.image_data_format() == 'channels_last': bn_axis = -1 else: raise ValueError('Only channels last supported') prefix = 'stage{}/block{}'.format(stage, block) bottleneck_channels = int(out_channels * bottleneck_ratio) if strides < 2: c_hat, c = channel_split(inputs, '{}/spl'.format(prefix)) inputs = c x = YoloConv2D(bottleneck_channels, kernel_size=(1, 1), strides=1, padding='same', name='{}/1x1conv_1'.format(prefix))(inputs) x = CustomBatchNormalization(axis=bn_axis, name='{}/bn_1x1conv_1'.format(prefix))(x) x = Activation('relu', name='{}/relu_1x1conv_1'.format(prefix))(x) x = YoloDepthwiseConv2D(kernel_size=3, strides=strides, padding='same', name='{}/3x3dwconv'.format(prefix))(x) x = CustomBatchNormalization(axis=bn_axis, name='{}/bn_3x3dwconv'.format(prefix))(x) x = YoloConv2D(bottleneck_channels, kernel_size=1, strides=1, padding='same', name='{}/1x1conv_2'.format(prefix))(x) x = CustomBatchNormalization(axis=bn_axis, name='{}/bn_1x1conv_2'.format(prefix))(x) x = Activation('relu', name='{}/relu_1x1conv_2'.format(prefix))(x) if strides < 2: ret = Concatenate(axis=bn_axis, name='{}/concat_1'.format(prefix))([x, c_hat]) else: s2 = YoloDepthwiseConv2D(kernel_size=3, strides=2, padding='same', name='{}/3x3dwconv_2'.format(prefix))(inputs) s2 = CustomBatchNormalization( axis=bn_axis, name='{}/bn_3x3dwconv_2'.format(prefix))(s2) s2 = YoloConv2D(bottleneck_channels, kernel_size=1, strides=1, padding='same', name='{}/1x1_conv_3'.format(prefix))(s2) s2 = CustomBatchNormalization( axis=bn_axis, name='{}/bn_1x1conv_3'.format(prefix))(s2) s2 = Activation('relu', name='{}/relu_1x1conv_3'.format(prefix))(s2) ret = Concatenate(axis=bn_axis, name='{}/concat_2'.format(prefix))([x, s2]) ret = Lambda(channel_shuffle, name='{}/channel_shuffle'.format(prefix))(ret) return ret