Initial snapshot: hand motion pipeline (Dyn-HaMR + dex-retargeting + SPIDER)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
+117
@@ -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
|
||||
+143
@@ -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])
|
||||
+257
@@ -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))
|
||||
+169
@@ -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])
|
||||
+245
@@ -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)
|
||||
+204
@@ -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)
|
||||
+99
@@ -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()
|
||||
+341
@@ -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)
|
||||
Reference in New Issue
Block a user