| 11 | expansion = 4 |
| 12 | |
| 13 | def __init__(self, inplanes, planes, stride=1): |
| 14 | super().__init__() |
| 15 | |
| 16 | # all conv layers have stride 1. an avgpool is performed after the second convolution when stride > 1 |
| 17 | self.conv1 = nn.Conv2d(inplanes, planes, 1, bias=False) |
| 18 | self.bn1 = nn.BatchNorm2d(planes) |
| 19 | self.relu1 = nn.ReLU(inplace=True) |
| 20 | |
| 21 | self.conv2 = nn.Conv2d(planes, planes, 3, padding=1, bias=False) |
| 22 | self.bn2 = nn.BatchNorm2d(planes) |
| 23 | self.relu2 = nn.ReLU(inplace=True) |
| 24 | |
| 25 | self.avgpool = nn.AvgPool2d(stride) if stride > 1 else nn.Identity() |
| 26 | |
| 27 | self.conv3 = nn.Conv2d(planes, planes * self.expansion, 1, bias=False) |
| 28 | self.bn3 = nn.BatchNorm2d(planes * self.expansion) |
| 29 | self.relu3 = nn.ReLU(inplace=True) |
| 30 | |
| 31 | self.downsample = None |
| 32 | self.stride = stride |
| 33 | |
| 34 | if stride > 1 or inplanes != planes * Bottleneck.expansion: |
| 35 | # downsampling layer is prepended with an avgpool, and the subsequent convolution has stride 1 |
| 36 | self.downsample = nn.Sequential(OrderedDict([ |
| 37 | ("-1", nn.AvgPool2d(stride)), |
| 38 | ("0", nn.Conv2d(inplanes, planes * self.expansion, 1, stride=1, bias=False)), |
| 39 | ("1", nn.BatchNorm2d(planes * self.expansion)) |
| 40 | ])) |
| 41 | |
| 42 | def forward(self, x: torch.Tensor): |
| 43 | identity = x |