text: # Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  
  from paddle import fluid
  from paddle.fluid.param_attr import ParamAttr
  from paddle.fluid.regularizer import L2Decay
  
  from ppdet.core.workspace import register
  
  __all__ = ['MobileNet']
  
  
  @register
  class MobileNet(object):
      """
      MobileNet v1, see https://arxiv.org/abs/1704.04861
  
      Args:
          norm_type (str): normalization type, 'bn' and 'sync_bn' are supported
          norm_decay (float): weight decay for normalization layer weights
          conv_group_scale (int): scaling factor for convolution groups
          with_extra_blocks (bool): if extra blocks should be added
          extra_block_filters (list): number of filter for each extra block
      """
      __shared__ = ['norm_type', 'weight_prefix_name']
  
      def __init__(self,
                   norm_type='bn',
                   norm_decay=0.,
                   conv_group_scale=1,
                   conv_learning_rate=1.0,
                   with_extra_blocks=False,
                   extra_block_filters=[[256, 512], [128, 256], [128, 256],
                                        [64, 128]],
                   weight_prefix_name=''):
          self.norm_type = norm_type
          self.norm_decay = norm_decay
          self.conv_group_scale = conv_group_scale
          self.conv_learning_rate = conv_learning_rate
          self.with_extra_blocks = with_extra_blocks
          self.extra_block_filters = extra_block_filters
          self.prefix_name = weight_prefix_name
  
      def _conv_norm(self,
                     input,
                     filter_size,
                     num_filters,
                     stride,
                     padding,
                     num_groups=1,
                     act='relu',
                     use_cudnn=True,
                     name=None):
          parameter_attr = ParamAttr(
              learning_rate=self.conv_learning_rate,
              initializer=fluid.initializer.MSRA(),
              name=name + "_weights")
          conv = fluid.layers.conv2d(
              input=input,
              num_filters=num_filters,
              filter_size=filter_size,
              stride=stride,
              padding=padding,
              groups=num_groups,
              act=None,
              use_cudnn=use_cudnn,
              param_attr=parameter_attr,
              bias_attr=False)
  
          bn_name = name + "_bn"
          norm_decay = self.norm_decay
          bn_param_attr = ParamAttr(
              regularizer=L2Decay(norm_decay), name=bn_name + '_scale')
          bn_bias_attr = ParamAttr(
              regularizer=L2Decay(norm_decay), name=bn_name + '_offset')
          return fluid.layers.batch_norm(
              input=conv,
              act=act,
              param_attr=bn_param_attr,
              bias_attr=bn_bias_attr,
              moving_mean_name=bn_name + '_mean',
              moving_variance_name=bn_name + '_variance')
  
      def depthwise_separable(self,
                              input,
                              num_filters1,
                              num_filters2,
                              num_groups,
                              stride,
                              scale,
                              name=None):
          depthwise_conv = self._conv_norm(
              input=input,
              filter_size=3,
              num_filters=int(num_filters1 * scale),
              stride=stride,
              padding=1,
              num_groups=int(num_groups * scale),
              use_cudnn=False,
              name=name + "_dw")
  
          pointwise_conv = self._conv_norm(
              input=depthwise_conv,
              filter_size=1,
              num_filters=int(num_filters2 * scale),
              stride=1,
              padding=0,
              name=name + "_sep")
          return pointwise_conv
  
      def _extra_block(self,
                       input,
                       num_filters1,
                       num_filters2,
                       num_groups,
                       stride,
                       name=None):
          pointwise_conv = self._conv_norm(
              input=input,
              filter_size=1,
              num_filters=int(num_filters1),
              stride=1,
              num_groups=int(num_groups),
              padding=0,
              name=name + "_extra1")
          normal_conv = self._conv_norm(
              input=pointwise_conv,
              filter_size=3,
              num_filters=int(num_filters2),
              stride=2,
              num_groups=int(num_groups),
              padding=1,
              name=name + "_extra2")
          return normal_conv
  
      def __call__(self, input):
          scale = self.conv_group_scale
  
          blocks = []
          # input 1/1
          out = self._conv_norm(
              input, 3, int(32 * scale), 2, 1, name=self.prefix_name + "conv1")
          # 1/2
          out = self.depthwise_separable(
              out, 32, 64, 32, 1, scale, name=self.prefix_name + "conv2_1")
          out = self.depthwise_separable(
              out, 64, 128, 64, 2, scale, name=self.prefix_name + "conv2_2")
          # 1/4
          out = self.depthwise_separable(
              out, 128, 128, 128, 1, scale, name=self.prefix_name + "conv3_1")
          out = self.depthwise_separable(
              out, 128, 256, 128, 2, scale, name=self.prefix_name + "conv3_2")
          # 1/8
          blocks.append(out)
          out = self.depthwise_separable(
              out, 256, 256, 256, 1, scale, name=self.prefix_name + "conv4_1")
          out = self.depthwise_separable(
              out, 256, 512, 256, 2, scale, name=self.prefix_name + "conv4_2")
          # 1/16
          blocks.append(out)
          for i in range(5):
              out = self.depthwise_separable(
                  out,
                  512,
                  512,
                  512,
                  1,
                  scale,
                  name=self.prefix_name + "conv5_" + str(i + 1))
          module11 = out
  
          out = self.depthwise_separable(
              out, 512, 1024, 512, 2, scale, name=self.prefix_name + "conv5_6")
          # 1/32
          out = self.depthwise_separable(
              out, 1024, 1024, 1024, 1, scale, name=self.prefix_name + "conv6")
          module13 = out
          blocks.append(out)
          if not self.with_extra_blocks:
              return blocks
  
          num_filters = self.extra_block_filters
          module14 = self._extra_block(module13, num_filters[0][0],
                                       num_filters[0][1], 1, 2,
                                       self.prefix_name + "conv7_1")
          module15 = self._extra_block(module14, num_filters[1][0],
                                       num_filters[1][1], 1, 2,
                                       self.prefix_name + "conv7_2")
          module16 = self._extra_block(module15, num_filters[2][0],
                                       num_filters[2][1], 1, 2,
                                       self.prefix_name + "conv7_3")
          module17 = self._extra_block(module16, num_filters[3][0],
                                       num_filters[3][1], 1, 2,
                                       self.prefix_name + "conv7_4")
          return module11, module13, module14, module15, module16, module17
  