# Copyright (c) 2021 - present / Neuralmagic, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
PyTorch ResNet, ResNet V2, ResNext implementations.
Further info on ResNet can be found in the paper
`here <https://arxiv.org/abs/1512.03385>`__.
Further info on ResNet V2 can be found in the paper
`here <https://arxiv.org/abs/1603.05027>`__.
Further info on ResNext can be found in the paper
`here <https://arxiv.org/abs/1611.05431>`__.
"""
from typing import List
from torch import Tensor
from torch.nn import (
AdaptiveAvgPool2d,
BatchNorm2d,
Conv2d,
Linear,
MaxPool2d,
Module,
Sequential,
Sigmoid,
Softmax,
init,
)
from sparseml.pytorch.models.registry import ModelRegistry
from sparseml.pytorch.nn import ReLU
try:
from torch.nn.quantized import FloatFunctional
except Exception:
FloatFunctional = None
__all__ = [
"ResNetSectionSettings",
"ResNet",
"resnet18",
"resnet34",
"resnet50",
"resnet101",
"resnet152",
"resnetv2_18",
"resnetv2_34",
"resnetv2_50",
"resnetv2_101",
"resnetv2_152",
"resnet50_2xwidth",
"resnet101_2xwidth",
"resnext50",
"resnext101",
"resnext152",
]
def _init_conv(conv: Conv2d):
init.kaiming_normal_(conv.weight, mode="fan_out", nonlinearity="relu")
def _init_batch_norm(norm: BatchNorm2d, weight_const: float = 1.0):
init.constant_(norm.weight, weight_const)
init.constant_(norm.bias, 0.0)
def _init_linear(linear: Linear):
init.normal_(linear.weight, 0, 0.01)
init.constant_(linear.bias, 0)
class _Input(Module):
IN_CHANNELS = 3
OUT_CHANNELS = 64
def __init__(self):
super().__init__()
self.conv = Conv2d(
_Input.IN_CHANNELS,
_Input.OUT_CHANNELS,
kernel_size=7,
stride=2,
padding=3,
bias=False,
)
self.bn = BatchNorm2d(_Input.OUT_CHANNELS)
self.act = ReLU(num_channels=_Input.OUT_CHANNELS, inplace=True)
self.pool = MaxPool2d(kernel_size=3, stride=2, padding=1)
self.initialize()
def forward(self, inp: Tensor):
out = self.conv(inp)
out = self.bn(out)
out = self.act(out)
out = self.pool(out)
return out
def initialize(self):
_init_conv(self.conv)
_init_batch_norm(self.bn)
class _IdentityModifier(Module):
def __init__(self, in_channels: int, out_channels: int, stride: int):
super().__init__()
self.conv = Conv2d(
in_channels, out_channels, kernel_size=1, stride=stride, bias=False
)
self.bn = BatchNorm2d(out_channels)
self.initialize()
def forward(self, inp: Tensor):
out = self.conv(inp)
out = self.bn(out)
return out
def initialize(self):
_init_conv(self.conv)
_init_batch_norm(self.bn)
@staticmethod
def required(in_channels: int, out_channels: int, stride: int) -> bool:
return in_channels != out_channels or stride > 1
class _AddReLU(Module):
"""
Wrapper for the FloatFunctional class that enables QATWrapper used to
quantize the first input to the Add operation
"""
def __init__(self, num_channels):
super().__init__()
if FloatFunctional:
self.functional = FloatFunctional()
self.wrap_qat = True
self.qat_wrapper_kwargs = {"num_inputs": 1, "num_outputs": 1}
else:
self.functional = ReLU(num_channels=num_channels, inplace=True)
def forward(self, x, y):
if isinstance(self.functional, FloatFunctional):
return self.functional.add_relu(x, y)
else:
return self.functional(x + y)
class _BasicBlock(Module):
def __init__(self, in_channels: int, out_channels: int, stride: int = 1):
super().__init__()
self.conv1 = Conv2d(
in_channels,
out_channels,
kernel_size=3,
stride=stride,
padding=1,
bias=False,
)
self.bn1 = BatchNorm2d(out_channels)
self.act1 = ReLU(num_channels=out_channels, inplace=True)
self.conv2 = Conv2d(
out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False
)
self.bn2 = BatchNorm2d(out_channels)
self.identity = (
_IdentityModifier(in_channels, out_channels, stride)
if _IdentityModifier.required(in_channels, out_channels, stride)
else None
)
# self.add_relu = _AddReLU(out_channels)
if FloatFunctional:
self.add_relu = FloatFunctional()
else:
self.add_relu = ReLU(num_channels=out_channels, inplace=True)
self.initialize()
def forward(self, inp: Tensor):
out = self.conv1(inp)
out = self.bn1(out)
out = self.act1(out)
out = self.conv2(out)
out = self.bn2(out)
identity_val = self.identity(inp) if self.identity is not None else inp
# out = self.add_relu(identity_val, out)
# return out
if isinstance(self.add_relu, FloatFunctional):
return self.add_relu.add_relu(identity_val, out)
else:
return self.add_relu(identity_val + out)
def initialize(self):
_init_conv(self.conv1)
_init_batch_norm(self.bn1)
_init_conv(self.conv2)
_init_batch_norm(self.bn2, 0.0)
class _BottleneckBlock(Module):
def __init__(
self,
in_channels: int,
out_channels: int,
proj_channels: int,
stride: int = 1,
groups: int = 1,
):
super().__init__()
self.conv1 = Conv2d(in_channels, proj_channels, kernel_size=1, bias=False)
self.bn1 = BatchNorm2d(proj_channels)
self.act1 = ReLU(num_channels=proj_channels, inplace=True)
self.conv2 = Conv2d(
proj_channels,
proj_channels,
kernel_size=3,
stride=stride,
padding=1,
bias=False,
groups=groups,
)
self.bn2 = BatchNorm2d(proj_channels)
self.act2 = ReLU(num_channels=proj_channels, inplace=True)
self.conv3 = Conv2d(proj_channels, out_channels, kernel_size=1, bias=False)
self.bn3 = BatchNorm2d(out_channels)
self.identity = (
_IdentityModifier(in_channels, out_channels, stride)
if _IdentityModifier.required(in_channels, out_channels, stride)
else None
)
# self.add_relu = _AddReLU(out_channels)
if FloatFunctional:
self.add_relu = FloatFunctional()
else:
self.add_relu = ReLU(num_channels=out_channels, inplace=True)
self.initialize()
def forward(self, inp: Tensor):
out = self.conv1(inp)
out = self.bn1(out)
out = self.act1(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.act2(out)
out = self.conv3(out)
out = self.bn3(out)
identity_val = self.identity(inp) if self.identity is not None else inp
# out = self.add_relu(identity_val, out)
# return out
if isinstance(self.add_relu, FloatFunctional):
return self.add_relu.add_relu(identity_val, out)
else:
return self.add_relu(identity_val + out)
def initialize(self):
_init_conv(self.conv1)
_init_batch_norm(self.bn1)
_init_conv(self.conv2)
_init_batch_norm(self.bn2)
_init_conv(self.conv3)
_init_batch_norm(self.bn3, 0.0)
class _BasicBlockV2(Module):
def __init__(self, in_channels: int, out_channels: int, stride: int = 1):
super().__init__()
self.bn1 = BatchNorm2d(in_channels)
self.act1 = ReLU(num_channels=in_channels, inplace=True)
self.conv1 = Conv2d(
in_channels,
out_channels,
kernel_size=3,
stride=stride,
padding=1,
bias=False,
)
self.bn2 = BatchNorm2d(out_channels)
self.act2 = ReLU(num_channels=out_channels, inplace=True)
self.conv2 = Conv2d(
out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False
)
self.identity = (
Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False)
if in_channels != out_channels or stride != 1
else None
)
self.initialize()
def forward(self, inp: Tensor):
identity = inp
out = self.bn1(inp)
out = self.act1(out)
if self.identity is not None:
identity = self.identity(out)
out = self.conv1(out)
out = self.bn2(out)
out = self.act2(out)
out = self.conv2(out)
out += identity
return out
def initialize(self):
_init_conv(self.conv1)
_init_batch_norm(self.bn1)
_init_conv(self.conv2)
_init_batch_norm(self.bn2, 0.0)
class _BottleneckBlockV2(Module):
def __init__(
self,
in_channels: int,
out_channels: int,
proj_channels: int,
stride: int = 1,
groups: int = 1,
):
super().__init__()
self.bn1 = BatchNorm2d(in_channels)
self.act1 = ReLU(num_channels=in_channels, inplace=True)
self.conv1 = Conv2d(in_channels, proj_channels, kernel_size=1, bias=False)
self.bn2 = BatchNorm2d(proj_channels)
self.act2 = ReLU(num_channels=proj_channels, inplace=True)
self.conv2 = Conv2d(
proj_channels,
proj_channels,
kernel_size=3,
stride=stride,
padding=1,
bias=False,
groups=groups,
)
self.bn3 = BatchNorm2d(proj_channels)
self.act3 = ReLU(num_channels=proj_channels, inplace=True)
self.conv3 = Conv2d(proj_channels, out_channels, kernel_size=1, bias=False)
self.identity = (
Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False)
if in_channels != out_channels or stride != 1
else None
)
self.initialize()
def forward(self, inp: Tensor):
identity = inp
out = self.bn1(inp)
out = self.act1(out)
if self.identity is not None:
identity = self.identity(out)
out = self.conv1(out)
out = self.bn2(out)
out = self.act2(out)
out = self.conv2(out)
out = self.bn3(out)
out = self.act3(out)
out = self.conv3(out)
out += identity
return out
def initialize(self):
_init_batch_norm(self.bn1)
_init_conv(self.conv1)
_init_batch_norm(self.bn2)
_init_conv(self.conv2)
_init_batch_norm(self.bn3)
_init_conv(self.conv3)
if self.identity is not None:
_init_conv(self.identity)
class _Classifier(Module):
def __init__(self, in_channels: int, num_classes: int, class_type: str = "single"):
super().__init__()
self.avgpool = AdaptiveAvgPool2d(1)
self.fc = Linear(in_channels, num_classes)
if class_type == "single":
self.softmax = Softmax(dim=1)
elif class_type == "multi":
self.softmax = Sigmoid()
else:
raise ValueError("unknown class_type given of {}".format(class_type))
self.initialize()
def forward(self, inp: Tensor):
out = self.avgpool(inp)
out = out.view(out.size(0), -1)
logits = self.fc(out)
classes = self.softmax(logits)
return logits, classes
def initialize(self):
_init_linear(self.fc)
[docs]class ResNetSectionSettings(object):
"""
Settings to describe how to put together a ResNet based architecture
using user supplied configurations.
:param num_blocks: the number of blocks to put in the section
(ie Basic or Bottleneck blocks)
:param in_channels: the number of input channels to the section
:param out_channels: the number of output channels from the section
:param downsample: True to apply stride 2 for downsampling of the input,
False otherwise
:param proj_channels: The number of channels in the projection for a
bottleneck block, if < 0 then uses basic
:param groups: The number of groups to use for each 3x3 conv (ResNext)
:param use_se: True to use squeeze excite, False otherwise
:param version: 1 for original ResNet model, 2 for ResNet v2 model
"""
def __init__(
self,
num_blocks: int,
in_channels: int,
out_channels: int,
downsample: bool,
proj_channels: int = -1,
groups: int = 1,
use_se: bool = False,
version: int = 1,
):
if use_se:
# TODO: add support for squeeze excite
raise NotImplementedError("squeeze excite not supported yet")
if version != 1 and version != 2:
raise ValueError(
"unknown version given of {}, only 1 and 2 are supported".format(
version
)
)
self.num_blocks = num_blocks
self.in_channels = in_channels
self.out_channels = out_channels
self.downsample = downsample
self.proj_channels = proj_channels
self.groups = groups
self.use_se = use_se
self.version = version
[docs]class ResNet(Module):
"""
ResNet, ResNet V2, ResNext implementations.
:param sec_settings: the settings for each section in the ResNet model
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
"""
def __init__(
self,
sec_settings: List[ResNetSectionSettings],
num_classes: int,
class_type: str,
):
super().__init__()
self.input = _Input()
self.sections = Sequential(
*[ResNet.create_section(settings) for settings in sec_settings]
)
self.classifier = _Classifier(
sec_settings[-1].out_channels, num_classes, class_type
)
[docs] def forward(self, inp: Tensor):
out = self.input(inp)
out = self.sections(out)
logits, classes = self.classifier(out)
return logits, classes
[docs] @staticmethod
def create_section(settings: ResNetSectionSettings) -> Sequential:
blocks = []
in_channels = settings.in_channels
stride = 2 if settings.downsample else 1
for _ in range(settings.num_blocks):
if settings.proj_channels > 0 and settings.version == 1:
blocks.append(
_BottleneckBlock(
in_channels,
settings.out_channels,
settings.proj_channels,
stride,
settings.groups,
)
)
elif settings.proj_channels > 0 and settings.version == 2:
blocks.append(
_BottleneckBlockV2(
in_channels,
settings.out_channels,
settings.proj_channels,
stride,
settings.groups,
)
)
elif settings.version == 1:
blocks.append(_BasicBlock(in_channels, settings.out_channels, stride))
elif settings.version == 2:
blocks.append(_BasicBlockV2(in_channels, settings.out_channels, stride))
else:
raise ValueError(
"could not figure out which block to use given "
"version:{} and proj_channels:{}".format(
settings.version, settings.proj_channels
)
)
in_channels = settings.out_channels
stride = 1
return Sequential(*blocks)
[docs]@ModelRegistry.register(
key=["resnet18", "resnet_18", "resnet-18", "resnetv1_18", "resnetv1-18"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v1",
sub_architecture="18",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnet18(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet 18 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=2, in_channels=64, out_channels=64, downsample=False
),
ResNetSectionSettings(
num_blocks=2, in_channels=64, out_channels=128, downsample=True
),
ResNetSectionSettings(
num_blocks=2, in_channels=128, out_channels=256, downsample=True
),
ResNetSectionSettings(
num_blocks=2, in_channels=256, out_channels=512, downsample=True
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnetv2_18", "resnetv2-18"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v2",
sub_architecture="18",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnetv2_18(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet V2 18 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=2, in_channels=64, out_channels=64, downsample=False, version=2
),
ResNetSectionSettings(
num_blocks=2, in_channels=64, out_channels=128, downsample=True, version=2
),
ResNetSectionSettings(
num_blocks=2, in_channels=128, out_channels=256, downsample=True, version=2
),
ResNetSectionSettings(
num_blocks=2, in_channels=256, out_channels=512, downsample=True, version=2
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnet34", "resnet_34", "resnet-34", "resnetv1_34", "resnetv1-34"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v1",
sub_architecture="34",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnet34(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet 34 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3, in_channels=64, out_channels=64, downsample=False
),
ResNetSectionSettings(
num_blocks=4, in_channels=64, out_channels=128, downsample=True
),
ResNetSectionSettings(
num_blocks=6, in_channels=128, out_channels=256, downsample=True
),
ResNetSectionSettings(
num_blocks=3, in_channels=256, out_channels=512, downsample=True
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnetv2_34", "resnetv2-34"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v2",
sub_architecture="34",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnetv2_34(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet V2 34 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3, in_channels=64, out_channels=64, downsample=False, version=2
),
ResNetSectionSettings(
num_blocks=4, in_channels=64, out_channels=128, downsample=True, version=2
),
ResNetSectionSettings(
num_blocks=6, in_channels=128, out_channels=256, downsample=True, version=2
),
ResNetSectionSettings(
num_blocks=3, in_channels=256, out_channels=512, downsample=True, version=2
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnet50", "resnet_50", "resnet-50", "resnetv1_50", "resnetv1-50"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v1",
sub_architecture="50",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnet50(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet 50 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=64,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=128,
),
ResNetSectionSettings(
num_blocks=6,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=256,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=512,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnetv2_50", "resnetv2-50"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v2",
sub_architecture="50",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnetv2_50(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet V2 50 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=64,
version=2,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=128,
version=2,
),
ResNetSectionSettings(
num_blocks=6,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=256,
version=2,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=512,
version=2,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=[
"resnet50_2xwidth",
"resnet_50_2xwidth",
"resnet-50-2xwidth",
"resnetv1_50_2xwidth",
"resnetv1-50-2xwidth",
],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v1",
sub_architecture="50_2x",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnet50_2xwidth(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
ResNet 50 implementation where channel sizes for 3x3 convolutions are doubled;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=128,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=256,
),
ResNetSectionSettings(
num_blocks=6,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=512,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=1024,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnext50", "resnext_50", "resnext-50"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnext",
sub_architecture="50",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnext50(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNext 50 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=128,
groups=32,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=256,
groups=32,
),
ResNetSectionSettings(
num_blocks=6,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=512,
groups=32,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=1024,
groups=32,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnet101", "resnet_101", "resnet-101", "resnetv1_101", "resnetv1-101"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v1",
sub_architecture="101",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnet101(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet 101 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=64,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=128,
),
ResNetSectionSettings(
num_blocks=23,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=256,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=512,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnetv2_101", "resnetv2-101"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v2",
sub_architecture="101",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnetv2_101(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet V2 101 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=64,
version=2,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=128,
version=2,
),
ResNetSectionSettings(
num_blocks=23,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=256,
version=2,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=512,
version=2,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=[
"resnet101_2xwidth",
"resnet_101_2xwidth",
"resnet-101-2xwidth",
"resnetv1_101_2xwidth",
"resnetv1-101-2xwidth",
],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v1",
sub_architecture="101_2x",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnet101_2xwidth(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
ResNet 101 implementation where channel sizes for 3x3 convolutions are doubled;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=128,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=256,
),
ResNetSectionSettings(
num_blocks=23,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=512,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=1024,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnext101", "resnext_101", "resnext-101"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnext",
sub_architecture="101",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnext101(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNext 101 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=128,
groups=32,
),
ResNetSectionSettings(
num_blocks=4,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=256,
groups=32,
),
ResNetSectionSettings(
num_blocks=23,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=512,
groups=32,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=1024,
groups=32,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnet152", "resnet_152", "resnet-152", "resnetv1_152", "resnetv1-152"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v1",
sub_architecture="152",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnet152(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet 152 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=64,
),
ResNetSectionSettings(
num_blocks=8,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=128,
),
ResNetSectionSettings(
num_blocks=36,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=256,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=512,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnetv2_152", "resnetv2-152"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnet_v2",
sub_architecture="152",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnetv2_152(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNet V2 152 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=64,
version=2,
),
ResNetSectionSettings(
num_blocks=8,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=128,
version=2,
),
ResNetSectionSettings(
num_blocks=36,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=256,
version=2,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=512,
version=2,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)
[docs]@ModelRegistry.register(
key=["resnext152", "resnext_152", "resnext-152"],
input_shape=(3, 224, 224),
domain="cv",
sub_domain="classification",
architecture="resnext",
sub_architecture="152",
default_dataset="imagenet",
default_desc="base",
def_ignore_error_tensors=["classifier.fc.weight", "classifier.fc.bias"],
)
def resnext152(num_classes: int = 1000, class_type: str = "single") -> ResNet:
"""
Standard ResNext 152 implementation;
expected input shape is (B, 3, 224, 224)
:param num_classes: the number of classes to classify
:param class_type: one of [single, multi] to support multi class training;
default single
:return: The created ResNet Module
"""
sec_settings = [
ResNetSectionSettings(
num_blocks=3,
in_channels=64,
out_channels=256,
downsample=False,
proj_channels=128,
groups=32,
),
ResNetSectionSettings(
num_blocks=8,
in_channels=256,
out_channels=512,
downsample=True,
proj_channels=256,
groups=32,
),
ResNetSectionSettings(
num_blocks=36,
in_channels=512,
out_channels=1024,
downsample=True,
proj_channels=512,
groups=32,
),
ResNetSectionSettings(
num_blocks=3,
in_channels=1024,
out_channels=2048,
downsample=True,
proj_channels=1024,
groups=32,
),
]
return ResNet(
sec_settings=sec_settings, num_classes=num_classes, class_type=class_type
)