resnet.py 10.7 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 24 25 26 27 28

__all__ = [
    'ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152'
]

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


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

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

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

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

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

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

L
LielinJiang 已提交
77 78
        out = self.conv2(out)
        out = self.bn2(out)
L
LielinJiang 已提交
79

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

L
LielinJiang 已提交
83 84
        out += identity
        out = self.relu(out)
L
LielinJiang 已提交
85

L
LielinJiang 已提交
86
        return out
L
LielinJiang 已提交
87

L
LielinJiang 已提交
88 89

class BottleneckBlock(nn.Layer):
L
LielinJiang 已提交
90 91 92

    expansion = 4

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

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

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

C
cnn 已提交
121
        self.conv3 = nn.Conv2D(
L
LielinJiang 已提交
122 123 124 125 126
            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 已提交
127

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

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

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

L
LielinJiang 已提交
139 140
        out = self.conv3(out)
        out = self.bn3(out)
L
LielinJiang 已提交
141

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

L
LielinJiang 已提交
145 146
        out += identity
        out = self.relu(out)
L
LielinJiang 已提交
147

L
LielinJiang 已提交
148
        return out
L
LielinJiang 已提交
149

L
LielinJiang 已提交
150 151

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

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

            resnet50 = ResNet(BottleneckBlock, 50)

            resnet18 = ResNet(BasicBlock, 18)

    """

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

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

C
cnn 已提交
191
        self.conv1 = nn.Conv2D(
L
LielinJiang 已提交
192 193 194 195 196 197 198 199
            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 已提交
200
        self.maxpool = nn.MaxPool2D(kernel_size=3, stride=2, padding=1)
L
LielinJiang 已提交
201 202 203 204
        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 已提交
205
        if with_pool:
C
cnn 已提交
206
            self.avgpool = nn.AdaptiveAvgPool2D((1, 1))
L
LielinJiang 已提交
207 208

        if num_classes > 0:
L
LielinJiang 已提交
209 210 211 212 213 214 215 216 217 218 219
            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 已提交
220
                nn.Conv2D(
L
LielinJiang 已提交
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 246 247 248 249 250 251 252
                    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)

        if self.with_pool > 0:
            x = self.avgpool(x)

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

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

        param = paddle.load(weight_path)
267 268
        model.set_dict(param)

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

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

            # 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

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

            # 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

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

            # 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

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

            # 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

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

            # build model
            model = resnet152()

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