You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
384 lines
11 KiB
384 lines
11 KiB
# Copyright (c) 2020 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 division
|
|
from __future__ import print_function
|
|
|
|
import math
|
|
import paddle.fluid as fluid
|
|
|
|
from paddle.fluid.dygraph.nn import Conv2D, Pool2D, BatchNorm, Linear
|
|
from paddle.fluid.dygraph.container import Sequential
|
|
|
|
from paddle.utils.download import get_weights_path_from_url
|
|
|
|
__all__ = [
|
|
'ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152'
|
|
]
|
|
|
|
model_urls = {
|
|
'resnet18': ('https://paddle-hapi.bj.bcebos.com/models/resnet18.pdparams',
|
|
'0ba53eea9bc970962d0ef96f7b94057e'),
|
|
'resnet34': ('https://paddle-hapi.bj.bcebos.com/models/resnet34.pdparams',
|
|
'46bc9f7c3dd2e55b7866285bee91eff3'),
|
|
'resnet50': ('https://paddle-hapi.bj.bcebos.com/models/resnet50.pdparams',
|
|
'5ce890a9ad386df17cf7fe2313dca0a1'),
|
|
'resnet101': ('https://paddle-hapi.bj.bcebos.com/models/resnet101.pdparams',
|
|
'fb07a451df331e4b0bb861ed97c3a9b9'),
|
|
'resnet152': ('https://paddle-hapi.bj.bcebos.com/models/resnet152.pdparams',
|
|
'f9c700f26d3644bb76ad2226ed5f5713'),
|
|
}
|
|
|
|
|
|
class ConvBNLayer(fluid.dygraph.Layer):
|
|
def __init__(self,
|
|
num_channels,
|
|
num_filters,
|
|
filter_size,
|
|
stride=1,
|
|
groups=1,
|
|
act=None):
|
|
super(ConvBNLayer, self).__init__()
|
|
|
|
self._conv = Conv2D(
|
|
num_channels=num_channels,
|
|
num_filters=num_filters,
|
|
filter_size=filter_size,
|
|
stride=stride,
|
|
padding=(filter_size - 1) // 2,
|
|
groups=groups,
|
|
act=None,
|
|
bias_attr=False)
|
|
|
|
self._batch_norm = BatchNorm(num_filters, act=act)
|
|
|
|
def forward(self, inputs):
|
|
x = self._conv(inputs)
|
|
x = self._batch_norm(x)
|
|
|
|
return x
|
|
|
|
|
|
class BasicBlock(fluid.dygraph.Layer):
|
|
"""residual block of resnet18 and resnet34
|
|
"""
|
|
expansion = 1
|
|
|
|
def __init__(self, num_channels, num_filters, stride, shortcut=True):
|
|
super(BasicBlock, self).__init__()
|
|
|
|
self.conv0 = ConvBNLayer(
|
|
num_channels=num_channels,
|
|
num_filters=num_filters,
|
|
filter_size=3,
|
|
act='relu')
|
|
self.conv1 = ConvBNLayer(
|
|
num_channels=num_filters,
|
|
num_filters=num_filters,
|
|
filter_size=3,
|
|
stride=stride,
|
|
act='relu')
|
|
|
|
if not shortcut:
|
|
self.short = ConvBNLayer(
|
|
num_channels=num_channels,
|
|
num_filters=num_filters,
|
|
filter_size=1,
|
|
stride=stride)
|
|
|
|
self.shortcut = shortcut
|
|
|
|
def forward(self, inputs):
|
|
y = self.conv0(inputs)
|
|
conv1 = self.conv1(y)
|
|
|
|
if self.shortcut:
|
|
short = inputs
|
|
else:
|
|
short = self.short(inputs)
|
|
|
|
y = short + conv1
|
|
|
|
return fluid.layers.relu(y)
|
|
|
|
|
|
class BottleneckBlock(fluid.dygraph.Layer):
|
|
"""residual block of resnet50, resnet101 amd resnet152
|
|
"""
|
|
|
|
expansion = 4
|
|
|
|
def __init__(self, num_channels, num_filters, stride, shortcut=True):
|
|
super(BottleneckBlock, self).__init__()
|
|
|
|
self.conv0 = ConvBNLayer(
|
|
num_channels=num_channels,
|
|
num_filters=num_filters,
|
|
filter_size=1,
|
|
act='relu')
|
|
self.conv1 = ConvBNLayer(
|
|
num_channels=num_filters,
|
|
num_filters=num_filters,
|
|
filter_size=3,
|
|
stride=stride,
|
|
act='relu')
|
|
self.conv2 = ConvBNLayer(
|
|
num_channels=num_filters,
|
|
num_filters=num_filters * self.expansion,
|
|
filter_size=1,
|
|
act=None)
|
|
|
|
if not shortcut:
|
|
self.short = ConvBNLayer(
|
|
num_channels=num_channels,
|
|
num_filters=num_filters * self.expansion,
|
|
filter_size=1,
|
|
stride=stride)
|
|
|
|
self.shortcut = shortcut
|
|
|
|
self._num_channels_out = num_filters * self.expansion
|
|
|
|
def forward(self, inputs):
|
|
x = self.conv0(inputs)
|
|
conv1 = self.conv1(x)
|
|
conv2 = self.conv2(conv1)
|
|
|
|
if self.shortcut:
|
|
short = inputs
|
|
else:
|
|
short = self.short(inputs)
|
|
|
|
x = fluid.layers.elementwise_add(x=short, y=conv2)
|
|
|
|
return fluid.layers.relu(x)
|
|
|
|
|
|
class ResNet(fluid.dygraph.Layer):
|
|
"""ResNet model from
|
|
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
|
|
|
Args:
|
|
Block (BasicBlock|BottleneckBlock): block module of model.
|
|
depth (int): layers of resnet, default: 50.
|
|
num_classes (int): output dim of last fc layer. If num_classes <=0, last fc layer
|
|
will not be defined. Default: 1000.
|
|
with_pool (bool): use pool before the last fc layer or not. Default: True.
|
|
classifier_activation (str): activation for the last fc layer. Default: 'softmax'.
|
|
|
|
Examples:
|
|
.. code-block:: python
|
|
|
|
from paddle.vision.models import ResNet
|
|
from paddle.vision.models.resnet import BottleneckBlock, BasicBlock
|
|
|
|
resnet50 = ResNet(BottleneckBlock, 50)
|
|
|
|
resnet18 = ResNet(BasicBlock, 18)
|
|
|
|
"""
|
|
|
|
def __init__(self,
|
|
Block,
|
|
depth=50,
|
|
num_classes=1000,
|
|
with_pool=True,
|
|
classifier_activation='softmax'):
|
|
super(ResNet, self).__init__()
|
|
|
|
self.num_classes = num_classes
|
|
self.with_pool = with_pool
|
|
|
|
layer_config = {
|
|
18: [2, 2, 2, 2],
|
|
34: [3, 4, 6, 3],
|
|
50: [3, 4, 6, 3],
|
|
101: [3, 4, 23, 3],
|
|
152: [3, 8, 36, 3],
|
|
}
|
|
assert depth in layer_config.keys(), \
|
|
"supported depth are {} but input layer is {}".format(
|
|
layer_config.keys(), depth)
|
|
|
|
layers = layer_config[depth]
|
|
|
|
in_channels = 64
|
|
out_channels = [64, 128, 256, 512]
|
|
|
|
self.conv = ConvBNLayer(
|
|
num_channels=3, num_filters=64, filter_size=7, stride=2, act='relu')
|
|
self.pool = Pool2D(
|
|
pool_size=3, pool_stride=2, pool_padding=1, pool_type='max')
|
|
|
|
self.layers = []
|
|
for idx, num_blocks in enumerate(layers):
|
|
blocks = []
|
|
shortcut = False
|
|
for b in range(num_blocks):
|
|
if b == 1:
|
|
in_channels = out_channels[idx] * Block.expansion
|
|
block = Block(
|
|
num_channels=in_channels,
|
|
num_filters=out_channels[idx],
|
|
stride=2 if b == 0 and idx != 0 else 1,
|
|
shortcut=shortcut)
|
|
blocks.append(block)
|
|
shortcut = True
|
|
layer = self.add_sublayer("layer_{}".format(idx),
|
|
Sequential(*blocks))
|
|
self.layers.append(layer)
|
|
|
|
if with_pool:
|
|
self.global_pool = Pool2D(
|
|
pool_size=7, pool_type='avg', global_pooling=True)
|
|
|
|
if num_classes > 0:
|
|
stdv = 1.0 / math.sqrt(out_channels[-1] * Block.expansion * 1.0)
|
|
self.fc_input_dim = out_channels[-1] * Block.expansion * 1 * 1
|
|
self.fc = Linear(
|
|
self.fc_input_dim,
|
|
num_classes,
|
|
act=classifier_activation,
|
|
param_attr=fluid.param_attr.ParamAttr(
|
|
initializer=fluid.initializer.Uniform(-stdv, stdv)))
|
|
|
|
def forward(self, inputs):
|
|
x = self.conv(inputs)
|
|
x = self.pool(x)
|
|
for layer in self.layers:
|
|
x = layer(x)
|
|
|
|
if self.with_pool:
|
|
x = self.global_pool(x)
|
|
|
|
if self.num_classes > -1:
|
|
x = fluid.layers.reshape(x, shape=[-1, self.fc_input_dim])
|
|
x = self.fc(x)
|
|
return x
|
|
|
|
|
|
def _resnet(arch, Block, depth, pretrained, **kwargs):
|
|
model = ResNet(Block, depth, **kwargs)
|
|
if pretrained:
|
|
assert arch in model_urls, "{} model do not have a pretrained model now, you should set pretrained=False".format(
|
|
arch)
|
|
weight_path = get_weights_path_from_url(model_urls[arch][0],
|
|
model_urls[arch][1])
|
|
assert weight_path.endswith(
|
|
'.pdparams'), "suffix of weight must be .pdparams"
|
|
param, _ = fluid.load_dygraph(weight_path)
|
|
model.set_dict(param)
|
|
|
|
return model
|
|
|
|
|
|
def resnet18(pretrained=False, **kwargs):
|
|
"""ResNet 18-layer model
|
|
|
|
Args:
|
|
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
|
|
|
Examples:
|
|
.. code-block:: python
|
|
|
|
from paddle.vision.models import resnet18
|
|
|
|
# build model
|
|
model = resnet18()
|
|
|
|
# build model and load imagenet pretrained weight
|
|
# model = resnet18(pretrained=True)
|
|
"""
|
|
return _resnet('resnet18', BasicBlock, 18, pretrained, **kwargs)
|
|
|
|
|
|
def resnet34(pretrained=False, **kwargs):
|
|
"""ResNet 34-layer model
|
|
|
|
Args:
|
|
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
|
|
|
Examples:
|
|
.. code-block:: python
|
|
|
|
from paddle.vision.models import resnet34
|
|
|
|
# build model
|
|
model = resnet34()
|
|
|
|
# build model and load imagenet pretrained weight
|
|
# model = resnet34(pretrained=True)
|
|
"""
|
|
return _resnet('resnet34', BasicBlock, 34, pretrained, **kwargs)
|
|
|
|
|
|
def resnet50(pretrained=False, **kwargs):
|
|
"""ResNet 50-layer model
|
|
|
|
Args:
|
|
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
|
|
|
Examples:
|
|
.. code-block:: python
|
|
|
|
from paddle.vision.models import resnet50
|
|
|
|
# build model
|
|
model = resnet50()
|
|
|
|
# build model and load imagenet pretrained weight
|
|
# model = resnet50(pretrained=True)
|
|
"""
|
|
return _resnet('resnet50', BottleneckBlock, 50, pretrained, **kwargs)
|
|
|
|
|
|
def resnet101(pretrained=False, **kwargs):
|
|
"""ResNet 101-layer model
|
|
|
|
Args:
|
|
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
|
|
|
Examples:
|
|
.. code-block:: python
|
|
|
|
from paddle.vision.models import resnet101
|
|
|
|
# build model
|
|
model = resnet101()
|
|
|
|
# build model and load imagenet pretrained weight
|
|
# model = resnet101(pretrained=True)
|
|
"""
|
|
return _resnet('resnet101', BottleneckBlock, 101, pretrained, **kwargs)
|
|
|
|
|
|
def resnet152(pretrained=False, **kwargs):
|
|
"""ResNet 152-layer model
|
|
|
|
Args:
|
|
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
|
|
|
Examples:
|
|
.. code-block:: python
|
|
|
|
from paddle.vision.models import resnet152
|
|
|
|
# build model
|
|
model = resnet152()
|
|
|
|
# build model and load imagenet pretrained weight
|
|
# model = resnet152(pretrained=True)
|
|
"""
|
|
return _resnet('resnet152', BottleneckBlock, 152, pretrained, **kwargs)
|