Custom Architectures#

from torch import nn

from antelope import ml

class Model(nn.Module):
    def __init__(self, in_features, out_features):

        super().__init__()

        self.MLP = nn.Sequential(nn.Flatten(),
                                 nn.Linear(in_features, 32),
                                 nn.Linear(32, out_features))

    def forward(self, x):
        return self.MLP(x)

ml(model=Model, dataset='CIFAR10')

Input and output shapes#

antelope-brethren-star automatically detects the shape signature of your model.

class Model(nn.Module):
+   def __init__(self, in_features, out_features):
        super().__init__()

        self.model = nn.Sequential(nn.Linear(in_features, 128), nn.Linear(128, out_features))

    def forward(self, x):
        return self.model(x)

Inferrable signature arguments include in_shape, out_shape, in_features, out_features, in_channels, out_channels, in_dim, out_dim.

Just include them as args to your model and The Antelope will detect and fill them in.

Shaping pre-computation#

Architectures can also be passed in piece-wise using the piece-wise syntax. For example, eyes=ResNet18 predictor=MLP will combine the encoder of ResNet18 with an MLP predictor head. In this case, each piece, or “part”, pre-computes the output shape of the earlier. To make this shape pre-computation faster, and define it yourself, you can give your architecture a shape(self, in_shape): method that outputs the expected output shape given an input shape.

It’s best practice to always include this method just to simplify shape pre-computation.

Example: ResNet18#

from antelope.Agents.Blocks.Architectures.Residual import Residual

from antelope.Utils import cnn_feature_shape


class ResBlock(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, down_sample=None):
        super().__init__()

        if down_sample is None and (in_channels != out_channels or stride != 1):
            down_sample = nn.Sequential(nn.Conv2d(in_channels, out_channels,
                                                  kernel_size=1, stride=stride, bias=False),
                                        nn.BatchNorm2d(out_channels))

        block = nn.Sequential(nn.Conv2d(in_channels, out_channels,
                                        kernel_size=kernel_size, padding=kernel_size // 2,
                                        stride=stride, bias=False),
                              nn.BatchNorm2d(out_channels),
                              nn.ReLU(inplace=True),
                              nn.Conv2d(out_channels, out_channels,
                                        kernel_size=kernel_size, padding=kernel_size // 2,
                                        bias=False),
                              nn.BatchNorm2d(out_channels))

        self.ResBlock = nn.Sequential(Residual(block, down_sample),
                                      nn.ReLU(inplace=True))

    def shape(self, in_shape):
        return cnn_feature_shape(in_shape, self.ResBlock)  # Pre-computes the shapes of basic CNNs

    def forward(self, x):
        return self.ResBlock(x)


class ResNet18(nn.Module):
    """
    A full ResNet backbone with computationally-efficient defaults.
    """
    def __init__(self, in_channels, kernel_size=3, stride=2, dims=(64, 64, 128, 256, 512), depths=(2, 2, 2, 2)):
        super().__init__()

        # ResNet
        self.ResNet = nn.Sequential(nn.Conv2d(in_channels, dims[0],
                                              kernel_size=kernel_size, padding=1, bias=False),
                                    nn.BatchNorm2d(dims[0]),
                                    nn.ReLU(inplace=True),
                                    *[nn.Sequential(*[ResBlock(dims[i + (j > 0)], dims[i + 1], kernel_size,
                                                               1 + (stride - 1) * (i > 0 and j > 0))
                                                      for j in range(depth)])
                                      for i, depth in enumerate(depths)])

    def shape(self, in_shape):
        return cnn_feature_shape(in_shape, self.ResNet)  # Pre-computes the shapes of basic CNNs

    def forward(self, x):
        return self.ResNet(x)


ml(model=ResNet18, dataset='CIFAR10')

Example: TIMM#

You have access to all the computer vision models of TIMM, straight away!

For example, MobileNet:

from antelope.Agents.Blocks.Architectures.TIMM import TIMM

ml(model=TIMM(name='mobilenetv2_100.ra_in1k'))

All TIMM models are listed here.

CNN#

class Model(nn.Module):
    def __init__(self, in_channels):
        super().__init__()

        self.CNN = nn.Sequential(nn.Conv2d(in_channels, 8), nn.MaxPool2d(2, 2),
                                 nn.Conv2d(8, 8))

    def forward(self, x):
        return self.CNN(x)

ml(model=Model, dataset='CIFAR10')

Since there are no args for the out-shape (e.g. out_shape, out_channels, out_features), the convolution will be followed by a default flattening and output mapping neural network. However, including a learn method would disable this automatic extrapolation. Once a learn method is added, the model is considered sufficiently advanced that it doesn’t need to be automatically extrapolated.

If you’re feeling brave, this works as well:#

Not exactly scalable, but:

ml model='nn.Sequential(nn.Linear(3 * 32 * 32, 128), nn.Linear(128, 10))' dataset=CIFAR10