206 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| # -*- encoding: utf-8 -*-
 | |
| 
 | |
| """
 | |
| @Author  :   Peike Li
 | |
| @Contact :   peike.li@yahoo.com
 | |
| @File    :   resnet.py
 | |
| @Time    :   8/4/19 3:35 PM
 | |
| @Desc    :   
 | |
| @License :   This source code is licensed under the license found in the 
 | |
|              LICENSE file in the root directory of this source tree.
 | |
| """
 | |
| 
 | |
| import functools
 | |
| import torch.nn as nn
 | |
| import math
 | |
| from torch.utils.model_zoo import load_url
 | |
| 
 | |
| from modules import InPlaceABNSync
 | |
| 
 | |
| BatchNorm2d = functools.partial(InPlaceABNSync, activation='none')
 | |
| 
 | |
| __all__ = ['ResNet', 'resnet18', 'resnet50', 'resnet101']  # resnet101 is coming soon!
 | |
| 
 | |
| model_urls = {
 | |
|     'resnet18': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet18-imagenet.pth',
 | |
|     'resnet50': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet50-imagenet.pth',
 | |
|     'resnet101': 'http://sceneparsing.csail.mit.edu/model/pretrained_resnet/resnet101-imagenet.pth'
 | |
| }
 | |
| 
 | |
| 
 | |
| def conv3x3(in_planes, out_planes, stride=1):
 | |
|     "3x3 convolution with padding"
 | |
|     return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
 | |
|                      padding=1, bias=False)
 | |
| 
 | |
| 
 | |
| class BasicBlock(nn.Module):
 | |
|     expansion = 1
 | |
| 
 | |
|     def __init__(self, inplanes, planes, stride=1, downsample=None):
 | |
|         super(BasicBlock, self).__init__()
 | |
|         self.conv1 = conv3x3(inplanes, planes, stride)
 | |
|         self.bn1 = BatchNorm2d(planes)
 | |
|         self.relu = nn.ReLU(inplace=True)
 | |
|         self.conv2 = conv3x3(planes, planes)
 | |
|         self.bn2 = BatchNorm2d(planes)
 | |
|         self.downsample = downsample
 | |
|         self.stride = stride
 | |
| 
 | |
|     def forward(self, x):
 | |
|         residual = x
 | |
| 
 | |
|         out = self.conv1(x)
 | |
|         out = self.bn1(out)
 | |
|         out = self.relu(out)
 | |
| 
 | |
|         out = self.conv2(out)
 | |
|         out = self.bn2(out)
 | |
| 
 | |
|         if self.downsample is not None:
 | |
|             residual = self.downsample(x)
 | |
| 
 | |
|         out += residual
 | |
|         out = self.relu(out)
 | |
| 
 | |
|         return out
 | |
| 
 | |
| 
 | |
| class Bottleneck(nn.Module):
 | |
|     expansion = 4
 | |
| 
 | |
|     def __init__(self, inplanes, planes, stride=1, downsample=None):
 | |
|         super(Bottleneck, self).__init__()
 | |
|         self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
 | |
|         self.bn1 = BatchNorm2d(planes)
 | |
|         self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
 | |
|                                padding=1, bias=False)
 | |
|         self.bn2 = BatchNorm2d(planes)
 | |
|         self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
 | |
|         self.bn3 = BatchNorm2d(planes * 4)
 | |
|         self.relu = nn.ReLU(inplace=True)
 | |
|         self.downsample = downsample
 | |
|         self.stride = stride
 | |
| 
 | |
|     def forward(self, x):
 | |
|         residual = x
 | |
| 
 | |
|         out = self.conv1(x)
 | |
|         out = self.bn1(out)
 | |
|         out = self.relu(out)
 | |
| 
 | |
|         out = self.conv2(out)
 | |
|         out = self.bn2(out)
 | |
|         out = self.relu(out)
 | |
| 
 | |
|         out = self.conv3(out)
 | |
|         out = self.bn3(out)
 | |
| 
 | |
|         if self.downsample is not None:
 | |
|             residual = self.downsample(x)
 | |
| 
 | |
|         out += residual
 | |
|         out = self.relu(out)
 | |
| 
 | |
|         return out
 | |
| 
 | |
| 
 | |
| class ResNet(nn.Module):
 | |
| 
 | |
|     def __init__(self, block, layers, num_classes=1000):
 | |
|         self.inplanes = 128
 | |
|         super(ResNet, self).__init__()
 | |
|         self.conv1 = conv3x3(3, 64, stride=2)
 | |
|         self.bn1 = BatchNorm2d(64)
 | |
|         self.relu1 = nn.ReLU(inplace=True)
 | |
|         self.conv2 = conv3x3(64, 64)
 | |
|         self.bn2 = BatchNorm2d(64)
 | |
|         self.relu2 = nn.ReLU(inplace=True)
 | |
|         self.conv3 = conv3x3(64, 128)
 | |
|         self.bn3 = BatchNorm2d(128)
 | |
|         self.relu3 = nn.ReLU(inplace=True)
 | |
|         self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
 | |
| 
 | |
|         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)
 | |
|         self.avgpool = nn.AvgPool2d(7, stride=1)
 | |
|         self.fc = nn.Linear(512 * block.expansion, num_classes)
 | |
| 
 | |
|         for m in self.modules():
 | |
|             if isinstance(m, nn.Conv2d):
 | |
|                 n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
 | |
|                 m.weight.data.normal_(0, math.sqrt(2. / n))
 | |
|             elif isinstance(m, BatchNorm2d):
 | |
|                 m.weight.data.fill_(1)
 | |
|                 m.bias.data.zero_()
 | |
| 
 | |
|     def _make_layer(self, block, planes, blocks, stride=1):
 | |
|         downsample = None
 | |
|         if stride != 1 or self.inplanes != planes * block.expansion:
 | |
|             downsample = nn.Sequential(
 | |
|                 nn.Conv2d(self.inplanes, planes * block.expansion,
 | |
|                           kernel_size=1, stride=stride, bias=False),
 | |
|                 BatchNorm2d(planes * block.expansion),
 | |
|             )
 | |
| 
 | |
|         layers = []
 | |
|         layers.append(block(self.inplanes, planes, stride, downsample))
 | |
|         self.inplanes = planes * block.expansion
 | |
|         for i in range(1, blocks):
 | |
|             layers.append(block(self.inplanes, planes))
 | |
| 
 | |
|         return nn.Sequential(*layers)
 | |
| 
 | |
|     def forward(self, x):
 | |
|         x = self.relu1(self.bn1(self.conv1(x)))
 | |
|         x = self.relu2(self.bn2(self.conv2(x)))
 | |
|         x = self.relu3(self.bn3(self.conv3(x)))
 | |
|         x = self.maxpool(x)
 | |
| 
 | |
|         x = self.layer1(x)
 | |
|         x = self.layer2(x)
 | |
|         x = self.layer3(x)
 | |
|         x = self.layer4(x)
 | |
| 
 | |
|         x = self.avgpool(x)
 | |
|         x = x.view(x.size(0), -1)
 | |
|         x = self.fc(x)
 | |
| 
 | |
|         return x
 | |
| 
 | |
| 
 | |
| def resnet18(pretrained=False, **kwargs):
 | |
|     """Constructs a ResNet-18 model.
 | |
|     Args:
 | |
|         pretrained (bool): If True, returns a model pre-trained on ImageNet
 | |
|     """
 | |
|     model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
 | |
|     if pretrained:
 | |
|         model.load_state_dict(load_url(model_urls['resnet18']))
 | |
|     return model
 | |
| 
 | |
| 
 | |
| def resnet50(pretrained=False, **kwargs):
 | |
|     """Constructs a ResNet-50 model.
 | |
|     Args:
 | |
|         pretrained (bool): If True, returns a model pre-trained on ImageNet
 | |
|     """
 | |
|     model = ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)
 | |
|     if pretrained:
 | |
|         model.load_state_dict(load_url(model_urls['resnet50']), strict=False)
 | |
|     return model
 | |
| 
 | |
| 
 | |
| def resnet101(pretrained=False, **kwargs):
 | |
|     """Constructs a ResNet-101 model.
 | |
|     Args:
 | |
|         pretrained (bool): If True, returns a model pre-trained on ImageNet
 | |
|     """
 | |
|     model = ResNet(Bottleneck, [3, 4, 23, 3], **kwargs)
 | |
|     if pretrained:
 | |
|         model.load_state_dict(load_url(model_urls['resnet101']), strict=False)
 | |
|     return model
 |