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,36 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from .alexnet import AlexNet
|
||||
from .cpm import CPM
|
||||
from .hourglass import HourglassNet
|
||||
from .hourglass_ae import HourglassAENet
|
||||
from .hrformer import HRFormer
|
||||
from .hrnet import HRNet
|
||||
from .litehrnet import LiteHRNet
|
||||
from .mobilenet_v2 import MobileNetV2
|
||||
from .mobilenet_v3 import MobileNetV3
|
||||
from .mspn import MSPN
|
||||
from .regnet import RegNet
|
||||
from .resnest import ResNeSt
|
||||
from .resnet import ResNet, ResNetV1d
|
||||
from .resnext import ResNeXt
|
||||
from .rsn import RSN
|
||||
from .scnet import SCNet
|
||||
from .seresnet import SEResNet
|
||||
from .seresnext import SEResNeXt
|
||||
from .shufflenet_v1 import ShuffleNetV1
|
||||
from .shufflenet_v2 import ShuffleNetV2
|
||||
from .tcn import TCN
|
||||
from .v2v_net import V2VNet
|
||||
from .vgg import VGG
|
||||
from .vipnas_mbv3 import ViPNAS_MobileNetV3
|
||||
from .vipnas_resnet import ViPNAS_ResNet
|
||||
from .vit import ViT
|
||||
from .vit_moe import ViTMoE
|
||||
|
||||
__all__ = [
|
||||
'AlexNet', 'HourglassNet', 'HourglassAENet', 'HRNet', 'MobileNetV2',
|
||||
'MobileNetV3', 'RegNet', 'ResNet', 'ResNetV1d', 'ResNeXt', 'SCNet',
|
||||
'SEResNet', 'SEResNeXt', 'ShuffleNetV1', 'ShuffleNetV2', 'CPM', 'RSN',
|
||||
'MSPN', 'ResNeSt', 'VGG', 'TCN', 'ViPNAS_ResNet', 'ViPNAS_MobileNetV3',
|
||||
'LiteHRNet', 'V2VNet', 'HRFormer', 'ViT', 'ViTMoE'
|
||||
]
|
||||
@@ -0,0 +1,56 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch.nn as nn
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class AlexNet(BaseBackbone):
|
||||
"""`AlexNet <https://en.wikipedia.org/wiki/AlexNet>`__ backbone.
|
||||
|
||||
The input for AlexNet is a 224x224 RGB image.
|
||||
|
||||
Args:
|
||||
num_classes (int): number of classes for classification.
|
||||
The default value is -1, which uses the backbone as
|
||||
a feature extractor without the top classifier.
|
||||
"""
|
||||
|
||||
def __init__(self, num_classes=-1):
|
||||
super().__init__()
|
||||
self.num_classes = num_classes
|
||||
self.features = nn.Sequential(
|
||||
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2),
|
||||
nn.Conv2d(64, 192, kernel_size=5, padding=2),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2),
|
||||
nn.Conv2d(192, 384, kernel_size=3, padding=1),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Conv2d(384, 256, kernel_size=3, padding=1),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Conv2d(256, 256, kernel_size=3, padding=1),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2),
|
||||
)
|
||||
if self.num_classes > 0:
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Dropout(),
|
||||
nn.Linear(256 * 6 * 6, 4096),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Dropout(),
|
||||
nn.Linear(4096, 4096),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Linear(4096, num_classes),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
x = self.features(x)
|
||||
if self.num_classes > 0:
|
||||
x = x.view(x.size(0), 256 * 6 * 6)
|
||||
x = self.classifier(x)
|
||||
|
||||
return x
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import logging
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import torch.nn as nn
|
||||
|
||||
# from .utils import load_checkpoint
|
||||
from mmcv_custom.checkpoint import load_checkpoint
|
||||
|
||||
class BaseBackbone(nn.Module, metaclass=ABCMeta):
|
||||
"""Base backbone.
|
||||
|
||||
This class defines the basic functions of a backbone. Any backbone that
|
||||
inherits this class should at least define its own `forward` function.
|
||||
"""
|
||||
|
||||
def init_weights(self, pretrained=None, patch_padding='pad', part_features=None):
|
||||
"""Init backbone weights.
|
||||
|
||||
Args:
|
||||
pretrained (str | None): If pretrained is a string, then it
|
||||
initializes backbone weights by loading the pretrained
|
||||
checkpoint. If pretrained is None, then it follows default
|
||||
initializer or customized initializer in subclasses.
|
||||
"""
|
||||
if isinstance(pretrained, str):
|
||||
logger = logging.getLogger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger, patch_padding=patch_padding, part_features=part_features)
|
||||
elif pretrained is None:
|
||||
# use default initializer or customized initializer in subclasses
|
||||
pass
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None.'
|
||||
f' But received {type(pretrained)}.')
|
||||
|
||||
@abstractmethod
|
||||
def forward(self, x):
|
||||
"""Forward function.
|
||||
|
||||
Args:
|
||||
x (Tensor | tuple[Tensor]): x could be a torch.Tensor or a tuple of
|
||||
torch.Tensor, containing input data for forward computation.
|
||||
"""
|
||||
@@ -0,0 +1,186 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule, constant_init, normal_init
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from mmpose.utils import get_root_logger
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .utils import load_checkpoint
|
||||
|
||||
|
||||
class CpmBlock(nn.Module):
|
||||
"""CpmBlock for Convolutional Pose Machine.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
channels (list): Output channels of each conv module.
|
||||
kernels (list): Kernel sizes of each conv module.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
channels=(128, 128, 128),
|
||||
kernels=(11, 11, 11),
|
||||
norm_cfg=None):
|
||||
super().__init__()
|
||||
|
||||
assert len(channels) == len(kernels)
|
||||
layers = []
|
||||
for i in range(len(channels)):
|
||||
if i == 0:
|
||||
input_channels = in_channels
|
||||
else:
|
||||
input_channels = channels[i - 1]
|
||||
layers.append(
|
||||
ConvModule(
|
||||
input_channels,
|
||||
channels[i],
|
||||
kernels[i],
|
||||
padding=(kernels[i] - 1) // 2,
|
||||
norm_cfg=norm_cfg))
|
||||
self.model = nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
out = self.model(x)
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class CPM(BaseBackbone):
|
||||
"""CPM backbone.
|
||||
|
||||
Convolutional Pose Machines.
|
||||
More details can be found in the `paper
|
||||
<https://arxiv.org/abs/1602.00134>`__ .
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the CPM.
|
||||
out_channels (int): The output channels of the CPM.
|
||||
feat_channels (int): Feature channel of each CPM stage.
|
||||
middle_channels (int): Feature channel of conv after the middle stage.
|
||||
num_stages (int): Number of stages.
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import CPM
|
||||
>>> import torch
|
||||
>>> self = CPM(3, 17)
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 368, 368)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_output in level_outputs:
|
||||
... print(tuple(level_output.shape))
|
||||
(1, 17, 46, 46)
|
||||
(1, 17, 46, 46)
|
||||
(1, 17, 46, 46)
|
||||
(1, 17, 46, 46)
|
||||
(1, 17, 46, 46)
|
||||
(1, 17, 46, 46)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
feat_channels=128,
|
||||
middle_channels=32,
|
||||
num_stages=6,
|
||||
norm_cfg=dict(type='BN', requires_grad=True)):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
|
||||
assert in_channels == 3
|
||||
|
||||
self.num_stages = num_stages
|
||||
assert self.num_stages >= 1
|
||||
|
||||
self.stem = nn.Sequential(
|
||||
ConvModule(in_channels, 128, 9, padding=4, norm_cfg=norm_cfg),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
|
||||
ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
|
||||
ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
|
||||
ConvModule(128, 32, 5, padding=2, norm_cfg=norm_cfg),
|
||||
ConvModule(32, 512, 9, padding=4, norm_cfg=norm_cfg),
|
||||
ConvModule(512, 512, 1, padding=0, norm_cfg=norm_cfg),
|
||||
ConvModule(512, out_channels, 1, padding=0, act_cfg=None))
|
||||
|
||||
self.middle = nn.Sequential(
|
||||
ConvModule(in_channels, 128, 9, padding=4, norm_cfg=norm_cfg),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
|
||||
ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2, padding=1),
|
||||
ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
|
||||
|
||||
self.cpm_stages = nn.ModuleList([
|
||||
CpmBlock(
|
||||
middle_channels + out_channels,
|
||||
channels=[feat_channels, feat_channels, feat_channels],
|
||||
kernels=[11, 11, 11],
|
||||
norm_cfg=norm_cfg) for _ in range(num_stages - 1)
|
||||
])
|
||||
|
||||
self.middle_conv = nn.ModuleList([
|
||||
nn.Sequential(
|
||||
ConvModule(
|
||||
128, middle_channels, 5, padding=2, norm_cfg=norm_cfg))
|
||||
for _ in range(num_stages - 1)
|
||||
])
|
||||
|
||||
self.out_convs = nn.ModuleList([
|
||||
nn.Sequential(
|
||||
ConvModule(
|
||||
feat_channels,
|
||||
feat_channels,
|
||||
1,
|
||||
padding=0,
|
||||
norm_cfg=norm_cfg),
|
||||
ConvModule(feat_channels, out_channels, 1, act_cfg=None))
|
||||
for _ in range(num_stages - 1)
|
||||
])
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
if isinstance(pretrained, str):
|
||||
logger = get_root_logger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
normal_init(m, std=0.001)
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, 1)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
stage1_out = self.stem(x)
|
||||
middle_out = self.middle(x)
|
||||
out_feats = []
|
||||
|
||||
out_feats.append(stage1_out)
|
||||
|
||||
for ind in range(self.num_stages - 1):
|
||||
single_stage = self.cpm_stages[ind]
|
||||
out_conv = self.out_convs[ind]
|
||||
|
||||
inp_feat = torch.cat(
|
||||
[out_feats[-1], self.middle_conv[ind](middle_out)], 1)
|
||||
cpm_feat = single_stage(inp_feat)
|
||||
out_feat = out_conv(cpm_feat)
|
||||
out_feats.append(out_feat)
|
||||
|
||||
return out_feats
|
||||
@@ -0,0 +1,212 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule, constant_init, normal_init
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from mmpose.utils import get_root_logger
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .resnet import BasicBlock, ResLayer
|
||||
from .utils import load_checkpoint
|
||||
|
||||
|
||||
class HourglassModule(nn.Module):
|
||||
"""Hourglass Module for HourglassNet backbone.
|
||||
|
||||
Generate module recursively and use BasicBlock as the base unit.
|
||||
|
||||
Args:
|
||||
depth (int): Depth of current HourglassModule.
|
||||
stage_channels (list[int]): Feature channels of sub-modules in current
|
||||
and follow-up HourglassModule.
|
||||
stage_blocks (list[int]): Number of sub-modules stacked in current and
|
||||
follow-up HourglassModule.
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
depth,
|
||||
stage_channels,
|
||||
stage_blocks,
|
||||
norm_cfg=dict(type='BN', requires_grad=True)):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
|
||||
self.depth = depth
|
||||
|
||||
cur_block = stage_blocks[0]
|
||||
next_block = stage_blocks[1]
|
||||
|
||||
cur_channel = stage_channels[0]
|
||||
next_channel = stage_channels[1]
|
||||
|
||||
self.up1 = ResLayer(
|
||||
BasicBlock, cur_block, cur_channel, cur_channel, norm_cfg=norm_cfg)
|
||||
|
||||
self.low1 = ResLayer(
|
||||
BasicBlock,
|
||||
cur_block,
|
||||
cur_channel,
|
||||
next_channel,
|
||||
stride=2,
|
||||
norm_cfg=norm_cfg)
|
||||
|
||||
if self.depth > 1:
|
||||
self.low2 = HourglassModule(depth - 1, stage_channels[1:],
|
||||
stage_blocks[1:])
|
||||
else:
|
||||
self.low2 = ResLayer(
|
||||
BasicBlock,
|
||||
next_block,
|
||||
next_channel,
|
||||
next_channel,
|
||||
norm_cfg=norm_cfg)
|
||||
|
||||
self.low3 = ResLayer(
|
||||
BasicBlock,
|
||||
cur_block,
|
||||
next_channel,
|
||||
cur_channel,
|
||||
norm_cfg=norm_cfg,
|
||||
downsample_first=False)
|
||||
|
||||
self.up2 = nn.Upsample(scale_factor=2)
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
up1 = self.up1(x)
|
||||
low1 = self.low1(x)
|
||||
low2 = self.low2(low1)
|
||||
low3 = self.low3(low2)
|
||||
up2 = self.up2(low3)
|
||||
return up1 + up2
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class HourglassNet(BaseBackbone):
|
||||
"""HourglassNet backbone.
|
||||
|
||||
Stacked Hourglass Networks for Human Pose Estimation.
|
||||
More details can be found in the `paper
|
||||
<https://arxiv.org/abs/1603.06937>`__ .
|
||||
|
||||
Args:
|
||||
downsample_times (int): Downsample times in a HourglassModule.
|
||||
num_stacks (int): Number of HourglassModule modules stacked,
|
||||
1 for Hourglass-52, 2 for Hourglass-104.
|
||||
stage_channels (list[int]): Feature channel of each sub-module in a
|
||||
HourglassModule.
|
||||
stage_blocks (list[int]): Number of sub-modules stacked in a
|
||||
HourglassModule.
|
||||
feat_channel (int): Feature channel of conv after a HourglassModule.
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import HourglassNet
|
||||
>>> import torch
|
||||
>>> self = HourglassNet()
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 511, 511)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_output in level_outputs:
|
||||
... print(tuple(level_output.shape))
|
||||
(1, 256, 128, 128)
|
||||
(1, 256, 128, 128)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
downsample_times=5,
|
||||
num_stacks=2,
|
||||
stage_channels=(256, 256, 384, 384, 384, 512),
|
||||
stage_blocks=(2, 2, 2, 2, 2, 4),
|
||||
feat_channel=256,
|
||||
norm_cfg=dict(type='BN', requires_grad=True)):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
|
||||
self.num_stacks = num_stacks
|
||||
assert self.num_stacks >= 1
|
||||
assert len(stage_channels) == len(stage_blocks)
|
||||
assert len(stage_channels) > downsample_times
|
||||
|
||||
cur_channel = stage_channels[0]
|
||||
|
||||
self.stem = nn.Sequential(
|
||||
ConvModule(3, 128, 7, padding=3, stride=2, norm_cfg=norm_cfg),
|
||||
ResLayer(BasicBlock, 1, 128, 256, stride=2, norm_cfg=norm_cfg))
|
||||
|
||||
self.hourglass_modules = nn.ModuleList([
|
||||
HourglassModule(downsample_times, stage_channels, stage_blocks)
|
||||
for _ in range(num_stacks)
|
||||
])
|
||||
|
||||
self.inters = ResLayer(
|
||||
BasicBlock,
|
||||
num_stacks - 1,
|
||||
cur_channel,
|
||||
cur_channel,
|
||||
norm_cfg=norm_cfg)
|
||||
|
||||
self.conv1x1s = nn.ModuleList([
|
||||
ConvModule(
|
||||
cur_channel, cur_channel, 1, norm_cfg=norm_cfg, act_cfg=None)
|
||||
for _ in range(num_stacks - 1)
|
||||
])
|
||||
|
||||
self.out_convs = nn.ModuleList([
|
||||
ConvModule(
|
||||
cur_channel, feat_channel, 3, padding=1, norm_cfg=norm_cfg)
|
||||
for _ in range(num_stacks)
|
||||
])
|
||||
|
||||
self.remap_convs = nn.ModuleList([
|
||||
ConvModule(
|
||||
feat_channel, cur_channel, 1, norm_cfg=norm_cfg, act_cfg=None)
|
||||
for _ in range(num_stacks - 1)
|
||||
])
|
||||
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
if isinstance(pretrained, str):
|
||||
logger = get_root_logger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
normal_init(m, std=0.001)
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, 1)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
inter_feat = self.stem(x)
|
||||
out_feats = []
|
||||
|
||||
for ind in range(self.num_stacks):
|
||||
single_hourglass = self.hourglass_modules[ind]
|
||||
out_conv = self.out_convs[ind]
|
||||
|
||||
hourglass_feat = single_hourglass(inter_feat)
|
||||
out_feat = out_conv(hourglass_feat)
|
||||
out_feats.append(out_feat)
|
||||
|
||||
if ind < self.num_stacks - 1:
|
||||
inter_feat = self.conv1x1s[ind](
|
||||
inter_feat) + self.remap_convs[ind](
|
||||
out_feat)
|
||||
inter_feat = self.inters[ind](self.relu(inter_feat))
|
||||
|
||||
return out_feats
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule, MaxPool2d, constant_init, normal_init
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from mmpose.utils import get_root_logger
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .utils import load_checkpoint
|
||||
|
||||
|
||||
class HourglassAEModule(nn.Module):
|
||||
"""Modified Hourglass Module for HourglassNet_AE backbone.
|
||||
|
||||
Generate module recursively and use BasicBlock as the base unit.
|
||||
|
||||
Args:
|
||||
depth (int): Depth of current HourglassModule.
|
||||
stage_channels (list[int]): Feature channels of sub-modules in current
|
||||
and follow-up HourglassModule.
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
depth,
|
||||
stage_channels,
|
||||
norm_cfg=dict(type='BN', requires_grad=True)):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
|
||||
self.depth = depth
|
||||
|
||||
cur_channel = stage_channels[0]
|
||||
next_channel = stage_channels[1]
|
||||
|
||||
self.up1 = ConvModule(
|
||||
cur_channel, cur_channel, 3, padding=1, norm_cfg=norm_cfg)
|
||||
|
||||
self.pool1 = MaxPool2d(2, 2)
|
||||
|
||||
self.low1 = ConvModule(
|
||||
cur_channel, next_channel, 3, padding=1, norm_cfg=norm_cfg)
|
||||
|
||||
if self.depth > 1:
|
||||
self.low2 = HourglassAEModule(depth - 1, stage_channels[1:])
|
||||
else:
|
||||
self.low2 = ConvModule(
|
||||
next_channel, next_channel, 3, padding=1, norm_cfg=norm_cfg)
|
||||
|
||||
self.low3 = ConvModule(
|
||||
next_channel, cur_channel, 3, padding=1, norm_cfg=norm_cfg)
|
||||
|
||||
self.up2 = nn.UpsamplingNearest2d(scale_factor=2)
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
up1 = self.up1(x)
|
||||
pool1 = self.pool1(x)
|
||||
low1 = self.low1(pool1)
|
||||
low2 = self.low2(low1)
|
||||
low3 = self.low3(low2)
|
||||
up2 = self.up2(low3)
|
||||
return up1 + up2
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class HourglassAENet(BaseBackbone):
|
||||
"""Hourglass-AE Network proposed by Newell et al.
|
||||
|
||||
Associative Embedding: End-to-End Learning for Joint
|
||||
Detection and Grouping.
|
||||
|
||||
More details can be found in the `paper
|
||||
<https://arxiv.org/abs/1611.05424>`__ .
|
||||
|
||||
Args:
|
||||
downsample_times (int): Downsample times in a HourglassModule.
|
||||
num_stacks (int): Number of HourglassModule modules stacked,
|
||||
1 for Hourglass-52, 2 for Hourglass-104.
|
||||
stage_channels (list[int]): Feature channel of each sub-module in a
|
||||
HourglassModule.
|
||||
stage_blocks (list[int]): Number of sub-modules stacked in a
|
||||
HourglassModule.
|
||||
feat_channels (int): Feature channel of conv after a HourglassModule.
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import HourglassAENet
|
||||
>>> import torch
|
||||
>>> self = HourglassAENet()
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 512, 512)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_output in level_outputs:
|
||||
... print(tuple(level_output.shape))
|
||||
(1, 34, 128, 128)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
downsample_times=4,
|
||||
num_stacks=1,
|
||||
out_channels=34,
|
||||
stage_channels=(256, 384, 512, 640, 768),
|
||||
feat_channels=256,
|
||||
norm_cfg=dict(type='BN', requires_grad=True)):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
|
||||
self.num_stacks = num_stacks
|
||||
assert self.num_stacks >= 1
|
||||
assert len(stage_channels) > downsample_times
|
||||
|
||||
cur_channels = stage_channels[0]
|
||||
|
||||
self.stem = nn.Sequential(
|
||||
ConvModule(3, 64, 7, padding=3, stride=2, norm_cfg=norm_cfg),
|
||||
ConvModule(64, 128, 3, padding=1, norm_cfg=norm_cfg),
|
||||
MaxPool2d(2, 2),
|
||||
ConvModule(128, 128, 3, padding=1, norm_cfg=norm_cfg),
|
||||
ConvModule(128, feat_channels, 3, padding=1, norm_cfg=norm_cfg),
|
||||
)
|
||||
|
||||
self.hourglass_modules = nn.ModuleList([
|
||||
nn.Sequential(
|
||||
HourglassAEModule(
|
||||
downsample_times, stage_channels, norm_cfg=norm_cfg),
|
||||
ConvModule(
|
||||
feat_channels,
|
||||
feat_channels,
|
||||
3,
|
||||
padding=1,
|
||||
norm_cfg=norm_cfg),
|
||||
ConvModule(
|
||||
feat_channels,
|
||||
feat_channels,
|
||||
3,
|
||||
padding=1,
|
||||
norm_cfg=norm_cfg)) for _ in range(num_stacks)
|
||||
])
|
||||
|
||||
self.out_convs = nn.ModuleList([
|
||||
ConvModule(
|
||||
cur_channels,
|
||||
out_channels,
|
||||
1,
|
||||
padding=0,
|
||||
norm_cfg=None,
|
||||
act_cfg=None) for _ in range(num_stacks)
|
||||
])
|
||||
|
||||
self.remap_out_convs = nn.ModuleList([
|
||||
ConvModule(
|
||||
out_channels,
|
||||
feat_channels,
|
||||
1,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None) for _ in range(num_stacks - 1)
|
||||
])
|
||||
|
||||
self.remap_feature_convs = nn.ModuleList([
|
||||
ConvModule(
|
||||
feat_channels,
|
||||
feat_channels,
|
||||
1,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None) for _ in range(num_stacks - 1)
|
||||
])
|
||||
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
if isinstance(pretrained, str):
|
||||
logger = get_root_logger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
normal_init(m, std=0.001)
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, 1)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
inter_feat = self.stem(x)
|
||||
out_feats = []
|
||||
|
||||
for ind in range(self.num_stacks):
|
||||
single_hourglass = self.hourglass_modules[ind]
|
||||
out_conv = self.out_convs[ind]
|
||||
|
||||
hourglass_feat = single_hourglass(inter_feat)
|
||||
out_feat = out_conv(hourglass_feat)
|
||||
out_feats.append(out_feat)
|
||||
|
||||
if ind < self.num_stacks - 1:
|
||||
inter_feat = inter_feat + self.remap_out_convs[ind](
|
||||
out_feat) + self.remap_feature_convs[ind](
|
||||
hourglass_feat)
|
||||
|
||||
return out_feats
|
||||
@@ -0,0 +1,746 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
|
||||
import math
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
# from timm.models.layers import to_2tuple, trunc_normal_
|
||||
from mmcv.cnn import (build_activation_layer, build_conv_layer,
|
||||
build_norm_layer, trunc_normal_init)
|
||||
from mmcv.cnn.bricks.transformer import build_dropout
|
||||
from mmcv.runner import BaseModule
|
||||
from torch.nn.functional import pad
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .hrnet import Bottleneck, HRModule, HRNet
|
||||
|
||||
|
||||
def nlc_to_nchw(x, hw_shape):
|
||||
"""Convert [N, L, C] shape tensor to [N, C, H, W] shape tensor.
|
||||
|
||||
Args:
|
||||
x (Tensor): The input tensor of shape [N, L, C] before conversion.
|
||||
hw_shape (Sequence[int]): The height and width of output feature map.
|
||||
|
||||
Returns:
|
||||
Tensor: The output tensor of shape [N, C, H, W] after conversion.
|
||||
"""
|
||||
H, W = hw_shape
|
||||
assert len(x.shape) == 3
|
||||
B, L, C = x.shape
|
||||
assert L == H * W, 'The seq_len doesn\'t match H, W'
|
||||
return x.transpose(1, 2).reshape(B, C, H, W)
|
||||
|
||||
|
||||
def nchw_to_nlc(x):
|
||||
"""Flatten [N, C, H, W] shape tensor to [N, L, C] shape tensor.
|
||||
|
||||
Args:
|
||||
x (Tensor): The input tensor of shape [N, C, H, W] before conversion.
|
||||
|
||||
Returns:
|
||||
Tensor: The output tensor of shape [N, L, C] after conversion.
|
||||
"""
|
||||
assert len(x.shape) == 4
|
||||
return x.flatten(2).transpose(1, 2).contiguous()
|
||||
|
||||
|
||||
def build_drop_path(drop_path_rate):
|
||||
"""Build drop path layer."""
|
||||
return build_dropout(dict(type='DropPath', drop_prob=drop_path_rate))
|
||||
|
||||
|
||||
class WindowMSA(BaseModule):
|
||||
"""Window based multi-head self-attention (W-MSA) module with relative
|
||||
position bias.
|
||||
|
||||
Args:
|
||||
embed_dims (int): Number of input channels.
|
||||
num_heads (int): Number of attention heads.
|
||||
window_size (tuple[int]): The height and width of the window.
|
||||
qkv_bias (bool, optional): If True, add a learnable bias to q, k, v.
|
||||
Default: True.
|
||||
qk_scale (float | None, optional): Override default qk scale of
|
||||
head_dim ** -0.5 if set. Default: None.
|
||||
attn_drop_rate (float, optional): Dropout ratio of attention weight.
|
||||
Default: 0.0
|
||||
proj_drop_rate (float, optional): Dropout ratio of output. Default: 0.
|
||||
with_rpe (bool, optional): If True, use relative position bias.
|
||||
Default: True.
|
||||
init_cfg (dict | None, optional): The Config for initialization.
|
||||
Default: None.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
embed_dims,
|
||||
num_heads,
|
||||
window_size,
|
||||
qkv_bias=True,
|
||||
qk_scale=None,
|
||||
attn_drop_rate=0.,
|
||||
proj_drop_rate=0.,
|
||||
with_rpe=True,
|
||||
init_cfg=None):
|
||||
|
||||
super().__init__(init_cfg=init_cfg)
|
||||
self.embed_dims = embed_dims
|
||||
self.window_size = window_size # Wh, Ww
|
||||
self.num_heads = num_heads
|
||||
head_embed_dims = embed_dims // num_heads
|
||||
self.scale = qk_scale or head_embed_dims**-0.5
|
||||
|
||||
self.with_rpe = with_rpe
|
||||
if self.with_rpe:
|
||||
# define a parameter table of relative position bias
|
||||
self.relative_position_bias_table = nn.Parameter(
|
||||
torch.zeros(
|
||||
(2 * window_size[0] - 1) * (2 * window_size[1] - 1),
|
||||
num_heads)) # 2*Wh-1 * 2*Ww-1, nH
|
||||
|
||||
Wh, Ww = self.window_size
|
||||
rel_index_coords = self.double_step_seq(2 * Ww - 1, Wh, 1, Ww)
|
||||
rel_position_index = rel_index_coords + rel_index_coords.T
|
||||
rel_position_index = rel_position_index.flip(1).contiguous()
|
||||
self.register_buffer('relative_position_index', rel_position_index)
|
||||
|
||||
self.qkv = nn.Linear(embed_dims, embed_dims * 3, bias=qkv_bias)
|
||||
self.attn_drop = nn.Dropout(attn_drop_rate)
|
||||
self.proj = nn.Linear(embed_dims, embed_dims)
|
||||
self.proj_drop = nn.Dropout(proj_drop_rate)
|
||||
|
||||
self.softmax = nn.Softmax(dim=-1)
|
||||
|
||||
def init_weights(self):
|
||||
trunc_normal_init(self.relative_position_bias_table, std=0.02)
|
||||
|
||||
def forward(self, x, mask=None):
|
||||
"""
|
||||
Args:
|
||||
|
||||
x (tensor): input features with shape of (B*num_windows, N, C)
|
||||
mask (tensor | None, Optional): mask with shape of (num_windows,
|
||||
Wh*Ww, Wh*Ww), value should be between (-inf, 0].
|
||||
"""
|
||||
B, N, C = x.shape
|
||||
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads,
|
||||
C // self.num_heads).permute(2, 0, 3, 1, 4)
|
||||
q, k, v = qkv[0], qkv[1], qkv[2]
|
||||
|
||||
q = q * self.scale
|
||||
attn = (q @ k.transpose(-2, -1))
|
||||
|
||||
if self.with_rpe:
|
||||
relative_position_bias = self.relative_position_bias_table[
|
||||
self.relative_position_index.view(-1)].view(
|
||||
self.window_size[0] * self.window_size[1],
|
||||
self.window_size[0] * self.window_size[1],
|
||||
-1) # Wh*Ww,Wh*Ww,nH
|
||||
relative_position_bias = relative_position_bias.permute(
|
||||
2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww
|
||||
attn = attn + relative_position_bias.unsqueeze(0)
|
||||
|
||||
if mask is not None:
|
||||
nW = mask.shape[0]
|
||||
attn = attn.view(B // nW, nW, self.num_heads, N,
|
||||
N) + mask.unsqueeze(1).unsqueeze(0)
|
||||
attn = attn.view(-1, self.num_heads, N, N)
|
||||
attn = self.softmax(attn)
|
||||
|
||||
attn = self.attn_drop(attn)
|
||||
|
||||
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
|
||||
x = self.proj(x)
|
||||
x = self.proj_drop(x)
|
||||
return x
|
||||
|
||||
@staticmethod
|
||||
def double_step_seq(step1, len1, step2, len2):
|
||||
seq1 = torch.arange(0, step1 * len1, step1)
|
||||
seq2 = torch.arange(0, step2 * len2, step2)
|
||||
return (seq1[:, None] + seq2[None, :]).reshape(1, -1)
|
||||
|
||||
|
||||
class LocalWindowSelfAttention(BaseModule):
|
||||
r""" Local-window Self Attention (LSA) module with relative position bias.
|
||||
|
||||
This module is the short-range self-attention module in the
|
||||
Interlaced Sparse Self-Attention <https://arxiv.org/abs/1907.12273>`_.
|
||||
|
||||
Args:
|
||||
embed_dims (int): Number of input channels.
|
||||
num_heads (int): Number of attention heads.
|
||||
window_size (tuple[int] | int): The height and width of the window.
|
||||
qkv_bias (bool, optional): If True, add a learnable bias to q, k, v.
|
||||
Default: True.
|
||||
qk_scale (float | None, optional): Override default qk scale of
|
||||
head_dim ** -0.5 if set. Default: None.
|
||||
attn_drop_rate (float, optional): Dropout ratio of attention weight.
|
||||
Default: 0.0
|
||||
proj_drop_rate (float, optional): Dropout ratio of output. Default: 0.
|
||||
with_rpe (bool, optional): If True, use relative position bias.
|
||||
Default: True.
|
||||
with_pad_mask (bool, optional): If True, mask out the padded tokens in
|
||||
the attention process. Default: False.
|
||||
init_cfg (dict | None, optional): The Config for initialization.
|
||||
Default: None.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
embed_dims,
|
||||
num_heads,
|
||||
window_size,
|
||||
qkv_bias=True,
|
||||
qk_scale=None,
|
||||
attn_drop_rate=0.,
|
||||
proj_drop_rate=0.,
|
||||
with_rpe=True,
|
||||
with_pad_mask=False,
|
||||
init_cfg=None):
|
||||
super().__init__(init_cfg=init_cfg)
|
||||
if isinstance(window_size, int):
|
||||
window_size = (window_size, window_size)
|
||||
self.window_size = window_size
|
||||
self.with_pad_mask = with_pad_mask
|
||||
self.attn = WindowMSA(
|
||||
embed_dims=embed_dims,
|
||||
num_heads=num_heads,
|
||||
window_size=window_size,
|
||||
qkv_bias=qkv_bias,
|
||||
qk_scale=qk_scale,
|
||||
attn_drop_rate=attn_drop_rate,
|
||||
proj_drop_rate=proj_drop_rate,
|
||||
with_rpe=with_rpe,
|
||||
init_cfg=init_cfg)
|
||||
|
||||
def forward(self, x, H, W, **kwargs):
|
||||
"""Forward function."""
|
||||
B, N, C = x.shape
|
||||
x = x.view(B, H, W, C)
|
||||
Wh, Ww = self.window_size
|
||||
|
||||
# center-pad the feature on H and W axes
|
||||
pad_h = math.ceil(H / Wh) * Wh - H
|
||||
pad_w = math.ceil(W / Ww) * Ww - W
|
||||
x = pad(x, (0, 0, pad_w // 2, pad_w - pad_w // 2, pad_h // 2,
|
||||
pad_h - pad_h // 2))
|
||||
|
||||
# permute
|
||||
x = x.view(B, math.ceil(H / Wh), Wh, math.ceil(W / Ww), Ww, C)
|
||||
x = x.permute(0, 1, 3, 2, 4, 5)
|
||||
x = x.reshape(-1, Wh * Ww, C) # (B*num_window, Wh*Ww, C)
|
||||
|
||||
# attention
|
||||
if self.with_pad_mask and pad_h > 0 and pad_w > 0:
|
||||
pad_mask = x.new_zeros(1, H, W, 1)
|
||||
pad_mask = pad(
|
||||
pad_mask, [
|
||||
0, 0, pad_w // 2, pad_w - pad_w // 2, pad_h // 2,
|
||||
pad_h - pad_h // 2
|
||||
],
|
||||
value=-float('inf'))
|
||||
pad_mask = pad_mask.view(1, math.ceil(H / Wh), Wh,
|
||||
math.ceil(W / Ww), Ww, 1)
|
||||
pad_mask = pad_mask.permute(1, 3, 0, 2, 4, 5)
|
||||
pad_mask = pad_mask.reshape(-1, Wh * Ww)
|
||||
pad_mask = pad_mask[:, None, :].expand([-1, Wh * Ww, -1])
|
||||
out = self.attn(x, pad_mask, **kwargs)
|
||||
else:
|
||||
out = self.attn(x, **kwargs)
|
||||
|
||||
# reverse permutation
|
||||
out = out.reshape(B, math.ceil(H / Wh), math.ceil(W / Ww), Wh, Ww, C)
|
||||
out = out.permute(0, 1, 3, 2, 4, 5)
|
||||
out = out.reshape(B, H + pad_h, W + pad_w, C)
|
||||
|
||||
# de-pad
|
||||
out = out[:, pad_h // 2:H + pad_h // 2, pad_w // 2:W + pad_w // 2]
|
||||
return out.reshape(B, N, C)
|
||||
|
||||
|
||||
class CrossFFN(BaseModule):
|
||||
r"""FFN with Depthwise Conv of HRFormer.
|
||||
|
||||
Args:
|
||||
in_features (int): The feature dimension.
|
||||
hidden_features (int, optional): The hidden dimension of FFNs.
|
||||
Defaults: The same as in_features.
|
||||
act_cfg (dict, optional): Config of activation layer.
|
||||
Default: dict(type='GELU').
|
||||
dw_act_cfg (dict, optional): Config of activation layer appended
|
||||
right after DW Conv. Default: dict(type='GELU').
|
||||
norm_cfg (dict, optional): Config of norm layer.
|
||||
Default: dict(type='SyncBN').
|
||||
init_cfg (dict | list | None, optional): The init config.
|
||||
Default: None.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_features,
|
||||
hidden_features=None,
|
||||
out_features=None,
|
||||
act_cfg=dict(type='GELU'),
|
||||
dw_act_cfg=dict(type='GELU'),
|
||||
norm_cfg=dict(type='SyncBN'),
|
||||
init_cfg=None):
|
||||
super().__init__(init_cfg=init_cfg)
|
||||
out_features = out_features or in_features
|
||||
hidden_features = hidden_features or in_features
|
||||
self.fc1 = nn.Conv2d(in_features, hidden_features, kernel_size=1)
|
||||
self.act1 = build_activation_layer(act_cfg)
|
||||
self.norm1 = build_norm_layer(norm_cfg, hidden_features)[1]
|
||||
self.dw3x3 = nn.Conv2d(
|
||||
hidden_features,
|
||||
hidden_features,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
groups=hidden_features,
|
||||
padding=1)
|
||||
self.act2 = build_activation_layer(dw_act_cfg)
|
||||
self.norm2 = build_norm_layer(norm_cfg, hidden_features)[1]
|
||||
self.fc2 = nn.Conv2d(hidden_features, out_features, kernel_size=1)
|
||||
self.act3 = build_activation_layer(act_cfg)
|
||||
self.norm3 = build_norm_layer(norm_cfg, out_features)[1]
|
||||
|
||||
# put the modules togather
|
||||
self.layers = [
|
||||
self.fc1, self.norm1, self.act1, self.dw3x3, self.norm2, self.act2,
|
||||
self.fc2, self.norm3, self.act3
|
||||
]
|
||||
|
||||
def forward(self, x, H, W):
|
||||
"""Forward function."""
|
||||
x = nlc_to_nchw(x, (H, W))
|
||||
for layer in self.layers:
|
||||
x = layer(x)
|
||||
x = nchw_to_nlc(x)
|
||||
return x
|
||||
|
||||
|
||||
class HRFormerBlock(BaseModule):
|
||||
"""High-Resolution Block for HRFormer.
|
||||
|
||||
Args:
|
||||
in_features (int): The input dimension.
|
||||
out_features (int): The output dimension.
|
||||
num_heads (int): The number of head within each LSA.
|
||||
window_size (int, optional): The window size for the LSA.
|
||||
Default: 7
|
||||
mlp_ratio (int, optional): The expansion ration of FFN.
|
||||
Default: 4
|
||||
act_cfg (dict, optional): Config of activation layer.
|
||||
Default: dict(type='GELU').
|
||||
norm_cfg (dict, optional): Config of norm layer.
|
||||
Default: dict(type='SyncBN').
|
||||
transformer_norm_cfg (dict, optional): Config of transformer norm
|
||||
layer. Default: dict(type='LN', eps=1e-6).
|
||||
init_cfg (dict | list | None, optional): The init config.
|
||||
Default: None.
|
||||
"""
|
||||
|
||||
expansion = 1
|
||||
|
||||
def __init__(self,
|
||||
in_features,
|
||||
out_features,
|
||||
num_heads,
|
||||
window_size=7,
|
||||
mlp_ratio=4.0,
|
||||
drop_path=0.0,
|
||||
act_cfg=dict(type='GELU'),
|
||||
norm_cfg=dict(type='SyncBN'),
|
||||
transformer_norm_cfg=dict(type='LN', eps=1e-6),
|
||||
init_cfg=None,
|
||||
**kwargs):
|
||||
super(HRFormerBlock, self).__init__(init_cfg=init_cfg)
|
||||
self.num_heads = num_heads
|
||||
self.window_size = window_size
|
||||
self.mlp_ratio = mlp_ratio
|
||||
|
||||
self.norm1 = build_norm_layer(transformer_norm_cfg, in_features)[1]
|
||||
self.attn = LocalWindowSelfAttention(
|
||||
in_features,
|
||||
num_heads=num_heads,
|
||||
window_size=window_size,
|
||||
init_cfg=None,
|
||||
**kwargs)
|
||||
|
||||
self.norm2 = build_norm_layer(transformer_norm_cfg, out_features)[1]
|
||||
self.ffn = CrossFFN(
|
||||
in_features=in_features,
|
||||
hidden_features=int(in_features * mlp_ratio),
|
||||
out_features=out_features,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg,
|
||||
dw_act_cfg=act_cfg,
|
||||
init_cfg=None)
|
||||
|
||||
self.drop_path = build_drop_path(
|
||||
drop_path) if drop_path > 0.0 else nn.Identity()
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
B, C, H, W = x.size()
|
||||
# Attention
|
||||
x = x.view(B, C, -1).permute(0, 2, 1)
|
||||
x = x + self.drop_path(self.attn(self.norm1(x), H, W))
|
||||
# FFN
|
||||
x = x + self.drop_path(self.ffn(self.norm2(x), H, W))
|
||||
x = x.permute(0, 2, 1).view(B, C, H, W)
|
||||
return x
|
||||
|
||||
def extra_repr(self):
|
||||
"""(Optional) Set the extra information about this module."""
|
||||
return 'num_heads={}, window_size={}, mlp_ratio={}'.format(
|
||||
self.num_heads, self.window_size, self.mlp_ratio)
|
||||
|
||||
|
||||
class HRFomerModule(HRModule):
|
||||
"""High-Resolution Module for HRFormer.
|
||||
|
||||
Args:
|
||||
num_branches (int): The number of branches in the HRFormerModule.
|
||||
block (nn.Module): The building block of HRFormer.
|
||||
The block should be the HRFormerBlock.
|
||||
num_blocks (tuple): The number of blocks in each branch.
|
||||
The length must be equal to num_branches.
|
||||
num_inchannels (tuple): The number of input channels in each branch.
|
||||
The length must be equal to num_branches.
|
||||
num_channels (tuple): The number of channels in each branch.
|
||||
The length must be equal to num_branches.
|
||||
num_heads (tuple): The number of heads within the LSAs.
|
||||
num_window_sizes (tuple): The window size for the LSAs.
|
||||
num_mlp_ratios (tuple): The expansion ratio for the FFNs.
|
||||
drop_path (int, optional): The drop path rate of HRFomer.
|
||||
Default: 0.0
|
||||
multiscale_output (bool, optional): Whether to output multi-level
|
||||
features produced by multiple branches. If False, only the first
|
||||
level feature will be output. Default: True.
|
||||
conv_cfg (dict, optional): Config of the conv layers.
|
||||
Default: None.
|
||||
norm_cfg (dict, optional): Config of the norm layers appended
|
||||
right after conv. Default: dict(type='SyncBN', requires_grad=True)
|
||||
transformer_norm_cfg (dict, optional): Config of the norm layers.
|
||||
Default: dict(type='LN', eps=1e-6)
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False
|
||||
upsample_cfg(dict, optional): The config of upsample layers in fuse
|
||||
layers. Default: dict(mode='bilinear', align_corners=False)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
num_branches,
|
||||
block,
|
||||
num_blocks,
|
||||
num_inchannels,
|
||||
num_channels,
|
||||
num_heads,
|
||||
num_window_sizes,
|
||||
num_mlp_ratios,
|
||||
multiscale_output=True,
|
||||
drop_paths=0.0,
|
||||
with_rpe=True,
|
||||
with_pad_mask=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||
transformer_norm_cfg=dict(type='LN', eps=1e-6),
|
||||
with_cp=False,
|
||||
upsample_cfg=dict(mode='bilinear', align_corners=False)):
|
||||
|
||||
self.transformer_norm_cfg = transformer_norm_cfg
|
||||
self.drop_paths = drop_paths
|
||||
self.num_heads = num_heads
|
||||
self.num_window_sizes = num_window_sizes
|
||||
self.num_mlp_ratios = num_mlp_ratios
|
||||
self.with_rpe = with_rpe
|
||||
self.with_pad_mask = with_pad_mask
|
||||
|
||||
super().__init__(num_branches, block, num_blocks, num_inchannels,
|
||||
num_channels, multiscale_output, with_cp, conv_cfg,
|
||||
norm_cfg, upsample_cfg)
|
||||
|
||||
def _make_one_branch(self,
|
||||
branch_index,
|
||||
block,
|
||||
num_blocks,
|
||||
num_channels,
|
||||
stride=1):
|
||||
"""Build one branch."""
|
||||
# HRFormerBlock does not support down sample layer yet.
|
||||
assert stride == 1 and self.in_channels[branch_index] == num_channels[
|
||||
branch_index]
|
||||
layers = []
|
||||
layers.append(
|
||||
block(
|
||||
self.in_channels[branch_index],
|
||||
num_channels[branch_index],
|
||||
num_heads=self.num_heads[branch_index],
|
||||
window_size=self.num_window_sizes[branch_index],
|
||||
mlp_ratio=self.num_mlp_ratios[branch_index],
|
||||
drop_path=self.drop_paths[0],
|
||||
norm_cfg=self.norm_cfg,
|
||||
transformer_norm_cfg=self.transformer_norm_cfg,
|
||||
init_cfg=None,
|
||||
with_rpe=self.with_rpe,
|
||||
with_pad_mask=self.with_pad_mask))
|
||||
|
||||
self.in_channels[
|
||||
branch_index] = self.in_channels[branch_index] * block.expansion
|
||||
for i in range(1, num_blocks[branch_index]):
|
||||
layers.append(
|
||||
block(
|
||||
self.in_channels[branch_index],
|
||||
num_channels[branch_index],
|
||||
num_heads=self.num_heads[branch_index],
|
||||
window_size=self.num_window_sizes[branch_index],
|
||||
mlp_ratio=self.num_mlp_ratios[branch_index],
|
||||
drop_path=self.drop_paths[i],
|
||||
norm_cfg=self.norm_cfg,
|
||||
transformer_norm_cfg=self.transformer_norm_cfg,
|
||||
init_cfg=None,
|
||||
with_rpe=self.with_rpe,
|
||||
with_pad_mask=self.with_pad_mask))
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _make_fuse_layers(self):
|
||||
"""Build fuse layers."""
|
||||
if self.num_branches == 1:
|
||||
return None
|
||||
num_branches = self.num_branches
|
||||
num_inchannels = self.in_channels
|
||||
fuse_layers = []
|
||||
for i in range(num_branches if self.multiscale_output else 1):
|
||||
fuse_layer = []
|
||||
for j in range(num_branches):
|
||||
if j > i:
|
||||
fuse_layer.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
num_inchannels[j],
|
||||
num_inchannels[i],
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
num_inchannels[i])[1],
|
||||
nn.Upsample(
|
||||
scale_factor=2**(j - i),
|
||||
mode=self.upsample_cfg['mode'],
|
||||
align_corners=self.
|
||||
upsample_cfg['align_corners'])))
|
||||
elif j == i:
|
||||
fuse_layer.append(None)
|
||||
else:
|
||||
conv3x3s = []
|
||||
for k in range(i - j):
|
||||
if k == i - j - 1:
|
||||
num_outchannels_conv3x3 = num_inchannels[i]
|
||||
with_out_act = False
|
||||
else:
|
||||
num_outchannels_conv3x3 = num_inchannels[j]
|
||||
with_out_act = True
|
||||
sub_modules = [
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
num_inchannels[j],
|
||||
num_inchannels[j],
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
groups=num_inchannels[j],
|
||||
bias=False,
|
||||
),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
num_inchannels[j])[1],
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
num_inchannels[j],
|
||||
num_outchannels_conv3x3,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
bias=False,
|
||||
),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
num_outchannels_conv3x3)[1]
|
||||
]
|
||||
if with_out_act:
|
||||
sub_modules.append(nn.ReLU(False))
|
||||
conv3x3s.append(nn.Sequential(*sub_modules))
|
||||
fuse_layer.append(nn.Sequential(*conv3x3s))
|
||||
fuse_layers.append(nn.ModuleList(fuse_layer))
|
||||
|
||||
return nn.ModuleList(fuse_layers)
|
||||
|
||||
def get_num_inchannels(self):
|
||||
"""Return the number of input channels."""
|
||||
return self.in_channels
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class HRFormer(HRNet):
|
||||
"""HRFormer backbone.
|
||||
|
||||
This backbone is the implementation of `HRFormer: High-Resolution
|
||||
Transformer for Dense Prediction <https://arxiv.org/abs/2110.09408>`_.
|
||||
|
||||
Args:
|
||||
extra (dict): Detailed configuration for each stage of HRNet.
|
||||
There must be 4 stages, the configuration for each stage must have
|
||||
5 keys:
|
||||
|
||||
- num_modules (int): The number of HRModule in this stage.
|
||||
- num_branches (int): The number of branches in the HRModule.
|
||||
- block (str): The type of block.
|
||||
- num_blocks (tuple): The number of blocks in each branch.
|
||||
The length must be equal to num_branches.
|
||||
- num_channels (tuple): The number of channels in each branch.
|
||||
The length must be equal to num_branches.
|
||||
in_channels (int): Number of input image channels. Normally 3.
|
||||
conv_cfg (dict): Dictionary to construct and config conv layer.
|
||||
Default: None.
|
||||
norm_cfg (dict): Config of norm layer.
|
||||
Use `SyncBN` by default.
|
||||
transformer_norm_cfg (dict): Config of transformer norm layer.
|
||||
Use `LN` by default.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: False.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
Example:
|
||||
>>> from mmpose.models import HRFormer
|
||||
>>> import torch
|
||||
>>> extra = dict(
|
||||
>>> 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='HRFORMER',
|
||||
>>> 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='HRFORMER',
|
||||
>>> 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=2,
|
||||
>>> num_branches=4,
|
||||
>>> block='HRFORMER',
|
||||
>>> 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)))
|
||||
>>> self = HRFormer(extra, in_channels=1)
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 1, 32, 32)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 32, 8, 8)
|
||||
(1, 64, 4, 4)
|
||||
(1, 128, 2, 2)
|
||||
(1, 256, 1, 1)
|
||||
"""
|
||||
|
||||
blocks_dict = {'BOTTLENECK': Bottleneck, 'HRFORMERBLOCK': HRFormerBlock}
|
||||
|
||||
def __init__(self,
|
||||
extra,
|
||||
in_channels=3,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
transformer_norm_cfg=dict(type='LN', eps=1e-6),
|
||||
norm_eval=False,
|
||||
with_cp=False,
|
||||
zero_init_residual=False,
|
||||
frozen_stages=-1):
|
||||
|
||||
# stochastic depth
|
||||
depths = [
|
||||
extra[stage]['num_blocks'][0] * extra[stage]['num_modules']
|
||||
for stage in ['stage2', 'stage3', 'stage4']
|
||||
]
|
||||
depth_s2, depth_s3, _ = depths
|
||||
drop_path_rate = extra['drop_path_rate']
|
||||
dpr = [
|
||||
x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))
|
||||
]
|
||||
extra['stage2']['drop_path_rates'] = dpr[0:depth_s2]
|
||||
extra['stage3']['drop_path_rates'] = dpr[depth_s2:depth_s2 + depth_s3]
|
||||
extra['stage4']['drop_path_rates'] = dpr[depth_s2 + depth_s3:]
|
||||
|
||||
# HRFormer use bilinear upsample as default
|
||||
upsample_cfg = extra.get('upsample', {
|
||||
'mode': 'bilinear',
|
||||
'align_corners': False
|
||||
})
|
||||
extra['upsample'] = upsample_cfg
|
||||
self.transformer_norm_cfg = transformer_norm_cfg
|
||||
self.with_rpe = extra.get('with_rpe', True)
|
||||
self.with_pad_mask = extra.get('with_pad_mask', False)
|
||||
|
||||
super().__init__(extra, in_channels, conv_cfg, norm_cfg, norm_eval,
|
||||
with_cp, zero_init_residual, frozen_stages)
|
||||
|
||||
def _make_stage(self,
|
||||
layer_config,
|
||||
num_inchannels,
|
||||
multiscale_output=True):
|
||||
"""Make each stage."""
|
||||
num_modules = layer_config['num_modules']
|
||||
num_branches = layer_config['num_branches']
|
||||
num_blocks = layer_config['num_blocks']
|
||||
num_channels = layer_config['num_channels']
|
||||
block = self.blocks_dict[layer_config['block']]
|
||||
num_heads = layer_config['num_heads']
|
||||
num_window_sizes = layer_config['window_sizes']
|
||||
num_mlp_ratios = layer_config['mlp_ratios']
|
||||
drop_path_rates = layer_config['drop_path_rates']
|
||||
|
||||
modules = []
|
||||
for i in range(num_modules):
|
||||
# multiscale_output is only used at the last module
|
||||
if not multiscale_output and i == num_modules - 1:
|
||||
reset_multiscale_output = False
|
||||
else:
|
||||
reset_multiscale_output = True
|
||||
|
||||
modules.append(
|
||||
HRFomerModule(
|
||||
num_branches,
|
||||
block,
|
||||
num_blocks,
|
||||
num_inchannels,
|
||||
num_channels,
|
||||
num_heads,
|
||||
num_window_sizes,
|
||||
num_mlp_ratios,
|
||||
reset_multiscale_output,
|
||||
drop_paths=drop_path_rates[num_blocks[0] *
|
||||
i:num_blocks[0] * (i + 1)],
|
||||
with_rpe=self.with_rpe,
|
||||
with_pad_mask=self.with_pad_mask,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
transformer_norm_cfg=self.transformer_norm_cfg,
|
||||
with_cp=self.with_cp,
|
||||
upsample_cfg=self.upsample_cfg))
|
||||
num_inchannels = modules[-1].get_num_inchannels()
|
||||
|
||||
return nn.Sequential(*modules), num_inchannels
|
||||
@@ -0,0 +1,604 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import (build_conv_layer, build_norm_layer, constant_init,
|
||||
normal_init)
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from mmpose.utils import get_root_logger
|
||||
from ..builder import BACKBONES
|
||||
from .resnet import BasicBlock, Bottleneck, get_expansion
|
||||
from .utils import load_checkpoint
|
||||
|
||||
|
||||
class HRModule(nn.Module):
|
||||
"""High-Resolution Module for HRNet.
|
||||
|
||||
In this module, every branch has 4 BasicBlocks/Bottlenecks. Fusion/Exchange
|
||||
is in this module.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
num_branches,
|
||||
blocks,
|
||||
num_blocks,
|
||||
in_channels,
|
||||
num_channels,
|
||||
multiscale_output=False,
|
||||
with_cp=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
upsample_cfg=dict(mode='nearest', align_corners=None)):
|
||||
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self._check_branches(num_branches, num_blocks, in_channels,
|
||||
num_channels)
|
||||
|
||||
self.in_channels = in_channels
|
||||
self.num_branches = num_branches
|
||||
|
||||
self.multiscale_output = multiscale_output
|
||||
self.norm_cfg = norm_cfg
|
||||
self.conv_cfg = conv_cfg
|
||||
self.upsample_cfg = upsample_cfg
|
||||
self.with_cp = with_cp
|
||||
self.branches = self._make_branches(num_branches, blocks, num_blocks,
|
||||
num_channels)
|
||||
self.fuse_layers = self._make_fuse_layers()
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
@staticmethod
|
||||
def _check_branches(num_branches, num_blocks, in_channels, num_channels):
|
||||
"""Check input to avoid ValueError."""
|
||||
if num_branches != len(num_blocks):
|
||||
error_msg = f'NUM_BRANCHES({num_branches}) ' \
|
||||
f'!= NUM_BLOCKS({len(num_blocks)})'
|
||||
raise ValueError(error_msg)
|
||||
|
||||
if num_branches != len(num_channels):
|
||||
error_msg = f'NUM_BRANCHES({num_branches}) ' \
|
||||
f'!= NUM_CHANNELS({len(num_channels)})'
|
||||
raise ValueError(error_msg)
|
||||
|
||||
if num_branches != len(in_channels):
|
||||
error_msg = f'NUM_BRANCHES({num_branches}) ' \
|
||||
f'!= NUM_INCHANNELS({len(in_channels)})'
|
||||
raise ValueError(error_msg)
|
||||
|
||||
def _make_one_branch(self,
|
||||
branch_index,
|
||||
block,
|
||||
num_blocks,
|
||||
num_channels,
|
||||
stride=1):
|
||||
"""Make one branch."""
|
||||
downsample = None
|
||||
if stride != 1 or \
|
||||
self.in_channels[branch_index] != \
|
||||
num_channels[branch_index] * get_expansion(block):
|
||||
downsample = nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.in_channels[branch_index],
|
||||
num_channels[branch_index] * get_expansion(block),
|
||||
kernel_size=1,
|
||||
stride=stride,
|
||||
bias=False),
|
||||
build_norm_layer(
|
||||
self.norm_cfg,
|
||||
num_channels[branch_index] * get_expansion(block))[1])
|
||||
|
||||
layers = []
|
||||
layers.append(
|
||||
block(
|
||||
self.in_channels[branch_index],
|
||||
num_channels[branch_index] * get_expansion(block),
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
with_cp=self.with_cp,
|
||||
norm_cfg=self.norm_cfg,
|
||||
conv_cfg=self.conv_cfg))
|
||||
self.in_channels[branch_index] = \
|
||||
num_channels[branch_index] * get_expansion(block)
|
||||
for _ in range(1, num_blocks[branch_index]):
|
||||
layers.append(
|
||||
block(
|
||||
self.in_channels[branch_index],
|
||||
num_channels[branch_index] * get_expansion(block),
|
||||
with_cp=self.with_cp,
|
||||
norm_cfg=self.norm_cfg,
|
||||
conv_cfg=self.conv_cfg))
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _make_branches(self, num_branches, block, num_blocks, num_channels):
|
||||
"""Make branches."""
|
||||
branches = []
|
||||
|
||||
for i in range(num_branches):
|
||||
branches.append(
|
||||
self._make_one_branch(i, block, num_blocks, num_channels))
|
||||
|
||||
return nn.ModuleList(branches)
|
||||
|
||||
def _make_fuse_layers(self):
|
||||
"""Make fuse layer."""
|
||||
if self.num_branches == 1:
|
||||
return None
|
||||
|
||||
num_branches = self.num_branches
|
||||
in_channels = self.in_channels
|
||||
fuse_layers = []
|
||||
num_out_branches = num_branches if self.multiscale_output else 1
|
||||
|
||||
for i in range(num_out_branches):
|
||||
fuse_layer = []
|
||||
for j in range(num_branches):
|
||||
if j > i:
|
||||
fuse_layer.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[i],
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg, in_channels[i])[1],
|
||||
nn.Upsample(
|
||||
scale_factor=2**(j - i),
|
||||
mode=self.upsample_cfg['mode'],
|
||||
align_corners=self.
|
||||
upsample_cfg['align_corners'])))
|
||||
elif j == i:
|
||||
fuse_layer.append(None)
|
||||
else:
|
||||
conv_downsamples = []
|
||||
for k in range(i - j):
|
||||
if k == i - j - 1:
|
||||
conv_downsamples.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[i],
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
in_channels[i])[1]))
|
||||
else:
|
||||
conv_downsamples.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[j],
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
in_channels[j])[1],
|
||||
nn.ReLU(inplace=True)))
|
||||
fuse_layer.append(nn.Sequential(*conv_downsamples))
|
||||
fuse_layers.append(nn.ModuleList(fuse_layer))
|
||||
|
||||
return nn.ModuleList(fuse_layers)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
if self.num_branches == 1:
|
||||
return [self.branches[0](x[0])]
|
||||
|
||||
for i in range(self.num_branches):
|
||||
x[i] = self.branches[i](x[i])
|
||||
|
||||
x_fuse = []
|
||||
for i in range(len(self.fuse_layers)):
|
||||
y = 0
|
||||
for j in range(self.num_branches):
|
||||
if i == j:
|
||||
y += x[j]
|
||||
else:
|
||||
y += self.fuse_layers[i][j](x[j])
|
||||
x_fuse.append(self.relu(y))
|
||||
return x_fuse
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class HRNet(nn.Module):
|
||||
"""HRNet backbone.
|
||||
|
||||
`High-Resolution Representations for Labeling Pixels and Regions
|
||||
<https://arxiv.org/abs/1904.04514>`__
|
||||
|
||||
Args:
|
||||
extra (dict): detailed configuration for each stage of HRNet.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
zero_init_residual (bool): whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import HRNet
|
||||
>>> import torch
|
||||
>>> 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)))
|
||||
>>> self = HRNet(extra, in_channels=1)
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 1, 32, 32)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 32, 8, 8)
|
||||
"""
|
||||
|
||||
blocks_dict = {'BASIC': BasicBlock, 'BOTTLENECK': Bottleneck}
|
||||
|
||||
def __init__(self,
|
||||
extra,
|
||||
in_channels=3,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
norm_eval=False,
|
||||
with_cp=False,
|
||||
zero_init_residual=False,
|
||||
frozen_stages=-1):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.extra = extra
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.norm_eval = norm_eval
|
||||
self.with_cp = with_cp
|
||||
self.zero_init_residual = zero_init_residual
|
||||
self.frozen_stages = frozen_stages
|
||||
|
||||
# stem net
|
||||
self.norm1_name, norm1 = build_norm_layer(self.norm_cfg, 64, postfix=1)
|
||||
self.norm2_name, norm2 = build_norm_layer(self.norm_cfg, 64, postfix=2)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
64,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
bias=False)
|
||||
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.conv2 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
64,
|
||||
64,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
bias=False)
|
||||
|
||||
self.add_module(self.norm2_name, norm2)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
self.upsample_cfg = self.extra.get('upsample', {
|
||||
'mode': 'nearest',
|
||||
'align_corners': None
|
||||
})
|
||||
|
||||
# stage 1
|
||||
self.stage1_cfg = self.extra['stage1']
|
||||
num_channels = self.stage1_cfg['num_channels'][0]
|
||||
block_type = self.stage1_cfg['block']
|
||||
num_blocks = self.stage1_cfg['num_blocks'][0]
|
||||
|
||||
block = self.blocks_dict[block_type]
|
||||
stage1_out_channels = num_channels * get_expansion(block)
|
||||
self.layer1 = self._make_layer(block, 64, stage1_out_channels,
|
||||
num_blocks)
|
||||
|
||||
# stage 2
|
||||
self.stage2_cfg = self.extra['stage2']
|
||||
num_channels = self.stage2_cfg['num_channels']
|
||||
block_type = self.stage2_cfg['block']
|
||||
|
||||
block = self.blocks_dict[block_type]
|
||||
num_channels = [
|
||||
channel * get_expansion(block) for channel in num_channels
|
||||
]
|
||||
self.transition1 = self._make_transition_layer([stage1_out_channels],
|
||||
num_channels)
|
||||
self.stage2, pre_stage_channels = self._make_stage(
|
||||
self.stage2_cfg, num_channels)
|
||||
|
||||
# stage 3
|
||||
self.stage3_cfg = self.extra['stage3']
|
||||
num_channels = self.stage3_cfg['num_channels']
|
||||
block_type = self.stage3_cfg['block']
|
||||
|
||||
block = self.blocks_dict[block_type]
|
||||
num_channels = [
|
||||
channel * get_expansion(block) for channel in num_channels
|
||||
]
|
||||
self.transition2 = self._make_transition_layer(pre_stage_channels,
|
||||
num_channels)
|
||||
self.stage3, pre_stage_channels = self._make_stage(
|
||||
self.stage3_cfg, num_channels)
|
||||
|
||||
# stage 4
|
||||
self.stage4_cfg = self.extra['stage4']
|
||||
num_channels = self.stage4_cfg['num_channels']
|
||||
block_type = self.stage4_cfg['block']
|
||||
|
||||
block = self.blocks_dict[block_type]
|
||||
num_channels = [
|
||||
channel * get_expansion(block) for channel in num_channels
|
||||
]
|
||||
self.transition3 = self._make_transition_layer(pre_stage_channels,
|
||||
num_channels)
|
||||
|
||||
self.stage4, pre_stage_channels = self._make_stage(
|
||||
self.stage4_cfg,
|
||||
num_channels,
|
||||
multiscale_output=self.stage4_cfg.get('multiscale_output', False))
|
||||
|
||||
self._freeze_stages()
|
||||
|
||||
@property
|
||||
def norm1(self):
|
||||
"""nn.Module: the normalization layer named "norm1" """
|
||||
return getattr(self, self.norm1_name)
|
||||
|
||||
@property
|
||||
def norm2(self):
|
||||
"""nn.Module: the normalization layer named "norm2" """
|
||||
return getattr(self, self.norm2_name)
|
||||
|
||||
def _make_transition_layer(self, num_channels_pre_layer,
|
||||
num_channels_cur_layer):
|
||||
"""Make transition layer."""
|
||||
num_branches_cur = len(num_channels_cur_layer)
|
||||
num_branches_pre = len(num_channels_pre_layer)
|
||||
|
||||
transition_layers = []
|
||||
for i in range(num_branches_cur):
|
||||
if i < num_branches_pre:
|
||||
if num_channels_cur_layer[i] != num_channels_pre_layer[i]:
|
||||
transition_layers.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
num_channels_pre_layer[i],
|
||||
num_channels_cur_layer[i],
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
num_channels_cur_layer[i])[1],
|
||||
nn.ReLU(inplace=True)))
|
||||
else:
|
||||
transition_layers.append(None)
|
||||
else:
|
||||
conv_downsamples = []
|
||||
for j in range(i + 1 - num_branches_pre):
|
||||
in_channels = num_channels_pre_layer[-1]
|
||||
out_channels = num_channels_cur_layer[i] \
|
||||
if j == i - num_branches_pre else in_channels
|
||||
conv_downsamples.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg, out_channels)[1],
|
||||
nn.ReLU(inplace=True)))
|
||||
transition_layers.append(nn.Sequential(*conv_downsamples))
|
||||
|
||||
return nn.ModuleList(transition_layers)
|
||||
|
||||
def _make_layer(self, block, in_channels, out_channels, blocks, stride=1):
|
||||
"""Make layer."""
|
||||
downsample = None
|
||||
if stride != 1 or in_channels != out_channels:
|
||||
downsample = nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
stride=stride,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg, out_channels)[1])
|
||||
|
||||
layers = []
|
||||
layers.append(
|
||||
block(
|
||||
in_channels,
|
||||
out_channels,
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
with_cp=self.with_cp,
|
||||
norm_cfg=self.norm_cfg,
|
||||
conv_cfg=self.conv_cfg))
|
||||
for _ in range(1, blocks):
|
||||
layers.append(
|
||||
block(
|
||||
out_channels,
|
||||
out_channels,
|
||||
with_cp=self.with_cp,
|
||||
norm_cfg=self.norm_cfg,
|
||||
conv_cfg=self.conv_cfg))
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _make_stage(self, layer_config, in_channels, multiscale_output=True):
|
||||
"""Make stage."""
|
||||
num_modules = layer_config['num_modules']
|
||||
num_branches = layer_config['num_branches']
|
||||
num_blocks = layer_config['num_blocks']
|
||||
num_channels = layer_config['num_channels']
|
||||
block = self.blocks_dict[layer_config['block']]
|
||||
|
||||
hr_modules = []
|
||||
for i in range(num_modules):
|
||||
# multi_scale_output is only used for the last module
|
||||
if not multiscale_output and i == num_modules - 1:
|
||||
reset_multiscale_output = False
|
||||
else:
|
||||
reset_multiscale_output = True
|
||||
|
||||
hr_modules.append(
|
||||
HRModule(
|
||||
num_branches,
|
||||
block,
|
||||
num_blocks,
|
||||
in_channels,
|
||||
num_channels,
|
||||
reset_multiscale_output,
|
||||
with_cp=self.with_cp,
|
||||
norm_cfg=self.norm_cfg,
|
||||
conv_cfg=self.conv_cfg,
|
||||
upsample_cfg=self.upsample_cfg))
|
||||
|
||||
in_channels = hr_modules[-1].in_channels
|
||||
|
||||
return nn.Sequential(*hr_modules), in_channels
|
||||
|
||||
def _freeze_stages(self):
|
||||
"""Freeze parameters."""
|
||||
if self.frozen_stages >= 0:
|
||||
self.norm1.eval()
|
||||
self.norm2.eval()
|
||||
|
||||
for m in [self.conv1, self.norm1, self.conv2, self.norm2]:
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
if i == 1:
|
||||
m = getattr(self, 'layer1')
|
||||
else:
|
||||
m = getattr(self, f'stage{i}')
|
||||
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
if i < 4:
|
||||
m = getattr(self, f'transition{i}')
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
if isinstance(pretrained, str):
|
||||
logger = get_root_logger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
normal_init(m, std=0.001)
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, 1)
|
||||
|
||||
if self.zero_init_residual:
|
||||
for m in self.modules():
|
||||
if isinstance(m, Bottleneck):
|
||||
constant_init(m.norm3, 0)
|
||||
elif isinstance(m, BasicBlock):
|
||||
constant_init(m.norm2, 0)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
x = self.conv1(x)
|
||||
x = self.norm1(x)
|
||||
x = self.relu(x)
|
||||
x = self.conv2(x)
|
||||
x = self.norm2(x)
|
||||
x = self.relu(x)
|
||||
x = self.layer1(x)
|
||||
|
||||
x_list = []
|
||||
for i in range(self.stage2_cfg['num_branches']):
|
||||
if self.transition1[i] is not None:
|
||||
x_list.append(self.transition1[i](x))
|
||||
else:
|
||||
x_list.append(x)
|
||||
y_list = self.stage2(x_list)
|
||||
|
||||
x_list = []
|
||||
for i in range(self.stage3_cfg['num_branches']):
|
||||
if self.transition2[i] is not None:
|
||||
x_list.append(self.transition2[i](y_list[-1]))
|
||||
else:
|
||||
x_list.append(y_list[i])
|
||||
y_list = self.stage3(x_list)
|
||||
|
||||
x_list = []
|
||||
for i in range(self.stage4_cfg['num_branches']):
|
||||
if self.transition3[i] is not None:
|
||||
x_list.append(self.transition3[i](y_list[-1]))
|
||||
else:
|
||||
x_list.append(y_list[i])
|
||||
y_list = self.stage4(x_list)
|
||||
|
||||
return y_list
|
||||
|
||||
def train(self, mode=True):
|
||||
"""Convert the model into training mode."""
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
@@ -0,0 +1,984 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Adapted from https://github.com/HRNet/Lite-HRNet
|
||||
# Original licence: Apache License 2.0.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
import mmcv
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import (ConvModule, DepthwiseSeparableConvModule,
|
||||
build_conv_layer, build_norm_layer, constant_init,
|
||||
normal_init)
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from mmpose.utils import get_root_logger
|
||||
from ..builder import BACKBONES
|
||||
from .utils import channel_shuffle, load_checkpoint
|
||||
|
||||
|
||||
class SpatialWeighting(nn.Module):
|
||||
"""Spatial weighting module.
|
||||
|
||||
Args:
|
||||
channels (int): The channels of the module.
|
||||
ratio (int): channel reduction ratio.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: None.
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: (dict(type='ReLU'), dict(type='Sigmoid')).
|
||||
The last ConvModule uses Sigmoid by default.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
channels,
|
||||
ratio=16,
|
||||
conv_cfg=None,
|
||||
norm_cfg=None,
|
||||
act_cfg=(dict(type='ReLU'), dict(type='Sigmoid'))):
|
||||
super().__init__()
|
||||
if isinstance(act_cfg, dict):
|
||||
act_cfg = (act_cfg, act_cfg)
|
||||
assert len(act_cfg) == 2
|
||||
assert mmcv.is_tuple_of(act_cfg, dict)
|
||||
self.global_avgpool = nn.AdaptiveAvgPool2d(1)
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=channels,
|
||||
out_channels=int(channels / ratio),
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg[0])
|
||||
self.conv2 = ConvModule(
|
||||
in_channels=int(channels / ratio),
|
||||
out_channels=channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg[1])
|
||||
|
||||
def forward(self, x):
|
||||
out = self.global_avgpool(x)
|
||||
out = self.conv1(out)
|
||||
out = self.conv2(out)
|
||||
return x * out
|
||||
|
||||
|
||||
class CrossResolutionWeighting(nn.Module):
|
||||
"""Cross-resolution channel weighting module.
|
||||
|
||||
Args:
|
||||
channels (int): The channels of the module.
|
||||
ratio (int): channel reduction ratio.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: None.
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: (dict(type='ReLU'), dict(type='Sigmoid')).
|
||||
The last ConvModule uses Sigmoid by default.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
channels,
|
||||
ratio=16,
|
||||
conv_cfg=None,
|
||||
norm_cfg=None,
|
||||
act_cfg=(dict(type='ReLU'), dict(type='Sigmoid'))):
|
||||
super().__init__()
|
||||
if isinstance(act_cfg, dict):
|
||||
act_cfg = (act_cfg, act_cfg)
|
||||
assert len(act_cfg) == 2
|
||||
assert mmcv.is_tuple_of(act_cfg, dict)
|
||||
self.channels = channels
|
||||
total_channel = sum(channels)
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=total_channel,
|
||||
out_channels=int(total_channel / ratio),
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg[0])
|
||||
self.conv2 = ConvModule(
|
||||
in_channels=int(total_channel / ratio),
|
||||
out_channels=total_channel,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg[1])
|
||||
|
||||
def forward(self, x):
|
||||
mini_size = x[-1].size()[-2:]
|
||||
out = [F.adaptive_avg_pool2d(s, mini_size) for s in x[:-1]] + [x[-1]]
|
||||
out = torch.cat(out, dim=1)
|
||||
out = self.conv1(out)
|
||||
out = self.conv2(out)
|
||||
out = torch.split(out, self.channels, dim=1)
|
||||
out = [
|
||||
s * F.interpolate(a, size=s.size()[-2:], mode='nearest')
|
||||
for s, a in zip(x, out)
|
||||
]
|
||||
return out
|
||||
|
||||
|
||||
class ConditionalChannelWeighting(nn.Module):
|
||||
"""Conditional channel weighting block.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the block.
|
||||
stride (int): Stride of the 3x3 convolution layer.
|
||||
reduce_ratio (int): channel reduction ratio.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
stride,
|
||||
reduce_ratio,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
with_cp=False):
|
||||
super().__init__()
|
||||
self.with_cp = with_cp
|
||||
self.stride = stride
|
||||
assert stride in [1, 2]
|
||||
|
||||
branch_channels = [channel // 2 for channel in in_channels]
|
||||
|
||||
self.cross_resolution_weighting = CrossResolutionWeighting(
|
||||
branch_channels,
|
||||
ratio=reduce_ratio,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg)
|
||||
|
||||
self.depthwise_convs = nn.ModuleList([
|
||||
ConvModule(
|
||||
channel,
|
||||
channel,
|
||||
kernel_size=3,
|
||||
stride=self.stride,
|
||||
padding=1,
|
||||
groups=channel,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None) for channel in branch_channels
|
||||
])
|
||||
|
||||
self.spatial_weighting = nn.ModuleList([
|
||||
SpatialWeighting(channels=channel, ratio=4)
|
||||
for channel in branch_channels
|
||||
])
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
x = [s.chunk(2, dim=1) for s in x]
|
||||
x1 = [s[0] for s in x]
|
||||
x2 = [s[1] for s in x]
|
||||
|
||||
x2 = self.cross_resolution_weighting(x2)
|
||||
x2 = [dw(s) for s, dw in zip(x2, self.depthwise_convs)]
|
||||
x2 = [sw(s) for s, sw in zip(x2, self.spatial_weighting)]
|
||||
|
||||
out = [torch.cat([s1, s2], dim=1) for s1, s2 in zip(x1, x2)]
|
||||
out = [channel_shuffle(s, 2) for s in out]
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class Stem(nn.Module):
|
||||
"""Stem network block.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the block.
|
||||
stem_channels (int): Output channels of the stem layer.
|
||||
out_channels (int): The output channels of the block.
|
||||
expand_ratio (int): adjusts number of channels of the hidden layer
|
||||
in InvertedResidual by this amount.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
stem_channels,
|
||||
out_channels,
|
||||
expand_ratio,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
with_cp=False):
|
||||
super().__init__()
|
||||
self.in_channels = in_channels
|
||||
self.out_channels = out_channels
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.with_cp = with_cp
|
||||
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=in_channels,
|
||||
out_channels=stem_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=dict(type='ReLU'))
|
||||
|
||||
mid_channels = int(round(stem_channels * expand_ratio))
|
||||
branch_channels = stem_channels // 2
|
||||
if stem_channels == self.out_channels:
|
||||
inc_channels = self.out_channels - branch_channels
|
||||
else:
|
||||
inc_channels = self.out_channels - stem_channels
|
||||
|
||||
self.branch1 = nn.Sequential(
|
||||
ConvModule(
|
||||
branch_channels,
|
||||
branch_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
groups=branch_channels,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None),
|
||||
ConvModule(
|
||||
branch_channels,
|
||||
inc_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=dict(type='ReLU')),
|
||||
)
|
||||
|
||||
self.expand_conv = ConvModule(
|
||||
branch_channels,
|
||||
mid_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=dict(type='ReLU'))
|
||||
self.depthwise_conv = ConvModule(
|
||||
mid_channels,
|
||||
mid_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
groups=mid_channels,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None)
|
||||
self.linear_conv = ConvModule(
|
||||
mid_channels,
|
||||
branch_channels
|
||||
if stem_channels == self.out_channels else stem_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=dict(type='ReLU'))
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
x = self.conv1(x)
|
||||
x1, x2 = x.chunk(2, dim=1)
|
||||
|
||||
x2 = self.expand_conv(x2)
|
||||
x2 = self.depthwise_conv(x2)
|
||||
x2 = self.linear_conv(x2)
|
||||
|
||||
out = torch.cat((self.branch1(x1), x2), dim=1)
|
||||
|
||||
out = channel_shuffle(out, 2)
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class IterativeHead(nn.Module):
|
||||
"""Extra iterative head for feature learning.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the block.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
"""
|
||||
|
||||
def __init__(self, in_channels, norm_cfg=dict(type='BN')):
|
||||
super().__init__()
|
||||
projects = []
|
||||
num_branchs = len(in_channels)
|
||||
self.in_channels = in_channels[::-1]
|
||||
|
||||
for i in range(num_branchs):
|
||||
if i != num_branchs - 1:
|
||||
projects.append(
|
||||
DepthwiseSeparableConvModule(
|
||||
in_channels=self.in_channels[i],
|
||||
out_channels=self.in_channels[i + 1],
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=dict(type='ReLU'),
|
||||
dw_act_cfg=None,
|
||||
pw_act_cfg=dict(type='ReLU')))
|
||||
else:
|
||||
projects.append(
|
||||
DepthwiseSeparableConvModule(
|
||||
in_channels=self.in_channels[i],
|
||||
out_channels=self.in_channels[i],
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=dict(type='ReLU'),
|
||||
dw_act_cfg=None,
|
||||
pw_act_cfg=dict(type='ReLU')))
|
||||
self.projects = nn.ModuleList(projects)
|
||||
|
||||
def forward(self, x):
|
||||
x = x[::-1]
|
||||
|
||||
y = []
|
||||
last_x = None
|
||||
for i, s in enumerate(x):
|
||||
if last_x is not None:
|
||||
last_x = F.interpolate(
|
||||
last_x,
|
||||
size=s.size()[-2:],
|
||||
mode='bilinear',
|
||||
align_corners=True)
|
||||
s = s + last_x
|
||||
s = self.projects[i](s)
|
||||
y.append(s)
|
||||
last_x = s
|
||||
|
||||
return y[::-1]
|
||||
|
||||
|
||||
class ShuffleUnit(nn.Module):
|
||||
"""InvertedResidual block for ShuffleNetV2 backbone.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the block.
|
||||
out_channels (int): The output channels of the block.
|
||||
stride (int): Stride of the 3x3 convolution layer. Default: 1
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU').
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
stride=1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU'),
|
||||
with_cp=False):
|
||||
super().__init__()
|
||||
self.stride = stride
|
||||
self.with_cp = with_cp
|
||||
|
||||
branch_features = out_channels // 2
|
||||
if self.stride == 1:
|
||||
assert in_channels == branch_features * 2, (
|
||||
f'in_channels ({in_channels}) should equal to '
|
||||
f'branch_features * 2 ({branch_features * 2}) '
|
||||
'when stride is 1')
|
||||
|
||||
if in_channels != branch_features * 2:
|
||||
assert self.stride != 1, (
|
||||
f'stride ({self.stride}) should not equal 1 when '
|
||||
f'in_channels != branch_features * 2')
|
||||
|
||||
if self.stride > 1:
|
||||
self.branch1 = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=3,
|
||||
stride=self.stride,
|
||||
padding=1,
|
||||
groups=in_channels,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None),
|
||||
ConvModule(
|
||||
in_channels,
|
||||
branch_features,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg),
|
||||
)
|
||||
|
||||
self.branch2 = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels if (self.stride > 1) else branch_features,
|
||||
branch_features,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg),
|
||||
ConvModule(
|
||||
branch_features,
|
||||
branch_features,
|
||||
kernel_size=3,
|
||||
stride=self.stride,
|
||||
padding=1,
|
||||
groups=branch_features,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None),
|
||||
ConvModule(
|
||||
branch_features,
|
||||
branch_features,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg))
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
if self.stride > 1:
|
||||
out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
|
||||
else:
|
||||
x1, x2 = x.chunk(2, dim=1)
|
||||
out = torch.cat((x1, self.branch2(x2)), dim=1)
|
||||
|
||||
out = channel_shuffle(out, 2)
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class LiteHRModule(nn.Module):
|
||||
"""High-Resolution Module for LiteHRNet.
|
||||
|
||||
It contains conditional channel weighting blocks and
|
||||
shuffle blocks.
|
||||
|
||||
|
||||
Args:
|
||||
num_branches (int): Number of branches in the module.
|
||||
num_blocks (int): Number of blocks in the module.
|
||||
in_channels (list(int)): Number of input image channels.
|
||||
reduce_ratio (int): Channel reduction ratio.
|
||||
module_type (str): 'LITE' or 'NAIVE'
|
||||
multiscale_output (bool): Whether to output multi-scale features.
|
||||
with_fuse (bool): Whether to use fuse layers.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
num_branches,
|
||||
num_blocks,
|
||||
in_channels,
|
||||
reduce_ratio,
|
||||
module_type,
|
||||
multiscale_output=False,
|
||||
with_fuse=True,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
with_cp=False,
|
||||
):
|
||||
super().__init__()
|
||||
self._check_branches(num_branches, in_channels)
|
||||
|
||||
self.in_channels = in_channels
|
||||
self.num_branches = num_branches
|
||||
|
||||
self.module_type = module_type
|
||||
self.multiscale_output = multiscale_output
|
||||
self.with_fuse = with_fuse
|
||||
self.norm_cfg = norm_cfg
|
||||
self.conv_cfg = conv_cfg
|
||||
self.with_cp = with_cp
|
||||
|
||||
if self.module_type.upper() == 'LITE':
|
||||
self.layers = self._make_weighting_blocks(num_blocks, reduce_ratio)
|
||||
elif self.module_type.upper() == 'NAIVE':
|
||||
self.layers = self._make_naive_branches(num_branches, num_blocks)
|
||||
else:
|
||||
raise ValueError("module_type should be either 'LITE' or 'NAIVE'.")
|
||||
if self.with_fuse:
|
||||
self.fuse_layers = self._make_fuse_layers()
|
||||
self.relu = nn.ReLU()
|
||||
|
||||
def _check_branches(self, num_branches, in_channels):
|
||||
"""Check input to avoid ValueError."""
|
||||
if num_branches != len(in_channels):
|
||||
error_msg = f'NUM_BRANCHES({num_branches}) ' \
|
||||
f'!= NUM_INCHANNELS({len(in_channels)})'
|
||||
raise ValueError(error_msg)
|
||||
|
||||
def _make_weighting_blocks(self, num_blocks, reduce_ratio, stride=1):
|
||||
"""Make channel weighting blocks."""
|
||||
layers = []
|
||||
for i in range(num_blocks):
|
||||
layers.append(
|
||||
ConditionalChannelWeighting(
|
||||
self.in_channels,
|
||||
stride=stride,
|
||||
reduce_ratio=reduce_ratio,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
with_cp=self.with_cp))
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _make_one_branch(self, branch_index, num_blocks, stride=1):
|
||||
"""Make one branch."""
|
||||
layers = []
|
||||
layers.append(
|
||||
ShuffleUnit(
|
||||
self.in_channels[branch_index],
|
||||
self.in_channels[branch_index],
|
||||
stride=stride,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=dict(type='ReLU'),
|
||||
with_cp=self.with_cp))
|
||||
for i in range(1, num_blocks):
|
||||
layers.append(
|
||||
ShuffleUnit(
|
||||
self.in_channels[branch_index],
|
||||
self.in_channels[branch_index],
|
||||
stride=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=dict(type='ReLU'),
|
||||
with_cp=self.with_cp))
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _make_naive_branches(self, num_branches, num_blocks):
|
||||
"""Make branches."""
|
||||
branches = []
|
||||
|
||||
for i in range(num_branches):
|
||||
branches.append(self._make_one_branch(i, num_blocks))
|
||||
|
||||
return nn.ModuleList(branches)
|
||||
|
||||
def _make_fuse_layers(self):
|
||||
"""Make fuse layer."""
|
||||
if self.num_branches == 1:
|
||||
return None
|
||||
|
||||
num_branches = self.num_branches
|
||||
in_channels = self.in_channels
|
||||
fuse_layers = []
|
||||
num_out_branches = num_branches if self.multiscale_output else 1
|
||||
for i in range(num_out_branches):
|
||||
fuse_layer = []
|
||||
for j in range(num_branches):
|
||||
if j > i:
|
||||
fuse_layer.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[i],
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg, in_channels[i])[1],
|
||||
nn.Upsample(
|
||||
scale_factor=2**(j - i), mode='nearest')))
|
||||
elif j == i:
|
||||
fuse_layer.append(None)
|
||||
else:
|
||||
conv_downsamples = []
|
||||
for k in range(i - j):
|
||||
if k == i - j - 1:
|
||||
conv_downsamples.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[j],
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
groups=in_channels[j],
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
in_channels[j])[1],
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[i],
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
in_channels[i])[1]))
|
||||
else:
|
||||
conv_downsamples.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[j],
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
groups=in_channels[j],
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
in_channels[j])[1],
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels[j],
|
||||
in_channels[j],
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
in_channels[j])[1],
|
||||
nn.ReLU(inplace=True)))
|
||||
fuse_layer.append(nn.Sequential(*conv_downsamples))
|
||||
fuse_layers.append(nn.ModuleList(fuse_layer))
|
||||
|
||||
return nn.ModuleList(fuse_layers)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
if self.num_branches == 1:
|
||||
return [self.layers[0](x[0])]
|
||||
|
||||
if self.module_type.upper() == 'LITE':
|
||||
out = self.layers(x)
|
||||
elif self.module_type.upper() == 'NAIVE':
|
||||
for i in range(self.num_branches):
|
||||
x[i] = self.layers[i](x[i])
|
||||
out = x
|
||||
|
||||
if self.with_fuse:
|
||||
out_fuse = []
|
||||
for i in range(len(self.fuse_layers)):
|
||||
# `y = 0` will lead to decreased accuracy (0.5~1 mAP)
|
||||
y = out[0] if i == 0 else self.fuse_layers[i][0](out[0])
|
||||
for j in range(self.num_branches):
|
||||
if i == j:
|
||||
y += out[j]
|
||||
else:
|
||||
y += self.fuse_layers[i][j](out[j])
|
||||
out_fuse.append(self.relu(y))
|
||||
out = out_fuse
|
||||
if not self.multiscale_output:
|
||||
out = [out[0]]
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class LiteHRNet(nn.Module):
|
||||
"""Lite-HRNet backbone.
|
||||
|
||||
`Lite-HRNet: A Lightweight High-Resolution Network
|
||||
<https://arxiv.org/abs/2104.06403>`_.
|
||||
|
||||
Code adapted from 'https://github.com/HRNet/Lite-HRNet'.
|
||||
|
||||
Args:
|
||||
extra (dict): detailed configuration for each stage of HRNet.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import LiteHRNet
|
||||
>>> import torch
|
||||
>>> 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=False)
|
||||
>>> self = LiteHRNet(extra, in_channels=1)
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 1, 32, 32)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 40, 8, 8)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
extra,
|
||||
in_channels=3,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
norm_eval=False,
|
||||
with_cp=False):
|
||||
super().__init__()
|
||||
self.extra = extra
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.norm_eval = norm_eval
|
||||
self.with_cp = with_cp
|
||||
|
||||
self.stem = Stem(
|
||||
in_channels,
|
||||
stem_channels=self.extra['stem']['stem_channels'],
|
||||
out_channels=self.extra['stem']['out_channels'],
|
||||
expand_ratio=self.extra['stem']['expand_ratio'],
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg)
|
||||
|
||||
self.num_stages = self.extra['num_stages']
|
||||
self.stages_spec = self.extra['stages_spec']
|
||||
|
||||
num_channels_last = [
|
||||
self.stem.out_channels,
|
||||
]
|
||||
for i in range(self.num_stages):
|
||||
num_channels = self.stages_spec['num_channels'][i]
|
||||
num_channels = [num_channels[i] for i in range(len(num_channels))]
|
||||
setattr(
|
||||
self, f'transition{i}',
|
||||
self._make_transition_layer(num_channels_last, num_channels))
|
||||
|
||||
stage, num_channels_last = self._make_stage(
|
||||
self.stages_spec, i, num_channels, multiscale_output=True)
|
||||
setattr(self, f'stage{i}', stage)
|
||||
|
||||
self.with_head = self.extra['with_head']
|
||||
if self.with_head:
|
||||
self.head_layer = IterativeHead(
|
||||
in_channels=num_channels_last,
|
||||
norm_cfg=self.norm_cfg,
|
||||
)
|
||||
|
||||
def _make_transition_layer(self, num_channels_pre_layer,
|
||||
num_channels_cur_layer):
|
||||
"""Make transition layer."""
|
||||
num_branches_cur = len(num_channels_cur_layer)
|
||||
num_branches_pre = len(num_channels_pre_layer)
|
||||
|
||||
transition_layers = []
|
||||
for i in range(num_branches_cur):
|
||||
if i < num_branches_pre:
|
||||
if num_channels_cur_layer[i] != num_channels_pre_layer[i]:
|
||||
transition_layers.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
num_channels_pre_layer[i],
|
||||
num_channels_pre_layer[i],
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
groups=num_channels_pre_layer[i],
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
num_channels_pre_layer[i])[1],
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
num_channels_pre_layer[i],
|
||||
num_channels_cur_layer[i],
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg,
|
||||
num_channels_cur_layer[i])[1],
|
||||
nn.ReLU()))
|
||||
else:
|
||||
transition_layers.append(None)
|
||||
else:
|
||||
conv_downsamples = []
|
||||
for j in range(i + 1 - num_branches_pre):
|
||||
in_channels = num_channels_pre_layer[-1]
|
||||
out_channels = num_channels_cur_layer[i] \
|
||||
if j == i - num_branches_pre else in_channels
|
||||
conv_downsamples.append(
|
||||
nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
groups=in_channels,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg, in_channels)[1],
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg, out_channels)[1],
|
||||
nn.ReLU()))
|
||||
transition_layers.append(nn.Sequential(*conv_downsamples))
|
||||
|
||||
return nn.ModuleList(transition_layers)
|
||||
|
||||
def _make_stage(self,
|
||||
stages_spec,
|
||||
stage_index,
|
||||
in_channels,
|
||||
multiscale_output=True):
|
||||
num_modules = stages_spec['num_modules'][stage_index]
|
||||
num_branches = stages_spec['num_branches'][stage_index]
|
||||
num_blocks = stages_spec['num_blocks'][stage_index]
|
||||
reduce_ratio = stages_spec['reduce_ratios'][stage_index]
|
||||
with_fuse = stages_spec['with_fuse'][stage_index]
|
||||
module_type = stages_spec['module_type'][stage_index]
|
||||
|
||||
modules = []
|
||||
for i in range(num_modules):
|
||||
# multi_scale_output is only used last module
|
||||
if not multiscale_output and i == num_modules - 1:
|
||||
reset_multiscale_output = False
|
||||
else:
|
||||
reset_multiscale_output = True
|
||||
|
||||
modules.append(
|
||||
LiteHRModule(
|
||||
num_branches,
|
||||
num_blocks,
|
||||
in_channels,
|
||||
reduce_ratio,
|
||||
module_type,
|
||||
multiscale_output=reset_multiscale_output,
|
||||
with_fuse=with_fuse,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
with_cp=self.with_cp))
|
||||
in_channels = modules[-1].in_channels
|
||||
|
||||
return nn.Sequential(*modules), in_channels
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
if isinstance(pretrained, str):
|
||||
logger = get_root_logger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
normal_init(m, std=0.001)
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, 1)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
x = self.stem(x)
|
||||
|
||||
y_list = [x]
|
||||
for i in range(self.num_stages):
|
||||
x_list = []
|
||||
transition = getattr(self, f'transition{i}')
|
||||
for j in range(self.stages_spec['num_branches'][i]):
|
||||
if transition[j]:
|
||||
if j >= len(y_list):
|
||||
x_list.append(transition[j](y_list[-1]))
|
||||
else:
|
||||
x_list.append(transition[j](y_list[j]))
|
||||
else:
|
||||
x_list.append(y_list[j])
|
||||
y_list = getattr(self, f'stage{i}')(x_list)
|
||||
|
||||
x = y_list
|
||||
if self.with_head:
|
||||
x = self.head_layer(x)
|
||||
|
||||
return [x[0]]
|
||||
|
||||
def train(self, mode=True):
|
||||
"""Convert the model into training mode."""
|
||||
super().train(mode)
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import torch.nn as nn
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import ConvModule, constant_init, kaiming_init
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .utils import load_checkpoint, make_divisible
|
||||
|
||||
|
||||
class InvertedResidual(nn.Module):
|
||||
"""InvertedResidual block for MobileNetV2.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the InvertedResidual block.
|
||||
out_channels (int): The output channels of the InvertedResidual block.
|
||||
stride (int): Stride of the middle (first) 3x3 convolution.
|
||||
expand_ratio (int): adjusts number of channels of the hidden layer
|
||||
in InvertedResidual by this amount.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU6').
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
stride,
|
||||
expand_ratio,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU6'),
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
act_cfg = copy.deepcopy(act_cfg)
|
||||
super().__init__()
|
||||
self.stride = stride
|
||||
assert stride in [1, 2], f'stride must in [1, 2]. ' \
|
||||
f'But received {stride}.'
|
||||
self.with_cp = with_cp
|
||||
self.use_res_connect = self.stride == 1 and in_channels == out_channels
|
||||
hidden_dim = int(round(in_channels * expand_ratio))
|
||||
|
||||
layers = []
|
||||
if expand_ratio != 1:
|
||||
layers.append(
|
||||
ConvModule(
|
||||
in_channels=in_channels,
|
||||
out_channels=hidden_dim,
|
||||
kernel_size=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg))
|
||||
layers.extend([
|
||||
ConvModule(
|
||||
in_channels=hidden_dim,
|
||||
out_channels=hidden_dim,
|
||||
kernel_size=3,
|
||||
stride=stride,
|
||||
padding=1,
|
||||
groups=hidden_dim,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg),
|
||||
ConvModule(
|
||||
in_channels=hidden_dim,
|
||||
out_channels=out_channels,
|
||||
kernel_size=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None)
|
||||
])
|
||||
self.conv = nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
if self.use_res_connect:
|
||||
return x + self.conv(x)
|
||||
return self.conv(x)
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class MobileNetV2(BaseBackbone):
|
||||
"""MobileNetV2 backbone.
|
||||
|
||||
Args:
|
||||
widen_factor (float): Width multiplier, multiply number of
|
||||
channels in each layer by this amount. Default: 1.0.
|
||||
out_indices (None or Sequence[int]): Output from which stages.
|
||||
Default: (7, ).
|
||||
frozen_stages (int): Stages to be frozen (all param fixed).
|
||||
Default: -1, which means not freezing any parameters.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU6').
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
# Parameters to build layers. 4 parameters are needed to construct a
|
||||
# layer, from left to right: expand_ratio, channel, num_blocks, stride.
|
||||
arch_settings = [[1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2],
|
||||
[6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2],
|
||||
[6, 320, 1, 1]]
|
||||
|
||||
def __init__(self,
|
||||
widen_factor=1.,
|
||||
out_indices=(7, ),
|
||||
frozen_stages=-1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU6'),
|
||||
norm_eval=False,
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
act_cfg = copy.deepcopy(act_cfg)
|
||||
super().__init__()
|
||||
self.widen_factor = widen_factor
|
||||
self.out_indices = out_indices
|
||||
for index in out_indices:
|
||||
if index not in range(0, 8):
|
||||
raise ValueError('the item in out_indices must in '
|
||||
f'range(0, 8). But received {index}')
|
||||
|
||||
if frozen_stages not in range(-1, 8):
|
||||
raise ValueError('frozen_stages must be in range(-1, 8). '
|
||||
f'But received {frozen_stages}')
|
||||
self.out_indices = out_indices
|
||||
self.frozen_stages = frozen_stages
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.act_cfg = act_cfg
|
||||
self.norm_eval = norm_eval
|
||||
self.with_cp = with_cp
|
||||
|
||||
self.in_channels = make_divisible(32 * widen_factor, 8)
|
||||
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=3,
|
||||
out_channels=self.in_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=self.act_cfg)
|
||||
|
||||
self.layers = []
|
||||
|
||||
for i, layer_cfg in enumerate(self.arch_settings):
|
||||
expand_ratio, channel, num_blocks, stride = layer_cfg
|
||||
out_channels = make_divisible(channel * widen_factor, 8)
|
||||
inverted_res_layer = self.make_layer(
|
||||
out_channels=out_channels,
|
||||
num_blocks=num_blocks,
|
||||
stride=stride,
|
||||
expand_ratio=expand_ratio)
|
||||
layer_name = f'layer{i + 1}'
|
||||
self.add_module(layer_name, inverted_res_layer)
|
||||
self.layers.append(layer_name)
|
||||
|
||||
if widen_factor > 1.0:
|
||||
self.out_channel = int(1280 * widen_factor)
|
||||
else:
|
||||
self.out_channel = 1280
|
||||
|
||||
layer = ConvModule(
|
||||
in_channels=self.in_channels,
|
||||
out_channels=self.out_channel,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=self.act_cfg)
|
||||
self.add_module('conv2', layer)
|
||||
self.layers.append('conv2')
|
||||
|
||||
def make_layer(self, out_channels, num_blocks, stride, expand_ratio):
|
||||
"""Stack InvertedResidual blocks to build a layer for MobileNetV2.
|
||||
|
||||
Args:
|
||||
out_channels (int): out_channels of block.
|
||||
num_blocks (int): number of blocks.
|
||||
stride (int): stride of the first block. Default: 1
|
||||
expand_ratio (int): Expand the number of channels of the
|
||||
hidden layer in InvertedResidual by this ratio. Default: 6.
|
||||
"""
|
||||
layers = []
|
||||
for i in range(num_blocks):
|
||||
if i >= 1:
|
||||
stride = 1
|
||||
layers.append(
|
||||
InvertedResidual(
|
||||
self.in_channels,
|
||||
out_channels,
|
||||
stride,
|
||||
expand_ratio=expand_ratio,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=self.act_cfg,
|
||||
with_cp=self.with_cp))
|
||||
self.in_channels = out_channels
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
if isinstance(pretrained, str):
|
||||
logger = logging.getLogger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, 1)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
|
||||
outs = []
|
||||
for i, layer_name in enumerate(self.layers):
|
||||
layer = getattr(self, layer_name)
|
||||
x = layer(x)
|
||||
if i in self.out_indices:
|
||||
outs.append(x)
|
||||
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
return tuple(outs)
|
||||
|
||||
def _freeze_stages(self):
|
||||
if self.frozen_stages >= 0:
|
||||
for param in self.conv1.parameters():
|
||||
param.requires_grad = False
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
layer = getattr(self, f'layer{i}')
|
||||
layer.eval()
|
||||
for param in layer.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def train(self, mode=True):
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule, constant_init, kaiming_init
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .utils import InvertedResidual, load_checkpoint
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class MobileNetV3(BaseBackbone):
|
||||
"""MobileNetV3 backbone.
|
||||
|
||||
Args:
|
||||
arch (str): Architecture of mobilnetv3, from {small, big}.
|
||||
Default: small.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
out_indices (None or Sequence[int]): Output from which stages.
|
||||
Default: (-1, ), which means output tensors from final stage.
|
||||
frozen_stages (int): Stages to be frozen (all param fixed).
|
||||
Default: -1, which means not freezing any parameters.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save
|
||||
some memory while slowing down the training speed.
|
||||
Default: False.
|
||||
"""
|
||||
# Parameters to build each block:
|
||||
# [kernel size, mid channels, out channels, with_se, act type, stride]
|
||||
arch_settings = {
|
||||
'small': [[3, 16, 16, True, 'ReLU', 2],
|
||||
[3, 72, 24, False, 'ReLU', 2],
|
||||
[3, 88, 24, False, 'ReLU', 1],
|
||||
[5, 96, 40, True, 'HSwish', 2],
|
||||
[5, 240, 40, True, 'HSwish', 1],
|
||||
[5, 240, 40, True, 'HSwish', 1],
|
||||
[5, 120, 48, True, 'HSwish', 1],
|
||||
[5, 144, 48, True, 'HSwish', 1],
|
||||
[5, 288, 96, True, 'HSwish', 2],
|
||||
[5, 576, 96, True, 'HSwish', 1],
|
||||
[5, 576, 96, True, 'HSwish', 1]],
|
||||
'big': [[3, 16, 16, False, 'ReLU', 1],
|
||||
[3, 64, 24, False, 'ReLU', 2],
|
||||
[3, 72, 24, False, 'ReLU', 1],
|
||||
[5, 72, 40, True, 'ReLU', 2],
|
||||
[5, 120, 40, True, 'ReLU', 1],
|
||||
[5, 120, 40, True, 'ReLU', 1],
|
||||
[3, 240, 80, False, 'HSwish', 2],
|
||||
[3, 200, 80, False, 'HSwish', 1],
|
||||
[3, 184, 80, False, 'HSwish', 1],
|
||||
[3, 184, 80, False, 'HSwish', 1],
|
||||
[3, 480, 112, True, 'HSwish', 1],
|
||||
[3, 672, 112, True, 'HSwish', 1],
|
||||
[5, 672, 160, True, 'HSwish', 1],
|
||||
[5, 672, 160, True, 'HSwish', 2],
|
||||
[5, 960, 160, True, 'HSwish', 1]]
|
||||
} # yapf: disable
|
||||
|
||||
def __init__(self,
|
||||
arch='small',
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
out_indices=(-1, ),
|
||||
frozen_stages=-1,
|
||||
norm_eval=False,
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
assert arch in self.arch_settings
|
||||
for index in out_indices:
|
||||
if index not in range(-len(self.arch_settings[arch]),
|
||||
len(self.arch_settings[arch])):
|
||||
raise ValueError('the item in out_indices must in '
|
||||
f'range(0, {len(self.arch_settings[arch])}). '
|
||||
f'But received {index}')
|
||||
|
||||
if frozen_stages not in range(-1, len(self.arch_settings[arch])):
|
||||
raise ValueError('frozen_stages must be in range(-1, '
|
||||
f'{len(self.arch_settings[arch])}). '
|
||||
f'But received {frozen_stages}')
|
||||
self.arch = arch
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.out_indices = out_indices
|
||||
self.frozen_stages = frozen_stages
|
||||
self.norm_eval = norm_eval
|
||||
self.with_cp = with_cp
|
||||
|
||||
self.in_channels = 16
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=3,
|
||||
out_channels=self.in_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=dict(type='HSwish'))
|
||||
|
||||
self.layers = self._make_layer()
|
||||
self.feat_dim = self.arch_settings[arch][-1][2]
|
||||
|
||||
def _make_layer(self):
|
||||
layers = []
|
||||
layer_setting = self.arch_settings[self.arch]
|
||||
for i, params in enumerate(layer_setting):
|
||||
(kernel_size, mid_channels, out_channels, with_se, act,
|
||||
stride) = params
|
||||
if with_se:
|
||||
se_cfg = dict(
|
||||
channels=mid_channels,
|
||||
ratio=4,
|
||||
act_cfg=(dict(type='ReLU'), dict(type='HSigmoid')))
|
||||
else:
|
||||
se_cfg = None
|
||||
|
||||
layer = InvertedResidual(
|
||||
in_channels=self.in_channels,
|
||||
out_channels=out_channels,
|
||||
mid_channels=mid_channels,
|
||||
kernel_size=kernel_size,
|
||||
stride=stride,
|
||||
se_cfg=se_cfg,
|
||||
with_expand_conv=True,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=dict(type=act),
|
||||
with_cp=self.with_cp)
|
||||
self.in_channels = out_channels
|
||||
layer_name = f'layer{i + 1}'
|
||||
self.add_module(layer_name, layer)
|
||||
layers.append(layer_name)
|
||||
return layers
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
if isinstance(pretrained, str):
|
||||
logger = logging.getLogger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
constant_init(m, 1)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
|
||||
outs = []
|
||||
for i, layer_name in enumerate(self.layers):
|
||||
layer = getattr(self, layer_name)
|
||||
x = layer(x)
|
||||
if i in self.out_indices or \
|
||||
i - len(self.layers) in self.out_indices:
|
||||
outs.append(x)
|
||||
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
return tuple(outs)
|
||||
|
||||
def _freeze_stages(self):
|
||||
if self.frozen_stages >= 0:
|
||||
for param in self.conv1.parameters():
|
||||
param.requires_grad = False
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
layer = getattr(self, f'layer{i}')
|
||||
layer.eval()
|
||||
for param in layer.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def train(self, mode=True):
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
@@ -0,0 +1,513 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy as cp
|
||||
from collections import OrderedDict
|
||||
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from mmcv.cnn import (ConvModule, MaxPool2d, constant_init, kaiming_init,
|
||||
normal_init)
|
||||
from mmcv.runner.checkpoint import load_state_dict
|
||||
|
||||
from mmpose.utils import get_root_logger
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .resnet import Bottleneck as _Bottleneck
|
||||
from .utils.utils import get_state_dict
|
||||
|
||||
|
||||
class Bottleneck(_Bottleneck):
|
||||
expansion = 4
|
||||
"""Bottleneck block for MSPN.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
stride (int): stride of the block. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
"""
|
||||
|
||||
def __init__(self, in_channels, out_channels, **kwargs):
|
||||
super().__init__(in_channels, out_channels * 4, **kwargs)
|
||||
|
||||
|
||||
class DownsampleModule(nn.Module):
|
||||
"""Downsample module for MSPN.
|
||||
|
||||
Args:
|
||||
block (nn.Module): Downsample block.
|
||||
num_blocks (list): Number of blocks in each downsample unit.
|
||||
num_units (int): Numbers of downsample units. Default: 4
|
||||
has_skip (bool): Have skip connections from prior upsample
|
||||
module or not. Default:False
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
in_channels (int): Number of channels of the input feature to
|
||||
downsample module. Default: 64
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
block,
|
||||
num_blocks,
|
||||
num_units=4,
|
||||
has_skip=False,
|
||||
norm_cfg=dict(type='BN'),
|
||||
in_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.has_skip = has_skip
|
||||
self.in_channels = in_channels
|
||||
assert len(num_blocks) == num_units
|
||||
self.num_blocks = num_blocks
|
||||
self.num_units = num_units
|
||||
self.norm_cfg = norm_cfg
|
||||
self.layer1 = self._make_layer(block, in_channels, num_blocks[0])
|
||||
for i in range(1, num_units):
|
||||
module_name = f'layer{i + 1}'
|
||||
self.add_module(
|
||||
module_name,
|
||||
self._make_layer(
|
||||
block, in_channels * pow(2, i), num_blocks[i], stride=2))
|
||||
|
||||
def _make_layer(self, block, out_channels, blocks, stride=1):
|
||||
downsample = None
|
||||
if stride != 1 or self.in_channels != out_channels * block.expansion:
|
||||
downsample = ConvModule(
|
||||
self.in_channels,
|
||||
out_channels * block.expansion,
|
||||
kernel_size=1,
|
||||
stride=stride,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=None,
|
||||
inplace=True)
|
||||
|
||||
units = list()
|
||||
units.append(
|
||||
block(
|
||||
self.in_channels,
|
||||
out_channels,
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
norm_cfg=self.norm_cfg))
|
||||
self.in_channels = out_channels * block.expansion
|
||||
for _ in range(1, blocks):
|
||||
units.append(block(self.in_channels, out_channels))
|
||||
|
||||
return nn.Sequential(*units)
|
||||
|
||||
def forward(self, x, skip1, skip2):
|
||||
out = list()
|
||||
for i in range(self.num_units):
|
||||
module_name = f'layer{i + 1}'
|
||||
module_i = getattr(self, module_name)
|
||||
x = module_i(x)
|
||||
if self.has_skip:
|
||||
x = x + skip1[i] + skip2[i]
|
||||
out.append(x)
|
||||
out.reverse()
|
||||
|
||||
return tuple(out)
|
||||
|
||||
|
||||
class UpsampleUnit(nn.Module):
|
||||
"""Upsample unit for upsample module.
|
||||
|
||||
Args:
|
||||
ind (int): Indicates whether to interpolate (>0) and whether to
|
||||
generate feature map for the next hourglass-like module.
|
||||
num_units (int): Number of units that form a upsample module. Along
|
||||
with ind and gen_cross_conv, nm_units is used to decide whether
|
||||
to generate feature map for the next hourglass-like module.
|
||||
in_channels (int): Channel number of the skip-in feature maps from
|
||||
the corresponding downsample unit.
|
||||
unit_channels (int): Channel number in this unit. Default:256.
|
||||
gen_skip: (bool): Whether or not to generate skips for the posterior
|
||||
downsample module. Default:False
|
||||
gen_cross_conv (bool): Whether to generate feature map for the next
|
||||
hourglass-like module. Default:False
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
out_channels (int): Number of channels of feature output by upsample
|
||||
module. Must equal to in_channels of downsample module. Default:64
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
ind,
|
||||
num_units,
|
||||
in_channels,
|
||||
unit_channels=256,
|
||||
gen_skip=False,
|
||||
gen_cross_conv=False,
|
||||
norm_cfg=dict(type='BN'),
|
||||
out_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.num_units = num_units
|
||||
self.norm_cfg = norm_cfg
|
||||
self.in_skip = ConvModule(
|
||||
in_channels,
|
||||
unit_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=None,
|
||||
inplace=True)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
self.ind = ind
|
||||
if self.ind > 0:
|
||||
self.up_conv = ConvModule(
|
||||
unit_channels,
|
||||
unit_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=None,
|
||||
inplace=True)
|
||||
|
||||
self.gen_skip = gen_skip
|
||||
if self.gen_skip:
|
||||
self.out_skip1 = ConvModule(
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True)
|
||||
|
||||
self.out_skip2 = ConvModule(
|
||||
unit_channels,
|
||||
in_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True)
|
||||
|
||||
self.gen_cross_conv = gen_cross_conv
|
||||
if self.ind == num_units - 1 and self.gen_cross_conv:
|
||||
self.cross_conv = ConvModule(
|
||||
unit_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True)
|
||||
|
||||
def forward(self, x, up_x):
|
||||
out = self.in_skip(x)
|
||||
|
||||
if self.ind > 0:
|
||||
up_x = F.interpolate(
|
||||
up_x,
|
||||
size=(x.size(2), x.size(3)),
|
||||
mode='bilinear',
|
||||
align_corners=True)
|
||||
up_x = self.up_conv(up_x)
|
||||
out = out + up_x
|
||||
out = self.relu(out)
|
||||
|
||||
skip1 = None
|
||||
skip2 = None
|
||||
if self.gen_skip:
|
||||
skip1 = self.out_skip1(x)
|
||||
skip2 = self.out_skip2(out)
|
||||
|
||||
cross_conv = None
|
||||
if self.ind == self.num_units - 1 and self.gen_cross_conv:
|
||||
cross_conv = self.cross_conv(out)
|
||||
|
||||
return out, skip1, skip2, cross_conv
|
||||
|
||||
|
||||
class UpsampleModule(nn.Module):
|
||||
"""Upsample module for MSPN.
|
||||
|
||||
Args:
|
||||
unit_channels (int): Channel number in the upsample units.
|
||||
Default:256.
|
||||
num_units (int): Numbers of upsample units. Default: 4
|
||||
gen_skip (bool): Whether to generate skip for posterior downsample
|
||||
module or not. Default:False
|
||||
gen_cross_conv (bool): Whether to generate feature map for the next
|
||||
hourglass-like module. Default:False
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
out_channels (int): Number of channels of feature output by upsample
|
||||
module. Must equal to in_channels of downsample module. Default:64
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
unit_channels=256,
|
||||
num_units=4,
|
||||
gen_skip=False,
|
||||
gen_cross_conv=False,
|
||||
norm_cfg=dict(type='BN'),
|
||||
out_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.in_channels = list()
|
||||
for i in range(num_units):
|
||||
self.in_channels.append(Bottleneck.expansion * out_channels *
|
||||
pow(2, i))
|
||||
self.in_channels.reverse()
|
||||
self.num_units = num_units
|
||||
self.gen_skip = gen_skip
|
||||
self.gen_cross_conv = gen_cross_conv
|
||||
self.norm_cfg = norm_cfg
|
||||
for i in range(num_units):
|
||||
module_name = f'up{i + 1}'
|
||||
self.add_module(
|
||||
module_name,
|
||||
UpsampleUnit(
|
||||
i,
|
||||
self.num_units,
|
||||
self.in_channels[i],
|
||||
unit_channels,
|
||||
self.gen_skip,
|
||||
self.gen_cross_conv,
|
||||
norm_cfg=self.norm_cfg,
|
||||
out_channels=64))
|
||||
|
||||
def forward(self, x):
|
||||
out = list()
|
||||
skip1 = list()
|
||||
skip2 = list()
|
||||
cross_conv = None
|
||||
for i in range(self.num_units):
|
||||
module_i = getattr(self, f'up{i + 1}')
|
||||
if i == 0:
|
||||
outi, skip1_i, skip2_i, _ = module_i(x[i], None)
|
||||
elif i == self.num_units - 1:
|
||||
outi, skip1_i, skip2_i, cross_conv = module_i(x[i], out[i - 1])
|
||||
else:
|
||||
outi, skip1_i, skip2_i, _ = module_i(x[i], out[i - 1])
|
||||
out.append(outi)
|
||||
skip1.append(skip1_i)
|
||||
skip2.append(skip2_i)
|
||||
skip1.reverse()
|
||||
skip2.reverse()
|
||||
|
||||
return out, skip1, skip2, cross_conv
|
||||
|
||||
|
||||
class SingleStageNetwork(nn.Module):
|
||||
"""Single_stage Network.
|
||||
|
||||
Args:
|
||||
unit_channels (int): Channel number in the upsample units. Default:256.
|
||||
num_units (int): Numbers of downsample/upsample units. Default: 4
|
||||
gen_skip (bool): Whether to generate skip for posterior downsample
|
||||
module or not. Default:False
|
||||
gen_cross_conv (bool): Whether to generate feature map for the next
|
||||
hourglass-like module. Default:False
|
||||
has_skip (bool): Have skip connections from prior upsample
|
||||
module or not. Default:False
|
||||
num_blocks (list): Number of blocks in each downsample unit.
|
||||
Default: [2, 2, 2, 2] Note: Make sure num_units==len(num_blocks)
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
in_channels (int): Number of channels of the feature from ResNetTop.
|
||||
Default: 64.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
has_skip=False,
|
||||
gen_skip=False,
|
||||
gen_cross_conv=False,
|
||||
unit_channels=256,
|
||||
num_units=4,
|
||||
num_blocks=[2, 2, 2, 2],
|
||||
norm_cfg=dict(type='BN'),
|
||||
in_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
num_blocks = cp.deepcopy(num_blocks)
|
||||
super().__init__()
|
||||
assert len(num_blocks) == num_units
|
||||
self.has_skip = has_skip
|
||||
self.gen_skip = gen_skip
|
||||
self.gen_cross_conv = gen_cross_conv
|
||||
self.num_units = num_units
|
||||
self.unit_channels = unit_channels
|
||||
self.num_blocks = num_blocks
|
||||
self.norm_cfg = norm_cfg
|
||||
|
||||
self.downsample = DownsampleModule(Bottleneck, num_blocks, num_units,
|
||||
has_skip, norm_cfg, in_channels)
|
||||
self.upsample = UpsampleModule(unit_channels, num_units, gen_skip,
|
||||
gen_cross_conv, norm_cfg, in_channels)
|
||||
|
||||
def forward(self, x, skip1, skip2):
|
||||
mid = self.downsample(x, skip1, skip2)
|
||||
out, skip1, skip2, cross_conv = self.upsample(mid)
|
||||
|
||||
return out, skip1, skip2, cross_conv
|
||||
|
||||
|
||||
class ResNetTop(nn.Module):
|
||||
"""ResNet top for MSPN.
|
||||
|
||||
Args:
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
channels (int): Number of channels of the feature output by ResNetTop.
|
||||
"""
|
||||
|
||||
def __init__(self, norm_cfg=dict(type='BN'), channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.top = nn.Sequential(
|
||||
ConvModule(
|
||||
3,
|
||||
channels,
|
||||
kernel_size=7,
|
||||
stride=2,
|
||||
padding=3,
|
||||
norm_cfg=norm_cfg,
|
||||
inplace=True), MaxPool2d(kernel_size=3, stride=2, padding=1))
|
||||
|
||||
def forward(self, img):
|
||||
return self.top(img)
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class MSPN(BaseBackbone):
|
||||
"""MSPN backbone. Paper ref: Li et al. "Rethinking on Multi-Stage Networks
|
||||
for Human Pose Estimation" (CVPR 2020).
|
||||
|
||||
Args:
|
||||
unit_channels (int): Number of Channels in an upsample unit.
|
||||
Default: 256
|
||||
num_stages (int): Number of stages in a multi-stage MSPN. Default: 4
|
||||
num_units (int): Number of downsample/upsample units in a single-stage
|
||||
network. Default: 4
|
||||
Note: Make sure num_units == len(self.num_blocks)
|
||||
num_blocks (list): Number of bottlenecks in each
|
||||
downsample unit. Default: [2, 2, 2, 2]
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
res_top_channels (int): Number of channels of feature from ResNetTop.
|
||||
Default: 64.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import MSPN
|
||||
>>> import torch
|
||||
>>> self = MSPN(num_stages=2,num_units=2,num_blocks=[2,2])
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 511, 511)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_output in level_outputs:
|
||||
... for feature in level_output:
|
||||
... print(tuple(feature.shape))
|
||||
...
|
||||
(1, 256, 64, 64)
|
||||
(1, 256, 128, 128)
|
||||
(1, 256, 64, 64)
|
||||
(1, 256, 128, 128)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
unit_channels=256,
|
||||
num_stages=4,
|
||||
num_units=4,
|
||||
num_blocks=[2, 2, 2, 2],
|
||||
norm_cfg=dict(type='BN'),
|
||||
res_top_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
num_blocks = cp.deepcopy(num_blocks)
|
||||
super().__init__()
|
||||
self.unit_channels = unit_channels
|
||||
self.num_stages = num_stages
|
||||
self.num_units = num_units
|
||||
self.num_blocks = num_blocks
|
||||
self.norm_cfg = norm_cfg
|
||||
|
||||
assert self.num_stages > 0
|
||||
assert self.num_units > 1
|
||||
assert self.num_units == len(self.num_blocks)
|
||||
self.top = ResNetTop(norm_cfg=norm_cfg)
|
||||
self.multi_stage_mspn = nn.ModuleList([])
|
||||
for i in range(self.num_stages):
|
||||
if i == 0:
|
||||
has_skip = False
|
||||
else:
|
||||
has_skip = True
|
||||
if i != self.num_stages - 1:
|
||||
gen_skip = True
|
||||
gen_cross_conv = True
|
||||
else:
|
||||
gen_skip = False
|
||||
gen_cross_conv = False
|
||||
self.multi_stage_mspn.append(
|
||||
SingleStageNetwork(has_skip, gen_skip, gen_cross_conv,
|
||||
unit_channels, num_units, num_blocks,
|
||||
norm_cfg, res_top_channels))
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
out_feats = []
|
||||
skip1 = None
|
||||
skip2 = None
|
||||
x = self.top(x)
|
||||
for i in range(self.num_stages):
|
||||
out, skip1, skip2, x = self.multi_stage_mspn[i](x, skip1, skip2)
|
||||
out_feats.append(out)
|
||||
|
||||
return out_feats
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize model weights."""
|
||||
if isinstance(pretrained, str):
|
||||
logger = get_root_logger()
|
||||
state_dict_tmp = get_state_dict(pretrained)
|
||||
state_dict = OrderedDict()
|
||||
state_dict['top'] = OrderedDict()
|
||||
state_dict['bottlenecks'] = OrderedDict()
|
||||
for k, v in state_dict_tmp.items():
|
||||
if k.startswith('layer'):
|
||||
if 'downsample.0' in k:
|
||||
state_dict['bottlenecks'][k.replace(
|
||||
'downsample.0', 'downsample.conv')] = v
|
||||
elif 'downsample.1' in k:
|
||||
state_dict['bottlenecks'][k.replace(
|
||||
'downsample.1', 'downsample.bn')] = v
|
||||
else:
|
||||
state_dict['bottlenecks'][k] = v
|
||||
elif k.startswith('conv1'):
|
||||
state_dict['top'][k.replace('conv1', 'top.0.conv')] = v
|
||||
elif k.startswith('bn1'):
|
||||
state_dict['top'][k.replace('bn1', 'top.0.bn')] = v
|
||||
|
||||
load_state_dict(
|
||||
self.top, state_dict['top'], strict=False, logger=logger)
|
||||
for i in range(self.num_stages):
|
||||
load_state_dict(
|
||||
self.multi_stage_mspn[i].downsample,
|
||||
state_dict['bottlenecks'],
|
||||
strict=False,
|
||||
logger=logger)
|
||||
else:
|
||||
for m in self.multi_stage_mspn.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
constant_init(m, 1)
|
||||
elif isinstance(m, nn.Linear):
|
||||
normal_init(m, std=0.01)
|
||||
|
||||
for m in self.top.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
@@ -0,0 +1,317 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import numpy as np
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import build_conv_layer, build_norm_layer
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .resnet import ResNet
|
||||
from .resnext import Bottleneck
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class RegNet(ResNet):
|
||||
"""RegNet backbone.
|
||||
|
||||
More details can be found in `paper <https://arxiv.org/abs/2003.13678>`__ .
|
||||
|
||||
Args:
|
||||
arch (dict): The parameter of RegNets.
|
||||
- w0 (int): initial width
|
||||
- wa (float): slope of width
|
||||
- wm (float): quantization parameter to quantize the width
|
||||
- depth (int): depth of the backbone
|
||||
- group_w (int): width of group
|
||||
- bot_mul (float): bottleneck ratio, i.e. expansion of bottleneck.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
base_channels (int): Base channels after stem layer.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
out_indices (Sequence[int]): Output from which stages.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer. Default: "pytorch".
|
||||
frozen_stages (int): Stages to be frozen (all param fixed). -1 means
|
||||
not freezing any parameters. Default: -1.
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN', requires_grad=True).
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
zero_init_residual (bool): whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: True.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import RegNet
|
||||
>>> import torch
|
||||
>>> self = RegNet(
|
||||
arch=dict(
|
||||
w0=88,
|
||||
wa=26.31,
|
||||
wm=2.25,
|
||||
group_w=48,
|
||||
depth=25,
|
||||
bot_mul=1.0),
|
||||
out_indices=(0, 1, 2, 3))
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 32, 32)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 96, 8, 8)
|
||||
(1, 192, 4, 4)
|
||||
(1, 432, 2, 2)
|
||||
(1, 1008, 1, 1)
|
||||
"""
|
||||
arch_settings = {
|
||||
'regnetx_400mf':
|
||||
dict(w0=24, wa=24.48, wm=2.54, group_w=16, depth=22, bot_mul=1.0),
|
||||
'regnetx_800mf':
|
||||
dict(w0=56, wa=35.73, wm=2.28, group_w=16, depth=16, bot_mul=1.0),
|
||||
'regnetx_1.6gf':
|
||||
dict(w0=80, wa=34.01, wm=2.25, group_w=24, depth=18, bot_mul=1.0),
|
||||
'regnetx_3.2gf':
|
||||
dict(w0=88, wa=26.31, wm=2.25, group_w=48, depth=25, bot_mul=1.0),
|
||||
'regnetx_4.0gf':
|
||||
dict(w0=96, wa=38.65, wm=2.43, group_w=40, depth=23, bot_mul=1.0),
|
||||
'regnetx_6.4gf':
|
||||
dict(w0=184, wa=60.83, wm=2.07, group_w=56, depth=17, bot_mul=1.0),
|
||||
'regnetx_8.0gf':
|
||||
dict(w0=80, wa=49.56, wm=2.88, group_w=120, depth=23, bot_mul=1.0),
|
||||
'regnetx_12gf':
|
||||
dict(w0=168, wa=73.36, wm=2.37, group_w=112, depth=19, bot_mul=1.0),
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
arch,
|
||||
in_channels=3,
|
||||
stem_channels=32,
|
||||
base_channels=32,
|
||||
strides=(2, 2, 2, 2),
|
||||
dilations=(1, 1, 1, 1),
|
||||
out_indices=(3, ),
|
||||
style='pytorch',
|
||||
deep_stem=False,
|
||||
avg_down=False,
|
||||
frozen_stages=-1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=False,
|
||||
with_cp=False,
|
||||
zero_init_residual=True):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super(ResNet, self).__init__()
|
||||
|
||||
# Generate RegNet parameters first
|
||||
if isinstance(arch, str):
|
||||
assert arch in self.arch_settings, \
|
||||
f'"arch": "{arch}" is not one of the' \
|
||||
' arch_settings'
|
||||
arch = self.arch_settings[arch]
|
||||
elif not isinstance(arch, dict):
|
||||
raise TypeError('Expect "arch" to be either a string '
|
||||
f'or a dict, got {type(arch)}')
|
||||
|
||||
widths, num_stages = self.generate_regnet(
|
||||
arch['w0'],
|
||||
arch['wa'],
|
||||
arch['wm'],
|
||||
arch['depth'],
|
||||
)
|
||||
# Convert to per stage format
|
||||
stage_widths, stage_blocks = self.get_stages_from_blocks(widths)
|
||||
# Generate group widths and bot muls
|
||||
group_widths = [arch['group_w'] for _ in range(num_stages)]
|
||||
self.bottleneck_ratio = [arch['bot_mul'] for _ in range(num_stages)]
|
||||
# Adjust the compatibility of stage_widths and group_widths
|
||||
stage_widths, group_widths = self.adjust_width_group(
|
||||
stage_widths, self.bottleneck_ratio, group_widths)
|
||||
|
||||
# Group params by stage
|
||||
self.stage_widths = stage_widths
|
||||
self.group_widths = group_widths
|
||||
self.depth = sum(stage_blocks)
|
||||
self.stem_channels = stem_channels
|
||||
self.base_channels = base_channels
|
||||
self.num_stages = num_stages
|
||||
assert 1 <= num_stages <= 4
|
||||
self.strides = strides
|
||||
self.dilations = dilations
|
||||
assert len(strides) == len(dilations) == num_stages
|
||||
self.out_indices = out_indices
|
||||
assert max(out_indices) < num_stages
|
||||
self.style = style
|
||||
self.deep_stem = deep_stem
|
||||
if self.deep_stem:
|
||||
raise NotImplementedError(
|
||||
'deep_stem has not been implemented for RegNet')
|
||||
self.avg_down = avg_down
|
||||
self.frozen_stages = frozen_stages
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.with_cp = with_cp
|
||||
self.norm_eval = norm_eval
|
||||
self.zero_init_residual = zero_init_residual
|
||||
self.stage_blocks = stage_blocks[:num_stages]
|
||||
|
||||
self._make_stem_layer(in_channels, stem_channels)
|
||||
|
||||
_in_channels = stem_channels
|
||||
self.res_layers = []
|
||||
for i, num_blocks in enumerate(self.stage_blocks):
|
||||
stride = self.strides[i]
|
||||
dilation = self.dilations[i]
|
||||
group_width = self.group_widths[i]
|
||||
width = int(round(self.stage_widths[i] * self.bottleneck_ratio[i]))
|
||||
stage_groups = width // group_width
|
||||
|
||||
res_layer = self.make_res_layer(
|
||||
block=Bottleneck,
|
||||
num_blocks=num_blocks,
|
||||
in_channels=_in_channels,
|
||||
out_channels=self.stage_widths[i],
|
||||
expansion=1,
|
||||
stride=stride,
|
||||
dilation=dilation,
|
||||
style=self.style,
|
||||
avg_down=self.avg_down,
|
||||
with_cp=self.with_cp,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
base_channels=self.stage_widths[i],
|
||||
groups=stage_groups,
|
||||
width_per_group=group_width)
|
||||
_in_channels = self.stage_widths[i]
|
||||
layer_name = f'layer{i + 1}'
|
||||
self.add_module(layer_name, res_layer)
|
||||
self.res_layers.append(layer_name)
|
||||
|
||||
self._freeze_stages()
|
||||
|
||||
self.feat_dim = stage_widths[-1]
|
||||
|
||||
def _make_stem_layer(self, in_channels, base_channels):
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
base_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
bias=False)
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
self.norm_cfg, base_channels, postfix=1)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
@staticmethod
|
||||
def generate_regnet(initial_width,
|
||||
width_slope,
|
||||
width_parameter,
|
||||
depth,
|
||||
divisor=8):
|
||||
"""Generates per block width from RegNet parameters.
|
||||
|
||||
Args:
|
||||
initial_width ([int]): Initial width of the backbone
|
||||
width_slope ([float]): Slope of the quantized linear function
|
||||
width_parameter ([int]): Parameter used to quantize the width.
|
||||
depth ([int]): Depth of the backbone.
|
||||
divisor (int, optional): The divisor of channels. Defaults to 8.
|
||||
|
||||
Returns:
|
||||
list, int: return a list of widths of each stage and the number of
|
||||
stages
|
||||
"""
|
||||
assert width_slope >= 0
|
||||
assert initial_width > 0
|
||||
assert width_parameter > 1
|
||||
assert initial_width % divisor == 0
|
||||
widths_cont = np.arange(depth) * width_slope + initial_width
|
||||
ks = np.round(
|
||||
np.log(widths_cont / initial_width) / np.log(width_parameter))
|
||||
widths = initial_width * np.power(width_parameter, ks)
|
||||
widths = np.round(np.divide(widths, divisor)) * divisor
|
||||
num_stages = len(np.unique(widths))
|
||||
widths, widths_cont = widths.astype(int).tolist(), widths_cont.tolist()
|
||||
return widths, num_stages
|
||||
|
||||
@staticmethod
|
||||
def quantize_float(number, divisor):
|
||||
"""Converts a float to closest non-zero int divisible by divior.
|
||||
|
||||
Args:
|
||||
number (int): Original number to be quantized.
|
||||
divisor (int): Divisor used to quantize the number.
|
||||
|
||||
Returns:
|
||||
int: quantized number that is divisible by devisor.
|
||||
"""
|
||||
return int(round(number / divisor) * divisor)
|
||||
|
||||
def adjust_width_group(self, widths, bottleneck_ratio, groups):
|
||||
"""Adjusts the compatibility of widths and groups.
|
||||
|
||||
Args:
|
||||
widths (list[int]): Width of each stage.
|
||||
bottleneck_ratio (float): Bottleneck ratio.
|
||||
groups (int): number of groups in each stage
|
||||
|
||||
Returns:
|
||||
tuple(list): The adjusted widths and groups of each stage.
|
||||
"""
|
||||
bottleneck_width = [
|
||||
int(w * b) for w, b in zip(widths, bottleneck_ratio)
|
||||
]
|
||||
groups = [min(g, w_bot) for g, w_bot in zip(groups, bottleneck_width)]
|
||||
bottleneck_width = [
|
||||
self.quantize_float(w_bot, g)
|
||||
for w_bot, g in zip(bottleneck_width, groups)
|
||||
]
|
||||
widths = [
|
||||
int(w_bot / b)
|
||||
for w_bot, b in zip(bottleneck_width, bottleneck_ratio)
|
||||
]
|
||||
return widths, groups
|
||||
|
||||
def get_stages_from_blocks(self, widths):
|
||||
"""Gets widths/stage_blocks of network at each stage.
|
||||
|
||||
Args:
|
||||
widths (list[int]): Width in each stage.
|
||||
|
||||
Returns:
|
||||
tuple(list): width and depth of each stage
|
||||
"""
|
||||
width_diff = [
|
||||
width != width_prev
|
||||
for width, width_prev in zip(widths + [0], [0] + widths)
|
||||
]
|
||||
stage_widths = [
|
||||
width for width, diff in zip(widths, width_diff[:-1]) if diff
|
||||
]
|
||||
stage_blocks = np.diff([
|
||||
depth for depth, diff in zip(range(len(width_diff)), width_diff)
|
||||
if diff
|
||||
]).tolist()
|
||||
return stage_widths, stage_blocks
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
x = self.norm1(x)
|
||||
x = self.relu(x)
|
||||
|
||||
outs = []
|
||||
for i, layer_name in enumerate(self.res_layers):
|
||||
res_layer = getattr(self, layer_name)
|
||||
x = res_layer(x)
|
||||
if i in self.out_indices:
|
||||
outs.append(x)
|
||||
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
return tuple(outs)
|
||||
@@ -0,0 +1,338 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import build_conv_layer, build_norm_layer
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .resnet import Bottleneck as _Bottleneck
|
||||
from .resnet import ResLayer, ResNetV1d
|
||||
|
||||
|
||||
class RSoftmax(nn.Module):
|
||||
"""Radix Softmax module in ``SplitAttentionConv2d``.
|
||||
|
||||
Args:
|
||||
radix (int): Radix of input.
|
||||
groups (int): Groups of input.
|
||||
"""
|
||||
|
||||
def __init__(self, radix, groups):
|
||||
super().__init__()
|
||||
self.radix = radix
|
||||
self.groups = groups
|
||||
|
||||
def forward(self, x):
|
||||
batch = x.size(0)
|
||||
if self.radix > 1:
|
||||
x = x.view(batch, self.groups, self.radix, -1).transpose(1, 2)
|
||||
x = F.softmax(x, dim=1)
|
||||
x = x.reshape(batch, -1)
|
||||
else:
|
||||
x = torch.sigmoid(x)
|
||||
return x
|
||||
|
||||
|
||||
class SplitAttentionConv2d(nn.Module):
|
||||
"""Split-Attention Conv2d.
|
||||
|
||||
Args:
|
||||
in_channels (int): Same as nn.Conv2d.
|
||||
out_channels (int): Same as nn.Conv2d.
|
||||
kernel_size (int | tuple[int]): Same as nn.Conv2d.
|
||||
stride (int | tuple[int]): Same as nn.Conv2d.
|
||||
padding (int | tuple[int]): Same as nn.Conv2d.
|
||||
dilation (int | tuple[int]): Same as nn.Conv2d.
|
||||
groups (int): Same as nn.Conv2d.
|
||||
radix (int): Radix of SpltAtConv2d. Default: 2
|
||||
reduction_factor (int): Reduction factor of SplitAttentionConv2d.
|
||||
Default: 4.
|
||||
conv_cfg (dict): Config dict for convolution layer. Default: None,
|
||||
which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer. Default: None.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
channels,
|
||||
kernel_size,
|
||||
stride=1,
|
||||
padding=0,
|
||||
dilation=1,
|
||||
groups=1,
|
||||
radix=2,
|
||||
reduction_factor=4,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN')):
|
||||
super().__init__()
|
||||
inter_channels = max(in_channels * radix // reduction_factor, 32)
|
||||
self.radix = radix
|
||||
self.groups = groups
|
||||
self.channels = channels
|
||||
self.conv = build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
channels * radix,
|
||||
kernel_size,
|
||||
stride=stride,
|
||||
padding=padding,
|
||||
dilation=dilation,
|
||||
groups=groups * radix,
|
||||
bias=False)
|
||||
self.norm0_name, norm0 = build_norm_layer(
|
||||
norm_cfg, channels * radix, postfix=0)
|
||||
self.add_module(self.norm0_name, norm0)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.fc1 = build_conv_layer(
|
||||
None, channels, inter_channels, 1, groups=self.groups)
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
norm_cfg, inter_channels, postfix=1)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.fc2 = build_conv_layer(
|
||||
None, inter_channels, channels * radix, 1, groups=self.groups)
|
||||
self.rsoftmax = RSoftmax(radix, groups)
|
||||
|
||||
@property
|
||||
def norm0(self):
|
||||
return getattr(self, self.norm0_name)
|
||||
|
||||
@property
|
||||
def norm1(self):
|
||||
return getattr(self, self.norm1_name)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv(x)
|
||||
x = self.norm0(x)
|
||||
x = self.relu(x)
|
||||
|
||||
batch, rchannel = x.shape[:2]
|
||||
if self.radix > 1:
|
||||
splits = x.view(batch, self.radix, -1, *x.shape[2:])
|
||||
gap = splits.sum(dim=1)
|
||||
else:
|
||||
gap = x
|
||||
gap = F.adaptive_avg_pool2d(gap, 1)
|
||||
gap = self.fc1(gap)
|
||||
|
||||
gap = self.norm1(gap)
|
||||
gap = self.relu(gap)
|
||||
|
||||
atten = self.fc2(gap)
|
||||
atten = self.rsoftmax(atten).view(batch, -1, 1, 1)
|
||||
|
||||
if self.radix > 1:
|
||||
attens = atten.view(batch, self.radix, -1, *atten.shape[2:])
|
||||
out = torch.sum(attens * splits, dim=1)
|
||||
else:
|
||||
out = atten * x
|
||||
return out.contiguous()
|
||||
|
||||
|
||||
class Bottleneck(_Bottleneck):
|
||||
"""Bottleneck block for ResNeSt.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
groups (int): Groups of conv2.
|
||||
width_per_group (int): Width per group of conv2. 64x4d indicates
|
||||
``groups=64, width_per_group=4`` and 32x8d indicates
|
||||
``groups=32, width_per_group=8``.
|
||||
radix (int): Radix of SpltAtConv2d. Default: 2
|
||||
reduction_factor (int): Reduction factor of SplitAttentionConv2d.
|
||||
Default: 4.
|
||||
avg_down_stride (bool): Whether to use average pool for stride in
|
||||
Bottleneck. Default: True.
|
||||
stride (int): stride of the block. Default: 1
|
||||
dilation (int): dilation of convolution. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
groups=1,
|
||||
width_per_group=4,
|
||||
base_channels=64,
|
||||
radix=2,
|
||||
reduction_factor=4,
|
||||
avg_down_stride=True,
|
||||
**kwargs):
|
||||
super().__init__(in_channels, out_channels, **kwargs)
|
||||
|
||||
self.groups = groups
|
||||
self.width_per_group = width_per_group
|
||||
|
||||
# For ResNet bottleneck, middle channels are determined by expansion
|
||||
# and out_channels, but for ResNeXt bottleneck, it is determined by
|
||||
# groups and width_per_group and the stage it is located in.
|
||||
if groups != 1:
|
||||
assert self.mid_channels % base_channels == 0
|
||||
self.mid_channels = (
|
||||
groups * width_per_group * self.mid_channels // base_channels)
|
||||
|
||||
self.avg_down_stride = avg_down_stride and self.conv2_stride > 1
|
||||
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
self.norm_cfg, self.mid_channels, postfix=1)
|
||||
self.norm3_name, norm3 = build_norm_layer(
|
||||
self.norm_cfg, self.out_channels, postfix=3)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.in_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=1,
|
||||
stride=self.conv1_stride,
|
||||
bias=False)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.conv2 = SplitAttentionConv2d(
|
||||
self.mid_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=3,
|
||||
stride=1 if self.avg_down_stride else self.conv2_stride,
|
||||
padding=self.dilation,
|
||||
dilation=self.dilation,
|
||||
groups=groups,
|
||||
radix=radix,
|
||||
reduction_factor=reduction_factor,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg)
|
||||
delattr(self, self.norm2_name)
|
||||
|
||||
if self.avg_down_stride:
|
||||
self.avd_layer = nn.AvgPool2d(3, self.conv2_stride, padding=1)
|
||||
|
||||
self.conv3 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.mid_channels,
|
||||
self.out_channels,
|
||||
kernel_size=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm3_name, norm3)
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.norm1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
|
||||
if self.avg_down_stride:
|
||||
out = self.avd_layer(out)
|
||||
|
||||
out = self.conv3(out)
|
||||
out = self.norm3(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ResNeSt(ResNetV1d):
|
||||
"""ResNeSt backbone.
|
||||
|
||||
Please refer to the `paper <https://arxiv.org/pdf/2004.08955.pdf>`__
|
||||
for details.
|
||||
|
||||
Args:
|
||||
depth (int): Network depth, from {50, 101, 152, 200}.
|
||||
groups (int): Groups of conv2 in Bottleneck. Default: 32.
|
||||
width_per_group (int): Width per group of conv2 in Bottleneck.
|
||||
Default: 4.
|
||||
radix (int): Radix of SpltAtConv2d. Default: 2
|
||||
reduction_factor (int): Reduction factor of SplitAttentionConv2d.
|
||||
Default: 4.
|
||||
avg_down_stride (bool): Whether to use average pool for stride in
|
||||
Bottleneck. Default: True.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
stem_channels (int): Output channels of the stem layer. Default: 64.
|
||||
num_stages (int): Stages of the network. Default: 4.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
Default: ``(1, 2, 2, 2)``.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
Default: ``(1, 1, 1, 1)``.
|
||||
out_indices (Sequence[int]): Output from which stages. If only one
|
||||
stage is specified, a single tensor (feature map) is returned,
|
||||
otherwise multiple stages are specified, a tuple of tensors will
|
||||
be returned. Default: ``(3, )``.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv.
|
||||
Default: False.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
conv_cfg (dict | None): The config dict for conv layers. Default: None.
|
||||
norm_cfg (dict): The config dict for norm layers.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: True.
|
||||
"""
|
||||
|
||||
arch_settings = {
|
||||
50: (Bottleneck, (3, 4, 6, 3)),
|
||||
101: (Bottleneck, (3, 4, 23, 3)),
|
||||
152: (Bottleneck, (3, 8, 36, 3)),
|
||||
200: (Bottleneck, (3, 24, 36, 3)),
|
||||
269: (Bottleneck, (3, 30, 48, 8))
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
depth,
|
||||
groups=1,
|
||||
width_per_group=4,
|
||||
radix=2,
|
||||
reduction_factor=4,
|
||||
avg_down_stride=True,
|
||||
**kwargs):
|
||||
self.groups = groups
|
||||
self.width_per_group = width_per_group
|
||||
self.radix = radix
|
||||
self.reduction_factor = reduction_factor
|
||||
self.avg_down_stride = avg_down_stride
|
||||
super().__init__(depth=depth, **kwargs)
|
||||
|
||||
def make_res_layer(self, **kwargs):
|
||||
return ResLayer(
|
||||
groups=self.groups,
|
||||
width_per_group=self.width_per_group,
|
||||
base_channels=self.base_channels,
|
||||
radix=self.radix,
|
||||
reduction_factor=self.reduction_factor,
|
||||
avg_down_stride=self.avg_down_stride,
|
||||
**kwargs)
|
||||
@@ -0,0 +1,701 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch.nn as nn
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import (ConvModule, build_conv_layer, build_norm_layer,
|
||||
constant_init, kaiming_init)
|
||||
from mmcv.utils.parrots_wrapper import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
|
||||
class BasicBlock(nn.Module):
|
||||
"""BasicBlock for ResNet.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
expansion (int): The ratio of ``out_channels/mid_channels`` where
|
||||
``mid_channels`` is the output channels of conv1. This is a
|
||||
reserved argument in BasicBlock and should always be 1. Default: 1.
|
||||
stride (int): stride of the block. Default: 1
|
||||
dilation (int): dilation of convolution. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None.
|
||||
style (str): `pytorch` or `caffe`. It is unused and reserved for
|
||||
unified API with Bottleneck.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
expansion=1,
|
||||
stride=1,
|
||||
dilation=1,
|
||||
downsample=None,
|
||||
style='pytorch',
|
||||
with_cp=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN')):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.in_channels = in_channels
|
||||
self.out_channels = out_channels
|
||||
self.expansion = expansion
|
||||
assert self.expansion == 1
|
||||
assert out_channels % expansion == 0
|
||||
self.mid_channels = out_channels // expansion
|
||||
self.stride = stride
|
||||
self.dilation = dilation
|
||||
self.style = style
|
||||
self.with_cp = with_cp
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
norm_cfg, self.mid_channels, postfix=1)
|
||||
self.norm2_name, norm2 = build_norm_layer(
|
||||
norm_cfg, out_channels, postfix=2)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
self.mid_channels,
|
||||
3,
|
||||
stride=stride,
|
||||
padding=dilation,
|
||||
dilation=dilation,
|
||||
bias=False)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.conv2 = build_conv_layer(
|
||||
conv_cfg,
|
||||
self.mid_channels,
|
||||
out_channels,
|
||||
3,
|
||||
padding=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm2_name, norm2)
|
||||
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.downsample = downsample
|
||||
|
||||
@property
|
||||
def norm1(self):
|
||||
"""nn.Module: the normalization layer named "norm1" """
|
||||
return getattr(self, self.norm1_name)
|
||||
|
||||
@property
|
||||
def norm2(self):
|
||||
"""nn.Module: the normalization layer named "norm2" """
|
||||
return getattr(self, self.norm2_name)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
|
||||
def _inner_forward(x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.norm1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.norm2(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class Bottleneck(nn.Module):
|
||||
"""Bottleneck block for ResNet.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
expansion (int): The ratio of ``out_channels/mid_channels`` where
|
||||
``mid_channels`` is the input/output channels of conv2. Default: 4.
|
||||
stride (int): stride of the block. Default: 1
|
||||
dilation (int): dilation of convolution. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None.
|
||||
style (str): ``"pytorch"`` or ``"caffe"``. If set to "pytorch", the
|
||||
stride-two layer is the 3x3 conv layer, otherwise the stride-two
|
||||
layer is the first 1x1 conv layer. Default: "pytorch".
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
expansion=4,
|
||||
stride=1,
|
||||
dilation=1,
|
||||
downsample=None,
|
||||
style='pytorch',
|
||||
with_cp=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN')):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
assert style in ['pytorch', 'caffe']
|
||||
|
||||
self.in_channels = in_channels
|
||||
self.out_channels = out_channels
|
||||
self.expansion = expansion
|
||||
assert out_channels % expansion == 0
|
||||
self.mid_channels = out_channels // expansion
|
||||
self.stride = stride
|
||||
self.dilation = dilation
|
||||
self.style = style
|
||||
self.with_cp = with_cp
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
|
||||
if self.style == 'pytorch':
|
||||
self.conv1_stride = 1
|
||||
self.conv2_stride = stride
|
||||
else:
|
||||
self.conv1_stride = stride
|
||||
self.conv2_stride = 1
|
||||
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
norm_cfg, self.mid_channels, postfix=1)
|
||||
self.norm2_name, norm2 = build_norm_layer(
|
||||
norm_cfg, self.mid_channels, postfix=2)
|
||||
self.norm3_name, norm3 = build_norm_layer(
|
||||
norm_cfg, out_channels, postfix=3)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=1,
|
||||
stride=self.conv1_stride,
|
||||
bias=False)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.conv2 = build_conv_layer(
|
||||
conv_cfg,
|
||||
self.mid_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=3,
|
||||
stride=self.conv2_stride,
|
||||
padding=dilation,
|
||||
dilation=dilation,
|
||||
bias=False)
|
||||
|
||||
self.add_module(self.norm2_name, norm2)
|
||||
self.conv3 = build_conv_layer(
|
||||
conv_cfg,
|
||||
self.mid_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm3_name, norm3)
|
||||
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.downsample = downsample
|
||||
|
||||
@property
|
||||
def norm1(self):
|
||||
"""nn.Module: the normalization layer named "norm1" """
|
||||
return getattr(self, self.norm1_name)
|
||||
|
||||
@property
|
||||
def norm2(self):
|
||||
"""nn.Module: the normalization layer named "norm2" """
|
||||
return getattr(self, self.norm2_name)
|
||||
|
||||
@property
|
||||
def norm3(self):
|
||||
"""nn.Module: the normalization layer named "norm3" """
|
||||
return getattr(self, self.norm3_name)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
|
||||
def _inner_forward(x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.norm1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.norm2(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv3(out)
|
||||
out = self.norm3(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def get_expansion(block, expansion=None):
|
||||
"""Get the expansion of a residual block.
|
||||
|
||||
The block expansion will be obtained by the following order:
|
||||
|
||||
1. If ``expansion`` is given, just return it.
|
||||
2. If ``block`` has the attribute ``expansion``, then return
|
||||
``block.expansion``.
|
||||
3. Return the default value according the the block type:
|
||||
1 for ``BasicBlock`` and 4 for ``Bottleneck``.
|
||||
|
||||
Args:
|
||||
block (class): The block class.
|
||||
expansion (int | None): The given expansion ratio.
|
||||
|
||||
Returns:
|
||||
int: The expansion of the block.
|
||||
"""
|
||||
if isinstance(expansion, int):
|
||||
assert expansion > 0
|
||||
elif expansion is None:
|
||||
if hasattr(block, 'expansion'):
|
||||
expansion = block.expansion
|
||||
elif issubclass(block, BasicBlock):
|
||||
expansion = 1
|
||||
elif issubclass(block, Bottleneck):
|
||||
expansion = 4
|
||||
else:
|
||||
raise TypeError(f'expansion is not specified for {block.__name__}')
|
||||
else:
|
||||
raise TypeError('expansion must be an integer or None')
|
||||
|
||||
return expansion
|
||||
|
||||
|
||||
class ResLayer(nn.Sequential):
|
||||
"""ResLayer to build ResNet style backbone.
|
||||
|
||||
Args:
|
||||
block (nn.Module): Residual block used to build ResLayer.
|
||||
num_blocks (int): Number of blocks.
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
expansion (int, optional): The expansion for BasicBlock/Bottleneck.
|
||||
If not specified, it will firstly be obtained via
|
||||
``block.expansion``. If the block has no attribute "expansion",
|
||||
the following default values will be used: 1 for BasicBlock and
|
||||
4 for Bottleneck. Default: None.
|
||||
stride (int): stride of the first block. Default: 1.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
downsample_first (bool): Downsample at the first block or last block.
|
||||
False for Hourglass, True for ResNet. Default: True
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
block,
|
||||
num_blocks,
|
||||
in_channels,
|
||||
out_channels,
|
||||
expansion=None,
|
||||
stride=1,
|
||||
avg_down=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
downsample_first=True,
|
||||
**kwargs):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
self.block = block
|
||||
self.expansion = get_expansion(block, expansion)
|
||||
|
||||
downsample = None
|
||||
if stride != 1 or in_channels != out_channels:
|
||||
downsample = []
|
||||
conv_stride = stride
|
||||
if avg_down and stride != 1:
|
||||
conv_stride = 1
|
||||
downsample.append(
|
||||
nn.AvgPool2d(
|
||||
kernel_size=stride,
|
||||
stride=stride,
|
||||
ceil_mode=True,
|
||||
count_include_pad=False))
|
||||
downsample.extend([
|
||||
build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
stride=conv_stride,
|
||||
bias=False),
|
||||
build_norm_layer(norm_cfg, out_channels)[1]
|
||||
])
|
||||
downsample = nn.Sequential(*downsample)
|
||||
|
||||
layers = []
|
||||
if downsample_first:
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels,
|
||||
expansion=self.expansion,
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
**kwargs))
|
||||
in_channels = out_channels
|
||||
for _ in range(1, num_blocks):
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels,
|
||||
expansion=self.expansion,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
**kwargs))
|
||||
else: # downsample_first=False is for HourglassModule
|
||||
for i in range(0, num_blocks - 1):
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=in_channels,
|
||||
expansion=self.expansion,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
**kwargs))
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels,
|
||||
expansion=self.expansion,
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
**kwargs))
|
||||
|
||||
super().__init__(*layers)
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ResNet(BaseBackbone):
|
||||
"""ResNet backbone.
|
||||
|
||||
Please refer to the `paper <https://arxiv.org/abs/1512.03385>`__ for
|
||||
details.
|
||||
|
||||
Args:
|
||||
depth (int): Network depth, from {18, 34, 50, 101, 152}.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
stem_channels (int): Output channels of the stem layer. Default: 64.
|
||||
base_channels (int): Middle channels of the first stage. Default: 64.
|
||||
num_stages (int): Stages of the network. Default: 4.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
Default: ``(1, 2, 2, 2)``.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
Default: ``(1, 1, 1, 1)``.
|
||||
out_indices (Sequence[int]): Output from which stages. If only one
|
||||
stage is specified, a single tensor (feature map) is returned,
|
||||
otherwise multiple stages are specified, a tuple of tensors will
|
||||
be returned. Default: ``(3, )``.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv.
|
||||
Default: False.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
conv_cfg (dict | None): The config dict for conv layers. Default: None.
|
||||
norm_cfg (dict): The config dict for norm layers.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: True.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import ResNet
|
||||
>>> import torch
|
||||
>>> self = ResNet(depth=18, out_indices=(0, 1, 2, 3))
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 32, 32)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 64, 8, 8)
|
||||
(1, 128, 4, 4)
|
||||
(1, 256, 2, 2)
|
||||
(1, 512, 1, 1)
|
||||
"""
|
||||
|
||||
arch_settings = {
|
||||
18: (BasicBlock, (2, 2, 2, 2)),
|
||||
34: (BasicBlock, (3, 4, 6, 3)),
|
||||
50: (Bottleneck, (3, 4, 6, 3)),
|
||||
101: (Bottleneck, (3, 4, 23, 3)),
|
||||
152: (Bottleneck, (3, 8, 36, 3))
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
depth,
|
||||
in_channels=3,
|
||||
stem_channels=64,
|
||||
base_channels=64,
|
||||
expansion=None,
|
||||
num_stages=4,
|
||||
strides=(1, 2, 2, 2),
|
||||
dilations=(1, 1, 1, 1),
|
||||
out_indices=(3, ),
|
||||
style='pytorch',
|
||||
deep_stem=False,
|
||||
avg_down=False,
|
||||
frozen_stages=-1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=False,
|
||||
with_cp=False,
|
||||
zero_init_residual=True):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
if depth not in self.arch_settings:
|
||||
raise KeyError(f'invalid depth {depth} for resnet')
|
||||
self.depth = depth
|
||||
self.stem_channels = stem_channels
|
||||
self.base_channels = base_channels
|
||||
self.num_stages = num_stages
|
||||
assert 1 <= num_stages <= 4
|
||||
self.strides = strides
|
||||
self.dilations = dilations
|
||||
assert len(strides) == len(dilations) == num_stages
|
||||
self.out_indices = out_indices
|
||||
assert max(out_indices) < num_stages
|
||||
self.style = style
|
||||
self.deep_stem = deep_stem
|
||||
self.avg_down = avg_down
|
||||
self.frozen_stages = frozen_stages
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.with_cp = with_cp
|
||||
self.norm_eval = norm_eval
|
||||
self.zero_init_residual = zero_init_residual
|
||||
self.block, stage_blocks = self.arch_settings[depth]
|
||||
self.stage_blocks = stage_blocks[:num_stages]
|
||||
self.expansion = get_expansion(self.block, expansion)
|
||||
|
||||
self._make_stem_layer(in_channels, stem_channels)
|
||||
|
||||
self.res_layers = []
|
||||
_in_channels = stem_channels
|
||||
_out_channels = base_channels * self.expansion
|
||||
for i, num_blocks in enumerate(self.stage_blocks):
|
||||
stride = strides[i]
|
||||
dilation = dilations[i]
|
||||
res_layer = self.make_res_layer(
|
||||
block=self.block,
|
||||
num_blocks=num_blocks,
|
||||
in_channels=_in_channels,
|
||||
out_channels=_out_channels,
|
||||
expansion=self.expansion,
|
||||
stride=stride,
|
||||
dilation=dilation,
|
||||
style=self.style,
|
||||
avg_down=self.avg_down,
|
||||
with_cp=with_cp,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg)
|
||||
_in_channels = _out_channels
|
||||
_out_channels *= 2
|
||||
layer_name = f'layer{i + 1}'
|
||||
self.add_module(layer_name, res_layer)
|
||||
self.res_layers.append(layer_name)
|
||||
|
||||
self._freeze_stages()
|
||||
|
||||
self.feat_dim = res_layer[-1].out_channels
|
||||
|
||||
def make_res_layer(self, **kwargs):
|
||||
"""Make a ResLayer."""
|
||||
return ResLayer(**kwargs)
|
||||
|
||||
@property
|
||||
def norm1(self):
|
||||
"""nn.Module: the normalization layer named "norm1" """
|
||||
return getattr(self, self.norm1_name)
|
||||
|
||||
def _make_stem_layer(self, in_channels, stem_channels):
|
||||
"""Make stem layer."""
|
||||
if self.deep_stem:
|
||||
self.stem = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels,
|
||||
stem_channels // 2,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True),
|
||||
ConvModule(
|
||||
stem_channels // 2,
|
||||
stem_channels // 2,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True),
|
||||
ConvModule(
|
||||
stem_channels // 2,
|
||||
stem_channels,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True))
|
||||
else:
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
stem_channels,
|
||||
kernel_size=7,
|
||||
stride=2,
|
||||
padding=3,
|
||||
bias=False)
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
self.norm_cfg, stem_channels, postfix=1)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
|
||||
def _freeze_stages(self):
|
||||
"""Freeze parameters."""
|
||||
if self.frozen_stages >= 0:
|
||||
if self.deep_stem:
|
||||
self.stem.eval()
|
||||
for param in self.stem.parameters():
|
||||
param.requires_grad = False
|
||||
else:
|
||||
self.norm1.eval()
|
||||
for m in [self.conv1, self.norm1]:
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
m = getattr(self, f'layer{i}')
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
super().init_weights(pretrained)
|
||||
if pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, 1)
|
||||
|
||||
if self.zero_init_residual:
|
||||
for m in self.modules():
|
||||
if isinstance(m, Bottleneck):
|
||||
constant_init(m.norm3, 0)
|
||||
elif isinstance(m, BasicBlock):
|
||||
constant_init(m.norm2, 0)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
if self.deep_stem:
|
||||
x = self.stem(x)
|
||||
else:
|
||||
x = self.conv1(x)
|
||||
x = self.norm1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
outs = []
|
||||
for i, layer_name in enumerate(self.res_layers):
|
||||
res_layer = getattr(self, layer_name)
|
||||
x = res_layer(x)
|
||||
if i in self.out_indices:
|
||||
outs.append(x)
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
return tuple(outs)
|
||||
|
||||
def train(self, mode=True):
|
||||
"""Convert the model into training mode."""
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
# trick: eval have effect on BatchNorm only
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ResNetV1d(ResNet):
|
||||
r"""ResNetV1d variant described in `Bag of Tricks
|
||||
<https://arxiv.org/pdf/1812.01187.pdf>`__.
|
||||
|
||||
Compared with default ResNet(ResNetV1b), ResNetV1d replaces the 7x7 conv in
|
||||
the input stem with three 3x3 convs. And in the downsampling block, a 2x2
|
||||
avg_pool with stride 2 is added before conv, whose stride is changed to 1.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(deep_stem=True, avg_down=True, **kwargs)
|
||||
@@ -0,0 +1,162 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from mmcv.cnn import build_conv_layer, build_norm_layer
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .resnet import Bottleneck as _Bottleneck
|
||||
from .resnet import ResLayer, ResNet
|
||||
|
||||
|
||||
class Bottleneck(_Bottleneck):
|
||||
"""Bottleneck block for ResNeXt.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
groups (int): Groups of conv2.
|
||||
width_per_group (int): Width per group of conv2. 64x4d indicates
|
||||
``groups=64, width_per_group=4`` and 32x8d indicates
|
||||
``groups=32, width_per_group=8``.
|
||||
stride (int): stride of the block. Default: 1
|
||||
dilation (int): dilation of convolution. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
base_channels=64,
|
||||
groups=32,
|
||||
width_per_group=4,
|
||||
**kwargs):
|
||||
super().__init__(in_channels, out_channels, **kwargs)
|
||||
self.groups = groups
|
||||
self.width_per_group = width_per_group
|
||||
|
||||
# For ResNet bottleneck, middle channels are determined by expansion
|
||||
# and out_channels, but for ResNeXt bottleneck, it is determined by
|
||||
# groups and width_per_group and the stage it is located in.
|
||||
if groups != 1:
|
||||
assert self.mid_channels % base_channels == 0
|
||||
self.mid_channels = (
|
||||
groups * width_per_group * self.mid_channels // base_channels)
|
||||
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
self.norm_cfg, self.mid_channels, postfix=1)
|
||||
self.norm2_name, norm2 = build_norm_layer(
|
||||
self.norm_cfg, self.mid_channels, postfix=2)
|
||||
self.norm3_name, norm3 = build_norm_layer(
|
||||
self.norm_cfg, self.out_channels, postfix=3)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.in_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=1,
|
||||
stride=self.conv1_stride,
|
||||
bias=False)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.conv2 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.mid_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=3,
|
||||
stride=self.conv2_stride,
|
||||
padding=self.dilation,
|
||||
dilation=self.dilation,
|
||||
groups=groups,
|
||||
bias=False)
|
||||
|
||||
self.add_module(self.norm2_name, norm2)
|
||||
self.conv3 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.mid_channels,
|
||||
self.out_channels,
|
||||
kernel_size=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm3_name, norm3)
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ResNeXt(ResNet):
|
||||
"""ResNeXt backbone.
|
||||
|
||||
Please refer to the `paper <https://arxiv.org/abs/1611.05431>`__ for
|
||||
details.
|
||||
|
||||
Args:
|
||||
depth (int): Network depth, from {50, 101, 152}.
|
||||
groups (int): Groups of conv2 in Bottleneck. Default: 32.
|
||||
width_per_group (int): Width per group of conv2 in Bottleneck.
|
||||
Default: 4.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
stem_channels (int): Output channels of the stem layer. Default: 64.
|
||||
num_stages (int): Stages of the network. Default: 4.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
Default: ``(1, 2, 2, 2)``.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
Default: ``(1, 1, 1, 1)``.
|
||||
out_indices (Sequence[int]): Output from which stages. If only one
|
||||
stage is specified, a single tensor (feature map) is returned,
|
||||
otherwise multiple stages are specified, a tuple of tensors will
|
||||
be returned. Default: ``(3, )``.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv.
|
||||
Default: False.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
conv_cfg (dict | None): The config dict for conv layers. Default: None.
|
||||
norm_cfg (dict): The config dict for norm layers.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: True.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import ResNeXt
|
||||
>>> import torch
|
||||
>>> self = ResNeXt(depth=50, out_indices=(0, 1, 2, 3))
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 32, 32)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 256, 8, 8)
|
||||
(1, 512, 4, 4)
|
||||
(1, 1024, 2, 2)
|
||||
(1, 2048, 1, 1)
|
||||
"""
|
||||
|
||||
arch_settings = {
|
||||
50: (Bottleneck, (3, 4, 6, 3)),
|
||||
101: (Bottleneck, (3, 4, 23, 3)),
|
||||
152: (Bottleneck, (3, 8, 36, 3))
|
||||
}
|
||||
|
||||
def __init__(self, depth, groups=32, width_per_group=4, **kwargs):
|
||||
self.groups = groups
|
||||
self.width_per_group = width_per_group
|
||||
super().__init__(depth, **kwargs)
|
||||
|
||||
def make_res_layer(self, **kwargs):
|
||||
return ResLayer(
|
||||
groups=self.groups,
|
||||
width_per_group=self.width_per_group,
|
||||
base_channels=self.base_channels,
|
||||
**kwargs)
|
||||
@@ -0,0 +1,616 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy as cp
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from mmcv.cnn import (ConvModule, MaxPool2d, constant_init, kaiming_init,
|
||||
normal_init)
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
|
||||
class RSB(nn.Module):
|
||||
"""Residual Steps block for RSN. Paper ref: Cai et al. "Learning Delicate
|
||||
Local Representations for Multi-Person Pose Estimation" (ECCV 2020).
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
num_steps (int): Numbers of steps in RSB
|
||||
stride (int): stride of the block. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None.
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
expand_times (int): Times by which the in_channels are expanded.
|
||||
Default:26.
|
||||
res_top_channels (int): Number of channels of feature output by
|
||||
ResNet_top. Default:64.
|
||||
"""
|
||||
|
||||
expansion = 1
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
num_steps=4,
|
||||
stride=1,
|
||||
downsample=None,
|
||||
with_cp=False,
|
||||
norm_cfg=dict(type='BN'),
|
||||
expand_times=26,
|
||||
res_top_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
assert num_steps > 1
|
||||
self.in_channels = in_channels
|
||||
self.branch_channels = self.in_channels * expand_times
|
||||
self.branch_channels //= res_top_channels
|
||||
self.out_channels = out_channels
|
||||
self.stride = stride
|
||||
self.downsample = downsample
|
||||
self.with_cp = with_cp
|
||||
self.norm_cfg = norm_cfg
|
||||
self.num_steps = num_steps
|
||||
self.conv_bn_relu1 = ConvModule(
|
||||
self.in_channels,
|
||||
self.num_steps * self.branch_channels,
|
||||
kernel_size=1,
|
||||
stride=self.stride,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=False)
|
||||
for i in range(self.num_steps):
|
||||
for j in range(i + 1):
|
||||
module_name = f'conv_bn_relu2_{i + 1}_{j + 1}'
|
||||
self.add_module(
|
||||
module_name,
|
||||
ConvModule(
|
||||
self.branch_channels,
|
||||
self.branch_channels,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=False))
|
||||
self.conv_bn3 = ConvModule(
|
||||
self.num_steps * self.branch_channels,
|
||||
self.out_channels * self.expansion,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
act_cfg=None,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=False)
|
||||
self.relu = nn.ReLU(inplace=False)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
|
||||
identity = x
|
||||
x = self.conv_bn_relu1(x)
|
||||
spx = torch.split(x, self.branch_channels, 1)
|
||||
outputs = list()
|
||||
outs = list()
|
||||
for i in range(self.num_steps):
|
||||
outputs_i = list()
|
||||
outputs.append(outputs_i)
|
||||
for j in range(i + 1):
|
||||
if j == 0:
|
||||
inputs = spx[i]
|
||||
else:
|
||||
inputs = outputs[i][j - 1]
|
||||
if i > j:
|
||||
inputs = inputs + outputs[i - 1][j]
|
||||
module_name = f'conv_bn_relu2_{i + 1}_{j + 1}'
|
||||
module_i_j = getattr(self, module_name)
|
||||
outputs[i].append(module_i_j(inputs))
|
||||
|
||||
outs.append(outputs[i][i])
|
||||
out = torch.cat(tuple(outs), 1)
|
||||
out = self.conv_bn3(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(identity)
|
||||
out = out + identity
|
||||
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class Downsample_module(nn.Module):
|
||||
"""Downsample module for RSN.
|
||||
|
||||
Args:
|
||||
block (nn.Module): Downsample block.
|
||||
num_blocks (list): Number of blocks in each downsample unit.
|
||||
num_units (int): Numbers of downsample units. Default: 4
|
||||
has_skip (bool): Have skip connections from prior upsample
|
||||
module or not. Default:False
|
||||
num_steps (int): Number of steps in a block. Default:4
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
in_channels (int): Number of channels of the input feature to
|
||||
downsample module. Default: 64
|
||||
expand_times (int): Times by which the in_channels are expanded.
|
||||
Default:26.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
block,
|
||||
num_blocks,
|
||||
num_steps=4,
|
||||
num_units=4,
|
||||
has_skip=False,
|
||||
norm_cfg=dict(type='BN'),
|
||||
in_channels=64,
|
||||
expand_times=26):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.has_skip = has_skip
|
||||
self.in_channels = in_channels
|
||||
assert len(num_blocks) == num_units
|
||||
self.num_blocks = num_blocks
|
||||
self.num_units = num_units
|
||||
self.num_steps = num_steps
|
||||
self.norm_cfg = norm_cfg
|
||||
self.layer1 = self._make_layer(
|
||||
block,
|
||||
in_channels,
|
||||
num_blocks[0],
|
||||
expand_times=expand_times,
|
||||
res_top_channels=in_channels)
|
||||
for i in range(1, num_units):
|
||||
module_name = f'layer{i + 1}'
|
||||
self.add_module(
|
||||
module_name,
|
||||
self._make_layer(
|
||||
block,
|
||||
in_channels * pow(2, i),
|
||||
num_blocks[i],
|
||||
stride=2,
|
||||
expand_times=expand_times,
|
||||
res_top_channels=in_channels))
|
||||
|
||||
def _make_layer(self,
|
||||
block,
|
||||
out_channels,
|
||||
blocks,
|
||||
stride=1,
|
||||
expand_times=26,
|
||||
res_top_channels=64):
|
||||
downsample = None
|
||||
if stride != 1 or self.in_channels != out_channels * block.expansion:
|
||||
downsample = ConvModule(
|
||||
self.in_channels,
|
||||
out_channels * block.expansion,
|
||||
kernel_size=1,
|
||||
stride=stride,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=None,
|
||||
inplace=True)
|
||||
|
||||
units = list()
|
||||
units.append(
|
||||
block(
|
||||
self.in_channels,
|
||||
out_channels,
|
||||
num_steps=self.num_steps,
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
norm_cfg=self.norm_cfg,
|
||||
expand_times=expand_times,
|
||||
res_top_channels=res_top_channels))
|
||||
self.in_channels = out_channels * block.expansion
|
||||
for _ in range(1, blocks):
|
||||
units.append(
|
||||
block(
|
||||
self.in_channels,
|
||||
out_channels,
|
||||
num_steps=self.num_steps,
|
||||
expand_times=expand_times,
|
||||
res_top_channels=res_top_channels))
|
||||
|
||||
return nn.Sequential(*units)
|
||||
|
||||
def forward(self, x, skip1, skip2):
|
||||
out = list()
|
||||
for i in range(self.num_units):
|
||||
module_name = f'layer{i + 1}'
|
||||
module_i = getattr(self, module_name)
|
||||
x = module_i(x)
|
||||
if self.has_skip:
|
||||
x = x + skip1[i] + skip2[i]
|
||||
out.append(x)
|
||||
out.reverse()
|
||||
|
||||
return tuple(out)
|
||||
|
||||
|
||||
class Upsample_unit(nn.Module):
|
||||
"""Upsample unit for upsample module.
|
||||
|
||||
Args:
|
||||
ind (int): Indicates whether to interpolate (>0) and whether to
|
||||
generate feature map for the next hourglass-like module.
|
||||
num_units (int): Number of units that form a upsample module. Along
|
||||
with ind and gen_cross_conv, nm_units is used to decide whether
|
||||
to generate feature map for the next hourglass-like module.
|
||||
in_channels (int): Channel number of the skip-in feature maps from
|
||||
the corresponding downsample unit.
|
||||
unit_channels (int): Channel number in this unit. Default:256.
|
||||
gen_skip: (bool): Whether or not to generate skips for the posterior
|
||||
downsample module. Default:False
|
||||
gen_cross_conv (bool): Whether to generate feature map for the next
|
||||
hourglass-like module. Default:False
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
out_channels (in): Number of channels of feature output by upsample
|
||||
module. Must equal to in_channels of downsample module. Default:64
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
ind,
|
||||
num_units,
|
||||
in_channels,
|
||||
unit_channels=256,
|
||||
gen_skip=False,
|
||||
gen_cross_conv=False,
|
||||
norm_cfg=dict(type='BN'),
|
||||
out_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.num_units = num_units
|
||||
self.norm_cfg = norm_cfg
|
||||
self.in_skip = ConvModule(
|
||||
in_channels,
|
||||
unit_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=None,
|
||||
inplace=True)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
|
||||
self.ind = ind
|
||||
if self.ind > 0:
|
||||
self.up_conv = ConvModule(
|
||||
unit_channels,
|
||||
unit_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=None,
|
||||
inplace=True)
|
||||
|
||||
self.gen_skip = gen_skip
|
||||
if self.gen_skip:
|
||||
self.out_skip1 = ConvModule(
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True)
|
||||
|
||||
self.out_skip2 = ConvModule(
|
||||
unit_channels,
|
||||
in_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True)
|
||||
|
||||
self.gen_cross_conv = gen_cross_conv
|
||||
if self.ind == num_units - 1 and self.gen_cross_conv:
|
||||
self.cross_conv = ConvModule(
|
||||
unit_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True)
|
||||
|
||||
def forward(self, x, up_x):
|
||||
out = self.in_skip(x)
|
||||
|
||||
if self.ind > 0:
|
||||
up_x = F.interpolate(
|
||||
up_x,
|
||||
size=(x.size(2), x.size(3)),
|
||||
mode='bilinear',
|
||||
align_corners=True)
|
||||
up_x = self.up_conv(up_x)
|
||||
out = out + up_x
|
||||
out = self.relu(out)
|
||||
|
||||
skip1 = None
|
||||
skip2 = None
|
||||
if self.gen_skip:
|
||||
skip1 = self.out_skip1(x)
|
||||
skip2 = self.out_skip2(out)
|
||||
|
||||
cross_conv = None
|
||||
if self.ind == self.num_units - 1 and self.gen_cross_conv:
|
||||
cross_conv = self.cross_conv(out)
|
||||
|
||||
return out, skip1, skip2, cross_conv
|
||||
|
||||
|
||||
class Upsample_module(nn.Module):
|
||||
"""Upsample module for RSN.
|
||||
|
||||
Args:
|
||||
unit_channels (int): Channel number in the upsample units.
|
||||
Default:256.
|
||||
num_units (int): Numbers of upsample units. Default: 4
|
||||
gen_skip (bool): Whether to generate skip for posterior downsample
|
||||
module or not. Default:False
|
||||
gen_cross_conv (bool): Whether to generate feature map for the next
|
||||
hourglass-like module. Default:False
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
out_channels (int): Number of channels of feature output by upsample
|
||||
module. Must equal to in_channels of downsample module. Default:64
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
unit_channels=256,
|
||||
num_units=4,
|
||||
gen_skip=False,
|
||||
gen_cross_conv=False,
|
||||
norm_cfg=dict(type='BN'),
|
||||
out_channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.in_channels = list()
|
||||
for i in range(num_units):
|
||||
self.in_channels.append(RSB.expansion * out_channels * pow(2, i))
|
||||
self.in_channels.reverse()
|
||||
self.num_units = num_units
|
||||
self.gen_skip = gen_skip
|
||||
self.gen_cross_conv = gen_cross_conv
|
||||
self.norm_cfg = norm_cfg
|
||||
for i in range(num_units):
|
||||
module_name = f'up{i + 1}'
|
||||
self.add_module(
|
||||
module_name,
|
||||
Upsample_unit(
|
||||
i,
|
||||
self.num_units,
|
||||
self.in_channels[i],
|
||||
unit_channels,
|
||||
self.gen_skip,
|
||||
self.gen_cross_conv,
|
||||
norm_cfg=self.norm_cfg,
|
||||
out_channels=64))
|
||||
|
||||
def forward(self, x):
|
||||
out = list()
|
||||
skip1 = list()
|
||||
skip2 = list()
|
||||
cross_conv = None
|
||||
for i in range(self.num_units):
|
||||
module_i = getattr(self, f'up{i + 1}')
|
||||
if i == 0:
|
||||
outi, skip1_i, skip2_i, _ = module_i(x[i], None)
|
||||
elif i == self.num_units - 1:
|
||||
outi, skip1_i, skip2_i, cross_conv = module_i(x[i], out[i - 1])
|
||||
else:
|
||||
outi, skip1_i, skip2_i, _ = module_i(x[i], out[i - 1])
|
||||
out.append(outi)
|
||||
skip1.append(skip1_i)
|
||||
skip2.append(skip2_i)
|
||||
skip1.reverse()
|
||||
skip2.reverse()
|
||||
|
||||
return out, skip1, skip2, cross_conv
|
||||
|
||||
|
||||
class Single_stage_RSN(nn.Module):
|
||||
"""Single_stage Residual Steps Network.
|
||||
|
||||
Args:
|
||||
unit_channels (int): Channel number in the upsample units. Default:256.
|
||||
num_units (int): Numbers of downsample/upsample units. Default: 4
|
||||
gen_skip (bool): Whether to generate skip for posterior downsample
|
||||
module or not. Default:False
|
||||
gen_cross_conv (bool): Whether to generate feature map for the next
|
||||
hourglass-like module. Default:False
|
||||
has_skip (bool): Have skip connections from prior upsample
|
||||
module or not. Default:False
|
||||
num_steps (int): Number of steps in RSB. Default: 4
|
||||
num_blocks (list): Number of blocks in each downsample unit.
|
||||
Default: [2, 2, 2, 2] Note: Make sure num_units==len(num_blocks)
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
in_channels (int): Number of channels of the feature from ResNet_Top.
|
||||
Default: 64.
|
||||
expand_times (int): Times by which the in_channels are expanded in RSB.
|
||||
Default:26.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
has_skip=False,
|
||||
gen_skip=False,
|
||||
gen_cross_conv=False,
|
||||
unit_channels=256,
|
||||
num_units=4,
|
||||
num_steps=4,
|
||||
num_blocks=[2, 2, 2, 2],
|
||||
norm_cfg=dict(type='BN'),
|
||||
in_channels=64,
|
||||
expand_times=26):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
num_blocks = cp.deepcopy(num_blocks)
|
||||
super().__init__()
|
||||
assert len(num_blocks) == num_units
|
||||
self.has_skip = has_skip
|
||||
self.gen_skip = gen_skip
|
||||
self.gen_cross_conv = gen_cross_conv
|
||||
self.num_units = num_units
|
||||
self.num_steps = num_steps
|
||||
self.unit_channels = unit_channels
|
||||
self.num_blocks = num_blocks
|
||||
self.norm_cfg = norm_cfg
|
||||
|
||||
self.downsample = Downsample_module(RSB, num_blocks, num_steps,
|
||||
num_units, has_skip, norm_cfg,
|
||||
in_channels, expand_times)
|
||||
self.upsample = Upsample_module(unit_channels, num_units, gen_skip,
|
||||
gen_cross_conv, norm_cfg, in_channels)
|
||||
|
||||
def forward(self, x, skip1, skip2):
|
||||
mid = self.downsample(x, skip1, skip2)
|
||||
out, skip1, skip2, cross_conv = self.upsample(mid)
|
||||
|
||||
return out, skip1, skip2, cross_conv
|
||||
|
||||
|
||||
class ResNet_top(nn.Module):
|
||||
"""ResNet top for RSN.
|
||||
|
||||
Args:
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
channels (int): Number of channels of the feature output by ResNet_top.
|
||||
"""
|
||||
|
||||
def __init__(self, norm_cfg=dict(type='BN'), channels=64):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.top = nn.Sequential(
|
||||
ConvModule(
|
||||
3,
|
||||
channels,
|
||||
kernel_size=7,
|
||||
stride=2,
|
||||
padding=3,
|
||||
norm_cfg=norm_cfg,
|
||||
inplace=True), MaxPool2d(kernel_size=3, stride=2, padding=1))
|
||||
|
||||
def forward(self, img):
|
||||
return self.top(img)
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class RSN(BaseBackbone):
|
||||
"""Residual Steps Network backbone. Paper ref: Cai et al. "Learning
|
||||
Delicate Local Representations for Multi-Person Pose Estimation" (ECCV
|
||||
2020).
|
||||
|
||||
Args:
|
||||
unit_channels (int): Number of Channels in an upsample unit.
|
||||
Default: 256
|
||||
num_stages (int): Number of stages in a multi-stage RSN. Default: 4
|
||||
num_units (int): NUmber of downsample/upsample units in a single-stage
|
||||
RSN. Default: 4 Note: Make sure num_units == len(self.num_blocks)
|
||||
num_blocks (list): Number of RSBs (Residual Steps Block) in each
|
||||
downsample unit. Default: [2, 2, 2, 2]
|
||||
num_steps (int): Number of steps in a RSB. Default:4
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
res_top_channels (int): Number of channels of feature from ResNet_top.
|
||||
Default: 64.
|
||||
expand_times (int): Times by which the in_channels are expanded in RSB.
|
||||
Default:26.
|
||||
Example:
|
||||
>>> from mmpose.models import RSN
|
||||
>>> import torch
|
||||
>>> self = RSN(num_stages=2,num_units=2,num_blocks=[2,2])
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 511, 511)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_output in level_outputs:
|
||||
... for feature in level_output:
|
||||
... print(tuple(feature.shape))
|
||||
...
|
||||
(1, 256, 64, 64)
|
||||
(1, 256, 128, 128)
|
||||
(1, 256, 64, 64)
|
||||
(1, 256, 128, 128)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
unit_channels=256,
|
||||
num_stages=4,
|
||||
num_units=4,
|
||||
num_blocks=[2, 2, 2, 2],
|
||||
num_steps=4,
|
||||
norm_cfg=dict(type='BN'),
|
||||
res_top_channels=64,
|
||||
expand_times=26):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = cp.deepcopy(norm_cfg)
|
||||
num_blocks = cp.deepcopy(num_blocks)
|
||||
super().__init__()
|
||||
self.unit_channels = unit_channels
|
||||
self.num_stages = num_stages
|
||||
self.num_units = num_units
|
||||
self.num_blocks = num_blocks
|
||||
self.num_steps = num_steps
|
||||
self.norm_cfg = norm_cfg
|
||||
|
||||
assert self.num_stages > 0
|
||||
assert self.num_steps > 1
|
||||
assert self.num_units > 1
|
||||
assert self.num_units == len(self.num_blocks)
|
||||
self.top = ResNet_top(norm_cfg=norm_cfg)
|
||||
self.multi_stage_rsn = nn.ModuleList([])
|
||||
for i in range(self.num_stages):
|
||||
if i == 0:
|
||||
has_skip = False
|
||||
else:
|
||||
has_skip = True
|
||||
if i != self.num_stages - 1:
|
||||
gen_skip = True
|
||||
gen_cross_conv = True
|
||||
else:
|
||||
gen_skip = False
|
||||
gen_cross_conv = False
|
||||
self.multi_stage_rsn.append(
|
||||
Single_stage_RSN(has_skip, gen_skip, gen_cross_conv,
|
||||
unit_channels, num_units, num_steps,
|
||||
num_blocks, norm_cfg, res_top_channels,
|
||||
expand_times))
|
||||
|
||||
def forward(self, x):
|
||||
"""Model forward function."""
|
||||
out_feats = []
|
||||
skip1 = None
|
||||
skip2 = None
|
||||
x = self.top(x)
|
||||
for i in range(self.num_stages):
|
||||
out, skip1, skip2, x = self.multi_stage_rsn[i](x, skip1, skip2)
|
||||
out_feats.append(out)
|
||||
|
||||
return out_feats
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize model weights."""
|
||||
for m in self.multi_stage_rsn.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
constant_init(m, 1)
|
||||
elif isinstance(m, nn.Linear):
|
||||
normal_init(m, std=0.01)
|
||||
|
||||
for m in self.top.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
@@ -0,0 +1,248 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import build_conv_layer, build_norm_layer
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .resnet import Bottleneck, ResNet
|
||||
|
||||
|
||||
class SCConv(nn.Module):
|
||||
"""SCConv (Self-calibrated Convolution)
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the SCConv.
|
||||
out_channels (int): The output channel of the SCConv.
|
||||
stride (int): stride of SCConv.
|
||||
pooling_r (int): size of pooling for scconv.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
stride,
|
||||
pooling_r,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN', momentum=0.1)):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
|
||||
assert in_channels == out_channels
|
||||
|
||||
self.k2 = nn.Sequential(
|
||||
nn.AvgPool2d(kernel_size=pooling_r, stride=pooling_r),
|
||||
build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(norm_cfg, in_channels)[1],
|
||||
)
|
||||
self.k3 = nn.Sequential(
|
||||
build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(norm_cfg, in_channels)[1],
|
||||
)
|
||||
self.k4 = nn.Sequential(
|
||||
build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=3,
|
||||
stride=stride,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(norm_cfg, out_channels)[1],
|
||||
nn.ReLU(inplace=True),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
identity = x
|
||||
|
||||
out = torch.sigmoid(
|
||||
torch.add(identity, F.interpolate(self.k2(x),
|
||||
identity.size()[2:])))
|
||||
out = torch.mul(self.k3(x), out)
|
||||
out = self.k4(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class SCBottleneck(Bottleneck):
|
||||
"""SC(Self-calibrated) Bottleneck.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the SCBottleneck block.
|
||||
out_channels (int): The output channel of the SCBottleneck block.
|
||||
"""
|
||||
|
||||
pooling_r = 4
|
||||
|
||||
def __init__(self, in_channels, out_channels, **kwargs):
|
||||
super().__init__(in_channels, out_channels, **kwargs)
|
||||
self.mid_channels = out_channels // self.expansion // 2
|
||||
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
self.norm_cfg, self.mid_channels, postfix=1)
|
||||
self.norm2_name, norm2 = build_norm_layer(
|
||||
self.norm_cfg, self.mid_channels, postfix=2)
|
||||
self.norm3_name, norm3 = build_norm_layer(
|
||||
self.norm_cfg, out_channels, postfix=3)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
|
||||
self.k1 = nn.Sequential(
|
||||
build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.mid_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=3,
|
||||
stride=self.stride,
|
||||
padding=1,
|
||||
bias=False),
|
||||
build_norm_layer(self.norm_cfg, self.mid_channels)[1],
|
||||
nn.ReLU(inplace=True))
|
||||
|
||||
self.conv2 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm2_name, norm2)
|
||||
|
||||
self.scconv = SCConv(self.mid_channels, self.mid_channels, self.stride,
|
||||
self.pooling_r, self.conv_cfg, self.norm_cfg)
|
||||
|
||||
self.conv3 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.mid_channels * 2,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm3_name, norm3)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
|
||||
def _inner_forward(x):
|
||||
identity = x
|
||||
|
||||
out_a = self.conv1(x)
|
||||
out_a = self.norm1(out_a)
|
||||
out_a = self.relu(out_a)
|
||||
|
||||
out_a = self.k1(out_a)
|
||||
|
||||
out_b = self.conv2(x)
|
||||
out_b = self.norm2(out_b)
|
||||
out_b = self.relu(out_b)
|
||||
|
||||
out_b = self.scconv(out_b)
|
||||
|
||||
out = self.conv3(torch.cat([out_a, out_b], dim=1))
|
||||
out = self.norm3(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class SCNet(ResNet):
|
||||
"""SCNet backbone.
|
||||
|
||||
Improving Convolutional Networks with Self-Calibrated Convolutions,
|
||||
Jiang-Jiang Liu, Qibin Hou, Ming-Ming Cheng, Changhu Wang, Jiashi Feng,
|
||||
IEEE CVPR, 2020.
|
||||
http://mftp.mmcheng.net/Papers/20cvprSCNet.pdf
|
||||
|
||||
Args:
|
||||
depth (int): Depth of scnet, from {50, 101}.
|
||||
in_channels (int): Number of input image channels. Normally 3.
|
||||
base_channels (int): Number of base channels of hidden layer.
|
||||
num_stages (int): SCNet stages, normally 4.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
out_indices (Sequence[int]): Output from which stages.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters.
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import SCNet
|
||||
>>> import torch
|
||||
>>> self = SCNet(depth=50, out_indices=(0, 1, 2, 3))
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 224, 224)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 256, 56, 56)
|
||||
(1, 512, 28, 28)
|
||||
(1, 1024, 14, 14)
|
||||
(1, 2048, 7, 7)
|
||||
"""
|
||||
|
||||
arch_settings = {
|
||||
50: (SCBottleneck, [3, 4, 6, 3]),
|
||||
101: (SCBottleneck, [3, 4, 23, 3])
|
||||
}
|
||||
|
||||
def __init__(self, depth, **kwargs):
|
||||
if depth not in self.arch_settings:
|
||||
raise KeyError(f'invalid depth {depth} for SCNet')
|
||||
super().__init__(depth, **kwargs)
|
||||
@@ -0,0 +1,125 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch.utils.checkpoint as cp
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .resnet import Bottleneck, ResLayer, ResNet
|
||||
from .utils.se_layer import SELayer
|
||||
|
||||
|
||||
class SEBottleneck(Bottleneck):
|
||||
"""SEBottleneck block for SEResNet.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the SEBottleneck block.
|
||||
out_channels (int): The output channel of the SEBottleneck block.
|
||||
se_ratio (int): Squeeze ratio in SELayer. Default: 16
|
||||
"""
|
||||
|
||||
def __init__(self, in_channels, out_channels, se_ratio=16, **kwargs):
|
||||
super().__init__(in_channels, out_channels, **kwargs)
|
||||
self.se_layer = SELayer(out_channels, ratio=se_ratio)
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.norm1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.norm2(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv3(out)
|
||||
out = self.norm3(out)
|
||||
|
||||
out = self.se_layer(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class SEResNet(ResNet):
|
||||
"""SEResNet backbone.
|
||||
|
||||
Please refer to the `paper <https://arxiv.org/abs/1709.01507>`__ for
|
||||
details.
|
||||
|
||||
Args:
|
||||
depth (int): Network depth, from {50, 101, 152}.
|
||||
se_ratio (int): Squeeze ratio in SELayer. Default: 16.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
stem_channels (int): Output channels of the stem layer. Default: 64.
|
||||
num_stages (int): Stages of the network. Default: 4.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
Default: ``(1, 2, 2, 2)``.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
Default: ``(1, 1, 1, 1)``.
|
||||
out_indices (Sequence[int]): Output from which stages. If only one
|
||||
stage is specified, a single tensor (feature map) is returned,
|
||||
otherwise multiple stages are specified, a tuple of tensors will
|
||||
be returned. Default: ``(3, )``.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv.
|
||||
Default: False.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
conv_cfg (dict | None): The config dict for conv layers. Default: None.
|
||||
norm_cfg (dict): The config dict for norm layers.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: True.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import SEResNet
|
||||
>>> import torch
|
||||
>>> self = SEResNet(depth=50, out_indices=(0, 1, 2, 3))
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 224, 224)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 256, 56, 56)
|
||||
(1, 512, 28, 28)
|
||||
(1, 1024, 14, 14)
|
||||
(1, 2048, 7, 7)
|
||||
"""
|
||||
|
||||
arch_settings = {
|
||||
50: (SEBottleneck, (3, 4, 6, 3)),
|
||||
101: (SEBottleneck, (3, 4, 23, 3)),
|
||||
152: (SEBottleneck, (3, 8, 36, 3))
|
||||
}
|
||||
|
||||
def __init__(self, depth, se_ratio=16, **kwargs):
|
||||
if depth not in self.arch_settings:
|
||||
raise KeyError(f'invalid depth {depth} for SEResNet')
|
||||
self.se_ratio = se_ratio
|
||||
super().__init__(depth, **kwargs)
|
||||
|
||||
def make_res_layer(self, **kwargs):
|
||||
return ResLayer(se_ratio=self.se_ratio, **kwargs)
|
||||
@@ -0,0 +1,168 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from mmcv.cnn import build_conv_layer, build_norm_layer
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .resnet import ResLayer
|
||||
from .seresnet import SEBottleneck as _SEBottleneck
|
||||
from .seresnet import SEResNet
|
||||
|
||||
|
||||
class SEBottleneck(_SEBottleneck):
|
||||
"""SEBottleneck block for SEResNeXt.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
base_channels (int): Middle channels of the first stage. Default: 64.
|
||||
groups (int): Groups of conv2.
|
||||
width_per_group (int): Width per group of conv2. 64x4d indicates
|
||||
``groups=64, width_per_group=4`` and 32x8d indicates
|
||||
``groups=32, width_per_group=8``.
|
||||
stride (int): stride of the block. Default: 1
|
||||
dilation (int): dilation of convolution. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None
|
||||
se_ratio (int): Squeeze ratio in SELayer. Default: 16
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
base_channels=64,
|
||||
groups=32,
|
||||
width_per_group=4,
|
||||
se_ratio=16,
|
||||
**kwargs):
|
||||
super().__init__(in_channels, out_channels, se_ratio, **kwargs)
|
||||
self.groups = groups
|
||||
self.width_per_group = width_per_group
|
||||
|
||||
# We follow the same rational of ResNext to compute mid_channels.
|
||||
# For SEResNet bottleneck, middle channels are determined by expansion
|
||||
# and out_channels, but for SEResNeXt bottleneck, it is determined by
|
||||
# groups and width_per_group and the stage it is located in.
|
||||
if groups != 1:
|
||||
assert self.mid_channels % base_channels == 0
|
||||
self.mid_channels = (
|
||||
groups * width_per_group * self.mid_channels // base_channels)
|
||||
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
self.norm_cfg, self.mid_channels, postfix=1)
|
||||
self.norm2_name, norm2 = build_norm_layer(
|
||||
self.norm_cfg, self.mid_channels, postfix=2)
|
||||
self.norm3_name, norm3 = build_norm_layer(
|
||||
self.norm_cfg, self.out_channels, postfix=3)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.in_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=1,
|
||||
stride=self.conv1_stride,
|
||||
bias=False)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.conv2 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.mid_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=3,
|
||||
stride=self.conv2_stride,
|
||||
padding=self.dilation,
|
||||
dilation=self.dilation,
|
||||
groups=groups,
|
||||
bias=False)
|
||||
|
||||
self.add_module(self.norm2_name, norm2)
|
||||
self.conv3 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
self.mid_channels,
|
||||
self.out_channels,
|
||||
kernel_size=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm3_name, norm3)
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class SEResNeXt(SEResNet):
|
||||
"""SEResNeXt backbone.
|
||||
|
||||
Please refer to the `paper <https://arxiv.org/abs/1709.01507>`__ for
|
||||
details.
|
||||
|
||||
Args:
|
||||
depth (int): Network depth, from {50, 101, 152}.
|
||||
groups (int): Groups of conv2 in Bottleneck. Default: 32.
|
||||
width_per_group (int): Width per group of conv2 in Bottleneck.
|
||||
Default: 4.
|
||||
se_ratio (int): Squeeze ratio in SELayer. Default: 16.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
stem_channels (int): Output channels of the stem layer. Default: 64.
|
||||
num_stages (int): Stages of the network. Default: 4.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
Default: ``(1, 2, 2, 2)``.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
Default: ``(1, 1, 1, 1)``.
|
||||
out_indices (Sequence[int]): Output from which stages. If only one
|
||||
stage is specified, a single tensor (feature map) is returned,
|
||||
otherwise multiple stages are specified, a tuple of tensors will
|
||||
be returned. Default: ``(3, )``.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv.
|
||||
Default: False.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
conv_cfg (dict | None): The config dict for conv layers. Default: None.
|
||||
norm_cfg (dict): The config dict for norm layers.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: True.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import SEResNeXt
|
||||
>>> import torch
|
||||
>>> self = SEResNet(depth=50, out_indices=(0, 1, 2, 3))
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 3, 224, 224)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 256, 56, 56)
|
||||
(1, 512, 28, 28)
|
||||
(1, 1024, 14, 14)
|
||||
(1, 2048, 7, 7)
|
||||
"""
|
||||
|
||||
arch_settings = {
|
||||
50: (SEBottleneck, (3, 4, 6, 3)),
|
||||
101: (SEBottleneck, (3, 4, 23, 3)),
|
||||
152: (SEBottleneck, (3, 8, 36, 3))
|
||||
}
|
||||
|
||||
def __init__(self, depth, groups=32, width_per_group=4, **kwargs):
|
||||
self.groups = groups
|
||||
self.width_per_group = width_per_group
|
||||
super().__init__(depth, **kwargs)
|
||||
|
||||
def make_res_layer(self, **kwargs):
|
||||
return ResLayer(
|
||||
groups=self.groups,
|
||||
width_per_group=self.width_per_group,
|
||||
base_channels=self.base_channels,
|
||||
**kwargs)
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import (ConvModule, build_activation_layer, constant_init,
|
||||
normal_init)
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .utils import channel_shuffle, load_checkpoint, make_divisible
|
||||
|
||||
|
||||
class ShuffleUnit(nn.Module):
|
||||
"""ShuffleUnit block.
|
||||
|
||||
ShuffleNet unit with pointwise group convolution (GConv) and channel
|
||||
shuffle.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the ShuffleUnit.
|
||||
out_channels (int): The output channels of the ShuffleUnit.
|
||||
groups (int, optional): The number of groups to be used in grouped 1x1
|
||||
convolutions in each ShuffleUnit. Default: 3
|
||||
first_block (bool, optional): Whether it is the first ShuffleUnit of a
|
||||
sequential ShuffleUnits. Default: True, which means not using the
|
||||
grouped 1x1 convolution.
|
||||
combine (str, optional): The ways to combine the input and output
|
||||
branches. Default: 'add'.
|
||||
conv_cfg (dict): Config dict for convolution layer. Default: None,
|
||||
which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU').
|
||||
with_cp (bool, optional): Use checkpoint or not. Using checkpoint
|
||||
will save some memory while slowing down the training speed.
|
||||
Default: False.
|
||||
|
||||
Returns:
|
||||
Tensor: The output tensor.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
groups=3,
|
||||
first_block=True,
|
||||
combine='add',
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU'),
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
act_cfg = copy.deepcopy(act_cfg)
|
||||
super().__init__()
|
||||
self.in_channels = in_channels
|
||||
self.out_channels = out_channels
|
||||
self.first_block = first_block
|
||||
self.combine = combine
|
||||
self.groups = groups
|
||||
self.bottleneck_channels = self.out_channels // 4
|
||||
self.with_cp = with_cp
|
||||
|
||||
if self.combine == 'add':
|
||||
self.depthwise_stride = 1
|
||||
self._combine_func = self._add
|
||||
assert in_channels == out_channels, (
|
||||
'in_channels must be equal to out_channels when combine '
|
||||
'is add')
|
||||
elif self.combine == 'concat':
|
||||
self.depthwise_stride = 2
|
||||
self._combine_func = self._concat
|
||||
self.out_channels -= self.in_channels
|
||||
self.avgpool = nn.AvgPool2d(kernel_size=3, stride=2, padding=1)
|
||||
else:
|
||||
raise ValueError(f'Cannot combine tensors with {self.combine}. '
|
||||
'Only "add" and "concat" are supported')
|
||||
|
||||
self.first_1x1_groups = 1 if first_block else self.groups
|
||||
self.g_conv_1x1_compress = ConvModule(
|
||||
in_channels=self.in_channels,
|
||||
out_channels=self.bottleneck_channels,
|
||||
kernel_size=1,
|
||||
groups=self.first_1x1_groups,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg)
|
||||
|
||||
self.depthwise_conv3x3_bn = ConvModule(
|
||||
in_channels=self.bottleneck_channels,
|
||||
out_channels=self.bottleneck_channels,
|
||||
kernel_size=3,
|
||||
stride=self.depthwise_stride,
|
||||
padding=1,
|
||||
groups=self.bottleneck_channels,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None)
|
||||
|
||||
self.g_conv_1x1_expand = ConvModule(
|
||||
in_channels=self.bottleneck_channels,
|
||||
out_channels=self.out_channels,
|
||||
kernel_size=1,
|
||||
groups=self.groups,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None)
|
||||
|
||||
self.act = build_activation_layer(act_cfg)
|
||||
|
||||
@staticmethod
|
||||
def _add(x, out):
|
||||
# residual connection
|
||||
return x + out
|
||||
|
||||
@staticmethod
|
||||
def _concat(x, out):
|
||||
# concatenate along channel axis
|
||||
return torch.cat((x, out), 1)
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
residual = x
|
||||
|
||||
out = self.g_conv_1x1_compress(x)
|
||||
out = self.depthwise_conv3x3_bn(out)
|
||||
|
||||
if self.groups > 1:
|
||||
out = channel_shuffle(out, self.groups)
|
||||
|
||||
out = self.g_conv_1x1_expand(out)
|
||||
|
||||
if self.combine == 'concat':
|
||||
residual = self.avgpool(residual)
|
||||
out = self.act(out)
|
||||
out = self._combine_func(residual, out)
|
||||
else:
|
||||
out = self._combine_func(residual, out)
|
||||
out = self.act(out)
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ShuffleNetV1(BaseBackbone):
|
||||
"""ShuffleNetV1 backbone.
|
||||
|
||||
Args:
|
||||
groups (int, optional): The number of groups to be used in grouped 1x1
|
||||
convolutions in each ShuffleUnit. Default: 3.
|
||||
widen_factor (float, optional): Width multiplier - adjusts the number
|
||||
of channels in each layer by this amount. Default: 1.0.
|
||||
out_indices (Sequence[int]): Output from which stages.
|
||||
Default: (2, )
|
||||
frozen_stages (int): Stages to be frozen (all param fixed).
|
||||
Default: -1, which means not freezing any parameters.
|
||||
conv_cfg (dict): Config dict for convolution layer. Default: None,
|
||||
which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU').
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
groups=3,
|
||||
widen_factor=1.0,
|
||||
out_indices=(2, ),
|
||||
frozen_stages=-1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU'),
|
||||
norm_eval=False,
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
act_cfg = copy.deepcopy(act_cfg)
|
||||
super().__init__()
|
||||
self.stage_blocks = [4, 8, 4]
|
||||
self.groups = groups
|
||||
|
||||
for index in out_indices:
|
||||
if index not in range(0, 3):
|
||||
raise ValueError('the item in out_indices must in '
|
||||
f'range(0, 3). But received {index}')
|
||||
|
||||
if frozen_stages not in range(-1, 3):
|
||||
raise ValueError('frozen_stages must be in range(-1, 3). '
|
||||
f'But received {frozen_stages}')
|
||||
self.out_indices = out_indices
|
||||
self.frozen_stages = frozen_stages
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.act_cfg = act_cfg
|
||||
self.norm_eval = norm_eval
|
||||
self.with_cp = with_cp
|
||||
|
||||
if groups == 1:
|
||||
channels = (144, 288, 576)
|
||||
elif groups == 2:
|
||||
channels = (200, 400, 800)
|
||||
elif groups == 3:
|
||||
channels = (240, 480, 960)
|
||||
elif groups == 4:
|
||||
channels = (272, 544, 1088)
|
||||
elif groups == 8:
|
||||
channels = (384, 768, 1536)
|
||||
else:
|
||||
raise ValueError(f'{groups} groups is not supported for 1x1 '
|
||||
'Grouped Convolutions')
|
||||
|
||||
channels = [make_divisible(ch * widen_factor, 8) for ch in channels]
|
||||
|
||||
self.in_channels = int(24 * widen_factor)
|
||||
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=3,
|
||||
out_channels=self.in_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg)
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
|
||||
self.layers = nn.ModuleList()
|
||||
for i, num_blocks in enumerate(self.stage_blocks):
|
||||
first_block = (i == 0)
|
||||
layer = self.make_layer(channels[i], num_blocks, first_block)
|
||||
self.layers.append(layer)
|
||||
|
||||
def _freeze_stages(self):
|
||||
if self.frozen_stages >= 0:
|
||||
for param in self.conv1.parameters():
|
||||
param.requires_grad = False
|
||||
for i in range(self.frozen_stages):
|
||||
layer = self.layers[i]
|
||||
layer.eval()
|
||||
for param in layer.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
if isinstance(pretrained, str):
|
||||
logger = logging.getLogger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for name, m in self.named_modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
if 'conv1' in name:
|
||||
normal_init(m, mean=0, std=0.01)
|
||||
else:
|
||||
normal_init(m, mean=0, std=1.0 / m.weight.shape[1])
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m, val=1, bias=0.0001)
|
||||
if isinstance(m, _BatchNorm):
|
||||
if m.running_mean is not None:
|
||||
nn.init.constant_(m.running_mean, 0)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None. But received '
|
||||
f'{type(pretrained)}')
|
||||
|
||||
def make_layer(self, out_channels, num_blocks, first_block=False):
|
||||
"""Stack ShuffleUnit blocks to make a layer.
|
||||
|
||||
Args:
|
||||
out_channels (int): out_channels of the block.
|
||||
num_blocks (int): Number of blocks.
|
||||
first_block (bool, optional): Whether is the first ShuffleUnit of a
|
||||
sequential ShuffleUnits. Default: False, which means using
|
||||
the grouped 1x1 convolution.
|
||||
"""
|
||||
layers = []
|
||||
for i in range(num_blocks):
|
||||
first_block = first_block if i == 0 else False
|
||||
combine_mode = 'concat' if i == 0 else 'add'
|
||||
layers.append(
|
||||
ShuffleUnit(
|
||||
self.in_channels,
|
||||
out_channels,
|
||||
groups=self.groups,
|
||||
first_block=first_block,
|
||||
combine=combine_mode,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=self.act_cfg,
|
||||
with_cp=self.with_cp))
|
||||
self.in_channels = out_channels
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
x = self.maxpool(x)
|
||||
|
||||
outs = []
|
||||
for i, layer in enumerate(self.layers):
|
||||
x = layer(x)
|
||||
if i in self.out_indices:
|
||||
outs.append(x)
|
||||
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
return tuple(outs)
|
||||
|
||||
def train(self, mode=True):
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import ConvModule, constant_init, normal_init
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .utils import channel_shuffle, load_checkpoint
|
||||
|
||||
|
||||
class InvertedResidual(nn.Module):
|
||||
"""InvertedResidual block for ShuffleNetV2 backbone.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of the block.
|
||||
out_channels (int): The output channels of the block.
|
||||
stride (int): Stride of the 3x3 convolution layer. Default: 1
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU').
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
stride=1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU'),
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
act_cfg = copy.deepcopy(act_cfg)
|
||||
super().__init__()
|
||||
self.stride = stride
|
||||
self.with_cp = with_cp
|
||||
|
||||
branch_features = out_channels // 2
|
||||
if self.stride == 1:
|
||||
assert in_channels == branch_features * 2, (
|
||||
f'in_channels ({in_channels}) should equal to '
|
||||
f'branch_features * 2 ({branch_features * 2}) '
|
||||
'when stride is 1')
|
||||
|
||||
if in_channels != branch_features * 2:
|
||||
assert self.stride != 1, (
|
||||
f'stride ({self.stride}) should not equal 1 when '
|
||||
f'in_channels != branch_features * 2')
|
||||
|
||||
if self.stride > 1:
|
||||
self.branch1 = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels,
|
||||
in_channels,
|
||||
kernel_size=3,
|
||||
stride=self.stride,
|
||||
padding=1,
|
||||
groups=in_channels,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None),
|
||||
ConvModule(
|
||||
in_channels,
|
||||
branch_features,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg),
|
||||
)
|
||||
|
||||
self.branch2 = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels if (self.stride > 1) else branch_features,
|
||||
branch_features,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg),
|
||||
ConvModule(
|
||||
branch_features,
|
||||
branch_features,
|
||||
kernel_size=3,
|
||||
stride=self.stride,
|
||||
padding=1,
|
||||
groups=branch_features,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None),
|
||||
ConvModule(
|
||||
branch_features,
|
||||
branch_features,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg))
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
if self.stride > 1:
|
||||
out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
|
||||
else:
|
||||
x1, x2 = x.chunk(2, dim=1)
|
||||
out = torch.cat((x1, self.branch2(x2)), dim=1)
|
||||
|
||||
out = channel_shuffle(out, 2)
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ShuffleNetV2(BaseBackbone):
|
||||
"""ShuffleNetV2 backbone.
|
||||
|
||||
Args:
|
||||
widen_factor (float): Width multiplier - adjusts the number of
|
||||
channels in each layer by this amount. Default: 1.0.
|
||||
out_indices (Sequence[int]): Output from which stages.
|
||||
Default: (0, 1, 2, 3).
|
||||
frozen_stages (int): Stages to be frozen (all param fixed).
|
||||
Default: -1, which means not freezing any parameters.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU').
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
widen_factor=1.0,
|
||||
out_indices=(3, ),
|
||||
frozen_stages=-1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU'),
|
||||
norm_eval=False,
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
act_cfg = copy.deepcopy(act_cfg)
|
||||
super().__init__()
|
||||
self.stage_blocks = [4, 8, 4]
|
||||
for index in out_indices:
|
||||
if index not in range(0, 4):
|
||||
raise ValueError('the item in out_indices must in '
|
||||
f'range(0, 4). But received {index}')
|
||||
|
||||
if frozen_stages not in range(-1, 4):
|
||||
raise ValueError('frozen_stages must be in range(-1, 4). '
|
||||
f'But received {frozen_stages}')
|
||||
self.out_indices = out_indices
|
||||
self.frozen_stages = frozen_stages
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.act_cfg = act_cfg
|
||||
self.norm_eval = norm_eval
|
||||
self.with_cp = with_cp
|
||||
|
||||
if widen_factor == 0.5:
|
||||
channels = [48, 96, 192, 1024]
|
||||
elif widen_factor == 1.0:
|
||||
channels = [116, 232, 464, 1024]
|
||||
elif widen_factor == 1.5:
|
||||
channels = [176, 352, 704, 1024]
|
||||
elif widen_factor == 2.0:
|
||||
channels = [244, 488, 976, 2048]
|
||||
else:
|
||||
raise ValueError('widen_factor must be in [0.5, 1.0, 1.5, 2.0]. '
|
||||
f'But received {widen_factor}')
|
||||
|
||||
self.in_channels = 24
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=3,
|
||||
out_channels=self.in_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg)
|
||||
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
|
||||
self.layers = nn.ModuleList()
|
||||
for i, num_blocks in enumerate(self.stage_blocks):
|
||||
layer = self._make_layer(channels[i], num_blocks)
|
||||
self.layers.append(layer)
|
||||
|
||||
output_channels = channels[-1]
|
||||
self.layers.append(
|
||||
ConvModule(
|
||||
in_channels=self.in_channels,
|
||||
out_channels=output_channels,
|
||||
kernel_size=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg))
|
||||
|
||||
def _make_layer(self, out_channels, num_blocks):
|
||||
"""Stack blocks to make a layer.
|
||||
|
||||
Args:
|
||||
out_channels (int): out_channels of the block.
|
||||
num_blocks (int): number of blocks.
|
||||
"""
|
||||
layers = []
|
||||
for i in range(num_blocks):
|
||||
stride = 2 if i == 0 else 1
|
||||
layers.append(
|
||||
InvertedResidual(
|
||||
in_channels=self.in_channels,
|
||||
out_channels=out_channels,
|
||||
stride=stride,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=self.act_cfg,
|
||||
with_cp=self.with_cp))
|
||||
self.in_channels = out_channels
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _freeze_stages(self):
|
||||
if self.frozen_stages >= 0:
|
||||
for param in self.conv1.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
for i in range(self.frozen_stages):
|
||||
m = self.layers[i]
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
if isinstance(pretrained, str):
|
||||
logger = logging.getLogger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for name, m in self.named_modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
if 'conv1' in name:
|
||||
normal_init(m, mean=0, std=0.01)
|
||||
else:
|
||||
normal_init(m, mean=0, std=1.0 / m.weight.shape[1])
|
||||
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
||||
constant_init(m.weight, val=1, bias=0.0001)
|
||||
if isinstance(m, _BatchNorm):
|
||||
if m.running_mean is not None:
|
||||
nn.init.constant_(m.running_mean, 0)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None. But received '
|
||||
f'{type(pretrained)}')
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
x = self.maxpool(x)
|
||||
|
||||
outs = []
|
||||
for i, layer in enumerate(self.layers):
|
||||
x = layer(x)
|
||||
if i in self.out_indices:
|
||||
outs.append(x)
|
||||
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
return tuple(outs)
|
||||
|
||||
def train(self, mode=True):
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.BatchNorm2d):
|
||||
m.eval()
|
||||
@@ -0,0 +1,267 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule, build_conv_layer, constant_init, kaiming_init
|
||||
from mmcv.utils.parrots_wrapper import _BatchNorm
|
||||
|
||||
from mmpose.core import WeightNormClipHook
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
|
||||
class BasicTemporalBlock(nn.Module):
|
||||
"""Basic block for VideoPose3D.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
mid_channels (int): The output channels of conv1. Default: 1024.
|
||||
kernel_size (int): Size of the convolving kernel. Default: 3.
|
||||
dilation (int): Spacing between kernel elements. Default: 3.
|
||||
dropout (float): Dropout rate. Default: 0.25.
|
||||
causal (bool): Use causal convolutions instead of symmetric
|
||||
convolutions (for real-time applications). Default: False.
|
||||
residual (bool): Use residual connection. Default: True.
|
||||
use_stride_conv (bool): Use optimized TCN that designed
|
||||
specifically for single-frame batching, i.e. where batches have
|
||||
input length = receptive field, and output length = 1. This
|
||||
implementation replaces dilated convolutions with strided
|
||||
convolutions to avoid generating unused intermediate results.
|
||||
Default: False.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: dict(type='Conv1d').
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN1d').
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
mid_channels=1024,
|
||||
kernel_size=3,
|
||||
dilation=3,
|
||||
dropout=0.25,
|
||||
causal=False,
|
||||
residual=True,
|
||||
use_stride_conv=False,
|
||||
conv_cfg=dict(type='Conv1d'),
|
||||
norm_cfg=dict(type='BN1d')):
|
||||
# Protect mutable default arguments
|
||||
conv_cfg = copy.deepcopy(conv_cfg)
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.in_channels = in_channels
|
||||
self.out_channels = out_channels
|
||||
self.mid_channels = mid_channels
|
||||
self.kernel_size = kernel_size
|
||||
self.dilation = dilation
|
||||
self.dropout = dropout
|
||||
self.causal = causal
|
||||
self.residual = residual
|
||||
self.use_stride_conv = use_stride_conv
|
||||
|
||||
self.pad = (kernel_size - 1) * dilation // 2
|
||||
if use_stride_conv:
|
||||
self.stride = kernel_size
|
||||
self.causal_shift = kernel_size // 2 if causal else 0
|
||||
self.dilation = 1
|
||||
else:
|
||||
self.stride = 1
|
||||
self.causal_shift = kernel_size // 2 * dilation if causal else 0
|
||||
|
||||
self.conv1 = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels,
|
||||
mid_channels,
|
||||
kernel_size=kernel_size,
|
||||
stride=self.stride,
|
||||
dilation=self.dilation,
|
||||
bias='auto',
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg))
|
||||
self.conv2 = nn.Sequential(
|
||||
ConvModule(
|
||||
mid_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
bias='auto',
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg))
|
||||
|
||||
if residual and in_channels != out_channels:
|
||||
self.short_cut = build_conv_layer(conv_cfg, in_channels,
|
||||
out_channels, 1)
|
||||
else:
|
||||
self.short_cut = None
|
||||
|
||||
self.dropout = nn.Dropout(dropout) if dropout > 0 else None
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
if self.use_stride_conv:
|
||||
assert self.causal_shift + self.kernel_size // 2 < x.shape[2]
|
||||
else:
|
||||
assert 0 <= self.pad + self.causal_shift < x.shape[2] - \
|
||||
self.pad + self.causal_shift <= x.shape[2]
|
||||
|
||||
out = self.conv1(x)
|
||||
if self.dropout is not None:
|
||||
out = self.dropout(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
if self.dropout is not None:
|
||||
out = self.dropout(out)
|
||||
|
||||
if self.residual:
|
||||
if self.use_stride_conv:
|
||||
res = x[:, :, self.causal_shift +
|
||||
self.kernel_size // 2::self.kernel_size]
|
||||
else:
|
||||
res = x[:, :,
|
||||
(self.pad + self.causal_shift):(x.shape[2] - self.pad +
|
||||
self.causal_shift)]
|
||||
|
||||
if self.short_cut is not None:
|
||||
res = self.short_cut(res)
|
||||
out = out + res
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class TCN(BaseBackbone):
|
||||
"""TCN backbone.
|
||||
|
||||
Temporal Convolutional Networks.
|
||||
More details can be found in the
|
||||
`paper <https://arxiv.org/abs/1811.11742>`__ .
|
||||
|
||||
Args:
|
||||
in_channels (int): Number of input channels, which equals to
|
||||
num_keypoints * num_features.
|
||||
stem_channels (int): Number of feature channels. Default: 1024.
|
||||
num_blocks (int): NUmber of basic temporal convolutional blocks.
|
||||
Default: 2.
|
||||
kernel_sizes (Sequence[int]): Sizes of the convolving kernel of
|
||||
each basic block. Default: ``(3, 3, 3)``.
|
||||
dropout (float): Dropout rate. Default: 0.25.
|
||||
causal (bool): Use causal convolutions instead of symmetric
|
||||
convolutions (for real-time applications).
|
||||
Default: False.
|
||||
residual (bool): Use residual connection. Default: True.
|
||||
use_stride_conv (bool): Use TCN backbone optimized for
|
||||
single-frame batching, i.e. where batches have input length =
|
||||
receptive field, and output length = 1. This implementation
|
||||
replaces dilated convolutions with strided convolutions to avoid
|
||||
generating unused intermediate results. The weights are
|
||||
interchangeable with the reference implementation. Default: False
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: dict(type='Conv1d').
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN1d').
|
||||
max_norm (float|None): if not None, the weight of convolution layers
|
||||
will be clipped to have a maximum norm of max_norm.
|
||||
|
||||
Example:
|
||||
>>> from mmpose.models import TCN
|
||||
>>> import torch
|
||||
>>> self = TCN(in_channels=34)
|
||||
>>> self.eval()
|
||||
>>> inputs = torch.rand(1, 34, 243)
|
||||
>>> level_outputs = self.forward(inputs)
|
||||
>>> for level_out in level_outputs:
|
||||
... print(tuple(level_out.shape))
|
||||
(1, 1024, 235)
|
||||
(1, 1024, 217)
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
stem_channels=1024,
|
||||
num_blocks=2,
|
||||
kernel_sizes=(3, 3, 3),
|
||||
dropout=0.25,
|
||||
causal=False,
|
||||
residual=True,
|
||||
use_stride_conv=False,
|
||||
conv_cfg=dict(type='Conv1d'),
|
||||
norm_cfg=dict(type='BN1d'),
|
||||
max_norm=None):
|
||||
# Protect mutable default arguments
|
||||
conv_cfg = copy.deepcopy(conv_cfg)
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.in_channels = in_channels
|
||||
self.stem_channels = stem_channels
|
||||
self.num_blocks = num_blocks
|
||||
self.kernel_sizes = kernel_sizes
|
||||
self.dropout = dropout
|
||||
self.causal = causal
|
||||
self.residual = residual
|
||||
self.use_stride_conv = use_stride_conv
|
||||
self.max_norm = max_norm
|
||||
|
||||
assert num_blocks == len(kernel_sizes) - 1
|
||||
for ks in kernel_sizes:
|
||||
assert ks % 2 == 1, 'Only odd filter widths are supported.'
|
||||
|
||||
self.expand_conv = ConvModule(
|
||||
in_channels,
|
||||
stem_channels,
|
||||
kernel_size=kernel_sizes[0],
|
||||
stride=kernel_sizes[0] if use_stride_conv else 1,
|
||||
bias='auto',
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg)
|
||||
|
||||
dilation = kernel_sizes[0]
|
||||
self.tcn_blocks = nn.ModuleList()
|
||||
for i in range(1, num_blocks + 1):
|
||||
self.tcn_blocks.append(
|
||||
BasicTemporalBlock(
|
||||
in_channels=stem_channels,
|
||||
out_channels=stem_channels,
|
||||
mid_channels=stem_channels,
|
||||
kernel_size=kernel_sizes[i],
|
||||
dilation=dilation,
|
||||
dropout=dropout,
|
||||
causal=causal,
|
||||
residual=residual,
|
||||
use_stride_conv=use_stride_conv,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg))
|
||||
dilation *= kernel_sizes[i]
|
||||
|
||||
if self.max_norm is not None:
|
||||
# Apply weight norm clip to conv layers
|
||||
weight_clip = WeightNormClipHook(self.max_norm)
|
||||
for module in self.modules():
|
||||
if isinstance(module, nn.modules.conv._ConvNd):
|
||||
weight_clip.register(module)
|
||||
|
||||
self.dropout = nn.Dropout(dropout) if dropout > 0 else None
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
x = self.expand_conv(x)
|
||||
|
||||
if self.dropout is not None:
|
||||
x = self.dropout(x)
|
||||
|
||||
outs = []
|
||||
for i in range(self.num_blocks):
|
||||
x = self.tcn_blocks[i](x)
|
||||
outs.append(x)
|
||||
|
||||
return tuple(outs)
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights."""
|
||||
super().init_weights(pretrained)
|
||||
if pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.modules.conv._ConvNd):
|
||||
kaiming_init(m, mode='fan_in', nonlinearity='relu')
|
||||
elif isinstance(m, _BatchNorm):
|
||||
constant_init(m, 1)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from .channel_shuffle import channel_shuffle
|
||||
from .inverted_residual import InvertedResidual
|
||||
from .make_divisible import make_divisible
|
||||
from .se_layer import SELayer
|
||||
from .utils import load_checkpoint
|
||||
|
||||
__all__ = [
|
||||
'channel_shuffle', 'make_divisible', 'InvertedResidual', 'SELayer',
|
||||
'load_checkpoint'
|
||||
]
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch
|
||||
|
||||
|
||||
def channel_shuffle(x, groups):
|
||||
"""Channel Shuffle operation.
|
||||
|
||||
This function enables cross-group information flow for multiple groups
|
||||
convolution layers.
|
||||
|
||||
Args:
|
||||
x (Tensor): The input tensor.
|
||||
groups (int): The number of groups to divide the input tensor
|
||||
in the channel dimension.
|
||||
|
||||
Returns:
|
||||
Tensor: The output tensor after channel shuffle operation.
|
||||
"""
|
||||
|
||||
batch_size, num_channels, height, width = x.size()
|
||||
assert (num_channels % groups == 0), ('num_channels should be '
|
||||
'divisible by groups')
|
||||
channels_per_group = num_channels // groups
|
||||
|
||||
x = x.view(batch_size, groups, channels_per_group, height, width)
|
||||
x = torch.transpose(x, 1, 2).contiguous()
|
||||
x = x.view(batch_size, -1, height, width)
|
||||
|
||||
return x
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch.nn as nn
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import ConvModule
|
||||
|
||||
from .se_layer import SELayer
|
||||
|
||||
|
||||
class InvertedResidual(nn.Module):
|
||||
"""Inverted Residual Block.
|
||||
|
||||
Args:
|
||||
in_channels (int): The input channels of this Module.
|
||||
out_channels (int): The output channels of this Module.
|
||||
mid_channels (int): The input channels of the depthwise convolution.
|
||||
kernel_size (int): The kernel size of the depthwise convolution.
|
||||
Default: 3.
|
||||
groups (None or int): The group number of the depthwise convolution.
|
||||
Default: None, which means group number = mid_channels.
|
||||
stride (int): The stride of the depthwise convolution. Default: 1.
|
||||
se_cfg (dict): Config dict for se layer. Default: None, which means no
|
||||
se layer.
|
||||
with_expand_conv (bool): Use expand conv or not. If set False,
|
||||
mid_channels must be the same with in_channels.
|
||||
Default: True.
|
||||
conv_cfg (dict): Config dict for convolution layer. Default: None,
|
||||
which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
act_cfg (dict): Config dict for activation layer.
|
||||
Default: dict(type='ReLU').
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
|
||||
Returns:
|
||||
Tensor: The output tensor.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
mid_channels,
|
||||
kernel_size=3,
|
||||
groups=None,
|
||||
stride=1,
|
||||
se_cfg=None,
|
||||
with_expand_conv=True,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
act_cfg=dict(type='ReLU'),
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
act_cfg = copy.deepcopy(act_cfg)
|
||||
super().__init__()
|
||||
self.with_res_shortcut = (stride == 1 and in_channels == out_channels)
|
||||
assert stride in [1, 2]
|
||||
self.with_cp = with_cp
|
||||
self.with_se = se_cfg is not None
|
||||
self.with_expand_conv = with_expand_conv
|
||||
|
||||
if groups is None:
|
||||
groups = mid_channels
|
||||
|
||||
if self.with_se:
|
||||
assert isinstance(se_cfg, dict)
|
||||
if not self.with_expand_conv:
|
||||
assert mid_channels == in_channels
|
||||
|
||||
if self.with_expand_conv:
|
||||
self.expand_conv = ConvModule(
|
||||
in_channels=in_channels,
|
||||
out_channels=mid_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg)
|
||||
self.depthwise_conv = ConvModule(
|
||||
in_channels=mid_channels,
|
||||
out_channels=mid_channels,
|
||||
kernel_size=kernel_size,
|
||||
stride=stride,
|
||||
padding=kernel_size // 2,
|
||||
groups=groups,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg)
|
||||
if self.with_se:
|
||||
self.se = SELayer(**se_cfg)
|
||||
self.linear_conv = ConvModule(
|
||||
in_channels=mid_channels,
|
||||
out_channels=out_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None)
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
def _inner_forward(x):
|
||||
out = x
|
||||
|
||||
if self.with_expand_conv:
|
||||
out = self.expand_conv(out)
|
||||
|
||||
out = self.depthwise_conv(out)
|
||||
|
||||
if self.with_se:
|
||||
out = self.se(out)
|
||||
|
||||
out = self.linear_conv(out)
|
||||
|
||||
if self.with_res_shortcut:
|
||||
return x + out
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
return out
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
def make_divisible(value, divisor, min_value=None, min_ratio=0.9):
|
||||
"""Make divisible function.
|
||||
|
||||
This function rounds the channel number down to the nearest value that can
|
||||
be divisible by the divisor.
|
||||
|
||||
Args:
|
||||
value (int): The original channel number.
|
||||
divisor (int): The divisor to fully divide the channel number.
|
||||
min_value (int, optional): The minimum value of the output channel.
|
||||
Default: None, means that the minimum value equal to the divisor.
|
||||
min_ratio (float, optional): The minimum ratio of the rounded channel
|
||||
number to the original channel number. Default: 0.9.
|
||||
Returns:
|
||||
int: The modified output channel number
|
||||
"""
|
||||
|
||||
if min_value is None:
|
||||
min_value = divisor
|
||||
new_value = max(min_value, int(value + divisor / 2) // divisor * divisor)
|
||||
# Make sure that round down does not go down by more than (1-min_ratio).
|
||||
if new_value < min_ratio * value:
|
||||
new_value += divisor
|
||||
return new_value
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import mmcv
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule
|
||||
|
||||
|
||||
class SELayer(nn.Module):
|
||||
"""Squeeze-and-Excitation Module.
|
||||
|
||||
Args:
|
||||
channels (int): The input (and output) channels of the SE layer.
|
||||
ratio (int): Squeeze ratio in SELayer, the intermediate channel will be
|
||||
``int(channels/ratio)``. Default: 16.
|
||||
conv_cfg (None or dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
act_cfg (dict or Sequence[dict]): Config dict for activation layer.
|
||||
If act_cfg is a dict, two activation layers will be configurated
|
||||
by this dict. If act_cfg is a sequence of dicts, the first
|
||||
activation layer will be configurated by the first dict and the
|
||||
second activation layer will be configurated by the second dict.
|
||||
Default: (dict(type='ReLU'), dict(type='Sigmoid'))
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
channels,
|
||||
ratio=16,
|
||||
conv_cfg=None,
|
||||
act_cfg=(dict(type='ReLU'), dict(type='Sigmoid'))):
|
||||
super().__init__()
|
||||
if isinstance(act_cfg, dict):
|
||||
act_cfg = (act_cfg, act_cfg)
|
||||
assert len(act_cfg) == 2
|
||||
assert mmcv.is_tuple_of(act_cfg, dict)
|
||||
self.global_avgpool = nn.AdaptiveAvgPool2d(1)
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=channels,
|
||||
out_channels=int(channels / ratio),
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
act_cfg=act_cfg[0])
|
||||
self.conv2 = ConvModule(
|
||||
in_channels=int(channels / ratio),
|
||||
out_channels=channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
act_cfg=act_cfg[1])
|
||||
|
||||
def forward(self, x):
|
||||
out = self.global_avgpool(x)
|
||||
out = self.conv1(out)
|
||||
out = self.conv2(out)
|
||||
return x * out
|
||||
@@ -0,0 +1,87 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from collections import OrderedDict
|
||||
|
||||
from mmcv.runner.checkpoint import _load_checkpoint, load_state_dict
|
||||
|
||||
|
||||
def load_checkpoint(model,
|
||||
filename,
|
||||
map_location='cpu',
|
||||
strict=False,
|
||||
logger=None):
|
||||
"""Load checkpoint from a file or URI.
|
||||
|
||||
Args:
|
||||
model (Module): Module to load checkpoint.
|
||||
filename (str): Accept local filepath, URL, ``torchvision://xxx``,
|
||||
``open-mmlab://xxx``.
|
||||
map_location (str): Same as :func:`torch.load`.
|
||||
strict (bool): Whether to allow different params for the model and
|
||||
checkpoint.
|
||||
logger (:mod:`logging.Logger` or None): The logger for error message.
|
||||
|
||||
Returns:
|
||||
dict or OrderedDict: The loaded checkpoint.
|
||||
"""
|
||||
checkpoint = _load_checkpoint(filename, map_location)
|
||||
# OrderedDict is a subclass of dict
|
||||
if not isinstance(checkpoint, dict):
|
||||
raise RuntimeError(
|
||||
f'No state_dict found in checkpoint file {filename}')
|
||||
# get state_dict from checkpoint
|
||||
if 'state_dict' in checkpoint:
|
||||
state_dict_tmp = checkpoint['state_dict']
|
||||
else:
|
||||
state_dict_tmp = checkpoint
|
||||
|
||||
state_dict = OrderedDict()
|
||||
# strip prefix of state_dict
|
||||
for k, v in state_dict_tmp.items():
|
||||
if k.startswith('module.backbone.'):
|
||||
state_dict[k[16:]] = v
|
||||
elif k.startswith('module.'):
|
||||
state_dict[k[7:]] = v
|
||||
elif k.startswith('backbone.'):
|
||||
state_dict[k[9:]] = v
|
||||
else:
|
||||
state_dict[k] = v
|
||||
# load state_dict
|
||||
load_state_dict(model, state_dict, strict, logger)
|
||||
return checkpoint
|
||||
|
||||
|
||||
def get_state_dict(filename, map_location='cpu'):
|
||||
"""Get state_dict from a file or URI.
|
||||
|
||||
Args:
|
||||
filename (str): Accept local filepath, URL, ``torchvision://xxx``,
|
||||
``open-mmlab://xxx``.
|
||||
map_location (str): Same as :func:`torch.load`.
|
||||
|
||||
Returns:
|
||||
OrderedDict: The state_dict.
|
||||
"""
|
||||
checkpoint = _load_checkpoint(filename, map_location)
|
||||
# OrderedDict is a subclass of dict
|
||||
if not isinstance(checkpoint, dict):
|
||||
raise RuntimeError(
|
||||
f'No state_dict found in checkpoint file {filename}')
|
||||
# get state_dict from checkpoint
|
||||
if 'state_dict' in checkpoint:
|
||||
state_dict_tmp = checkpoint['state_dict']
|
||||
else:
|
||||
state_dict_tmp = checkpoint
|
||||
|
||||
state_dict = OrderedDict()
|
||||
# strip prefix of state_dict
|
||||
for k, v in state_dict_tmp.items():
|
||||
if k.startswith('module.backbone.'):
|
||||
state_dict[k[16:]] = v
|
||||
elif k.startswith('module.'):
|
||||
state_dict[k[7:]] = v
|
||||
elif k.startswith('backbone.'):
|
||||
state_dict[k[9:]] = v
|
||||
else:
|
||||
state_dict[k] = v
|
||||
|
||||
return state_dict
|
||||
@@ -0,0 +1,257 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright and License Information
|
||||
# Adapted from
|
||||
# https://github.com/microsoft/voxelpose-pytorch/blob/main/lib/models/v2v_net.py
|
||||
# Original Licence: MIT License
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from mmcv.cnn import ConvModule
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
|
||||
class Basic3DBlock(nn.Module):
|
||||
"""A basic 3D convolutional block.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
kernel_size (int): Kernel size of the convolution operation
|
||||
conv_cfg (dict): Dictionary to construct and config conv layer.
|
||||
Default: dict(type='Conv3d')
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN3d')
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size,
|
||||
conv_cfg=dict(type='Conv3d'),
|
||||
norm_cfg=dict(type='BN3d')):
|
||||
super(Basic3DBlock, self).__init__()
|
||||
self.block = ConvModule(
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size,
|
||||
stride=1,
|
||||
padding=((kernel_size - 1) // 2),
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
bias=True)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
return self.block(x)
|
||||
|
||||
|
||||
class Res3DBlock(nn.Module):
|
||||
"""A residual 3D convolutional block.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
kernel_size (int): Kernel size of the convolution operation
|
||||
Default: 3
|
||||
conv_cfg (dict): Dictionary to construct and config conv layer.
|
||||
Default: dict(type='Conv3d')
|
||||
norm_cfg (dict): Dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN3d')
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size=3,
|
||||
conv_cfg=dict(type='Conv3d'),
|
||||
norm_cfg=dict(type='BN3d')):
|
||||
super(Res3DBlock, self).__init__()
|
||||
self.res_branch = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size,
|
||||
stride=1,
|
||||
padding=((kernel_size - 1) // 2),
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
bias=True),
|
||||
ConvModule(
|
||||
out_channels,
|
||||
out_channels,
|
||||
kernel_size,
|
||||
stride=1,
|
||||
padding=((kernel_size - 1) // 2),
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None,
|
||||
bias=True))
|
||||
|
||||
if in_channels == out_channels:
|
||||
self.skip_con = nn.Sequential()
|
||||
else:
|
||||
self.skip_con = ConvModule(
|
||||
in_channels,
|
||||
out_channels,
|
||||
1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=None,
|
||||
bias=True)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
res = self.res_branch(x)
|
||||
skip = self.skip_con(x)
|
||||
return F.relu(res + skip, True)
|
||||
|
||||
|
||||
class Pool3DBlock(nn.Module):
|
||||
"""A 3D max-pool block.
|
||||
|
||||
Args:
|
||||
pool_size (int): Pool size of the 3D max-pool layer
|
||||
"""
|
||||
|
||||
def __init__(self, pool_size):
|
||||
super(Pool3DBlock, self).__init__()
|
||||
self.pool_size = pool_size
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
return F.max_pool3d(
|
||||
x, kernel_size=self.pool_size, stride=self.pool_size)
|
||||
|
||||
|
||||
class Upsample3DBlock(nn.Module):
|
||||
"""A 3D upsample block.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
kernel_size (int): Kernel size of the transposed convolution operation.
|
||||
Default: 2
|
||||
stride (int): Kernel size of the transposed convolution operation.
|
||||
Default: 2
|
||||
"""
|
||||
|
||||
def __init__(self, in_channels, out_channels, kernel_size=2, stride=2):
|
||||
super(Upsample3DBlock, self).__init__()
|
||||
assert kernel_size == 2
|
||||
assert stride == 2
|
||||
self.block = nn.Sequential(
|
||||
nn.ConvTranspose3d(
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size=kernel_size,
|
||||
stride=stride,
|
||||
padding=0,
|
||||
output_padding=0), nn.BatchNorm3d(out_channels), nn.ReLU(True))
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
return self.block(x)
|
||||
|
||||
|
||||
class EncoderDecorder(nn.Module):
|
||||
"""An encoder-decoder block.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block
|
||||
"""
|
||||
|
||||
def __init__(self, in_channels=32):
|
||||
super(EncoderDecorder, self).__init__()
|
||||
|
||||
self.encoder_pool1 = Pool3DBlock(2)
|
||||
self.encoder_res1 = Res3DBlock(in_channels, in_channels * 2)
|
||||
self.encoder_pool2 = Pool3DBlock(2)
|
||||
self.encoder_res2 = Res3DBlock(in_channels * 2, in_channels * 4)
|
||||
|
||||
self.mid_res = Res3DBlock(in_channels * 4, in_channels * 4)
|
||||
|
||||
self.decoder_res2 = Res3DBlock(in_channels * 4, in_channels * 4)
|
||||
self.decoder_upsample2 = Upsample3DBlock(in_channels * 4,
|
||||
in_channels * 2, 2, 2)
|
||||
self.decoder_res1 = Res3DBlock(in_channels * 2, in_channels * 2)
|
||||
self.decoder_upsample1 = Upsample3DBlock(in_channels * 2, in_channels,
|
||||
2, 2)
|
||||
|
||||
self.skip_res1 = Res3DBlock(in_channels, in_channels)
|
||||
self.skip_res2 = Res3DBlock(in_channels * 2, in_channels * 2)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
skip_x1 = self.skip_res1(x)
|
||||
x = self.encoder_pool1(x)
|
||||
x = self.encoder_res1(x)
|
||||
|
||||
skip_x2 = self.skip_res2(x)
|
||||
x = self.encoder_pool2(x)
|
||||
x = self.encoder_res2(x)
|
||||
|
||||
x = self.mid_res(x)
|
||||
|
||||
x = self.decoder_res2(x)
|
||||
x = self.decoder_upsample2(x)
|
||||
x = x + skip_x2
|
||||
|
||||
x = self.decoder_res1(x)
|
||||
x = self.decoder_upsample1(x)
|
||||
x = x + skip_x1
|
||||
|
||||
return x
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class V2VNet(BaseBackbone):
|
||||
"""V2VNet.
|
||||
|
||||
Please refer to the `paper <https://arxiv.org/abs/1711.07399>`
|
||||
for details.
|
||||
|
||||
Args:
|
||||
input_channels (int):
|
||||
Number of channels of the input feature volume.
|
||||
output_channels (int):
|
||||
Number of channels of the output volume.
|
||||
mid_channels (int):
|
||||
Input and output channels of the encoder-decoder block.
|
||||
"""
|
||||
|
||||
def __init__(self, input_channels, output_channels, mid_channels=32):
|
||||
super(V2VNet, self).__init__()
|
||||
|
||||
self.front_layers = nn.Sequential(
|
||||
Basic3DBlock(input_channels, mid_channels // 2, 7),
|
||||
Res3DBlock(mid_channels // 2, mid_channels),
|
||||
)
|
||||
|
||||
self.encoder_decoder = EncoderDecorder(in_channels=mid_channels)
|
||||
|
||||
self.output_layer = nn.Conv3d(
|
||||
mid_channels, output_channels, kernel_size=1, stride=1, padding=0)
|
||||
|
||||
self._initialize_weights()
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
x = self.front_layers(x)
|
||||
x = self.encoder_decoder(x)
|
||||
x = self.output_layer(x)
|
||||
|
||||
return x
|
||||
|
||||
def _initialize_weights(self):
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv3d):
|
||||
nn.init.normal_(m.weight, 0, 0.001)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.ConvTranspose3d):
|
||||
nn.init.normal_(m.weight, 0, 0.001)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
@@ -0,0 +1,193 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule, constant_init, kaiming_init, normal_init
|
||||
from mmcv.utils.parrots_wrapper import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
|
||||
def make_vgg_layer(in_channels,
|
||||
out_channels,
|
||||
num_blocks,
|
||||
conv_cfg=None,
|
||||
norm_cfg=None,
|
||||
act_cfg=dict(type='ReLU'),
|
||||
dilation=1,
|
||||
with_norm=False,
|
||||
ceil_mode=False):
|
||||
layers = []
|
||||
for _ in range(num_blocks):
|
||||
layer = ConvModule(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels,
|
||||
kernel_size=3,
|
||||
dilation=dilation,
|
||||
padding=dilation,
|
||||
bias=True,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg)
|
||||
layers.append(layer)
|
||||
in_channels = out_channels
|
||||
layers.append(nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=ceil_mode))
|
||||
|
||||
return layers
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class VGG(BaseBackbone):
|
||||
"""VGG backbone.
|
||||
|
||||
Args:
|
||||
depth (int): Depth of vgg, from {11, 13, 16, 19}.
|
||||
with_norm (bool): Use BatchNorm or not.
|
||||
num_classes (int): number of classes for classification.
|
||||
num_stages (int): VGG stages, normally 5.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
out_indices (Sequence[int]): Output from which stages. If only one
|
||||
stage is specified, a single tensor (feature map) is returned,
|
||||
otherwise multiple stages are specified, a tuple of tensors will
|
||||
be returned. When it is None, the default behavior depends on
|
||||
whether num_classes is specified. If num_classes <= 0, the default
|
||||
value is (4, ), outputting the last feature map before classifier.
|
||||
If num_classes > 0, the default value is (5, ), outputting the
|
||||
classification score. Default: None.
|
||||
frozen_stages (int): Stages to be frozen (all param fixed). -1 means
|
||||
not freezing any parameters.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
ceil_mode (bool): Whether to use ceil_mode of MaxPool. Default: False.
|
||||
with_last_pool (bool): Whether to keep the last pooling before
|
||||
classifier. Default: True.
|
||||
"""
|
||||
|
||||
# Parameters to build layers. Each element specifies the number of conv in
|
||||
# each stage. For example, VGG11 contains 11 layers with learnable
|
||||
# parameters. 11 is computed as 11 = (1 + 1 + 2 + 2 + 2) + 3,
|
||||
# where 3 indicates the last three fully-connected layers.
|
||||
arch_settings = {
|
||||
11: (1, 1, 2, 2, 2),
|
||||
13: (2, 2, 2, 2, 2),
|
||||
16: (2, 2, 3, 3, 3),
|
||||
19: (2, 2, 4, 4, 4)
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
depth,
|
||||
num_classes=-1,
|
||||
num_stages=5,
|
||||
dilations=(1, 1, 1, 1, 1),
|
||||
out_indices=None,
|
||||
frozen_stages=-1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=None,
|
||||
act_cfg=dict(type='ReLU'),
|
||||
norm_eval=False,
|
||||
ceil_mode=False,
|
||||
with_last_pool=True):
|
||||
super().__init__()
|
||||
if depth not in self.arch_settings:
|
||||
raise KeyError(f'invalid depth {depth} for vgg')
|
||||
assert num_stages >= 1 and num_stages <= 5
|
||||
stage_blocks = self.arch_settings[depth]
|
||||
self.stage_blocks = stage_blocks[:num_stages]
|
||||
assert len(dilations) == num_stages
|
||||
|
||||
self.num_classes = num_classes
|
||||
self.frozen_stages = frozen_stages
|
||||
self.norm_eval = norm_eval
|
||||
with_norm = norm_cfg is not None
|
||||
|
||||
if out_indices is None:
|
||||
out_indices = (5, ) if num_classes > 0 else (4, )
|
||||
assert max(out_indices) <= num_stages
|
||||
self.out_indices = out_indices
|
||||
|
||||
self.in_channels = 3
|
||||
start_idx = 0
|
||||
vgg_layers = []
|
||||
self.range_sub_modules = []
|
||||
for i, num_blocks in enumerate(self.stage_blocks):
|
||||
num_modules = num_blocks + 1
|
||||
end_idx = start_idx + num_modules
|
||||
dilation = dilations[i]
|
||||
out_channels = 64 * 2**i if i < 4 else 512
|
||||
vgg_layer = make_vgg_layer(
|
||||
self.in_channels,
|
||||
out_channels,
|
||||
num_blocks,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=act_cfg,
|
||||
dilation=dilation,
|
||||
with_norm=with_norm,
|
||||
ceil_mode=ceil_mode)
|
||||
vgg_layers.extend(vgg_layer)
|
||||
self.in_channels = out_channels
|
||||
self.range_sub_modules.append([start_idx, end_idx])
|
||||
start_idx = end_idx
|
||||
if not with_last_pool:
|
||||
vgg_layers.pop(-1)
|
||||
self.range_sub_modules[-1][1] -= 1
|
||||
self.module_name = 'features'
|
||||
self.add_module(self.module_name, nn.Sequential(*vgg_layers))
|
||||
|
||||
if self.num_classes > 0:
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Linear(512 * 7 * 7, 4096),
|
||||
nn.ReLU(True),
|
||||
nn.Dropout(),
|
||||
nn.Linear(4096, 4096),
|
||||
nn.ReLU(True),
|
||||
nn.Dropout(),
|
||||
nn.Linear(4096, num_classes),
|
||||
)
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
super().init_weights(pretrained)
|
||||
if pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
kaiming_init(m)
|
||||
elif isinstance(m, _BatchNorm):
|
||||
constant_init(m, 1)
|
||||
elif isinstance(m, nn.Linear):
|
||||
normal_init(m, std=0.01)
|
||||
|
||||
def forward(self, x):
|
||||
outs = []
|
||||
vgg_layers = getattr(self, self.module_name)
|
||||
for i in range(len(self.stage_blocks)):
|
||||
for j in range(*self.range_sub_modules[i]):
|
||||
vgg_layer = vgg_layers[j]
|
||||
x = vgg_layer(x)
|
||||
if i in self.out_indices:
|
||||
outs.append(x)
|
||||
if self.num_classes > 0:
|
||||
x = x.view(x.size(0), -1)
|
||||
x = self.classifier(x)
|
||||
outs.append(x)
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
else:
|
||||
return tuple(outs)
|
||||
|
||||
def _freeze_stages(self):
|
||||
vgg_layers = getattr(self, self.module_name)
|
||||
for i in range(self.frozen_stages):
|
||||
for j in range(*self.range_sub_modules[i]):
|
||||
m = vgg_layers[j]
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def train(self, mode=True):
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
# trick: eval have effect on BatchNorm only
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import torch.nn as nn
|
||||
from mmcv.cnn import ConvModule
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
from .utils import InvertedResidual, load_checkpoint
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ViPNAS_MobileNetV3(BaseBackbone):
|
||||
"""ViPNAS_MobileNetV3 backbone.
|
||||
|
||||
"ViPNAS: Efficient Video Pose Estimation via Neural Architecture Search"
|
||||
More details can be found in the `paper
|
||||
<https://arxiv.org/abs/2105.10154>`__ .
|
||||
|
||||
Args:
|
||||
wid (list(int)): Searched width config for each stage.
|
||||
expan (list(int)): Searched expansion ratio config for each stage.
|
||||
dep (list(int)): Searched depth config for each stage.
|
||||
ks (list(int)): Searched kernel size config for each stage.
|
||||
group (list(int)): Searched group number config for each stage.
|
||||
att (list(bool)): Searched attention config for each stage.
|
||||
stride (list(int)): Stride config for each stage.
|
||||
act (list(dict)): Activation config for each stage.
|
||||
conv_cfg (dict): Config dict for convolution layer.
|
||||
Default: None, which means using conv2d.
|
||||
norm_cfg (dict): Config dict for normalization layer.
|
||||
Default: dict(type='BN').
|
||||
frozen_stages (int): Stages to be frozen (all param fixed).
|
||||
Default: -1, which means not freezing any parameters.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save
|
||||
some memory while slowing down the training speed.
|
||||
Default: False.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
wid=[16, 16, 24, 40, 80, 112, 160],
|
||||
expan=[None, 1, 5, 4, 5, 5, 6],
|
||||
dep=[None, 1, 4, 4, 4, 4, 4],
|
||||
ks=[3, 3, 7, 7, 5, 7, 5],
|
||||
group=[None, 8, 120, 20, 100, 280, 240],
|
||||
att=[None, True, True, False, True, True, True],
|
||||
stride=[2, 1, 2, 2, 2, 1, 2],
|
||||
act=[
|
||||
'HSwish', 'ReLU', 'ReLU', 'ReLU', 'HSwish', 'HSwish',
|
||||
'HSwish'
|
||||
],
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
frozen_stages=-1,
|
||||
norm_eval=False,
|
||||
with_cp=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
self.wid = wid
|
||||
self.expan = expan
|
||||
self.dep = dep
|
||||
self.ks = ks
|
||||
self.group = group
|
||||
self.att = att
|
||||
self.stride = stride
|
||||
self.act = act
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.frozen_stages = frozen_stages
|
||||
self.norm_eval = norm_eval
|
||||
self.with_cp = with_cp
|
||||
|
||||
self.conv1 = ConvModule(
|
||||
in_channels=3,
|
||||
out_channels=self.wid[0],
|
||||
kernel_size=self.ks[0],
|
||||
stride=self.stride[0],
|
||||
padding=self.ks[0] // 2,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
act_cfg=dict(type=self.act[0]))
|
||||
|
||||
self.layers = self._make_layer()
|
||||
|
||||
def _make_layer(self):
|
||||
layers = []
|
||||
layer_index = 0
|
||||
for i, dep in enumerate(self.dep[1:]):
|
||||
mid_channels = self.wid[i + 1] * self.expan[i + 1]
|
||||
|
||||
if self.att[i + 1]:
|
||||
se_cfg = dict(
|
||||
channels=mid_channels,
|
||||
ratio=4,
|
||||
act_cfg=(dict(type='ReLU'), dict(type='HSigmoid')))
|
||||
else:
|
||||
se_cfg = None
|
||||
|
||||
if self.expan[i + 1] == 1:
|
||||
with_expand_conv = False
|
||||
else:
|
||||
with_expand_conv = True
|
||||
|
||||
for j in range(dep):
|
||||
if j == 0:
|
||||
stride = self.stride[i + 1]
|
||||
in_channels = self.wid[i]
|
||||
else:
|
||||
stride = 1
|
||||
in_channels = self.wid[i + 1]
|
||||
|
||||
layer = InvertedResidual(
|
||||
in_channels=in_channels,
|
||||
out_channels=self.wid[i + 1],
|
||||
mid_channels=mid_channels,
|
||||
kernel_size=self.ks[i + 1],
|
||||
groups=self.group[i + 1],
|
||||
stride=stride,
|
||||
se_cfg=se_cfg,
|
||||
with_expand_conv=with_expand_conv,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
act_cfg=dict(type=self.act[i + 1]),
|
||||
with_cp=self.with_cp)
|
||||
layer_index += 1
|
||||
layer_name = f'layer{layer_index}'
|
||||
self.add_module(layer_name, layer)
|
||||
layers.append(layer_name)
|
||||
return layers
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
if isinstance(pretrained, str):
|
||||
logger = logging.getLogger()
|
||||
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
||||
elif pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.normal_(m.weight, std=0.001)
|
||||
for name, _ in m.named_parameters():
|
||||
if name in ['bias']:
|
||||
nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
nn.init.constant_(m.weight, 1)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
else:
|
||||
raise TypeError('pretrained must be a str or None')
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
|
||||
for i, layer_name in enumerate(self.layers):
|
||||
layer = getattr(self, layer_name)
|
||||
x = layer(x)
|
||||
|
||||
return x
|
||||
|
||||
def _freeze_stages(self):
|
||||
if self.frozen_stages >= 0:
|
||||
for param in self.conv1.parameters():
|
||||
param.requires_grad = False
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
layer = getattr(self, f'layer{i}')
|
||||
layer.eval()
|
||||
for param in layer.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def train(self, mode=True):
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
+589
@@ -0,0 +1,589 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
|
||||
import torch.nn as nn
|
||||
import torch.utils.checkpoint as cp
|
||||
from mmcv.cnn import ConvModule, build_conv_layer, build_norm_layer
|
||||
from mmcv.cnn.bricks import ContextBlock
|
||||
from mmcv.utils.parrots_wrapper import _BatchNorm
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
|
||||
class ViPNAS_Bottleneck(nn.Module):
|
||||
"""Bottleneck block for ViPNAS_ResNet.
|
||||
|
||||
Args:
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
expansion (int): The ratio of ``out_channels/mid_channels`` where
|
||||
``mid_channels`` is the input/output channels of conv2. Default: 4.
|
||||
stride (int): stride of the block. Default: 1
|
||||
dilation (int): dilation of convolution. Default: 1
|
||||
downsample (nn.Module): downsample operation on identity branch.
|
||||
Default: None.
|
||||
style (str): ``"pytorch"`` or ``"caffe"``. If set to "pytorch", the
|
||||
stride-two layer is the 3x3 conv layer, otherwise the stride-two
|
||||
layer is the first 1x1 conv layer. Default: "pytorch".
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed.
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
kernel_size (int): kernel size of conv2 searched in ViPANS.
|
||||
groups (int): group number of conv2 searched in ViPNAS.
|
||||
attention (bool): whether to use attention module in the end of
|
||||
the block.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
in_channels,
|
||||
out_channels,
|
||||
expansion=4,
|
||||
stride=1,
|
||||
dilation=1,
|
||||
downsample=None,
|
||||
style='pytorch',
|
||||
with_cp=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
kernel_size=3,
|
||||
groups=1,
|
||||
attention=False):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
assert style in ['pytorch', 'caffe']
|
||||
|
||||
self.in_channels = in_channels
|
||||
self.out_channels = out_channels
|
||||
self.expansion = expansion
|
||||
assert out_channels % expansion == 0
|
||||
self.mid_channels = out_channels // expansion
|
||||
self.stride = stride
|
||||
self.dilation = dilation
|
||||
self.style = style
|
||||
self.with_cp = with_cp
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
|
||||
if self.style == 'pytorch':
|
||||
self.conv1_stride = 1
|
||||
self.conv2_stride = stride
|
||||
else:
|
||||
self.conv1_stride = stride
|
||||
self.conv2_stride = 1
|
||||
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
norm_cfg, self.mid_channels, postfix=1)
|
||||
self.norm2_name, norm2 = build_norm_layer(
|
||||
norm_cfg, self.mid_channels, postfix=2)
|
||||
self.norm3_name, norm3 = build_norm_layer(
|
||||
norm_cfg, out_channels, postfix=3)
|
||||
|
||||
self.conv1 = build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=1,
|
||||
stride=self.conv1_stride,
|
||||
bias=False)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.conv2 = build_conv_layer(
|
||||
conv_cfg,
|
||||
self.mid_channels,
|
||||
self.mid_channels,
|
||||
kernel_size=kernel_size,
|
||||
stride=self.conv2_stride,
|
||||
padding=kernel_size // 2,
|
||||
groups=groups,
|
||||
dilation=dilation,
|
||||
bias=False)
|
||||
|
||||
self.add_module(self.norm2_name, norm2)
|
||||
self.conv3 = build_conv_layer(
|
||||
conv_cfg,
|
||||
self.mid_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
bias=False)
|
||||
self.add_module(self.norm3_name, norm3)
|
||||
|
||||
if attention:
|
||||
self.attention = ContextBlock(out_channels,
|
||||
max(1.0 / 16, 16.0 / out_channels))
|
||||
else:
|
||||
self.attention = None
|
||||
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.downsample = downsample
|
||||
|
||||
@property
|
||||
def norm1(self):
|
||||
"""nn.Module: the normalization layer named "norm1" """
|
||||
return getattr(self, self.norm1_name)
|
||||
|
||||
@property
|
||||
def norm2(self):
|
||||
"""nn.Module: the normalization layer named "norm2" """
|
||||
return getattr(self, self.norm2_name)
|
||||
|
||||
@property
|
||||
def norm3(self):
|
||||
"""nn.Module: the normalization layer named "norm3" """
|
||||
return getattr(self, self.norm3_name)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
|
||||
def _inner_forward(x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.norm1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.norm2(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv3(out)
|
||||
out = self.norm3(out)
|
||||
|
||||
if self.attention is not None:
|
||||
out = self.attention(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
|
||||
return out
|
||||
|
||||
if self.with_cp and x.requires_grad:
|
||||
out = cp.checkpoint(_inner_forward, x)
|
||||
else:
|
||||
out = _inner_forward(x)
|
||||
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def get_expansion(block, expansion=None):
|
||||
"""Get the expansion of a residual block.
|
||||
|
||||
The block expansion will be obtained by the following order:
|
||||
|
||||
1. If ``expansion`` is given, just return it.
|
||||
2. If ``block`` has the attribute ``expansion``, then return
|
||||
``block.expansion``.
|
||||
3. Return the default value according the the block type:
|
||||
4 for ``ViPNAS_Bottleneck``.
|
||||
|
||||
Args:
|
||||
block (class): The block class.
|
||||
expansion (int | None): The given expansion ratio.
|
||||
|
||||
Returns:
|
||||
int: The expansion of the block.
|
||||
"""
|
||||
if isinstance(expansion, int):
|
||||
assert expansion > 0
|
||||
elif expansion is None:
|
||||
if hasattr(block, 'expansion'):
|
||||
expansion = block.expansion
|
||||
elif issubclass(block, ViPNAS_Bottleneck):
|
||||
expansion = 1
|
||||
else:
|
||||
raise TypeError(f'expansion is not specified for {block.__name__}')
|
||||
else:
|
||||
raise TypeError('expansion must be an integer or None')
|
||||
|
||||
return expansion
|
||||
|
||||
|
||||
class ViPNAS_ResLayer(nn.Sequential):
|
||||
"""ViPNAS_ResLayer to build ResNet style backbone.
|
||||
|
||||
Args:
|
||||
block (nn.Module): Residual block used to build ViPNAS ResLayer.
|
||||
num_blocks (int): Number of blocks.
|
||||
in_channels (int): Input channels of this block.
|
||||
out_channels (int): Output channels of this block.
|
||||
expansion (int, optional): The expansion for BasicBlock/Bottleneck.
|
||||
If not specified, it will firstly be obtained via
|
||||
``block.expansion``. If the block has no attribute "expansion",
|
||||
the following default values will be used: 1 for BasicBlock and
|
||||
4 for Bottleneck. Default: None.
|
||||
stride (int): stride of the first block. Default: 1.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False
|
||||
conv_cfg (dict): dictionary to construct and config conv layer.
|
||||
Default: None
|
||||
norm_cfg (dict): dictionary to construct and config norm layer.
|
||||
Default: dict(type='BN')
|
||||
downsample_first (bool): Downsample at the first block or last block.
|
||||
False for Hourglass, True for ResNet. Default: True
|
||||
kernel_size (int): Kernel Size of the corresponding convolution layer
|
||||
searched in the block.
|
||||
groups (int): Group number of the corresponding convolution layer
|
||||
searched in the block.
|
||||
attention (bool): Whether to use attention module in the end of the
|
||||
block.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
block,
|
||||
num_blocks,
|
||||
in_channels,
|
||||
out_channels,
|
||||
expansion=None,
|
||||
stride=1,
|
||||
avg_down=False,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN'),
|
||||
downsample_first=True,
|
||||
kernel_size=3,
|
||||
groups=1,
|
||||
attention=False,
|
||||
**kwargs):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
self.block = block
|
||||
self.expansion = get_expansion(block, expansion)
|
||||
|
||||
downsample = None
|
||||
if stride != 1 or in_channels != out_channels:
|
||||
downsample = []
|
||||
conv_stride = stride
|
||||
if avg_down and stride != 1:
|
||||
conv_stride = 1
|
||||
downsample.append(
|
||||
nn.AvgPool2d(
|
||||
kernel_size=stride,
|
||||
stride=stride,
|
||||
ceil_mode=True,
|
||||
count_include_pad=False))
|
||||
downsample.extend([
|
||||
build_conv_layer(
|
||||
conv_cfg,
|
||||
in_channels,
|
||||
out_channels,
|
||||
kernel_size=1,
|
||||
stride=conv_stride,
|
||||
bias=False),
|
||||
build_norm_layer(norm_cfg, out_channels)[1]
|
||||
])
|
||||
downsample = nn.Sequential(*downsample)
|
||||
|
||||
layers = []
|
||||
if downsample_first:
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels,
|
||||
expansion=self.expansion,
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
kernel_size=kernel_size,
|
||||
groups=groups,
|
||||
attention=attention,
|
||||
**kwargs))
|
||||
in_channels = out_channels
|
||||
for _ in range(1, num_blocks):
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels,
|
||||
expansion=self.expansion,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
kernel_size=kernel_size,
|
||||
groups=groups,
|
||||
attention=attention,
|
||||
**kwargs))
|
||||
else: # downsample_first=False is for HourglassModule
|
||||
for i in range(0, num_blocks - 1):
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=in_channels,
|
||||
expansion=self.expansion,
|
||||
stride=1,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
kernel_size=kernel_size,
|
||||
groups=groups,
|
||||
attention=attention,
|
||||
**kwargs))
|
||||
layers.append(
|
||||
block(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels,
|
||||
expansion=self.expansion,
|
||||
stride=stride,
|
||||
downsample=downsample,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
kernel_size=kernel_size,
|
||||
groups=groups,
|
||||
attention=attention,
|
||||
**kwargs))
|
||||
|
||||
super().__init__(*layers)
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ViPNAS_ResNet(BaseBackbone):
|
||||
"""ViPNAS_ResNet backbone.
|
||||
|
||||
"ViPNAS: Efficient Video Pose Estimation via Neural Architecture Search"
|
||||
More details can be found in the `paper
|
||||
<https://arxiv.org/abs/2105.10154>`__ .
|
||||
|
||||
Args:
|
||||
depth (int): Network depth, from {18, 34, 50, 101, 152}.
|
||||
in_channels (int): Number of input image channels. Default: 3.
|
||||
num_stages (int): Stages of the network. Default: 4.
|
||||
strides (Sequence[int]): Strides of the first block of each stage.
|
||||
Default: ``(1, 2, 2, 2)``.
|
||||
dilations (Sequence[int]): Dilation of each stage.
|
||||
Default: ``(1, 1, 1, 1)``.
|
||||
out_indices (Sequence[int]): Output from which stages. If only one
|
||||
stage is specified, a single tensor (feature map) is returned,
|
||||
otherwise multiple stages are specified, a tuple of tensors will
|
||||
be returned. Default: ``(3, )``.
|
||||
style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
|
||||
layer is the 3x3 conv layer, otherwise the stride-two layer is
|
||||
the first 1x1 conv layer.
|
||||
deep_stem (bool): Replace 7x7 conv in input stem with 3 3x3 conv.
|
||||
Default: False.
|
||||
avg_down (bool): Use AvgPool instead of stride conv when
|
||||
downsampling in the bottleneck. Default: False.
|
||||
frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
|
||||
-1 means not freezing any parameters. Default: -1.
|
||||
conv_cfg (dict | None): The config dict for conv layers. Default: None.
|
||||
norm_cfg (dict): The config dict for norm layers.
|
||||
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
||||
freeze running stats (mean and var). Note: Effect on Batch Norm
|
||||
and its variants only. Default: False.
|
||||
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
||||
memory while slowing down the training speed. Default: False.
|
||||
zero_init_residual (bool): Whether to use zero init for last norm layer
|
||||
in resblocks to let them behave as identity. Default: True.
|
||||
wid (list(int)): Searched width config for each stage.
|
||||
expan (list(int)): Searched expansion ratio config for each stage.
|
||||
dep (list(int)): Searched depth config for each stage.
|
||||
ks (list(int)): Searched kernel size config for each stage.
|
||||
group (list(int)): Searched group number config for each stage.
|
||||
att (list(bool)): Searched attention config for each stage.
|
||||
"""
|
||||
|
||||
arch_settings = {
|
||||
50: ViPNAS_Bottleneck,
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
depth,
|
||||
in_channels=3,
|
||||
num_stages=4,
|
||||
strides=(1, 2, 2, 2),
|
||||
dilations=(1, 1, 1, 1),
|
||||
out_indices=(3, ),
|
||||
style='pytorch',
|
||||
deep_stem=False,
|
||||
avg_down=False,
|
||||
frozen_stages=-1,
|
||||
conv_cfg=None,
|
||||
norm_cfg=dict(type='BN', requires_grad=True),
|
||||
norm_eval=False,
|
||||
with_cp=False,
|
||||
zero_init_residual=True,
|
||||
wid=[48, 80, 160, 304, 608],
|
||||
expan=[None, 1, 1, 1, 1],
|
||||
dep=[None, 4, 6, 7, 3],
|
||||
ks=[7, 3, 5, 5, 5],
|
||||
group=[None, 16, 16, 16, 16],
|
||||
att=[None, True, False, True, True]):
|
||||
# Protect mutable default arguments
|
||||
norm_cfg = copy.deepcopy(norm_cfg)
|
||||
super().__init__()
|
||||
if depth not in self.arch_settings:
|
||||
raise KeyError(f'invalid depth {depth} for resnet')
|
||||
self.depth = depth
|
||||
self.stem_channels = dep[0]
|
||||
self.num_stages = num_stages
|
||||
assert 1 <= num_stages <= 4
|
||||
self.strides = strides
|
||||
self.dilations = dilations
|
||||
assert len(strides) == len(dilations) == num_stages
|
||||
self.out_indices = out_indices
|
||||
assert max(out_indices) < num_stages
|
||||
self.style = style
|
||||
self.deep_stem = deep_stem
|
||||
self.avg_down = avg_down
|
||||
self.frozen_stages = frozen_stages
|
||||
self.conv_cfg = conv_cfg
|
||||
self.norm_cfg = norm_cfg
|
||||
self.with_cp = with_cp
|
||||
self.norm_eval = norm_eval
|
||||
self.zero_init_residual = zero_init_residual
|
||||
self.block = self.arch_settings[depth]
|
||||
self.stage_blocks = dep[1:1 + num_stages]
|
||||
|
||||
self._make_stem_layer(in_channels, wid[0], ks[0])
|
||||
|
||||
self.res_layers = []
|
||||
_in_channels = wid[0]
|
||||
for i, num_blocks in enumerate(self.stage_blocks):
|
||||
expansion = get_expansion(self.block, expan[i + 1])
|
||||
_out_channels = wid[i + 1] * expansion
|
||||
stride = strides[i]
|
||||
dilation = dilations[i]
|
||||
res_layer = self.make_res_layer(
|
||||
block=self.block,
|
||||
num_blocks=num_blocks,
|
||||
in_channels=_in_channels,
|
||||
out_channels=_out_channels,
|
||||
expansion=expansion,
|
||||
stride=stride,
|
||||
dilation=dilation,
|
||||
style=self.style,
|
||||
avg_down=self.avg_down,
|
||||
with_cp=with_cp,
|
||||
conv_cfg=conv_cfg,
|
||||
norm_cfg=norm_cfg,
|
||||
kernel_size=ks[i + 1],
|
||||
groups=group[i + 1],
|
||||
attention=att[i + 1])
|
||||
_in_channels = _out_channels
|
||||
layer_name = f'layer{i + 1}'
|
||||
self.add_module(layer_name, res_layer)
|
||||
self.res_layers.append(layer_name)
|
||||
|
||||
self._freeze_stages()
|
||||
|
||||
self.feat_dim = res_layer[-1].out_channels
|
||||
|
||||
def make_res_layer(self, **kwargs):
|
||||
"""Make a ViPNAS ResLayer."""
|
||||
return ViPNAS_ResLayer(**kwargs)
|
||||
|
||||
@property
|
||||
def norm1(self):
|
||||
"""nn.Module: the normalization layer named "norm1" """
|
||||
return getattr(self, self.norm1_name)
|
||||
|
||||
def _make_stem_layer(self, in_channels, stem_channels, kernel_size):
|
||||
"""Make stem layer."""
|
||||
if self.deep_stem:
|
||||
self.stem = nn.Sequential(
|
||||
ConvModule(
|
||||
in_channels,
|
||||
stem_channels // 2,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True),
|
||||
ConvModule(
|
||||
stem_channels // 2,
|
||||
stem_channels // 2,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True),
|
||||
ConvModule(
|
||||
stem_channels // 2,
|
||||
stem_channels,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
conv_cfg=self.conv_cfg,
|
||||
norm_cfg=self.norm_cfg,
|
||||
inplace=True))
|
||||
else:
|
||||
self.conv1 = build_conv_layer(
|
||||
self.conv_cfg,
|
||||
in_channels,
|
||||
stem_channels,
|
||||
kernel_size=kernel_size,
|
||||
stride=2,
|
||||
padding=kernel_size // 2,
|
||||
bias=False)
|
||||
self.norm1_name, norm1 = build_norm_layer(
|
||||
self.norm_cfg, stem_channels, postfix=1)
|
||||
self.add_module(self.norm1_name, norm1)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
|
||||
def _freeze_stages(self):
|
||||
"""Freeze parameters."""
|
||||
if self.frozen_stages >= 0:
|
||||
if self.deep_stem:
|
||||
self.stem.eval()
|
||||
for param in self.stem.parameters():
|
||||
param.requires_grad = False
|
||||
else:
|
||||
self.norm1.eval()
|
||||
for m in [self.conv1, self.norm1]:
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
m = getattr(self, f'layer{i}')
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize model weights."""
|
||||
super().init_weights(pretrained)
|
||||
if pretrained is None:
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.normal_(m.weight, std=0.001)
|
||||
for name, _ in m.named_parameters():
|
||||
if name in ['bias']:
|
||||
nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
nn.init.constant_(m.weight, 1)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
|
||||
def forward(self, x):
|
||||
"""Forward function."""
|
||||
if self.deep_stem:
|
||||
x = self.stem(x)
|
||||
else:
|
||||
x = self.conv1(x)
|
||||
x = self.norm1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
outs = []
|
||||
for i, layer_name in enumerate(self.res_layers):
|
||||
res_layer = getattr(self, layer_name)
|
||||
x = res_layer(x)
|
||||
if i in self.out_indices:
|
||||
outs.append(x)
|
||||
if len(outs) == 1:
|
||||
return outs[0]
|
||||
return tuple(outs)
|
||||
|
||||
def train(self, mode=True):
|
||||
"""Convert the model into training mode."""
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
if mode and self.norm_eval:
|
||||
for m in self.modules():
|
||||
# trick: eval have effect on BatchNorm only
|
||||
if isinstance(m, _BatchNorm):
|
||||
m.eval()
|
||||
@@ -0,0 +1,341 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import math
|
||||
|
||||
import torch
|
||||
from functools import partial
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
import torch.utils.checkpoint as checkpoint
|
||||
|
||||
from timm.models.layers import drop_path, to_2tuple, trunc_normal_
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
def get_abs_pos(abs_pos, h, w, ori_h, ori_w, has_cls_token=True):
|
||||
"""
|
||||
Calculate absolute positional embeddings. If needed, resize embeddings and remove cls_token
|
||||
dimension for the original embeddings.
|
||||
Args:
|
||||
abs_pos (Tensor): absolute positional embeddings with (1, num_position, C).
|
||||
has_cls_token (bool): If true, has 1 embedding in abs_pos for cls token.
|
||||
hw (Tuple): size of input image tokens.
|
||||
|
||||
Returns:
|
||||
Absolute positional embeddings after processing with shape (1, H, W, C)
|
||||
"""
|
||||
cls_token = None
|
||||
B, L, C = abs_pos.shape
|
||||
if has_cls_token:
|
||||
cls_token = abs_pos[:, 0:1]
|
||||
abs_pos = abs_pos[:, 1:]
|
||||
|
||||
if ori_h != h or ori_w != w:
|
||||
new_abs_pos = F.interpolate(
|
||||
abs_pos.reshape(1, ori_h, ori_w, -1).permute(0, 3, 1, 2),
|
||||
size=(h, w),
|
||||
mode="bicubic",
|
||||
align_corners=False,
|
||||
).permute(0, 2, 3, 1).reshape(B, -1, C)
|
||||
|
||||
else:
|
||||
new_abs_pos = abs_pos
|
||||
|
||||
if cls_token is not None:
|
||||
new_abs_pos = torch.cat([cls_token, new_abs_pos], dim=1)
|
||||
return new_abs_pos
|
||||
|
||||
class DropPath(nn.Module):
|
||||
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
||||
"""
|
||||
def __init__(self, drop_prob=None):
|
||||
super(DropPath, self).__init__()
|
||||
self.drop_prob = drop_prob
|
||||
|
||||
def forward(self, x):
|
||||
return drop_path(x, self.drop_prob, self.training)
|
||||
|
||||
def extra_repr(self):
|
||||
return 'p={}'.format(self.drop_prob)
|
||||
|
||||
class Mlp(nn.Module):
|
||||
def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
|
||||
super().__init__()
|
||||
out_features = out_features or in_features
|
||||
hidden_features = hidden_features or in_features
|
||||
self.fc1 = nn.Linear(in_features, hidden_features)
|
||||
self.act = act_layer()
|
||||
self.fc2 = nn.Linear(hidden_features, out_features)
|
||||
self.drop = nn.Dropout(drop)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.fc1(x)
|
||||
x = self.act(x)
|
||||
x = self.fc2(x)
|
||||
x = self.drop(x)
|
||||
return x
|
||||
|
||||
class Attention(nn.Module):
|
||||
def __init__(
|
||||
self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0.,
|
||||
proj_drop=0., attn_head_dim=None,):
|
||||
super().__init__()
|
||||
self.num_heads = num_heads
|
||||
head_dim = dim // num_heads
|
||||
self.dim = dim
|
||||
|
||||
if attn_head_dim is not None:
|
||||
head_dim = attn_head_dim
|
||||
all_head_dim = head_dim * self.num_heads
|
||||
|
||||
self.scale = qk_scale or head_dim ** -0.5
|
||||
|
||||
self.qkv = nn.Linear(dim, all_head_dim * 3, bias=qkv_bias)
|
||||
|
||||
self.attn_drop = nn.Dropout(attn_drop)
|
||||
self.proj = nn.Linear(all_head_dim, dim)
|
||||
self.proj_drop = nn.Dropout(proj_drop)
|
||||
|
||||
def forward(self, x):
|
||||
B, N, C = x.shape
|
||||
qkv = self.qkv(x)
|
||||
qkv = qkv.reshape(B, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
||||
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
|
||||
|
||||
q = q * self.scale
|
||||
attn = (q @ k.transpose(-2, -1))
|
||||
|
||||
attn = attn.softmax(dim=-1)
|
||||
attn = self.attn_drop(attn)
|
||||
|
||||
x = (attn @ v).transpose(1, 2).reshape(B, N, -1)
|
||||
x = self.proj(x)
|
||||
x = self.proj_drop(x)
|
||||
|
||||
return x
|
||||
|
||||
class Block(nn.Module):
|
||||
|
||||
def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None,
|
||||
drop=0., attn_drop=0., drop_path=0., act_layer=nn.GELU,
|
||||
norm_layer=nn.LayerNorm, attn_head_dim=None
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.norm1 = norm_layer(dim)
|
||||
self.attn = Attention(
|
||||
dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale,
|
||||
attn_drop=attn_drop, proj_drop=drop, attn_head_dim=attn_head_dim
|
||||
)
|
||||
|
||||
# NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
|
||||
self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
||||
self.norm2 = norm_layer(dim)
|
||||
mlp_hidden_dim = int(dim * mlp_ratio)
|
||||
self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
|
||||
|
||||
def forward(self, x):
|
||||
x = x + self.drop_path(self.attn(self.norm1(x)))
|
||||
x = x + self.drop_path(self.mlp(self.norm2(x)))
|
||||
return x
|
||||
|
||||
|
||||
class PatchEmbed(nn.Module):
|
||||
""" Image to Patch Embedding
|
||||
"""
|
||||
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, ratio=1):
|
||||
super().__init__()
|
||||
img_size = to_2tuple(img_size)
|
||||
patch_size = to_2tuple(patch_size)
|
||||
num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) * (ratio ** 2)
|
||||
self.patch_shape = (int(img_size[0] // patch_size[0] * ratio), int(img_size[1] // patch_size[1] * ratio))
|
||||
self.origin_patch_shape = (int(img_size[0] // patch_size[0]), int(img_size[1] // patch_size[1]))
|
||||
self.img_size = img_size
|
||||
self.patch_size = patch_size
|
||||
self.num_patches = num_patches
|
||||
|
||||
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=(patch_size[0] // ratio), padding=4 + 2 * (ratio//2-1))
|
||||
|
||||
def forward(self, x, **kwargs):
|
||||
B, C, H, W = x.shape
|
||||
x = self.proj(x)
|
||||
Hp, Wp = x.shape[2], x.shape[3]
|
||||
|
||||
x = x.flatten(2).transpose(1, 2)
|
||||
return x, (Hp, Wp)
|
||||
|
||||
|
||||
class HybridEmbed(nn.Module):
|
||||
""" CNN Feature Map Embedding
|
||||
Extract feature map from CNN, flatten, project to embedding dim.
|
||||
"""
|
||||
def __init__(self, backbone, img_size=224, feature_size=None, in_chans=3, embed_dim=768):
|
||||
super().__init__()
|
||||
assert isinstance(backbone, nn.Module)
|
||||
img_size = to_2tuple(img_size)
|
||||
self.img_size = img_size
|
||||
self.backbone = backbone
|
||||
if feature_size is None:
|
||||
with torch.no_grad():
|
||||
training = backbone.training
|
||||
if training:
|
||||
backbone.eval()
|
||||
o = self.backbone(torch.zeros(1, in_chans, img_size[0], img_size[1]))[-1]
|
||||
feature_size = o.shape[-2:]
|
||||
feature_dim = o.shape[1]
|
||||
backbone.train(training)
|
||||
else:
|
||||
feature_size = to_2tuple(feature_size)
|
||||
feature_dim = self.backbone.feature_info.channels()[-1]
|
||||
self.num_patches = feature_size[0] * feature_size[1]
|
||||
self.proj = nn.Linear(feature_dim, embed_dim)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.backbone(x)[-1]
|
||||
x = x.flatten(2).transpose(1, 2)
|
||||
x = self.proj(x)
|
||||
return x
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ViT(BaseBackbone):
|
||||
|
||||
def __init__(self,
|
||||
img_size=224, patch_size=16, in_chans=3, num_classes=80, embed_dim=768, depth=12,
|
||||
num_heads=12, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop_rate=0., attn_drop_rate=0.,
|
||||
drop_path_rate=0., hybrid_backbone=None, norm_layer=None, use_checkpoint=False,
|
||||
frozen_stages=-1, ratio=1, last_norm=True,
|
||||
patch_padding='pad', freeze_attn=False, freeze_ffn=False,
|
||||
):
|
||||
# Protect mutable default arguments
|
||||
super(ViT, self).__init__()
|
||||
norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6)
|
||||
self.num_classes = num_classes
|
||||
self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
|
||||
self.frozen_stages = frozen_stages
|
||||
self.use_checkpoint = use_checkpoint
|
||||
self.patch_padding = patch_padding
|
||||
self.freeze_attn = freeze_attn
|
||||
self.freeze_ffn = freeze_ffn
|
||||
self.depth = depth
|
||||
|
||||
if hybrid_backbone is not None:
|
||||
self.patch_embed = HybridEmbed(
|
||||
hybrid_backbone, img_size=img_size, in_chans=in_chans, embed_dim=embed_dim)
|
||||
else:
|
||||
self.patch_embed = PatchEmbed(
|
||||
img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, ratio=ratio)
|
||||
num_patches = self.patch_embed.num_patches
|
||||
|
||||
# since the pretraining model has class token
|
||||
self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim))
|
||||
|
||||
dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
|
||||
|
||||
self.blocks = nn.ModuleList([
|
||||
Block(
|
||||
dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale,
|
||||
drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer,
|
||||
)
|
||||
for i in range(depth)])
|
||||
|
||||
self.last_norm = norm_layer(embed_dim) if last_norm else nn.Identity()
|
||||
|
||||
if self.pos_embed is not None:
|
||||
trunc_normal_(self.pos_embed, std=.02)
|
||||
|
||||
self._freeze_stages()
|
||||
|
||||
def _freeze_stages(self):
|
||||
"""Freeze parameters."""
|
||||
if self.frozen_stages >= 0:
|
||||
self.patch_embed.eval()
|
||||
for param in self.patch_embed.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
m = self.blocks[i]
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
if self.freeze_attn:
|
||||
for i in range(0, self.depth):
|
||||
m = self.blocks[i]
|
||||
m.attn.eval()
|
||||
m.norm1.eval()
|
||||
for param in m.attn.parameters():
|
||||
param.requires_grad = False
|
||||
for param in m.norm1.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
if self.freeze_ffn:
|
||||
self.pos_embed.requires_grad = False
|
||||
self.patch_embed.eval()
|
||||
for param in self.patch_embed.parameters():
|
||||
param.requires_grad = False
|
||||
for i in range(0, self.depth):
|
||||
m = self.blocks[i]
|
||||
m.mlp.eval()
|
||||
m.norm2.eval()
|
||||
for param in m.mlp.parameters():
|
||||
param.requires_grad = False
|
||||
for param in m.norm2.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
super().init_weights(pretrained, patch_padding=self.patch_padding)
|
||||
|
||||
if pretrained is None:
|
||||
def _init_weights(m):
|
||||
if isinstance(m, nn.Linear):
|
||||
trunc_normal_(m.weight, std=.02)
|
||||
if isinstance(m, nn.Linear) and m.bias is not None:
|
||||
nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.LayerNorm):
|
||||
nn.init.constant_(m.bias, 0)
|
||||
nn.init.constant_(m.weight, 1.0)
|
||||
|
||||
self.apply(_init_weights)
|
||||
|
||||
def get_num_layers(self):
|
||||
return len(self.blocks)
|
||||
|
||||
@torch.jit.ignore
|
||||
def no_weight_decay(self):
|
||||
return {'pos_embed', 'cls_token'}
|
||||
|
||||
def forward_features(self, x):
|
||||
B, C, H, W = x.shape
|
||||
x, (Hp, Wp) = self.patch_embed(x)
|
||||
|
||||
if self.pos_embed is not None:
|
||||
# fit for multiple GPU training
|
||||
# since the first element for pos embed (sin-cos manner) is zero, it will cause no difference
|
||||
x = x + self.pos_embed[:, 1:] + self.pos_embed[:, :1]
|
||||
|
||||
for blk in self.blocks:
|
||||
if self.use_checkpoint:
|
||||
x = checkpoint.checkpoint(blk, x)
|
||||
else:
|
||||
x = blk(x)
|
||||
|
||||
x = self.last_norm(x)
|
||||
|
||||
xp = x.permute(0, 2, 1).reshape(B, -1, Hp, Wp).contiguous()
|
||||
|
||||
return xp
|
||||
|
||||
def forward(self, x):
|
||||
x = self.forward_features(x)
|
||||
return x
|
||||
|
||||
def train(self, mode=True):
|
||||
"""Convert the model into training mode."""
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
@@ -0,0 +1,385 @@
|
||||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import math
|
||||
|
||||
import torch
|
||||
from functools import partial
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
import torch.utils.checkpoint as checkpoint
|
||||
|
||||
from timm.models.layers import drop_path, to_2tuple, trunc_normal_
|
||||
|
||||
from ..builder import BACKBONES
|
||||
from .base_backbone import BaseBackbone
|
||||
|
||||
def get_abs_pos(abs_pos, h, w, ori_h, ori_w, has_cls_token=True):
|
||||
"""
|
||||
Calculate absolute positional embeddings. If needed, resize embeddings and remove cls_token
|
||||
dimension for the original embeddings.
|
||||
Args:
|
||||
abs_pos (Tensor): absolute positional embeddings with (1, num_position, C).
|
||||
has_cls_token (bool): If true, has 1 embedding in abs_pos for cls token.
|
||||
hw (Tuple): size of input image tokens.
|
||||
|
||||
Returns:
|
||||
Absolute positional embeddings after processing with shape (1, H, W, C)
|
||||
"""
|
||||
cls_token = None
|
||||
B, L, C = abs_pos.shape
|
||||
if has_cls_token:
|
||||
cls_token = abs_pos[:, 0:1]
|
||||
abs_pos = abs_pos[:, 1:]
|
||||
|
||||
if ori_h != h or ori_w != w:
|
||||
new_abs_pos = F.interpolate(
|
||||
abs_pos.reshape(1, ori_h, ori_w, -1).permute(0, 3, 1, 2),
|
||||
size=(h, w),
|
||||
mode="bicubic",
|
||||
align_corners=False,
|
||||
).permute(0, 2, 3, 1).reshape(B, -1, C)
|
||||
|
||||
else:
|
||||
new_abs_pos = abs_pos
|
||||
|
||||
if cls_token is not None:
|
||||
new_abs_pos = torch.cat([cls_token, new_abs_pos], dim=1)
|
||||
return new_abs_pos
|
||||
|
||||
class DropPath(nn.Module):
|
||||
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
||||
"""
|
||||
def __init__(self, drop_prob=None):
|
||||
super(DropPath, self).__init__()
|
||||
self.drop_prob = drop_prob
|
||||
|
||||
def forward(self, x):
|
||||
return drop_path(x, self.drop_prob, self.training)
|
||||
|
||||
def extra_repr(self):
|
||||
return 'p={}'.format(self.drop_prob)
|
||||
|
||||
class Mlp(nn.Module):
|
||||
def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
|
||||
super().__init__()
|
||||
out_features = out_features or in_features
|
||||
hidden_features = hidden_features or in_features
|
||||
self.fc1 = nn.Linear(in_features, hidden_features)
|
||||
self.act = act_layer()
|
||||
self.fc2 = nn.Linear(hidden_features, out_features)
|
||||
self.drop = nn.Dropout(drop)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.fc1(x)
|
||||
x = self.act(x)
|
||||
x = self.fc2(x)
|
||||
x = self.drop(x)
|
||||
return x
|
||||
|
||||
class MoEMlp(nn.Module):
|
||||
def __init__(self, num_expert=1, in_features=1024, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0., part_features=256):
|
||||
super().__init__()
|
||||
out_features = out_features or in_features
|
||||
hidden_features = hidden_features or in_features
|
||||
self.part_features = part_features
|
||||
self.fc1 = nn.Linear(in_features, hidden_features)
|
||||
self.act = act_layer()
|
||||
self.fc2 = nn.Linear(hidden_features, out_features - part_features)
|
||||
self.drop = nn.Dropout(drop)
|
||||
|
||||
self.num_expert = num_expert
|
||||
experts = []
|
||||
|
||||
for i in range(num_expert):
|
||||
experts.append(
|
||||
nn.Linear(hidden_features, part_features)
|
||||
)
|
||||
self.experts = nn.ModuleList(experts)
|
||||
|
||||
def forward(self, x, indices):
|
||||
|
||||
expert_x = torch.zeros_like(x[:, :, -self.part_features:], device=x.device, dtype=x.dtype)
|
||||
|
||||
x = self.fc1(x)
|
||||
x = self.act(x)
|
||||
shared_x = self.fc2(x)
|
||||
indices = indices.view(-1, 1, 1)
|
||||
|
||||
# to support ddp training
|
||||
for i in range(self.num_expert):
|
||||
selectedIndex = (indices == i)
|
||||
current_x = self.experts[i](x) * selectedIndex
|
||||
expert_x = expert_x + current_x
|
||||
|
||||
x = torch.cat([shared_x, expert_x], dim=-1)
|
||||
|
||||
return x
|
||||
|
||||
class Attention(nn.Module):
|
||||
def __init__(
|
||||
self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0.,
|
||||
proj_drop=0., attn_head_dim=None,):
|
||||
super().__init__()
|
||||
self.num_heads = num_heads
|
||||
head_dim = dim // num_heads
|
||||
self.dim = dim
|
||||
|
||||
if attn_head_dim is not None:
|
||||
head_dim = attn_head_dim
|
||||
all_head_dim = head_dim * self.num_heads
|
||||
|
||||
self.scale = qk_scale or head_dim ** -0.5
|
||||
|
||||
self.qkv = nn.Linear(dim, all_head_dim * 3, bias=qkv_bias)
|
||||
|
||||
self.attn_drop = nn.Dropout(attn_drop)
|
||||
self.proj = nn.Linear(all_head_dim, dim)
|
||||
self.proj_drop = nn.Dropout(proj_drop)
|
||||
|
||||
def forward(self, x):
|
||||
B, N, C = x.shape
|
||||
qkv = self.qkv(x)
|
||||
qkv = qkv.reshape(B, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
||||
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
|
||||
|
||||
q = q * self.scale
|
||||
attn = (q @ k.transpose(-2, -1))
|
||||
|
||||
attn = attn.softmax(dim=-1)
|
||||
attn = self.attn_drop(attn)
|
||||
|
||||
x = (attn @ v).transpose(1, 2).reshape(B, N, -1)
|
||||
x = self.proj(x)
|
||||
x = self.proj_drop(x)
|
||||
|
||||
return x
|
||||
|
||||
class Block(nn.Module):
|
||||
|
||||
def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None,
|
||||
drop=0., attn_drop=0., drop_path=0., act_layer=nn.GELU,
|
||||
norm_layer=nn.LayerNorm, attn_head_dim=None, num_expert=1, part_features=None
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.norm1 = norm_layer(dim)
|
||||
self.attn = Attention(
|
||||
dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale,
|
||||
attn_drop=attn_drop, proj_drop=drop, attn_head_dim=attn_head_dim
|
||||
)
|
||||
|
||||
# NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
|
||||
self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
||||
self.norm2 = norm_layer(dim)
|
||||
mlp_hidden_dim = int(dim * mlp_ratio)
|
||||
self.mlp = MoEMlp(num_expert=num_expert, in_features=dim, hidden_features=mlp_hidden_dim,
|
||||
act_layer=act_layer, drop=drop, part_features=part_features)
|
||||
|
||||
def forward(self, x, indices=None):
|
||||
|
||||
x = x + self.drop_path(self.attn(self.norm1(x)))
|
||||
x = x + self.drop_path(self.mlp(self.norm2(x), indices))
|
||||
return x
|
||||
|
||||
|
||||
class PatchEmbed(nn.Module):
|
||||
""" Image to Patch Embedding
|
||||
"""
|
||||
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, ratio=1):
|
||||
super().__init__()
|
||||
img_size = to_2tuple(img_size)
|
||||
patch_size = to_2tuple(patch_size)
|
||||
num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) * (ratio ** 2)
|
||||
self.patch_shape = (int(img_size[0] // patch_size[0] * ratio), int(img_size[1] // patch_size[1] * ratio))
|
||||
self.origin_patch_shape = (int(img_size[0] // patch_size[0]), int(img_size[1] // patch_size[1]))
|
||||
self.img_size = img_size
|
||||
self.patch_size = patch_size
|
||||
self.num_patches = num_patches
|
||||
|
||||
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=(patch_size[0] // ratio), padding=4 + 2 * (ratio//2-1))
|
||||
|
||||
def forward(self, x, **kwargs):
|
||||
B, C, H, W = x.shape
|
||||
x = self.proj(x)
|
||||
Hp, Wp = x.shape[2], x.shape[3]
|
||||
|
||||
x = x.flatten(2).transpose(1, 2)
|
||||
return x, (Hp, Wp)
|
||||
|
||||
|
||||
class HybridEmbed(nn.Module):
|
||||
""" CNN Feature Map Embedding
|
||||
Extract feature map from CNN, flatten, project to embedding dim.
|
||||
"""
|
||||
def __init__(self, backbone, img_size=224, feature_size=None, in_chans=3, embed_dim=768):
|
||||
super().__init__()
|
||||
assert isinstance(backbone, nn.Module)
|
||||
img_size = to_2tuple(img_size)
|
||||
self.img_size = img_size
|
||||
self.backbone = backbone
|
||||
if feature_size is None:
|
||||
with torch.no_grad():
|
||||
training = backbone.training
|
||||
if training:
|
||||
backbone.eval()
|
||||
o = self.backbone(torch.zeros(1, in_chans, img_size[0], img_size[1]))[-1]
|
||||
feature_size = o.shape[-2:]
|
||||
feature_dim = o.shape[1]
|
||||
backbone.train(training)
|
||||
else:
|
||||
feature_size = to_2tuple(feature_size)
|
||||
feature_dim = self.backbone.feature_info.channels()[-1]
|
||||
self.num_patches = feature_size[0] * feature_size[1]
|
||||
self.proj = nn.Linear(feature_dim, embed_dim)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.backbone(x)[-1]
|
||||
x = x.flatten(2).transpose(1, 2)
|
||||
x = self.proj(x)
|
||||
return x
|
||||
|
||||
|
||||
@BACKBONES.register_module()
|
||||
class ViTMoE(BaseBackbone):
|
||||
|
||||
def __init__(self,
|
||||
img_size=224, patch_size=16, in_chans=3, num_classes=80, embed_dim=768, depth=12,
|
||||
num_heads=12, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop_rate=0., attn_drop_rate=0.,
|
||||
drop_path_rate=0., hybrid_backbone=None, norm_layer=None, use_checkpoint=False,
|
||||
frozen_stages=-1, ratio=1, last_norm=True,
|
||||
patch_padding='pad', freeze_attn=False, freeze_ffn=False,
|
||||
num_expert=1, part_features=None
|
||||
):
|
||||
# Protect mutable default arguments
|
||||
super(ViTMoE, self).__init__()
|
||||
norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6)
|
||||
self.num_classes = num_classes
|
||||
self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
|
||||
self.frozen_stages = frozen_stages
|
||||
self.use_checkpoint = use_checkpoint
|
||||
self.patch_padding = patch_padding
|
||||
self.freeze_attn = freeze_attn
|
||||
self.freeze_ffn = freeze_ffn
|
||||
self.depth = depth
|
||||
|
||||
if hybrid_backbone is not None:
|
||||
self.patch_embed = HybridEmbed(
|
||||
hybrid_backbone, img_size=img_size, in_chans=in_chans, embed_dim=embed_dim)
|
||||
else:
|
||||
self.patch_embed = PatchEmbed(
|
||||
img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, ratio=ratio)
|
||||
num_patches = self.patch_embed.num_patches
|
||||
|
||||
self.part_features = part_features
|
||||
|
||||
self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim))
|
||||
|
||||
dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
|
||||
|
||||
self.blocks = nn.ModuleList([
|
||||
Block(
|
||||
dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale,
|
||||
drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer,
|
||||
num_expert=num_expert, part_features=part_features
|
||||
)
|
||||
for i in range(depth)])
|
||||
|
||||
self.last_norm = norm_layer(embed_dim) if last_norm else nn.Identity()
|
||||
|
||||
if self.pos_embed is not None:
|
||||
trunc_normal_(self.pos_embed, std=.02)
|
||||
|
||||
self._freeze_stages()
|
||||
|
||||
def _freeze_stages(self):
|
||||
"""Freeze parameters."""
|
||||
if self.frozen_stages >= 0:
|
||||
self.patch_embed.eval()
|
||||
for param in self.patch_embed.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
for i in range(1, self.frozen_stages + 1):
|
||||
m = self.blocks[i]
|
||||
m.eval()
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
if self.freeze_attn:
|
||||
for i in range(0, self.depth):
|
||||
m = self.blocks[i]
|
||||
m.attn.eval()
|
||||
m.norm1.eval()
|
||||
for param in m.attn.parameters():
|
||||
param.requires_grad = False
|
||||
for param in m.norm1.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
if self.freeze_ffn:
|
||||
self.pos_embed.requires_grad = False
|
||||
self.patch_embed.eval()
|
||||
for param in self.patch_embed.parameters():
|
||||
param.requires_grad = False
|
||||
for i in range(0, self.depth):
|
||||
m = self.blocks[i]
|
||||
m.mlp.eval()
|
||||
m.norm2.eval()
|
||||
for param in m.mlp.parameters():
|
||||
param.requires_grad = False
|
||||
for param in m.norm2.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def init_weights(self, pretrained=None):
|
||||
"""Initialize the weights in backbone.
|
||||
Args:
|
||||
pretrained (str, optional): Path to pre-trained weights.
|
||||
Defaults to None.
|
||||
"""
|
||||
super().init_weights(pretrained, patch_padding=self.patch_padding, part_features=self.part_features)
|
||||
|
||||
if pretrained is None:
|
||||
def _init_weights(m):
|
||||
if isinstance(m, nn.Linear):
|
||||
trunc_normal_(m.weight, std=.02)
|
||||
if isinstance(m, nn.Linear) and m.bias is not None:
|
||||
nn.init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.LayerNorm):
|
||||
nn.init.constant_(m.bias, 0)
|
||||
nn.init.constant_(m.weight, 1.0)
|
||||
|
||||
self.apply(_init_weights)
|
||||
|
||||
def get_num_layers(self):
|
||||
return len(self.blocks)
|
||||
|
||||
@torch.jit.ignore
|
||||
def no_weight_decay(self):
|
||||
return {'pos_embed', 'cls_token'}
|
||||
|
||||
def forward_features(self, x, dataset_source=None):
|
||||
B, C, H, W = x.shape
|
||||
x, (Hp, Wp) = self.patch_embed(x)
|
||||
|
||||
if self.pos_embed is not None:
|
||||
# fit for multiple GPU training
|
||||
# since the first element for pos embed (sin-cos manner) is zero, it will cause no difference
|
||||
x = x + self.pos_embed[:, 1:] + self.pos_embed[:, :1]
|
||||
|
||||
for blk in self.blocks:
|
||||
if self.use_checkpoint:
|
||||
x = checkpoint.checkpoint(blk, x, dataset_source)
|
||||
else:
|
||||
x = blk(x, dataset_source)
|
||||
|
||||
x = self.last_norm(x)
|
||||
|
||||
xp = x.permute(0, 2, 1).reshape(B, -1, Hp, Wp).contiguous()
|
||||
|
||||
return xp
|
||||
|
||||
def forward(self, x, dataset_source=None):
|
||||
x = self.forward_features(x, dataset_source)
|
||||
return x
|
||||
|
||||
def train(self, mode=True):
|
||||
"""Convert the model into training mode."""
|
||||
super().train(mode)
|
||||
self._freeze_stages()
|
||||
Reference in New Issue
Block a user