# copyright (c) 2021 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. # This code is refer from: https://github.com/PaddlePaddle/PaddleClas/blob/develop/ppcls/arch/backbone/legendary_models/pp_lcnet.py from __future__ import absolute_import from __future__ import division from __future__ import print_function import math import numpy as np import paddle from paddle import ParamAttr, reshape, transpose import paddle.nn as nn import paddle.nn.functional as F from paddle.nn import Conv2D, BatchNorm, Linear, Dropout from paddle.nn import AdaptiveAvgPool2D, MaxPool2D, AvgPool2D from paddle.nn.initializer import KaimingNormal from paddle.regularizer import L2Decay from paddle.nn.functional import hardswish, hardsigmoid class ConvBNLayer(nn.Layer): def __init__(self, num_channels, filter_size, num_filters, stride, padding, channels=None, num_groups=1, act='hard_swish'): super(ConvBNLayer, self).__init__() self._conv = Conv2D( in_channels=num_channels, out_channels=num_filters, kernel_size=filter_size, stride=stride, padding=padding, groups=num_groups, weight_attr=ParamAttr(initializer=KaimingNormal()), bias_attr=False) self._batch_norm = BatchNorm( num_filters, act=act, param_attr=ParamAttr(regularizer=L2Decay(0.0)), bias_attr=ParamAttr(regularizer=L2Decay(0.0))) def forward(self, inputs): y = self._conv(inputs) y = self._batch_norm(y) return y class DepthwiseSeparable(nn.Layer): def __init__(self, num_channels, num_filters1, num_filters2, num_groups, stride, scale, dw_size=3, padding=1, use_se=False): super(DepthwiseSeparable, self).__init__() self.use_se = use_se self._depthwise_conv = ConvBNLayer( num_channels=num_channels, num_filters=int(num_filters1 * scale), filter_size=dw_size, stride=stride, padding=padding, num_groups=int(num_groups * scale)) if use_se: self._se = SEModule(int(num_filters1 * scale)) self._pointwise_conv = ConvBNLayer( num_channels=int(num_filters1 * scale), filter_size=1, num_filters=int(num_filters2 * scale), stride=1, padding=0) def forward(self, inputs): y = self._depthwise_conv(inputs) if self.use_se: y = self._se(y) y = self._pointwise_conv(y) return y class MobileNetV1Enhance(nn.Layer): def __init__(self, in_channels=3, scale=0.5, **kwargs): super().__init__() self.scale = scale self.block_list = [] self.conv1 = ConvBNLayer( num_channels=3, filter_size=3, channels=3, num_filters=int(32 * scale), stride=2, padding=1) conv2_1 = DepthwiseSeparable( num_channels=int(32 * scale), num_filters1=32, num_filters2=64, num_groups=32, stride=1, scale=scale) self.block_list.append(conv2_1) conv2_2 = DepthwiseSeparable( num_channels=int(64 * scale), num_filters1=64, num_filters2=128, num_groups=64, stride=1, scale=scale) self.block_list.append(conv2_2) conv3_1 = DepthwiseSeparable( num_channels=int(128 * scale), num_filters1=128, num_filters2=128, num_groups=128, stride=1, scale=scale) self.block_list.append(conv3_1) conv3_2 = DepthwiseSeparable( num_channels=int(128 * scale), num_filters1=128, num_filters2=256, num_groups=128, stride=(2, 1), scale=scale) self.block_list.append(conv3_2) conv4_1 = DepthwiseSeparable( num_channels=int(256 * scale), num_filters1=256, num_filters2=256, num_groups=256, stride=1, scale=scale) self.block_list.append(conv4_1) conv4_2 = DepthwiseSeparable( num_channels=int(256 * scale), num_filters1=256, num_filters2=512, num_groups=256, stride=(2, 1), scale=scale) self.block_list.append(conv4_2) for _ in range(5): conv5 = DepthwiseSeparable( num_channels=int(512 * scale), num_filters1=512, num_filters2=512, num_groups=512, stride=1, dw_size=5, padding=2, scale=scale, use_se=False) self.block_list.append(conv5) conv5_6 = DepthwiseSeparable( num_channels=int(512 * scale), num_filters1=512, num_filters2=1024, num_groups=512, stride=(2, 1), dw_size=5, padding=2, scale=scale, use_se=True) self.block_list.append(conv5_6) conv6 = DepthwiseSeparable( num_channels=int(1024 * scale), num_filters1=1024, num_filters2=1024, num_groups=1024, stride=1, dw_size=5, padding=2, use_se=True, scale=scale) self.block_list.append(conv6) self.block_list = nn.Sequential(*self.block_list) self.pool = nn.MaxPool2D(kernel_size=2, stride=2, padding=0) self.out_channels = int(1024 * scale) def forward(self, inputs): y = self.conv1(inputs) y = self.block_list(y) y = self.pool(y) return y class SEModule(nn.Layer): def __init__(self, channel, reduction=4): super(SEModule, self).__init__() self.avg_pool = AdaptiveAvgPool2D(1) self.conv1 = Conv2D( in_channels=channel, out_channels=channel // reduction, kernel_size=1, stride=1, padding=0, weight_attr=ParamAttr(), bias_attr=ParamAttr()) self.conv2 = Conv2D( in_channels=channel // reduction, out_channels=channel, kernel_size=1, stride=1, padding=0, weight_attr=ParamAttr(), bias_attr=ParamAttr()) def forward(self, inputs): outputs = self.avg_pool(inputs) outputs = self.conv1(outputs) outputs = F.relu(outputs) outputs = self.conv2(outputs) outputs = hardsigmoid(outputs) return paddle.multiply(x=inputs, y=outputs)