mobilenet.py 7.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 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

23
from ppdet.experimental import mixed_precision_global_state
24 25 26 27 28 29 30 31 32 33 34 35 36
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
37
        conv_decay (float): weight decay for convolution layer weights.
38 39 40 41
        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
    """
42
    __shared__ = ['norm_type', 'weight_prefix_name']
43 44 45 46

    def __init__(self,
                 norm_type='bn',
                 norm_decay=0.,
47
                 conv_decay=0.,
48
                 conv_group_scale=1,
49
                 conv_learning_rate=1.0,
50 51
                 with_extra_blocks=False,
                 extra_block_filters=[[256, 512], [128, 256], [128, 256],
52 53
                                      [64, 128]],
                 weight_prefix_name=''):
54 55
        self.norm_type = norm_type
        self.norm_decay = norm_decay
56
        self.conv_decay = conv_decay
57
        self.conv_group_scale = conv_group_scale
58
        self.conv_learning_rate = conv_learning_rate
59 60
        self.with_extra_blocks = with_extra_blocks
        self.extra_block_filters = extra_block_filters
61
        self.prefix_name = weight_prefix_name
62 63 64 65 66 67 68 69 70 71 72 73

    def _conv_norm(self,
                   input,
                   filter_size,
                   num_filters,
                   stride,
                   padding,
                   num_groups=1,
                   act='relu',
                   use_cudnn=True,
                   name=None):
        parameter_attr = ParamAttr(
74
            learning_rate=self.conv_learning_rate,
75
            initializer=fluid.initializer.MSRA(),
76
            regularizer=L2Decay(self.conv_decay),
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
            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):
112
        mixed_precision_enabled = mixed_precision_global_state() is not None
113 114 115 116 117 118 119
        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),
120
            use_cudnn=mixed_precision_enabled,
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
            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,
142
            num_filters=int(num_filters1),
143
            stride=1,
144
            num_groups=int(num_groups),
145
            padding=0,
146
            act='relu6',
147 148 149 150
            name=name + "_extra1")
        normal_conv = self._conv_norm(
            input=pointwise_conv,
            filter_size=3,
151
            num_filters=int(num_filters2),
152
            stride=2,
153
            num_groups=int(num_groups),
154
            padding=1,
155
            act='relu6',
156 157 158 159 160 161 162 163
            name=name + "_extra2")
        return normal_conv

    def __call__(self, input):
        scale = self.conv_group_scale

        blocks = []
        # input 1/1
164 165
        out = self._conv_norm(
            input, 3, int(32 * scale), 2, 1, name=self.prefix_name + "conv1")
166 167
        # 1/2
        out = self.depthwise_separable(
168
            out, 32, 64, 32, 1, scale, name=self.prefix_name + "conv2_1")
169
        out = self.depthwise_separable(
170
            out, 64, 128, 64, 2, scale, name=self.prefix_name + "conv2_2")
171 172
        # 1/4
        out = self.depthwise_separable(
173
            out, 128, 128, 128, 1, scale, name=self.prefix_name + "conv3_1")
174
        out = self.depthwise_separable(
175
            out, 128, 256, 128, 2, scale, name=self.prefix_name + "conv3_2")
176 177 178
        # 1/8
        blocks.append(out)
        out = self.depthwise_separable(
179
            out, 256, 256, 256, 1, scale, name=self.prefix_name + "conv4_1")
180
        out = self.depthwise_separable(
181
            out, 256, 512, 256, 2, scale, name=self.prefix_name + "conv4_2")
182 183 184 185
        # 1/16
        blocks.append(out)
        for i in range(5):
            out = self.depthwise_separable(
186 187 188 189 190 191 192
                out,
                512,
                512,
                512,
                1,
                scale,
                name=self.prefix_name + "conv5_" + str(i + 1))
193 194 195
        module11 = out

        out = self.depthwise_separable(
196
            out, 512, 1024, 512, 2, scale, name=self.prefix_name + "conv5_6")
197 198
        # 1/32
        out = self.depthwise_separable(
199
            out, 1024, 1024, 1024, 1, scale, name=self.prefix_name + "conv6")
200 201 202 203 204 205 206
        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],
207 208
                                     num_filters[0][1], 1, 2,
                                     self.prefix_name + "conv7_1")
209
        module15 = self._extra_block(module14, num_filters[1][0],
210 211
                                     num_filters[1][1], 1, 2,
                                     self.prefix_name + "conv7_2")
212
        module16 = self._extra_block(module15, num_filters[2][0],
213 214
                                     num_filters[2][1], 1, 2,
                                     self.prefix_name + "conv7_3")
215
        module17 = self._extra_block(module16, num_filters[3][0],
216 217
                                     num_filters[3][1], 1, 2,
                                     self.prefix_name + "conv7_4")
218
        return module11, module13, module14, module15, module16, module17