Initial snapshot: hand motion pipeline (Dyn-HaMR + dex-retargeting + SPIDER)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 14:13:49 +08:00
commit 7e4ef6f98b
7002 changed files with 1452249 additions and 0 deletions
@@ -0,0 +1,21 @@
# Copyright (c) OpenMMLab. All rights reserved.
import torch
from mmpose.models.backbones import AlexNet
def test_alexnet_backbone():
"""Test alexnet backbone."""
model = AlexNet(-1)
model.train()
imgs = torch.randn(1, 3, 256, 192)
feat = model(imgs)
assert feat.shape == (1, 256, 7, 5)
model = AlexNet(1)
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == (1, 1)
@@ -0,0 +1,117 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules import GroupNorm
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones.utils import (InvertedResidual, SELayer,
channel_shuffle, make_divisible)
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (GroupNorm, _BatchNorm)):
return True
return False
def test_make_divisible():
# test min_value is None
result = make_divisible(34, 8, None)
assert result == 32
# test when new_value > min_ratio * value
result = make_divisible(10, 8, min_ratio=0.9)
assert result == 16
# test min_value = 0.8
result = make_divisible(33, 8, min_ratio=0.8)
assert result == 32
def test_channel_shuffle():
x = torch.randn(1, 24, 56, 56)
with pytest.raises(AssertionError):
# num_channels should be divisible by groups
channel_shuffle(x, 7)
groups = 3
batch_size, num_channels, height, width = x.size()
channels_per_group = num_channels // groups
out = channel_shuffle(x, groups)
# test the output value when groups = 3
for b in range(batch_size):
for c in range(num_channels):
c_out = c % channels_per_group * groups + c // channels_per_group
for i in range(height):
for j in range(width):
assert x[b, c, i, j] == out[b, c_out, i, j]
def test_inverted_residual():
with pytest.raises(AssertionError):
# stride must be in [1, 2]
InvertedResidual(16, 16, 32, stride=3)
with pytest.raises(AssertionError):
# se_cfg must be None or dict
InvertedResidual(16, 16, 32, se_cfg=list())
with pytest.raises(AssertionError):
# in_channeld and out_channels must be the same if
# with_expand_conv is False
InvertedResidual(16, 16, 32, with_expand_conv=False)
# Test InvertedResidual forward, stride=1
block = InvertedResidual(16, 16, 32, stride=1)
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert getattr(block, 'se', None) is None
assert block.with_res_shortcut
assert x_out.shape == torch.Size((1, 16, 56, 56))
# Test InvertedResidual forward, stride=2
block = InvertedResidual(16, 16, 32, stride=2)
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert not block.with_res_shortcut
assert x_out.shape == torch.Size((1, 16, 28, 28))
# Test InvertedResidual forward with se layer
se_cfg = dict(channels=32)
block = InvertedResidual(16, 16, 32, stride=1, se_cfg=se_cfg)
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert isinstance(block.se, SELayer)
assert x_out.shape == torch.Size((1, 16, 56, 56))
# Test InvertedResidual forward, with_expand_conv=False
block = InvertedResidual(32, 16, 32, with_expand_conv=False)
x = torch.randn(1, 32, 56, 56)
x_out = block(x)
assert getattr(block, 'expand_conv', None) is None
assert x_out.shape == torch.Size((1, 16, 56, 56))
# Test InvertedResidual forward with GroupNorm
block = InvertedResidual(
16, 16, 32, norm_cfg=dict(type='GN', num_groups=2))
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
for m in block.modules():
if is_norm(m):
assert isinstance(m, GroupNorm)
assert x_out.shape == torch.Size((1, 16, 56, 56))
# Test InvertedResidual forward with HSigmoid
block = InvertedResidual(16, 16, 32, act_cfg=dict(type='HSigmoid'))
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 16, 56, 56))
# Test InvertedResidual forward with checkpoint
block = InvertedResidual(16, 16, 32, with_cp=True)
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert block.with_cp
assert x_out.shape == torch.Size((1, 16, 56, 56))
@@ -0,0 +1,64 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models import CPM
from mmpose.models.backbones.cpm import CpmBlock
def test_cpm_block():
with pytest.raises(AssertionError):
# len(channels) == len(kernels)
CpmBlock(
3, channels=[3, 3, 3], kernels=[
1,
])
# Test CPM Block
model = CpmBlock(3, channels=[3, 3, 3], kernels=[1, 1, 1])
model.train()
imgs = torch.randn(1, 3, 10, 10)
feat = model(imgs)
assert feat.shape == torch.Size([1, 3, 10, 10])
def test_cpm_backbone():
with pytest.raises(AssertionError):
# CPM's num_stacks should larger than 0
CPM(in_channels=3, out_channels=17, num_stages=-1)
with pytest.raises(AssertionError):
# CPM's in_channels should be 3
CPM(in_channels=2, out_channels=17)
# Test CPM
model = CPM(in_channels=3, out_channels=17, num_stages=1)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 256, 192)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([1, 17, 32, 24])
imgs = torch.randn(1, 3, 384, 288)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([1, 17, 48, 36])
imgs = torch.randn(1, 3, 368, 368)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([1, 17, 46, 46])
# Test CPM multi-stages
model = CPM(in_channels=3, out_channels=17, num_stages=2)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 368, 368)
feat = model(imgs)
assert len(feat) == 2
assert feat[0].shape == torch.Size([1, 17, 46, 46])
assert feat[1].shape == torch.Size([1, 17, 46, 46])
@@ -0,0 +1,77 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models import HourglassAENet, HourglassNet
def test_hourglass_backbone():
with pytest.raises(AssertionError):
# HourglassNet's num_stacks should larger than 0
HourglassNet(num_stacks=0)
with pytest.raises(AssertionError):
# len(stage_channels) should equal len(stage_blocks)
HourglassNet(
stage_channels=[256, 256, 384, 384, 384],
stage_blocks=[2, 2, 2, 2, 2, 4])
with pytest.raises(AssertionError):
# len(stage_channels) should larger than downsample_times
HourglassNet(
downsample_times=5,
stage_channels=[256, 256, 384, 384, 384],
stage_blocks=[2, 2, 2, 2, 2])
# Test HourglassNet-52
model = HourglassNet(num_stacks=1)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 256, 256)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([1, 256, 64, 64])
# Test HourglassNet-104
model = HourglassNet(num_stacks=2)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 256, 256)
feat = model(imgs)
assert len(feat) == 2
assert feat[0].shape == torch.Size([1, 256, 64, 64])
assert feat[1].shape == torch.Size([1, 256, 64, 64])
def test_hourglass_ae_backbone():
with pytest.raises(AssertionError):
# HourglassAENet's num_stacks should larger than 0
HourglassAENet(num_stacks=0)
with pytest.raises(AssertionError):
# len(stage_channels) should larger than downsample_times
HourglassAENet(
downsample_times=5, stage_channels=[256, 256, 384, 384, 384])
# num_stack=1
model = HourglassAENet(num_stacks=1)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 256, 256)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([1, 34, 64, 64])
# num_stack=2
model = HourglassAENet(num_stacks=2)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 256, 256)
feat = model(imgs)
assert len(feat) == 2
assert feat[0].shape == torch.Size([1, 34, 64, 64])
assert feat[1].shape == torch.Size([1, 34, 64, 64])
@@ -0,0 +1,187 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models.backbones.hrformer import (HRFomerModule, HRFormer,
HRFormerBlock)
def test_hrformer_module():
norm_cfg = dict(type='BN')
block = HRFormerBlock
# Test multiscale forward
num_channles = (32, 64)
num_inchannels = [c * block.expansion for c in num_channles]
hrmodule = HRFomerModule(
num_branches=2,
block=block,
num_blocks=(2, 2),
num_inchannels=num_inchannels,
num_channels=num_channles,
num_heads=(1, 2),
num_window_sizes=(7, 7),
num_mlp_ratios=(4, 4),
drop_paths=(0., 0.),
norm_cfg=norm_cfg)
feats = [
torch.randn(1, num_inchannels[0], 64, 64),
torch.randn(1, num_inchannels[1], 32, 32)
]
feats = hrmodule(feats)
assert len(str(hrmodule)) > 0
assert len(feats) == 2
assert feats[0].shape == torch.Size([1, num_inchannels[0], 64, 64])
assert feats[1].shape == torch.Size([1, num_inchannels[1], 32, 32])
# Test single scale forward
num_channles = (32, 64)
in_channels = [c * block.expansion for c in num_channles]
hrmodule = HRFomerModule(
num_branches=2,
block=block,
num_blocks=(2, 2),
num_inchannels=num_inchannels,
num_channels=num_channles,
num_heads=(1, 2),
num_window_sizes=(7, 7),
num_mlp_ratios=(4, 4),
drop_paths=(0., 0.),
norm_cfg=norm_cfg,
multiscale_output=False,
)
feats = [
torch.randn(1, in_channels[0], 64, 64),
torch.randn(1, in_channels[1], 32, 32)
]
feats = hrmodule(feats)
assert len(feats) == 1
assert feats[0].shape == torch.Size([1, in_channels[0], 64, 64])
# Test single branch HRFormer module
hrmodule = HRFomerModule(
num_branches=1,
block=block,
num_blocks=(1, ),
num_inchannels=[num_inchannels[0]],
num_channels=[num_channles[0]],
num_heads=(1, ),
num_window_sizes=(7, ),
num_mlp_ratios=(4, ),
drop_paths=(0.1, ),
norm_cfg=norm_cfg,
)
feats = [
torch.randn(1, in_channels[0], 64, 64),
]
feats = hrmodule(feats)
assert len(feats) == 1
assert feats[0].shape == torch.Size([1, in_channels[0], 64, 64])
# Value tests
kwargs = dict(
num_branches=2,
block=block,
num_blocks=(2, 2),
num_inchannels=num_inchannels,
num_channels=num_channles,
num_heads=(1, 2),
num_window_sizes=(7, 7),
num_mlp_ratios=(4, 4),
drop_paths=(0.1, 0.1),
norm_cfg=norm_cfg,
)
with pytest.raises(ValueError):
# len(num_blocks) should equal num_branches
kwargs['num_blocks'] = [2, 2, 2]
HRFomerModule(**kwargs)
kwargs['num_blocks'] = [2, 2]
with pytest.raises(ValueError):
# len(num_blocks) should equal num_branches
kwargs['num_channels'] = [2]
HRFomerModule(**kwargs)
kwargs['num_channels'] = [2, 2]
with pytest.raises(ValueError):
# len(num_blocks) should equal num_branches
kwargs['num_inchannels'] = [2]
HRFomerModule(**kwargs)
kwargs['num_inchannels'] = [2, 2]
def test_hrformer_backbone():
norm_cfg = dict(type='BN')
# only have 3 stages
extra = dict(
drop_path_rate=0.2,
stage1=dict(
num_modules=1,
num_branches=1,
block='BOTTLENECK',
num_blocks=(2, ),
num_channels=(64, )),
stage2=dict(
num_modules=1,
num_branches=2,
block='HRFORMERBLOCK',
window_sizes=(7, 7),
num_heads=(1, 2),
mlp_ratios=(4, 4),
num_blocks=(2, 2),
num_channels=(32, 64)),
stage3=dict(
num_modules=4,
num_branches=3,
block='HRFORMERBLOCK',
window_sizes=(7, 7, 7),
num_heads=(1, 2, 4),
mlp_ratios=(4, 4, 4),
num_blocks=(2, 2, 2),
num_channels=(32, 64, 128)),
stage4=dict(
num_modules=3,
num_branches=4,
block='HRFORMERBLOCK',
window_sizes=(7, 7, 7, 7),
num_heads=(1, 2, 4, 8),
mlp_ratios=(4, 4, 4, 4),
num_blocks=(2, 2, 2, 2),
num_channels=(32, 64, 128, 256),
multiscale_output=True))
with pytest.raises(ValueError):
# len(num_blocks) should equal num_branches
extra['stage4']['num_branches'] = 3
HRFormer(extra=extra)
extra['stage4']['num_branches'] = 4
# Test HRFormer-S
model = HRFormer(extra=extra, norm_cfg=norm_cfg)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 64, 64)
feats = model(imgs)
assert len(feats) == 4
assert feats[0].shape == torch.Size([1, 32, 16, 16])
assert feats[3].shape == torch.Size([1, 256, 2, 2])
# Test single scale output and model
# without relative position bias
extra['stage4']['multiscale_output'] = False
extra['with_rpe'] = False
model = HRFormer(extra=extra, norm_cfg=norm_cfg)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 64, 64)
feats = model(imgs)
assert len(feats) == 1
assert feats[0].shape == torch.Size([1, 32, 16, 16])
@@ -0,0 +1,129 @@
# Copyright (c) OpenMMLab. All rights reserved.
import torch
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import HRNet
from mmpose.models.backbones.hrnet import HRModule
from mmpose.models.backbones.resnet import BasicBlock, Bottleneck
def is_block(modules):
"""Check if is HRModule building block."""
if isinstance(modules, (HRModule, )):
return True
return False
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (_BatchNorm, )):
return True
return False
def all_zeros(modules):
"""Check if the weight(and bias) is all zero."""
weight_zero = torch.equal(modules.weight.data,
torch.zeros_like(modules.weight.data))
if hasattr(modules, 'bias'):
bias_zero = torch.equal(modules.bias.data,
torch.zeros_like(modules.bias.data))
else:
bias_zero = True
return weight_zero and bias_zero
def test_hrmodule():
# Test HRModule forward
block = HRModule(
num_branches=1,
blocks=BasicBlock,
num_blocks=(4, ),
in_channels=[
64,
],
num_channels=(64, ))
x = torch.randn(2, 64, 56, 56)
x_out = block([x])
assert x_out[0].shape == torch.Size([2, 64, 56, 56])
def test_hrnet_backbone():
extra = dict(
stage1=dict(
num_modules=1,
num_branches=1,
block='BOTTLENECK',
num_blocks=(4, ),
num_channels=(64, )),
stage2=dict(
num_modules=1,
num_branches=2,
block='BASIC',
num_blocks=(4, 4),
num_channels=(32, 64)),
stage3=dict(
num_modules=4,
num_branches=3,
block='BASIC',
num_blocks=(4, 4, 4),
num_channels=(32, 64, 128)),
stage4=dict(
num_modules=3,
num_branches=4,
block='BASIC',
num_blocks=(4, 4, 4, 4),
num_channels=(32, 64, 128, 256)))
model = HRNet(extra, in_channels=3)
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([2, 32, 56, 56])
# Test HRNet zero initialization of residual
model = HRNet(extra, in_channels=3, zero_init_residual=True)
model.init_weights()
for m in model.modules():
if isinstance(m, Bottleneck):
assert all_zeros(m.norm3)
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([2, 32, 56, 56])
# Test HRNet with the first three stages frozen
frozen_stages = 3
model = HRNet(extra, in_channels=3, frozen_stages=frozen_stages)
model.init_weights()
model.train()
if frozen_stages >= 0:
assert model.norm1.training is False
assert model.norm2.training is False
for layer in [model.conv1, model.norm1, model.conv2, model.norm2]:
for param in layer.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
if i == 1:
layer = getattr(model, 'layer1')
else:
layer = getattr(model, f'stage{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
if i < 4:
layer = getattr(model, f'transition{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
@@ -0,0 +1,143 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import LiteHRNet
from mmpose.models.backbones.litehrnet import LiteHRModule
from mmpose.models.backbones.resnet import Bottleneck
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (_BatchNorm, )):
return True
return False
def all_zeros(modules):
"""Check if the weight(and bias) is all zero."""
weight_zero = torch.equal(modules.weight.data,
torch.zeros_like(modules.weight.data))
if hasattr(modules, 'bias'):
bias_zero = torch.equal(modules.bias.data,
torch.zeros_like(modules.bias.data))
else:
bias_zero = True
return weight_zero and bias_zero
def test_litehrmodule():
# Test LiteHRModule forward
block = LiteHRModule(
num_branches=1,
num_blocks=1,
in_channels=[
40,
],
reduce_ratio=8,
module_type='LITE')
x = torch.randn(2, 40, 56, 56)
x_out = block([[x]])
assert x_out[0][0].shape == torch.Size([2, 40, 56, 56])
block = LiteHRModule(
num_branches=1,
num_blocks=1,
in_channels=[
40,
],
reduce_ratio=8,
module_type='NAIVE')
x = torch.randn(2, 40, 56, 56)
x_out = block([x])
assert x_out[0].shape == torch.Size([2, 40, 56, 56])
with pytest.raises(ValueError):
block = LiteHRModule(
num_branches=1,
num_blocks=1,
in_channels=[
40,
],
reduce_ratio=8,
module_type='none')
def test_litehrnet_backbone():
extra = dict(
stem=dict(stem_channels=32, out_channels=32, expand_ratio=1),
num_stages=3,
stages_spec=dict(
num_modules=(2, 4, 2),
num_branches=(2, 3, 4),
num_blocks=(2, 2, 2),
module_type=('LITE', 'LITE', 'LITE'),
with_fuse=(True, True, True),
reduce_ratios=(8, 8, 8),
num_channels=(
(40, 80),
(40, 80, 160),
(40, 80, 160, 320),
)),
with_head=True)
model = LiteHRNet(extra, in_channels=3)
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([2, 40, 56, 56])
# Test HRNet zero initialization of residual
model = LiteHRNet(extra, in_channels=3)
model.init_weights()
for m in model.modules():
if isinstance(m, Bottleneck):
assert all_zeros(m.norm3)
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([2, 40, 56, 56])
extra = dict(
stem=dict(stem_channels=32, out_channels=32, expand_ratio=1),
num_stages=3,
stages_spec=dict(
num_modules=(2, 4, 2),
num_branches=(2, 3, 4),
num_blocks=(2, 2, 2),
module_type=('NAIVE', 'NAIVE', 'NAIVE'),
with_fuse=(True, True, True),
reduce_ratios=(8, 8, 8),
num_channels=(
(40, 80),
(40, 80, 160),
(40, 80, 160, 320),
)),
with_head=True)
model = LiteHRNet(extra, in_channels=3)
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([2, 40, 56, 56])
# Test HRNet zero initialization of residual
model = LiteHRNet(extra, in_channels=3)
model.init_weights()
for m in model.modules():
if isinstance(m, Bottleneck):
assert all_zeros(m.norm3)
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 1
assert feat[0].shape == torch.Size([2, 40, 56, 56])
@@ -0,0 +1,257 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules import GroupNorm
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import MobileNetV2
from mmpose.models.backbones.mobilenet_v2 import InvertedResidual
def is_block(modules):
"""Check if is ResNet building block."""
if isinstance(modules, (InvertedResidual, )):
return True
return False
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (GroupNorm, _BatchNorm)):
return True
return False
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_mobilenetv2_invertedresidual():
with pytest.raises(AssertionError):
# stride must be in [1, 2]
InvertedResidual(16, 24, stride=3, expand_ratio=6)
# Test InvertedResidual with checkpoint forward, stride=1
block = InvertedResidual(16, 24, stride=1, expand_ratio=6)
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 24, 56, 56))
# Test InvertedResidual with expand_ratio=1
block = InvertedResidual(16, 16, stride=1, expand_ratio=1)
assert len(block.conv) == 2
# Test InvertedResidual with use_res_connect
block = InvertedResidual(16, 16, stride=1, expand_ratio=6)
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert block.use_res_connect is True
assert x_out.shape == torch.Size((1, 16, 56, 56))
# Test InvertedResidual with checkpoint forward, stride=2
block = InvertedResidual(16, 24, stride=2, expand_ratio=6)
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 24, 28, 28))
# Test InvertedResidual with checkpoint forward
block = InvertedResidual(16, 24, stride=1, expand_ratio=6, with_cp=True)
assert block.with_cp
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 24, 56, 56))
# Test InvertedResidual with act_cfg=dict(type='ReLU')
block = InvertedResidual(
16, 24, stride=1, expand_ratio=6, act_cfg=dict(type='ReLU'))
x = torch.randn(1, 16, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 24, 56, 56))
def test_mobilenetv2_backbone():
with pytest.raises(TypeError):
# pretrained must be a string path
model = MobileNetV2()
model.init_weights(pretrained=0)
with pytest.raises(ValueError):
# frozen_stages must in range(1, 8)
MobileNetV2(frozen_stages=8)
with pytest.raises(ValueError):
# tout_indices in range(-1, 8)
MobileNetV2(out_indices=[8])
# Test MobileNetV2 with first stage frozen
frozen_stages = 1
model = MobileNetV2(frozen_stages=frozen_stages)
model.init_weights()
model.train()
for mod in model.conv1.modules():
for param in mod.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test MobileNetV2 with norm_eval=True
model = MobileNetV2(norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test MobileNetV2 forward with widen_factor=1.0
model = MobileNetV2(widen_factor=1.0, out_indices=range(0, 8))
model.init_weights()
model.train()
assert check_norm_state(model.modules(), True)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 8
assert feat[0].shape == torch.Size((1, 16, 112, 112))
assert feat[1].shape == torch.Size((1, 24, 56, 56))
assert feat[2].shape == torch.Size((1, 32, 28, 28))
assert feat[3].shape == torch.Size((1, 64, 14, 14))
assert feat[4].shape == torch.Size((1, 96, 14, 14))
assert feat[5].shape == torch.Size((1, 160, 7, 7))
assert feat[6].shape == torch.Size((1, 320, 7, 7))
assert feat[7].shape == torch.Size((1, 1280, 7, 7))
# Test MobileNetV2 forward with widen_factor=0.5
model = MobileNetV2(widen_factor=0.5, out_indices=range(0, 7))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 7
assert feat[0].shape == torch.Size((1, 8, 112, 112))
assert feat[1].shape == torch.Size((1, 16, 56, 56))
assert feat[2].shape == torch.Size((1, 16, 28, 28))
assert feat[3].shape == torch.Size((1, 32, 14, 14))
assert feat[4].shape == torch.Size((1, 48, 14, 14))
assert feat[5].shape == torch.Size((1, 80, 7, 7))
assert feat[6].shape == torch.Size((1, 160, 7, 7))
# Test MobileNetV2 forward with widen_factor=2.0
model = MobileNetV2(widen_factor=2.0)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size((1, 2560, 7, 7))
# Test MobileNetV2 forward with out_indices=None
model = MobileNetV2(widen_factor=1.0)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size((1, 1280, 7, 7))
# Test MobileNetV2 forward with dict(type='ReLU')
model = MobileNetV2(
widen_factor=1.0, act_cfg=dict(type='ReLU'), out_indices=range(0, 7))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 7
assert feat[0].shape == torch.Size((1, 16, 112, 112))
assert feat[1].shape == torch.Size((1, 24, 56, 56))
assert feat[2].shape == torch.Size((1, 32, 28, 28))
assert feat[3].shape == torch.Size((1, 64, 14, 14))
assert feat[4].shape == torch.Size((1, 96, 14, 14))
assert feat[5].shape == torch.Size((1, 160, 7, 7))
assert feat[6].shape == torch.Size((1, 320, 7, 7))
# Test MobileNetV2 with GroupNorm forward
model = MobileNetV2(widen_factor=1.0, out_indices=range(0, 7))
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 7
assert feat[0].shape == torch.Size((1, 16, 112, 112))
assert feat[1].shape == torch.Size((1, 24, 56, 56))
assert feat[2].shape == torch.Size((1, 32, 28, 28))
assert feat[3].shape == torch.Size((1, 64, 14, 14))
assert feat[4].shape == torch.Size((1, 96, 14, 14))
assert feat[5].shape == torch.Size((1, 160, 7, 7))
assert feat[6].shape == torch.Size((1, 320, 7, 7))
# Test MobileNetV2 with BatchNorm forward
model = MobileNetV2(
widen_factor=1.0,
norm_cfg=dict(type='GN', num_groups=2, requires_grad=True),
out_indices=range(0, 7))
for m in model.modules():
if is_norm(m):
assert isinstance(m, GroupNorm)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 7
assert feat[0].shape == torch.Size((1, 16, 112, 112))
assert feat[1].shape == torch.Size((1, 24, 56, 56))
assert feat[2].shape == torch.Size((1, 32, 28, 28))
assert feat[3].shape == torch.Size((1, 64, 14, 14))
assert feat[4].shape == torch.Size((1, 96, 14, 14))
assert feat[5].shape == torch.Size((1, 160, 7, 7))
assert feat[6].shape == torch.Size((1, 320, 7, 7))
# Test MobileNetV2 with layers 1, 3, 5 out forward
model = MobileNetV2(widen_factor=1.0, out_indices=(0, 2, 4))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size((1, 16, 112, 112))
assert feat[1].shape == torch.Size((1, 32, 28, 28))
assert feat[2].shape == torch.Size((1, 96, 14, 14))
# Test MobileNetV2 with checkpoint forward
model = MobileNetV2(
widen_factor=1.0, with_cp=True, out_indices=range(0, 7))
for m in model.modules():
if is_block(m):
assert m.with_cp
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 7
assert feat[0].shape == torch.Size((1, 16, 112, 112))
assert feat[1].shape == torch.Size((1, 24, 56, 56))
assert feat[2].shape == torch.Size((1, 32, 28, 28))
assert feat[3].shape == torch.Size((1, 64, 14, 14))
assert feat[4].shape == torch.Size((1, 96, 14, 14))
assert feat[5].shape == torch.Size((1, 160, 7, 7))
assert feat[6].shape == torch.Size((1, 320, 7, 7))
@@ -0,0 +1,169 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules import GroupNorm
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import MobileNetV3
from mmpose.models.backbones.utils import InvertedResidual
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (GroupNorm, _BatchNorm)):
return True
return False
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_mobilenetv3_backbone():
with pytest.raises(TypeError):
# pretrained must be a string path
model = MobileNetV3()
model.init_weights(pretrained=0)
with pytest.raises(AssertionError):
# arch must in [small, big]
MobileNetV3(arch='others')
with pytest.raises(ValueError):
# frozen_stages must less than 12 when arch is small
MobileNetV3(arch='small', frozen_stages=12)
with pytest.raises(ValueError):
# frozen_stages must less than 16 when arch is big
MobileNetV3(arch='big', frozen_stages=16)
with pytest.raises(ValueError):
# max out_indices must less than 11 when arch is small
MobileNetV3(arch='small', out_indices=(11, ))
with pytest.raises(ValueError):
# max out_indices must less than 15 when arch is big
MobileNetV3(arch='big', out_indices=(15, ))
# Test MobileNetv3
model = MobileNetV3()
model.init_weights()
model.train()
# Test MobileNetv3 with first stage frozen
frozen_stages = 1
model = MobileNetV3(frozen_stages=frozen_stages)
model.init_weights()
model.train()
for param in model.conv1.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test MobileNetv3 with norm eval
model = MobileNetV3(norm_eval=True, out_indices=range(0, 11))
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test MobileNetv3 forward with small arch
model = MobileNetV3(out_indices=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 11
assert feat[0].shape == torch.Size([1, 16, 56, 56])
assert feat[1].shape == torch.Size([1, 24, 28, 28])
assert feat[2].shape == torch.Size([1, 24, 28, 28])
assert feat[3].shape == torch.Size([1, 40, 14, 14])
assert feat[4].shape == torch.Size([1, 40, 14, 14])
assert feat[5].shape == torch.Size([1, 40, 14, 14])
assert feat[6].shape == torch.Size([1, 48, 14, 14])
assert feat[7].shape == torch.Size([1, 48, 14, 14])
assert feat[8].shape == torch.Size([1, 96, 7, 7])
assert feat[9].shape == torch.Size([1, 96, 7, 7])
assert feat[10].shape == torch.Size([1, 96, 7, 7])
# Test MobileNetv3 forward with small arch and GroupNorm
model = MobileNetV3(
out_indices=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
norm_cfg=dict(type='GN', num_groups=2, requires_grad=True))
for m in model.modules():
if is_norm(m):
assert isinstance(m, GroupNorm)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 11
assert feat[0].shape == torch.Size([1, 16, 56, 56])
assert feat[1].shape == torch.Size([1, 24, 28, 28])
assert feat[2].shape == torch.Size([1, 24, 28, 28])
assert feat[3].shape == torch.Size([1, 40, 14, 14])
assert feat[4].shape == torch.Size([1, 40, 14, 14])
assert feat[5].shape == torch.Size([1, 40, 14, 14])
assert feat[6].shape == torch.Size([1, 48, 14, 14])
assert feat[7].shape == torch.Size([1, 48, 14, 14])
assert feat[8].shape == torch.Size([1, 96, 7, 7])
assert feat[9].shape == torch.Size([1, 96, 7, 7])
assert feat[10].shape == torch.Size([1, 96, 7, 7])
# Test MobileNetv3 forward with big arch
model = MobileNetV3(
arch='big',
out_indices=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 15
assert feat[0].shape == torch.Size([1, 16, 112, 112])
assert feat[1].shape == torch.Size([1, 24, 56, 56])
assert feat[2].shape == torch.Size([1, 24, 56, 56])
assert feat[3].shape == torch.Size([1, 40, 28, 28])
assert feat[4].shape == torch.Size([1, 40, 28, 28])
assert feat[5].shape == torch.Size([1, 40, 28, 28])
assert feat[6].shape == torch.Size([1, 80, 14, 14])
assert feat[7].shape == torch.Size([1, 80, 14, 14])
assert feat[8].shape == torch.Size([1, 80, 14, 14])
assert feat[9].shape == torch.Size([1, 80, 14, 14])
assert feat[10].shape == torch.Size([1, 112, 14, 14])
assert feat[11].shape == torch.Size([1, 112, 14, 14])
assert feat[12].shape == torch.Size([1, 160, 14, 14])
assert feat[13].shape == torch.Size([1, 160, 7, 7])
assert feat[14].shape == torch.Size([1, 160, 7, 7])
# Test MobileNetv3 forward with big arch
model = MobileNetV3(arch='big', out_indices=(0, ))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 16, 112, 112])
# Test MobileNetv3 with checkpoint forward
model = MobileNetV3(with_cp=True)
for m in model.modules():
if isinstance(m, InvertedResidual):
assert m.with_cp
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 96, 7, 7])
@@ -0,0 +1,32 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models import MSPN
def test_mspn_backbone():
with pytest.raises(AssertionError):
# MSPN's num_stages should larger than 0
MSPN(num_stages=0)
with pytest.raises(AssertionError):
# MSPN's num_units should larger than 1
MSPN(num_units=1)
with pytest.raises(AssertionError):
# len(num_blocks) should equal num_units
MSPN(num_units=2, num_blocks=[2, 2, 2])
# Test MSPN's outputs
model = MSPN(num_stages=2, num_units=2, num_blocks=[2, 2])
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 511, 511)
feat = model(imgs)
assert len(feat) == 2
assert len(feat[0]) == 2
assert len(feat[1]) == 2
assert feat[0][0].shape == torch.Size([1, 256, 64, 64])
assert feat[0][1].shape == torch.Size([1, 256, 128, 128])
assert feat[1][0].shape == torch.Size([1, 256, 64, 64])
assert feat[1][1].shape == torch.Size([1, 256, 128, 128])
@@ -0,0 +1,92 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models.backbones import RegNet
regnet_test_data = [
('regnetx_400mf',
dict(w0=24, wa=24.48, wm=2.54, group_w=16, depth=22,
bot_mul=1.0), [32, 64, 160, 384]),
('regnetx_800mf',
dict(w0=56, wa=35.73, wm=2.28, group_w=16, depth=16,
bot_mul=1.0), [64, 128, 288, 672]),
('regnetx_1.6gf',
dict(w0=80, wa=34.01, wm=2.25, group_w=24, depth=18,
bot_mul=1.0), [72, 168, 408, 912]),
('regnetx_3.2gf',
dict(w0=88, wa=26.31, wm=2.25, group_w=48, depth=25,
bot_mul=1.0), [96, 192, 432, 1008]),
('regnetx_4.0gf',
dict(w0=96, wa=38.65, wm=2.43, group_w=40, depth=23,
bot_mul=1.0), [80, 240, 560, 1360]),
('regnetx_6.4gf',
dict(w0=184, wa=60.83, wm=2.07, group_w=56, depth=17,
bot_mul=1.0), [168, 392, 784, 1624]),
('regnetx_8.0gf',
dict(w0=80, wa=49.56, wm=2.88, group_w=120, depth=23,
bot_mul=1.0), [80, 240, 720, 1920]),
('regnetx_12gf',
dict(w0=168, wa=73.36, wm=2.37, group_w=112, depth=19,
bot_mul=1.0), [224, 448, 896, 2240]),
]
@pytest.mark.parametrize('arch_name,arch,out_channels', regnet_test_data)
def test_regnet_backbone(arch_name, arch, out_channels):
with pytest.raises(AssertionError):
# ResNeXt depth should be in [50, 101, 152]
RegNet(arch_name + '233')
# output the last feature map
model = RegNet(arch_name)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert isinstance(feat, torch.Tensor)
assert feat.shape == (1, out_channels[-1], 7, 7)
# output feature map of all stages
model = RegNet(arch_name, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, out_channels[0], 56, 56)
assert feat[1].shape == (1, out_channels[1], 28, 28)
assert feat[2].shape == (1, out_channels[2], 14, 14)
assert feat[3].shape == (1, out_channels[3], 7, 7)
@pytest.mark.parametrize('arch_name,arch,out_channels', regnet_test_data)
def test_custom_arch(arch_name, arch, out_channels):
# output the last feature map
model = RegNet(arch)
model.init_weights()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert isinstance(feat, torch.Tensor)
assert feat.shape == (1, out_channels[-1], 7, 7)
# output feature map of all stages
model = RegNet(arch, out_indices=(0, 1, 2, 3))
model.init_weights()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, out_channels[0], 56, 56)
assert feat[1].shape == (1, out_channels[1], 28, 28)
assert feat[2].shape == (1, out_channels[2], 14, 14)
assert feat[3].shape == (1, out_channels[3], 7, 7)
def test_exception():
# arch must be a str or dict
with pytest.raises(TypeError):
_ = RegNet(50)
@@ -0,0 +1,44 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models.backbones import ResNeSt
from mmpose.models.backbones.resnest import Bottleneck as BottleneckS
def test_bottleneck():
with pytest.raises(AssertionError):
# Style must be in ['pytorch', 'caffe']
BottleneckS(64, 64, radix=2, reduction_factor=4, style='tensorflow')
# Test ResNeSt Bottleneck structure
block = BottleneckS(
64, 256, radix=2, reduction_factor=4, stride=2, style='pytorch')
assert block.avd_layer.stride == 2
assert block.conv2.channels == 64
# Test ResNeSt Bottleneck forward
block = BottleneckS(64, 64, radix=2, reduction_factor=4)
x = torch.randn(2, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([2, 64, 56, 56])
def test_resnest():
with pytest.raises(KeyError):
# ResNeSt depth should be in [50, 101, 152, 200]
ResNeSt(depth=18)
# Test ResNeSt with radix 2, reduction_factor 4
model = ResNeSt(
depth=50, radix=2, reduction_factor=4, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([2, 256, 56, 56])
assert feat[1].shape == torch.Size([2, 512, 28, 28])
assert feat[2].shape == torch.Size([2, 1024, 14, 14])
assert feat[3].shape == torch.Size([2, 2048, 7, 7])
@@ -0,0 +1,562 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
import torch.nn as nn
from mmcv.cnn import ConvModule
from mmcv.utils.parrots_wrapper import _BatchNorm
from mmpose.models.backbones import ResNet, ResNetV1d
from mmpose.models.backbones.resnet import (BasicBlock, Bottleneck, ResLayer,
get_expansion)
def is_block(modules):
"""Check if is ResNet building block."""
if isinstance(modules, (BasicBlock, Bottleneck)):
return True
return False
def all_zeros(modules):
"""Check if the weight(and bias) is all zero."""
weight_zero = torch.equal(modules.weight.data,
torch.zeros_like(modules.weight.data))
if hasattr(modules, 'bias'):
bias_zero = torch.equal(modules.bias.data,
torch.zeros_like(modules.bias.data))
else:
bias_zero = True
return weight_zero and bias_zero
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_get_expansion():
assert get_expansion(Bottleneck, 2) == 2
assert get_expansion(BasicBlock) == 1
assert get_expansion(Bottleneck) == 4
class MyResBlock(nn.Module):
expansion = 8
assert get_expansion(MyResBlock) == 8
# expansion must be an integer or None
with pytest.raises(TypeError):
get_expansion(Bottleneck, '0')
# expansion is not specified and cannot be inferred
with pytest.raises(TypeError):
class SomeModule(nn.Module):
pass
get_expansion(SomeModule)
def test_basic_block():
# expansion must be 1
with pytest.raises(AssertionError):
BasicBlock(64, 64, expansion=2)
# BasicBlock with stride 1, out_channels == in_channels
block = BasicBlock(64, 64)
assert block.in_channels == 64
assert block.mid_channels == 64
assert block.out_channels == 64
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 64
assert block.conv1.kernel_size == (3, 3)
assert block.conv1.stride == (1, 1)
assert block.conv2.in_channels == 64
assert block.conv2.out_channels == 64
assert block.conv2.kernel_size == (3, 3)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
# BasicBlock with stride 1 and downsample
downsample = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=1, bias=False), nn.BatchNorm2d(128))
block = BasicBlock(64, 128, downsample=downsample)
assert block.in_channels == 64
assert block.mid_channels == 128
assert block.out_channels == 128
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 128
assert block.conv1.kernel_size == (3, 3)
assert block.conv1.stride == (1, 1)
assert block.conv2.in_channels == 128
assert block.conv2.out_channels == 128
assert block.conv2.kernel_size == (3, 3)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 128, 56, 56])
# BasicBlock with stride 2 and downsample
downsample = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=1, stride=2, bias=False),
nn.BatchNorm2d(128))
block = BasicBlock(64, 128, stride=2, downsample=downsample)
assert block.in_channels == 64
assert block.mid_channels == 128
assert block.out_channels == 128
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 128
assert block.conv1.kernel_size == (3, 3)
assert block.conv1.stride == (2, 2)
assert block.conv2.in_channels == 128
assert block.conv2.out_channels == 128
assert block.conv2.kernel_size == (3, 3)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 128, 28, 28])
# forward with checkpointing
block = BasicBlock(64, 64, with_cp=True)
assert block.with_cp
x = torch.randn(1, 64, 56, 56, requires_grad=True)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_bottleneck():
# style must be in ['pytorch', 'caffe']
with pytest.raises(AssertionError):
Bottleneck(64, 64, style='tensorflow')
# expansion must be divisible by out_channels
with pytest.raises(AssertionError):
Bottleneck(64, 64, expansion=3)
# Test Bottleneck style
block = Bottleneck(64, 64, stride=2, style='pytorch')
assert block.conv1.stride == (1, 1)
assert block.conv2.stride == (2, 2)
block = Bottleneck(64, 64, stride=2, style='caffe')
assert block.conv1.stride == (2, 2)
assert block.conv2.stride == (1, 1)
# Bottleneck with stride 1
block = Bottleneck(64, 64, style='pytorch')
assert block.in_channels == 64
assert block.mid_channels == 16
assert block.out_channels == 64
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 16
assert block.conv1.kernel_size == (1, 1)
assert block.conv2.in_channels == 16
assert block.conv2.out_channels == 16
assert block.conv2.kernel_size == (3, 3)
assert block.conv3.in_channels == 16
assert block.conv3.out_channels == 64
assert block.conv3.kernel_size == (1, 1)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 64, 56, 56)
# Bottleneck with stride 1 and downsample
downsample = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=1), nn.BatchNorm2d(128))
block = Bottleneck(64, 128, style='pytorch', downsample=downsample)
assert block.in_channels == 64
assert block.mid_channels == 32
assert block.out_channels == 128
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 32
assert block.conv1.kernel_size == (1, 1)
assert block.conv2.in_channels == 32
assert block.conv2.out_channels == 32
assert block.conv2.kernel_size == (3, 3)
assert block.conv3.in_channels == 32
assert block.conv3.out_channels == 128
assert block.conv3.kernel_size == (1, 1)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 128, 56, 56)
# Bottleneck with stride 2 and downsample
downsample = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=1, stride=2), nn.BatchNorm2d(128))
block = Bottleneck(
64, 128, stride=2, style='pytorch', downsample=downsample)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 128, 28, 28)
# Bottleneck with expansion 2
block = Bottleneck(64, 64, style='pytorch', expansion=2)
assert block.in_channels == 64
assert block.mid_channels == 32
assert block.out_channels == 64
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 32
assert block.conv1.kernel_size == (1, 1)
assert block.conv2.in_channels == 32
assert block.conv2.out_channels == 32
assert block.conv2.kernel_size == (3, 3)
assert block.conv3.in_channels == 32
assert block.conv3.out_channels == 64
assert block.conv3.kernel_size == (1, 1)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 64, 56, 56)
# Test Bottleneck with checkpointing
block = Bottleneck(64, 64, with_cp=True)
block.train()
assert block.with_cp
x = torch.randn(1, 64, 56, 56, requires_grad=True)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_basicblock_reslayer():
# 3 BasicBlock w/o downsample
layer = ResLayer(BasicBlock, 3, 32, 32)
assert len(layer) == 3
for i in range(3):
assert layer[i].in_channels == 32
assert layer[i].out_channels == 32
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 32, 56, 56)
# 3 BasicBlock w/ stride 1 and downsample
layer = ResLayer(BasicBlock, 3, 32, 64)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].downsample is not None and len(layer[0].downsample) == 2
assert isinstance(layer[0].downsample[0], nn.Conv2d)
assert layer[0].downsample[0].stride == (1, 1)
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 56, 56)
# 3 BasicBlock w/ stride 2 and downsample
layer = ResLayer(BasicBlock, 3, 32, 64, stride=2)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 2
assert layer[0].downsample is not None and len(layer[0].downsample) == 2
assert isinstance(layer[0].downsample[0], nn.Conv2d)
assert layer[0].downsample[0].stride == (2, 2)
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 28, 28)
# 3 BasicBlock w/ stride 2 and downsample with avg pool
layer = ResLayer(BasicBlock, 3, 32, 64, stride=2, avg_down=True)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 2
assert layer[0].downsample is not None and len(layer[0].downsample) == 3
assert isinstance(layer[0].downsample[0], nn.AvgPool2d)
assert layer[0].downsample[0].stride == 2
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 28, 28)
def test_bottleneck_reslayer():
# 3 Bottleneck w/o downsample
layer = ResLayer(Bottleneck, 3, 32, 32)
assert len(layer) == 3
for i in range(3):
assert layer[i].in_channels == 32
assert layer[i].out_channels == 32
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 32, 56, 56)
# 3 Bottleneck w/ stride 1 and downsample
layer = ResLayer(Bottleneck, 3, 32, 64)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 1
assert layer[0].conv1.out_channels == 16
assert layer[0].downsample is not None and len(layer[0].downsample) == 2
assert isinstance(layer[0].downsample[0], nn.Conv2d)
assert layer[0].downsample[0].stride == (1, 1)
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].conv1.out_channels == 16
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 56, 56)
# 3 Bottleneck w/ stride 2 and downsample
layer = ResLayer(Bottleneck, 3, 32, 64, stride=2)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 2
assert layer[0].conv1.out_channels == 16
assert layer[0].downsample is not None and len(layer[0].downsample) == 2
assert isinstance(layer[0].downsample[0], nn.Conv2d)
assert layer[0].downsample[0].stride == (2, 2)
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].conv1.out_channels == 16
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 28, 28)
# 3 Bottleneck w/ stride 2 and downsample with avg pool
layer = ResLayer(Bottleneck, 3, 32, 64, stride=2, avg_down=True)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 2
assert layer[0].conv1.out_channels == 16
assert layer[0].downsample is not None and len(layer[0].downsample) == 3
assert isinstance(layer[0].downsample[0], nn.AvgPool2d)
assert layer[0].downsample[0].stride == 2
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].conv1.out_channels == 16
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 28, 28)
# 3 Bottleneck with custom expansion
layer = ResLayer(Bottleneck, 3, 32, 32, expansion=2)
assert len(layer) == 3
for i in range(3):
assert layer[i].in_channels == 32
assert layer[i].out_channels == 32
assert layer[i].stride == 1
assert layer[i].conv1.out_channels == 16
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 32, 56, 56)
def test_resnet():
"""Test resnet backbone."""
with pytest.raises(KeyError):
# ResNet depth should be in [18, 34, 50, 101, 152]
ResNet(20)
with pytest.raises(AssertionError):
# In ResNet: 1 <= num_stages <= 4
ResNet(50, num_stages=0)
with pytest.raises(AssertionError):
# In ResNet: 1 <= num_stages <= 4
ResNet(50, num_stages=5)
with pytest.raises(AssertionError):
# len(strides) == len(dilations) == num_stages
ResNet(50, strides=(1, ), dilations=(1, 1), num_stages=3)
with pytest.raises(TypeError):
# pretrained must be a string path
model = ResNet(50)
model.init_weights(pretrained=0)
with pytest.raises(AssertionError):
# Style must be in ['pytorch', 'caffe']
ResNet(50, style='tensorflow')
# Test ResNet50 norm_eval=True
model = ResNet(50, norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test ResNet50 with torchvision pretrained weight
model = ResNet(depth=50, norm_eval=True)
model.init_weights('torchvision://resnet50')
model.train()
assert check_norm_state(model.modules(), False)
# Test ResNet50 with first stage frozen
frozen_stages = 1
model = ResNet(50, frozen_stages=frozen_stages)
model.init_weights()
model.train()
assert model.norm1.training is False
for layer in [model.conv1, model.norm1]:
for param in layer.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test ResNet18 forward
model = ResNet(18, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, 64, 56, 56)
assert feat[1].shape == (1, 128, 28, 28)
assert feat[2].shape == (1, 256, 14, 14)
assert feat[3].shape == (1, 512, 7, 7)
# Test ResNet50 with BatchNorm forward
model = ResNet(50, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, 256, 56, 56)
assert feat[1].shape == (1, 512, 28, 28)
assert feat[2].shape == (1, 1024, 14, 14)
assert feat[3].shape == (1, 2048, 7, 7)
# Test ResNet50 with layers 1, 2, 3 out forward
model = ResNet(50, out_indices=(0, 1, 2))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == (1, 256, 56, 56)
assert feat[1].shape == (1, 512, 28, 28)
assert feat[2].shape == (1, 1024, 14, 14)
# Test ResNet50 with layers 3 (top feature maps) out forward
model = ResNet(50, out_indices=(3, ))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == (1, 2048, 7, 7)
# Test ResNet50 with checkpoint forward
model = ResNet(50, out_indices=(0, 1, 2, 3), with_cp=True)
for m in model.modules():
if is_block(m):
assert m.with_cp
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, 256, 56, 56)
assert feat[1].shape == (1, 512, 28, 28)
assert feat[2].shape == (1, 1024, 14, 14)
assert feat[3].shape == (1, 2048, 7, 7)
# zero initialization of residual blocks
model = ResNet(50, out_indices=(0, 1, 2, 3), zero_init_residual=True)
model.init_weights()
for m in model.modules():
if isinstance(m, Bottleneck):
assert all_zeros(m.norm3)
elif isinstance(m, BasicBlock):
assert all_zeros(m.norm2)
# non-zero initialization of residual blocks
model = ResNet(50, out_indices=(0, 1, 2, 3), zero_init_residual=False)
model.init_weights()
for m in model.modules():
if isinstance(m, Bottleneck):
assert not all_zeros(m.norm3)
elif isinstance(m, BasicBlock):
assert not all_zeros(m.norm2)
def test_resnet_v1d():
model = ResNetV1d(depth=50, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
assert len(model.stem) == 3
for i in range(3):
assert isinstance(model.stem[i], ConvModule)
imgs = torch.randn(1, 3, 224, 224)
feat = model.stem(imgs)
assert feat.shape == (1, 64, 112, 112)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, 256, 56, 56)
assert feat[1].shape == (1, 512, 28, 28)
assert feat[2].shape == (1, 1024, 14, 14)
assert feat[3].shape == (1, 2048, 7, 7)
# Test ResNet50V1d with first stage frozen
frozen_stages = 1
model = ResNetV1d(depth=50, frozen_stages=frozen_stages)
assert len(model.stem) == 3
for i in range(3):
assert isinstance(model.stem[i], ConvModule)
model.init_weights()
model.train()
check_norm_state(model.stem, False)
for param in model.stem.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
def test_resnet_half_channel():
model = ResNet(50, base_channels=32, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, 128, 56, 56)
assert feat[1].shape == (1, 256, 28, 28)
assert feat[2].shape == (1, 512, 14, 14)
assert feat[3].shape == (1, 1024, 7, 7)
@@ -0,0 +1,60 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models.backbones import ResNeXt
from mmpose.models.backbones.resnext import Bottleneck as BottleneckX
def test_bottleneck():
with pytest.raises(AssertionError):
# Style must be in ['pytorch', 'caffe']
BottleneckX(64, 64, groups=32, width_per_group=4, style='tensorflow')
# Test ResNeXt Bottleneck structure
block = BottleneckX(
64, 256, groups=32, width_per_group=4, stride=2, style='pytorch')
assert block.conv2.stride == (2, 2)
assert block.conv2.groups == 32
assert block.conv2.out_channels == 128
# Test ResNeXt Bottleneck forward
block = BottleneckX(64, 64, base_channels=16, groups=32, width_per_group=4)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_resnext():
with pytest.raises(KeyError):
# ResNeXt depth should be in [50, 101, 152]
ResNeXt(depth=18)
# Test ResNeXt with group 32, width_per_group 4
model = ResNeXt(
depth=50, groups=32, width_per_group=4, out_indices=(0, 1, 2, 3))
for m in model.modules():
if isinstance(m, BottleneckX):
assert m.conv2.groups == 32
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([1, 256, 56, 56])
assert feat[1].shape == torch.Size([1, 512, 28, 28])
assert feat[2].shape == torch.Size([1, 1024, 14, 14])
assert feat[3].shape == torch.Size([1, 2048, 7, 7])
# Test ResNeXt with group 32, width_per_group 4 and layers 3 out forward
model = ResNeXt(depth=50, groups=32, width_per_group=4, out_indices=(3, ))
for m in model.modules():
if isinstance(m, BottleneckX):
assert m.conv2.groups == 32
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 2048, 7, 7])
@@ -0,0 +1,35 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models import RSN
def test_rsn_backbone():
with pytest.raises(AssertionError):
# RSN's num_stages should larger than 0
RSN(num_stages=0)
with pytest.raises(AssertionError):
# RSN's num_steps should larger than 1
RSN(num_steps=1)
with pytest.raises(AssertionError):
# RSN's num_units should larger than 1
RSN(num_units=1)
with pytest.raises(AssertionError):
# len(num_blocks) should equal num_units
RSN(num_units=2, num_blocks=[2, 2, 2])
# Test RSN's outputs
model = RSN(num_stages=2, num_units=2, num_blocks=[2, 2])
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 511, 511)
feat = model(imgs)
assert len(feat) == 2
assert len(feat[0]) == 2
assert len(feat[1]) == 2
assert feat[0][0].shape == torch.Size([1, 256, 64, 64])
assert feat[0][1].shape == torch.Size([1, 256, 128, 128])
assert feat[1][0].shape == torch.Size([1, 256, 64, 64])
assert feat[1][1].shape == torch.Size([1, 256, 128, 128])
@@ -0,0 +1,163 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import SCNet
from mmpose.models.backbones.scnet import SCBottleneck, SCConv
def is_block(modules):
"""Check if is SCNet building block."""
if isinstance(modules, (SCBottleneck, )):
return True
return False
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (_BatchNorm, )):
return True
return False
def all_zeros(modules):
"""Check if the weight(and bias) is all zero."""
weight_zero = torch.equal(modules.weight.data,
torch.zeros_like(modules.weight.data))
if hasattr(modules, 'bias'):
bias_zero = torch.equal(modules.bias.data,
torch.zeros_like(modules.bias.data))
else:
bias_zero = True
return weight_zero and bias_zero
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_scnet_scconv():
# Test scconv forward
layer = SCConv(64, 64, 1, 4)
x = torch.randn(1, 64, 56, 56)
x_out = layer(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_scnet_bottleneck():
# Test Bottleneck forward
block = SCBottleneck(64, 64)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_scnet_backbone():
"""Test scnet backbone."""
with pytest.raises(KeyError):
# SCNet depth should be in [50, 101]
SCNet(20)
with pytest.raises(TypeError):
# pretrained must be a string path
model = SCNet(50)
model.init_weights(pretrained=0)
# Test SCNet norm_eval=True
model = SCNet(50, norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test SCNet50 with first stage frozen
frozen_stages = 1
model = SCNet(50, frozen_stages=frozen_stages)
model.init_weights()
model.train()
assert model.norm1.training is False
for layer in [model.conv1, model.norm1]:
for param in layer.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test SCNet with BatchNorm forward
model = SCNet(50, out_indices=(0, 1, 2, 3))
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
model.init_weights()
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([2, 256, 56, 56])
assert feat[1].shape == torch.Size([2, 512, 28, 28])
assert feat[2].shape == torch.Size([2, 1024, 14, 14])
assert feat[3].shape == torch.Size([2, 2048, 7, 7])
# Test SCNet with layers 1, 2, 3 out forward
model = SCNet(50, out_indices=(0, 1, 2))
model.init_weights()
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size([2, 256, 56, 56])
assert feat[1].shape == torch.Size([2, 512, 28, 28])
assert feat[2].shape == torch.Size([2, 1024, 14, 14])
# Test SEResNet50 with layers 3 (top feature maps) out forward
model = SCNet(50, out_indices=(3, ))
model.init_weights()
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([2, 2048, 7, 7])
# Test SEResNet50 with checkpoint forward
model = SCNet(50, out_indices=(0, 1, 2, 3), with_cp=True)
for m in model.modules():
if is_block(m):
assert m.with_cp
model.init_weights()
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([2, 256, 56, 56])
assert feat[1].shape == torch.Size([2, 512, 28, 28])
assert feat[2].shape == torch.Size([2, 1024, 14, 14])
assert feat[3].shape == torch.Size([2, 2048, 7, 7])
# Test SCNet zero initialization of residual
model = SCNet(50, out_indices=(0, 1, 2, 3), zero_init_residual=True)
model.init_weights()
for m in model.modules():
if isinstance(m, SCBottleneck):
assert all_zeros(m.norm3)
model.train()
imgs = torch.randn(2, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([2, 256, 56, 56])
assert feat[1].shape == torch.Size([2, 512, 28, 28])
assert feat[2].shape == torch.Size([2, 1024, 14, 14])
assert feat[3].shape == torch.Size([2, 2048, 7, 7])
@@ -0,0 +1,243 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules import AvgPool2d
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import SEResNet
from mmpose.models.backbones.resnet import ResLayer
from mmpose.models.backbones.seresnet import SEBottleneck, SELayer
def all_zeros(modules):
"""Check if the weight(and bias) is all zero."""
weight_zero = torch.equal(modules.weight.data,
torch.zeros_like(modules.weight.data))
if hasattr(modules, 'bias'):
bias_zero = torch.equal(modules.bias.data,
torch.zeros_like(modules.bias.data))
else:
bias_zero = True
return weight_zero and bias_zero
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_selayer():
# Test selayer forward
layer = SELayer(64)
x = torch.randn(1, 64, 56, 56)
x_out = layer(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
# Test selayer forward with different ratio
layer = SELayer(64, ratio=8)
x = torch.randn(1, 64, 56, 56)
x_out = layer(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_bottleneck():
with pytest.raises(AssertionError):
# Style must be in ['pytorch', 'caffe']
SEBottleneck(64, 64, style='tensorflow')
# Test SEBottleneck with checkpoint forward
block = SEBottleneck(64, 64, with_cp=True)
assert block.with_cp
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
# Test Bottleneck style
block = SEBottleneck(64, 256, stride=2, style='pytorch')
assert block.conv1.stride == (1, 1)
assert block.conv2.stride == (2, 2)
block = SEBottleneck(64, 256, stride=2, style='caffe')
assert block.conv1.stride == (2, 2)
assert block.conv2.stride == (1, 1)
# Test Bottleneck forward
block = SEBottleneck(64, 64)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_res_layer():
# Test ResLayer of 3 Bottleneck w\o downsample
layer = ResLayer(SEBottleneck, 3, 64, 64, se_ratio=16)
assert len(layer) == 3
assert layer[0].conv1.in_channels == 64
assert layer[0].conv1.out_channels == 16
for i in range(1, len(layer)):
assert layer[i].conv1.in_channels == 64
assert layer[i].conv1.out_channels == 16
for i in range(len(layer)):
assert layer[i].downsample is None
x = torch.randn(1, 64, 56, 56)
x_out = layer(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
# Test ResLayer of 3 SEBottleneck with downsample
layer = ResLayer(SEBottleneck, 3, 64, 256, se_ratio=16)
assert layer[0].downsample[0].out_channels == 256
for i in range(1, len(layer)):
assert layer[i].downsample is None
x = torch.randn(1, 64, 56, 56)
x_out = layer(x)
assert x_out.shape == torch.Size([1, 256, 56, 56])
# Test ResLayer of 3 SEBottleneck with stride=2
layer = ResLayer(SEBottleneck, 3, 64, 256, stride=2, se_ratio=8)
assert layer[0].downsample[0].out_channels == 256
assert layer[0].downsample[0].stride == (2, 2)
for i in range(1, len(layer)):
assert layer[i].downsample is None
x = torch.randn(1, 64, 56, 56)
x_out = layer(x)
assert x_out.shape == torch.Size([1, 256, 28, 28])
# Test ResLayer of 3 SEBottleneck with stride=2 and average downsample
layer = ResLayer(
SEBottleneck, 3, 64, 256, stride=2, avg_down=True, se_ratio=8)
assert isinstance(layer[0].downsample[0], AvgPool2d)
assert layer[0].downsample[1].out_channels == 256
assert layer[0].downsample[1].stride == (1, 1)
for i in range(1, len(layer)):
assert layer[i].downsample is None
x = torch.randn(1, 64, 56, 56)
x_out = layer(x)
assert x_out.shape == torch.Size([1, 256, 28, 28])
def test_seresnet():
"""Test resnet backbone."""
with pytest.raises(KeyError):
# SEResNet depth should be in [50, 101, 152]
SEResNet(20)
with pytest.raises(AssertionError):
# In SEResNet: 1 <= num_stages <= 4
SEResNet(50, num_stages=0)
with pytest.raises(AssertionError):
# In SEResNet: 1 <= num_stages <= 4
SEResNet(50, num_stages=5)
with pytest.raises(AssertionError):
# len(strides) == len(dilations) == num_stages
SEResNet(50, strides=(1, ), dilations=(1, 1), num_stages=3)
with pytest.raises(TypeError):
# pretrained must be a string path
model = SEResNet(50)
model.init_weights(pretrained=0)
with pytest.raises(AssertionError):
# Style must be in ['pytorch', 'caffe']
SEResNet(50, style='tensorflow')
# Test SEResNet50 norm_eval=True
model = SEResNet(50, norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test SEResNet50 with torchvision pretrained weight
model = SEResNet(depth=50, norm_eval=True)
model.init_weights('torchvision://resnet50')
model.train()
assert check_norm_state(model.modules(), False)
# Test SEResNet50 with first stage frozen
frozen_stages = 1
model = SEResNet(50, frozen_stages=frozen_stages)
model.init_weights()
model.train()
assert model.norm1.training is False
for layer in [model.conv1, model.norm1]:
for param in layer.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test SEResNet50 with BatchNorm forward
model = SEResNet(50, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([1, 256, 56, 56])
assert feat[1].shape == torch.Size([1, 512, 28, 28])
assert feat[2].shape == torch.Size([1, 1024, 14, 14])
assert feat[3].shape == torch.Size([1, 2048, 7, 7])
# Test SEResNet50 with layers 1, 2, 3 out forward
model = SEResNet(50, out_indices=(0, 1, 2))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size([1, 256, 56, 56])
assert feat[1].shape == torch.Size([1, 512, 28, 28])
assert feat[2].shape == torch.Size([1, 1024, 14, 14])
# Test SEResNet50 with layers 3 (top feature maps) out forward
model = SEResNet(50, out_indices=(3, ))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 2048, 7, 7])
# Test SEResNet50 with checkpoint forward
model = SEResNet(50, out_indices=(0, 1, 2, 3), with_cp=True)
for m in model.modules():
if isinstance(m, SEBottleneck):
assert m.with_cp
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([1, 256, 56, 56])
assert feat[1].shape == torch.Size([1, 512, 28, 28])
assert feat[2].shape == torch.Size([1, 1024, 14, 14])
assert feat[3].shape == torch.Size([1, 2048, 7, 7])
# Test SEResNet50 zero initialization of residual
model = SEResNet(50, out_indices=(0, 1, 2, 3), zero_init_residual=True)
model.init_weights()
for m in model.modules():
if isinstance(m, SEBottleneck):
assert all_zeros(m.norm3)
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([1, 256, 56, 56])
assert feat[1].shape == torch.Size([1, 512, 28, 28])
assert feat[2].shape == torch.Size([1, 1024, 14, 14])
assert feat[3].shape == torch.Size([1, 2048, 7, 7])
@@ -0,0 +1,73 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmpose.models.backbones import SEResNeXt
from mmpose.models.backbones.seresnext import SEBottleneck as SEBottleneckX
def test_bottleneck():
with pytest.raises(AssertionError):
# Style must be in ['pytorch', 'caffe']
SEBottleneckX(64, 64, groups=32, width_per_group=4, style='tensorflow')
# Test SEResNeXt Bottleneck structure
block = SEBottleneckX(
64, 256, groups=32, width_per_group=4, stride=2, style='pytorch')
assert block.width_per_group == 4
assert block.conv2.stride == (2, 2)
assert block.conv2.groups == 32
assert block.conv2.out_channels == 128
assert block.conv2.out_channels == block.mid_channels
# Test SEResNeXt Bottleneck structure (groups=1)
block = SEBottleneckX(
64, 256, groups=1, width_per_group=4, stride=2, style='pytorch')
assert block.conv2.stride == (2, 2)
assert block.conv2.groups == 1
assert block.conv2.out_channels == 64
assert block.mid_channels == 64
assert block.conv2.out_channels == block.mid_channels
# Test SEResNeXt Bottleneck forward
block = SEBottleneckX(
64, 64, base_channels=16, groups=32, width_per_group=4)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_seresnext():
with pytest.raises(KeyError):
# SEResNeXt depth should be in [50, 101, 152]
SEResNeXt(depth=18)
# Test SEResNeXt with group 32, width_per_group 4
model = SEResNeXt(
depth=50, groups=32, width_per_group=4, out_indices=(0, 1, 2, 3))
for m in model.modules():
if isinstance(m, SEBottleneckX):
assert m.conv2.groups == 32
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size([1, 256, 56, 56])
assert feat[1].shape == torch.Size([1, 512, 28, 28])
assert feat[2].shape == torch.Size([1, 1024, 14, 14])
assert feat[3].shape == torch.Size([1, 2048, 7, 7])
# Test SEResNeXt with group 32, width_per_group 4 and layers 3 out forward
model = SEResNeXt(
depth=50, groups=32, width_per_group=4, out_indices=(3, ))
for m in model.modules():
if isinstance(m, SEBottleneckX):
assert m.conv2.groups == 32
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 2048, 7, 7])
@@ -0,0 +1,245 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules import GroupNorm
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import ShuffleNetV1
from mmpose.models.backbones.shufflenet_v1 import ShuffleUnit
def is_block(modules):
"""Check if is ResNet building block."""
if isinstance(modules, (ShuffleUnit, )):
return True
return False
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (GroupNorm, _BatchNorm)):
return True
return False
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_shufflenetv1_shuffleuint():
with pytest.raises(ValueError):
# combine must be in ['add', 'concat']
ShuffleUnit(24, 16, groups=3, first_block=True, combine='test')
with pytest.raises(AssertionError):
# inplanes must be equal tp = outplanes when combine='add'
ShuffleUnit(64, 24, groups=4, first_block=True, combine='add')
# Test ShuffleUnit with combine='add'
block = ShuffleUnit(24, 24, groups=3, first_block=True, combine='add')
x = torch.randn(1, 24, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 24, 56, 56))
# Test ShuffleUnit with combine='concat'
block = ShuffleUnit(24, 240, groups=3, first_block=True, combine='concat')
x = torch.randn(1, 24, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 240, 28, 28))
# Test ShuffleUnit with checkpoint forward
block = ShuffleUnit(
24, 24, groups=3, first_block=True, combine='add', with_cp=True)
assert block.with_cp
x = torch.randn(1, 24, 56, 56)
x.requires_grad = True
x_out = block(x)
assert x_out.shape == torch.Size((1, 24, 56, 56))
def test_shufflenetv1_backbone():
with pytest.raises(ValueError):
# frozen_stages must be in range(-1, 4)
ShuffleNetV1(frozen_stages=10)
with pytest.raises(ValueError):
# the item in out_indices must be in range(0, 4)
ShuffleNetV1(out_indices=[5])
with pytest.raises(ValueError):
# groups must be in [1, 2, 3, 4, 8]
ShuffleNetV1(groups=10)
with pytest.raises(TypeError):
# pretrained must be str or None
model = ShuffleNetV1()
model.init_weights(pretrained=1)
# Test ShuffleNetV1 norm state
model = ShuffleNetV1()
model.init_weights()
model.train()
assert check_norm_state(model.modules(), True)
# Test ShuffleNetV1 with first stage frozen
frozen_stages = 1
model = ShuffleNetV1(frozen_stages=frozen_stages, out_indices=(0, 1, 2))
model.init_weights()
model.train()
for param in model.conv1.parameters():
assert param.requires_grad is False
for i in range(frozen_stages):
layer = model.layers[i]
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test ShuffleNetV1 forward with groups=1
model = ShuffleNetV1(groups=1, out_indices=(0, 1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size((1, 144, 28, 28))
assert feat[1].shape == torch.Size((1, 288, 14, 14))
assert feat[2].shape == torch.Size((1, 576, 7, 7))
# Test ShuffleNetV1 forward with groups=2
model = ShuffleNetV1(groups=2, out_indices=(0, 1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size((1, 200, 28, 28))
assert feat[1].shape == torch.Size((1, 400, 14, 14))
assert feat[2].shape == torch.Size((1, 800, 7, 7))
# Test ShuffleNetV1 forward with groups=3
model = ShuffleNetV1(groups=3, out_indices=(0, 1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size((1, 240, 28, 28))
assert feat[1].shape == torch.Size((1, 480, 14, 14))
assert feat[2].shape == torch.Size((1, 960, 7, 7))
# Test ShuffleNetV1 forward with groups=4
model = ShuffleNetV1(groups=4, out_indices=(0, 1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size((1, 272, 28, 28))
assert feat[1].shape == torch.Size((1, 544, 14, 14))
assert feat[2].shape == torch.Size((1, 1088, 7, 7))
# Test ShuffleNetV1 forward with groups=8
model = ShuffleNetV1(groups=8, out_indices=(0, 1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size((1, 384, 28, 28))
assert feat[1].shape == torch.Size((1, 768, 14, 14))
assert feat[2].shape == torch.Size((1, 1536, 7, 7))
# Test ShuffleNetV1 forward with GroupNorm forward
model = ShuffleNetV1(
groups=3,
norm_cfg=dict(type='GN', num_groups=2, requires_grad=True),
out_indices=(0, 1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, GroupNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == torch.Size((1, 240, 28, 28))
assert feat[1].shape == torch.Size((1, 480, 14, 14))
assert feat[2].shape == torch.Size((1, 960, 7, 7))
# Test ShuffleNetV1 forward with layers 1, 2 forward
model = ShuffleNetV1(groups=3, out_indices=(1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 2
assert feat[0].shape == torch.Size((1, 480, 14, 14))
assert feat[1].shape == torch.Size((1, 960, 7, 7))
# Test ShuffleNetV1 forward with layers 2 forward
model = ShuffleNetV1(groups=3, out_indices=(2, ))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert isinstance(feat, torch.Tensor)
assert feat.shape == torch.Size((1, 960, 7, 7))
# Test ShuffleNetV1 forward with checkpoint forward
model = ShuffleNetV1(groups=3, with_cp=True)
for m in model.modules():
if is_block(m):
assert m.with_cp
# Test ShuffleNetV1 with norm_eval
model = ShuffleNetV1(norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
@@ -0,0 +1,204 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules import GroupNorm
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import ShuffleNetV2
from mmpose.models.backbones.shufflenet_v2 import InvertedResidual
def is_block(modules):
"""Check if is ResNet building block."""
if isinstance(modules, (InvertedResidual, )):
return True
return False
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (GroupNorm, _BatchNorm)):
return True
return False
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_shufflenetv2_invertedresidual():
with pytest.raises(AssertionError):
# when stride==1, in_channels should be equal to out_channels // 2 * 2
InvertedResidual(24, 32, stride=1)
with pytest.raises(AssertionError):
# when in_channels != out_channels // 2 * 2, stride should not be
# equal to 1.
InvertedResidual(24, 32, stride=1)
# Test InvertedResidual forward
block = InvertedResidual(24, 48, stride=2)
x = torch.randn(1, 24, 56, 56)
x_out = block(x)
assert x_out.shape == torch.Size((1, 48, 28, 28))
# Test InvertedResidual with checkpoint forward
block = InvertedResidual(48, 48, stride=1, with_cp=True)
assert block.with_cp
x = torch.randn(1, 48, 56, 56)
x.requires_grad = True
x_out = block(x)
assert x_out.shape == torch.Size((1, 48, 56, 56))
def test_shufflenetv2_backbone():
with pytest.raises(ValueError):
# groups must be in 0.5, 1.0, 1.5, 2.0]
ShuffleNetV2(widen_factor=3.0)
with pytest.raises(ValueError):
# frozen_stages must be in [0, 1, 2, 3]
ShuffleNetV2(widen_factor=1.0, frozen_stages=4)
with pytest.raises(ValueError):
# out_indices must be in [0, 1, 2, 3]
ShuffleNetV2(widen_factor=1.0, out_indices=(4, ))
with pytest.raises(TypeError):
# pretrained must be str or None
model = ShuffleNetV2()
model.init_weights(pretrained=1)
# Test ShuffleNetV2 norm state
model = ShuffleNetV2()
model.init_weights()
model.train()
assert check_norm_state(model.modules(), True)
# Test ShuffleNetV2 with first stage frozen
frozen_stages = 1
model = ShuffleNetV2(frozen_stages=frozen_stages)
model.init_weights()
model.train()
for param in model.conv1.parameters():
assert param.requires_grad is False
for i in range(0, frozen_stages):
layer = model.layers[i]
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test ShuffleNetV2 with norm_eval
model = ShuffleNetV2(norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test ShuffleNetV2 forward with widen_factor=0.5
model = ShuffleNetV2(widen_factor=0.5, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size((1, 48, 28, 28))
assert feat[1].shape == torch.Size((1, 96, 14, 14))
assert feat[2].shape == torch.Size((1, 192, 7, 7))
# Test ShuffleNetV2 forward with widen_factor=1.0
model = ShuffleNetV2(widen_factor=1.0, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size((1, 116, 28, 28))
assert feat[1].shape == torch.Size((1, 232, 14, 14))
assert feat[2].shape == torch.Size((1, 464, 7, 7))
# Test ShuffleNetV2 forward with widen_factor=1.5
model = ShuffleNetV2(widen_factor=1.5, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size((1, 176, 28, 28))
assert feat[1].shape == torch.Size((1, 352, 14, 14))
assert feat[2].shape == torch.Size((1, 704, 7, 7))
# Test ShuffleNetV2 forward with widen_factor=2.0
model = ShuffleNetV2(widen_factor=2.0, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == torch.Size((1, 244, 28, 28))
assert feat[1].shape == torch.Size((1, 488, 14, 14))
assert feat[2].shape == torch.Size((1, 976, 7, 7))
# Test ShuffleNetV2 forward with layers 3 forward
model = ShuffleNetV2(widen_factor=1.0, out_indices=(2, ))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert isinstance(feat, torch.Tensor)
assert feat.shape == torch.Size((1, 464, 7, 7))
# Test ShuffleNetV2 forward with layers 1 2 forward
model = ShuffleNetV2(widen_factor=1.0, out_indices=(1, 2))
model.init_weights()
model.train()
for m in model.modules():
if is_norm(m):
assert isinstance(m, _BatchNorm)
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 2
assert feat[0].shape == torch.Size((1, 232, 14, 14))
assert feat[1].shape == torch.Size((1, 464, 7, 7))
# Test ShuffleNetV2 forward with checkpoint forward
model = ShuffleNetV2(widen_factor=1.0, with_cp=True)
for m in model.modules():
if is_block(m):
assert m.with_cp
@@ -0,0 +1,153 @@
# Copyright (c) OpenMMLab. All rights reserved.
import numpy as np
import pytest
import torch
import torch.nn as nn
from mmpose.models.backbones import TCN
from mmpose.models.backbones.tcn import BasicTemporalBlock
def test_basic_temporal_block():
with pytest.raises(AssertionError):
# padding( + shift) should not be larger than x.shape[2]
block = BasicTemporalBlock(1024, 1024, dilation=81)
x = torch.rand(2, 1024, 150)
x_out = block(x)
with pytest.raises(AssertionError):
# when use_stride_conv is True, shift + kernel_size // 2 should
# not be larger than x.shape[2]
block = BasicTemporalBlock(
1024, 1024, kernel_size=5, causal=True, use_stride_conv=True)
x = torch.rand(2, 1024, 3)
x_out = block(x)
# BasicTemporalBlock with causal == False
block = BasicTemporalBlock(1024, 1024)
x = torch.rand(2, 1024, 241)
x_out = block(x)
assert x_out.shape == torch.Size([2, 1024, 235])
# BasicTemporalBlock with causal == True
block = BasicTemporalBlock(1024, 1024, causal=True)
x = torch.rand(2, 1024, 241)
x_out = block(x)
assert x_out.shape == torch.Size([2, 1024, 235])
# BasicTemporalBlock with residual == False
block = BasicTemporalBlock(1024, 1024, residual=False)
x = torch.rand(2, 1024, 241)
x_out = block(x)
assert x_out.shape == torch.Size([2, 1024, 235])
# BasicTemporalBlock, use_stride_conv == True
block = BasicTemporalBlock(1024, 1024, use_stride_conv=True)
x = torch.rand(2, 1024, 81)
x_out = block(x)
assert x_out.shape == torch.Size([2, 1024, 27])
# BasicTemporalBlock with use_stride_conv == True and causal == True
block = BasicTemporalBlock(1024, 1024, use_stride_conv=True, causal=True)
x = torch.rand(2, 1024, 81)
x_out = block(x)
assert x_out.shape == torch.Size([2, 1024, 27])
def test_tcn_backbone():
with pytest.raises(AssertionError):
# num_blocks should equal len(kernel_sizes) - 1
TCN(in_channels=34, num_blocks=3, kernel_sizes=(3, 3, 3))
with pytest.raises(AssertionError):
# kernel size should be odd
TCN(in_channels=34, kernel_sizes=(3, 4, 3))
# Test TCN with 2 blocks (use_stride_conv == False)
model = TCN(in_channels=34, num_blocks=2, kernel_sizes=(3, 3, 3))
pose2d = torch.rand((2, 34, 243))
feat = model(pose2d)
assert len(feat) == 2
assert feat[0].shape == (2, 1024, 235)
assert feat[1].shape == (2, 1024, 217)
# Test TCN with 4 blocks and weight norm clip
max_norm = 0.1
model = TCN(
in_channels=34,
num_blocks=4,
kernel_sizes=(3, 3, 3, 3, 3),
max_norm=max_norm)
pose2d = torch.rand((2, 34, 243))
feat = model(pose2d)
assert len(feat) == 4
assert feat[0].shape == (2, 1024, 235)
assert feat[1].shape == (2, 1024, 217)
assert feat[2].shape == (2, 1024, 163)
assert feat[3].shape == (2, 1024, 1)
for module in model.modules():
if isinstance(module, torch.nn.modules.conv._ConvNd):
norm = module.weight.norm().item()
np.testing.assert_allclose(
np.maximum(norm, max_norm), max_norm, rtol=1e-4)
# Test TCN with 4 blocks (use_stride_conv == True)
model = TCN(
in_channels=34,
num_blocks=4,
kernel_sizes=(3, 3, 3, 3, 3),
use_stride_conv=True)
pose2d = torch.rand((2, 34, 243))
feat = model(pose2d)
assert len(feat) == 4
assert feat[0].shape == (2, 1024, 27)
assert feat[1].shape == (2, 1024, 9)
assert feat[2].shape == (2, 1024, 3)
assert feat[3].shape == (2, 1024, 1)
# Check that the model w. or w/o use_stride_conv will have the same
# output and gradient after a forward+backward pass
model1 = TCN(
in_channels=34,
stem_channels=4,
num_blocks=1,
kernel_sizes=(3, 3),
dropout=0,
residual=False,
norm_cfg=None)
model2 = TCN(
in_channels=34,
stem_channels=4,
num_blocks=1,
kernel_sizes=(3, 3),
dropout=0,
residual=False,
norm_cfg=None,
use_stride_conv=True)
for m in model1.modules():
if isinstance(m, nn.Conv1d):
nn.init.constant_(m.weight, 0.5)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
for m in model2.modules():
if isinstance(m, nn.Conv1d):
nn.init.constant_(m.weight, 0.5)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
input1 = torch.rand((1, 34, 9))
input2 = input1.clone()
outputs1 = model1(input1)
outputs2 = model2(input2)
for output1, output2 in zip(outputs1, outputs2):
assert torch.isclose(output1, output2).all()
criterion = nn.MSELoss()
target = torch.rand(output1.shape)
loss1 = criterion(output1, target)
loss2 = criterion(output2, target)
loss1.backward()
loss2.backward()
for m1, m2 in zip(model1.modules(), model2.modules()):
if isinstance(m1, nn.Conv1d):
assert torch.isclose(m1.weight.grad, m2.weight.grad).all()
@@ -0,0 +1,13 @@
# Copyright (c) OpenMMLab. All rights reserved.
import torch
from mmpose.models import builder
def test_v2v_net():
"""Test V2VNet."""
cfg = dict(type='V2VNet', input_channels=17, output_channels=15),
model = builder.build_backbone(*cfg)
input = torch.randn(2, 17, 32, 32, 32)
output = model(input)
assert output.shape == (2, 15, 32, 32, 32)
@@ -0,0 +1,137 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from mmcv.utils.parrots_wrapper import _BatchNorm
from mmpose.models.backbones import VGG
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_vgg():
"""Test VGG backbone."""
with pytest.raises(KeyError):
# VGG depth should be in [11, 13, 16, 19]
VGG(18)
with pytest.raises(AssertionError):
# In VGG: 1 <= num_stages <= 5
VGG(11, num_stages=0)
with pytest.raises(AssertionError):
# In VGG: 1 <= num_stages <= 5
VGG(11, num_stages=6)
with pytest.raises(AssertionError):
# len(dilations) == num_stages
VGG(11, dilations=(1, 1), num_stages=3)
with pytest.raises(TypeError):
# pretrained must be a string path
model = VGG(11)
model.init_weights(pretrained=0)
# Test VGG11 norm_eval=True
model = VGG(11, norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test VGG11 forward without classifiers
model = VGG(11, out_indices=(0, 1, 2, 3, 4))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 5
assert feat[0].shape == (1, 64, 112, 112)
assert feat[1].shape == (1, 128, 56, 56)
assert feat[2].shape == (1, 256, 28, 28)
assert feat[3].shape == (1, 512, 14, 14)
assert feat[4].shape == (1, 512, 7, 7)
# Test VGG11 forward with classifiers
model = VGG(11, num_classes=10, out_indices=(0, 1, 2, 3, 4, 5))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 6
assert feat[0].shape == (1, 64, 112, 112)
assert feat[1].shape == (1, 128, 56, 56)
assert feat[2].shape == (1, 256, 28, 28)
assert feat[3].shape == (1, 512, 14, 14)
assert feat[4].shape == (1, 512, 7, 7)
assert feat[5].shape == (1, 10)
# Test VGG11BN forward
model = VGG(11, norm_cfg=dict(type='BN'), out_indices=(0, 1, 2, 3, 4))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 5
assert feat[0].shape == (1, 64, 112, 112)
assert feat[1].shape == (1, 128, 56, 56)
assert feat[2].shape == (1, 256, 28, 28)
assert feat[3].shape == (1, 512, 14, 14)
assert feat[4].shape == (1, 512, 7, 7)
# Test VGG11BN forward with classifiers
model = VGG(
11,
num_classes=10,
norm_cfg=dict(type='BN'),
out_indices=(0, 1, 2, 3, 4, 5))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 6
assert feat[0].shape == (1, 64, 112, 112)
assert feat[1].shape == (1, 128, 56, 56)
assert feat[2].shape == (1, 256, 28, 28)
assert feat[3].shape == (1, 512, 14, 14)
assert feat[4].shape == (1, 512, 7, 7)
assert feat[5].shape == (1, 10)
# Test VGG13 with layers 1, 2, 3 out forward
model = VGG(13, out_indices=(0, 1, 2))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == (1, 64, 112, 112)
assert feat[1].shape == (1, 128, 56, 56)
assert feat[2].shape == (1, 256, 28, 28)
# Test VGG16 with top feature maps out forward
model = VGG(16)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == (1, 512, 7, 7)
# Test VGG19 with classification score out forward
model = VGG(19, num_classes=10)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == (1, 10)
@@ -0,0 +1,99 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
from torch.nn.modules import GroupNorm
from torch.nn.modules.batchnorm import _BatchNorm
from mmpose.models.backbones import ViPNAS_MobileNetV3
from mmpose.models.backbones.utils import InvertedResidual
def is_norm(modules):
"""Check if is one of the norms."""
if isinstance(modules, (GroupNorm, _BatchNorm)):
return True
return False
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_mobilenetv3_backbone():
with pytest.raises(TypeError):
# pretrained must be a string path
model = ViPNAS_MobileNetV3()
model.init_weights(pretrained=0)
with pytest.raises(AttributeError):
# frozen_stages must no more than 21
model = ViPNAS_MobileNetV3(frozen_stages=22)
model.train()
# Test MobileNetv3
model = ViPNAS_MobileNetV3()
model.init_weights()
model.train()
# Test MobileNetv3 with first stage frozen
frozen_stages = 1
model = ViPNAS_MobileNetV3(frozen_stages=frozen_stages)
model.init_weights()
model.train()
for param in model.conv1.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test MobileNetv3 with norm eval
model = ViPNAS_MobileNetV3(norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test MobileNetv3 forward
model = ViPNAS_MobileNetV3()
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 160, 7, 7])
# Test MobileNetv3 forward with GroupNorm
model = ViPNAS_MobileNetV3(
norm_cfg=dict(type='GN', num_groups=2, requires_grad=True))
for m in model.modules():
if is_norm(m):
assert isinstance(m, GroupNorm)
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 160, 7, 7])
# Test MobileNetv3 with checkpoint forward
model = ViPNAS_MobileNetV3(with_cp=True)
for m in model.modules():
if isinstance(m, InvertedResidual):
assert m.with_cp
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == torch.Size([1, 160, 7, 7])
test_mobilenetv3_backbone()
@@ -0,0 +1,341 @@
# Copyright (c) OpenMMLab. All rights reserved.
import pytest
import torch
import torch.nn as nn
from mmcv.utils.parrots_wrapper import _BatchNorm
from mmpose.models.backbones import ViPNAS_ResNet
from mmpose.models.backbones.vipnas_resnet import (ViPNAS_Bottleneck,
ViPNAS_ResLayer,
get_expansion)
def is_block(modules):
"""Check if is ViPNAS_ResNet building block."""
if isinstance(modules, (ViPNAS_Bottleneck)):
return True
return False
def all_zeros(modules):
"""Check if the weight(and bias) is all zero."""
weight_zero = torch.equal(modules.weight.data,
torch.zeros_like(modules.weight.data))
if hasattr(modules, 'bias'):
bias_zero = torch.equal(modules.bias.data,
torch.zeros_like(modules.bias.data))
else:
bias_zero = True
return weight_zero and bias_zero
def check_norm_state(modules, train_state):
"""Check if norm layer is in correct train state."""
for mod in modules:
if isinstance(mod, _BatchNorm):
if mod.training != train_state:
return False
return True
def test_get_expansion():
assert get_expansion(ViPNAS_Bottleneck, 2) == 2
assert get_expansion(ViPNAS_Bottleneck) == 1
class MyResBlock(nn.Module):
expansion = 8
assert get_expansion(MyResBlock) == 8
# expansion must be an integer or None
with pytest.raises(TypeError):
get_expansion(ViPNAS_Bottleneck, '0')
# expansion is not specified and cannot be inferred
with pytest.raises(TypeError):
class SomeModule(nn.Module):
pass
get_expansion(SomeModule)
def test_vipnas_bottleneck():
# style must be in ['pytorch', 'caffe']
with pytest.raises(AssertionError):
ViPNAS_Bottleneck(64, 64, style='tensorflow')
# expansion must be divisible by out_channels
with pytest.raises(AssertionError):
ViPNAS_Bottleneck(64, 64, expansion=3)
# Test ViPNAS_Bottleneck style
block = ViPNAS_Bottleneck(64, 64, stride=2, style='pytorch')
assert block.conv1.stride == (1, 1)
assert block.conv2.stride == (2, 2)
block = ViPNAS_Bottleneck(64, 64, stride=2, style='caffe')
assert block.conv1.stride == (2, 2)
assert block.conv2.stride == (1, 1)
# ViPNAS_Bottleneck with stride 1
block = ViPNAS_Bottleneck(64, 64, style='pytorch')
assert block.in_channels == 64
assert block.mid_channels == 16
assert block.out_channels == 64
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 16
assert block.conv1.kernel_size == (1, 1)
assert block.conv2.in_channels == 16
assert block.conv2.out_channels == 16
assert block.conv2.kernel_size == (3, 3)
assert block.conv3.in_channels == 16
assert block.conv3.out_channels == 64
assert block.conv3.kernel_size == (1, 1)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 64, 56, 56)
# ViPNAS_Bottleneck with stride 1 and downsample
downsample = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=1), nn.BatchNorm2d(128))
block = ViPNAS_Bottleneck(64, 128, style='pytorch', downsample=downsample)
assert block.in_channels == 64
assert block.mid_channels == 32
assert block.out_channels == 128
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 32
assert block.conv1.kernel_size == (1, 1)
assert block.conv2.in_channels == 32
assert block.conv2.out_channels == 32
assert block.conv2.kernel_size == (3, 3)
assert block.conv3.in_channels == 32
assert block.conv3.out_channels == 128
assert block.conv3.kernel_size == (1, 1)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 128, 56, 56)
# ViPNAS_Bottleneck with stride 2 and downsample
downsample = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=1, stride=2), nn.BatchNorm2d(128))
block = ViPNAS_Bottleneck(
64, 128, stride=2, style='pytorch', downsample=downsample)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 128, 28, 28)
# ViPNAS_Bottleneck with expansion 2
block = ViPNAS_Bottleneck(64, 64, style='pytorch', expansion=2)
assert block.in_channels == 64
assert block.mid_channels == 32
assert block.out_channels == 64
assert block.conv1.in_channels == 64
assert block.conv1.out_channels == 32
assert block.conv1.kernel_size == (1, 1)
assert block.conv2.in_channels == 32
assert block.conv2.out_channels == 32
assert block.conv2.kernel_size == (3, 3)
assert block.conv3.in_channels == 32
assert block.conv3.out_channels == 64
assert block.conv3.kernel_size == (1, 1)
x = torch.randn(1, 64, 56, 56)
x_out = block(x)
assert x_out.shape == (1, 64, 56, 56)
# Test ViPNAS_Bottleneck with checkpointing
block = ViPNAS_Bottleneck(64, 64, with_cp=True)
block.train()
assert block.with_cp
x = torch.randn(1, 64, 56, 56, requires_grad=True)
x_out = block(x)
assert x_out.shape == torch.Size([1, 64, 56, 56])
def test_vipnas_bottleneck_reslayer():
# 3 Bottleneck w/o downsample
layer = ViPNAS_ResLayer(ViPNAS_Bottleneck, 3, 32, 32)
assert len(layer) == 3
for i in range(3):
assert layer[i].in_channels == 32
assert layer[i].out_channels == 32
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 32, 56, 56)
# 3 ViPNAS_Bottleneck w/ stride 1 and downsample
layer = ViPNAS_ResLayer(ViPNAS_Bottleneck, 3, 32, 64)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 1
assert layer[0].conv1.out_channels == 64
assert layer[0].downsample is not None and len(layer[0].downsample) == 2
assert isinstance(layer[0].downsample[0], nn.Conv2d)
assert layer[0].downsample[0].stride == (1, 1)
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].conv1.out_channels == 64
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 56, 56)
# 3 ViPNAS_Bottleneck w/ stride 2 and downsample
layer = ViPNAS_ResLayer(ViPNAS_Bottleneck, 3, 32, 64, stride=2)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 2
assert layer[0].conv1.out_channels == 64
assert layer[0].downsample is not None and len(layer[0].downsample) == 2
assert isinstance(layer[0].downsample[0], nn.Conv2d)
assert layer[0].downsample[0].stride == (2, 2)
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].conv1.out_channels == 64
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 28, 28)
# 3 ViPNAS_Bottleneck w/ stride 2 and downsample with avg pool
layer = ViPNAS_ResLayer(
ViPNAS_Bottleneck, 3, 32, 64, stride=2, avg_down=True)
assert len(layer) == 3
assert layer[0].in_channels == 32
assert layer[0].out_channels == 64
assert layer[0].stride == 2
assert layer[0].conv1.out_channels == 64
assert layer[0].downsample is not None and len(layer[0].downsample) == 3
assert isinstance(layer[0].downsample[0], nn.AvgPool2d)
assert layer[0].downsample[0].stride == 2
for i in range(1, 3):
assert layer[i].in_channels == 64
assert layer[i].out_channels == 64
assert layer[i].conv1.out_channels == 64
assert layer[i].stride == 1
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 64, 28, 28)
# 3 ViPNAS_Bottleneck with custom expansion
layer = ViPNAS_ResLayer(ViPNAS_Bottleneck, 3, 32, 32, expansion=2)
assert len(layer) == 3
for i in range(3):
assert layer[i].in_channels == 32
assert layer[i].out_channels == 32
assert layer[i].stride == 1
assert layer[i].conv1.out_channels == 16
assert layer[i].downsample is None
x = torch.randn(1, 32, 56, 56)
x_out = layer(x)
assert x_out.shape == (1, 32, 56, 56)
def test_resnet():
"""Test ViPNAS_ResNet backbone."""
with pytest.raises(KeyError):
# ViPNAS_ResNet depth should be in [50]
ViPNAS_ResNet(20)
with pytest.raises(AssertionError):
# In ViPNAS_ResNet: 1 <= num_stages <= 4
ViPNAS_ResNet(50, num_stages=0)
with pytest.raises(AssertionError):
# In ViPNAS_ResNet: 1 <= num_stages <= 4
ViPNAS_ResNet(50, num_stages=5)
with pytest.raises(AssertionError):
# len(strides) == len(dilations) == num_stages
ViPNAS_ResNet(50, strides=(1, ), dilations=(1, 1), num_stages=3)
with pytest.raises(TypeError):
# pretrained must be a string path
model = ViPNAS_ResNet(50)
model.init_weights(pretrained=0)
with pytest.raises(AssertionError):
# Style must be in ['pytorch', 'caffe']
ViPNAS_ResNet(50, style='tensorflow')
# Test ViPNAS_ResNet50 norm_eval=True
model = ViPNAS_ResNet(50, norm_eval=True)
model.init_weights()
model.train()
assert check_norm_state(model.modules(), False)
# Test ViPNAS_ResNet50 with first stage frozen
frozen_stages = 1
model = ViPNAS_ResNet(50, frozen_stages=frozen_stages)
model.init_weights()
model.train()
assert model.norm1.training is False
for layer in [model.conv1, model.norm1]:
for param in layer.parameters():
assert param.requires_grad is False
for i in range(1, frozen_stages + 1):
layer = getattr(model, f'layer{i}')
for mod in layer.modules():
if isinstance(mod, _BatchNorm):
assert mod.training is False
for param in layer.parameters():
assert param.requires_grad is False
# Test ViPNAS_ResNet50 with BatchNorm forward
model = ViPNAS_ResNet(50, out_indices=(0, 1, 2, 3))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, 80, 56, 56)
assert feat[1].shape == (1, 160, 28, 28)
assert feat[2].shape == (1, 304, 14, 14)
assert feat[3].shape == (1, 608, 7, 7)
# Test ViPNAS_ResNet50 with layers 1, 2, 3 out forward
model = ViPNAS_ResNet(50, out_indices=(0, 1, 2))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 3
assert feat[0].shape == (1, 80, 56, 56)
assert feat[1].shape == (1, 160, 28, 28)
assert feat[2].shape == (1, 304, 14, 14)
# Test ViPNAS_ResNet50 with layers 3 (top feature maps) out forward
model = ViPNAS_ResNet(50, out_indices=(3, ))
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert feat.shape == (1, 608, 7, 7)
# Test ViPNAS_ResNet50 with checkpoint forward
model = ViPNAS_ResNet(50, out_indices=(0, 1, 2, 3), with_cp=True)
for m in model.modules():
if is_block(m):
assert m.with_cp
model.init_weights()
model.train()
imgs = torch.randn(1, 3, 224, 224)
feat = model(imgs)
assert len(feat) == 4
assert feat[0].shape == (1, 80, 56, 56)
assert feat[1].shape == (1, 160, 28, 28)
assert feat[2].shape == (1, 304, 14, 14)
assert feat[3].shape == (1, 608, 7, 7)