ResNet 3D

Same code as the ResNet implementation on torchvision, just replacing 2D modules with 3D modules

Building blocks

conv3x3[source]

conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1)

3x3 convolution with padding

conv1x1[source]

conv1x1(in_planes, out_planes, stride=1)

1x1 convolution

class BasicBlock3d[source]

BasicBlock3d(inplanes, planes, stride=1, downsample=None, groups=1, base_width=64, dilation=1, norm_layer=None, act_layer=None) :: Module

Base class for all neural network modules.

Your models should also subclass this class.

Modules can also contain other Modules, allowing to nest them in
a tree structure. You can assign the submodules as regular attributes::

    import torch.nn as nn
    import torch.nn.functional as F

    class Model(nn.Module):
        def __init__(self):
            super(Model, self).__init__()
            self.conv1 = nn.Conv2d(1, 20, 5)
            self.conv2 = nn.Conv2d(20, 20, 5)

        def forward(self, x):
            x = F.relu(self.conv1(x))
            return F.relu(self.conv2(x))

Submodules assigned in this way will be registered, and will have their
parameters converted too when you call :meth:`to`, etc.

:ivar training: Boolean represents whether this module is in training or
                evaluation mode.
:vartype training: bool

class Bottleneck3d[source]

Bottleneck3d(inplanes, planes, stride=1, downsample=None, groups=1, base_width=64, dilation=1, norm_layer=None, act_layer=None) :: Module

Base class for all neural network modules.

Your models should also subclass this class.

Modules can also contain other Modules, allowing to nest them in
a tree structure. You can assign the submodules as regular attributes::

    import torch.nn as nn
    import torch.nn.functional as F

    class Model(nn.Module):
        def __init__(self):
            super(Model, self).__init__()
            self.conv1 = nn.Conv2d(1, 20, 5)
            self.conv2 = nn.Conv2d(20, 20, 5)

        def forward(self, x):
            x = F.relu(self.conv1(x))
            return F.relu(self.conv2(x))

Submodules assigned in this way will be registered, and will have their
parameters converted too when you call :meth:`to`, etc.

:ivar training: Boolean represents whether this module is in training or
                evaluation mode.
:vartype training: bool
BasicBlock3d(4, 64, norm_layer=partial(nn.BatchNorm3d, affine = False))
BasicBlock3d(
  (conv1): Conv3d(4, 64, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
  (bn1): BatchNorm3d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=True)
  (relu): ReLU(inplace=True)
  (conv2): Conv3d(64, 64, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
  (bn2): BatchNorm3d(64, eps=1e-05, momentum=0.1, affine=False, track_running_stats=True)
)
Bottleneck3d(4, 64)
Bottleneck3d(
  (conv1): Conv3d(4, 64, kernel_size=(1, 1, 1), stride=(1, 1, 1), bias=False)
  (bn1): BatchNorm3d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (conv2): Conv3d(64, 64, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
  (bn2): BatchNorm3d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (conv3): Conv3d(64, 256, kernel_size=(1, 1, 1), stride=(1, 1, 1), bias=False)
  (bn3): BatchNorm3d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (relu): ReLU(inplace=True)
)

Identity layer

Medical images, especially 3D images are large so batch size is limited when trainig in normal consumer hardware. This can lead to problems with the normalization layers, as performance can/will decrease for batch sizes under 32/under 8. This is discussed here, here and here. Replacing the normalization layer with an indentity layer might be a quick solution without the need to alter the whole architecture.

class IdentityLayer[source]

IdentityLayer(*args, **kwargs) :: Module

Returns input as is

ResNet basic module

Same as the ResNet module from torchvision, but all 2D submodules have been changed to 3D, and MaxPool has a kernel size of (1,3,3), to avoid reduction of the depth to 1 (depth of medical images can be very small).

class ResNet3D[source]

ResNet3D(block, layers, n_channels=3, num_classes=101, zero_init_residual=False, groups=1, width_per_group=64, replace_stride_with_dilation=None, norm_layer=None, act_layer=None, final_softmax=False, ps=0.5) :: Module

Base class for all neural network modules.

Your models should also subclass this class.

Modules can also contain other Modules, allowing to nest them in
a tree structure. You can assign the submodules as regular attributes::

    import torch.nn as nn
    import torch.nn.functional as F

    class Model(nn.Module):
        def __init__(self):
            super(Model, self).__init__()
            self.conv1 = nn.Conv2d(1, 20, 5)
            self.conv2 = nn.Conv2d(20, 20, 5)

        def forward(self, x):
            x = F.relu(self.conv1(x))
            return F.relu(self.conv2(x))

Submodules assigned in this way will be registered, and will have their
parameters converted too when you call :meth:`to`, etc.

:ivar training: Boolean represents whether this module is in training or
                evaluation mode.
:vartype training: bool
ResNet3D(BasicBlock3d, [2, 2, 2, 2], final_softmax = True, act_layer=nn.LeakyReLU, ps = 0.75)(torch.randn(10, 3, 8, 64, 64)).size()
torch.Size([10, 101])

ResNet architectures

Note that a pretrained ResNet18 for 3D already exists at torchvision.models.video

resnet18_3d[source]

resnet18_3d(pretrained=False, progress=False, **kwargs)

ResNet-34 model from
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
adapted to 3d

resnet34_3d[source]

resnet34_3d(pretrained=False, progress=False, **kwargs)

ResNet-34 model from
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
adapted to 3d

resnet50_3d[source]

resnet50_3d(pretrained=False, progress=False, **kwargs)

ResNet-50 model from
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
adapted to 3d

resnet101_3d[source]

resnet101_3d(pretrained=False, progress=False, **kwargs)

ResNet-101 model from
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`
adapted to 3d

resnet152_3d[source]

resnet152_3d(pretrained=False, progress=False, **kwargs)

ResNet-152 model from
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`
adapted to 3d
model = resnet18_3d()
input = torch.rand(2, 3, 15, 80, 80)
output = model(input)
print(output.size())
torch.Size([2, 101])
model = resnet101_3d()
input = torch.rand(2, 3, 15, 64, 64)
output = model(input)
print(output.size())
torch.Size([2, 100])

Resnet encoder

for UNet or DeepLabV3

build_backbone[source]

build_backbone(backbone, output_stride, norm_layer, n_channels, **kwargs)

m = build_backbone(resnet34_3d, 8, IdentityLayer, 5)
xb = m(torch.randn(10, 5, 10, 50, 50))
for x in xb: print(x.size())
torch.Size([10, 32, 11, 16, 16])
torch.Size([10, 64, 11, 16, 16])
torch.Size([10, 128, 6, 8, 8])
torch.Size([10, 256, 3, 4, 4])
torch.Size([10, 512, 2, 2, 2])