diff --git a/paddleslim/models/__init__.py b/paddleslim/models/__init__.py index 2bbd931509cc6b28eda8ea948f65bc279295c9da..14ea9f3d15fa953f0c4dba47aee6bc45a0e1ee62 100644 --- a/paddleslim/models/__init__.py +++ b/paddleslim/models/__init__.py @@ -15,5 +15,5 @@ from __future__ import absolute_import from .util import image_classification from .slimfacenet import SlimFaceNet_A_x0_60, SlimFaceNet_B_x0_75, SlimFaceNet_C_x0_75 - +from .slim_mobilenet import SlimMobileNet_v1, SlimMobileNet_v2, SlimMobileNet_v3, SlimMobileNet_v4, SlimMobileNet_v5 __all__ = ["image_classification"] diff --git a/paddleslim/models/slim_mobilenet.py b/paddleslim/models/slim_mobilenet.py new file mode 100644 index 0000000000000000000000000000000000000000..b2e42bfb74f49419610c600203c8fc070b01518b --- /dev/null +++ b/paddleslim/models/slim_mobilenet.py @@ -0,0 +1,322 @@ +#copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. +# +#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 + +import paddle.fluid as fluid +from paddle.fluid.initializer import MSRA +from paddle.fluid.param_attr import ParamAttr + +__all__ = [ + 'SlimMobileNet_v1', 'SlimMobileNet_v2', 'SlimMobileNet_v3', + 'SlimMobileNet_v4', 'SlimMobileNet_v5' +] + + +class SlimMobileNet(): + def __init__(self, scale=1.0, model_name='large', token=[]): + assert len(token) >= 45 + self.kernel_size_lis = token[:20] + self.exp_lis = token[20:40] + self.depth_lis = token[40:45] + + self.scale = scale + self.inplanes = 16 + if model_name == "large": + self.cfg_channel = [16, 24, 40, 80, 112, 160] + self.cfg_stride = [1, 2, 2, 2, 1, 2] + self.cfg_se = [False, False, True, False, True, True] + self.cfg_act = [ + 'relu', 'relu', 'relu', 'hard_swish', 'hard_swish', + 'hard_swish' + ] + self.cls_ch_squeeze = 960 + self.cls_ch_expand = 1280 + else: + raise NotImplementedError("mode[" + model_name + + "_model] is not implemented!") + + def net(self, input, class_dim=1000): + scale = self.scale + inplanes = self.inplanes + + kernel_size_lis = self.kernel_size_lis + exp_lis = self.exp_lis + depth_lis = self.depth_lis + cfg_channel = self.cfg_channel + cfg_stride = self.cfg_stride + cfg_se = self.cfg_se + cfg_act = self.cfg_act + + cls_ch_squeeze = self.cls_ch_squeeze + cls_ch_expand = self.cls_ch_expand + #conv1 + conv = self.conv_bn_layer( + input, + filter_size=3, + num_filters=self.make_divisible(inplanes * scale), + stride=2, + padding=1, + num_groups=1, + if_act=True, + act='hard_swish', + name='conv1') + inplanes = self.make_divisible(inplanes * scale) + + #conv2 + num_mid_filter = self.make_divisible(scale * inplanes) + _num_out_filter = cfg_channel[0] + num_out_filter = self.make_divisible(scale * _num_out_filter) + conv = self.residual_unit( + input=conv, + num_in_filter=inplanes, + num_mid_filter=num_mid_filter, + num_out_filter=num_out_filter, + act=cfg_act[0], + stride=cfg_stride[0], + filter_size=3, + use_se=cfg_se[0], + name='conv2', + short=True) + inplanes = self.make_divisible(scale * cfg_channel[0]) + + i = 3 + for depth_id in range(len(depth_lis)): + for repeat_time in range(depth_lis[depth_id]): + num_mid_filter = self.make_divisible( + scale * _num_out_filter * + exp_lis[depth_id * 4 + repeat_time]) + _num_out_filter = cfg_channel[depth_id + 1] + num_out_filter = self.make_divisible(scale * _num_out_filter) + stride = cfg_stride[depth_id + 1] if repeat_time == 0 else 1 + conv = self.residual_unit( + input=conv, + num_in_filter=inplanes, + num_mid_filter=num_mid_filter, + num_out_filter=num_out_filter, + act=cfg_act[depth_id + 1], + stride=stride, + filter_size=kernel_size_lis[depth_id * 4 + repeat_time], + use_se=cfg_se[depth_id + 1], + name='conv' + str(i)) + + inplanes = self.make_divisible(scale * + cfg_channel[depth_id + 1]) + i += 1 + + conv = self.conv_bn_layer( + input=conv, + filter_size=1, + num_filters=self.make_divisible(scale * cls_ch_squeeze), + stride=1, + padding=0, + num_groups=1, + if_act=True, + act='hard_swish', + name='conv_last') + conv = fluid.layers.pool2d( + input=conv, pool_type='avg', global_pooling=True, use_cudnn=False) + conv = fluid.layers.conv2d( + input=conv, + num_filters=cls_ch_expand, + filter_size=1, + stride=1, + padding=0, + act=None, + param_attr=ParamAttr(name='last_1x1_conv_weights'), + bias_attr=False) + conv = fluid.layers.hard_swish(conv) + drop = fluid.layers.dropout(x=conv, dropout_prob=0.2) + out = fluid.layers.fc(input=drop, + size=class_dim, + param_attr=ParamAttr(name='fc_weights'), + bias_attr=ParamAttr(name='fc_offset')) + return out + + def conv_bn_layer(self, + input, + filter_size, + num_filters, + stride, + padding, + num_groups=1, + if_act=True, + act=None, + name=None, + use_cudnn=True, + res_last_bn_init=False): + 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=ParamAttr(name=name + '_weights'), + bias_attr=False) + bn_name = name + '_bn' + bn = fluid.layers.batch_norm( + input=conv, + param_attr=ParamAttr( + name=bn_name + "_scale", + regularizer=fluid.regularizer.L2DecayRegularizer( + regularization_coeff=0.0)), + bias_attr=ParamAttr( + name=bn_name + "_offset", + regularizer=fluid.regularizer.L2DecayRegularizer( + regularization_coeff=0.0)), + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance') + if if_act: + if act == 'relu': + bn = fluid.layers.relu(bn) + elif act == 'hard_swish': + bn = fluid.layers.hard_swish(bn) + return bn + + def make_divisible(self, v, divisor=8, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + if new_v < 0.9 * v: + new_v += divisor + return new_v + + def se_block(self, input, num_out_filter, ratio=4, name=None): + num_mid_filter = num_out_filter // ratio + pool = fluid.layers.pool2d( + input=input, pool_type='avg', global_pooling=True, use_cudnn=False) + conv1 = fluid.layers.conv2d( + input=pool, + filter_size=1, + num_filters=num_mid_filter, + act='relu', + param_attr=ParamAttr(name=name + '_1_weights'), + bias_attr=ParamAttr(name=name + '_1_offset')) + conv2 = fluid.layers.conv2d( + input=conv1, + filter_size=1, + num_filters=num_out_filter, + act='hard_sigmoid', + param_attr=ParamAttr(name=name + '_2_weights'), + bias_attr=ParamAttr(name=name + '_2_offset')) + scale = fluid.layers.elementwise_mul(x=input, y=conv2, axis=0) + return scale + + def residual_unit(self, + input, + num_in_filter, + num_mid_filter, + num_out_filter, + stride, + filter_size, + act=None, + use_se=False, + name=None, + short=False): + + if not short: + conv0 = self.conv_bn_layer( + input=input, + filter_size=1, + num_filters=num_mid_filter, + stride=1, + padding=0, + if_act=True, + act=act, + name=name + '_expand') + else: + conv0 = input + + conv1 = self.conv_bn_layer( + input=conv0, + filter_size=filter_size, + num_filters=num_mid_filter, + stride=stride, + padding=int((filter_size - 1) // 2), + if_act=True, + act=act, + num_groups=num_mid_filter, + use_cudnn=False, + name=name + '_depthwise') + if use_se: + conv1 = self.se_block( + input=conv1, num_out_filter=num_mid_filter, name=name + '_se') + + conv2 = self.conv_bn_layer( + input=conv1, + filter_size=1, + num_filters=num_out_filter, + stride=1, + padding=0, + if_act=False, + name=name + '_linear', + res_last_bn_init=True) + if num_in_filter != num_out_filter or stride != 1: + return conv2 + else: + return fluid.layers.elementwise_add(x=input, y=conv2, act=None) + + +def SlimMobileNet_v1(token): + token = [ + 5, 3, 3, 7, 3, 3, 5, 7, 3, 3, 3, 3, 3, 3, 7, 3, 5, 3, 3, 3, 3, 3, 3, 6, + 3, 3, 3, 3, 4, 4, 4, 6, 4, 3, 4, 3, 6, 4, 3, 3, 2, 2, 2, 2, 4 + ] + model = SlimMobileNet(model_name='large', scale=1.0, token=token) + return model + + +def SlimMobileNet_v2(token): + token = [ + 5, 3, 5, 7, 3, 3, 7, 3, 5, 3, 3, 7, 3, 3, 3, 5, 5, 5, 3, 3, 3, 3, 4, 6, + 3, 3, 6, 3, 4, 4, 3, 4, 4, 4, 3, 6, 6, 4, 3, 3, 2, 2, 3, 2, 4 + ] + model = SlimMobileNet(model_name='large', scale=1.0, token=token) + return model + + +def SlimMobileNet_v3(token): + token = [ + 3, 3, 3, 3, 5, 3, 7, 7, 7, 3, 3, 7, 5, 3, 5, 7, 5, 3, 3, 3, 3, 3, 3, 3, + 3, 4, 3, 4, 3, 6, 4, 4, 4, 4, 6, 3, 6, 4, 6, 3, 2, 2, 3, 2, 4 + ] + model = SlimMobileNet(model_name='large', scale=1.0, token=token) + return model + + +def SlimMobileNet_v4(token): + token = [ + 3, 3, 3, 3, 5, 3, 3, 5, 7, 3, 5, 5, 5, 3, 3, 7, 3, 5, 3, 3, 3, 3, 4, 6, + 3, 4, 4, 6, 4, 6, 4, 6, 4, 6, 4, 4, 6, 6, 6, 4, 2, 3, 3, 3, 4 + ] + model = SlimMobileNet(model_name='large', scale=1.0, token=token) + return model + + +def SlimMobileNet_v5(token): + token = [ + 7, 7, 3, 5, 7, 3, 5, 3, 7, 5, 3, 3, 5, 3, 7, 5, 7, 7, 5, 3, 3, 3, 6, 3, + 4, 6, 3, 6, 6, 3, 6, 4, 6, 6, 4, 3, 6, 6, 6, 6, 4, 4, 4, 4, 4 + ] + model = SlimMobileNet(model_name='large', scale=1.0, token=token) + return model + + +if __name__ == "__main__": + pass