Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- src/custom_mesh_graphormer/modeling/data/mano_195_adjmat_indices.pt +3 -0
- src/custom_mesh_graphormer/modeling/data/mano_195_adjmat_size.pt +3 -0
- src/custom_timm/data/parsers/__pycache__/__init__.cpython-312.pyc +0 -0
- src/custom_timm/data/parsers/__pycache__/img_extensions.cpython-312.pyc +0 -0
- src/custom_timm/data/parsers/__pycache__/parser.cpython-312.pyc +0 -0
- src/custom_timm/data/parsers/__pycache__/parser_factory.cpython-312.pyc +0 -0
- src/custom_timm/data/parsers/__pycache__/parser_image_folder.cpython-312.pyc +0 -0
- src/custom_timm/data/parsers/__pycache__/parser_image_in_tar.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/beit.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/byoanet.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/byobnet.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/cait.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/coat.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/convit.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/convmixer.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/convnext.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/crossvit.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/cspnet.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/deit.cpython-312.pyc +0 -0
- src/custom_timm/models/__pycache__/densenet.cpython-312.pyc +0 -0
- src/custom_timm/models/layers/__init__.py +44 -0
- src/custom_timm/models/layers/activations.py +145 -0
- src/custom_timm/models/layers/activations_jit.py +90 -0
- src/custom_timm/models/layers/activations_me.py +218 -0
- src/custom_timm/models/layers/adaptive_avgmax_pool.py +118 -0
- src/custom_timm/models/layers/attention_pool2d.py +131 -0
- src/custom_timm/models/layers/blur_pool.py +42 -0
- src/custom_timm/models/layers/bottleneck_attn.py +157 -0
- src/custom_timm/models/layers/cbam.py +112 -0
- src/custom_timm/models/layers/classifier.py +56 -0
- src/custom_timm/models/layers/cond_conv2d.py +123 -0
- src/custom_timm/models/layers/config.py +115 -0
- src/custom_timm/models/layers/conv2d_same.py +42 -0
- src/custom_timm/models/layers/conv_bn_act.py +88 -0
- src/custom_timm/models/layers/create_act.py +154 -0
- src/custom_timm/models/layers/create_attn.py +89 -0
- src/custom_timm/models/layers/create_conv2d.py +36 -0
- src/custom_timm/models/layers/create_norm.py +56 -0
- src/custom_timm/models/layers/create_norm_act.py +91 -0
- src/custom_timm/models/layers/drop.py +169 -0
- src/custom_timm/models/layers/eca.py +145 -0
- src/custom_timm/models/layers/evo_norm.py +352 -0
- src/custom_timm/models/layers/fast_norm.py +78 -0
- src/custom_timm/models/layers/filter_response_norm.py +68 -0
- src/custom_timm/models/layers/gather_excite.py +90 -0
- src/custom_timm/models/layers/global_context.py +67 -0
- src/custom_timm/models/layers/halo_attn.py +233 -0
- src/custom_timm/models/layers/helpers.py +43 -0
- src/custom_timm/models/layers/inplace_abn.py +87 -0
- src/custom_timm/models/layers/lambda_layer.py +133 -0
src/custom_mesh_graphormer/modeling/data/mano_195_adjmat_indices.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f99d80a96bbced27df6b6dee4fbdc01ee326e7e2691a79ca596ad03f57db8a6a
|
| 3 |
+
size 21639
|
src/custom_mesh_graphormer/modeling/data/mano_195_adjmat_size.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cd137f9f2b1b8827251934784326a5b05f1415333101c13c849ed6b5eba6c3a4
|
| 3 |
+
size 173
|
src/custom_timm/data/parsers/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (350 Bytes). View file
|
|
|
src/custom_timm/data/parsers/__pycache__/img_extensions.cpython-312.pyc
ADDED
|
Binary file (2.84 kB). View file
|
|
|
src/custom_timm/data/parsers/__pycache__/parser.cpython-312.pyc
ADDED
|
Binary file (1.3 kB). View file
|
|
|
src/custom_timm/data/parsers/__pycache__/parser_factory.cpython-312.pyc
ADDED
|
Binary file (1.36 kB). View file
|
|
|
src/custom_timm/data/parsers/__pycache__/parser_image_folder.cpython-312.pyc
ADDED
|
Binary file (5.37 kB). View file
|
|
|
src/custom_timm/data/parsers/__pycache__/parser_image_in_tar.cpython-312.pyc
ADDED
|
Binary file (13.7 kB). View file
|
|
|
src/custom_timm/models/__pycache__/beit.cpython-312.pyc
ADDED
|
Binary file (27.1 kB). View file
|
|
|
src/custom_timm/models/__pycache__/byoanet.cpython-312.pyc
ADDED
|
Binary file (14.3 kB). View file
|
|
|
src/custom_timm/models/__pycache__/byobnet.cpython-312.pyc
ADDED
|
Binary file (66.9 kB). View file
|
|
|
src/custom_timm/models/__pycache__/cait.cpython-312.pyc
ADDED
|
Binary file (22.2 kB). View file
|
|
|
src/custom_timm/models/__pycache__/coat.cpython-312.pyc
ADDED
|
Binary file (35.2 kB). View file
|
|
|
src/custom_timm/models/__pycache__/convit.cpython-312.pyc
ADDED
|
Binary file (22.9 kB). View file
|
|
|
src/custom_timm/models/__pycache__/convmixer.cpython-312.pyc
ADDED
|
Binary file (7.31 kB). View file
|
|
|
src/custom_timm/models/__pycache__/convnext.cpython-312.pyc
ADDED
|
Binary file (29.6 kB). View file
|
|
|
src/custom_timm/models/__pycache__/crossvit.cpython-312.pyc
ADDED
|
Binary file (31.3 kB). View file
|
|
|
src/custom_timm/models/__pycache__/cspnet.cpython-312.pyc
ADDED
|
Binary file (41.4 kB). View file
|
|
|
src/custom_timm/models/__pycache__/deit.cpython-312.pyc
ADDED
|
Binary file (20.9 kB). View file
|
|
|
src/custom_timm/models/__pycache__/densenet.cpython-312.pyc
ADDED
|
Binary file (19 kB). View file
|
|
|
src/custom_timm/models/layers/__init__.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .activations import *
|
| 2 |
+
from .adaptive_avgmax_pool import \
|
| 3 |
+
adaptive_avgmax_pool2d, select_adaptive_pool2d, AdaptiveAvgMaxPool2d, SelectAdaptivePool2d
|
| 4 |
+
from .blur_pool import BlurPool2d
|
| 5 |
+
from .classifier import ClassifierHead, create_classifier
|
| 6 |
+
from .cond_conv2d import CondConv2d, get_condconv_initializer
|
| 7 |
+
from .config import is_exportable, is_scriptable, is_no_jit, set_exportable, set_scriptable, set_no_jit,\
|
| 8 |
+
set_layer_config
|
| 9 |
+
from .conv2d_same import Conv2dSame, conv2d_same
|
| 10 |
+
from .conv_bn_act import ConvNormAct, ConvNormActAa, ConvBnAct
|
| 11 |
+
from .create_act import create_act_layer, get_act_layer, get_act_fn
|
| 12 |
+
from .create_attn import get_attn, create_attn
|
| 13 |
+
from .create_conv2d import create_conv2d
|
| 14 |
+
from .create_norm import get_norm_layer, create_norm_layer
|
| 15 |
+
from .create_norm_act import get_norm_act_layer, create_norm_act_layer, get_norm_act_layer
|
| 16 |
+
from .drop import DropBlock2d, DropPath, drop_block_2d, drop_path
|
| 17 |
+
from .eca import EcaModule, CecaModule, EfficientChannelAttn, CircularEfficientChannelAttn
|
| 18 |
+
from .evo_norm import EvoNorm2dB0, EvoNorm2dB1, EvoNorm2dB2,\
|
| 19 |
+
EvoNorm2dS0, EvoNorm2dS0a, EvoNorm2dS1, EvoNorm2dS1a, EvoNorm2dS2, EvoNorm2dS2a
|
| 20 |
+
from .fast_norm import is_fast_norm, set_fast_norm, fast_group_norm, fast_layer_norm
|
| 21 |
+
from .filter_response_norm import FilterResponseNormTlu2d, FilterResponseNormAct2d
|
| 22 |
+
from .gather_excite import GatherExcite
|
| 23 |
+
from .global_context import GlobalContext
|
| 24 |
+
from .helpers import to_ntuple, to_2tuple, to_3tuple, to_4tuple, make_divisible, extend_tuple
|
| 25 |
+
from .inplace_abn import InplaceAbn
|
| 26 |
+
from .linear import Linear
|
| 27 |
+
from .mixed_conv2d import MixedConv2d
|
| 28 |
+
from .mlp import Mlp, GluMlp, GatedMlp, ConvMlp
|
| 29 |
+
from .non_local_attn import NonLocalAttn, BatNonLocalAttn
|
| 30 |
+
from .norm import GroupNorm, GroupNorm1, LayerNorm, LayerNorm2d
|
| 31 |
+
from .norm_act import BatchNormAct2d, GroupNormAct, convert_sync_batchnorm
|
| 32 |
+
from .padding import get_padding, get_same_padding, pad_same
|
| 33 |
+
from .patch_embed import PatchEmbed
|
| 34 |
+
from .pool2d_same import AvgPool2dSame, create_pool2d
|
| 35 |
+
from .squeeze_excite import SEModule, SqueezeExcite, EffectiveSEModule, EffectiveSqueezeExcite
|
| 36 |
+
from .selective_kernel import SelectiveKernel
|
| 37 |
+
from .separable_conv import SeparableConv2d, SeparableConvNormAct
|
| 38 |
+
from .space_to_depth import SpaceToDepthModule
|
| 39 |
+
from .split_attn import SplitAttn
|
| 40 |
+
from .split_batchnorm import SplitBatchNorm2d, convert_splitbn_model
|
| 41 |
+
from .std_conv import StdConv2d, StdConv2dSame, ScaledStdConv2d, ScaledStdConv2dSame
|
| 42 |
+
from .test_time_pool import TestTimePoolHead, apply_test_time_pool
|
| 43 |
+
from .trace_utils import _assert, _float_to_int
|
| 44 |
+
from .weight_init import trunc_normal_, trunc_normal_tf_, variance_scaling_, lecun_normal_
|
src/custom_timm/models/layers/activations.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Activations
|
| 2 |
+
|
| 3 |
+
A collection of activations fn and modules with a common interface so that they can
|
| 4 |
+
easily be swapped. All have an `inplace` arg even if not used.
|
| 5 |
+
|
| 6 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
from torch import nn as nn
|
| 11 |
+
from torch.nn import functional as F
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def swish(x, inplace: bool = False):
|
| 15 |
+
"""Swish - Described in: https://arxiv.org/abs/1710.05941
|
| 16 |
+
"""
|
| 17 |
+
return x.mul_(x.sigmoid()) if inplace else x.mul(x.sigmoid())
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class Swish(nn.Module):
|
| 21 |
+
def __init__(self, inplace: bool = False):
|
| 22 |
+
super(Swish, self).__init__()
|
| 23 |
+
self.inplace = inplace
|
| 24 |
+
|
| 25 |
+
def forward(self, x):
|
| 26 |
+
return swish(x, self.inplace)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def mish(x, inplace: bool = False):
|
| 30 |
+
"""Mish: A Self Regularized Non-Monotonic Neural Activation Function - https://arxiv.org/abs/1908.08681
|
| 31 |
+
NOTE: I don't have a working inplace variant
|
| 32 |
+
"""
|
| 33 |
+
return x.mul(F.softplus(x).tanh())
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class Mish(nn.Module):
|
| 37 |
+
"""Mish: A Self Regularized Non-Monotonic Neural Activation Function - https://arxiv.org/abs/1908.08681
|
| 38 |
+
"""
|
| 39 |
+
def __init__(self, inplace: bool = False):
|
| 40 |
+
super(Mish, self).__init__()
|
| 41 |
+
|
| 42 |
+
def forward(self, x):
|
| 43 |
+
return mish(x)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def sigmoid(x, inplace: bool = False):
|
| 47 |
+
return x.sigmoid_() if inplace else x.sigmoid()
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
# PyTorch has this, but not with a consistent inplace argmument interface
|
| 51 |
+
class Sigmoid(nn.Module):
|
| 52 |
+
def __init__(self, inplace: bool = False):
|
| 53 |
+
super(Sigmoid, self).__init__()
|
| 54 |
+
self.inplace = inplace
|
| 55 |
+
|
| 56 |
+
def forward(self, x):
|
| 57 |
+
return x.sigmoid_() if self.inplace else x.sigmoid()
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def tanh(x, inplace: bool = False):
|
| 61 |
+
return x.tanh_() if inplace else x.tanh()
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# PyTorch has this, but not with a consistent inplace argmument interface
|
| 65 |
+
class Tanh(nn.Module):
|
| 66 |
+
def __init__(self, inplace: bool = False):
|
| 67 |
+
super(Tanh, self).__init__()
|
| 68 |
+
self.inplace = inplace
|
| 69 |
+
|
| 70 |
+
def forward(self, x):
|
| 71 |
+
return x.tanh_() if self.inplace else x.tanh()
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def hard_swish(x, inplace: bool = False):
|
| 75 |
+
inner = F.relu6(x + 3.).div_(6.)
|
| 76 |
+
return x.mul_(inner) if inplace else x.mul(inner)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class HardSwish(nn.Module):
|
| 80 |
+
def __init__(self, inplace: bool = False):
|
| 81 |
+
super(HardSwish, self).__init__()
|
| 82 |
+
self.inplace = inplace
|
| 83 |
+
|
| 84 |
+
def forward(self, x):
|
| 85 |
+
return hard_swish(x, self.inplace)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def hard_sigmoid(x, inplace: bool = False):
|
| 89 |
+
if inplace:
|
| 90 |
+
return x.add_(3.).clamp_(0., 6.).div_(6.)
|
| 91 |
+
else:
|
| 92 |
+
return F.relu6(x + 3.) / 6.
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
class HardSigmoid(nn.Module):
|
| 96 |
+
def __init__(self, inplace: bool = False):
|
| 97 |
+
super(HardSigmoid, self).__init__()
|
| 98 |
+
self.inplace = inplace
|
| 99 |
+
|
| 100 |
+
def forward(self, x):
|
| 101 |
+
return hard_sigmoid(x, self.inplace)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def hard_mish(x, inplace: bool = False):
|
| 105 |
+
""" Hard Mish
|
| 106 |
+
Experimental, based on notes by Mish author Diganta Misra at
|
| 107 |
+
https://github.com/digantamisra98/H-Mish/blob/0da20d4bc58e696b6803f2523c58d3c8a82782d0/README.md
|
| 108 |
+
"""
|
| 109 |
+
if inplace:
|
| 110 |
+
return x.mul_(0.5 * (x + 2).clamp(min=0, max=2))
|
| 111 |
+
else:
|
| 112 |
+
return 0.5 * x * (x + 2).clamp(min=0, max=2)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class HardMish(nn.Module):
|
| 116 |
+
def __init__(self, inplace: bool = False):
|
| 117 |
+
super(HardMish, self).__init__()
|
| 118 |
+
self.inplace = inplace
|
| 119 |
+
|
| 120 |
+
def forward(self, x):
|
| 121 |
+
return hard_mish(x, self.inplace)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
class PReLU(nn.PReLU):
|
| 125 |
+
"""Applies PReLU (w/ dummy inplace arg)
|
| 126 |
+
"""
|
| 127 |
+
def __init__(self, num_parameters: int = 1, init: float = 0.25, inplace: bool = False) -> None:
|
| 128 |
+
super(PReLU, self).__init__(num_parameters=num_parameters, init=init)
|
| 129 |
+
|
| 130 |
+
def forward(self, input: torch.Tensor) -> torch.Tensor:
|
| 131 |
+
return F.prelu(input, self.weight)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def gelu(x: torch.Tensor, inplace: bool = False) -> torch.Tensor:
|
| 135 |
+
return F.gelu(x)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class GELU(nn.Module):
|
| 139 |
+
"""Applies the Gaussian Error Linear Units function (w/ dummy inplace arg)
|
| 140 |
+
"""
|
| 141 |
+
def __init__(self, inplace: bool = False):
|
| 142 |
+
super(GELU, self).__init__()
|
| 143 |
+
|
| 144 |
+
def forward(self, input: torch.Tensor) -> torch.Tensor:
|
| 145 |
+
return F.gelu(input)
|
src/custom_timm/models/layers/activations_jit.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Activations
|
| 2 |
+
|
| 3 |
+
A collection of jit-scripted activations fn and modules with a common interface so that they can
|
| 4 |
+
easily be swapped. All have an `inplace` arg even if not used.
|
| 5 |
+
|
| 6 |
+
All jit scripted activations are lacking in-place variations on purpose, scripted kernel fusion does not
|
| 7 |
+
currently work across in-place op boundaries, thus performance is equal to or less than the non-scripted
|
| 8 |
+
versions if they contain in-place ops.
|
| 9 |
+
|
| 10 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
from torch import nn as nn
|
| 15 |
+
from torch.nn import functional as F
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@torch.jit.script
|
| 19 |
+
def swish_jit(x, inplace: bool = False):
|
| 20 |
+
"""Swish - Described in: https://arxiv.org/abs/1710.05941
|
| 21 |
+
"""
|
| 22 |
+
return x.mul(x.sigmoid())
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@torch.jit.script
|
| 26 |
+
def mish_jit(x, _inplace: bool = False):
|
| 27 |
+
"""Mish: A Self Regularized Non-Monotonic Neural Activation Function - https://arxiv.org/abs/1908.08681
|
| 28 |
+
"""
|
| 29 |
+
return x.mul(F.softplus(x).tanh())
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class SwishJit(nn.Module):
|
| 33 |
+
def __init__(self, inplace: bool = False):
|
| 34 |
+
super(SwishJit, self).__init__()
|
| 35 |
+
|
| 36 |
+
def forward(self, x):
|
| 37 |
+
return swish_jit(x)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class MishJit(nn.Module):
|
| 41 |
+
def __init__(self, inplace: bool = False):
|
| 42 |
+
super(MishJit, self).__init__()
|
| 43 |
+
|
| 44 |
+
def forward(self, x):
|
| 45 |
+
return mish_jit(x)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@torch.jit.script
|
| 49 |
+
def hard_sigmoid_jit(x, inplace: bool = False):
|
| 50 |
+
# return F.relu6(x + 3.) / 6.
|
| 51 |
+
return (x + 3).clamp(min=0, max=6).div(6.) # clamp seems ever so slightly faster?
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class HardSigmoidJit(nn.Module):
|
| 55 |
+
def __init__(self, inplace: bool = False):
|
| 56 |
+
super(HardSigmoidJit, self).__init__()
|
| 57 |
+
|
| 58 |
+
def forward(self, x):
|
| 59 |
+
return hard_sigmoid_jit(x)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
@torch.jit.script
|
| 63 |
+
def hard_swish_jit(x, inplace: bool = False):
|
| 64 |
+
# return x * (F.relu6(x + 3.) / 6)
|
| 65 |
+
return x * (x + 3).clamp(min=0, max=6).div(6.) # clamp seems ever so slightly faster?
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class HardSwishJit(nn.Module):
|
| 69 |
+
def __init__(self, inplace: bool = False):
|
| 70 |
+
super(HardSwishJit, self).__init__()
|
| 71 |
+
|
| 72 |
+
def forward(self, x):
|
| 73 |
+
return hard_swish_jit(x)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
@torch.jit.script
|
| 77 |
+
def hard_mish_jit(x, inplace: bool = False):
|
| 78 |
+
""" Hard Mish
|
| 79 |
+
Experimental, based on notes by Mish author Diganta Misra at
|
| 80 |
+
https://github.com/digantamisra98/H-Mish/blob/0da20d4bc58e696b6803f2523c58d3c8a82782d0/README.md
|
| 81 |
+
"""
|
| 82 |
+
return 0.5 * x * (x + 2).clamp(min=0, max=2)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class HardMishJit(nn.Module):
|
| 86 |
+
def __init__(self, inplace: bool = False):
|
| 87 |
+
super(HardMishJit, self).__init__()
|
| 88 |
+
|
| 89 |
+
def forward(self, x):
|
| 90 |
+
return hard_mish_jit(x)
|
src/custom_timm/models/layers/activations_me.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Activations (memory-efficient w/ custom autograd)
|
| 2 |
+
|
| 3 |
+
A collection of activations fn and modules with a common interface so that they can
|
| 4 |
+
easily be swapped. All have an `inplace` arg even if not used.
|
| 5 |
+
|
| 6 |
+
These activations are not compatible with jit scripting or ONNX export of the model, please use either
|
| 7 |
+
the JIT or basic versions of the activations.
|
| 8 |
+
|
| 9 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
from torch import nn as nn
|
| 14 |
+
from torch.nn import functional as F
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@torch.jit.script
|
| 18 |
+
def swish_jit_fwd(x):
|
| 19 |
+
return x.mul(torch.sigmoid(x))
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@torch.jit.script
|
| 23 |
+
def swish_jit_bwd(x, grad_output):
|
| 24 |
+
x_sigmoid = torch.sigmoid(x)
|
| 25 |
+
return grad_output * (x_sigmoid * (1 + x * (1 - x_sigmoid)))
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class SwishJitAutoFn(torch.autograd.Function):
|
| 29 |
+
""" torch.jit.script optimised Swish w/ memory-efficient checkpoint
|
| 30 |
+
Inspired by conversation btw Jeremy Howard & Adam Pazske
|
| 31 |
+
https://twitter.com/jeremyphoward/status/1188251041835315200
|
| 32 |
+
"""
|
| 33 |
+
@staticmethod
|
| 34 |
+
def symbolic(g, x):
|
| 35 |
+
return g.op("Mul", x, g.op("Sigmoid", x))
|
| 36 |
+
|
| 37 |
+
@staticmethod
|
| 38 |
+
def forward(ctx, x):
|
| 39 |
+
ctx.save_for_backward(x)
|
| 40 |
+
return swish_jit_fwd(x)
|
| 41 |
+
|
| 42 |
+
@staticmethod
|
| 43 |
+
def backward(ctx, grad_output):
|
| 44 |
+
x = ctx.saved_tensors[0]
|
| 45 |
+
return swish_jit_bwd(x, grad_output)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def swish_me(x, inplace=False):
|
| 49 |
+
return SwishJitAutoFn.apply(x)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class SwishMe(nn.Module):
|
| 53 |
+
def __init__(self, inplace: bool = False):
|
| 54 |
+
super(SwishMe, self).__init__()
|
| 55 |
+
|
| 56 |
+
def forward(self, x):
|
| 57 |
+
return SwishJitAutoFn.apply(x)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
@torch.jit.script
|
| 61 |
+
def mish_jit_fwd(x):
|
| 62 |
+
return x.mul(torch.tanh(F.softplus(x)))
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@torch.jit.script
|
| 66 |
+
def mish_jit_bwd(x, grad_output):
|
| 67 |
+
x_sigmoid = torch.sigmoid(x)
|
| 68 |
+
x_tanh_sp = F.softplus(x).tanh()
|
| 69 |
+
return grad_output.mul(x_tanh_sp + x * x_sigmoid * (1 - x_tanh_sp * x_tanh_sp))
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class MishJitAutoFn(torch.autograd.Function):
|
| 73 |
+
""" Mish: A Self Regularized Non-Monotonic Neural Activation Function - https://arxiv.org/abs/1908.08681
|
| 74 |
+
A memory efficient, jit scripted variant of Mish
|
| 75 |
+
"""
|
| 76 |
+
@staticmethod
|
| 77 |
+
def forward(ctx, x):
|
| 78 |
+
ctx.save_for_backward(x)
|
| 79 |
+
return mish_jit_fwd(x)
|
| 80 |
+
|
| 81 |
+
@staticmethod
|
| 82 |
+
def backward(ctx, grad_output):
|
| 83 |
+
x = ctx.saved_tensors[0]
|
| 84 |
+
return mish_jit_bwd(x, grad_output)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def mish_me(x, inplace=False):
|
| 88 |
+
return MishJitAutoFn.apply(x)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class MishMe(nn.Module):
|
| 92 |
+
def __init__(self, inplace: bool = False):
|
| 93 |
+
super(MishMe, self).__init__()
|
| 94 |
+
|
| 95 |
+
def forward(self, x):
|
| 96 |
+
return MishJitAutoFn.apply(x)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
@torch.jit.script
|
| 100 |
+
def hard_sigmoid_jit_fwd(x, inplace: bool = False):
|
| 101 |
+
return (x + 3).clamp(min=0, max=6).div(6.)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
@torch.jit.script
|
| 105 |
+
def hard_sigmoid_jit_bwd(x, grad_output):
|
| 106 |
+
m = torch.ones_like(x) * ((x >= -3.) & (x <= 3.)) / 6.
|
| 107 |
+
return grad_output * m
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class HardSigmoidJitAutoFn(torch.autograd.Function):
|
| 111 |
+
@staticmethod
|
| 112 |
+
def forward(ctx, x):
|
| 113 |
+
ctx.save_for_backward(x)
|
| 114 |
+
return hard_sigmoid_jit_fwd(x)
|
| 115 |
+
|
| 116 |
+
@staticmethod
|
| 117 |
+
def backward(ctx, grad_output):
|
| 118 |
+
x = ctx.saved_tensors[0]
|
| 119 |
+
return hard_sigmoid_jit_bwd(x, grad_output)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def hard_sigmoid_me(x, inplace: bool = False):
|
| 123 |
+
return HardSigmoidJitAutoFn.apply(x)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
class HardSigmoidMe(nn.Module):
|
| 127 |
+
def __init__(self, inplace: bool = False):
|
| 128 |
+
super(HardSigmoidMe, self).__init__()
|
| 129 |
+
|
| 130 |
+
def forward(self, x):
|
| 131 |
+
return HardSigmoidJitAutoFn.apply(x)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
@torch.jit.script
|
| 135 |
+
def hard_swish_jit_fwd(x):
|
| 136 |
+
return x * (x + 3).clamp(min=0, max=6).div(6.)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
@torch.jit.script
|
| 140 |
+
def hard_swish_jit_bwd(x, grad_output):
|
| 141 |
+
m = torch.ones_like(x) * (x >= 3.)
|
| 142 |
+
m = torch.where((x >= -3.) & (x <= 3.), x / 3. + .5, m)
|
| 143 |
+
return grad_output * m
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
class HardSwishJitAutoFn(torch.autograd.Function):
|
| 147 |
+
"""A memory efficient, jit-scripted HardSwish activation"""
|
| 148 |
+
@staticmethod
|
| 149 |
+
def forward(ctx, x):
|
| 150 |
+
ctx.save_for_backward(x)
|
| 151 |
+
return hard_swish_jit_fwd(x)
|
| 152 |
+
|
| 153 |
+
@staticmethod
|
| 154 |
+
def backward(ctx, grad_output):
|
| 155 |
+
x = ctx.saved_tensors[0]
|
| 156 |
+
return hard_swish_jit_bwd(x, grad_output)
|
| 157 |
+
|
| 158 |
+
@staticmethod
|
| 159 |
+
def symbolic(g, self):
|
| 160 |
+
input = g.op("Add", self, g.op('Constant', value_t=torch.tensor(3, dtype=torch.float)))
|
| 161 |
+
hardtanh_ = g.op("Clip", input, g.op('Constant', value_t=torch.tensor(0, dtype=torch.float)), g.op('Constant', value_t=torch.tensor(6, dtype=torch.float)))
|
| 162 |
+
hardtanh_ = g.op("Div", hardtanh_, g.op('Constant', value_t=torch.tensor(6, dtype=torch.float)))
|
| 163 |
+
return g.op("Mul", self, hardtanh_)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def hard_swish_me(x, inplace=False):
|
| 167 |
+
return HardSwishJitAutoFn.apply(x)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
class HardSwishMe(nn.Module):
|
| 171 |
+
def __init__(self, inplace: bool = False):
|
| 172 |
+
super(HardSwishMe, self).__init__()
|
| 173 |
+
|
| 174 |
+
def forward(self, x):
|
| 175 |
+
return HardSwishJitAutoFn.apply(x)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
@torch.jit.script
|
| 179 |
+
def hard_mish_jit_fwd(x):
|
| 180 |
+
return 0.5 * x * (x + 2).clamp(min=0, max=2)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
@torch.jit.script
|
| 184 |
+
def hard_mish_jit_bwd(x, grad_output):
|
| 185 |
+
m = torch.ones_like(x) * (x >= -2.)
|
| 186 |
+
m = torch.where((x >= -2.) & (x <= 0.), x + 1., m)
|
| 187 |
+
return grad_output * m
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
class HardMishJitAutoFn(torch.autograd.Function):
|
| 191 |
+
""" A memory efficient, jit scripted variant of Hard Mish
|
| 192 |
+
Experimental, based on notes by Mish author Diganta Misra at
|
| 193 |
+
https://github.com/digantamisra98/H-Mish/blob/0da20d4bc58e696b6803f2523c58d3c8a82782d0/README.md
|
| 194 |
+
"""
|
| 195 |
+
@staticmethod
|
| 196 |
+
def forward(ctx, x):
|
| 197 |
+
ctx.save_for_backward(x)
|
| 198 |
+
return hard_mish_jit_fwd(x)
|
| 199 |
+
|
| 200 |
+
@staticmethod
|
| 201 |
+
def backward(ctx, grad_output):
|
| 202 |
+
x = ctx.saved_tensors[0]
|
| 203 |
+
return hard_mish_jit_bwd(x, grad_output)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def hard_mish_me(x, inplace: bool = False):
|
| 207 |
+
return HardMishJitAutoFn.apply(x)
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
class HardMishMe(nn.Module):
|
| 211 |
+
def __init__(self, inplace: bool = False):
|
| 212 |
+
super(HardMishMe, self).__init__()
|
| 213 |
+
|
| 214 |
+
def forward(self, x):
|
| 215 |
+
return HardMishJitAutoFn.apply(x)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
|
src/custom_timm/models/layers/adaptive_avgmax_pool.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" PyTorch selectable adaptive pooling
|
| 2 |
+
Adaptive pooling with the ability to select the type of pooling from:
|
| 3 |
+
* 'avg' - Average pooling
|
| 4 |
+
* 'max' - Max pooling
|
| 5 |
+
* 'avgmax' - Sum of average and max pooling re-scaled by 0.5
|
| 6 |
+
* 'avgmaxc' - Concatenation of average and max pooling along feature dim, doubles feature dim
|
| 7 |
+
|
| 8 |
+
Both a functional and a nn.Module version of the pooling is provided.
|
| 9 |
+
|
| 10 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 11 |
+
"""
|
| 12 |
+
import torch
|
| 13 |
+
import torch.nn as nn
|
| 14 |
+
import torch.nn.functional as F
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def adaptive_pool_feat_mult(pool_type='avg'):
|
| 18 |
+
if pool_type == 'catavgmax':
|
| 19 |
+
return 2
|
| 20 |
+
else:
|
| 21 |
+
return 1
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def adaptive_avgmax_pool2d(x, output_size=1):
|
| 25 |
+
x_avg = F.adaptive_avg_pool2d(x, output_size)
|
| 26 |
+
x_max = F.adaptive_max_pool2d(x, output_size)
|
| 27 |
+
return 0.5 * (x_avg + x_max)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def adaptive_catavgmax_pool2d(x, output_size=1):
|
| 31 |
+
x_avg = F.adaptive_avg_pool2d(x, output_size)
|
| 32 |
+
x_max = F.adaptive_max_pool2d(x, output_size)
|
| 33 |
+
return torch.cat((x_avg, x_max), 1)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def select_adaptive_pool2d(x, pool_type='avg', output_size=1):
|
| 37 |
+
"""Selectable global pooling function with dynamic input kernel size
|
| 38 |
+
"""
|
| 39 |
+
if pool_type == 'avg':
|
| 40 |
+
x = F.adaptive_avg_pool2d(x, output_size)
|
| 41 |
+
elif pool_type == 'avgmax':
|
| 42 |
+
x = adaptive_avgmax_pool2d(x, output_size)
|
| 43 |
+
elif pool_type == 'catavgmax':
|
| 44 |
+
x = adaptive_catavgmax_pool2d(x, output_size)
|
| 45 |
+
elif pool_type == 'max':
|
| 46 |
+
x = F.adaptive_max_pool2d(x, output_size)
|
| 47 |
+
else:
|
| 48 |
+
assert False, 'Invalid pool type: %s' % pool_type
|
| 49 |
+
return x
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class FastAdaptiveAvgPool2d(nn.Module):
|
| 53 |
+
def __init__(self, flatten=False):
|
| 54 |
+
super(FastAdaptiveAvgPool2d, self).__init__()
|
| 55 |
+
self.flatten = flatten
|
| 56 |
+
|
| 57 |
+
def forward(self, x):
|
| 58 |
+
return x.mean((2, 3), keepdim=not self.flatten)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class AdaptiveAvgMaxPool2d(nn.Module):
|
| 62 |
+
def __init__(self, output_size=1):
|
| 63 |
+
super(AdaptiveAvgMaxPool2d, self).__init__()
|
| 64 |
+
self.output_size = output_size
|
| 65 |
+
|
| 66 |
+
def forward(self, x):
|
| 67 |
+
return adaptive_avgmax_pool2d(x, self.output_size)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class AdaptiveCatAvgMaxPool2d(nn.Module):
|
| 71 |
+
def __init__(self, output_size=1):
|
| 72 |
+
super(AdaptiveCatAvgMaxPool2d, self).__init__()
|
| 73 |
+
self.output_size = output_size
|
| 74 |
+
|
| 75 |
+
def forward(self, x):
|
| 76 |
+
return adaptive_catavgmax_pool2d(x, self.output_size)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class SelectAdaptivePool2d(nn.Module):
|
| 80 |
+
"""Selectable global pooling layer with dynamic input kernel size
|
| 81 |
+
"""
|
| 82 |
+
def __init__(self, output_size=1, pool_type='fast', flatten=False):
|
| 83 |
+
super(SelectAdaptivePool2d, self).__init__()
|
| 84 |
+
self.pool_type = pool_type or '' # convert other falsy values to empty string for consistent TS typing
|
| 85 |
+
self.flatten = nn.Flatten(1) if flatten else nn.Identity()
|
| 86 |
+
if pool_type == '':
|
| 87 |
+
self.pool = nn.Identity() # pass through
|
| 88 |
+
elif pool_type == 'fast':
|
| 89 |
+
assert output_size == 1
|
| 90 |
+
self.pool = FastAdaptiveAvgPool2d(flatten)
|
| 91 |
+
self.flatten = nn.Identity()
|
| 92 |
+
elif pool_type == 'avg':
|
| 93 |
+
self.pool = nn.AdaptiveAvgPool2d(output_size)
|
| 94 |
+
elif pool_type == 'avgmax':
|
| 95 |
+
self.pool = AdaptiveAvgMaxPool2d(output_size)
|
| 96 |
+
elif pool_type == 'catavgmax':
|
| 97 |
+
self.pool = AdaptiveCatAvgMaxPool2d(output_size)
|
| 98 |
+
elif pool_type == 'max':
|
| 99 |
+
self.pool = nn.AdaptiveMaxPool2d(output_size)
|
| 100 |
+
else:
|
| 101 |
+
assert False, 'Invalid pool type: %s' % pool_type
|
| 102 |
+
|
| 103 |
+
def is_identity(self):
|
| 104 |
+
return not self.pool_type
|
| 105 |
+
|
| 106 |
+
def forward(self, x):
|
| 107 |
+
x = self.pool(x)
|
| 108 |
+
x = self.flatten(x)
|
| 109 |
+
return x
|
| 110 |
+
|
| 111 |
+
def feat_mult(self):
|
| 112 |
+
return adaptive_pool_feat_mult(self.pool_type)
|
| 113 |
+
|
| 114 |
+
def __repr__(self):
|
| 115 |
+
return self.__class__.__name__ + ' (' \
|
| 116 |
+
+ 'pool_type=' + self.pool_type \
|
| 117 |
+
+ ', flatten=' + str(self.flatten) + ')'
|
| 118 |
+
|
src/custom_timm/models/layers/attention_pool2d.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Attention Pool 2D
|
| 2 |
+
|
| 3 |
+
Implementations of 2D spatial feature pooling using multi-head attention instead of average pool.
|
| 4 |
+
|
| 5 |
+
Based on idea in CLIP by OpenAI, licensed Apache 2.0
|
| 6 |
+
https://github.com/openai/CLIP/blob/3b473b0e682c091a9e53623eebc1ca1657385717/clip/model.py
|
| 7 |
+
|
| 8 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 9 |
+
"""
|
| 10 |
+
from typing import Union, Tuple
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
import torch.nn as nn
|
| 14 |
+
|
| 15 |
+
from .helpers import to_2tuple
|
| 16 |
+
from .pos_embed import apply_rot_embed, RotaryEmbedding
|
| 17 |
+
from .weight_init import trunc_normal_
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class RotAttentionPool2d(nn.Module):
|
| 21 |
+
""" Attention based 2D feature pooling w/ rotary (relative) pos embedding.
|
| 22 |
+
This is a multi-head attention based replacement for (spatial) average pooling in NN architectures.
|
| 23 |
+
|
| 24 |
+
Adapted from the AttentionPool2d in CLIP w/ rotary embedding instead of learned embed.
|
| 25 |
+
https://github.com/openai/CLIP/blob/3b473b0e682c091a9e53623eebc1ca1657385717/clip/model.py
|
| 26 |
+
|
| 27 |
+
NOTE: While this impl does not require a fixed feature size, performance at differeing resolutions from
|
| 28 |
+
train varies widely and falls off dramatically. I'm not sure if there is a way around this... -RW
|
| 29 |
+
"""
|
| 30 |
+
def __init__(
|
| 31 |
+
self,
|
| 32 |
+
in_features: int,
|
| 33 |
+
out_features: int = None,
|
| 34 |
+
embed_dim: int = None,
|
| 35 |
+
num_heads: int = 4,
|
| 36 |
+
qkv_bias: bool = True,
|
| 37 |
+
):
|
| 38 |
+
super().__init__()
|
| 39 |
+
embed_dim = embed_dim or in_features
|
| 40 |
+
out_features = out_features or in_features
|
| 41 |
+
self.qkv = nn.Linear(in_features, embed_dim * 3, bias=qkv_bias)
|
| 42 |
+
self.proj = nn.Linear(embed_dim, out_features)
|
| 43 |
+
self.num_heads = num_heads
|
| 44 |
+
assert embed_dim % num_heads == 0
|
| 45 |
+
self.head_dim = embed_dim // num_heads
|
| 46 |
+
self.scale = self.head_dim ** -0.5
|
| 47 |
+
self.pos_embed = RotaryEmbedding(self.head_dim)
|
| 48 |
+
|
| 49 |
+
trunc_normal_(self.qkv.weight, std=in_features ** -0.5)
|
| 50 |
+
nn.init.zeros_(self.qkv.bias)
|
| 51 |
+
|
| 52 |
+
def forward(self, x):
|
| 53 |
+
B, _, H, W = x.shape
|
| 54 |
+
N = H * W
|
| 55 |
+
x = x.reshape(B, -1, N).permute(0, 2, 1)
|
| 56 |
+
|
| 57 |
+
x = torch.cat([x.mean(1, keepdim=True), x], dim=1)
|
| 58 |
+
|
| 59 |
+
x = self.qkv(x).reshape(B, N + 1, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4)
|
| 60 |
+
q, k, v = x[0], x[1], x[2]
|
| 61 |
+
|
| 62 |
+
qc, q = q[:, :, :1], q[:, :, 1:]
|
| 63 |
+
sin_emb, cos_emb = self.pos_embed.get_embed((H, W))
|
| 64 |
+
q = apply_rot_embed(q, sin_emb, cos_emb)
|
| 65 |
+
q = torch.cat([qc, q], dim=2)
|
| 66 |
+
|
| 67 |
+
kc, k = k[:, :, :1], k[:, :, 1:]
|
| 68 |
+
k = apply_rot_embed(k, sin_emb, cos_emb)
|
| 69 |
+
k = torch.cat([kc, k], dim=2)
|
| 70 |
+
|
| 71 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale
|
| 72 |
+
attn = attn.softmax(dim=-1)
|
| 73 |
+
|
| 74 |
+
x = (attn @ v).transpose(1, 2).reshape(B, N + 1, -1)
|
| 75 |
+
x = self.proj(x)
|
| 76 |
+
return x[:, 0]
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class AttentionPool2d(nn.Module):
|
| 80 |
+
""" Attention based 2D feature pooling w/ learned (absolute) pos embedding.
|
| 81 |
+
This is a multi-head attention based replacement for (spatial) average pooling in NN architectures.
|
| 82 |
+
|
| 83 |
+
It was based on impl in CLIP by OpenAI
|
| 84 |
+
https://github.com/openai/CLIP/blob/3b473b0e682c091a9e53623eebc1ca1657385717/clip/model.py
|
| 85 |
+
|
| 86 |
+
NOTE: This requires feature size upon construction and well prevent adaptive sizing of the network.
|
| 87 |
+
"""
|
| 88 |
+
def __init__(
|
| 89 |
+
self,
|
| 90 |
+
in_features: int,
|
| 91 |
+
feat_size: Union[int, Tuple[int, int]],
|
| 92 |
+
out_features: int = None,
|
| 93 |
+
embed_dim: int = None,
|
| 94 |
+
num_heads: int = 4,
|
| 95 |
+
qkv_bias: bool = True,
|
| 96 |
+
):
|
| 97 |
+
super().__init__()
|
| 98 |
+
|
| 99 |
+
embed_dim = embed_dim or in_features
|
| 100 |
+
out_features = out_features or in_features
|
| 101 |
+
assert embed_dim % num_heads == 0
|
| 102 |
+
self.feat_size = to_2tuple(feat_size)
|
| 103 |
+
self.qkv = nn.Linear(in_features, embed_dim * 3, bias=qkv_bias)
|
| 104 |
+
self.proj = nn.Linear(embed_dim, out_features)
|
| 105 |
+
self.num_heads = num_heads
|
| 106 |
+
self.head_dim = embed_dim // num_heads
|
| 107 |
+
self.scale = self.head_dim ** -0.5
|
| 108 |
+
|
| 109 |
+
spatial_dim = self.feat_size[0] * self.feat_size[1]
|
| 110 |
+
self.pos_embed = nn.Parameter(torch.zeros(spatial_dim + 1, in_features))
|
| 111 |
+
trunc_normal_(self.pos_embed, std=in_features ** -0.5)
|
| 112 |
+
trunc_normal_(self.qkv.weight, std=in_features ** -0.5)
|
| 113 |
+
nn.init.zeros_(self.qkv.bias)
|
| 114 |
+
|
| 115 |
+
def forward(self, x):
|
| 116 |
+
B, _, H, W = x.shape
|
| 117 |
+
N = H * W
|
| 118 |
+
assert self.feat_size[0] == H
|
| 119 |
+
assert self.feat_size[1] == W
|
| 120 |
+
x = x.reshape(B, -1, N).permute(0, 2, 1)
|
| 121 |
+
x = torch.cat([x.mean(1, keepdim=True), x], dim=1)
|
| 122 |
+
x = x + self.pos_embed.unsqueeze(0).to(x.dtype)
|
| 123 |
+
|
| 124 |
+
x = self.qkv(x).reshape(B, N + 1, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4)
|
| 125 |
+
q, k, v = x[0], x[1], x[2]
|
| 126 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale
|
| 127 |
+
attn = attn.softmax(dim=-1)
|
| 128 |
+
|
| 129 |
+
x = (attn @ v).transpose(1, 2).reshape(B, N + 1, -1)
|
| 130 |
+
x = self.proj(x)
|
| 131 |
+
return x[:, 0]
|
src/custom_timm/models/layers/blur_pool.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
BlurPool layer inspired by
|
| 3 |
+
- Kornia's Max_BlurPool2d
|
| 4 |
+
- Making Convolutional Networks Shift-Invariant Again :cite:`zhang2019shiftinvar`
|
| 5 |
+
|
| 6 |
+
Hacked together by Chris Ha and Ross Wightman
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
import numpy as np
|
| 13 |
+
from .padding import get_padding
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class BlurPool2d(nn.Module):
|
| 17 |
+
r"""Creates a module that computes blurs and downsample a given feature map.
|
| 18 |
+
See :cite:`zhang2019shiftinvar` for more details.
|
| 19 |
+
Corresponds to the Downsample class, which does blurring and subsampling
|
| 20 |
+
|
| 21 |
+
Args:
|
| 22 |
+
channels = Number of input channels
|
| 23 |
+
filt_size (int): binomial filter size for blurring. currently supports 3 (default) and 5.
|
| 24 |
+
stride (int): downsampling filter stride
|
| 25 |
+
|
| 26 |
+
Returns:
|
| 27 |
+
torch.Tensor: the transformed tensor.
|
| 28 |
+
"""
|
| 29 |
+
def __init__(self, channels, filt_size=3, stride=2) -> None:
|
| 30 |
+
super(BlurPool2d, self).__init__()
|
| 31 |
+
assert filt_size > 1
|
| 32 |
+
self.channels = channels
|
| 33 |
+
self.filt_size = filt_size
|
| 34 |
+
self.stride = stride
|
| 35 |
+
self.padding = [get_padding(filt_size, stride, dilation=1)] * 4
|
| 36 |
+
coeffs = torch.tensor((np.poly1d((0.5, 0.5)) ** (self.filt_size - 1)).coeffs.astype(np.float32))
|
| 37 |
+
blur_filter = (coeffs[:, None] * coeffs[None, :])[None, None, :, :].repeat(self.channels, 1, 1, 1)
|
| 38 |
+
self.register_buffer('filt', blur_filter, persistent=False)
|
| 39 |
+
|
| 40 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 41 |
+
x = F.pad(x, self.padding, 'reflect')
|
| 42 |
+
return F.conv2d(x, self.filt, stride=self.stride, groups=self.channels)
|
src/custom_timm/models/layers/bottleneck_attn.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Bottleneck Self Attention (Bottleneck Transformers)
|
| 2 |
+
|
| 3 |
+
Paper: `Bottleneck Transformers for Visual Recognition` - https://arxiv.org/abs/2101.11605
|
| 4 |
+
|
| 5 |
+
@misc{2101.11605,
|
| 6 |
+
Author = {Aravind Srinivas and Tsung-Yi Lin and Niki Parmar and Jonathon Shlens and Pieter Abbeel and Ashish Vaswani},
|
| 7 |
+
Title = {Bottleneck Transformers for Visual Recognition},
|
| 8 |
+
Year = {2021},
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
Based on ref gist at: https://gist.github.com/aravindsrinivas/56359b79f0ce4449bcb04ab4b56a57a2
|
| 12 |
+
|
| 13 |
+
This impl is a WIP but given that it is based on the ref gist likely not too far off.
|
| 14 |
+
|
| 15 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 16 |
+
"""
|
| 17 |
+
from typing import List
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
import torch.nn as nn
|
| 21 |
+
import torch.nn.functional as F
|
| 22 |
+
|
| 23 |
+
from .helpers import to_2tuple, make_divisible
|
| 24 |
+
from .weight_init import trunc_normal_
|
| 25 |
+
from .trace_utils import _assert
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def rel_logits_1d(q, rel_k, permute_mask: List[int]):
|
| 29 |
+
""" Compute relative logits along one dimension
|
| 30 |
+
|
| 31 |
+
As per: https://gist.github.com/aravindsrinivas/56359b79f0ce4449bcb04ab4b56a57a2
|
| 32 |
+
Originally from: `Attention Augmented Convolutional Networks` - https://arxiv.org/abs/1904.09925
|
| 33 |
+
|
| 34 |
+
Args:
|
| 35 |
+
q: (batch, heads, height, width, dim)
|
| 36 |
+
rel_k: (2 * width - 1, dim)
|
| 37 |
+
permute_mask: permute output dim according to this
|
| 38 |
+
"""
|
| 39 |
+
B, H, W, dim = q.shape
|
| 40 |
+
x = (q @ rel_k.transpose(-1, -2))
|
| 41 |
+
x = x.reshape(-1, W, 2 * W -1)
|
| 42 |
+
|
| 43 |
+
# pad to shift from relative to absolute indexing
|
| 44 |
+
x_pad = F.pad(x, [0, 1]).flatten(1)
|
| 45 |
+
x_pad = F.pad(x_pad, [0, W - 1])
|
| 46 |
+
|
| 47 |
+
# reshape and slice out the padded elements
|
| 48 |
+
x_pad = x_pad.reshape(-1, W + 1, 2 * W - 1)
|
| 49 |
+
x = x_pad[:, :W, W - 1:]
|
| 50 |
+
|
| 51 |
+
# reshape and tile
|
| 52 |
+
x = x.reshape(B, H, 1, W, W).expand(-1, -1, H, -1, -1)
|
| 53 |
+
return x.permute(permute_mask)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class PosEmbedRel(nn.Module):
|
| 57 |
+
""" Relative Position Embedding
|
| 58 |
+
As per: https://gist.github.com/aravindsrinivas/56359b79f0ce4449bcb04ab4b56a57a2
|
| 59 |
+
Originally from: `Attention Augmented Convolutional Networks` - https://arxiv.org/abs/1904.09925
|
| 60 |
+
"""
|
| 61 |
+
def __init__(self, feat_size, dim_head, scale):
|
| 62 |
+
super().__init__()
|
| 63 |
+
self.height, self.width = to_2tuple(feat_size)
|
| 64 |
+
self.dim_head = dim_head
|
| 65 |
+
self.height_rel = nn.Parameter(torch.randn(self.height * 2 - 1, dim_head) * scale)
|
| 66 |
+
self.width_rel = nn.Parameter(torch.randn(self.width * 2 - 1, dim_head) * scale)
|
| 67 |
+
|
| 68 |
+
def forward(self, q):
|
| 69 |
+
B, HW, _ = q.shape
|
| 70 |
+
|
| 71 |
+
# relative logits in width dimension.
|
| 72 |
+
q = q.reshape(B, self.height, self.width, -1)
|
| 73 |
+
rel_logits_w = rel_logits_1d(q, self.width_rel, permute_mask=(0, 1, 3, 2, 4))
|
| 74 |
+
|
| 75 |
+
# relative logits in height dimension.
|
| 76 |
+
q = q.transpose(1, 2)
|
| 77 |
+
rel_logits_h = rel_logits_1d(q, self.height_rel, permute_mask=(0, 3, 1, 4, 2))
|
| 78 |
+
|
| 79 |
+
rel_logits = rel_logits_h + rel_logits_w
|
| 80 |
+
rel_logits = rel_logits.reshape(B, HW, HW)
|
| 81 |
+
return rel_logits
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class BottleneckAttn(nn.Module):
|
| 85 |
+
""" Bottleneck Attention
|
| 86 |
+
Paper: `Bottleneck Transformers for Visual Recognition` - https://arxiv.org/abs/2101.11605
|
| 87 |
+
|
| 88 |
+
The internal dimensions of the attention module are controlled by the interaction of several arguments.
|
| 89 |
+
* the output dimension of the module is specified by dim_out, which falls back to input dim if not set
|
| 90 |
+
* the value (v) dimension is set to dim_out // num_heads, the v projection determines the output dim
|
| 91 |
+
* the query and key (qk) dimensions are determined by
|
| 92 |
+
* num_heads * dim_head if dim_head is not None
|
| 93 |
+
* num_heads * (dim_out * attn_ratio // num_heads) if dim_head is None
|
| 94 |
+
* as seen above, attn_ratio determines the ratio of q and k relative to the output if dim_head not used
|
| 95 |
+
|
| 96 |
+
Args:
|
| 97 |
+
dim (int): input dimension to the module
|
| 98 |
+
dim_out (int): output dimension of the module, same as dim if not set
|
| 99 |
+
stride (int): output stride of the module, avg pool used if stride == 2 (default: 1).
|
| 100 |
+
num_heads (int): parallel attention heads (default: 4)
|
| 101 |
+
dim_head (int): dimension of query and key heads, calculated from dim_out * attn_ratio // num_heads if not set
|
| 102 |
+
qk_ratio (float): ratio of q and k dimensions to output dimension when dim_head not set. (default: 1.0)
|
| 103 |
+
qkv_bias (bool): add bias to q, k, and v projections
|
| 104 |
+
scale_pos_embed (bool): scale the position embedding as well as Q @ K
|
| 105 |
+
"""
|
| 106 |
+
def __init__(
|
| 107 |
+
self, dim, dim_out=None, feat_size=None, stride=1, num_heads=4, dim_head=None,
|
| 108 |
+
qk_ratio=1.0, qkv_bias=False, scale_pos_embed=False):
|
| 109 |
+
super().__init__()
|
| 110 |
+
assert feat_size is not None, 'A concrete feature size matching expected input (H, W) is required'
|
| 111 |
+
dim_out = dim_out or dim
|
| 112 |
+
assert dim_out % num_heads == 0
|
| 113 |
+
self.num_heads = num_heads
|
| 114 |
+
self.dim_head_qk = dim_head or make_divisible(dim_out * qk_ratio, divisor=8) // num_heads
|
| 115 |
+
self.dim_head_v = dim_out // self.num_heads
|
| 116 |
+
self.dim_out_qk = num_heads * self.dim_head_qk
|
| 117 |
+
self.dim_out_v = num_heads * self.dim_head_v
|
| 118 |
+
self.scale = self.dim_head_qk ** -0.5
|
| 119 |
+
self.scale_pos_embed = scale_pos_embed
|
| 120 |
+
|
| 121 |
+
self.qkv = nn.Conv2d(dim, self.dim_out_qk * 2 + self.dim_out_v, 1, bias=qkv_bias)
|
| 122 |
+
|
| 123 |
+
# NOTE I'm only supporting relative pos embedding for now
|
| 124 |
+
self.pos_embed = PosEmbedRel(feat_size, dim_head=self.dim_head_qk, scale=self.scale)
|
| 125 |
+
|
| 126 |
+
self.pool = nn.AvgPool2d(2, 2) if stride == 2 else nn.Identity()
|
| 127 |
+
|
| 128 |
+
self.reset_parameters()
|
| 129 |
+
|
| 130 |
+
def reset_parameters(self):
|
| 131 |
+
trunc_normal_(self.qkv.weight, std=self.qkv.weight.shape[1] ** -0.5) # fan-in
|
| 132 |
+
trunc_normal_(self.pos_embed.height_rel, std=self.scale)
|
| 133 |
+
trunc_normal_(self.pos_embed.width_rel, std=self.scale)
|
| 134 |
+
|
| 135 |
+
def forward(self, x):
|
| 136 |
+
B, C, H, W = x.shape
|
| 137 |
+
_assert(H == self.pos_embed.height, '')
|
| 138 |
+
_assert(W == self.pos_embed.width, '')
|
| 139 |
+
|
| 140 |
+
x = self.qkv(x) # B, (2 * dim_head_qk + dim_head_v) * num_heads, H, W
|
| 141 |
+
|
| 142 |
+
# NOTE head vs channel split ordering in qkv projection was decided before I allowed qk to differ from v
|
| 143 |
+
# So, this is more verbose than if heads were before qkv splits, but throughput is not impacted.
|
| 144 |
+
q, k, v = torch.split(x, [self.dim_out_qk, self.dim_out_qk, self.dim_out_v], dim=1)
|
| 145 |
+
q = q.reshape(B * self.num_heads, self.dim_head_qk, -1).transpose(-1, -2)
|
| 146 |
+
k = k.reshape(B * self.num_heads, self.dim_head_qk, -1) # no transpose, for q @ k
|
| 147 |
+
v = v.reshape(B * self.num_heads, self.dim_head_v, -1).transpose(-1, -2)
|
| 148 |
+
|
| 149 |
+
if self.scale_pos_embed:
|
| 150 |
+
attn = (q @ k + self.pos_embed(q)) * self.scale # B * num_heads, H * W, H * W
|
| 151 |
+
else:
|
| 152 |
+
attn = (q @ k) * self.scale + self.pos_embed(q)
|
| 153 |
+
attn = attn.softmax(dim=-1)
|
| 154 |
+
|
| 155 |
+
out = (attn @ v).transpose(-1, -2).reshape(B, self.dim_out_v, H, W) # B, dim_out, H, W
|
| 156 |
+
out = self.pool(out)
|
| 157 |
+
return out
|
src/custom_timm/models/layers/cbam.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" CBAM (sort-of) Attention
|
| 2 |
+
|
| 3 |
+
Experimental impl of CBAM: Convolutional Block Attention Module: https://arxiv.org/abs/1807.06521
|
| 4 |
+
|
| 5 |
+
WARNING: Results with these attention layers have been mixed. They can significantly reduce performance on
|
| 6 |
+
some tasks, especially fine-grained it seems. I may end up removing this impl.
|
| 7 |
+
|
| 8 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 9 |
+
"""
|
| 10 |
+
import torch
|
| 11 |
+
from torch import nn as nn
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
|
| 14 |
+
from .conv_bn_act import ConvNormAct
|
| 15 |
+
from .create_act import create_act_layer, get_act_layer
|
| 16 |
+
from .helpers import make_divisible
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class ChannelAttn(nn.Module):
|
| 20 |
+
""" Original CBAM channel attention module, currently avg + max pool variant only.
|
| 21 |
+
"""
|
| 22 |
+
def __init__(
|
| 23 |
+
self, channels, rd_ratio=1./16, rd_channels=None, rd_divisor=1,
|
| 24 |
+
act_layer=nn.ReLU, gate_layer='sigmoid', mlp_bias=False):
|
| 25 |
+
super(ChannelAttn, self).__init__()
|
| 26 |
+
if not rd_channels:
|
| 27 |
+
rd_channels = make_divisible(channels * rd_ratio, rd_divisor, round_limit=0.)
|
| 28 |
+
self.fc1 = nn.Conv2d(channels, rd_channels, 1, bias=mlp_bias)
|
| 29 |
+
self.act = act_layer(inplace=True)
|
| 30 |
+
self.fc2 = nn.Conv2d(rd_channels, channels, 1, bias=mlp_bias)
|
| 31 |
+
self.gate = create_act_layer(gate_layer)
|
| 32 |
+
|
| 33 |
+
def forward(self, x):
|
| 34 |
+
x_avg = self.fc2(self.act(self.fc1(x.mean((2, 3), keepdim=True))))
|
| 35 |
+
x_max = self.fc2(self.act(self.fc1(x.amax((2, 3), keepdim=True))))
|
| 36 |
+
return x * self.gate(x_avg + x_max)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class LightChannelAttn(ChannelAttn):
|
| 40 |
+
"""An experimental 'lightweight' that sums avg + max pool first
|
| 41 |
+
"""
|
| 42 |
+
def __init__(
|
| 43 |
+
self, channels, rd_ratio=1./16, rd_channels=None, rd_divisor=1,
|
| 44 |
+
act_layer=nn.ReLU, gate_layer='sigmoid', mlp_bias=False):
|
| 45 |
+
super(LightChannelAttn, self).__init__(
|
| 46 |
+
channels, rd_ratio, rd_channels, rd_divisor, act_layer, gate_layer, mlp_bias)
|
| 47 |
+
|
| 48 |
+
def forward(self, x):
|
| 49 |
+
x_pool = 0.5 * x.mean((2, 3), keepdim=True) + 0.5 * x.amax((2, 3), keepdim=True)
|
| 50 |
+
x_attn = self.fc2(self.act(self.fc1(x_pool)))
|
| 51 |
+
return x * F.sigmoid(x_attn)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class SpatialAttn(nn.Module):
|
| 55 |
+
""" Original CBAM spatial attention module
|
| 56 |
+
"""
|
| 57 |
+
def __init__(self, kernel_size=7, gate_layer='sigmoid'):
|
| 58 |
+
super(SpatialAttn, self).__init__()
|
| 59 |
+
self.conv = ConvNormAct(2, 1, kernel_size, apply_act=False)
|
| 60 |
+
self.gate = create_act_layer(gate_layer)
|
| 61 |
+
|
| 62 |
+
def forward(self, x):
|
| 63 |
+
x_attn = torch.cat([x.mean(dim=1, keepdim=True), x.amax(dim=1, keepdim=True)], dim=1)
|
| 64 |
+
x_attn = self.conv(x_attn)
|
| 65 |
+
return x * self.gate(x_attn)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class LightSpatialAttn(nn.Module):
|
| 69 |
+
"""An experimental 'lightweight' variant that sums avg_pool and max_pool results.
|
| 70 |
+
"""
|
| 71 |
+
def __init__(self, kernel_size=7, gate_layer='sigmoid'):
|
| 72 |
+
super(LightSpatialAttn, self).__init__()
|
| 73 |
+
self.conv = ConvNormAct(1, 1, kernel_size, apply_act=False)
|
| 74 |
+
self.gate = create_act_layer(gate_layer)
|
| 75 |
+
|
| 76 |
+
def forward(self, x):
|
| 77 |
+
x_attn = 0.5 * x.mean(dim=1, keepdim=True) + 0.5 * x.amax(dim=1, keepdim=True)
|
| 78 |
+
x_attn = self.conv(x_attn)
|
| 79 |
+
return x * self.gate(x_attn)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class CbamModule(nn.Module):
|
| 83 |
+
def __init__(
|
| 84 |
+
self, channels, rd_ratio=1./16, rd_channels=None, rd_divisor=1,
|
| 85 |
+
spatial_kernel_size=7, act_layer=nn.ReLU, gate_layer='sigmoid', mlp_bias=False):
|
| 86 |
+
super(CbamModule, self).__init__()
|
| 87 |
+
self.channel = ChannelAttn(
|
| 88 |
+
channels, rd_ratio=rd_ratio, rd_channels=rd_channels,
|
| 89 |
+
rd_divisor=rd_divisor, act_layer=act_layer, gate_layer=gate_layer, mlp_bias=mlp_bias)
|
| 90 |
+
self.spatial = SpatialAttn(spatial_kernel_size, gate_layer=gate_layer)
|
| 91 |
+
|
| 92 |
+
def forward(self, x):
|
| 93 |
+
x = self.channel(x)
|
| 94 |
+
x = self.spatial(x)
|
| 95 |
+
return x
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class LightCbamModule(nn.Module):
|
| 99 |
+
def __init__(
|
| 100 |
+
self, channels, rd_ratio=1./16, rd_channels=None, rd_divisor=1,
|
| 101 |
+
spatial_kernel_size=7, act_layer=nn.ReLU, gate_layer='sigmoid', mlp_bias=False):
|
| 102 |
+
super(LightCbamModule, self).__init__()
|
| 103 |
+
self.channel = LightChannelAttn(
|
| 104 |
+
channels, rd_ratio=rd_ratio, rd_channels=rd_channels,
|
| 105 |
+
rd_divisor=rd_divisor, act_layer=act_layer, gate_layer=gate_layer, mlp_bias=mlp_bias)
|
| 106 |
+
self.spatial = LightSpatialAttn(spatial_kernel_size)
|
| 107 |
+
|
| 108 |
+
def forward(self, x):
|
| 109 |
+
x = self.channel(x)
|
| 110 |
+
x = self.spatial(x)
|
| 111 |
+
return x
|
| 112 |
+
|
src/custom_timm/models/layers/classifier.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Classifier head and layer factory
|
| 2 |
+
|
| 3 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 4 |
+
"""
|
| 5 |
+
from torch import nn as nn
|
| 6 |
+
from torch.nn import functional as F
|
| 7 |
+
|
| 8 |
+
from .adaptive_avgmax_pool import SelectAdaptivePool2d
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def _create_pool(num_features, num_classes, pool_type='avg', use_conv=False):
|
| 12 |
+
flatten_in_pool = not use_conv # flatten when we use a Linear layer after pooling
|
| 13 |
+
if not pool_type:
|
| 14 |
+
assert num_classes == 0 or use_conv,\
|
| 15 |
+
'Pooling can only be disabled if classifier is also removed or conv classifier is used'
|
| 16 |
+
flatten_in_pool = False # disable flattening if pooling is pass-through (no pooling)
|
| 17 |
+
global_pool = SelectAdaptivePool2d(pool_type=pool_type, flatten=flatten_in_pool)
|
| 18 |
+
num_pooled_features = num_features * global_pool.feat_mult()
|
| 19 |
+
return global_pool, num_pooled_features
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _create_fc(num_features, num_classes, use_conv=False):
|
| 23 |
+
if num_classes <= 0:
|
| 24 |
+
fc = nn.Identity() # pass-through (no classifier)
|
| 25 |
+
elif use_conv:
|
| 26 |
+
fc = nn.Conv2d(num_features, num_classes, 1, bias=True)
|
| 27 |
+
else:
|
| 28 |
+
fc = nn.Linear(num_features, num_classes, bias=True)
|
| 29 |
+
return fc
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def create_classifier(num_features, num_classes, pool_type='avg', use_conv=False):
|
| 33 |
+
global_pool, num_pooled_features = _create_pool(num_features, num_classes, pool_type, use_conv=use_conv)
|
| 34 |
+
fc = _create_fc(num_pooled_features, num_classes, use_conv=use_conv)
|
| 35 |
+
return global_pool, fc
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class ClassifierHead(nn.Module):
|
| 39 |
+
"""Classifier head w/ configurable global pooling and dropout."""
|
| 40 |
+
|
| 41 |
+
def __init__(self, in_chs, num_classes, pool_type='avg', drop_rate=0., use_conv=False):
|
| 42 |
+
super(ClassifierHead, self).__init__()
|
| 43 |
+
self.drop_rate = drop_rate
|
| 44 |
+
self.global_pool, num_pooled_features = _create_pool(in_chs, num_classes, pool_type, use_conv=use_conv)
|
| 45 |
+
self.fc = _create_fc(num_pooled_features, num_classes, use_conv=use_conv)
|
| 46 |
+
self.flatten = nn.Flatten(1) if use_conv and pool_type else nn.Identity()
|
| 47 |
+
|
| 48 |
+
def forward(self, x, pre_logits: bool = False):
|
| 49 |
+
x = self.global_pool(x)
|
| 50 |
+
if self.drop_rate:
|
| 51 |
+
x = F.dropout(x, p=float(self.drop_rate), training=self.training)
|
| 52 |
+
if pre_logits:
|
| 53 |
+
return x.flatten(1)
|
| 54 |
+
else:
|
| 55 |
+
x = self.fc(x)
|
| 56 |
+
return self.flatten(x)
|
src/custom_timm/models/layers/cond_conv2d.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" PyTorch Conditionally Parameterized Convolution (CondConv)
|
| 2 |
+
|
| 3 |
+
Paper: CondConv: Conditionally Parameterized Convolutions for Efficient Inference
|
| 4 |
+
(https://arxiv.org/abs/1904.04971)
|
| 5 |
+
|
| 6 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import math
|
| 10 |
+
from functools import partial
|
| 11 |
+
import numpy as np
|
| 12 |
+
import torch
|
| 13 |
+
from torch import nn as nn
|
| 14 |
+
from torch.nn import functional as F
|
| 15 |
+
|
| 16 |
+
from .helpers import to_2tuple
|
| 17 |
+
from .conv2d_same import conv2d_same
|
| 18 |
+
from .padding import get_padding_value
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def get_condconv_initializer(initializer, num_experts, expert_shape):
|
| 22 |
+
def condconv_initializer(weight):
|
| 23 |
+
"""CondConv initializer function."""
|
| 24 |
+
num_params = np.prod(expert_shape)
|
| 25 |
+
if (len(weight.shape) != 2 or weight.shape[0] != num_experts or
|
| 26 |
+
weight.shape[1] != num_params):
|
| 27 |
+
raise (ValueError(
|
| 28 |
+
'CondConv variables must have shape [num_experts, num_params]'))
|
| 29 |
+
for i in range(num_experts):
|
| 30 |
+
initializer(weight[i].view(expert_shape))
|
| 31 |
+
return condconv_initializer
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class CondConv2d(nn.Module):
|
| 35 |
+
""" Conditionally Parameterized Convolution
|
| 36 |
+
Inspired by: https://github.com/tensorflow/tpu/blob/master/models/official/efficientnet/condconv/condconv_layers.py
|
| 37 |
+
|
| 38 |
+
Grouped convolution hackery for parallel execution of the per-sample kernel filters inspired by this discussion:
|
| 39 |
+
https://github.com/pytorch/pytorch/issues/17983
|
| 40 |
+
"""
|
| 41 |
+
__constants__ = ['in_channels', 'out_channels', 'dynamic_padding']
|
| 42 |
+
|
| 43 |
+
def __init__(self, in_channels, out_channels, kernel_size=3,
|
| 44 |
+
stride=1, padding='', dilation=1, groups=1, bias=False, num_experts=4):
|
| 45 |
+
super(CondConv2d, self).__init__()
|
| 46 |
+
|
| 47 |
+
self.in_channels = in_channels
|
| 48 |
+
self.out_channels = out_channels
|
| 49 |
+
self.kernel_size = to_2tuple(kernel_size)
|
| 50 |
+
self.stride = to_2tuple(stride)
|
| 51 |
+
padding_val, is_padding_dynamic = get_padding_value(
|
| 52 |
+
padding, kernel_size, stride=stride, dilation=dilation)
|
| 53 |
+
self.dynamic_padding = is_padding_dynamic # if in forward to work with torchscript
|
| 54 |
+
self.padding = to_2tuple(padding_val)
|
| 55 |
+
self.dilation = to_2tuple(dilation)
|
| 56 |
+
self.groups = groups
|
| 57 |
+
self.num_experts = num_experts
|
| 58 |
+
|
| 59 |
+
self.weight_shape = (self.out_channels, self.in_channels // self.groups) + self.kernel_size
|
| 60 |
+
weight_num_param = 1
|
| 61 |
+
for wd in self.weight_shape:
|
| 62 |
+
weight_num_param *= wd
|
| 63 |
+
self.weight = torch.nn.Parameter(torch.Tensor(self.num_experts, weight_num_param))
|
| 64 |
+
|
| 65 |
+
if bias:
|
| 66 |
+
self.bias_shape = (self.out_channels,)
|
| 67 |
+
self.bias = torch.nn.Parameter(torch.Tensor(self.num_experts, self.out_channels))
|
| 68 |
+
else:
|
| 69 |
+
self.register_parameter('bias', None)
|
| 70 |
+
|
| 71 |
+
self.reset_parameters()
|
| 72 |
+
|
| 73 |
+
def reset_parameters(self):
|
| 74 |
+
init_weight = get_condconv_initializer(
|
| 75 |
+
partial(nn.init.kaiming_uniform_, a=math.sqrt(5)), self.num_experts, self.weight_shape)
|
| 76 |
+
init_weight(self.weight)
|
| 77 |
+
if self.bias is not None:
|
| 78 |
+
fan_in = np.prod(self.weight_shape[1:])
|
| 79 |
+
bound = 1 / math.sqrt(fan_in)
|
| 80 |
+
init_bias = get_condconv_initializer(
|
| 81 |
+
partial(nn.init.uniform_, a=-bound, b=bound), self.num_experts, self.bias_shape)
|
| 82 |
+
init_bias(self.bias)
|
| 83 |
+
|
| 84 |
+
def forward(self, x, routing_weights):
|
| 85 |
+
B, C, H, W = x.shape
|
| 86 |
+
weight = torch.matmul(routing_weights, self.weight)
|
| 87 |
+
new_weight_shape = (B * self.out_channels, self.in_channels // self.groups) + self.kernel_size
|
| 88 |
+
weight = weight.view(new_weight_shape)
|
| 89 |
+
bias = None
|
| 90 |
+
if self.bias is not None:
|
| 91 |
+
bias = torch.matmul(routing_weights, self.bias)
|
| 92 |
+
bias = bias.view(B * self.out_channels)
|
| 93 |
+
# move batch elements with channels so each batch element can be efficiently convolved with separate kernel
|
| 94 |
+
# reshape instead of view to work with channels_last input
|
| 95 |
+
x = x.reshape(1, B * C, H, W)
|
| 96 |
+
if self.dynamic_padding:
|
| 97 |
+
out = conv2d_same(
|
| 98 |
+
x, weight, bias, stride=self.stride, padding=self.padding,
|
| 99 |
+
dilation=self.dilation, groups=self.groups * B)
|
| 100 |
+
else:
|
| 101 |
+
out = F.conv2d(
|
| 102 |
+
x, weight, bias, stride=self.stride, padding=self.padding,
|
| 103 |
+
dilation=self.dilation, groups=self.groups * B)
|
| 104 |
+
out = out.permute([1, 0, 2, 3]).view(B, self.out_channels, out.shape[-2], out.shape[-1])
|
| 105 |
+
|
| 106 |
+
# Literal port (from TF definition)
|
| 107 |
+
# x = torch.split(x, 1, 0)
|
| 108 |
+
# weight = torch.split(weight, 1, 0)
|
| 109 |
+
# if self.bias is not None:
|
| 110 |
+
# bias = torch.matmul(routing_weights, self.bias)
|
| 111 |
+
# bias = torch.split(bias, 1, 0)
|
| 112 |
+
# else:
|
| 113 |
+
# bias = [None] * B
|
| 114 |
+
# out = []
|
| 115 |
+
# for xi, wi, bi in zip(x, weight, bias):
|
| 116 |
+
# wi = wi.view(*self.weight_shape)
|
| 117 |
+
# if bi is not None:
|
| 118 |
+
# bi = bi.view(*self.bias_shape)
|
| 119 |
+
# out.append(self.conv_fn(
|
| 120 |
+
# xi, wi, bi, stride=self.stride, padding=self.padding,
|
| 121 |
+
# dilation=self.dilation, groups=self.groups))
|
| 122 |
+
# out = torch.cat(out, 0)
|
| 123 |
+
return out
|
src/custom_timm/models/layers/config.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Model / Layer Config singleton state
|
| 2 |
+
"""
|
| 3 |
+
from typing import Any, Optional
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
'is_exportable', 'is_scriptable', 'is_no_jit',
|
| 7 |
+
'set_exportable', 'set_scriptable', 'set_no_jit', 'set_layer_config'
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
# Set to True if prefer to have layers with no jit optimization (includes activations)
|
| 11 |
+
_NO_JIT = False
|
| 12 |
+
|
| 13 |
+
# Set to True if prefer to have activation layers with no jit optimization
|
| 14 |
+
# NOTE not currently used as no difference between no_jit and no_activation jit as only layers obeying
|
| 15 |
+
# the jit flags so far are activations. This will change as more layers are updated and/or added.
|
| 16 |
+
_NO_ACTIVATION_JIT = False
|
| 17 |
+
|
| 18 |
+
# Set to True if exporting a model with Same padding via ONNX
|
| 19 |
+
_EXPORTABLE = False
|
| 20 |
+
|
| 21 |
+
# Set to True if wanting to use torch.jit.script on a model
|
| 22 |
+
_SCRIPTABLE = False
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def is_no_jit():
|
| 26 |
+
return _NO_JIT
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class set_no_jit:
|
| 30 |
+
def __init__(self, mode: bool) -> None:
|
| 31 |
+
global _NO_JIT
|
| 32 |
+
self.prev = _NO_JIT
|
| 33 |
+
_NO_JIT = mode
|
| 34 |
+
|
| 35 |
+
def __enter__(self) -> None:
|
| 36 |
+
pass
|
| 37 |
+
|
| 38 |
+
def __exit__(self, *args: Any) -> bool:
|
| 39 |
+
global _NO_JIT
|
| 40 |
+
_NO_JIT = self.prev
|
| 41 |
+
return False
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def is_exportable():
|
| 45 |
+
return _EXPORTABLE
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class set_exportable:
|
| 49 |
+
def __init__(self, mode: bool) -> None:
|
| 50 |
+
global _EXPORTABLE
|
| 51 |
+
self.prev = _EXPORTABLE
|
| 52 |
+
_EXPORTABLE = mode
|
| 53 |
+
|
| 54 |
+
def __enter__(self) -> None:
|
| 55 |
+
pass
|
| 56 |
+
|
| 57 |
+
def __exit__(self, *args: Any) -> bool:
|
| 58 |
+
global _EXPORTABLE
|
| 59 |
+
_EXPORTABLE = self.prev
|
| 60 |
+
return False
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def is_scriptable():
|
| 64 |
+
return _SCRIPTABLE
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class set_scriptable:
|
| 68 |
+
def __init__(self, mode: bool) -> None:
|
| 69 |
+
global _SCRIPTABLE
|
| 70 |
+
self.prev = _SCRIPTABLE
|
| 71 |
+
_SCRIPTABLE = mode
|
| 72 |
+
|
| 73 |
+
def __enter__(self) -> None:
|
| 74 |
+
pass
|
| 75 |
+
|
| 76 |
+
def __exit__(self, *args: Any) -> bool:
|
| 77 |
+
global _SCRIPTABLE
|
| 78 |
+
_SCRIPTABLE = self.prev
|
| 79 |
+
return False
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class set_layer_config:
|
| 83 |
+
""" Layer config context manager that allows setting all layer config flags at once.
|
| 84 |
+
If a flag arg is None, it will not change the current value.
|
| 85 |
+
"""
|
| 86 |
+
def __init__(
|
| 87 |
+
self,
|
| 88 |
+
scriptable: Optional[bool] = None,
|
| 89 |
+
exportable: Optional[bool] = None,
|
| 90 |
+
no_jit: Optional[bool] = None,
|
| 91 |
+
no_activation_jit: Optional[bool] = None):
|
| 92 |
+
global _SCRIPTABLE
|
| 93 |
+
global _EXPORTABLE
|
| 94 |
+
global _NO_JIT
|
| 95 |
+
global _NO_ACTIVATION_JIT
|
| 96 |
+
self.prev = _SCRIPTABLE, _EXPORTABLE, _NO_JIT, _NO_ACTIVATION_JIT
|
| 97 |
+
if scriptable is not None:
|
| 98 |
+
_SCRIPTABLE = scriptable
|
| 99 |
+
if exportable is not None:
|
| 100 |
+
_EXPORTABLE = exportable
|
| 101 |
+
if no_jit is not None:
|
| 102 |
+
_NO_JIT = no_jit
|
| 103 |
+
if no_activation_jit is not None:
|
| 104 |
+
_NO_ACTIVATION_JIT = no_activation_jit
|
| 105 |
+
|
| 106 |
+
def __enter__(self) -> None:
|
| 107 |
+
pass
|
| 108 |
+
|
| 109 |
+
def __exit__(self, *args: Any) -> bool:
|
| 110 |
+
global _SCRIPTABLE
|
| 111 |
+
global _EXPORTABLE
|
| 112 |
+
global _NO_JIT
|
| 113 |
+
global _NO_ACTIVATION_JIT
|
| 114 |
+
_SCRIPTABLE, _EXPORTABLE, _NO_JIT, _NO_ACTIVATION_JIT = self.prev
|
| 115 |
+
return False
|
src/custom_timm/models/layers/conv2d_same.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Conv2d w/ Same Padding
|
| 2 |
+
|
| 3 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 4 |
+
"""
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
import torch.nn.functional as F
|
| 8 |
+
from typing import Tuple, Optional
|
| 9 |
+
|
| 10 |
+
from .padding import pad_same, get_padding_value
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def conv2d_same(
|
| 14 |
+
x, weight: torch.Tensor, bias: Optional[torch.Tensor] = None, stride: Tuple[int, int] = (1, 1),
|
| 15 |
+
padding: Tuple[int, int] = (0, 0), dilation: Tuple[int, int] = (1, 1), groups: int = 1):
|
| 16 |
+
x = pad_same(x, weight.shape[-2:], stride, dilation)
|
| 17 |
+
return F.conv2d(x, weight, bias, stride, (0, 0), dilation, groups)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class Conv2dSame(nn.Conv2d):
|
| 21 |
+
""" Tensorflow like 'SAME' convolution wrapper for 2D convolutions
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
def __init__(self, in_channels, out_channels, kernel_size, stride=1,
|
| 25 |
+
padding=0, dilation=1, groups=1, bias=True):
|
| 26 |
+
super(Conv2dSame, self).__init__(
|
| 27 |
+
in_channels, out_channels, kernel_size, stride, 0, dilation, groups, bias)
|
| 28 |
+
|
| 29 |
+
def forward(self, x):
|
| 30 |
+
return conv2d_same(x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def create_conv2d_pad(in_chs, out_chs, kernel_size, **kwargs):
|
| 34 |
+
padding = kwargs.pop('padding', '')
|
| 35 |
+
kwargs.setdefault('bias', False)
|
| 36 |
+
padding, is_dynamic = get_padding_value(padding, kernel_size, **kwargs)
|
| 37 |
+
if is_dynamic:
|
| 38 |
+
return Conv2dSame(in_chs, out_chs, kernel_size, **kwargs)
|
| 39 |
+
else:
|
| 40 |
+
return nn.Conv2d(in_chs, out_chs, kernel_size, padding=padding, **kwargs)
|
| 41 |
+
|
| 42 |
+
|
src/custom_timm/models/layers/conv_bn_act.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Conv2d + BN + Act
|
| 2 |
+
|
| 3 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 4 |
+
"""
|
| 5 |
+
import functools
|
| 6 |
+
from torch import nn as nn
|
| 7 |
+
|
| 8 |
+
from .create_conv2d import create_conv2d
|
| 9 |
+
from .create_norm_act import get_norm_act_layer
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class ConvNormAct(nn.Module):
|
| 13 |
+
def __init__(
|
| 14 |
+
self, in_channels, out_channels, kernel_size=1, stride=1, padding='', dilation=1, groups=1,
|
| 15 |
+
bias=False, apply_act=True, norm_layer=nn.BatchNorm2d, act_layer=nn.ReLU, drop_layer=None):
|
| 16 |
+
super(ConvNormAct, self).__init__()
|
| 17 |
+
self.conv = create_conv2d(
|
| 18 |
+
in_channels, out_channels, kernel_size, stride=stride,
|
| 19 |
+
padding=padding, dilation=dilation, groups=groups, bias=bias)
|
| 20 |
+
|
| 21 |
+
# NOTE for backwards compatibility with models that use separate norm and act layer definitions
|
| 22 |
+
norm_act_layer = get_norm_act_layer(norm_layer, act_layer)
|
| 23 |
+
# NOTE for backwards (weight) compatibility, norm layer name remains `.bn`
|
| 24 |
+
norm_kwargs = dict(drop_layer=drop_layer) if drop_layer is not None else {}
|
| 25 |
+
self.bn = norm_act_layer(out_channels, apply_act=apply_act, **norm_kwargs)
|
| 26 |
+
|
| 27 |
+
@property
|
| 28 |
+
def in_channels(self):
|
| 29 |
+
return self.conv.in_channels
|
| 30 |
+
|
| 31 |
+
@property
|
| 32 |
+
def out_channels(self):
|
| 33 |
+
return self.conv.out_channels
|
| 34 |
+
|
| 35 |
+
def forward(self, x):
|
| 36 |
+
x = self.conv(x)
|
| 37 |
+
x = self.bn(x)
|
| 38 |
+
return x
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
ConvBnAct = ConvNormAct
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def create_aa(aa_layer, channels, stride=2, enable=True):
|
| 45 |
+
if not aa_layer or not enable:
|
| 46 |
+
return nn.Identity()
|
| 47 |
+
if isinstance(aa_layer, functools.partial):
|
| 48 |
+
if issubclass(aa_layer.func, nn.AvgPool2d):
|
| 49 |
+
return aa_layer()
|
| 50 |
+
else:
|
| 51 |
+
return aa_layer(channels)
|
| 52 |
+
elif issubclass(aa_layer, nn.AvgPool2d):
|
| 53 |
+
return aa_layer(stride)
|
| 54 |
+
else:
|
| 55 |
+
return aa_layer(channels=channels, stride=stride)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
class ConvNormActAa(nn.Module):
|
| 59 |
+
def __init__(
|
| 60 |
+
self, in_channels, out_channels, kernel_size=1, stride=1, padding='', dilation=1, groups=1,
|
| 61 |
+
bias=False, apply_act=True, norm_layer=nn.BatchNorm2d, act_layer=nn.ReLU, aa_layer=None, drop_layer=None):
|
| 62 |
+
super(ConvNormActAa, self).__init__()
|
| 63 |
+
use_aa = aa_layer is not None and stride == 2
|
| 64 |
+
|
| 65 |
+
self.conv = create_conv2d(
|
| 66 |
+
in_channels, out_channels, kernel_size, stride=1 if use_aa else stride,
|
| 67 |
+
padding=padding, dilation=dilation, groups=groups, bias=bias)
|
| 68 |
+
|
| 69 |
+
# NOTE for backwards compatibility with models that use separate norm and act layer definitions
|
| 70 |
+
norm_act_layer = get_norm_act_layer(norm_layer, act_layer)
|
| 71 |
+
# NOTE for backwards (weight) compatibility, norm layer name remains `.bn`
|
| 72 |
+
norm_kwargs = dict(drop_layer=drop_layer) if drop_layer is not None else {}
|
| 73 |
+
self.bn = norm_act_layer(out_channels, apply_act=apply_act, **norm_kwargs)
|
| 74 |
+
self.aa = create_aa(aa_layer, out_channels, stride=stride, enable=use_aa)
|
| 75 |
+
|
| 76 |
+
@property
|
| 77 |
+
def in_channels(self):
|
| 78 |
+
return self.conv.in_channels
|
| 79 |
+
|
| 80 |
+
@property
|
| 81 |
+
def out_channels(self):
|
| 82 |
+
return self.conv.out_channels
|
| 83 |
+
|
| 84 |
+
def forward(self, x):
|
| 85 |
+
x = self.conv(x)
|
| 86 |
+
x = self.bn(x)
|
| 87 |
+
x = self.aa(x)
|
| 88 |
+
return x
|
src/custom_timm/models/layers/create_act.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Activation Factory
|
| 2 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 3 |
+
"""
|
| 4 |
+
from typing import Union, Callable, Type
|
| 5 |
+
|
| 6 |
+
from .activations import *
|
| 7 |
+
from .activations_jit import *
|
| 8 |
+
from .activations_me import *
|
| 9 |
+
from .config import is_exportable, is_scriptable, is_no_jit
|
| 10 |
+
|
| 11 |
+
# PyTorch has an optimized, native 'silu' (aka 'swish') operator as of PyTorch 1.7.
|
| 12 |
+
# Also hardsigmoid, hardswish, and soon mish. This code will use native version if present.
|
| 13 |
+
# Eventually, the custom SiLU, Mish, Hard*, layers will be removed and only native variants will be used.
|
| 14 |
+
_has_silu = 'silu' in dir(torch.nn.functional)
|
| 15 |
+
_has_hardswish = 'hardswish' in dir(torch.nn.functional)
|
| 16 |
+
_has_hardsigmoid = 'hardsigmoid' in dir(torch.nn.functional)
|
| 17 |
+
_has_mish = 'mish' in dir(torch.nn.functional)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
_ACT_FN_DEFAULT = dict(
|
| 21 |
+
silu=F.silu if _has_silu else swish,
|
| 22 |
+
swish=F.silu if _has_silu else swish,
|
| 23 |
+
mish=F.mish if _has_mish else mish,
|
| 24 |
+
relu=F.relu,
|
| 25 |
+
relu6=F.relu6,
|
| 26 |
+
leaky_relu=F.leaky_relu,
|
| 27 |
+
elu=F.elu,
|
| 28 |
+
celu=F.celu,
|
| 29 |
+
selu=F.selu,
|
| 30 |
+
gelu=gelu,
|
| 31 |
+
sigmoid=sigmoid,
|
| 32 |
+
tanh=tanh,
|
| 33 |
+
hard_sigmoid=F.hardsigmoid if _has_hardsigmoid else hard_sigmoid,
|
| 34 |
+
hard_swish=F.hardswish if _has_hardswish else hard_swish,
|
| 35 |
+
hard_mish=hard_mish,
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
_ACT_FN_JIT = dict(
|
| 39 |
+
silu=F.silu if _has_silu else swish_jit,
|
| 40 |
+
swish=F.silu if _has_silu else swish_jit,
|
| 41 |
+
mish=F.mish if _has_mish else mish_jit,
|
| 42 |
+
hard_sigmoid=F.hardsigmoid if _has_hardsigmoid else hard_sigmoid_jit,
|
| 43 |
+
hard_swish=F.hardswish if _has_hardswish else hard_swish_jit,
|
| 44 |
+
hard_mish=hard_mish_jit
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
_ACT_FN_ME = dict(
|
| 48 |
+
silu=F.silu if _has_silu else swish_me,
|
| 49 |
+
swish=F.silu if _has_silu else swish_me,
|
| 50 |
+
mish=F.mish if _has_mish else mish_me,
|
| 51 |
+
hard_sigmoid=F.hardsigmoid if _has_hardsigmoid else hard_sigmoid_me,
|
| 52 |
+
hard_swish=F.hardswish if _has_hardswish else hard_swish_me,
|
| 53 |
+
hard_mish=hard_mish_me,
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
_ACT_FNS = (_ACT_FN_ME, _ACT_FN_JIT, _ACT_FN_DEFAULT)
|
| 57 |
+
for a in _ACT_FNS:
|
| 58 |
+
a.setdefault('hardsigmoid', a.get('hard_sigmoid'))
|
| 59 |
+
a.setdefault('hardswish', a.get('hard_swish'))
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
_ACT_LAYER_DEFAULT = dict(
|
| 63 |
+
silu=nn.SiLU if _has_silu else Swish,
|
| 64 |
+
swish=nn.SiLU if _has_silu else Swish,
|
| 65 |
+
mish=nn.Mish if _has_mish else Mish,
|
| 66 |
+
relu=nn.ReLU,
|
| 67 |
+
relu6=nn.ReLU6,
|
| 68 |
+
leaky_relu=nn.LeakyReLU,
|
| 69 |
+
elu=nn.ELU,
|
| 70 |
+
prelu=PReLU,
|
| 71 |
+
celu=nn.CELU,
|
| 72 |
+
selu=nn.SELU,
|
| 73 |
+
gelu=GELU,
|
| 74 |
+
sigmoid=Sigmoid,
|
| 75 |
+
tanh=Tanh,
|
| 76 |
+
hard_sigmoid=nn.Hardsigmoid if _has_hardsigmoid else HardSigmoid,
|
| 77 |
+
hard_swish=nn.Hardswish if _has_hardswish else HardSwish,
|
| 78 |
+
hard_mish=HardMish,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
_ACT_LAYER_JIT = dict(
|
| 82 |
+
silu=nn.SiLU if _has_silu else SwishJit,
|
| 83 |
+
swish=nn.SiLU if _has_silu else SwishJit,
|
| 84 |
+
mish=nn.Mish if _has_mish else MishJit,
|
| 85 |
+
hard_sigmoid=nn.Hardsigmoid if _has_hardsigmoid else HardSigmoidJit,
|
| 86 |
+
hard_swish=nn.Hardswish if _has_hardswish else HardSwishJit,
|
| 87 |
+
hard_mish=HardMishJit
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
_ACT_LAYER_ME = dict(
|
| 91 |
+
silu=nn.SiLU if _has_silu else SwishMe,
|
| 92 |
+
swish=nn.SiLU if _has_silu else SwishMe,
|
| 93 |
+
mish=nn.Mish if _has_mish else MishMe,
|
| 94 |
+
hard_sigmoid=nn.Hardsigmoid if _has_hardsigmoid else HardSigmoidMe,
|
| 95 |
+
hard_swish=nn.Hardswish if _has_hardswish else HardSwishMe,
|
| 96 |
+
hard_mish=HardMishMe,
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
_ACT_LAYERS = (_ACT_LAYER_ME, _ACT_LAYER_JIT, _ACT_LAYER_DEFAULT)
|
| 100 |
+
for a in _ACT_LAYERS:
|
| 101 |
+
a.setdefault('hardsigmoid', a.get('hard_sigmoid'))
|
| 102 |
+
a.setdefault('hardswish', a.get('hard_swish'))
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def get_act_fn(name: Union[Callable, str] = 'relu'):
|
| 106 |
+
""" Activation Function Factory
|
| 107 |
+
Fetching activation fns by name with this function allows export or torch script friendly
|
| 108 |
+
functions to be returned dynamically based on current config.
|
| 109 |
+
"""
|
| 110 |
+
if not name:
|
| 111 |
+
return None
|
| 112 |
+
if isinstance(name, Callable):
|
| 113 |
+
return name
|
| 114 |
+
if not (is_no_jit() or is_exportable() or is_scriptable()):
|
| 115 |
+
# If not exporting or scripting the model, first look for a memory-efficient version with
|
| 116 |
+
# custom autograd, then fallback
|
| 117 |
+
if name in _ACT_FN_ME:
|
| 118 |
+
return _ACT_FN_ME[name]
|
| 119 |
+
if not (is_no_jit() or is_exportable()):
|
| 120 |
+
if name in _ACT_FN_JIT:
|
| 121 |
+
return _ACT_FN_JIT[name]
|
| 122 |
+
return _ACT_FN_DEFAULT[name]
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def get_act_layer(name: Union[Type[nn.Module], str] = 'relu'):
|
| 126 |
+
""" Activation Layer Factory
|
| 127 |
+
Fetching activation layers by name with this function allows export or torch script friendly
|
| 128 |
+
functions to be returned dynamically based on current config.
|
| 129 |
+
"""
|
| 130 |
+
if not name:
|
| 131 |
+
return None
|
| 132 |
+
if not isinstance(name, str):
|
| 133 |
+
# callable, module, etc
|
| 134 |
+
return name
|
| 135 |
+
if not (is_no_jit() or is_exportable() or is_scriptable()):
|
| 136 |
+
if name in _ACT_LAYER_ME:
|
| 137 |
+
return _ACT_LAYER_ME[name]
|
| 138 |
+
if not (is_no_jit() or is_exportable()):
|
| 139 |
+
if name in _ACT_LAYER_JIT:
|
| 140 |
+
return _ACT_LAYER_JIT[name]
|
| 141 |
+
return _ACT_LAYER_DEFAULT[name]
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def create_act_layer(name: Union[nn.Module, str], inplace=None, **kwargs):
|
| 145 |
+
act_layer = get_act_layer(name)
|
| 146 |
+
if act_layer is None:
|
| 147 |
+
return None
|
| 148 |
+
if inplace is None:
|
| 149 |
+
return act_layer(**kwargs)
|
| 150 |
+
try:
|
| 151 |
+
return act_layer(inplace=inplace, **kwargs)
|
| 152 |
+
except TypeError:
|
| 153 |
+
# recover if act layer doesn't have inplace arg
|
| 154 |
+
return act_layer(**kwargs)
|
src/custom_timm/models/layers/create_attn.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Attention Factory
|
| 2 |
+
|
| 3 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 4 |
+
"""
|
| 5 |
+
import torch
|
| 6 |
+
from functools import partial
|
| 7 |
+
|
| 8 |
+
from .bottleneck_attn import BottleneckAttn
|
| 9 |
+
from .cbam import CbamModule, LightCbamModule
|
| 10 |
+
from .eca import EcaModule, CecaModule
|
| 11 |
+
from .gather_excite import GatherExcite
|
| 12 |
+
from .global_context import GlobalContext
|
| 13 |
+
from .halo_attn import HaloAttn
|
| 14 |
+
from .lambda_layer import LambdaLayer
|
| 15 |
+
from .non_local_attn import NonLocalAttn, BatNonLocalAttn
|
| 16 |
+
from .selective_kernel import SelectiveKernel
|
| 17 |
+
from .split_attn import SplitAttn
|
| 18 |
+
from .squeeze_excite import SEModule, EffectiveSEModule
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def get_attn(attn_type):
|
| 22 |
+
if isinstance(attn_type, torch.nn.Module):
|
| 23 |
+
return attn_type
|
| 24 |
+
module_cls = None
|
| 25 |
+
if attn_type:
|
| 26 |
+
if isinstance(attn_type, str):
|
| 27 |
+
attn_type = attn_type.lower()
|
| 28 |
+
# Lightweight attention modules (channel and/or coarse spatial).
|
| 29 |
+
# Typically added to existing network architecture blocks in addition to existing convolutions.
|
| 30 |
+
if attn_type == 'se':
|
| 31 |
+
module_cls = SEModule
|
| 32 |
+
elif attn_type == 'ese':
|
| 33 |
+
module_cls = EffectiveSEModule
|
| 34 |
+
elif attn_type == 'eca':
|
| 35 |
+
module_cls = EcaModule
|
| 36 |
+
elif attn_type == 'ecam':
|
| 37 |
+
module_cls = partial(EcaModule, use_mlp=True)
|
| 38 |
+
elif attn_type == 'ceca':
|
| 39 |
+
module_cls = CecaModule
|
| 40 |
+
elif attn_type == 'ge':
|
| 41 |
+
module_cls = GatherExcite
|
| 42 |
+
elif attn_type == 'gc':
|
| 43 |
+
module_cls = GlobalContext
|
| 44 |
+
elif attn_type == 'gca':
|
| 45 |
+
module_cls = partial(GlobalContext, fuse_add=True, fuse_scale=False)
|
| 46 |
+
elif attn_type == 'cbam':
|
| 47 |
+
module_cls = CbamModule
|
| 48 |
+
elif attn_type == 'lcbam':
|
| 49 |
+
module_cls = LightCbamModule
|
| 50 |
+
|
| 51 |
+
# Attention / attention-like modules w/ significant params
|
| 52 |
+
# Typically replace some of the existing workhorse convs in a network architecture.
|
| 53 |
+
# All of these accept a stride argument and can spatially downsample the input.
|
| 54 |
+
elif attn_type == 'sk':
|
| 55 |
+
module_cls = SelectiveKernel
|
| 56 |
+
elif attn_type == 'splat':
|
| 57 |
+
module_cls = SplitAttn
|
| 58 |
+
|
| 59 |
+
# Self-attention / attention-like modules w/ significant compute and/or params
|
| 60 |
+
# Typically replace some of the existing workhorse convs in a network architecture.
|
| 61 |
+
# All of these accept a stride argument and can spatially downsample the input.
|
| 62 |
+
elif attn_type == 'lambda':
|
| 63 |
+
return LambdaLayer
|
| 64 |
+
elif attn_type == 'bottleneck':
|
| 65 |
+
return BottleneckAttn
|
| 66 |
+
elif attn_type == 'halo':
|
| 67 |
+
return HaloAttn
|
| 68 |
+
elif attn_type == 'nl':
|
| 69 |
+
module_cls = NonLocalAttn
|
| 70 |
+
elif attn_type == 'bat':
|
| 71 |
+
module_cls = BatNonLocalAttn
|
| 72 |
+
|
| 73 |
+
# Woops!
|
| 74 |
+
else:
|
| 75 |
+
assert False, "Invalid attn module (%s)" % attn_type
|
| 76 |
+
elif isinstance(attn_type, bool):
|
| 77 |
+
if attn_type:
|
| 78 |
+
module_cls = SEModule
|
| 79 |
+
else:
|
| 80 |
+
module_cls = attn_type
|
| 81 |
+
return module_cls
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def create_attn(attn_type, channels, **kwargs):
|
| 85 |
+
module_cls = get_attn(attn_type)
|
| 86 |
+
if module_cls is not None:
|
| 87 |
+
# NOTE: it's expected the first (positional) argument of all attention layers is the # input channels
|
| 88 |
+
return module_cls(channels, **kwargs)
|
| 89 |
+
return None
|
src/custom_timm/models/layers/create_conv2d.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Create Conv2d Factory Method
|
| 2 |
+
|
| 3 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from .mixed_conv2d import MixedConv2d
|
| 7 |
+
from .cond_conv2d import CondConv2d
|
| 8 |
+
from .conv2d_same import create_conv2d_pad
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def create_conv2d(in_channels, out_channels, kernel_size, **kwargs):
|
| 12 |
+
""" Select a 2d convolution implementation based on arguments
|
| 13 |
+
Creates and returns one of torch.nn.Conv2d, Conv2dSame, MixedConv2d, or CondConv2d.
|
| 14 |
+
|
| 15 |
+
Used extensively by EfficientNet, MobileNetv3 and related networks.
|
| 16 |
+
"""
|
| 17 |
+
if isinstance(kernel_size, list):
|
| 18 |
+
assert 'num_experts' not in kwargs # MixNet + CondConv combo not supported currently
|
| 19 |
+
if 'groups' in kwargs:
|
| 20 |
+
groups = kwargs.pop('groups')
|
| 21 |
+
if groups == in_channels:
|
| 22 |
+
kwargs['depthwise'] = True
|
| 23 |
+
else:
|
| 24 |
+
assert groups == 1
|
| 25 |
+
# We're going to use only lists for defining the MixedConv2d kernel groups,
|
| 26 |
+
# ints, tuples, other iterables will continue to pass to normal conv and specify h, w.
|
| 27 |
+
m = MixedConv2d(in_channels, out_channels, kernel_size, **kwargs)
|
| 28 |
+
else:
|
| 29 |
+
depthwise = kwargs.pop('depthwise', False)
|
| 30 |
+
# for DW out_channels must be multiple of in_channels as must have out_channels % groups == 0
|
| 31 |
+
groups = in_channels if depthwise else kwargs.pop('groups', 1)
|
| 32 |
+
if 'num_experts' in kwargs and kwargs['num_experts'] > 0:
|
| 33 |
+
m = CondConv2d(in_channels, out_channels, kernel_size, groups=groups, **kwargs)
|
| 34 |
+
else:
|
| 35 |
+
m = create_conv2d_pad(in_channels, out_channels, kernel_size, groups=groups, **kwargs)
|
| 36 |
+
return m
|
src/custom_timm/models/layers/create_norm.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Norm Layer Factory
|
| 2 |
+
|
| 3 |
+
Create norm modules by string (to mirror create_act and creat_norm-act fns)
|
| 4 |
+
|
| 5 |
+
Copyright 2022 Ross Wightman
|
| 6 |
+
"""
|
| 7 |
+
import types
|
| 8 |
+
import functools
|
| 9 |
+
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
|
| 12 |
+
from .norm import GroupNorm, GroupNorm1, LayerNorm, LayerNorm2d
|
| 13 |
+
|
| 14 |
+
_NORM_MAP = dict(
|
| 15 |
+
batchnorm=nn.BatchNorm2d,
|
| 16 |
+
batchnorm2d=nn.BatchNorm2d,
|
| 17 |
+
batchnorm1d=nn.BatchNorm1d,
|
| 18 |
+
groupnorm=GroupNorm,
|
| 19 |
+
groupnorm1=GroupNorm1,
|
| 20 |
+
layernorm=LayerNorm,
|
| 21 |
+
layernorm2d=LayerNorm2d,
|
| 22 |
+
)
|
| 23 |
+
_NORM_TYPES = {m for n, m in _NORM_MAP.items()}
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def create_norm_layer(layer_name, num_features, act_layer=None, apply_act=True, **kwargs):
|
| 27 |
+
layer = get_norm_layer(layer_name, act_layer=act_layer)
|
| 28 |
+
layer_instance = layer(num_features, apply_act=apply_act, **kwargs)
|
| 29 |
+
return layer_instance
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def get_norm_layer(norm_layer):
|
| 33 |
+
assert isinstance(norm_layer, (type, str, types.FunctionType, functools.partial))
|
| 34 |
+
norm_kwargs = {}
|
| 35 |
+
|
| 36 |
+
# unbind partial fn, so args can be rebound later
|
| 37 |
+
if isinstance(norm_layer, functools.partial):
|
| 38 |
+
norm_kwargs.update(norm_layer.keywords)
|
| 39 |
+
norm_layer = norm_layer.func
|
| 40 |
+
|
| 41 |
+
if isinstance(norm_layer, str):
|
| 42 |
+
layer_name = norm_layer.replace('_', '')
|
| 43 |
+
norm_layer = _NORM_MAP.get(layer_name, None)
|
| 44 |
+
elif norm_layer in _NORM_TYPES:
|
| 45 |
+
norm_layer = norm_layer
|
| 46 |
+
elif isinstance(norm_layer, types.FunctionType):
|
| 47 |
+
# if function type, assume it is a lambda/fn that creates a norm layer
|
| 48 |
+
norm_layer = norm_layer
|
| 49 |
+
else:
|
| 50 |
+
type_name = norm_layer.__name__.lower().replace('_', '')
|
| 51 |
+
norm_layer = _NORM_MAP.get(type_name, None)
|
| 52 |
+
assert norm_layer is not None, f"No equivalent norm layer for {type_name}"
|
| 53 |
+
|
| 54 |
+
if norm_kwargs:
|
| 55 |
+
norm_layer = functools.partial(norm_layer, **norm_kwargs) # bind/rebind args
|
| 56 |
+
return norm_layer
|
src/custom_timm/models/layers/create_norm_act.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" NormAct (Normalizaiton + Activation Layer) Factory
|
| 2 |
+
|
| 3 |
+
Create norm + act combo modules that attempt to be backwards compatible with separate norm + act
|
| 4 |
+
isntances in models. Where these are used it will be possible to swap separate BN + act layers with
|
| 5 |
+
combined modules like IABN or EvoNorms.
|
| 6 |
+
|
| 7 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 8 |
+
"""
|
| 9 |
+
import types
|
| 10 |
+
import functools
|
| 11 |
+
|
| 12 |
+
from .evo_norm import *
|
| 13 |
+
from .filter_response_norm import FilterResponseNormAct2d, FilterResponseNormTlu2d
|
| 14 |
+
from .norm_act import BatchNormAct2d, GroupNormAct, LayerNormAct, LayerNormAct2d
|
| 15 |
+
from .inplace_abn import InplaceAbn
|
| 16 |
+
|
| 17 |
+
_NORM_ACT_MAP = dict(
|
| 18 |
+
batchnorm=BatchNormAct2d,
|
| 19 |
+
batchnorm2d=BatchNormAct2d,
|
| 20 |
+
groupnorm=GroupNormAct,
|
| 21 |
+
groupnorm1=functools.partial(GroupNormAct, num_groups=1),
|
| 22 |
+
layernorm=LayerNormAct,
|
| 23 |
+
layernorm2d=LayerNormAct2d,
|
| 24 |
+
evonormb0=EvoNorm2dB0,
|
| 25 |
+
evonormb1=EvoNorm2dB1,
|
| 26 |
+
evonormb2=EvoNorm2dB2,
|
| 27 |
+
evonorms0=EvoNorm2dS0,
|
| 28 |
+
evonorms0a=EvoNorm2dS0a,
|
| 29 |
+
evonorms1=EvoNorm2dS1,
|
| 30 |
+
evonorms1a=EvoNorm2dS1a,
|
| 31 |
+
evonorms2=EvoNorm2dS2,
|
| 32 |
+
evonorms2a=EvoNorm2dS2a,
|
| 33 |
+
frn=FilterResponseNormAct2d,
|
| 34 |
+
frntlu=FilterResponseNormTlu2d,
|
| 35 |
+
inplaceabn=InplaceAbn,
|
| 36 |
+
iabn=InplaceAbn,
|
| 37 |
+
)
|
| 38 |
+
_NORM_ACT_TYPES = {m for n, m in _NORM_ACT_MAP.items()}
|
| 39 |
+
# has act_layer arg to define act type
|
| 40 |
+
_NORM_ACT_REQUIRES_ARG = {
|
| 41 |
+
BatchNormAct2d, GroupNormAct, LayerNormAct, LayerNormAct2d, FilterResponseNormAct2d, InplaceAbn}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def create_norm_act_layer(layer_name, num_features, act_layer=None, apply_act=True, jit=False, **kwargs):
|
| 45 |
+
layer = get_norm_act_layer(layer_name, act_layer=act_layer)
|
| 46 |
+
layer_instance = layer(num_features, apply_act=apply_act, **kwargs)
|
| 47 |
+
if jit:
|
| 48 |
+
layer_instance = torch.jit.script(layer_instance)
|
| 49 |
+
return layer_instance
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def get_norm_act_layer(norm_layer, act_layer=None):
|
| 53 |
+
assert isinstance(norm_layer, (type, str, types.FunctionType, functools.partial))
|
| 54 |
+
assert act_layer is None or isinstance(act_layer, (type, str, types.FunctionType, functools.partial))
|
| 55 |
+
norm_act_kwargs = {}
|
| 56 |
+
|
| 57 |
+
# unbind partial fn, so args can be rebound later
|
| 58 |
+
if isinstance(norm_layer, functools.partial):
|
| 59 |
+
norm_act_kwargs.update(norm_layer.keywords)
|
| 60 |
+
norm_layer = norm_layer.func
|
| 61 |
+
|
| 62 |
+
if isinstance(norm_layer, str):
|
| 63 |
+
layer_name = norm_layer.replace('_', '').lower().split('-')[0]
|
| 64 |
+
norm_act_layer = _NORM_ACT_MAP.get(layer_name, None)
|
| 65 |
+
elif norm_layer in _NORM_ACT_TYPES:
|
| 66 |
+
norm_act_layer = norm_layer
|
| 67 |
+
elif isinstance(norm_layer, types.FunctionType):
|
| 68 |
+
# if function type, must be a lambda/fn that creates a norm_act layer
|
| 69 |
+
norm_act_layer = norm_layer
|
| 70 |
+
else:
|
| 71 |
+
type_name = norm_layer.__name__.lower()
|
| 72 |
+
if type_name.startswith('batchnorm'):
|
| 73 |
+
norm_act_layer = BatchNormAct2d
|
| 74 |
+
elif type_name.startswith('groupnorm'):
|
| 75 |
+
norm_act_layer = GroupNormAct
|
| 76 |
+
elif type_name.startswith('groupnorm1'):
|
| 77 |
+
norm_act_layer = functools.partial(GroupNormAct, num_groups=1)
|
| 78 |
+
elif type_name.startswith('layernorm2d'):
|
| 79 |
+
norm_act_layer = LayerNormAct2d
|
| 80 |
+
elif type_name.startswith('layernorm'):
|
| 81 |
+
norm_act_layer = LayerNormAct
|
| 82 |
+
else:
|
| 83 |
+
assert False, f"No equivalent norm_act layer for {type_name}"
|
| 84 |
+
|
| 85 |
+
if norm_act_layer in _NORM_ACT_REQUIRES_ARG:
|
| 86 |
+
# pass `act_layer` through for backwards compat where `act_layer=None` implies no activation.
|
| 87 |
+
# In the future, may force use of `apply_act` with `act_layer` arg bound to relevant NormAct types
|
| 88 |
+
norm_act_kwargs.setdefault('act_layer', act_layer)
|
| 89 |
+
if norm_act_kwargs:
|
| 90 |
+
norm_act_layer = functools.partial(norm_act_layer, **norm_act_kwargs) # bind/rebind args
|
| 91 |
+
return norm_act_layer
|
src/custom_timm/models/layers/drop.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" DropBlock, DropPath
|
| 2 |
+
|
| 3 |
+
PyTorch implementations of DropBlock and DropPath (Stochastic Depth) regularization layers.
|
| 4 |
+
|
| 5 |
+
Papers:
|
| 6 |
+
DropBlock: A regularization method for convolutional networks (https://arxiv.org/abs/1810.12890)
|
| 7 |
+
|
| 8 |
+
Deep Networks with Stochastic Depth (https://arxiv.org/abs/1603.09382)
|
| 9 |
+
|
| 10 |
+
Code:
|
| 11 |
+
DropBlock impl inspired by two Tensorflow impl that I liked:
|
| 12 |
+
- https://github.com/tensorflow/tpu/blob/master/models/official/resnet/resnet_model.py#L74
|
| 13 |
+
- https://github.com/clovaai/assembled-cnn/blob/master/nets/blocks.py
|
| 14 |
+
|
| 15 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 16 |
+
"""
|
| 17 |
+
import torch
|
| 18 |
+
import torch.nn as nn
|
| 19 |
+
import torch.nn.functional as F
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def drop_block_2d(
|
| 23 |
+
x, drop_prob: float = 0.1, block_size: int = 7, gamma_scale: float = 1.0,
|
| 24 |
+
with_noise: bool = False, inplace: bool = False, batchwise: bool = False):
|
| 25 |
+
""" DropBlock. See https://arxiv.org/pdf/1810.12890.pdf
|
| 26 |
+
|
| 27 |
+
DropBlock with an experimental gaussian noise option. This layer has been tested on a few training
|
| 28 |
+
runs with success, but needs further validation and possibly optimization for lower runtime impact.
|
| 29 |
+
"""
|
| 30 |
+
B, C, H, W = x.shape
|
| 31 |
+
total_size = W * H
|
| 32 |
+
clipped_block_size = min(block_size, min(W, H))
|
| 33 |
+
# seed_drop_rate, the gamma parameter
|
| 34 |
+
gamma = gamma_scale * drop_prob * total_size / clipped_block_size ** 2 / (
|
| 35 |
+
(W - block_size + 1) * (H - block_size + 1))
|
| 36 |
+
|
| 37 |
+
# Forces the block to be inside the feature map.
|
| 38 |
+
w_i, h_i = torch.meshgrid(torch.arange(W).to(x.device), torch.arange(H).to(x.device))
|
| 39 |
+
valid_block = ((w_i >= clipped_block_size // 2) & (w_i < W - (clipped_block_size - 1) // 2)) & \
|
| 40 |
+
((h_i >= clipped_block_size // 2) & (h_i < H - (clipped_block_size - 1) // 2))
|
| 41 |
+
valid_block = torch.reshape(valid_block, (1, 1, H, W)).to(dtype=x.dtype)
|
| 42 |
+
|
| 43 |
+
if batchwise:
|
| 44 |
+
# one mask for whole batch, quite a bit faster
|
| 45 |
+
uniform_noise = torch.rand((1, C, H, W), dtype=x.dtype, device=x.device)
|
| 46 |
+
else:
|
| 47 |
+
uniform_noise = torch.rand_like(x)
|
| 48 |
+
block_mask = ((2 - gamma - valid_block + uniform_noise) >= 1).to(dtype=x.dtype)
|
| 49 |
+
block_mask = -F.max_pool2d(
|
| 50 |
+
-block_mask,
|
| 51 |
+
kernel_size=clipped_block_size, # block_size,
|
| 52 |
+
stride=1,
|
| 53 |
+
padding=clipped_block_size // 2)
|
| 54 |
+
|
| 55 |
+
if with_noise:
|
| 56 |
+
normal_noise = torch.randn((1, C, H, W), dtype=x.dtype, device=x.device) if batchwise else torch.randn_like(x)
|
| 57 |
+
if inplace:
|
| 58 |
+
x.mul_(block_mask).add_(normal_noise * (1 - block_mask))
|
| 59 |
+
else:
|
| 60 |
+
x = x * block_mask + normal_noise * (1 - block_mask)
|
| 61 |
+
else:
|
| 62 |
+
normalize_scale = (block_mask.numel() / block_mask.to(dtype=torch.float32).sum().add(1e-7)).to(x.dtype)
|
| 63 |
+
if inplace:
|
| 64 |
+
x.mul_(block_mask * normalize_scale)
|
| 65 |
+
else:
|
| 66 |
+
x = x * block_mask * normalize_scale
|
| 67 |
+
return x
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def drop_block_fast_2d(
|
| 71 |
+
x: torch.Tensor, drop_prob: float = 0.1, block_size: int = 7,
|
| 72 |
+
gamma_scale: float = 1.0, with_noise: bool = False, inplace: bool = False):
|
| 73 |
+
""" DropBlock. See https://arxiv.org/pdf/1810.12890.pdf
|
| 74 |
+
|
| 75 |
+
DropBlock with an experimental gaussian noise option. Simplied from above without concern for valid
|
| 76 |
+
block mask at edges.
|
| 77 |
+
"""
|
| 78 |
+
B, C, H, W = x.shape
|
| 79 |
+
total_size = W * H
|
| 80 |
+
clipped_block_size = min(block_size, min(W, H))
|
| 81 |
+
gamma = gamma_scale * drop_prob * total_size / clipped_block_size ** 2 / (
|
| 82 |
+
(W - block_size + 1) * (H - block_size + 1))
|
| 83 |
+
|
| 84 |
+
block_mask = torch.empty_like(x).bernoulli_(gamma)
|
| 85 |
+
block_mask = F.max_pool2d(
|
| 86 |
+
block_mask.to(x.dtype), kernel_size=clipped_block_size, stride=1, padding=clipped_block_size // 2)
|
| 87 |
+
|
| 88 |
+
if with_noise:
|
| 89 |
+
normal_noise = torch.empty_like(x).normal_()
|
| 90 |
+
if inplace:
|
| 91 |
+
x.mul_(1. - block_mask).add_(normal_noise * block_mask)
|
| 92 |
+
else:
|
| 93 |
+
x = x * (1. - block_mask) + normal_noise * block_mask
|
| 94 |
+
else:
|
| 95 |
+
block_mask = 1 - block_mask
|
| 96 |
+
normalize_scale = (block_mask.numel() / block_mask.to(dtype=torch.float32).sum().add(1e-6)).to(dtype=x.dtype)
|
| 97 |
+
if inplace:
|
| 98 |
+
x.mul_(block_mask * normalize_scale)
|
| 99 |
+
else:
|
| 100 |
+
x = x * block_mask * normalize_scale
|
| 101 |
+
return x
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class DropBlock2d(nn.Module):
|
| 105 |
+
""" DropBlock. See https://arxiv.org/pdf/1810.12890.pdf
|
| 106 |
+
"""
|
| 107 |
+
|
| 108 |
+
def __init__(
|
| 109 |
+
self,
|
| 110 |
+
drop_prob: float = 0.1,
|
| 111 |
+
block_size: int = 7,
|
| 112 |
+
gamma_scale: float = 1.0,
|
| 113 |
+
with_noise: bool = False,
|
| 114 |
+
inplace: bool = False,
|
| 115 |
+
batchwise: bool = False,
|
| 116 |
+
fast: bool = True):
|
| 117 |
+
super(DropBlock2d, self).__init__()
|
| 118 |
+
self.drop_prob = drop_prob
|
| 119 |
+
self.gamma_scale = gamma_scale
|
| 120 |
+
self.block_size = block_size
|
| 121 |
+
self.with_noise = with_noise
|
| 122 |
+
self.inplace = inplace
|
| 123 |
+
self.batchwise = batchwise
|
| 124 |
+
self.fast = fast # FIXME finish comparisons of fast vs not
|
| 125 |
+
|
| 126 |
+
def forward(self, x):
|
| 127 |
+
if not self.training or not self.drop_prob:
|
| 128 |
+
return x
|
| 129 |
+
if self.fast:
|
| 130 |
+
return drop_block_fast_2d(
|
| 131 |
+
x, self.drop_prob, self.block_size, self.gamma_scale, self.with_noise, self.inplace)
|
| 132 |
+
else:
|
| 133 |
+
return drop_block_2d(
|
| 134 |
+
x, self.drop_prob, self.block_size, self.gamma_scale, self.with_noise, self.inplace, self.batchwise)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def drop_path(x, drop_prob: float = 0., training: bool = False, scale_by_keep: bool = True):
|
| 138 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
| 139 |
+
|
| 140 |
+
This is the same as the DropConnect impl I created for EfficientNet, etc networks, however,
|
| 141 |
+
the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
|
| 142 |
+
See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for
|
| 143 |
+
changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use
|
| 144 |
+
'survival rate' as the argument.
|
| 145 |
+
|
| 146 |
+
"""
|
| 147 |
+
if drop_prob == 0. or not training:
|
| 148 |
+
return x
|
| 149 |
+
keep_prob = 1 - drop_prob
|
| 150 |
+
shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
|
| 151 |
+
random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
|
| 152 |
+
if keep_prob > 0.0 and scale_by_keep:
|
| 153 |
+
random_tensor.div_(keep_prob)
|
| 154 |
+
return x * random_tensor
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
class DropPath(nn.Module):
|
| 158 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
|
| 159 |
+
"""
|
| 160 |
+
def __init__(self, drop_prob: float = 0., scale_by_keep: bool = True):
|
| 161 |
+
super(DropPath, self).__init__()
|
| 162 |
+
self.drop_prob = drop_prob
|
| 163 |
+
self.scale_by_keep = scale_by_keep
|
| 164 |
+
|
| 165 |
+
def forward(self, x):
|
| 166 |
+
return drop_path(x, self.drop_prob, self.training, self.scale_by_keep)
|
| 167 |
+
|
| 168 |
+
def extra_repr(self):
|
| 169 |
+
return f'drop_prob={round(self.drop_prob,3):0.3f}'
|
src/custom_timm/models/layers/eca.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
ECA module from ECAnet
|
| 3 |
+
|
| 4 |
+
paper: ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks
|
| 5 |
+
https://arxiv.org/abs/1910.03151
|
| 6 |
+
|
| 7 |
+
Original ECA model borrowed from https://github.com/BangguWu/ECANet
|
| 8 |
+
|
| 9 |
+
Modified circular ECA implementation and adaption for use in timm package
|
| 10 |
+
by Chris Ha https://github.com/VRandme
|
| 11 |
+
|
| 12 |
+
Original License:
|
| 13 |
+
|
| 14 |
+
MIT License
|
| 15 |
+
|
| 16 |
+
Copyright (c) 2019 BangguWu, Qilong Wang
|
| 17 |
+
|
| 18 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 19 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 20 |
+
in the Software without restriction, including without limitation the rights
|
| 21 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 22 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 23 |
+
furnished to do so, subject to the following conditions:
|
| 24 |
+
|
| 25 |
+
The above copyright notice and this permission notice shall be included in all
|
| 26 |
+
copies or substantial portions of the Software.
|
| 27 |
+
|
| 28 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 29 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 30 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 31 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 32 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 33 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 34 |
+
SOFTWARE.
|
| 35 |
+
"""
|
| 36 |
+
import math
|
| 37 |
+
from torch import nn
|
| 38 |
+
import torch.nn.functional as F
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
from .create_act import create_act_layer
|
| 42 |
+
from .helpers import make_divisible
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class EcaModule(nn.Module):
|
| 46 |
+
"""Constructs an ECA module.
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
channels: Number of channels of the input feature map for use in adaptive kernel sizes
|
| 50 |
+
for actual calculations according to channel.
|
| 51 |
+
gamma, beta: when channel is given parameters of mapping function
|
| 52 |
+
refer to original paper https://arxiv.org/pdf/1910.03151.pdf
|
| 53 |
+
(default=None. if channel size not given, use k_size given for kernel size.)
|
| 54 |
+
kernel_size: Adaptive selection of kernel size (default=3)
|
| 55 |
+
gamm: used in kernel_size calc, see above
|
| 56 |
+
beta: used in kernel_size calc, see above
|
| 57 |
+
act_layer: optional non-linearity after conv, enables conv bias, this is an experiment
|
| 58 |
+
gate_layer: gating non-linearity to use
|
| 59 |
+
"""
|
| 60 |
+
def __init__(
|
| 61 |
+
self, channels=None, kernel_size=3, gamma=2, beta=1, act_layer=None, gate_layer='sigmoid',
|
| 62 |
+
rd_ratio=1/8, rd_channels=None, rd_divisor=8, use_mlp=False):
|
| 63 |
+
super(EcaModule, self).__init__()
|
| 64 |
+
if channels is not None:
|
| 65 |
+
t = int(abs(math.log(channels, 2) + beta) / gamma)
|
| 66 |
+
kernel_size = max(t if t % 2 else t + 1, 3)
|
| 67 |
+
assert kernel_size % 2 == 1
|
| 68 |
+
padding = (kernel_size - 1) // 2
|
| 69 |
+
if use_mlp:
|
| 70 |
+
# NOTE 'mlp' mode is a timm experiment, not in paper
|
| 71 |
+
assert channels is not None
|
| 72 |
+
if rd_channels is None:
|
| 73 |
+
rd_channels = make_divisible(channels * rd_ratio, divisor=rd_divisor)
|
| 74 |
+
act_layer = act_layer or nn.ReLU
|
| 75 |
+
self.conv = nn.Conv1d(1, rd_channels, kernel_size=1, padding=0, bias=True)
|
| 76 |
+
self.act = create_act_layer(act_layer)
|
| 77 |
+
self.conv2 = nn.Conv1d(rd_channels, 1, kernel_size=kernel_size, padding=padding, bias=True)
|
| 78 |
+
else:
|
| 79 |
+
self.conv = nn.Conv1d(1, 1, kernel_size=kernel_size, padding=padding, bias=False)
|
| 80 |
+
self.act = None
|
| 81 |
+
self.conv2 = None
|
| 82 |
+
self.gate = create_act_layer(gate_layer)
|
| 83 |
+
|
| 84 |
+
def forward(self, x):
|
| 85 |
+
y = x.mean((2, 3)).view(x.shape[0], 1, -1) # view for 1d conv
|
| 86 |
+
y = self.conv(y)
|
| 87 |
+
if self.conv2 is not None:
|
| 88 |
+
y = self.act(y)
|
| 89 |
+
y = self.conv2(y)
|
| 90 |
+
y = self.gate(y).view(x.shape[0], -1, 1, 1)
|
| 91 |
+
return x * y.expand_as(x)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
EfficientChannelAttn = EcaModule # alias
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class CecaModule(nn.Module):
|
| 98 |
+
"""Constructs a circular ECA module.
|
| 99 |
+
|
| 100 |
+
ECA module where the conv uses circular padding rather than zero padding.
|
| 101 |
+
Unlike the spatial dimension, the channels do not have inherent ordering nor
|
| 102 |
+
locality. Although this module in essence, applies such an assumption, it is unnecessary
|
| 103 |
+
to limit the channels on either "edge" from being circularly adapted to each other.
|
| 104 |
+
This will fundamentally increase connectivity and possibly increase performance metrics
|
| 105 |
+
(accuracy, robustness), without significantly impacting resource metrics
|
| 106 |
+
(parameter size, throughput,latency, etc)
|
| 107 |
+
|
| 108 |
+
Args:
|
| 109 |
+
channels: Number of channels of the input feature map for use in adaptive kernel sizes
|
| 110 |
+
for actual calculations according to channel.
|
| 111 |
+
gamma, beta: when channel is given parameters of mapping function
|
| 112 |
+
refer to original paper https://arxiv.org/pdf/1910.03151.pdf
|
| 113 |
+
(default=None. if channel size not given, use k_size given for kernel size.)
|
| 114 |
+
kernel_size: Adaptive selection of kernel size (default=3)
|
| 115 |
+
gamm: used in kernel_size calc, see above
|
| 116 |
+
beta: used in kernel_size calc, see above
|
| 117 |
+
act_layer: optional non-linearity after conv, enables conv bias, this is an experiment
|
| 118 |
+
gate_layer: gating non-linearity to use
|
| 119 |
+
"""
|
| 120 |
+
|
| 121 |
+
def __init__(self, channels=None, kernel_size=3, gamma=2, beta=1, act_layer=None, gate_layer='sigmoid'):
|
| 122 |
+
super(CecaModule, self).__init__()
|
| 123 |
+
if channels is not None:
|
| 124 |
+
t = int(abs(math.log(channels, 2) + beta) / gamma)
|
| 125 |
+
kernel_size = max(t if t % 2 else t + 1, 3)
|
| 126 |
+
has_act = act_layer is not None
|
| 127 |
+
assert kernel_size % 2 == 1
|
| 128 |
+
|
| 129 |
+
# PyTorch circular padding mode is buggy as of pytorch 1.4
|
| 130 |
+
# see https://github.com/pytorch/pytorch/pull/17240
|
| 131 |
+
# implement manual circular padding
|
| 132 |
+
self.padding = (kernel_size - 1) // 2
|
| 133 |
+
self.conv = nn.Conv1d(1, 1, kernel_size=kernel_size, padding=0, bias=has_act)
|
| 134 |
+
self.gate = create_act_layer(gate_layer)
|
| 135 |
+
|
| 136 |
+
def forward(self, x):
|
| 137 |
+
y = x.mean((2, 3)).view(x.shape[0], 1, -1)
|
| 138 |
+
# Manually implement circular padding, F.pad does not seemed to be bugged
|
| 139 |
+
y = F.pad(y, (self.padding, self.padding), mode='circular')
|
| 140 |
+
y = self.conv(y)
|
| 141 |
+
y = self.gate(y).view(x.shape[0], -1, 1, 1)
|
| 142 |
+
return x * y.expand_as(x)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
CircularEfficientChannelAttn = CecaModule
|
src/custom_timm/models/layers/evo_norm.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" EvoNorm in PyTorch
|
| 2 |
+
|
| 3 |
+
Based on `Evolving Normalization-Activation Layers` - https://arxiv.org/abs/2004.02967
|
| 4 |
+
@inproceedings{NEURIPS2020,
|
| 5 |
+
author = {Liu, Hanxiao and Brock, Andy and Simonyan, Karen and Le, Quoc},
|
| 6 |
+
booktitle = {Advances in Neural Information Processing Systems},
|
| 7 |
+
editor = {H. Larochelle and M. Ranzato and R. Hadsell and M. F. Balcan and H. Lin},
|
| 8 |
+
pages = {13539--13550},
|
| 9 |
+
publisher = {Curran Associates, Inc.},
|
| 10 |
+
title = {Evolving Normalization-Activation Layers},
|
| 11 |
+
url = {https://proceedings.neurips.cc/paper/2020/file/9d4c03631b8b0c85ae08bf05eda37d0f-Paper.pdf},
|
| 12 |
+
volume = {33},
|
| 13 |
+
year = {2020}
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
An attempt at getting decent performing EvoNorms running in PyTorch.
|
| 17 |
+
While faster than other PyTorch impl, still quite a ways off the built-in BatchNorm
|
| 18 |
+
in terms of memory usage and throughput on GPUs.
|
| 19 |
+
|
| 20 |
+
I'm testing these modules on TPU w/ PyTorch XLA. Promising start but
|
| 21 |
+
currently working around some issues with builtin torch/tensor.var/std. Unlike
|
| 22 |
+
GPU, similar train speeds for EvoNormS variants and BatchNorm.
|
| 23 |
+
|
| 24 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 25 |
+
"""
|
| 26 |
+
from typing import Sequence, Union
|
| 27 |
+
|
| 28 |
+
import torch
|
| 29 |
+
import torch.nn as nn
|
| 30 |
+
import torch.nn.functional as F
|
| 31 |
+
|
| 32 |
+
from .create_act import create_act_layer
|
| 33 |
+
from .trace_utils import _assert
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def instance_std(x, eps: float = 1e-5):
|
| 37 |
+
std = x.float().var(dim=(2, 3), unbiased=False, keepdim=True).add(eps).sqrt().to(x.dtype)
|
| 38 |
+
return std.expand(x.shape)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def instance_std_tpu(x, eps: float = 1e-5):
|
| 42 |
+
std = manual_var(x, dim=(2, 3)).add(eps).sqrt()
|
| 43 |
+
return std.expand(x.shape)
|
| 44 |
+
# instance_std = instance_std_tpu
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def instance_rms(x, eps: float = 1e-5):
|
| 48 |
+
rms = x.float().square().mean(dim=(2, 3), keepdim=True).add(eps).sqrt().to(x.dtype)
|
| 49 |
+
return rms.expand(x.shape)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def manual_var(x, dim: Union[int, Sequence[int]], diff_sqm: bool = False):
|
| 53 |
+
xm = x.mean(dim=dim, keepdim=True)
|
| 54 |
+
if diff_sqm:
|
| 55 |
+
# difference of squared mean and mean squared, faster on TPU can be less stable
|
| 56 |
+
var = ((x * x).mean(dim=dim, keepdim=True) - (xm * xm)).clamp(0)
|
| 57 |
+
else:
|
| 58 |
+
var = ((x - xm) * (x - xm)).mean(dim=dim, keepdim=True)
|
| 59 |
+
return var
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def group_std(x, groups: int = 32, eps: float = 1e-5, flatten: bool = False):
|
| 63 |
+
B, C, H, W = x.shape
|
| 64 |
+
x_dtype = x.dtype
|
| 65 |
+
_assert(C % groups == 0, '')
|
| 66 |
+
if flatten:
|
| 67 |
+
x = x.reshape(B, groups, -1) # FIXME simpler shape causing TPU / XLA issues
|
| 68 |
+
std = x.float().var(dim=2, unbiased=False, keepdim=True).add(eps).sqrt().to(x_dtype)
|
| 69 |
+
else:
|
| 70 |
+
x = x.reshape(B, groups, C // groups, H, W)
|
| 71 |
+
std = x.float().var(dim=(2, 3, 4), unbiased=False, keepdim=True).add(eps).sqrt().to(x_dtype)
|
| 72 |
+
return std.expand(x.shape).reshape(B, C, H, W)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def group_std_tpu(x, groups: int = 32, eps: float = 1e-5, diff_sqm: bool = False, flatten: bool = False):
|
| 76 |
+
# This is a workaround for some stability / odd behaviour of .var and .std
|
| 77 |
+
# running on PyTorch XLA w/ TPUs. These manual var impl are producing much better results
|
| 78 |
+
B, C, H, W = x.shape
|
| 79 |
+
_assert(C % groups == 0, '')
|
| 80 |
+
if flatten:
|
| 81 |
+
x = x.reshape(B, groups, -1) # FIXME simpler shape causing TPU / XLA issues
|
| 82 |
+
var = manual_var(x, dim=-1, diff_sqm=diff_sqm)
|
| 83 |
+
else:
|
| 84 |
+
x = x.reshape(B, groups, C // groups, H, W)
|
| 85 |
+
var = manual_var(x, dim=(2, 3, 4), diff_sqm=diff_sqm)
|
| 86 |
+
return var.add(eps).sqrt().expand(x.shape).reshape(B, C, H, W)
|
| 87 |
+
#group_std = group_std_tpu # FIXME TPU temporary
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def group_rms(x, groups: int = 32, eps: float = 1e-5):
|
| 91 |
+
B, C, H, W = x.shape
|
| 92 |
+
_assert(C % groups == 0, '')
|
| 93 |
+
x_dtype = x.dtype
|
| 94 |
+
x = x.reshape(B, groups, C // groups, H, W)
|
| 95 |
+
rms = x.float().square().mean(dim=(2, 3, 4), keepdim=True).add(eps).sqrt_().to(x_dtype)
|
| 96 |
+
return rms.expand(x.shape).reshape(B, C, H, W)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class EvoNorm2dB0(nn.Module):
|
| 100 |
+
def __init__(self, num_features, apply_act=True, momentum=0.1, eps=1e-3, **_):
|
| 101 |
+
super().__init__()
|
| 102 |
+
self.apply_act = apply_act # apply activation (non-linearity)
|
| 103 |
+
self.momentum = momentum
|
| 104 |
+
self.eps = eps
|
| 105 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 106 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 107 |
+
self.v = nn.Parameter(torch.ones(num_features)) if apply_act else None
|
| 108 |
+
self.register_buffer('running_var', torch.ones(num_features))
|
| 109 |
+
self.reset_parameters()
|
| 110 |
+
|
| 111 |
+
def reset_parameters(self):
|
| 112 |
+
nn.init.ones_(self.weight)
|
| 113 |
+
nn.init.zeros_(self.bias)
|
| 114 |
+
if self.v is not None:
|
| 115 |
+
nn.init.ones_(self.v)
|
| 116 |
+
|
| 117 |
+
def forward(self, x):
|
| 118 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 119 |
+
x_dtype = x.dtype
|
| 120 |
+
v_shape = (1, -1, 1, 1)
|
| 121 |
+
if self.v is not None:
|
| 122 |
+
if self.training:
|
| 123 |
+
var = x.float().var(dim=(0, 2, 3), unbiased=False)
|
| 124 |
+
# var = manual_var(x, dim=(0, 2, 3)).squeeze()
|
| 125 |
+
n = x.numel() / x.shape[1]
|
| 126 |
+
self.running_var.copy_(
|
| 127 |
+
self.running_var * (1 - self.momentum) +
|
| 128 |
+
var.detach() * self.momentum * (n / (n - 1)))
|
| 129 |
+
else:
|
| 130 |
+
var = self.running_var
|
| 131 |
+
left = var.add(self.eps).sqrt_().to(x_dtype).view(v_shape).expand_as(x)
|
| 132 |
+
v = self.v.to(x_dtype).view(v_shape)
|
| 133 |
+
right = x * v + instance_std(x, self.eps)
|
| 134 |
+
x = x / left.max(right)
|
| 135 |
+
return x * self.weight.to(x_dtype).view(v_shape) + self.bias.to(x_dtype).view(v_shape)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class EvoNorm2dB1(nn.Module):
|
| 139 |
+
def __init__(self, num_features, apply_act=True, momentum=0.1, eps=1e-5, **_):
|
| 140 |
+
super().__init__()
|
| 141 |
+
self.apply_act = apply_act # apply activation (non-linearity)
|
| 142 |
+
self.momentum = momentum
|
| 143 |
+
self.eps = eps
|
| 144 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 145 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 146 |
+
self.register_buffer('running_var', torch.ones(num_features))
|
| 147 |
+
self.reset_parameters()
|
| 148 |
+
|
| 149 |
+
def reset_parameters(self):
|
| 150 |
+
nn.init.ones_(self.weight)
|
| 151 |
+
nn.init.zeros_(self.bias)
|
| 152 |
+
|
| 153 |
+
def forward(self, x):
|
| 154 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 155 |
+
x_dtype = x.dtype
|
| 156 |
+
v_shape = (1, -1, 1, 1)
|
| 157 |
+
if self.apply_act:
|
| 158 |
+
if self.training:
|
| 159 |
+
var = x.float().var(dim=(0, 2, 3), unbiased=False)
|
| 160 |
+
n = x.numel() / x.shape[1]
|
| 161 |
+
self.running_var.copy_(
|
| 162 |
+
self.running_var * (1 - self.momentum) +
|
| 163 |
+
var.detach().to(self.running_var.dtype) * self.momentum * (n / (n - 1)))
|
| 164 |
+
else:
|
| 165 |
+
var = self.running_var
|
| 166 |
+
var = var.to(x_dtype).view(v_shape)
|
| 167 |
+
left = var.add(self.eps).sqrt_()
|
| 168 |
+
right = (x + 1) * instance_rms(x, self.eps)
|
| 169 |
+
x = x / left.max(right)
|
| 170 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
class EvoNorm2dB2(nn.Module):
|
| 174 |
+
def __init__(self, num_features, apply_act=True, momentum=0.1, eps=1e-5, **_):
|
| 175 |
+
super().__init__()
|
| 176 |
+
self.apply_act = apply_act # apply activation (non-linearity)
|
| 177 |
+
self.momentum = momentum
|
| 178 |
+
self.eps = eps
|
| 179 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 180 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 181 |
+
self.register_buffer('running_var', torch.ones(num_features))
|
| 182 |
+
self.reset_parameters()
|
| 183 |
+
|
| 184 |
+
def reset_parameters(self):
|
| 185 |
+
nn.init.ones_(self.weight)
|
| 186 |
+
nn.init.zeros_(self.bias)
|
| 187 |
+
|
| 188 |
+
def forward(self, x):
|
| 189 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 190 |
+
x_dtype = x.dtype
|
| 191 |
+
v_shape = (1, -1, 1, 1)
|
| 192 |
+
if self.apply_act:
|
| 193 |
+
if self.training:
|
| 194 |
+
var = x.float().var(dim=(0, 2, 3), unbiased=False)
|
| 195 |
+
n = x.numel() / x.shape[1]
|
| 196 |
+
self.running_var.copy_(
|
| 197 |
+
self.running_var * (1 - self.momentum) +
|
| 198 |
+
var.detach().to(self.running_var.dtype) * self.momentum * (n / (n - 1)))
|
| 199 |
+
else:
|
| 200 |
+
var = self.running_var
|
| 201 |
+
var = var.to(x_dtype).view(v_shape)
|
| 202 |
+
left = var.add(self.eps).sqrt_()
|
| 203 |
+
right = instance_rms(x, self.eps) - x
|
| 204 |
+
x = x / left.max(right)
|
| 205 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
class EvoNorm2dS0(nn.Module):
|
| 209 |
+
def __init__(self, num_features, groups=32, group_size=None, apply_act=True, eps=1e-5, **_):
|
| 210 |
+
super().__init__()
|
| 211 |
+
self.apply_act = apply_act # apply activation (non-linearity)
|
| 212 |
+
if group_size:
|
| 213 |
+
assert num_features % group_size == 0
|
| 214 |
+
self.groups = num_features // group_size
|
| 215 |
+
else:
|
| 216 |
+
self.groups = groups
|
| 217 |
+
self.eps = eps
|
| 218 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 219 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 220 |
+
self.v = nn.Parameter(torch.ones(num_features)) if apply_act else None
|
| 221 |
+
self.reset_parameters()
|
| 222 |
+
|
| 223 |
+
def reset_parameters(self):
|
| 224 |
+
nn.init.ones_(self.weight)
|
| 225 |
+
nn.init.zeros_(self.bias)
|
| 226 |
+
if self.v is not None:
|
| 227 |
+
nn.init.ones_(self.v)
|
| 228 |
+
|
| 229 |
+
def forward(self, x):
|
| 230 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 231 |
+
x_dtype = x.dtype
|
| 232 |
+
v_shape = (1, -1, 1, 1)
|
| 233 |
+
if self.v is not None:
|
| 234 |
+
v = self.v.view(v_shape).to(x_dtype)
|
| 235 |
+
x = x * (x * v).sigmoid() / group_std(x, self.groups, self.eps)
|
| 236 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
class EvoNorm2dS0a(EvoNorm2dS0):
|
| 240 |
+
def __init__(self, num_features, groups=32, group_size=None, apply_act=True, eps=1e-3, **_):
|
| 241 |
+
super().__init__(
|
| 242 |
+
num_features, groups=groups, group_size=group_size, apply_act=apply_act, eps=eps)
|
| 243 |
+
|
| 244 |
+
def forward(self, x):
|
| 245 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 246 |
+
x_dtype = x.dtype
|
| 247 |
+
v_shape = (1, -1, 1, 1)
|
| 248 |
+
d = group_std(x, self.groups, self.eps)
|
| 249 |
+
if self.v is not None:
|
| 250 |
+
v = self.v.view(v_shape).to(x_dtype)
|
| 251 |
+
x = x * (x * v).sigmoid()
|
| 252 |
+
x = x / d
|
| 253 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
class EvoNorm2dS1(nn.Module):
|
| 257 |
+
def __init__(
|
| 258 |
+
self, num_features, groups=32, group_size=None,
|
| 259 |
+
apply_act=True, act_layer=None, eps=1e-5, **_):
|
| 260 |
+
super().__init__()
|
| 261 |
+
act_layer = act_layer or nn.SiLU
|
| 262 |
+
self.apply_act = apply_act # apply activation (non-linearity)
|
| 263 |
+
if act_layer is not None and apply_act:
|
| 264 |
+
self.act = create_act_layer(act_layer)
|
| 265 |
+
else:
|
| 266 |
+
self.act = nn.Identity()
|
| 267 |
+
if group_size:
|
| 268 |
+
assert num_features % group_size == 0
|
| 269 |
+
self.groups = num_features // group_size
|
| 270 |
+
else:
|
| 271 |
+
self.groups = groups
|
| 272 |
+
self.eps = eps
|
| 273 |
+
self.pre_act_norm = False
|
| 274 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 275 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 276 |
+
self.reset_parameters()
|
| 277 |
+
|
| 278 |
+
def reset_parameters(self):
|
| 279 |
+
nn.init.ones_(self.weight)
|
| 280 |
+
nn.init.zeros_(self.bias)
|
| 281 |
+
|
| 282 |
+
def forward(self, x):
|
| 283 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 284 |
+
x_dtype = x.dtype
|
| 285 |
+
v_shape = (1, -1, 1, 1)
|
| 286 |
+
if self.apply_act:
|
| 287 |
+
x = self.act(x) / group_std(x, self.groups, self.eps)
|
| 288 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
class EvoNorm2dS1a(EvoNorm2dS1):
|
| 292 |
+
def __init__(
|
| 293 |
+
self, num_features, groups=32, group_size=None,
|
| 294 |
+
apply_act=True, act_layer=None, eps=1e-3, **_):
|
| 295 |
+
super().__init__(
|
| 296 |
+
num_features, groups=groups, group_size=group_size, apply_act=apply_act, act_layer=act_layer, eps=eps)
|
| 297 |
+
|
| 298 |
+
def forward(self, x):
|
| 299 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 300 |
+
x_dtype = x.dtype
|
| 301 |
+
v_shape = (1, -1, 1, 1)
|
| 302 |
+
x = self.act(x) / group_std(x, self.groups, self.eps)
|
| 303 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
class EvoNorm2dS2(nn.Module):
|
| 307 |
+
def __init__(
|
| 308 |
+
self, num_features, groups=32, group_size=None,
|
| 309 |
+
apply_act=True, act_layer=None, eps=1e-5, **_):
|
| 310 |
+
super().__init__()
|
| 311 |
+
act_layer = act_layer or nn.SiLU
|
| 312 |
+
self.apply_act = apply_act # apply activation (non-linearity)
|
| 313 |
+
if act_layer is not None and apply_act:
|
| 314 |
+
self.act = create_act_layer(act_layer)
|
| 315 |
+
else:
|
| 316 |
+
self.act = nn.Identity()
|
| 317 |
+
if group_size:
|
| 318 |
+
assert num_features % group_size == 0
|
| 319 |
+
self.groups = num_features // group_size
|
| 320 |
+
else:
|
| 321 |
+
self.groups = groups
|
| 322 |
+
self.eps = eps
|
| 323 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 324 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 325 |
+
self.reset_parameters()
|
| 326 |
+
|
| 327 |
+
def reset_parameters(self):
|
| 328 |
+
nn.init.ones_(self.weight)
|
| 329 |
+
nn.init.zeros_(self.bias)
|
| 330 |
+
|
| 331 |
+
def forward(self, x):
|
| 332 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 333 |
+
x_dtype = x.dtype
|
| 334 |
+
v_shape = (1, -1, 1, 1)
|
| 335 |
+
if self.apply_act:
|
| 336 |
+
x = self.act(x) / group_rms(x, self.groups, self.eps)
|
| 337 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
class EvoNorm2dS2a(EvoNorm2dS2):
|
| 341 |
+
def __init__(
|
| 342 |
+
self, num_features, groups=32, group_size=None,
|
| 343 |
+
apply_act=True, act_layer=None, eps=1e-3, **_):
|
| 344 |
+
super().__init__(
|
| 345 |
+
num_features, groups=groups, group_size=group_size, apply_act=apply_act, act_layer=act_layer, eps=eps)
|
| 346 |
+
|
| 347 |
+
def forward(self, x):
|
| 348 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 349 |
+
x_dtype = x.dtype
|
| 350 |
+
v_shape = (1, -1, 1, 1)
|
| 351 |
+
x = self.act(x) / group_rms(x, self.groups, self.eps)
|
| 352 |
+
return x * self.weight.view(v_shape).to(x_dtype) + self.bias.view(v_shape).to(x_dtype)
|
src/custom_timm/models/layers/fast_norm.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" 'Fast' Normalization Functions
|
| 2 |
+
|
| 3 |
+
For GroupNorm and LayerNorm these functions bypass typical AMP upcast to float32.
|
| 4 |
+
|
| 5 |
+
Additionally, for LayerNorm, the APEX fused LN is used if available (which also does not upcast)
|
| 6 |
+
|
| 7 |
+
Hacked together by / Copyright 2022 Ross Wightman
|
| 8 |
+
"""
|
| 9 |
+
from typing import List, Optional
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
from torch.nn import functional as F
|
| 13 |
+
|
| 14 |
+
try:
|
| 15 |
+
from apex.normalization.fused_layer_norm import fused_layer_norm_affine
|
| 16 |
+
has_apex = True
|
| 17 |
+
except ImportError:
|
| 18 |
+
has_apex = False
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# fast (ie lower precision LN) can be disabled with this flag if issues crop up
|
| 22 |
+
_USE_FAST_NORM = False # defaulting to False for now
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def is_fast_norm():
|
| 26 |
+
return _USE_FAST_NORM
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def set_fast_norm(enable=True):
|
| 30 |
+
global _USE_FAST_NORM
|
| 31 |
+
_USE_FAST_NORM = enable
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def fast_group_norm(
|
| 35 |
+
x: torch.Tensor,
|
| 36 |
+
num_groups: int,
|
| 37 |
+
weight: Optional[torch.Tensor] = None,
|
| 38 |
+
bias: Optional[torch.Tensor] = None,
|
| 39 |
+
eps: float = 1e-5
|
| 40 |
+
) -> torch.Tensor:
|
| 41 |
+
if torch.jit.is_scripting():
|
| 42 |
+
# currently cannot use is_autocast_enabled within torchscript
|
| 43 |
+
return F.group_norm(x, num_groups, weight, bias, eps)
|
| 44 |
+
|
| 45 |
+
if torch.is_autocast_enabled():
|
| 46 |
+
# normally native AMP casts GN inputs to float32
|
| 47 |
+
# here we use the low precision autocast dtype
|
| 48 |
+
# FIXME what to do re CPU autocast?
|
| 49 |
+
dt = torch.get_autocast_gpu_dtype()
|
| 50 |
+
x, weight, bias = x.to(dt), weight.to(dt), bias.to(dt)
|
| 51 |
+
|
| 52 |
+
with torch.cuda.amp.autocast(enabled=False):
|
| 53 |
+
return F.group_norm(x, num_groups, weight, bias, eps)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def fast_layer_norm(
|
| 57 |
+
x: torch.Tensor,
|
| 58 |
+
normalized_shape: List[int],
|
| 59 |
+
weight: Optional[torch.Tensor] = None,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
eps: float = 1e-5
|
| 62 |
+
) -> torch.Tensor:
|
| 63 |
+
if torch.jit.is_scripting():
|
| 64 |
+
# currently cannot use is_autocast_enabled within torchscript
|
| 65 |
+
return F.layer_norm(x, normalized_shape, weight, bias, eps)
|
| 66 |
+
|
| 67 |
+
if has_apex:
|
| 68 |
+
return fused_layer_norm_affine(x, weight, bias, normalized_shape, eps)
|
| 69 |
+
|
| 70 |
+
if torch.is_autocast_enabled():
|
| 71 |
+
# normally native AMP casts LN inputs to float32
|
| 72 |
+
# apex LN does not, this is behaving like Apex
|
| 73 |
+
dt = torch.get_autocast_gpu_dtype()
|
| 74 |
+
# FIXME what to do re CPU autocast?
|
| 75 |
+
x, weight, bias = x.to(dt), weight.to(dt), bias.to(dt)
|
| 76 |
+
|
| 77 |
+
with torch.cuda.amp.autocast(enabled=False):
|
| 78 |
+
return F.layer_norm(x, normalized_shape, weight, bias, eps)
|
src/custom_timm/models/layers/filter_response_norm.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Filter Response Norm in PyTorch
|
| 2 |
+
|
| 3 |
+
Based on `Filter Response Normalization Layer` - https://arxiv.org/abs/1911.09737
|
| 4 |
+
|
| 5 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 6 |
+
"""
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
|
| 10 |
+
from .create_act import create_act_layer
|
| 11 |
+
from .trace_utils import _assert
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def inv_instance_rms(x, eps: float = 1e-5):
|
| 15 |
+
rms = x.square().float().mean(dim=(2, 3), keepdim=True).add(eps).rsqrt().to(x.dtype)
|
| 16 |
+
return rms.expand(x.shape)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class FilterResponseNormTlu2d(nn.Module):
|
| 20 |
+
def __init__(self, num_features, apply_act=True, eps=1e-5, rms=True, **_):
|
| 21 |
+
super(FilterResponseNormTlu2d, self).__init__()
|
| 22 |
+
self.apply_act = apply_act # apply activation (non-linearity)
|
| 23 |
+
self.rms = rms
|
| 24 |
+
self.eps = eps
|
| 25 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 26 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 27 |
+
self.tau = nn.Parameter(torch.zeros(num_features)) if apply_act else None
|
| 28 |
+
self.reset_parameters()
|
| 29 |
+
|
| 30 |
+
def reset_parameters(self):
|
| 31 |
+
nn.init.ones_(self.weight)
|
| 32 |
+
nn.init.zeros_(self.bias)
|
| 33 |
+
if self.tau is not None:
|
| 34 |
+
nn.init.zeros_(self.tau)
|
| 35 |
+
|
| 36 |
+
def forward(self, x):
|
| 37 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 38 |
+
x_dtype = x.dtype
|
| 39 |
+
v_shape = (1, -1, 1, 1)
|
| 40 |
+
x = x * inv_instance_rms(x, self.eps)
|
| 41 |
+
x = x * self.weight.view(v_shape).to(dtype=x_dtype) + self.bias.view(v_shape).to(dtype=x_dtype)
|
| 42 |
+
return torch.maximum(x, self.tau.reshape(v_shape).to(dtype=x_dtype)) if self.tau is not None else x
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class FilterResponseNormAct2d(nn.Module):
|
| 46 |
+
def __init__(self, num_features, apply_act=True, act_layer=nn.ReLU, inplace=None, rms=True, eps=1e-5, **_):
|
| 47 |
+
super(FilterResponseNormAct2d, self).__init__()
|
| 48 |
+
if act_layer is not None and apply_act:
|
| 49 |
+
self.act = create_act_layer(act_layer, inplace=inplace)
|
| 50 |
+
else:
|
| 51 |
+
self.act = nn.Identity()
|
| 52 |
+
self.rms = rms
|
| 53 |
+
self.eps = eps
|
| 54 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 55 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 56 |
+
self.reset_parameters()
|
| 57 |
+
|
| 58 |
+
def reset_parameters(self):
|
| 59 |
+
nn.init.ones_(self.weight)
|
| 60 |
+
nn.init.zeros_(self.bias)
|
| 61 |
+
|
| 62 |
+
def forward(self, x):
|
| 63 |
+
_assert(x.dim() == 4, 'expected 4D input')
|
| 64 |
+
x_dtype = x.dtype
|
| 65 |
+
v_shape = (1, -1, 1, 1)
|
| 66 |
+
x = x * inv_instance_rms(x, self.eps)
|
| 67 |
+
x = x * self.weight.view(v_shape).to(dtype=x_dtype) + self.bias.view(v_shape).to(dtype=x_dtype)
|
| 68 |
+
return self.act(x)
|
src/custom_timm/models/layers/gather_excite.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Gather-Excite Attention Block
|
| 2 |
+
|
| 3 |
+
Paper: `Gather-Excite: Exploiting Feature Context in CNNs` - https://arxiv.org/abs/1810.12348
|
| 4 |
+
|
| 5 |
+
Official code here, but it's only partial impl in Caffe: https://github.com/hujie-frank/GENet
|
| 6 |
+
|
| 7 |
+
I've tried to support all of the extent both w/ and w/o params. I don't believe I've seen another
|
| 8 |
+
impl that covers all of the cases.
|
| 9 |
+
|
| 10 |
+
NOTE: extent=0 + extra_params=False is equivalent to Squeeze-and-Excitation
|
| 11 |
+
|
| 12 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 13 |
+
"""
|
| 14 |
+
import math
|
| 15 |
+
|
| 16 |
+
from torch import nn as nn
|
| 17 |
+
import torch.nn.functional as F
|
| 18 |
+
|
| 19 |
+
from .create_act import create_act_layer, get_act_layer
|
| 20 |
+
from .create_conv2d import create_conv2d
|
| 21 |
+
from .helpers import make_divisible
|
| 22 |
+
from .mlp import ConvMlp
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class GatherExcite(nn.Module):
|
| 26 |
+
""" Gather-Excite Attention Module
|
| 27 |
+
"""
|
| 28 |
+
def __init__(
|
| 29 |
+
self, channels, feat_size=None, extra_params=False, extent=0, use_mlp=True,
|
| 30 |
+
rd_ratio=1./16, rd_channels=None, rd_divisor=1, add_maxpool=False,
|
| 31 |
+
act_layer=nn.ReLU, norm_layer=nn.BatchNorm2d, gate_layer='sigmoid'):
|
| 32 |
+
super(GatherExcite, self).__init__()
|
| 33 |
+
self.add_maxpool = add_maxpool
|
| 34 |
+
act_layer = get_act_layer(act_layer)
|
| 35 |
+
self.extent = extent
|
| 36 |
+
if extra_params:
|
| 37 |
+
self.gather = nn.Sequential()
|
| 38 |
+
if extent == 0:
|
| 39 |
+
assert feat_size is not None, 'spatial feature size must be specified for global extent w/ params'
|
| 40 |
+
self.gather.add_module(
|
| 41 |
+
'conv1', create_conv2d(channels, channels, kernel_size=feat_size, stride=1, depthwise=True))
|
| 42 |
+
if norm_layer:
|
| 43 |
+
self.gather.add_module(f'norm1', nn.BatchNorm2d(channels))
|
| 44 |
+
else:
|
| 45 |
+
assert extent % 2 == 0
|
| 46 |
+
num_conv = int(math.log2(extent))
|
| 47 |
+
for i in range(num_conv):
|
| 48 |
+
self.gather.add_module(
|
| 49 |
+
f'conv{i + 1}',
|
| 50 |
+
create_conv2d(channels, channels, kernel_size=3, stride=2, depthwise=True))
|
| 51 |
+
if norm_layer:
|
| 52 |
+
self.gather.add_module(f'norm{i + 1}', nn.BatchNorm2d(channels))
|
| 53 |
+
if i != num_conv - 1:
|
| 54 |
+
self.gather.add_module(f'act{i + 1}', act_layer(inplace=True))
|
| 55 |
+
else:
|
| 56 |
+
self.gather = None
|
| 57 |
+
if self.extent == 0:
|
| 58 |
+
self.gk = 0
|
| 59 |
+
self.gs = 0
|
| 60 |
+
else:
|
| 61 |
+
assert extent % 2 == 0
|
| 62 |
+
self.gk = self.extent * 2 - 1
|
| 63 |
+
self.gs = self.extent
|
| 64 |
+
|
| 65 |
+
if not rd_channels:
|
| 66 |
+
rd_channels = make_divisible(channels * rd_ratio, rd_divisor, round_limit=0.)
|
| 67 |
+
self.mlp = ConvMlp(channels, rd_channels, act_layer=act_layer) if use_mlp else nn.Identity()
|
| 68 |
+
self.gate = create_act_layer(gate_layer)
|
| 69 |
+
|
| 70 |
+
def forward(self, x):
|
| 71 |
+
size = x.shape[-2:]
|
| 72 |
+
if self.gather is not None:
|
| 73 |
+
x_ge = self.gather(x)
|
| 74 |
+
else:
|
| 75 |
+
if self.extent == 0:
|
| 76 |
+
# global extent
|
| 77 |
+
x_ge = x.mean(dim=(2, 3), keepdims=True)
|
| 78 |
+
if self.add_maxpool:
|
| 79 |
+
# experimental codepath, may remove or change
|
| 80 |
+
x_ge = 0.5 * x_ge + 0.5 * x.amax((2, 3), keepdim=True)
|
| 81 |
+
else:
|
| 82 |
+
x_ge = F.avg_pool2d(
|
| 83 |
+
x, kernel_size=self.gk, stride=self.gs, padding=self.gk // 2, count_include_pad=False)
|
| 84 |
+
if self.add_maxpool:
|
| 85 |
+
# experimental codepath, may remove or change
|
| 86 |
+
x_ge = 0.5 * x_ge + 0.5 * F.max_pool2d(x, kernel_size=self.gk, stride=self.gs, padding=self.gk // 2)
|
| 87 |
+
x_ge = self.mlp(x_ge)
|
| 88 |
+
if x_ge.shape[-1] != 1 or x_ge.shape[-2] != 1:
|
| 89 |
+
x_ge = F.interpolate(x_ge, size=size)
|
| 90 |
+
return x * self.gate(x_ge)
|
src/custom_timm/models/layers/global_context.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Global Context Attention Block
|
| 2 |
+
|
| 3 |
+
Paper: `GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond`
|
| 4 |
+
- https://arxiv.org/abs/1904.11492
|
| 5 |
+
|
| 6 |
+
Official code consulted as reference: https://github.com/xvjiarui/GCNet
|
| 7 |
+
|
| 8 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 9 |
+
"""
|
| 10 |
+
from torch import nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
|
| 13 |
+
from .create_act import create_act_layer, get_act_layer
|
| 14 |
+
from .helpers import make_divisible
|
| 15 |
+
from .mlp import ConvMlp
|
| 16 |
+
from .norm import LayerNorm2d
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class GlobalContext(nn.Module):
|
| 20 |
+
|
| 21 |
+
def __init__(self, channels, use_attn=True, fuse_add=False, fuse_scale=True, init_last_zero=False,
|
| 22 |
+
rd_ratio=1./8, rd_channels=None, rd_divisor=1, act_layer=nn.ReLU, gate_layer='sigmoid'):
|
| 23 |
+
super(GlobalContext, self).__init__()
|
| 24 |
+
act_layer = get_act_layer(act_layer)
|
| 25 |
+
|
| 26 |
+
self.conv_attn = nn.Conv2d(channels, 1, kernel_size=1, bias=True) if use_attn else None
|
| 27 |
+
|
| 28 |
+
if rd_channels is None:
|
| 29 |
+
rd_channels = make_divisible(channels * rd_ratio, rd_divisor, round_limit=0.)
|
| 30 |
+
if fuse_add:
|
| 31 |
+
self.mlp_add = ConvMlp(channels, rd_channels, act_layer=act_layer, norm_layer=LayerNorm2d)
|
| 32 |
+
else:
|
| 33 |
+
self.mlp_add = None
|
| 34 |
+
if fuse_scale:
|
| 35 |
+
self.mlp_scale = ConvMlp(channels, rd_channels, act_layer=act_layer, norm_layer=LayerNorm2d)
|
| 36 |
+
else:
|
| 37 |
+
self.mlp_scale = None
|
| 38 |
+
|
| 39 |
+
self.gate = create_act_layer(gate_layer)
|
| 40 |
+
self.init_last_zero = init_last_zero
|
| 41 |
+
self.reset_parameters()
|
| 42 |
+
|
| 43 |
+
def reset_parameters(self):
|
| 44 |
+
if self.conv_attn is not None:
|
| 45 |
+
nn.init.kaiming_normal_(self.conv_attn.weight, mode='fan_in', nonlinearity='relu')
|
| 46 |
+
if self.mlp_add is not None:
|
| 47 |
+
nn.init.zeros_(self.mlp_add.fc2.weight)
|
| 48 |
+
|
| 49 |
+
def forward(self, x):
|
| 50 |
+
B, C, H, W = x.shape
|
| 51 |
+
|
| 52 |
+
if self.conv_attn is not None:
|
| 53 |
+
attn = self.conv_attn(x).reshape(B, 1, H * W) # (B, 1, H * W)
|
| 54 |
+
attn = F.softmax(attn, dim=-1).unsqueeze(3) # (B, 1, H * W, 1)
|
| 55 |
+
context = x.reshape(B, C, H * W).unsqueeze(1) @ attn
|
| 56 |
+
context = context.view(B, C, 1, 1)
|
| 57 |
+
else:
|
| 58 |
+
context = x.mean(dim=(2, 3), keepdim=True)
|
| 59 |
+
|
| 60 |
+
if self.mlp_scale is not None:
|
| 61 |
+
mlp_x = self.mlp_scale(context)
|
| 62 |
+
x = x * self.gate(mlp_x)
|
| 63 |
+
if self.mlp_add is not None:
|
| 64 |
+
mlp_x = self.mlp_add(context)
|
| 65 |
+
x = x + mlp_x
|
| 66 |
+
|
| 67 |
+
return x
|
src/custom_timm/models/layers/halo_attn.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Halo Self Attention
|
| 2 |
+
|
| 3 |
+
Paper: `Scaling Local Self-Attention for Parameter Efficient Visual Backbones`
|
| 4 |
+
- https://arxiv.org/abs/2103.12731
|
| 5 |
+
|
| 6 |
+
@misc{2103.12731,
|
| 7 |
+
Author = {Ashish Vaswani and Prajit Ramachandran and Aravind Srinivas and Niki Parmar and Blake Hechtman and
|
| 8 |
+
Jonathon Shlens},
|
| 9 |
+
Title = {Scaling Local Self-Attention for Parameter Efficient Visual Backbones},
|
| 10 |
+
Year = {2021},
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
Status:
|
| 14 |
+
This impl is a WIP, there is no official ref impl and some details in paper weren't clear to me.
|
| 15 |
+
The attention mechanism works but it's slow as implemented.
|
| 16 |
+
|
| 17 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 18 |
+
"""
|
| 19 |
+
from typing import List
|
| 20 |
+
|
| 21 |
+
import torch
|
| 22 |
+
from torch import nn
|
| 23 |
+
import torch.nn.functional as F
|
| 24 |
+
|
| 25 |
+
from .helpers import make_divisible
|
| 26 |
+
from .weight_init import trunc_normal_
|
| 27 |
+
from .trace_utils import _assert
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def rel_logits_1d(q, rel_k, permute_mask: List[int]):
|
| 31 |
+
""" Compute relative logits along one dimension
|
| 32 |
+
|
| 33 |
+
As per: https://gist.github.com/aravindsrinivas/56359b79f0ce4449bcb04ab4b56a57a2
|
| 34 |
+
Originally from: `Attention Augmented Convolutional Networks` - https://arxiv.org/abs/1904.09925
|
| 35 |
+
|
| 36 |
+
Args:
|
| 37 |
+
q: (batch, height, width, dim)
|
| 38 |
+
rel_k: (2 * window - 1, dim)
|
| 39 |
+
permute_mask: permute output dim according to this
|
| 40 |
+
"""
|
| 41 |
+
B, H, W, dim = q.shape
|
| 42 |
+
rel_size = rel_k.shape[0]
|
| 43 |
+
win_size = (rel_size + 1) // 2
|
| 44 |
+
|
| 45 |
+
x = (q @ rel_k.transpose(-1, -2))
|
| 46 |
+
x = x.reshape(-1, W, rel_size)
|
| 47 |
+
|
| 48 |
+
# pad to shift from relative to absolute indexing
|
| 49 |
+
x_pad = F.pad(x, [0, 1]).flatten(1)
|
| 50 |
+
x_pad = F.pad(x_pad, [0, rel_size - W])
|
| 51 |
+
|
| 52 |
+
# reshape and slice out the padded elements
|
| 53 |
+
x_pad = x_pad.reshape(-1, W + 1, rel_size)
|
| 54 |
+
x = x_pad[:, :W, win_size - 1:]
|
| 55 |
+
|
| 56 |
+
# reshape and tile
|
| 57 |
+
x = x.reshape(B, H, 1, W, win_size).expand(-1, -1, win_size, -1, -1)
|
| 58 |
+
return x.permute(permute_mask)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class PosEmbedRel(nn.Module):
|
| 62 |
+
""" Relative Position Embedding
|
| 63 |
+
As per: https://gist.github.com/aravindsrinivas/56359b79f0ce4449bcb04ab4b56a57a2
|
| 64 |
+
Originally from: `Attention Augmented Convolutional Networks` - https://arxiv.org/abs/1904.09925
|
| 65 |
+
|
| 66 |
+
"""
|
| 67 |
+
def __init__(self, block_size, win_size, dim_head, scale):
|
| 68 |
+
"""
|
| 69 |
+
Args:
|
| 70 |
+
block_size (int): block size
|
| 71 |
+
win_size (int): neighbourhood window size
|
| 72 |
+
dim_head (int): attention head dim
|
| 73 |
+
scale (float): scale factor (for init)
|
| 74 |
+
"""
|
| 75 |
+
super().__init__()
|
| 76 |
+
self.block_size = block_size
|
| 77 |
+
self.dim_head = dim_head
|
| 78 |
+
self.height_rel = nn.Parameter(torch.randn(win_size * 2 - 1, dim_head) * scale)
|
| 79 |
+
self.width_rel = nn.Parameter(torch.randn(win_size * 2 - 1, dim_head) * scale)
|
| 80 |
+
|
| 81 |
+
def forward(self, q):
|
| 82 |
+
B, BB, HW, _ = q.shape
|
| 83 |
+
|
| 84 |
+
# relative logits in width dimension.
|
| 85 |
+
q = q.reshape(-1, self.block_size, self.block_size, self.dim_head)
|
| 86 |
+
rel_logits_w = rel_logits_1d(q, self.width_rel, permute_mask=(0, 1, 3, 2, 4))
|
| 87 |
+
|
| 88 |
+
# relative logits in height dimension.
|
| 89 |
+
q = q.transpose(1, 2)
|
| 90 |
+
rel_logits_h = rel_logits_1d(q, self.height_rel, permute_mask=(0, 3, 1, 4, 2))
|
| 91 |
+
|
| 92 |
+
rel_logits = rel_logits_h + rel_logits_w
|
| 93 |
+
rel_logits = rel_logits.reshape(B, BB, HW, -1)
|
| 94 |
+
return rel_logits
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
class HaloAttn(nn.Module):
|
| 98 |
+
""" Halo Attention
|
| 99 |
+
|
| 100 |
+
Paper: `Scaling Local Self-Attention for Parameter Efficient Visual Backbones`
|
| 101 |
+
- https://arxiv.org/abs/2103.12731
|
| 102 |
+
|
| 103 |
+
The internal dimensions of the attention module are controlled by the interaction of several arguments.
|
| 104 |
+
* the output dimension of the module is specified by dim_out, which falls back to input dim if not set
|
| 105 |
+
* the value (v) dimension is set to dim_out // num_heads, the v projection determines the output dim
|
| 106 |
+
* the query and key (qk) dimensions are determined by
|
| 107 |
+
* num_heads * dim_head if dim_head is not None
|
| 108 |
+
* num_heads * (dim_out * attn_ratio // num_heads) if dim_head is None
|
| 109 |
+
* as seen above, attn_ratio determines the ratio of q and k relative to the output if dim_head not used
|
| 110 |
+
|
| 111 |
+
Args:
|
| 112 |
+
dim (int): input dimension to the module
|
| 113 |
+
dim_out (int): output dimension of the module, same as dim if not set
|
| 114 |
+
feat_size (Tuple[int, int]): size of input feature_map (not used, for arg compat with bottle/lambda)
|
| 115 |
+
stride: output stride of the module, query downscaled if > 1 (default: 1).
|
| 116 |
+
num_heads: parallel attention heads (default: 8).
|
| 117 |
+
dim_head: dimension of query and key heads, calculated from dim_out * attn_ratio // num_heads if not set
|
| 118 |
+
block_size (int): size of blocks. (default: 8)
|
| 119 |
+
halo_size (int): size of halo overlap. (default: 3)
|
| 120 |
+
qk_ratio (float): ratio of q and k dimensions to output dimension when dim_head not set. (default: 1.0)
|
| 121 |
+
qkv_bias (bool) : add bias to q, k, and v projections
|
| 122 |
+
avg_down (bool): use average pool downsample instead of strided query blocks
|
| 123 |
+
scale_pos_embed (bool): scale the position embedding as well as Q @ K
|
| 124 |
+
"""
|
| 125 |
+
def __init__(
|
| 126 |
+
self, dim, dim_out=None, feat_size=None, stride=1, num_heads=8, dim_head=None, block_size=8, halo_size=3,
|
| 127 |
+
qk_ratio=1.0, qkv_bias=False, avg_down=False, scale_pos_embed=False):
|
| 128 |
+
super().__init__()
|
| 129 |
+
dim_out = dim_out or dim
|
| 130 |
+
assert dim_out % num_heads == 0
|
| 131 |
+
assert stride in (1, 2)
|
| 132 |
+
self.num_heads = num_heads
|
| 133 |
+
self.dim_head_qk = dim_head or make_divisible(dim_out * qk_ratio, divisor=8) // num_heads
|
| 134 |
+
self.dim_head_v = dim_out // self.num_heads
|
| 135 |
+
self.dim_out_qk = num_heads * self.dim_head_qk
|
| 136 |
+
self.dim_out_v = num_heads * self.dim_head_v
|
| 137 |
+
self.scale = self.dim_head_qk ** -0.5
|
| 138 |
+
self.scale_pos_embed = scale_pos_embed
|
| 139 |
+
self.block_size = self.block_size_ds = block_size
|
| 140 |
+
self.halo_size = halo_size
|
| 141 |
+
self.win_size = block_size + halo_size * 2 # neighbourhood window size
|
| 142 |
+
self.block_stride = 1
|
| 143 |
+
use_avg_pool = False
|
| 144 |
+
if stride > 1:
|
| 145 |
+
use_avg_pool = avg_down or block_size % stride != 0
|
| 146 |
+
self.block_stride = 1 if use_avg_pool else stride
|
| 147 |
+
self.block_size_ds = self.block_size // self.block_stride
|
| 148 |
+
|
| 149 |
+
# FIXME not clear if this stride behaviour is what the paper intended
|
| 150 |
+
# Also, the paper mentions using a 3D conv for dealing with the blocking/gather, and leaving
|
| 151 |
+
# data in unfolded block form. I haven't wrapped my head around how that'd look.
|
| 152 |
+
self.q = nn.Conv2d(dim, self.dim_out_qk, 1, stride=self.block_stride, bias=qkv_bias)
|
| 153 |
+
self.kv = nn.Conv2d(dim, self.dim_out_qk + self.dim_out_v, 1, bias=qkv_bias)
|
| 154 |
+
|
| 155 |
+
self.pos_embed = PosEmbedRel(
|
| 156 |
+
block_size=self.block_size_ds, win_size=self.win_size, dim_head=self.dim_head_qk, scale=self.scale)
|
| 157 |
+
|
| 158 |
+
self.pool = nn.AvgPool2d(2, 2) if use_avg_pool else nn.Identity()
|
| 159 |
+
|
| 160 |
+
self.reset_parameters()
|
| 161 |
+
|
| 162 |
+
def reset_parameters(self):
|
| 163 |
+
std = self.q.weight.shape[1] ** -0.5 # fan-in
|
| 164 |
+
trunc_normal_(self.q.weight, std=std)
|
| 165 |
+
trunc_normal_(self.kv.weight, std=std)
|
| 166 |
+
trunc_normal_(self.pos_embed.height_rel, std=self.scale)
|
| 167 |
+
trunc_normal_(self.pos_embed.width_rel, std=self.scale)
|
| 168 |
+
|
| 169 |
+
def forward(self, x):
|
| 170 |
+
B, C, H, W = x.shape
|
| 171 |
+
_assert(H % self.block_size == 0, '')
|
| 172 |
+
_assert(W % self.block_size == 0, '')
|
| 173 |
+
num_h_blocks = H // self.block_size
|
| 174 |
+
num_w_blocks = W // self.block_size
|
| 175 |
+
num_blocks = num_h_blocks * num_w_blocks
|
| 176 |
+
|
| 177 |
+
q = self.q(x)
|
| 178 |
+
# unfold
|
| 179 |
+
q = q.reshape(
|
| 180 |
+
-1, self.dim_head_qk,
|
| 181 |
+
num_h_blocks, self.block_size_ds, num_w_blocks, self.block_size_ds).permute(0, 1, 3, 5, 2, 4)
|
| 182 |
+
# B, num_heads * dim_head * block_size ** 2, num_blocks
|
| 183 |
+
q = q.reshape(B * self.num_heads, self.dim_head_qk, -1, num_blocks).transpose(1, 3)
|
| 184 |
+
# B * num_heads, num_blocks, block_size ** 2, dim_head
|
| 185 |
+
|
| 186 |
+
kv = self.kv(x)
|
| 187 |
+
# Generate overlapping windows for kv. This approach is good for GPU and CPU. However, unfold() is not
|
| 188 |
+
# lowered for PyTorch XLA so it will be very slow. See code at bottom of file for XLA friendly approach.
|
| 189 |
+
# FIXME figure out how to switch impl between this and conv2d if XLA being used.
|
| 190 |
+
kv = F.pad(kv, [self.halo_size, self.halo_size, self.halo_size, self.halo_size])
|
| 191 |
+
kv = kv.unfold(2, self.win_size, self.block_size).unfold(3, self.win_size, self.block_size).reshape(
|
| 192 |
+
B * self.num_heads, self.dim_head_qk + self.dim_head_v, num_blocks, -1).permute(0, 2, 3, 1)
|
| 193 |
+
k, v = torch.split(kv, [self.dim_head_qk, self.dim_head_v], dim=-1)
|
| 194 |
+
# B * num_heads, num_blocks, win_size ** 2, dim_head_qk or dim_head_v
|
| 195 |
+
|
| 196 |
+
if self.scale_pos_embed:
|
| 197 |
+
attn = (q @ k.transpose(-1, -2) + self.pos_embed(q)) * self.scale
|
| 198 |
+
else:
|
| 199 |
+
attn = (q @ k.transpose(-1, -2)) * self.scale + self.pos_embed(q)
|
| 200 |
+
# B * num_heads, num_blocks, block_size ** 2, win_size ** 2
|
| 201 |
+
attn = attn.softmax(dim=-1)
|
| 202 |
+
|
| 203 |
+
out = (attn @ v).transpose(1, 3) # B * num_heads, dim_head_v, block_size ** 2, num_blocks
|
| 204 |
+
# fold
|
| 205 |
+
out = out.reshape(-1, self.block_size_ds, self.block_size_ds, num_h_blocks, num_w_blocks)
|
| 206 |
+
out = out.permute(0, 3, 1, 4, 2).contiguous().view(
|
| 207 |
+
B, self.dim_out_v, H // self.block_stride, W // self.block_stride)
|
| 208 |
+
# B, dim_out, H // block_stride, W // block_stride
|
| 209 |
+
out = self.pool(out)
|
| 210 |
+
return out
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
""" Three alternatives for overlapping windows.
|
| 214 |
+
|
| 215 |
+
`.unfold().unfold()` is same speed as stride tricks with similar clarity as F.unfold()
|
| 216 |
+
|
| 217 |
+
if is_xla:
|
| 218 |
+
# This code achieves haloing on PyTorch XLA with reasonable runtime trade-off, it is
|
| 219 |
+
# EXTREMELY slow for backward on a GPU though so I need a way of selecting based on environment.
|
| 220 |
+
WW = self.win_size ** 2
|
| 221 |
+
pw = torch.eye(WW, dtype=x.dtype, device=x.device).reshape(WW, 1, self.win_size, self.win_size)
|
| 222 |
+
kv = F.conv2d(kv.reshape(-1, 1, H, W), pw, stride=self.block_size, padding=self.halo_size)
|
| 223 |
+
elif self.stride_tricks:
|
| 224 |
+
kv = F.pad(kv, [self.halo_size, self.halo_size, self.halo_size, self.halo_size]).contiguous()
|
| 225 |
+
kv = kv.as_strided((
|
| 226 |
+
B, self.dim_out_qk + self.dim_out_v, self.win_size, self.win_size, num_h_blocks, num_w_blocks),
|
| 227 |
+
stride=(kv.stride(0), kv.stride(1), kv.shape[-1], 1, self.block_size * kv.shape[-1], self.block_size))
|
| 228 |
+
else:
|
| 229 |
+
kv = F.unfold(kv, kernel_size=self.win_size, stride=self.block_size, padding=self.halo_size)
|
| 230 |
+
|
| 231 |
+
kv = kv.reshape(
|
| 232 |
+
B * self.num_heads, self.dim_head_qk + self.dim_head_v, -1, num_blocks).transpose(1, 3)
|
| 233 |
+
"""
|
src/custom_timm/models/layers/helpers.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Layer/Module Helpers
|
| 2 |
+
|
| 3 |
+
Hacked together by / Copyright 2020 Ross Wightman
|
| 4 |
+
"""
|
| 5 |
+
from itertools import repeat
|
| 6 |
+
import collections.abc
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# From PyTorch internals
|
| 10 |
+
def _ntuple(n):
|
| 11 |
+
def parse(x):
|
| 12 |
+
if isinstance(x, collections.abc.Iterable) and not isinstance(x, str):
|
| 13 |
+
return x
|
| 14 |
+
return tuple(repeat(x, n))
|
| 15 |
+
return parse
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
to_1tuple = _ntuple(1)
|
| 19 |
+
to_2tuple = _ntuple(2)
|
| 20 |
+
to_3tuple = _ntuple(3)
|
| 21 |
+
to_4tuple = _ntuple(4)
|
| 22 |
+
to_ntuple = _ntuple
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def make_divisible(v, divisor=8, min_value=None, round_limit=.9):
|
| 26 |
+
min_value = min_value or divisor
|
| 27 |
+
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
|
| 28 |
+
# Make sure that round down does not go down by more than 10%.
|
| 29 |
+
if new_v < round_limit * v:
|
| 30 |
+
new_v += divisor
|
| 31 |
+
return new_v
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def extend_tuple(x, n):
|
| 35 |
+
# pdas a tuple to specified n by padding with last value
|
| 36 |
+
if not isinstance(x, (tuple, list)):
|
| 37 |
+
x = (x,)
|
| 38 |
+
else:
|
| 39 |
+
x = tuple(x)
|
| 40 |
+
pad_n = n - len(x)
|
| 41 |
+
if pad_n <= 0:
|
| 42 |
+
return x[:n]
|
| 43 |
+
return x + (x[-1],) * pad_n
|
src/custom_timm/models/layers/inplace_abn.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn as nn
|
| 3 |
+
|
| 4 |
+
try:
|
| 5 |
+
from inplace_abn.functions import inplace_abn, inplace_abn_sync
|
| 6 |
+
has_iabn = True
|
| 7 |
+
except ImportError:
|
| 8 |
+
has_iabn = False
|
| 9 |
+
|
| 10 |
+
def inplace_abn(x, weight, bias, running_mean, running_var,
|
| 11 |
+
training=True, momentum=0.1, eps=1e-05, activation="leaky_relu", activation_param=0.01):
|
| 12 |
+
raise ImportError(
|
| 13 |
+
"Please install InplaceABN:'pip install git+https://github.com/mapillary/inplace_abn.git@v1.0.12'")
|
| 14 |
+
|
| 15 |
+
def inplace_abn_sync(**kwargs):
|
| 16 |
+
inplace_abn(**kwargs)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class InplaceAbn(nn.Module):
|
| 20 |
+
"""Activated Batch Normalization
|
| 21 |
+
|
| 22 |
+
This gathers a BatchNorm and an activation function in a single module
|
| 23 |
+
|
| 24 |
+
Parameters
|
| 25 |
+
----------
|
| 26 |
+
num_features : int
|
| 27 |
+
Number of feature channels in the input and output.
|
| 28 |
+
eps : float
|
| 29 |
+
Small constant to prevent numerical issues.
|
| 30 |
+
momentum : float
|
| 31 |
+
Momentum factor applied to compute running statistics.
|
| 32 |
+
affine : bool
|
| 33 |
+
If `True` apply learned scale and shift transformation after normalization.
|
| 34 |
+
act_layer : str or nn.Module type
|
| 35 |
+
Name or type of the activation functions, one of: `leaky_relu`, `elu`
|
| 36 |
+
act_param : float
|
| 37 |
+
Negative slope for the `leaky_relu` activation.
|
| 38 |
+
"""
|
| 39 |
+
|
| 40 |
+
def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True, apply_act=True,
|
| 41 |
+
act_layer="leaky_relu", act_param=0.01, drop_layer=None):
|
| 42 |
+
super(InplaceAbn, self).__init__()
|
| 43 |
+
self.num_features = num_features
|
| 44 |
+
self.affine = affine
|
| 45 |
+
self.eps = eps
|
| 46 |
+
self.momentum = momentum
|
| 47 |
+
if apply_act:
|
| 48 |
+
if isinstance(act_layer, str):
|
| 49 |
+
assert act_layer in ('leaky_relu', 'elu', 'identity', '')
|
| 50 |
+
self.act_name = act_layer if act_layer else 'identity'
|
| 51 |
+
else:
|
| 52 |
+
# convert act layer passed as type to string
|
| 53 |
+
if act_layer == nn.ELU:
|
| 54 |
+
self.act_name = 'elu'
|
| 55 |
+
elif act_layer == nn.LeakyReLU:
|
| 56 |
+
self.act_name = 'leaky_relu'
|
| 57 |
+
elif act_layer is None or act_layer == nn.Identity:
|
| 58 |
+
self.act_name = 'identity'
|
| 59 |
+
else:
|
| 60 |
+
assert False, f'Invalid act layer {act_layer.__name__} for IABN'
|
| 61 |
+
else:
|
| 62 |
+
self.act_name = 'identity'
|
| 63 |
+
self.act_param = act_param
|
| 64 |
+
if self.affine:
|
| 65 |
+
self.weight = nn.Parameter(torch.ones(num_features))
|
| 66 |
+
self.bias = nn.Parameter(torch.zeros(num_features))
|
| 67 |
+
else:
|
| 68 |
+
self.register_parameter('weight', None)
|
| 69 |
+
self.register_parameter('bias', None)
|
| 70 |
+
self.register_buffer('running_mean', torch.zeros(num_features))
|
| 71 |
+
self.register_buffer('running_var', torch.ones(num_features))
|
| 72 |
+
self.reset_parameters()
|
| 73 |
+
|
| 74 |
+
def reset_parameters(self):
|
| 75 |
+
nn.init.constant_(self.running_mean, 0)
|
| 76 |
+
nn.init.constant_(self.running_var, 1)
|
| 77 |
+
if self.affine:
|
| 78 |
+
nn.init.constant_(self.weight, 1)
|
| 79 |
+
nn.init.constant_(self.bias, 0)
|
| 80 |
+
|
| 81 |
+
def forward(self, x):
|
| 82 |
+
output = inplace_abn(
|
| 83 |
+
x, self.weight, self.bias, self.running_mean, self.running_var,
|
| 84 |
+
self.training, self.momentum, self.eps, self.act_name, self.act_param)
|
| 85 |
+
if isinstance(output, tuple):
|
| 86 |
+
output = output[0]
|
| 87 |
+
return output
|
src/custom_timm/models/layers/lambda_layer.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" Lambda Layer
|
| 2 |
+
|
| 3 |
+
Paper: `LambdaNetworks: Modeling Long-Range Interactions Without Attention`
|
| 4 |
+
- https://arxiv.org/abs/2102.08602
|
| 5 |
+
|
| 6 |
+
@misc{2102.08602,
|
| 7 |
+
Author = {Irwan Bello},
|
| 8 |
+
Title = {LambdaNetworks: Modeling Long-Range Interactions Without Attention},
|
| 9 |
+
Year = {2021},
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
Status:
|
| 13 |
+
This impl is a WIP. Code snippets in the paper were used as reference but
|
| 14 |
+
good chance some details are missing/wrong.
|
| 15 |
+
|
| 16 |
+
I've only implemented local lambda conv based pos embeddings.
|
| 17 |
+
|
| 18 |
+
For a PyTorch impl that includes other embedding options checkout
|
| 19 |
+
https://github.com/lucidrains/lambda-networks
|
| 20 |
+
|
| 21 |
+
Hacked together by / Copyright 2021 Ross Wightman
|
| 22 |
+
"""
|
| 23 |
+
import torch
|
| 24 |
+
from torch import nn
|
| 25 |
+
import torch.nn.functional as F
|
| 26 |
+
|
| 27 |
+
from .helpers import to_2tuple, make_divisible
|
| 28 |
+
from .weight_init import trunc_normal_
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def rel_pos_indices(size):
|
| 32 |
+
size = to_2tuple(size)
|
| 33 |
+
pos = torch.stack(torch.meshgrid(torch.arange(size[0]), torch.arange(size[1]))).flatten(1)
|
| 34 |
+
rel_pos = pos[:, None, :] - pos[:, :, None]
|
| 35 |
+
rel_pos[0] += size[0] - 1
|
| 36 |
+
rel_pos[1] += size[1] - 1
|
| 37 |
+
return rel_pos # 2, H * W, H * W
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class LambdaLayer(nn.Module):
|
| 41 |
+
"""Lambda Layer
|
| 42 |
+
|
| 43 |
+
Paper: `LambdaNetworks: Modeling Long-Range Interactions Without Attention`
|
| 44 |
+
- https://arxiv.org/abs/2102.08602
|
| 45 |
+
|
| 46 |
+
NOTE: intra-depth parameter 'u' is fixed at 1. It did not appear worth the complexity to add.
|
| 47 |
+
|
| 48 |
+
The internal dimensions of the lambda module are controlled via the interaction of several arguments.
|
| 49 |
+
* the output dimension of the module is specified by dim_out, which falls back to input dim if not set
|
| 50 |
+
* the value (v) dimension is set to dim_out // num_heads, the v projection determines the output dim
|
| 51 |
+
* the query (q) and key (k) dimension are determined by
|
| 52 |
+
* dim_head = (dim_out * attn_ratio // num_heads) if dim_head is None
|
| 53 |
+
* q = num_heads * dim_head, k = dim_head
|
| 54 |
+
* as seen above, attn_ratio determines the ratio of q and k relative to the output if dim_head not set
|
| 55 |
+
|
| 56 |
+
Args:
|
| 57 |
+
dim (int): input dimension to the module
|
| 58 |
+
dim_out (int): output dimension of the module, same as dim if not set
|
| 59 |
+
feat_size (Tuple[int, int]): size of input feature_map for relative pos variant H, W
|
| 60 |
+
stride (int): output stride of the module, avg pool used if stride == 2
|
| 61 |
+
num_heads (int): parallel attention heads.
|
| 62 |
+
dim_head (int): dimension of query and key heads, calculated from dim_out * attn_ratio // num_heads if not set
|
| 63 |
+
r (int): local lambda convolution radius. Use lambda conv if set, else relative pos if not. (default: 9)
|
| 64 |
+
qk_ratio (float): ratio of q and k dimensions to output dimension when dim_head not set. (default: 1.0)
|
| 65 |
+
qkv_bias (bool): add bias to q, k, and v projections
|
| 66 |
+
"""
|
| 67 |
+
def __init__(
|
| 68 |
+
self, dim, dim_out=None, feat_size=None, stride=1, num_heads=4, dim_head=16, r=9,
|
| 69 |
+
qk_ratio=1.0, qkv_bias=False):
|
| 70 |
+
super().__init__()
|
| 71 |
+
dim_out = dim_out or dim
|
| 72 |
+
assert dim_out % num_heads == 0, ' should be divided by num_heads'
|
| 73 |
+
self.dim_qk = dim_head or make_divisible(dim_out * qk_ratio, divisor=8) // num_heads
|
| 74 |
+
self.num_heads = num_heads
|
| 75 |
+
self.dim_v = dim_out // num_heads
|
| 76 |
+
|
| 77 |
+
self.qkv = nn.Conv2d(
|
| 78 |
+
dim,
|
| 79 |
+
num_heads * self.dim_qk + self.dim_qk + self.dim_v,
|
| 80 |
+
kernel_size=1, bias=qkv_bias)
|
| 81 |
+
self.norm_q = nn.BatchNorm2d(num_heads * self.dim_qk)
|
| 82 |
+
self.norm_v = nn.BatchNorm2d(self.dim_v)
|
| 83 |
+
|
| 84 |
+
if r is not None:
|
| 85 |
+
# local lambda convolution for pos
|
| 86 |
+
self.conv_lambda = nn.Conv3d(1, self.dim_qk, (r, r, 1), padding=(r // 2, r // 2, 0))
|
| 87 |
+
self.pos_emb = None
|
| 88 |
+
self.rel_pos_indices = None
|
| 89 |
+
else:
|
| 90 |
+
# relative pos embedding
|
| 91 |
+
assert feat_size is not None
|
| 92 |
+
feat_size = to_2tuple(feat_size)
|
| 93 |
+
rel_size = [2 * s - 1 for s in feat_size]
|
| 94 |
+
self.conv_lambda = None
|
| 95 |
+
self.pos_emb = nn.Parameter(torch.zeros(rel_size[0], rel_size[1], self.dim_qk))
|
| 96 |
+
self.register_buffer('rel_pos_indices', rel_pos_indices(feat_size), persistent=False)
|
| 97 |
+
|
| 98 |
+
self.pool = nn.AvgPool2d(2, 2) if stride == 2 else nn.Identity()
|
| 99 |
+
|
| 100 |
+
self.reset_parameters()
|
| 101 |
+
|
| 102 |
+
def reset_parameters(self):
|
| 103 |
+
trunc_normal_(self.qkv.weight, std=self.qkv.weight.shape[1] ** -0.5) # fan-in
|
| 104 |
+
if self.conv_lambda is not None:
|
| 105 |
+
trunc_normal_(self.conv_lambda.weight, std=self.dim_qk ** -0.5)
|
| 106 |
+
if self.pos_emb is not None:
|
| 107 |
+
trunc_normal_(self.pos_emb, std=.02)
|
| 108 |
+
|
| 109 |
+
def forward(self, x):
|
| 110 |
+
B, C, H, W = x.shape
|
| 111 |
+
M = H * W
|
| 112 |
+
qkv = self.qkv(x)
|
| 113 |
+
q, k, v = torch.split(qkv, [
|
| 114 |
+
self.num_heads * self.dim_qk, self.dim_qk, self.dim_v], dim=1)
|
| 115 |
+
q = self.norm_q(q).reshape(B, self.num_heads, self.dim_qk, M).transpose(-1, -2) # B, num_heads, M, K
|
| 116 |
+
v = self.norm_v(v).reshape(B, self.dim_v, M).transpose(-1, -2) # B, M, V
|
| 117 |
+
k = F.softmax(k.reshape(B, self.dim_qk, M), dim=-1) # B, K, M
|
| 118 |
+
|
| 119 |
+
content_lam = k @ v # B, K, V
|
| 120 |
+
content_out = q @ content_lam.unsqueeze(1) # B, num_heads, M, V
|
| 121 |
+
|
| 122 |
+
if self.pos_emb is None:
|
| 123 |
+
position_lam = self.conv_lambda(v.reshape(B, 1, H, W, self.dim_v)) # B, H, W, V, K
|
| 124 |
+
position_lam = position_lam.reshape(B, 1, self.dim_qk, H * W, self.dim_v).transpose(2, 3) # B, 1, M, K, V
|
| 125 |
+
else:
|
| 126 |
+
# FIXME relative pos embedding path not fully verified
|
| 127 |
+
pos_emb = self.pos_emb[self.rel_pos_indices[0], self.rel_pos_indices[1]].expand(B, -1, -1, -1)
|
| 128 |
+
position_lam = (pos_emb.transpose(-1, -2) @ v.unsqueeze(1)).unsqueeze(1) # B, 1, M, K, V
|
| 129 |
+
position_out = (q.unsqueeze(-2) @ position_lam).squeeze(-2) # B, num_heads, M, V
|
| 130 |
+
|
| 131 |
+
out = (content_out + position_out).transpose(-1, -2).reshape(B, C, H, W) # B, C (num_heads * V), H, W
|
| 132 |
+
out = self.pool(out)
|
| 133 |
+
return out
|