pytorch实现resnet50代码如下:
(1)一个block实现,如1x1,64,3x3,64,1x1,256。这段代码中,1x1的卷积核只是为了改变输出通道数,3x3的卷积可能改变卷积核大小,与参数stride有关。即定义Bottleneck(inplanes, planes, stride, downsample)时,改变的可能是卷积核大小。
class Bottleneck(nn.Module):expansion = 4def __init__(self, inplanes, planes, stride=1, downsample=None):super(Bottleneck, self).__init__()self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, stride=stride, bias=False) # changeself.bn1 = nn.BatchNorm2d(planes)self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, # changepadding=1, bias=False)self.bn2 = nn.BatchNorm2d(planes)self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)self.bn3 = nn.BatchNorm2d(planes * 4)self.relu = nn.ReLU(inplace=True)self.downsample = downsampleself.stride = stridedef forward(self, x):residual = xout = 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 += residualout = self.relu(out)return out
(2) resnet实现:_make_layer()函数实现一个layer。
强调点1:
对于每个layer而言,每经过一个block之后,通道数变为了4planes,因此下一个block的输入通道数应为4planes,故定义self.inplanes = planes * block.expansion。planes不变,总是为传入的参数,如64,128,256,512。变得是每次经过一个block之后的输出通道数,也就意味着下一个block的输入通道数变了。
强调点2:
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)
可以看到,构建每个layer时传入的stride参数不一样,那么这个参数代表了什么呢?
进入_make_layer()函数中看看,这个stride影响的是第一个block的构建,其他block均不受stride影响。进入block的__init()函数中可以看到,stride影响的是第2个3x3的卷积,可能会影响此时的feature map大小。
总结一下,第一个layer(共3个block,每个block有3层,共9层,均不受stride的影响,即stride=1,故不改变feature map的大小);第二个layer(共4个block,每个block有3层,共12层。第一个block的第2层受stride影响,可能会改变feature map的大小,其余11层均不改变feature map大小);第三个layer(共6个block,每个block有3层,共18层。第一个block的第2层受stride影响,可能会改变feature map的大小,其余17层均不改变feature map大小)…第四个layer和上面类似,不做赘述。
class ResNet(nn.Module):def __init__(self, block, layers, num_classes=1000):self.inplanes = 64super(ResNet, self).__init__()self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=0, ceil_mode=True) # changeself.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) # differentself.avgpool = nn.AvgPool2d(7)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_channelsm.weight.data.normal_(0, math.sqrt(2. / n))elif isinstance(m, nn.BatchNorm2d):m.weight.data.fill_(1)m.bias.data.zero_()def _make_layer(self, block, planes, blocks, stride=1):downsample = Noneif 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),nn.BatchNorm2d(planes * block.expansion),)layers = []layers.append(block(self.inplanes, planes, stride, downsample))self.inplanes = planes * block.expansionfor i in range(1, blocks):layers.append(block(self.inplanes, planes))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)x = self.avgpool(x)x = x.view(x.size(0), -1)x = self.fc(x)return x
resnet50调用resnet类:
def resnet50(pretrained=False):"""Constructs a ResNet-50 model.Args:pretrained (bool): If True, returns a model pre-trained on ImageNet"""model = ResNet(Bottleneck, [3, 4, 6, 3])if pretrained:model.load_state_dict(model_zoo.load_url(model_urls['resnet50']))return model