Spaces:
Runtime error
Runtime error
# 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. | |
from __future__ import absolute_import, division, print_function | |
import os | |
import paddle | |
import paddle.nn as nn | |
from paddle import ParamAttr | |
from paddle.nn import AdaptiveAvgPool2D, BatchNorm, Conv2D, Dropout, Linear | |
from paddle.regularizer import L2Decay | |
from paddle.nn.initializer import KaimingNormal | |
from paddle.utils.download import get_path_from_url | |
MODEL_URLS = { | |
"PPLCNet_x0.25": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_25_pretrained.pdparams", | |
"PPLCNet_x0.35": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_35_pretrained.pdparams", | |
"PPLCNet_x0.5": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_5_pretrained.pdparams", | |
"PPLCNet_x0.75": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_75_pretrained.pdparams", | |
"PPLCNet_x1.0": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x1_0_pretrained.pdparams", | |
"PPLCNet_x1.5": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x1_5_pretrained.pdparams", | |
"PPLCNet_x2.0": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x2_0_pretrained.pdparams", | |
"PPLCNet_x2.5": | |
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x2_5_pretrained.pdparams" | |
} | |
MODEL_STAGES_PATTERN = { | |
"PPLCNet": ["blocks2", "blocks3", "blocks4", "blocks5", "blocks6"] | |
} | |
__all__ = list(MODEL_URLS.keys()) | |
# Each element(list) represents a depthwise block, which is composed of k, in_c, out_c, s, use_se. | |
# k: kernel_size | |
# in_c: input channel number in depthwise block | |
# out_c: output channel number in depthwise block | |
# s: stride in depthwise block | |
# use_se: whether to use SE block | |
NET_CONFIG = { | |
"blocks2": | |
# k, in_c, out_c, s, use_se | |
[[3, 16, 32, 1, False]], | |
"blocks3": [[3, 32, 64, 2, False], [3, 64, 64, 1, False]], | |
"blocks4": [[3, 64, 128, 2, False], [3, 128, 128, 1, False]], | |
"blocks5": | |
[[3, 128, 256, 2, False], [5, 256, 256, 1, False], [5, 256, 256, 1, False], | |
[5, 256, 256, 1, False], [5, 256, 256, 1, False], [5, 256, 256, 1, False]], | |
"blocks6": [[5, 256, 512, 2, True], [5, 512, 512, 1, True]] | |
} | |
def make_divisible(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 | |
class ConvBNLayer(nn.Layer): | |
def __init__(self, | |
num_channels, | |
filter_size, | |
num_filters, | |
stride, | |
num_groups=1): | |
super().__init__() | |
self.conv = Conv2D( | |
in_channels=num_channels, | |
out_channels=num_filters, | |
kernel_size=filter_size, | |
stride=stride, | |
padding=(filter_size - 1) // 2, | |
groups=num_groups, | |
weight_attr=ParamAttr(initializer=KaimingNormal()), | |
bias_attr=False) | |
self.bn = BatchNorm( | |
num_filters, | |
param_attr=ParamAttr(regularizer=L2Decay(0.0)), | |
bias_attr=ParamAttr(regularizer=L2Decay(0.0))) | |
self.hardswish = nn.Hardswish() | |
def forward(self, x): | |
x = self.conv(x) | |
x = self.bn(x) | |
x = self.hardswish(x) | |
return x | |
class DepthwiseSeparable(nn.Layer): | |
def __init__(self, | |
num_channels, | |
num_filters, | |
stride, | |
dw_size=3, | |
use_se=False): | |
super().__init__() | |
self.use_se = use_se | |
self.dw_conv = ConvBNLayer( | |
num_channels=num_channels, | |
num_filters=num_channels, | |
filter_size=dw_size, | |
stride=stride, | |
num_groups=num_channels) | |
if use_se: | |
self.se = SEModule(num_channels) | |
self.pw_conv = ConvBNLayer( | |
num_channels=num_channels, | |
filter_size=1, | |
num_filters=num_filters, | |
stride=1) | |
def forward(self, x): | |
x = self.dw_conv(x) | |
if self.use_se: | |
x = self.se(x) | |
x = self.pw_conv(x) | |
return x | |
class SEModule(nn.Layer): | |
def __init__(self, channel, reduction=4): | |
super().__init__() | |
self.avg_pool = AdaptiveAvgPool2D(1) | |
self.conv1 = Conv2D( | |
in_channels=channel, | |
out_channels=channel // reduction, | |
kernel_size=1, | |
stride=1, | |
padding=0) | |
self.relu = nn.ReLU() | |
self.conv2 = Conv2D( | |
in_channels=channel // reduction, | |
out_channels=channel, | |
kernel_size=1, | |
stride=1, | |
padding=0) | |
self.hardsigmoid = nn.Hardsigmoid() | |
def forward(self, x): | |
identity = x | |
x = self.avg_pool(x) | |
x = self.conv1(x) | |
x = self.relu(x) | |
x = self.conv2(x) | |
x = self.hardsigmoid(x) | |
x = paddle.multiply(x=identity, y=x) | |
return x | |
class PPLCNet(nn.Layer): | |
def __init__(self, | |
in_channels=3, | |
scale=1.0, | |
pretrained=False, | |
use_ssld=False): | |
super().__init__() | |
self.out_channels = [ | |
int(NET_CONFIG["blocks3"][-1][2] * scale), | |
int(NET_CONFIG["blocks4"][-1][2] * scale), | |
int(NET_CONFIG["blocks5"][-1][2] * scale), | |
int(NET_CONFIG["blocks6"][-1][2] * scale) | |
] | |
self.scale = scale | |
self.conv1 = ConvBNLayer( | |
num_channels=in_channels, | |
filter_size=3, | |
num_filters=make_divisible(16 * scale), | |
stride=2) | |
self.blocks2 = nn.Sequential(* [ | |
DepthwiseSeparable( | |
num_channels=make_divisible(in_c * scale), | |
num_filters=make_divisible(out_c * scale), | |
dw_size=k, | |
stride=s, | |
use_se=se) | |
for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks2"]) | |
]) | |
self.blocks3 = nn.Sequential(* [ | |
DepthwiseSeparable( | |
num_channels=make_divisible(in_c * scale), | |
num_filters=make_divisible(out_c * scale), | |
dw_size=k, | |
stride=s, | |
use_se=se) | |
for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks3"]) | |
]) | |
self.blocks4 = nn.Sequential(* [ | |
DepthwiseSeparable( | |
num_channels=make_divisible(in_c * scale), | |
num_filters=make_divisible(out_c * scale), | |
dw_size=k, | |
stride=s, | |
use_se=se) | |
for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks4"]) | |
]) | |
self.blocks5 = nn.Sequential(* [ | |
DepthwiseSeparable( | |
num_channels=make_divisible(in_c * scale), | |
num_filters=make_divisible(out_c * scale), | |
dw_size=k, | |
stride=s, | |
use_se=se) | |
for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks5"]) | |
]) | |
self.blocks6 = nn.Sequential(* [ | |
DepthwiseSeparable( | |
num_channels=make_divisible(in_c * scale), | |
num_filters=make_divisible(out_c * scale), | |
dw_size=k, | |
stride=s, | |
use_se=se) | |
for i, (k, in_c, out_c, s, se) in enumerate(NET_CONFIG["blocks6"]) | |
]) | |
if pretrained: | |
self._load_pretrained( | |
MODEL_URLS['PPLCNet_x{}'.format(scale)], use_ssld=use_ssld) | |
def forward(self, x): | |
outs = [] | |
x = self.conv1(x) | |
x = self.blocks2(x) | |
x = self.blocks3(x) | |
outs.append(x) | |
x = self.blocks4(x) | |
outs.append(x) | |
x = self.blocks5(x) | |
outs.append(x) | |
x = self.blocks6(x) | |
outs.append(x) | |
return outs | |
def _load_pretrained(self, pretrained_url, use_ssld=False): | |
if use_ssld: | |
pretrained_url = pretrained_url.replace("_pretrained", | |
"_ssld_pretrained") | |
print(pretrained_url) | |
local_weight_path = get_path_from_url( | |
pretrained_url, os.path.expanduser("~/.paddleclas/weights")) | |
param_state_dict = paddle.load(local_weight_path) | |
self.set_dict(param_state_dict) | |
return | |