# 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