resnet.py 10.6 KB
Newer Older
L
LielinJiang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# 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

L
LielinJiang 已提交
18 19
import paddle
import paddle.nn as nn
L
LielinJiang 已提交
20

21
from paddle.utils.download import get_weights_path_from_url
L
LielinJiang 已提交
22

23
__all__ = []
L
LielinJiang 已提交
24 25 26

model_urls = {
    'resnet18': ('https://paddle-hapi.bj.bcebos.com/models/resnet18.pdparams',
L
LielinJiang 已提交
27
                 'cf548f46534aa3560945be4b95cd11c4'),
L
LielinJiang 已提交
28
    'resnet34': ('https://paddle-hapi.bj.bcebos.com/models/resnet34.pdparams',
L
LielinJiang 已提交
29
                 '8d2275cf8706028345f78ac0e1d31969'),
L
LielinJiang 已提交
30
    'resnet50': ('https://paddle-hapi.bj.bcebos.com/models/resnet50.pdparams',
L
LielinJiang 已提交
31
                 'ca6f485ee1ab0492d38f323885b0ad80'),
L
LielinJiang 已提交
32
    'resnet101': ('https://paddle-hapi.bj.bcebos.com/models/resnet101.pdparams',
L
LielinJiang 已提交
33
                  '02f35f034ca3858e1e54d4036443c92d'),
L
LielinJiang 已提交
34
    'resnet152': ('https://paddle-hapi.bj.bcebos.com/models/resnet152.pdparams',
L
LielinJiang 已提交
35
                  '7ad16a2f1e7333859ff986138630fd7a'),
L
LielinJiang 已提交
36 37 38
}


L
LielinJiang 已提交
39 40 41
class BasicBlock(nn.Layer):
    expansion = 1

L
LielinJiang 已提交
42
    def __init__(self,
L
LielinJiang 已提交
43 44
                 inplanes,
                 planes,
L
LielinJiang 已提交
45
                 stride=1,
L
LielinJiang 已提交
46
                 downsample=None,
L
LielinJiang 已提交
47
                 groups=1,
L
LielinJiang 已提交
48 49 50
                 base_width=64,
                 dilation=1,
                 norm_layer=None):
L
LielinJiang 已提交
51
        super(BasicBlock, self).__init__()
L
LielinJiang 已提交
52
        if norm_layer is None:
C
cnn 已提交
53
            norm_layer = nn.BatchNorm2D
L
LielinJiang 已提交
54

L
LielinJiang 已提交
55 56 57
        if dilation > 1:
            raise NotImplementedError(
                "Dilation > 1 not supported in BasicBlock")
L
LielinJiang 已提交
58

C
cnn 已提交
59
        self.conv1 = nn.Conv2D(
L
LielinJiang 已提交
60 61 62
            inplanes, planes, 3, padding=1, stride=stride, bias_attr=False)
        self.bn1 = norm_layer(planes)
        self.relu = nn.ReLU()
C
cnn 已提交
63
        self.conv2 = nn.Conv2D(planes, planes, 3, padding=1, bias_attr=False)
L
LielinJiang 已提交
64 65 66
        self.bn2 = norm_layer(planes)
        self.downsample = downsample
        self.stride = stride
L
LielinJiang 已提交
67

L
LielinJiang 已提交
68 69
    def forward(self, x):
        identity = x
L
LielinJiang 已提交
70

L
LielinJiang 已提交
71 72 73
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
L
LielinJiang 已提交
74

L
LielinJiang 已提交
75 76
        out = self.conv2(out)
        out = self.bn2(out)
L
LielinJiang 已提交
77

L
LielinJiang 已提交
78 79
        if self.downsample is not None:
            identity = self.downsample(x)
L
LielinJiang 已提交
80

L
LielinJiang 已提交
81 82
        out += identity
        out = self.relu(out)
L
LielinJiang 已提交
83

L
LielinJiang 已提交
84
        return out
L
LielinJiang 已提交
85

L
LielinJiang 已提交
86 87

class BottleneckBlock(nn.Layer):
L
LielinJiang 已提交
88 89 90

    expansion = 4

L
LielinJiang 已提交
91 92 93 94 95 96 97 98 99
    def __init__(self,
                 inplanes,
                 planes,
                 stride=1,
                 downsample=None,
                 groups=1,
                 base_width=64,
                 dilation=1,
                 norm_layer=None):
L
LielinJiang 已提交
100
        super(BottleneckBlock, self).__init__()
L
LielinJiang 已提交
101
        if norm_layer is None:
C
cnn 已提交
102
            norm_layer = nn.BatchNorm2D
L
LielinJiang 已提交
103 104
        width = int(planes * (base_width / 64.)) * groups

C
cnn 已提交
105
        self.conv1 = nn.Conv2D(inplanes, width, 1, bias_attr=False)
L
LielinJiang 已提交
106 107
        self.bn1 = norm_layer(width)

C
cnn 已提交
108
        self.conv2 = nn.Conv2D(
L
LielinJiang 已提交
109 110 111 112
            width,
            width,
            3,
            padding=dilation,
L
LielinJiang 已提交
113
            stride=stride,
L
LielinJiang 已提交
114 115 116 117
            groups=groups,
            dilation=dilation,
            bias_attr=False)
        self.bn2 = norm_layer(width)
L
LielinJiang 已提交
118

C
cnn 已提交
119
        self.conv3 = nn.Conv2D(
L
LielinJiang 已提交
120 121 122 123 124
            width, planes * self.expansion, 1, bias_attr=False)
        self.bn3 = norm_layer(planes * self.expansion)
        self.relu = nn.ReLU()
        self.downsample = downsample
        self.stride = stride
L
LielinJiang 已提交
125

L
LielinJiang 已提交
126 127
    def forward(self, x):
        identity = x
L
LielinJiang 已提交
128

L
LielinJiang 已提交
129 130 131
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
L
LielinJiang 已提交
132

L
LielinJiang 已提交
133 134 135
        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)
L
LielinJiang 已提交
136

L
LielinJiang 已提交
137 138
        out = self.conv3(out)
        out = self.bn3(out)
L
LielinJiang 已提交
139

L
LielinJiang 已提交
140 141
        if self.downsample is not None:
            identity = self.downsample(x)
L
LielinJiang 已提交
142

L
LielinJiang 已提交
143 144
        out += identity
        out = self.relu(out)
L
LielinJiang 已提交
145

L
LielinJiang 已提交
146
        return out
L
LielinJiang 已提交
147

L
LielinJiang 已提交
148 149

class ResNet(nn.Layer):
L
LielinJiang 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162
    """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.

    Examples:
        .. code-block:: python

163 164
            from paddle.vision.models import ResNet
            from paddle.vision.models.resnet import BottleneckBlock, BasicBlock
L
LielinJiang 已提交
165 166 167 168 169 170 171

            resnet50 = ResNet(BottleneckBlock, 50)

            resnet18 = ResNet(BasicBlock, 18)

    """

L
LielinJiang 已提交
172
    def __init__(self, block, depth, num_classes=1000, with_pool=True):
L
LielinJiang 已提交
173
        super(ResNet, self).__init__()
L
LielinJiang 已提交
174
        layer_cfg = {
L
LielinJiang 已提交
175 176 177 178
            18: [2, 2, 2, 2],
            34: [3, 4, 6, 3],
            50: [3, 4, 6, 3],
            101: [3, 4, 23, 3],
L
LielinJiang 已提交
179
            152: [3, 8, 36, 3]
L
LielinJiang 已提交
180
        }
L
LielinJiang 已提交
181 182 183
        layers = layer_cfg[depth]
        self.num_classes = num_classes
        self.with_pool = with_pool
C
cnn 已提交
184
        self._norm_layer = nn.BatchNorm2D
L
LielinJiang 已提交
185 186 187

        self.inplanes = 64
        self.dilation = 1
L
LielinJiang 已提交
188

C
cnn 已提交
189
        self.conv1 = nn.Conv2D(
L
LielinJiang 已提交
190 191 192 193 194 195 196 197
            3,
            self.inplanes,
            kernel_size=7,
            stride=2,
            padding=3,
            bias_attr=False)
        self.bn1 = self._norm_layer(self.inplanes)
        self.relu = nn.ReLU()
C
cnn 已提交
198
        self.maxpool = nn.MaxPool2D(kernel_size=3, stride=2, padding=1)
L
LielinJiang 已提交
199 200 201 202
        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
L
LielinJiang 已提交
203
        if with_pool:
C
cnn 已提交
204
            self.avgpool = nn.AdaptiveAvgPool2D((1, 1))
L
LielinJiang 已提交
205 206

        if num_classes > 0:
L
LielinJiang 已提交
207 208 209 210 211 212 213 214 215 216 217
            self.fc = nn.Linear(512 * block.expansion, num_classes)

    def _make_layer(self, block, planes, blocks, stride=1, dilate=False):
        norm_layer = self._norm_layer
        downsample = None
        previous_dilation = self.dilation
        if dilate:
            self.dilation *= stride
            stride = 1
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
C
cnn 已提交
218
                nn.Conv2D(
L
LielinJiang 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
                    self.inplanes,
                    planes * block.expansion,
                    1,
                    stride=stride,
                    bias_attr=False),
                norm_layer(planes * block.expansion), )

        layers = []
        layers.append(
            block(self.inplanes, planes, stride, downsample, 1, 64,
                  previous_dilation, norm_layer))
        self.inplanes = planes * block.expansion
        for _ in range(1, blocks):
            layers.append(block(self.inplanes, planes, norm_layer=norm_layer))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

L
LielinJiang 已提交
246
        if self.with_pool:
L
LielinJiang 已提交
247 248 249 250
            x = self.avgpool(x)

        if self.num_classes > 0:
            x = paddle.flatten(x, 1)
L
LielinJiang 已提交
251
            x = self.fc(x)
L
LielinJiang 已提交
252

L
LielinJiang 已提交
253 254 255 256 257 258 259 260 261 262
        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])
263 264

        param = paddle.load(weight_path)
265 266
        model.set_dict(param)

L
LielinJiang 已提交
267 268 269 270 271 272 273 274 275 276 277 278
    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

279
            from paddle.vision.models import resnet18
L
LielinJiang 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

            # 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

299
            from paddle.vision.models import resnet34
L
LielinJiang 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318

            # 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

319
            from paddle.vision.models import resnet50
L
LielinJiang 已提交
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

            # 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

339
            from paddle.vision.models import resnet101
L
LielinJiang 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358

            # 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

359
            from paddle.vision.models import resnet152
L
LielinJiang 已提交
360 361 362 363 364 365 366 367

            # build model
            model = resnet152()

            # build model and load imagenet pretrained weight
            # model = resnet152(pretrained=True)
    """
    return _resnet('resnet152', BottleneckBlock, 152, pretrained, **kwargs)