mirror of
				https://github.com/PaddlePaddle/PaddleOCR.git
				synced 2025-11-04 03:39:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			268 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#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__ = [
 | 
						|
    'MobileNetV3', 'MobileNetV3_small_x0_35', 'MobileNetV3_small_x0_5',
 | 
						|
    'MobileNetV3_small_x0_75', 'MobileNetV3_small_x1_0',
 | 
						|
    'MobileNetV3_small_x1_25', 'MobileNetV3_large_x0_35',
 | 
						|
    'MobileNetV3_large_x0_5', 'MobileNetV3_large_x0_75',
 | 
						|
    'MobileNetV3_large_x1_0', 'MobileNetV3_large_x1_25'
 | 
						|
]
 | 
						|
 | 
						|
 | 
						|
class MobileNetV3():
 | 
						|
    def __init__(self, params):
 | 
						|
        self.scale = params.get("scale", 0.5)
 | 
						|
        model_name = params.get("model_name", "small")
 | 
						|
        large_stride = params.get("large_stride", [1, 2, 2, 2])
 | 
						|
        small_stride = params.get("small_stride", [2, 2, 2, 2])
 | 
						|
 | 
						|
        assert isinstance(large_stride, list), "large_stride type must " \
 | 
						|
            "be list but got {}".format(type(large_stride))
 | 
						|
        assert isinstance(small_stride, list), "small_stride type must " \
 | 
						|
            "be list but got {}".format(type(small_stride))
 | 
						|
        assert len(large_stride) == 4, "large_stride length must be " \
 | 
						|
            "4 but got {}".format(len(large_stride))
 | 
						|
        assert len(small_stride) == 4, "small_stride length must be " \
 | 
						|
            "4 but got {}".format(len(small_stride))
 | 
						|
 | 
						|
        self.inplanes = 16
 | 
						|
        if model_name == "large":
 | 
						|
            self.cfg = [
 | 
						|
                # k, exp, c,  se,     nl,  s,
 | 
						|
                [3, 16, 16, False, 'relu', large_stride[0]],
 | 
						|
                [3, 64, 24, False, 'relu', (large_stride[1], 1)],
 | 
						|
                [3, 72, 24, False, 'relu', 1],
 | 
						|
                [5, 72, 40, True, 'relu', (large_stride[2], 1)],
 | 
						|
                [5, 120, 40, True, 'relu', 1],
 | 
						|
                [5, 120, 40, True, 'relu', 1],
 | 
						|
                [3, 240, 80, False, 'hard_swish', 1],
 | 
						|
                [3, 200, 80, False, 'hard_swish', 1],
 | 
						|
                [3, 184, 80, False, 'hard_swish', 1],
 | 
						|
                [3, 184, 80, False, 'hard_swish', 1],
 | 
						|
                [3, 480, 112, True, 'hard_swish', 1],
 | 
						|
                [3, 672, 112, True, 'hard_swish', 1],
 | 
						|
                [5, 672, 160, True, 'hard_swish', (large_stride[3], 1)],
 | 
						|
                [5, 960, 160, True, 'hard_swish', 1],
 | 
						|
                [5, 960, 160, True, 'hard_swish', 1],
 | 
						|
            ]
 | 
						|
            self.cls_ch_squeeze = 960
 | 
						|
            self.cls_ch_expand = 1280
 | 
						|
        elif model_name == "small":
 | 
						|
            self.cfg = [
 | 
						|
                # k, exp, c,  se,     nl,  s,
 | 
						|
                [3, 16, 16, True, 'relu', (small_stride[0], 1)],
 | 
						|
                [3, 72, 24, False, 'relu', (small_stride[1], 1)],
 | 
						|
                [3, 88, 24, False, 'relu', 1],
 | 
						|
                [5, 96, 40, True, 'hard_swish', (small_stride[2], 1)],
 | 
						|
                [5, 240, 40, True, 'hard_swish', 1],
 | 
						|
                [5, 240, 40, True, 'hard_swish', 1],
 | 
						|
                [5, 120, 48, True, 'hard_swish', 1],
 | 
						|
                [5, 144, 48, True, 'hard_swish', 1],
 | 
						|
                [5, 288, 96, True, 'hard_swish', (small_stride[3], 1)],
 | 
						|
                [5, 576, 96, True, 'hard_swish', 1],
 | 
						|
                [5, 576, 96, True, 'hard_swish', 1],
 | 
						|
            ]
 | 
						|
            self.cls_ch_squeeze = 576
 | 
						|
            self.cls_ch_expand = 1280
 | 
						|
        else:
 | 
						|
            raise NotImplementedError("mode[" + model_name +
 | 
						|
                                      "_model] is not implemented!")
 | 
						|
 | 
						|
        supported_scale = [0.35, 0.5, 0.75, 1.0, 1.25]
 | 
						|
        assert self.scale in supported_scale, \
 | 
						|
            "supported scales are {} but input scale is {}".format(supported_scale, self.scale)
 | 
						|
 | 
						|
    def __call__(self, input):
 | 
						|
        scale = self.scale
 | 
						|
        inplanes = self.inplanes
 | 
						|
        cfg = self.cfg
 | 
						|
        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')
 | 
						|
        i = 0
 | 
						|
        inplanes = self.make_divisible(inplanes * scale)
 | 
						|
        for layer_cfg in cfg:
 | 
						|
            conv = self.residual_unit(
 | 
						|
                input=conv,
 | 
						|
                num_in_filter=inplanes,
 | 
						|
                num_mid_filter=self.make_divisible(scale * layer_cfg[1]),
 | 
						|
                num_out_filter=self.make_divisible(scale * layer_cfg[2]),
 | 
						|
                act=layer_cfg[4],
 | 
						|
                stride=layer_cfg[5],
 | 
						|
                filter_size=layer_cfg[0],
 | 
						|
                use_se=layer_cfg[3],
 | 
						|
                name='conv' + str(i + 2))
 | 
						|
            inplanes = self.make_divisible(scale * layer_cfg[2])
 | 
						|
            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_size=2,
 | 
						|
            pool_stride=2,
 | 
						|
            pool_padding=0,
 | 
						|
            pool_type='max')
 | 
						|
        return conv
 | 
						|
 | 
						|
    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):
 | 
						|
 | 
						|
        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')
 | 
						|
 | 
						|
        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)
 |