Spaces:
Sleeping
Sleeping
# Copyright (c) OpenMMLab. All rights reserved. | |
import numpy as np | |
import pytest | |
import torch | |
import torch.nn as nn | |
from mmcv.cnn.bricks import (ACTIVATION_LAYERS, CONV_LAYERS, NORM_LAYERS, | |
PADDING_LAYERS, PLUGIN_LAYERS, | |
build_activation_layer, build_conv_layer, | |
build_norm_layer, build_padding_layer, | |
build_plugin_layer, build_upsample_layer, is_norm) | |
from mmcv.cnn.bricks.norm import infer_abbr as infer_norm_abbr | |
from mmcv.cnn.bricks.plugin import infer_abbr as infer_plugin_abbr | |
from mmcv.cnn.bricks.upsample import PixelShufflePack | |
from mmcv.utils.parrots_wrapper import _BatchNorm | |
def test_build_conv_layer(): | |
with pytest.raises(TypeError): | |
# cfg must be a dict | |
cfg = 'Conv2d' | |
build_conv_layer(cfg) | |
with pytest.raises(KeyError): | |
# `type` must be in cfg | |
cfg = dict(kernel_size=3) | |
build_conv_layer(cfg) | |
with pytest.raises(KeyError): | |
# unsupported conv type | |
cfg = dict(type='FancyConv') | |
build_conv_layer(cfg) | |
kwargs = dict( | |
in_channels=4, out_channels=8, kernel_size=3, groups=2, dilation=2) | |
cfg = None | |
layer = build_conv_layer(cfg, **kwargs) | |
assert isinstance(layer, nn.Conv2d) | |
assert layer.in_channels == kwargs['in_channels'] | |
assert layer.out_channels == kwargs['out_channels'] | |
assert layer.kernel_size == (kwargs['kernel_size'], kwargs['kernel_size']) | |
assert layer.groups == kwargs['groups'] | |
assert layer.dilation == (kwargs['dilation'], kwargs['dilation']) | |
cfg = dict(type='Conv') | |
layer = build_conv_layer(cfg, **kwargs) | |
assert isinstance(layer, nn.Conv2d) | |
assert layer.in_channels == kwargs['in_channels'] | |
assert layer.out_channels == kwargs['out_channels'] | |
assert layer.kernel_size == (kwargs['kernel_size'], kwargs['kernel_size']) | |
assert layer.groups == kwargs['groups'] | |
assert layer.dilation == (kwargs['dilation'], kwargs['dilation']) | |
cfg = dict(type='deconv') | |
layer = build_conv_layer(cfg, **kwargs) | |
assert isinstance(layer, nn.ConvTranspose2d) | |
assert layer.in_channels == kwargs['in_channels'] | |
assert layer.out_channels == kwargs['out_channels'] | |
assert layer.kernel_size == (kwargs['kernel_size'], kwargs['kernel_size']) | |
assert layer.groups == kwargs['groups'] | |
assert layer.dilation == (kwargs['dilation'], kwargs['dilation']) | |
# sparse convs cannot support the case when groups>1 | |
kwargs.pop('groups') | |
for type_name, module in CONV_LAYERS.module_dict.items(): | |
cfg = dict(type=type_name) | |
# SparseInverseConv2d and SparseInverseConv3d do not have the argument | |
# 'dilation' | |
if type_name == 'SparseInverseConv2d' or type_name == \ | |
'SparseInverseConv3d': | |
kwargs.pop('dilation') | |
layer = build_conv_layer(cfg, **kwargs) | |
assert isinstance(layer, module) | |
assert layer.in_channels == kwargs['in_channels'] | |
assert layer.out_channels == kwargs['out_channels'] | |
kwargs['dilation'] = 2 # recover the key | |
def test_infer_norm_abbr(): | |
with pytest.raises(TypeError): | |
# class_type must be a class | |
infer_norm_abbr(0) | |
class MyNorm: | |
_abbr_ = 'mn' | |
assert infer_norm_abbr(MyNorm) == 'mn' | |
class FancyBatchNorm: | |
pass | |
assert infer_norm_abbr(FancyBatchNorm) == 'bn' | |
class FancyInstanceNorm: | |
pass | |
assert infer_norm_abbr(FancyInstanceNorm) == 'in' | |
class FancyLayerNorm: | |
pass | |
assert infer_norm_abbr(FancyLayerNorm) == 'ln' | |
class FancyGroupNorm: | |
pass | |
assert infer_norm_abbr(FancyGroupNorm) == 'gn' | |
class FancyNorm: | |
pass | |
assert infer_norm_abbr(FancyNorm) == 'norm_layer' | |
def test_build_norm_layer(): | |
with pytest.raises(TypeError): | |
# cfg must be a dict | |
cfg = 'BN' | |
build_norm_layer(cfg, 3) | |
with pytest.raises(KeyError): | |
# `type` must be in cfg | |
cfg = dict() | |
build_norm_layer(cfg, 3) | |
with pytest.raises(KeyError): | |
# unsupported norm type | |
cfg = dict(type='FancyNorm') | |
build_norm_layer(cfg, 3) | |
with pytest.raises(AssertionError): | |
# postfix must be int or str | |
cfg = dict(type='BN') | |
build_norm_layer(cfg, 3, postfix=[1, 2]) | |
with pytest.raises(AssertionError): | |
# `num_groups` must be in cfg when using 'GN' | |
cfg = dict(type='GN') | |
build_norm_layer(cfg, 3) | |
# test each type of norm layer in norm_cfg | |
abbr_mapping = { | |
'BN': 'bn', | |
'BN1d': 'bn', | |
'BN2d': 'bn', | |
'BN3d': 'bn', | |
'SyncBN': 'bn', | |
'GN': 'gn', | |
'LN': 'ln', | |
'IN': 'in', | |
'IN1d': 'in', | |
'IN2d': 'in', | |
'IN3d': 'in', | |
} | |
for type_name, module in NORM_LAYERS.module_dict.items(): | |
if type_name == 'MMSyncBN': # skip MMSyncBN | |
continue | |
for postfix in ['_test', 1]: | |
cfg = dict(type=type_name) | |
if type_name == 'GN': | |
cfg['num_groups'] = 3 | |
name, layer = build_norm_layer(cfg, 3, postfix=postfix) | |
assert name == abbr_mapping[type_name] + str(postfix) | |
assert isinstance(layer, module) | |
if type_name == 'GN': | |
assert layer.num_channels == 3 | |
assert layer.num_groups == cfg['num_groups'] | |
elif type_name != 'LN': | |
assert layer.num_features == 3 | |
def test_build_activation_layer(): | |
with pytest.raises(TypeError): | |
# cfg must be a dict | |
cfg = 'ReLU' | |
build_activation_layer(cfg) | |
with pytest.raises(KeyError): | |
# `type` must be in cfg | |
cfg = dict() | |
build_activation_layer(cfg) | |
with pytest.raises(KeyError): | |
# unsupported activation type | |
cfg = dict(type='FancyReLU') | |
build_activation_layer(cfg) | |
# test each type of activation layer in activation_cfg | |
for type_name, module in ACTIVATION_LAYERS.module_dict.items(): | |
cfg['type'] = type_name | |
layer = build_activation_layer(cfg) | |
assert isinstance(layer, module) | |
# sanity check for Clamp | |
act = build_activation_layer(dict(type='Clamp')) | |
x = torch.randn(10) * 1000 | |
y = act(x) | |
assert np.logical_and((y >= -1).numpy(), (y <= 1).numpy()).all() | |
act = build_activation_layer(dict(type='Clip', min=0)) | |
y = act(x) | |
assert np.logical_and((y >= 0).numpy(), (y <= 1).numpy()).all() | |
act = build_activation_layer(dict(type='Clamp', max=0)) | |
y = act(x) | |
assert np.logical_and((y >= -1).numpy(), (y <= 0).numpy()).all() | |
def test_build_padding_layer(): | |
with pytest.raises(TypeError): | |
# cfg must be a dict | |
cfg = 'reflect' | |
build_padding_layer(cfg) | |
with pytest.raises(KeyError): | |
# `type` must be in cfg | |
cfg = dict() | |
build_padding_layer(cfg) | |
with pytest.raises(KeyError): | |
# unsupported activation type | |
cfg = dict(type='FancyPad') | |
build_padding_layer(cfg) | |
for type_name, module in PADDING_LAYERS.module_dict.items(): | |
cfg['type'] = type_name | |
layer = build_padding_layer(cfg, 2) | |
assert isinstance(layer, module) | |
input_x = torch.randn(1, 2, 5, 5) | |
cfg = dict(type='reflect') | |
padding_layer = build_padding_layer(cfg, 2) | |
res = padding_layer(input_x) | |
assert res.shape == (1, 2, 9, 9) | |
def test_upsample_layer(): | |
with pytest.raises(TypeError): | |
# cfg must be a dict | |
cfg = 'bilinear' | |
build_upsample_layer(cfg) | |
with pytest.raises(KeyError): | |
# `type` must be in cfg | |
cfg = dict() | |
build_upsample_layer(cfg) | |
with pytest.raises(KeyError): | |
# unsupported activation type | |
cfg = dict(type='FancyUpsample') | |
build_upsample_layer(cfg) | |
for type_name in ['nearest', 'bilinear']: | |
cfg['type'] = type_name | |
layer = build_upsample_layer(cfg) | |
assert isinstance(layer, nn.Upsample) | |
assert layer.mode == type_name | |
cfg = dict( | |
type='deconv', in_channels=3, out_channels=3, kernel_size=3, stride=2) | |
layer = build_upsample_layer(cfg) | |
assert isinstance(layer, nn.ConvTranspose2d) | |
cfg = dict(type='deconv') | |
kwargs = dict(in_channels=3, out_channels=3, kernel_size=3, stride=2) | |
layer = build_upsample_layer(cfg, **kwargs) | |
assert isinstance(layer, nn.ConvTranspose2d) | |
assert layer.in_channels == kwargs['in_channels'] | |
assert layer.out_channels == kwargs['out_channels'] | |
assert layer.kernel_size == (kwargs['kernel_size'], kwargs['kernel_size']) | |
assert layer.stride == (kwargs['stride'], kwargs['stride']) | |
layer = build_upsample_layer(cfg, 3, 3, 3, 2) | |
assert isinstance(layer, nn.ConvTranspose2d) | |
assert layer.in_channels == kwargs['in_channels'] | |
assert layer.out_channels == kwargs['out_channels'] | |
assert layer.kernel_size == (kwargs['kernel_size'], kwargs['kernel_size']) | |
assert layer.stride == (kwargs['stride'], kwargs['stride']) | |
cfg = dict( | |
type='pixel_shuffle', | |
in_channels=3, | |
out_channels=3, | |
scale_factor=2, | |
upsample_kernel=3) | |
layer = build_upsample_layer(cfg) | |
assert isinstance(layer, PixelShufflePack) | |
assert layer.scale_factor == 2 | |
assert layer.upsample_kernel == 3 | |
def test_pixel_shuffle_pack(): | |
x_in = torch.rand(2, 3, 10, 10) | |
pixel_shuffle = PixelShufflePack(3, 3, scale_factor=2, upsample_kernel=3) | |
assert pixel_shuffle.upsample_conv.kernel_size == (3, 3) | |
x_out = pixel_shuffle(x_in) | |
assert x_out.shape == (2, 3, 20, 20) | |
def test_is_norm(): | |
norm_set1 = [ | |
nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d, nn.InstanceNorm1d, | |
nn.InstanceNorm2d, nn.InstanceNorm3d, nn.LayerNorm | |
] | |
norm_set2 = [nn.GroupNorm] | |
for norm_type in norm_set1: | |
layer = norm_type(3) | |
assert is_norm(layer) | |
assert not is_norm(layer, exclude=(norm_type, )) | |
for norm_type in norm_set2: | |
layer = norm_type(3, 6) | |
assert is_norm(layer) | |
assert not is_norm(layer, exclude=(norm_type, )) | |
class MyNorm(nn.BatchNorm2d): | |
pass | |
layer = MyNorm(3) | |
assert is_norm(layer) | |
assert not is_norm(layer, exclude=_BatchNorm) | |
assert not is_norm(layer, exclude=(_BatchNorm, )) | |
layer = nn.Conv2d(3, 8, 1) | |
assert not is_norm(layer) | |
with pytest.raises(TypeError): | |
layer = nn.BatchNorm1d(3) | |
is_norm(layer, exclude='BN') | |
with pytest.raises(TypeError): | |
layer = nn.BatchNorm1d(3) | |
is_norm(layer, exclude=('BN', )) | |
def test_infer_plugin_abbr(): | |
with pytest.raises(TypeError): | |
# class_type must be a class | |
infer_plugin_abbr(0) | |
class MyPlugin: | |
_abbr_ = 'mp' | |
assert infer_plugin_abbr(MyPlugin) == 'mp' | |
class FancyPlugin: | |
pass | |
assert infer_plugin_abbr(FancyPlugin) == 'fancy_plugin' | |
def test_build_plugin_layer(): | |
with pytest.raises(TypeError): | |
# cfg must be a dict | |
cfg = 'Plugin' | |
build_plugin_layer(cfg) | |
with pytest.raises(KeyError): | |
# `type` must be in cfg | |
cfg = dict() | |
build_plugin_layer(cfg) | |
with pytest.raises(KeyError): | |
# unsupported plugin type | |
cfg = dict(type='FancyPlugin') | |
build_plugin_layer(cfg) | |
with pytest.raises(AssertionError): | |
# postfix must be int or str | |
cfg = dict(type='ConvModule') | |
build_plugin_layer(cfg, postfix=[1, 2]) | |
# test ContextBlock | |
for postfix in ['', '_test', 1]: | |
cfg = dict(type='ContextBlock') | |
name, layer = build_plugin_layer( | |
cfg, postfix=postfix, in_channels=16, ratio=1. / 4) | |
assert name == 'context_block' + str(postfix) | |
assert isinstance(layer, PLUGIN_LAYERS.module_dict['ContextBlock']) | |
# test GeneralizedAttention | |
for postfix in ['', '_test', 1]: | |
cfg = dict(type='GeneralizedAttention') | |
name, layer = build_plugin_layer(cfg, postfix=postfix, in_channels=16) | |
assert name == 'gen_attention_block' + str(postfix) | |
assert isinstance(layer, | |
PLUGIN_LAYERS.module_dict['GeneralizedAttention']) | |
# test NonLocal2d | |
for postfix in ['', '_test', 1]: | |
cfg = dict(type='NonLocal2d') | |
name, layer = build_plugin_layer(cfg, postfix=postfix, in_channels=16) | |
assert name == 'nonlocal_block' + str(postfix) | |
assert isinstance(layer, PLUGIN_LAYERS.module_dict['NonLocal2d']) | |
# test ConvModule | |
for postfix in ['', '_test', 1]: | |
cfg = dict(type='ConvModule') | |
name, layer = build_plugin_layer( | |
cfg, | |
postfix=postfix, | |
in_channels=16, | |
out_channels=4, | |
kernel_size=3) | |
assert name == 'conv_block' + str(postfix) | |
assert isinstance(layer, PLUGIN_LAYERS.module_dict['ConvModule']) | |