Spaces:
Running
on
L40S
Running
on
L40S
# -*- coding: utf-8 -*- | |
# Copyright (c) Facebook, Inc. and its affiliates. | |
import fvcore.nn.weight_init as weight_init | |
from torch import nn | |
from .batch_norm import FrozenBatchNorm2d, get_norm | |
from .wrappers import Conv2d | |
""" | |
CNN building blocks. | |
""" | |
class CNNBlockBase(nn.Module): | |
""" | |
A CNN block is assumed to have input channels, output channels and a stride. | |
The input and output of `forward()` method must be NCHW tensors. | |
The method can perform arbitrary computation but must match the given | |
channels and stride specification. | |
Attribute: | |
in_channels (int): | |
out_channels (int): | |
stride (int): | |
""" | |
def __init__(self, in_channels, out_channels, stride): | |
""" | |
The `__init__` method of any subclass should also contain these arguments. | |
Args: | |
in_channels (int): | |
out_channels (int): | |
stride (int): | |
""" | |
super().__init__() | |
self.in_channels = in_channels | |
self.out_channels = out_channels | |
self.stride = stride | |
def freeze(self): | |
""" | |
Make this block not trainable. | |
This method sets all parameters to `requires_grad=False`, | |
and convert all BatchNorm layers to FrozenBatchNorm | |
Returns: | |
the block itself | |
""" | |
for p in self.parameters(): | |
p.requires_grad = False | |
FrozenBatchNorm2d.convert_frozen_batchnorm(self) | |
return self | |
class DepthwiseSeparableConv2d(nn.Module): | |
""" | |
A kxk depthwise convolution + a 1x1 convolution. | |
In :paper:`xception`, norm & activation are applied on the second conv. | |
:paper:`mobilenet` uses norm & activation on both convs. | |
""" | |
def __init__( | |
self, | |
in_channels, | |
out_channels, | |
kernel_size=3, | |
padding=1, | |
dilation=1, | |
*, | |
norm1=None, | |
activation1=None, | |
norm2=None, | |
activation2=None, | |
): | |
""" | |
Args: | |
norm1, norm2 (str or callable): normalization for the two conv layers. | |
activation1, activation2 (callable(Tensor) -> Tensor): activation | |
function for the two conv layers. | |
""" | |
super().__init__() | |
self.depthwise = Conv2d( | |
in_channels, | |
in_channels, | |
kernel_size=kernel_size, | |
padding=padding, | |
dilation=dilation, | |
groups=in_channels, | |
bias=not norm1, | |
norm=get_norm(norm1, in_channels), | |
activation=activation1, | |
) | |
self.pointwise = Conv2d( | |
in_channels, | |
out_channels, | |
kernel_size=1, | |
bias=not norm2, | |
norm=get_norm(norm2, out_channels), | |
activation=activation2, | |
) | |
# default initialization | |
weight_init.c2_msra_fill(self.depthwise) | |
weight_init.c2_msra_fill(self.pointwise) | |
def forward(self, x): | |
return self.pointwise(self.depthwise(x)) | |