| 8 | from .auxilary import * |
| 9 | |
| 10 | class Bottleneck(nn.Module): |
| 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 | |
| 20 | self.conv2 = nn.Conv2d(planes, planes, 3, padding=1, bias=False) |
| 21 | self.bn2 = nn.BatchNorm2d(planes) |
| 22 | |
| 23 | self.avgpool = nn.AvgPool2d(stride) if stride > 1 else nn.Identity() |
| 24 | |
| 25 | self.conv3 = nn.Conv2d(planes, planes * self.expansion, 1, bias=False) |
| 26 | self.bn3 = nn.BatchNorm2d(planes * self.expansion) |
| 27 | |
| 28 | self.relu = nn.ReLU(inplace=True) |
| 29 | self.downsample = None |
| 30 | self.stride = stride |
| 31 | |
| 32 | if stride > 1 or inplanes != planes * Bottleneck.expansion: |
| 33 | # downsampling layer is prepended with an avgpool, and the subsequent convolution has stride 1 |
| 34 | self.downsample = nn.Sequential(OrderedDict([ |
| 35 | ("-1", nn.AvgPool2d(stride)), |
| 36 | ("0", nn.Conv2d(inplanes, planes * self.expansion, 1, stride=1, bias=False)), |
| 37 | ("1", nn.BatchNorm2d(planes * self.expansion)) |
| 38 | ])) |
| 39 | |
| 40 | def forward(self, x: torch.Tensor): |
| 41 | identity = x |
| 42 | |
| 43 | out = self.relu(self.bn1(self.conv1(x))) |
| 44 | out = self.relu(self.bn2(self.conv2(out))) |
| 45 | out = self.avgpool(out) |
| 46 | out = self.bn3(self.conv3(out)) |
| 47 | |
| 48 | if self.downsample is not None: |
| 49 | identity = self.downsample(x) |
| 50 | |
| 51 | out += identity |
| 52 | out = self.relu(out) |
| 53 | return out |
| 54 | |
| 55 | |
| 56 | class AttentionPool2d(nn.Module): |