Shortcuts

Source code for lumin.nn.models.blocks.conv_blocks

from typing import Callable, Union, Optional, Any

import torch.nn as nn
from torch.tensor import Tensor

from ..initialisations import lookup_normal_init
from ..layers.activations import lookup_act


__all__ = ['Conv1DBlock', 'Res1DBlock', 'ResNeXt1DBlock']


[docs]class Conv1DBlock(nn.Module): r''' Basic building block for a building and applying a single 1D convolutional layer. Arguments: in_c: number of input channels (number of features per object / rows in input matrix) out_c: number of output channels (number of features / rows in output matrix) kernel_sz: width of kernel, i.e. the number of columns to overlay padding: amount of padding columns to add at start and end of convolution. If left as 'auto', padding will be automatically computed to conserve the number of columns. stride: number of columns to move kernel when computing convolutions. Stride 1 = kernel centred on each column, stride 2 = kernel centred on ever other column and input size halved, et cetera. act: string representation of argument to pass to lookup_act bn: whether to use batch normalisation (default order weights->activation->batchnorm) lookup_init: function taking choice of activation function, number of inputs, and number of outputs an returning a function to initialise layer weights. lookup_act: function taking choice of activation function and returning an activation function layer Examples:: >>> conv = Conv1DBlock(in_c=3, out_c=16, kernel_sz=3) >>> >>> conv = Conv1DBlock(in_c=16, out_c=32, kernel_sz=3, stride=2) >>> >>> conv = Conv1DBlock(in_c=3, out_c=16, kernel_sz=3, act='swish', bn=True) ''' def __init__(self, in_c:int, out_c:int, kernel_sz:int, padding:Union[int,str]='auto', stride:int=1, act:str='relu', bn:bool=False, lookup_init:Callable[[str,Optional[int],Optional[int]],Callable[[Tensor],None]]=lookup_normal_init, lookup_act:Callable[[str],Any]=lookup_act): super().__init__() self.in_c,self.out_c,self.ks,self.pad,self.stride,self.act,self.bn = in_c,out_c,kernel_sz,padding,stride,act,bn self.lookup_init,self.lookup_act = lookup_init,lookup_act if self.pad == 'auto': self.pad = self.get_padding(self.ks) self.set_layers()
[docs] @staticmethod def get_padding(kernel_sz:int) -> int: r''' Automatically computes the required padding to keep the number of columns equal before and after convolution Arguments: kernel_sz: width of convolutional kernel Returns: size of padding ''' return kernel_sz//2
[docs] def set_layers(self) -> None: r''' One of the main function to overload when inheriting from class. By default calls `self.get_conv_layer` once but can be changed to produce more complicated architectures. Sets `self.layers` to the constructed architecture. ''' self.layers = self.get_conv_layer(in_c=self.in_c, out_c=self.out_c, kernel_sz=self.ks, padding=self.pad, stride=self.stride)
[docs] def get_conv_layer(self, in_c:int, out_c:int, kernel_sz:int, padding:Union[int,str]='auto', stride:int=1, pre_act:bool=False, groups:int=1) -> nn.Module: r''' Builds a sandwich of layers with a single concilutional layer, plus any requested batch norm and activation. Also initialises layers to requested scheme. Arguments: in_c: number of input channels (number of features per object / rows in input matrix) out_c: number of output channels (number of features / rows in output matrix) kernel_sz: width of kernel, i.e. the number of columns to overlay padding: amount of padding columns to add at start and end of convolution. If left as 'auto', padding will be automatically computed to conserve the number of columns. stride: number of columns to move kernel when computing convolutions. Stride 1 = kernel centred on each column, stride 2 = kernel centred on ever other column and input size halved, et cetera. pre_act: whether to apply batchnorm and activation layers prior to the weight layer, or afterwards groups: number of blocks of connections from input channels to output channels ''' if padding == 'auto': padding = self.get_padding(kernel_sz) layers = [] if pre_act: if self.bn: layers.append(nn.BatchNorm1d(in_c)) if self.act != 'linear': layers.append(self.lookup_act(self.act)) layers.append(nn.Conv1d(in_channels=in_c, out_channels=out_c, kernel_size=kernel_sz, padding=padding, stride=stride, groups=groups)) self.lookup_init(self.act)(layers[-1].weight) nn.init.zeros_(layers[-1].bias) if not pre_act: if self.act != 'linear': layers.append(self.lookup_act(self.act)) if self.bn: layers.append(nn.BatchNorm1d(out_c)) return nn.Sequential(*layers)
[docs] def forward(self, x:Tensor) -> Tensor: r''' Passes input through the layers. Might need to be overloaded in inheritance, depending on architecture. Arguments: x: input tensor Returns: Resulting tensor ''' return self.layers(x)
[docs]class Res1DBlock(Conv1DBlock): r''' Basic building block for a building and applying a pair of residually connected 1D convolutional layers (https://arxiv.org/abs/1512.03385). Batchnorm is applied 'pre-activation' as per https://arxiv.org/pdf/1603.05027.pdf, and convolutional shortcuts (again https://arxiv.org/pdf/1603.05027.pdf) are used when the stride of the first layer is greater than 1, or the number of input channels does not equal the number of output channels. Arguments: in_c: number of input channels (number of features per object / rows in input matrix) out_c: number of output channels (number of features / rows in output matrix) kernel_sz: width of kernel, i.e. the number of columns to overlay padding: amount of padding columns to add at start and end of convolution. If left as 'auto', padding will be automatically computed to conserve the number of columns. stride: number of columns to move kernel when computing convolutions. Stride 1 = kernel centred on each column, stride 2 = kernel centred on ever other column and input size halved, et cetera. act: string representation of argument to pass to lookup_act bn: whether to use batch normalisation (order is pre-activation: batchnorm->activation->weights) lookup_init: function taking choice of activation function, number of inputs, and number of outputs an returning a function to initialise layer weights. lookup_act: function taking choice of activation function and returning an activation function layer Examples:: >>> conv = Res1DBlock(in_c=16, out_c=16, kernel_sz=3) >>> >>> conv = Res1DBlock(in_c=16, out_c=32, kernel_sz=3, stride=2) >>> >>> conv = Res1DBlock(in_c=16, out_c=16, kernel_sz=3, act='swish', bn=True) ''' def __init__(self, in_c:int, out_c:int, kernel_sz:int, padding:Union[int,str]='auto', stride:int=1, act:str='relu', bn:bool=False, lookup_init:Callable[[str,Optional[int],Optional[int]],Callable[[Tensor],None]]=lookup_normal_init, lookup_act:Callable[[str],Any]=lookup_act): super().__init__(in_c=in_c, out_c=out_c, kernel_sz=kernel_sz, padding=padding, stride=stride, act=act, bn=bn, lookup_init=lookup_init, lookup_act=lookup_act)
[docs] def set_layers(self): r''' Constructs a pair of pre-activation convolutional layers, and a shortcut layer if necessary. ''' self.layers = nn.Sequential(self.get_conv_layer(in_c=self.in_c, out_c=self.out_c, kernel_sz=self.ks, padding=self.pad, stride=self.stride, pre_act=True), self.get_conv_layer(in_c=self.out_c, out_c=self.out_c, kernel_sz=self.ks, padding=self.pad, stride=1, pre_act=True)) if self.stride != 1 or self.in_c != self.out_c: self.shortcut = nn.Conv1d(in_channels=self.in_c, out_channels=self.out_c, kernel_size=1, stride=self.stride) else: self.shortcut = None
[docs] def forward(self, x:Tensor) -> Tensor: r''' Passes input through the pair of layers and then adds the resulting tensor to the original input, which may be passed through a shortcut connection is necessary. Arguments: x: input tensor Returns: Resulting tensor ''' skip = x if self.shortcut is None else self.shortcut(x) return skip + self.layers(x)
[docs]class ResNeXt1DBlock(Conv1DBlock): r''' Basic building block for a building and applying a set of residually connected groups of 1D convolutional layers (https://arxiv.org/abs/1611.05431). Batchnorm is applied 'pre-activation' as per https://arxiv.org/pdf/1603.05027.pdf, and convolutional shortcuts (again https://arxiv.org/pdf/1603.05027.pdf) are used when the stride of the first layer is greater than 1, or the number of input channels does not equal the number of output channels. Arguments: in_c: number of input channels (number of features per object / rows in input matrix) inter_c: number of intermediate channels in groups cardinality: number of groups out_c: number of output channels (number of features / rows in output matrix) kernel_sz: width of kernel, i.e. the number of columns to overlay padding: amount of padding columns to add at start and end of convolution. If left as 'auto', padding will be automatically computed to conserve the number of columns. stride: number of columns to move kernel when computing convolutions. Stride 1 = kernel centred on each column, stride 2 = kernel centred on ever other column and input size halved, et cetera. act: string representation of argument to pass to lookup_act bn: whether to use batch normalisation (order is pre-activation: batchnorm->activation->weights) lookup_init: function taking choice of activation function, number of inputs, and number of outputs an returning a function to initialise layer weights. lookup_act: function taking choice of activation function and returning an activation function layer Examples:: >>> conv = ResNeXt1DBlock(in_c=32, inter_c=4, cardinality=4, out_c=32, kernel_sz=3) >>> >>> conv = ResNeXt1DBlock(in_c=32, inter_c=4, cardinality=4, out_c=32, kernel_sz=3, stride=2) >>> >>> conv = ResNeXt1DBlock(in_c=32, inter_c=4, cardinality=4, out_c=32, kernel_sz=3, act='swish', bn=True) ''' def __init__(self, in_c:int, inter_c:int, cardinality:int, out_c:int, kernel_sz:int, padding:Union[int,str]='auto', stride:int=1, act:str='relu', bn:bool=False, lookup_init:Callable[[str,Optional[int],Optional[int]],Callable[[Tensor],None]]=lookup_normal_init, lookup_act:Callable[[str],Any]=lookup_act): self.inter_c,self.cardinality = inter_c,cardinality super().__init__(in_c=in_c, out_c=out_c, kernel_sz=kernel_sz, padding=padding, stride=stride, act=act, bn=bn, lookup_init=lookup_init, lookup_act=lookup_act)
[docs] def set_layers(self): r''' Constructs a set of grouped pre-activation convolutional layers, and a shortcut layer if necessary. ''' self.layers = nn.Sequential(self.get_conv_layer(in_c=self.in_c, out_c=self.inter_c*self.cardinality, kernel_sz=1, stride=self.stride, pre_act=True), self.get_conv_layer(in_c=self.inter_c*self.cardinality, out_c=self.inter_c*self.cardinality, kernel_sz=self.ks, padding=self.pad, stride=1, groups=self.cardinality, pre_act=True), self.get_conv_layer(in_c=self.inter_c*self.cardinality, out_c=self.out_c, kernel_sz=1, stride=1, pre_act=True)) if self.stride != 1 or self.in_c != self.out_c: self.shortcut = nn.Conv1d(in_channels=self.in_c, out_channels=self.out_c, kernel_size=1, stride=self.stride) else: self.shortcut = None
[docs] def forward(self, x:Tensor) -> Tensor: r''' Passes input through the set of layers and then adds the resulting tensor to the original input, which may be passed through a shortcut connection is necessary. Arguments: x: input tensor Returns: Resulting tensor ''' skip = x if self.shortcut is None else self.shortcut(x) return skip + self.layers(x)
Read the Docs v: v0.5.1
Versions
latest
stable
v0.5.1
v0.5.0
v0.4.0.1
v0.3.1
Downloads
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.

Docs

Access comprehensive developer and user documentation for LUMIN

View Docs

Tutorials

Get tutorials for beginner and advanced researchers demonstrating many of the features of LUMIN

View Tutorials