├── Asymmetric Convolution.py ├── Conditionally Parameterized Convolutions.py ├── Deformable Convolution.py ├── Depthwise Separable Convolution.py ├── Dynamic Convolution.py ├── Ghost Convolution.py ├── Graph Convolution.py ├── Heterogeneous Convolution.py ├── Hierarchical-Split Block.py ├── Involution.py ├── Octave Convolution.py ├── Res2Net Convotion.py ├── ResNeSt Block.py └── Self-Calibrated Convolution.py /Asymmetric Convolution.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch.nn.init as init 3 | from custom_layers.crop_layer import CropLayer 4 | # source: https://github.com/DingXiaoH/ACNet/blob/master/acnet/acb.py 5 | 6 | class ACBlock(nn.Module): 7 | 8 | def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, padding_mode='zeros', deploy=False, 9 | use_affine=True, reduce_gamma=False, use_last_bn=False, gamma_init=None ): 10 | super(ACBlock, self).__init__() 11 | self.deploy = deploy 12 | if deploy: 13 | self.fused_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(kernel_size,kernel_size), stride=stride, 14 | padding=padding, dilation=dilation, groups=groups, bias=True, padding_mode=padding_mode) 15 | else: 16 | self.square_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, 17 | kernel_size=(kernel_size, kernel_size), stride=stride, 18 | padding=padding, dilation=dilation, groups=groups, bias=False, 19 | padding_mode=padding_mode) 20 | self.square_bn = nn.BatchNorm2d(num_features=out_channels, affine=use_affine) 21 | 22 | center_offset_from_origin_border = padding - kernel_size // 2 23 | ver_pad_or_crop = (padding, center_offset_from_origin_border) 24 | hor_pad_or_crop = (center_offset_from_origin_border, padding) 25 | if center_offset_from_origin_border >= 0: 26 | self.ver_conv_crop_layer = nn.Identity() 27 | ver_conv_padding = ver_pad_or_crop 28 | self.hor_conv_crop_layer = nn.Identity() 29 | hor_conv_padding = hor_pad_or_crop 30 | else: 31 | self.ver_conv_crop_layer = CropLayer(crop_set=ver_pad_or_crop) 32 | ver_conv_padding = (0, 0) 33 | self.hor_conv_crop_layer = CropLayer(crop_set=hor_pad_or_crop) 34 | hor_conv_padding = (0, 0) 35 | self.ver_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(kernel_size, 1), 36 | stride=stride, 37 | padding=ver_conv_padding, dilation=dilation, groups=groups, bias=False, 38 | padding_mode=padding_mode) 39 | 40 | self.hor_conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=(1, kernel_size), 41 | stride=stride, 42 | padding=hor_conv_padding, dilation=dilation, groups=groups, bias=False, 43 | padding_mode=padding_mode) 44 | self.ver_bn = nn.BatchNorm2d(num_features=out_channels, affine=use_affine) 45 | self.hor_bn = nn.BatchNorm2d(num_features=out_channels, affine=use_affine) 46 | 47 | if reduce_gamma: 48 | assert not use_last_bn 49 | self.init_gamma(1.0 / 3) 50 | 51 | if use_last_bn: 52 | assert not reduce_gamma 53 | self.last_bn = nn.BatchNorm2d(num_features=out_channels, affine=True) 54 | 55 | if gamma_init is not None: 56 | assert not reduce_gamma 57 | self.init_gamma(gamma_init) 58 | 59 | 60 | def init_gamma(self, gamma_value): 61 | init.constant_(self.square_bn.weight, gamma_value) 62 | init.constant_(self.ver_bn.weight, gamma_value) 63 | init.constant_(self.hor_bn.weight, gamma_value) 64 | print('init gamma of square, ver and hor as ', gamma_value) 65 | 66 | def single_init(self): 67 | init.constant_(self.square_bn.weight, 1.0) 68 | init.constant_(self.ver_bn.weight, 0.0) 69 | init.constant_(self.hor_bn.weight, 0.0) 70 | print('init gamma of square as 1, ver and hor as 0') 71 | 72 | def forward(self, input): 73 | if self.deploy: 74 | return self.fused_conv(input) 75 | else: 76 | square_outputs = self.square_conv(input) 77 | square_outputs = self.square_bn(square_outputs) 78 | vertical_outputs = self.ver_conv_crop_layer(input) 79 | vertical_outputs = self.ver_conv(vertical_outputs) 80 | vertical_outputs = self.ver_bn(vertical_outputs) 81 | horizontal_outputs = self.hor_conv_crop_layer(input) 82 | horizontal_outputs = self.hor_conv(horizontal_outputs) 83 | horizontal_outputs = self.hor_bn(horizontal_outputs) 84 | result = square_outputs + vertical_outputs + horizontal_outputs 85 | if hasattr(self, 'last_bn'): 86 | return self.last_bn(result) 87 | return result 88 | -------------------------------------------------------------------------------- /Conditionally Parameterized Convolutions.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | import torch 4 | from torch import nn 5 | import torch.nn.functional as F 6 | from torch.nn.modules.conv import _ConvNd 7 | from torch.nn.modules.utils import _pair 8 | from torch.nn.parameter import Parameter 9 | # source: https://github.com/nibuiro/CondConv-pytorch/blob/master/condconv/condconv.py 10 | 11 | class _routing(nn.Module): 12 | 13 | def __init__(self, in_channels, num_experts, dropout_rate): 14 | super(_routing, self).__init__() 15 | 16 | self.dropout = nn.Dropout(dropout_rate) 17 | self.fc = nn.Linear(in_channels, num_experts) 18 | 19 | def forward(self, x): 20 | x = torch.flatten(x) 21 | x = self.dropout(x) 22 | x = self.fc(x) 23 | return F.sigmoid(x) 24 | 25 | 26 | class CondConv2D(_ConvNd): 27 | r"""Learn specialized convolutional kernels for each example. 28 | As described in the paper 29 | `CondConv: Conditionally Parameterized Convolutions for Efficient Inference`_ , 30 | conditionally parameterized convolutions (CondConv), 31 | which challenge the paradigm of static convolutional kernels 32 | by computing convolutional kernels as a function of the input. 33 | Args: 34 | in_channels (int): Number of channels in the input image 35 | out_channels (int): Number of channels produced by the convolution 36 | kernel_size (int or tuple): Size of the convolving kernel 37 | stride (int or tuple, optional): Stride of the convolution. Default: 1 38 | padding (int or tuple, optional): Zero-padding added to both sides of the input. Default: 0 39 | padding_mode (string, optional): ``'zeros'``, ``'reflect'``, ``'replicate'`` or ``'circular'``. Default: ``'zeros'`` 40 | dilation (int or tuple, optional): Spacing between kernel elements. Default: 1 41 | groups (int, optional): Number of blocked connections from input channels to output channels. Default: 1 42 | bias (bool, optional): If ``True``, adds a learnable bias to the output. Default: ``True`` 43 | num_experts (int): Number of experts per layer 44 | Shape: 45 | - Input: :math:`(N, C_{in}, H_{in}, W_{in})` 46 | - Output: :math:`(N, C_{out}, H_{out}, W_{out})` where 47 | .. math:: 48 | H_{out} = \left\lfloor\frac{H_{in} + 2 \times \text{padding}[0] - \text{dilation}[0] 49 | \times (\text{kernel\_size}[0] - 1) - 1}{\text{stride}[0]} + 1\right\rfloor 50 | .. math:: 51 | W_{out} = \left\lfloor\frac{W_{in} + 2 \times \text{padding}[1] - \text{dilation}[1] 52 | \times (\text{kernel\_size}[1] - 1) - 1}{\text{stride}[1]} + 1\right\rfloor 53 | Attributes: 54 | weight (Tensor): the learnable weights of the module of shape 55 | :math:`(\text{out\_channels}, \frac{\text{in\_channels}}{\text{groups}},` 56 | :math:`\text{kernel\_size[0]}, \text{kernel\_size[1]})`. 57 | The values of these weights are sampled from 58 | :math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})` where 59 | :math:`k = \frac{groups}{C_\text{in} * \prod_{i=0}^{1}\text{kernel\_size}[i]}` 60 | bias (Tensor): the learnable bias of the module of shape (out_channels). If :attr:`bias` is ``True``, 61 | then the values of these weights are 62 | sampled from :math:`\mathcal{U}(-\sqrt{k}, \sqrt{k})` where 63 | :math:`k = \frac{groups}{C_\text{in} * \prod_{i=0}^{1}\text{kernel\_size}[i]}` 64 | .. _CondConv: Conditionally Parameterized Convolutions for Efficient Inference: 65 | https://arxiv.org/abs/1904.04971 66 | """ 67 | 68 | def __init__(self, in_channels, out_channels, kernel_size, stride=1, 69 | padding=0, dilation=1, groups=1, 70 | bias=True, padding_mode='zeros', num_experts=3, dropout_rate=0.2): 71 | kernel_size = _pair(kernel_size) 72 | stride = _pair(stride) 73 | padding = _pair(padding) 74 | dilation = _pair(dilation) 75 | super(CondConv2D, self).__init__( 76 | in_channels, out_channels, kernel_size, stride, padding, dilation, 77 | False, _pair(0), groups, bias, padding_mode) 78 | 79 | self._avg_pooling = functools.partial(F.adaptive_avg_pool2d, output_size=(1, 1)) 80 | self._routing_fn = _routing(in_channels, num_experts, dropout_rate) 81 | 82 | self.weight = Parameter(torch.Tensor( 83 | num_experts, out_channels, in_channels // groups, *kernel_size)) 84 | 85 | self.reset_parameters() 86 | 87 | def _conv_forward(self, input, weight): 88 | if self.padding_mode != 'zeros': 89 | return F.conv2d(F.pad(input, self._padding_repeated_twice, mode=self.padding_mode), 90 | weight, self.bias, self.stride, 91 | _pair(0), self.dilation, self.groups) 92 | return F.conv2d(input, weight, self.bias, self.stride, 93 | self.padding, self.dilation, self.groups) 94 | 95 | def forward(self, inputs): 96 | b, _, _, _ = inputs.size() 97 | res = [] 98 | for input in inputs: 99 | input = input.unsqueeze(0) 100 | pooled_inputs = self._avg_pooling(input) 101 | routing_weights = self._routing_fn(pooled_inputs) 102 | kernels = torch.sum(routing_weights[: ,None, None, None, None] * self.weight, 0) 103 | out = self._conv_forward(input, kernels) 104 | res.append(out) 105 | return torch.cat(res, dim=0) 106 | -------------------------------------------------------------------------------- /Deformable Convolution.py: -------------------------------------------------------------------------------- 1 | from torch.autograd import Variable, Function 2 | import torch 3 | from torch import nn 4 | import numpy as np 5 | 6 | # source: https://github.com/ChunhuanLin/deform_conv_pytorch 7 | 8 | class DeformConv2D(nn.Module): 9 | def __init__(self, inc, outc, kernel_size=3, padding=1, bias=None): 10 | super(DeformConv2D, self).__init__() 11 | self.kernel_size = kernel_size 12 | self.padding = padding 13 | self.zero_padding = nn.ZeroPad2d(padding) 14 | self.conv_kernel = nn.Conv2d(inc, outc, kernel_size=kernel_size, stride=kernel_size, bias=bias) 15 | 16 | def forward(self, x, offset): 17 | dtype = offset.data.type() 18 | ks = self.kernel_size 19 | N = offset.size(1) // 2 20 | 21 | # Change offset's order from [x1, x2, ..., y1, y2, ...] to [x1, y1, x2, y2, ...] 22 | # Codes below are written to make sure same results of MXNet implementation. 23 | # You can remove them, and it won't influence the module's performance. 24 | offsets_index = Variable(torch.cat([torch.arange(0, 2*N, 2), torch.arange(1, 2*N+1, 2)]), requires_grad=False).type_as(x).long() 25 | offsets_index = offsets_index.unsqueeze(dim=0).unsqueeze(dim=-1).unsqueeze(dim=-1).expand(*offset.size()) 26 | offset = torch.gather(offset, dim=1, index=offsets_index) 27 | # ------------------------------------------------------------------------ 28 | 29 | if self.padding: 30 | x = self.zero_padding(x) 31 | 32 | # (b, 2N, h, w) 33 | p = self._get_p(offset, dtype) 34 | 35 | # (b, h, w, 2N) 36 | p = p.contiguous().permute(0, 2, 3, 1) 37 | q_lt = Variable(p.data, requires_grad=False).floor() 38 | q_rb = q_lt + 1 39 | 40 | q_lt = torch.cat([torch.clamp(q_lt[..., :N], 0, x.size(2)-1), torch.clamp(q_lt[..., N:], 0, x.size(3)-1)], dim=-1).long() 41 | q_rb = torch.cat([torch.clamp(q_rb[..., :N], 0, x.size(2)-1), torch.clamp(q_rb[..., N:], 0, x.size(3)-1)], dim=-1).long() 42 | q_lb = torch.cat([q_lt[..., :N], q_rb[..., N:]], -1) 43 | q_rt = torch.cat([q_rb[..., :N], q_lt[..., N:]], -1) 44 | 45 | # (b, h, w, N) 46 | mask = torch.cat([p[..., :N].lt(self.padding)+p[..., :N].gt(x.size(2)-1-self.padding), 47 | p[..., N:].lt(self.padding)+p[..., N:].gt(x.size(3)-1-self.padding)], dim=-1).type_as(p) 48 | mask = mask.detach() 49 | floor_p = p - (p - torch.floor(p)) 50 | p = p*(1-mask) + floor_p*mask 51 | p = torch.cat([torch.clamp(p[..., :N], 0, x.size(2)-1), torch.clamp(p[..., N:], 0, x.size(3)-1)], dim=-1) 52 | 53 | # bilinear kernel (b, h, w, N) 54 | g_lt = (1 + (q_lt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_lt[..., N:].type_as(p) - p[..., N:])) 55 | g_rb = (1 - (q_rb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_rb[..., N:].type_as(p) - p[..., N:])) 56 | g_lb = (1 + (q_lb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_lb[..., N:].type_as(p) - p[..., N:])) 57 | g_rt = (1 - (q_rt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_rt[..., N:].type_as(p) - p[..., N:])) 58 | 59 | # (b, c, h, w, N) 60 | x_q_lt = self._get_x_q(x, q_lt, N) 61 | x_q_rb = self._get_x_q(x, q_rb, N) 62 | x_q_lb = self._get_x_q(x, q_lb, N) 63 | x_q_rt = self._get_x_q(x, q_rt, N) 64 | 65 | # (b, c, h, w, N) 66 | x_offset = g_lt.unsqueeze(dim=1) * x_q_lt + \ 67 | g_rb.unsqueeze(dim=1) * x_q_rb + \ 68 | g_lb.unsqueeze(dim=1) * x_q_lb + \ 69 | g_rt.unsqueeze(dim=1) * x_q_rt 70 | 71 | x_offset = self._reshape_x_offset(x_offset, ks) 72 | out = self.conv_kernel(x_offset) 73 | 74 | return out 75 | 76 | def _get_p_n(self, N, dtype): 77 | p_n_x, p_n_y = np.meshgrid(range(-(self.kernel_size-1)//2, (self.kernel_size-1)//2+1), 78 | range(-(self.kernel_size-1)//2, (self.kernel_size-1)//2+1), indexing='ij') 79 | # (2N, 1) 80 | p_n = np.concatenate((p_n_x.flatten(), p_n_y.flatten())) 81 | p_n = np.reshape(p_n, (1, 2*N, 1, 1)) 82 | p_n = Variable(torch.from_numpy(p_n).type(dtype), requires_grad=False) 83 | 84 | return p_n 85 | 86 | @staticmethod 87 | def _get_p_0(h, w, N, dtype): 88 | p_0_x, p_0_y = np.meshgrid(range(1, h+1), range(1, w+1), indexing='ij') 89 | p_0_x = p_0_x.flatten().reshape(1, 1, h, w).repeat(N, axis=1) 90 | p_0_y = p_0_y.flatten().reshape(1, 1, h, w).repeat(N, axis=1) 91 | p_0 = np.concatenate((p_0_x, p_0_y), axis=1) 92 | p_0 = Variable(torch.from_numpy(p_0).type(dtype), requires_grad=False) 93 | 94 | return p_0 95 | 96 | def _get_p(self, offset, dtype): 97 | N, h, w = offset.size(1)//2, offset.size(2), offset.size(3) 98 | 99 | # (1, 2N, 1, 1) 100 | p_n = self._get_p_n(N, dtype) 101 | # (1, 2N, h, w) 102 | p_0 = self._get_p_0(h, w, N, dtype) 103 | p = p_0 + p_n + offset 104 | return p 105 | 106 | def _get_x_q(self, x, q, N): 107 | b, h, w, _ = q.size() 108 | padded_w = x.size(3) 109 | c = x.size(1) 110 | # (b, c, h*w) 111 | x = x.contiguous().view(b, c, -1) 112 | 113 | # (b, h, w, N) 114 | index = q[..., :N]*padded_w + q[..., N:] # offset_x*w + offset_y 115 | # (b, c, h*w*N) 116 | index = index.contiguous().unsqueeze(dim=1).expand(-1, c, -1, -1, -1).contiguous().view(b, c, -1) 117 | 118 | x_offset = x.gather(dim=-1, index=index).contiguous().view(b, c, h, w, N) 119 | 120 | return x_offset 121 | 122 | @staticmethod 123 | def _reshape_x_offset(x_offset, ks): 124 | b, c, h, w, N = x_offset.size() 125 | x_offset = torch.cat([x_offset[..., s:s+ks].contiguous().view(b, c, h, w*ks) for s in range(0, N, ks)], dim=-1) 126 | x_offset = x_offset.contiguous().view(b, c, h*ks, w*ks) 127 | 128 | return x_offset 129 | -------------------------------------------------------------------------------- /Depthwise Separable Convolution.py: -------------------------------------------------------------------------------- 1 | class DeepWise_PointWise_Conv(nn.Module): 2 | def __init__(self, in_ch, out_ch): 3 | super(DeepWise_PointWise_Conv, self).__init__() 4 | self.depth_conv = nn.Conv2d( 5 | in_channels=in_ch, 6 | out_channels=in_ch, 7 | kernel_size=3, 8 | stride=1, 9 | padding=1, 10 | groups=in_ch 11 | ) 12 | self.point_conv = nn.Conv2d( 13 | in_channels=in_ch, 14 | out_channels=out_ch, 15 | kernel_size=1, 16 | stride=1, 17 | padding=0, 18 | groups=1 19 | ) 20 | 21 | def forward(self, input): 22 | out = self.depth_conv(input) 23 | out = self.point_conv(out) 24 | return out 25 | 26 | -------------------------------------------------------------------------------- /Dynamic Convolution.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | # source: https://github.com/kaijieshi7/Dynamic-convolution-Pytorch/blob/master/dynamic_conv.py 6 | 7 | class attention1d(nn.Module): 8 | def __init__(self, in_planes, ratios, K, temperature, init_weight=True): 9 | super(attention1d, self).__init__() 10 | assert temperature%3==1 11 | self.avgpool = nn.AdaptiveAvgPool1d(1) 12 | if in_planes!=3: 13 | hidden_planes = int(in_planes*ratios)+1 14 | else: 15 | hidden_planes = K 16 | self.fc1 = nn.Conv1d(in_planes, hidden_planes, 1, bias=False) 17 | # self.bn = nn.BatchNorm2d(hidden_planes) 18 | self.fc2 = nn.Conv1d(hidden_planes, K, 1, bias=True) 19 | self.temperature = temperature 20 | if init_weight: 21 | self._initialize_weights() 22 | 23 | 24 | def _initialize_weights(self): 25 | for m in self.modules(): 26 | if isinstance(m, nn.Conv1d): 27 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 28 | if m.bias is not None: 29 | nn.init.constant_(m.bias, 0) 30 | if isinstance(m ,nn.BatchNorm2d): 31 | nn.init.constant_(m.weight, 1) 32 | nn.init.constant_(m.bias, 0) 33 | 34 | def updata_temperature(self): 35 | if self.temperature!=1: 36 | self.temperature -=3 37 | print('Change temperature to:', str(self.temperature)) 38 | 39 | 40 | def forward(self, x): 41 | x = self.avgpool(x) 42 | x = self.fc1(x) 43 | x = F.relu(x) 44 | x = self.fc2(x).view(x.size(0), -1) 45 | return F.softmax(x/self.temperature, 1) 46 | 47 | 48 | class Dynamic_conv1d(nn.Module): 49 | def __init__(self, in_planes, out_planes, kernel_size, ratio=0.25, stride=1, padding=0, dilation=1, groups=1, bias=True, K=4,temperature=34, init_weight=True): 50 | super(Dynamic_conv1d, self).__init__() 51 | assert in_planes%groups==0 52 | self.in_planes = in_planes 53 | self.out_planes = out_planes 54 | self.kernel_size = kernel_size 55 | self.stride = stride 56 | self.padding = padding 57 | self.dilation = dilation 58 | self.groups = groups 59 | self.bias = bias 60 | self.K = K 61 | self.attention = attention1d(in_planes, ratio, K, temperature) 62 | 63 | self.weight = nn.Parameter(torch.randn(K, out_planes, in_planes//groups, kernel_size), requires_grad=True) 64 | if bias: 65 | self.bias = nn.Parameter(torch.Tensor(K, out_planes)) 66 | else: 67 | self.bias = None 68 | if init_weight: 69 | self._initialize_weights() 70 | 71 | #TODO 初始化 72 | def _initialize_weights(self): 73 | for i in range(self.K): 74 | nn.init.kaiming_uniform_(self.weight[i]) 75 | 76 | 77 | def update_temperature(self): 78 | self.attention.updata_temperature() 79 | 80 | def forward(self, x):#将batch视作维度变量,进行组卷积,因为组卷积的权重是不同的,动态卷积的权重也是不同的 81 | softmax_attention = self.attention(x) 82 | batch_size, in_planes, height = x.size() 83 | x = x.view(1, -1, height, )# 变化成一个维度进行组卷积 84 | weight = self.weight.view(self.K, -1) 85 | 86 | # 动态卷积的权重的生成, 生成的是batch_size个卷积参数(每个参数不同) 87 | aggregate_weight = torch.mm(softmax_attention, weight).view(-1, self.in_planes, self.kernel_size,) 88 | if self.bias is not None: 89 | aggregate_bias = torch.mm(softmax_attention, self.bias).view(-1) 90 | output = F.conv1d(x, weight=aggregate_weight, bias=aggregate_bias, stride=self.stride, padding=self.padding, 91 | dilation=self.dilation, groups=self.groups*batch_size) 92 | else: 93 | output = F.conv1d(x, weight=aggregate_weight, bias=None, stride=self.stride, padding=self.padding, 94 | dilation=self.dilation, groups=self.groups * batch_size) 95 | 96 | output = output.view(batch_size, self.out_planes, output.size(-1)) 97 | return output 98 | 99 | 100 | 101 | class attention2d(nn.Module): 102 | def __init__(self, in_planes, ratios, K, temperature, init_weight=True): 103 | super(attention2d, self).__init__() 104 | assert temperature%3==1 105 | self.avgpool = nn.AdaptiveAvgPool2d(1) 106 | if in_planes!=3: 107 | hidden_planes = int(in_planes*ratios)+1 108 | else: 109 | hidden_planes = K 110 | self.fc1 = nn.Conv2d(in_planes, hidden_planes, 1, bias=False) 111 | # self.bn = nn.BatchNorm2d(hidden_planes) 112 | self.fc2 = nn.Conv2d(hidden_planes, K, 1, bias=True) 113 | self.temperature = temperature 114 | if init_weight: 115 | self._initialize_weights() 116 | 117 | 118 | def _initialize_weights(self): 119 | for m in self.modules(): 120 | if isinstance(m, nn.Conv2d): 121 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 122 | if m.bias is not None: 123 | nn.init.constant_(m.bias, 0) 124 | if isinstance(m ,nn.BatchNorm2d): 125 | nn.init.constant_(m.weight, 1) 126 | nn.init.constant_(m.bias, 0) 127 | 128 | def updata_temperature(self): 129 | if self.temperature!=1: 130 | self.temperature -=3 131 | print('Change temperature to:', str(self.temperature)) 132 | 133 | 134 | def forward(self, x): 135 | x = self.avgpool(x) 136 | x = self.fc1(x) 137 | x = F.relu(x) 138 | x = self.fc2(x).view(x.size(0), -1) 139 | return F.softmax(x/self.temperature, 1) 140 | 141 | 142 | class Dynamic_conv2d(nn.Module): 143 | def __init__(self, in_planes, out_planes, kernel_size, ratio=0.25, stride=1, padding=0, dilation=1, groups=1, bias=True, K=4,temperature=34, init_weight=True): 144 | super(Dynamic_conv2d, self).__init__() 145 | assert in_planes%groups==0 146 | self.in_planes = in_planes 147 | self.out_planes = out_planes 148 | self.kernel_size = kernel_size 149 | self.stride = stride 150 | self.padding = padding 151 | self.dilation = dilation 152 | self.groups = groups 153 | self.bias = bias 154 | self.K = K 155 | self.attention = attention2d(in_planes, ratio, K, temperature) 156 | 157 | self.weight = nn.Parameter(torch.randn(K, out_planes, in_planes//groups, kernel_size, kernel_size), requires_grad=True) 158 | if bias: 159 | self.bias = nn.Parameter(torch.Tensor(K, out_planes)) 160 | else: 161 | self.bias = None 162 | if init_weight: 163 | self._initialize_weights() 164 | 165 | #TODO 初始化 166 | def _initialize_weights(self): 167 | for i in range(self.K): 168 | nn.init.kaiming_uniform_(self.weight[i]) 169 | 170 | 171 | def update_temperature(self): 172 | self.attention.updata_temperature() 173 | 174 | def forward(self, x):#将batch视作维度变量,进行组卷积,因为组卷积的权重是不同的,动态卷积的权重也是不同的 175 | softmax_attention = self.attention(x) 176 | batch_size, in_planes, height, width = x.size() 177 | x = x.view(1, -1, height, width)# 变化成一个维度进行组卷积 178 | weight = self.weight.view(self.K, -1) 179 | 180 | # 动态卷积的权重的生成, 生成的是batch_size个卷积参数(每个参数不同) 181 | aggregate_weight = torch.mm(softmax_attention, weight).view(-1, self.in_planes, self.kernel_size, self.kernel_size) 182 | if self.bias is not None: 183 | aggregate_bias = torch.mm(softmax_attention, self.bias).view(-1) 184 | output = F.conv2d(x, weight=aggregate_weight, bias=aggregate_bias, stride=self.stride, padding=self.padding, 185 | dilation=self.dilation, groups=self.groups*batch_size) 186 | else: 187 | output = F.conv2d(x, weight=aggregate_weight, bias=None, stride=self.stride, padding=self.padding, 188 | dilation=self.dilation, groups=self.groups * batch_size) 189 | 190 | output = output.view(batch_size, self.out_planes, output.size(-2), output.size(-1)) 191 | return output 192 | 193 | 194 | class attention3d(nn.Module): 195 | def __init__(self, in_planes, ratios, K, temperature): 196 | super(attention3d, self).__init__() 197 | assert temperature%3==1 198 | self.avgpool = nn.AdaptiveAvgPool3d(1) 199 | if in_planes != 3: 200 | hidden_planes = int(in_planes * ratios)+1 201 | else: 202 | hidden_planes = K 203 | self.fc1 = nn.Conv3d(in_planes, hidden_planes, 1, bias=False) 204 | self.fc2 = nn.Conv3d(hidden_planes, K, 1, bias=False) 205 | self.temperature = temperature 206 | 207 | def updata_temperature(self): 208 | if self.temperature!=1: 209 | self.temperature -=3 210 | print('Change temperature to:', str(self.temperature)) 211 | 212 | def forward(self, x): 213 | x = self.avgpool(x) 214 | x = self.fc1(x) 215 | x = F.relu(x) 216 | x = self.fc2(x).view(x.size(0), -1) 217 | return F.softmax(x / self.temperature, 1) 218 | 219 | class Dynamic_conv3d(nn.Module): 220 | def __init__(self, in_planes, out_planes, kernel_size, ratio=0.25, stride=1, padding=0, dilation=1, groups=1, bias=True, K=4, temperature=34): 221 | super(Dynamic_conv3d, self).__init__() 222 | assert in_planes%groups==0 223 | self.in_planes = in_planes 224 | self.out_planes = out_planes 225 | self.kernel_size = kernel_size 226 | self.stride = stride 227 | self.padding = padding 228 | self.dilation = dilation 229 | self.groups = groups 230 | self.bias = bias 231 | self.K = K 232 | self.attention = attention3d(in_planes, ratio, K, temperature) 233 | 234 | self.weight = nn.Parameter(torch.randn(K, out_planes, in_planes//groups, kernel_size, kernel_size, kernel_size), requires_grad=True) 235 | if bias: 236 | self.bias = nn.Parameter(torch.Tensor(K, out_planes)) 237 | else: 238 | self.bias = None 239 | 240 | 241 | #TODO 初始化 242 | # nn.init.kaiming_uniform_(self.weight, ) 243 | 244 | def update_temperature(self): 245 | self.attention.updata_temperature() 246 | 247 | def forward(self, x):#将batch视作维度变量,进行组卷积,因为组卷积的权重是不同的,动态卷积的权重也是不同的 248 | softmax_attention = self.attention(x) 249 | batch_size, in_planes, depth, height, width = x.size() 250 | x = x.view(1, -1, depth, height, width)# 变化成一个维度进行组卷积 251 | weight = self.weight.view(self.K, -1) 252 | 253 | # 动态卷积的权重的生成, 生成的是batch_size个卷积参数(每个参数不同) 254 | aggregate_weight = torch.mm(softmax_attention, weight).view(-1, self.in_planes, self.kernel_size, self.kernel_size, self.kernel_size) 255 | if self.bias is not None: 256 | aggregate_bias = torch.mm(softmax_attention, self.bias).view(-1) 257 | output = F.conv3d(x, weight=aggregate_weight, bias=aggregate_bias, stride=self.stride, padding=self.padding, 258 | dilation=self.dilation, groups=self.groups*batch_size) 259 | else: 260 | output = F.conv3d(x, weight=aggregate_weight, bias=None, stride=self.stride, padding=self.padding, 261 | dilation=self.dilation, groups=self.groups * batch_size) 262 | 263 | output = output.view(batch_size, self.out_planes, output.size(-3), output.size(-2), output.size(-1)) 264 | return output 265 | -------------------------------------------------------------------------------- /Ghost Convolution.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates a GhostNet Model as defined in: 3 | GhostNet: More Features from Cheap Operations By Kai Han, Yunhe Wang, Qi Tian, Jianyuan Guo, Chunjing Xu, Chang Xu. 4 | https://arxiv.org/abs/1911.11907 5 | Modified from https://github.com/d-li14/mobilenetv3.pytorch 6 | """ 7 | import torch 8 | import torch.nn as nn 9 | import math 10 | 11 | 12 | __all__ = ['ghost_net'] 13 | 14 | 15 | def _make_divisible(v, divisor, min_value=None): 16 | """ 17 | This function is taken from the original tf repo. 18 | It ensures that all layers have a channel number that is divisible by 8 19 | It can be seen here: 20 | https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py 21 | """ 22 | if min_value is None: 23 | min_value = divisor 24 | new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) 25 | # Make sure that round down does not go down by more than 10%. 26 | if new_v < 0.9 * v: 27 | new_v += divisor 28 | return new_v 29 | 30 | 31 | class SELayer(nn.Module): 32 | def __init__(self, channel, reduction=4): 33 | super(SELayer, self).__init__() 34 | self.avg_pool = nn.AdaptiveAvgPool2d(1) 35 | self.fc = nn.Sequential( 36 | nn.Linear(channel, channel // reduction), 37 | nn.ReLU(inplace=True), 38 | nn.Linear(channel // reduction, channel), ) 39 | 40 | def forward(self, x): 41 | b, c, _, _ = x.size() 42 | y = self.avg_pool(x).view(b, c) 43 | y = self.fc(y).view(b, c, 1, 1) 44 | y = torch.clamp(y, 0, 1) 45 | return x * y 46 | 47 | 48 | def depthwise_conv(inp, oup, kernel_size=3, stride=1, relu=False): 49 | return nn.Sequential( 50 | nn.Conv2d(inp, oup, kernel_size, stride, kernel_size//2, groups=inp, bias=False), 51 | nn.BatchNorm2d(oup), 52 | nn.ReLU(inplace=True) if relu else nn.Sequential(), 53 | ) 54 | 55 | class GhostModule(nn.Module): 56 | def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True): 57 | super(GhostModule, self).__init__() 58 | self.oup = oup 59 | init_channels = math.ceil(oup / ratio) 60 | new_channels = init_channels*(ratio-1) 61 | 62 | self.primary_conv = nn.Sequential( 63 | nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False), 64 | nn.BatchNorm2d(init_channels), 65 | nn.ReLU(inplace=True) if relu else nn.Sequential(), 66 | ) 67 | 68 | self.cheap_operation = nn.Sequential( 69 | nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False), 70 | nn.BatchNorm2d(new_channels), 71 | nn.ReLU(inplace=True) if relu else nn.Sequential(), 72 | ) 73 | 74 | def forward(self, x): 75 | x1 = self.primary_conv(x) 76 | x2 = self.cheap_operation(x1) 77 | out = torch.cat([x1,x2], dim=1) 78 | return out[:,:self.oup,:,:] 79 | -------------------------------------------------------------------------------- /Graph Convolution.py: -------------------------------------------------------------------------------- 1 | import math 2 | import torch 3 | from torch.nn.parameter import Parameter 4 | from torch.nn.modules.module import Module 5 | # source: https://github.com/tkipf/pygcn/blob/master/pygcn/layers.py 6 | 7 | class GraphConvolution(Module): 8 | """ 9 | Simple GCN layer, similar to https://arxiv.org/abs/1609.02907 10 | """ 11 | 12 | def __init__(self, in_features, out_features, bias=True): 13 | super(GraphConvolution, self).__init__() 14 | self.in_features = in_features 15 | self.out_features = out_features 16 | self.weight = Parameter(torch.FloatTensor(in_features, out_features)) 17 | if bias: 18 | self.bias = Parameter(torch.FloatTensor(out_features)) 19 | else: 20 | self.register_parameter('bias', None) 21 | self.reset_parameters() 22 | 23 | def reset_parameters(self): 24 | stdv = 1. / math.sqrt(self.weight.size(1)) 25 | self.weight.data.uniform_(-stdv, stdv) 26 | if self.bias is not None: 27 | self.bias.data.uniform_(-stdv, stdv) 28 | 29 | def forward(self, input, adj): 30 | support = torch.mm(input, self.weight) 31 | output = torch.spmm(adj, support) 32 | if self.bias is not None: 33 | return output + self.bias 34 | else: 35 | return output 36 | 37 | def __repr__(self): 38 | return self.__class__.__name__ + ' (' \ 39 | + str(self.in_features) + ' -> ' \ 40 | + str(self.out_features) + ')' 41 | -------------------------------------------------------------------------------- /Heterogeneous Convolution.py: -------------------------------------------------------------------------------- 1 | # source: https://github.com/irvinxav/Efficient-HetConv-Heterogeneous-Kernel-Based-Convolutions 2 | 3 | class HetConv(nn.Module): 4 | def __init__(self, in_channels, out_channels, p): 5 | super(HetConv, self).__init__() 6 | # Groupwise Convolution 7 | self.gwc = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, groups=p, bias=False) 8 | # Pointwise Convolution 9 | self.pwc = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False) 10 | 11 | def forward(self, x): 12 | return self.gwc(x) + self.pwc(x) 13 | -------------------------------------------------------------------------------- /Hierarchical-Split Block.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | # source: https://github.com/bobo0810/HS-ResNet/blob/main/hsresnet/hs_block.py 5 | 6 | class HSBlock(nn.Module): 7 | ''' 8 | 替代3x3卷积 9 | ''' 10 | def __init__(self, in_ch, s=8): 11 | ''' 12 | 特征大小不改变 13 | :param in_ch: 输入通道 14 | :param s: 分组数 15 | ''' 16 | super(HSBlock, self).__init__() 17 | self.s = s 18 | self.module_list = nn.ModuleList() 19 | 20 | in_ch_range=torch.Tensor(in_ch) 21 | in_ch_list = list(in_ch_range.chunk(chunks=self.s, dim=0)) 22 | 23 | self.module_list.append(nn.Sequential()) 24 | channel_nums = [] 25 | for i in range(1,len(in_ch_list)): 26 | if i == 1: 27 | channels = len(in_ch_list[i]) 28 | else: 29 | random_tensor = torch.Tensor(channel_nums[i-2]) 30 | _, pre_ch = random_tensor.chunk(chunks=2, dim=0) 31 | channels= len(pre_ch)+len(in_ch_list[i]) 32 | channel_nums.append(channels) 33 | self.module_list.append(self.conv_bn_relu(in_ch=channels, out_ch=channels)) 34 | self.initialize_weights() 35 | 36 | def conv_bn_relu(self, in_ch, out_ch, kernel_size=3, stride=1, padding=1): 37 | conv_bn_relu = nn.Sequential( 38 | nn.Conv2d(in_ch, out_ch, kernel_size, stride, padding), 39 | nn.BatchNorm2d(out_ch), 40 | nn.ReLU(inplace=True) 41 | ) 42 | return conv_bn_relu 43 | 44 | def initialize_weights(self): 45 | for m in self.modules(): 46 | if isinstance(m, nn.Conv2d): 47 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 48 | if m.bias is not None: 49 | m.bias.data.zero_() 50 | elif isinstance(m, nn.BatchNorm2d): 51 | m.weight.data.fill_(1) 52 | m.bias.data.zero_() 53 | elif isinstance(m, nn.Linear): 54 | nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') 55 | if m.bias is not None: 56 | m.bias.data.zero_() 57 | 58 | def forward(self, x): 59 | x = list(x.chunk(chunks=self.s, dim=1)) 60 | for i in range(1, len(self.module_list)): 61 | y = self.module_list[i](x[i]) 62 | if i == len(self.module_list) - 1: 63 | x[0] = torch.cat((x[0], y), 1) 64 | else: 65 | y1, y2 = y.chunk(chunks=2, dim=1) 66 | x[0] = torch.cat((x[0], y1), 1) 67 | x[i + 1] = torch.cat((x[i + 1], y2), 1) 68 | return x[0] 69 | -------------------------------------------------------------------------------- /Involution.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | from mmcv.cnn import ConvModule 3 | 4 | # source: https://github.com/d-li14/involution 5 | 6 | 7 | class involution(nn.Module): 8 | 9 | def __init__(self, 10 | channels, 11 | kernel_size, 12 | stride): 13 | super(involution, self).__init__() 14 | self.kernel_size = kernel_size 15 | self.stride = stride 16 | self.channels = channels 17 | reduction_ratio = 4 18 | self.group_channels = 16 19 | self.groups = self.channels // self.group_channels 20 | self.conv1 = ConvModule( 21 | in_channels=channels, 22 | out_channels=channels // reduction_ratio, 23 | kernel_size=1, 24 | conv_cfg=None, 25 | norm_cfg=dict(type='BN'), 26 | act_cfg=dict(type='ReLU')) 27 | self.conv2 = ConvModule( 28 | in_channels=channels // reduction_ratio, 29 | out_channels=kernel_size**2 * self.groups, 30 | kernel_size=1, 31 | stride=1, 32 | conv_cfg=None, 33 | norm_cfg=None, 34 | act_cfg=None) 35 | if stride > 1: 36 | self.avgpool = nn.AvgPool2d(stride, stride) 37 | self.unfold = nn.Unfold(kernel_size, 1, (kernel_size-1)//2, stride) 38 | 39 | def forward(self, x): 40 | weight = self.conv2(self.conv1(x if self.stride == 1 else self.avgpool(x))) 41 | b, c, h, w = weight.shape 42 | weight = weight.view(b, self.groups, self.kernel_size**2, h, w).unsqueeze(2) 43 | out = self.unfold(x).view(b, self.groups, self.group_channels, self.kernel_size**2, h, w) 44 | out = (weight * out).sum(dim=3).view(b, self.channels, h, w) 45 | return out 46 | -------------------------------------------------------------------------------- /Octave Convolution.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | import torch.nn as nn 4 | # source: https://github.com/lxtGH/OctaveConv_pytorch/blob/master/libs/nn/OctaveConv2.py 5 | 6 | class OctaveConv(nn.Module): 7 | def __init__(self, in_channels, out_channels, kernel_size, alpha=0.5, stride=1, padding=1, dilation=1, 8 | groups=1, bias=False): 9 | super(OctaveConv, self).__init__() 10 | kernel_size = kernel_size[0] 11 | self.h2g_pool = nn.AvgPool2d(kernel_size=(2, 2), stride=2) 12 | self.upsample = torch.nn.Upsample(scale_factor=2, mode='nearest') 13 | self.stride = stride 14 | self.l2l = torch.nn.Conv2d(int(alpha * in_channels), int(alpha * out_channels), 15 | kernel_size, 1, padding, dilation, groups, bias) 16 | self.l2h = torch.nn.Conv2d(int(alpha * in_channels), out_channels - int(alpha * out_channels), 17 | kernel_size, 1, padding, dilation, groups, bias) 18 | self.h2l = torch.nn.Conv2d(in_channels - int(alpha * in_channels), int(alpha * out_channels), 19 | kernel_size, 1, padding, dilation, groups, bias) 20 | self.h2h = torch.nn.Conv2d(in_channels - int(alpha * in_channels), 21 | out_channels - int(alpha * out_channels), 22 | kernel_size, 1, padding, dilation, groups, bias) 23 | 24 | def forward(self, x): 25 | X_h, X_l = x 26 | 27 | if self.stride ==2: 28 | X_h, X_l = self.h2g_pool(X_h), self.h2g_pool(X_l) 29 | 30 | X_h2l = self.h2g_pool(X_h) 31 | 32 | X_h2h = self.h2h(X_h) 33 | X_l2h = self.l2h(X_l) 34 | 35 | X_l2l = self.l2l(X_l) 36 | X_h2l = self.h2l(X_h2l) 37 | 38 | X_l2h = self.upsample(X_l2h) 39 | X_h = X_l2h + X_h2h 40 | X_l = X_h2l + X_l2l 41 | 42 | return X_h, X_l 43 | 44 | 45 | class FirstOctaveConv(nn.Module): 46 | def __init__(self, in_channels, out_channels,kernel_size, alpha=0.5, stride=1, padding=1, dilation=1, 47 | groups=1, bias=False): 48 | super(FirstOctaveConv, self).__init__() 49 | self.stride = stride 50 | kernel_size = kernel_size[0] 51 | self.h2g_pool = nn.AvgPool2d(kernel_size=(2, 2), stride=2) 52 | self.h2l = torch.nn.Conv2d(in_channels, int(alpha * out_channels), 53 | kernel_size, 1, padding, dilation, groups, bias) 54 | self.h2h = torch.nn.Conv2d(in_channels, out_channels - int(alpha * out_channels), 55 | kernel_size, 1, padding, dilation, groups, bias) 56 | 57 | def forward(self, x): 58 | if self.stride ==2: 59 | x = self.h2g_pool(x) 60 | 61 | X_h2l = self.h2g_pool(x) 62 | X_h = x 63 | X_h = self.h2h(X_h) 64 | X_l = self.h2l(X_h2l) 65 | 66 | return X_h, X_l 67 | 68 | 69 | class LastOctaveConv(nn.Module): 70 | def __init__(self, in_channels, out_channels, kernel_size, alpha=0.5, stride=1, padding=1, dilation=1, 71 | groups=1, bias=False): 72 | super(LastOctaveConv, self).__init__() 73 | self.stride = stride 74 | kernel_size = kernel_size[0] 75 | self.h2g_pool = nn.AvgPool2d(kernel_size=(2,2), stride=2) 76 | 77 | self.l2h = torch.nn.Conv2d(int(alpha * in_channels), out_channels, 78 | kernel_size, 1, padding, dilation, groups, bias) 79 | self.h2h = torch.nn.Conv2d(in_channels - int(alpha * in_channels), 80 | out_channels, 81 | kernel_size, 1, padding, dilation, groups, bias) 82 | self.upsample = torch.nn.Upsample(scale_factor=2, mode='nearest') 83 | 84 | def forward(self, x): 85 | X_h, X_l = x 86 | 87 | if self.stride ==2: 88 | X_h, X_l = self.h2g_pool(X_h), self.h2g_pool(X_l) 89 | 90 | X_l2h = self.l2h(X_l) 91 | X_h2h = self.h2h(X_h) 92 | X_l2h = self.upsample(X_l2h) 93 | 94 | X_h = X_h2h + X_l2h 95 | 96 | return X_h 97 | 98 | 99 | class OctaveCBR(nn.Module): 100 | def __init__(self,in_channels, out_channels, kernel_size=(3,3),alpha=0.5, stride=1, padding=1, dilation=1, 101 | groups=1, bias=False, norm_layer=nn.BatchNorm2d): 102 | super(OctaveCBR, self).__init__() 103 | self.conv = OctaveConv(in_channels,out_channels,kernel_size, alpha, stride, padding, dilation, groups, bias) 104 | self.bn_h = norm_layer(int(out_channels*(1-alpha))) 105 | self.bn_l = norm_layer(int(out_channels*alpha)) 106 | self.relu = nn.ReLU(inplace=True) 107 | 108 | def forward(self, x): 109 | x_h, x_l = self.conv(x) 110 | x_h = self.relu(self.bn_h(x_h)) 111 | x_l = self.relu(self.bn_l(x_l)) 112 | return x_h, x_l 113 | 114 | 115 | class OctaveCB(nn.Module): 116 | def __init__(self, in_channels, out_channels, kernel_size=(3,3), alpha=0.5, stride=1, padding=1, dilation=1, 117 | groups=1, bias=False, norm_layer=nn.BatchNorm2d): 118 | super(OctaveCB, self).__init__() 119 | self.conv = OctaveConv(in_channels, out_channels, kernel_size, alpha, stride, padding, dilation, 120 | groups, bias) 121 | self.bn_h = norm_layer(int(out_channels * (1 - alpha))) 122 | self.bn_l = norm_layer(int(out_channels * alpha)) 123 | 124 | def forward(self, x): 125 | x_h, x_l = self.conv(x) 126 | x_h = self.bn_h(x_h) 127 | x_l = self.bn_l(x_l) 128 | return x_h, x_l 129 | 130 | 131 | class FirstOctaveCBR(nn.Module): 132 | def __init__(self, in_channels, out_channels, kernel_size=(3,3),alpha=0.5, stride=1, padding=1, dilation=1, 133 | groups=1, bias=False,norm_layer=nn.BatchNorm2d): 134 | super(FirstOctaveCBR, self).__init__() 135 | self.conv = FirstOctaveConv(in_channels,out_channels,kernel_size, alpha,stride,padding,dilation,groups,bias) 136 | self.bn_h = norm_layer(int(out_channels * (1 - alpha))) 137 | self.bn_l = norm_layer(int(out_channels * alpha)) 138 | self.relu = nn.ReLU(inplace=True) 139 | 140 | def forward(self, x): 141 | x_h, x_l = self.conv(x) 142 | x_h = self.relu(self.bn_h(x_h)) 143 | x_l = self.relu(self.bn_l(x_l)) 144 | return x_h, x_l 145 | 146 | 147 | class LastOCtaveCBR(nn.Module): 148 | def __init__(self, in_channels, out_channels, kernel_size=(3,3), alpha=0.5, stride=1, padding=1, dilation=1, 149 | groups=1, bias=False, norm_layer=nn.BatchNorm2d): 150 | super(LastOCtaveCBR, self).__init__() 151 | self.conv = LastOctaveConv(in_channels, out_channels, kernel_size, alpha, stride, padding, dilation, groups, bias) 152 | self.bn_h = norm_layer(out_channels) 153 | self.relu = nn.ReLU(inplace=True) 154 | 155 | def forward(self, x): 156 | x_h = self.conv(x) 157 | x_h = self.relu(self.bn_h(x_h)) 158 | return x_h 159 | 160 | 161 | class FirstOctaveCB(nn.Module): 162 | def __init__(self, in_channels, out_channels, kernel_size=(3,3), alpha=0.5,stride=1, padding=1, dilation=1, 163 | groups=1, bias=False, norm_layer=nn.BatchNorm2d): 164 | super(FirstOctaveCB, self).__init__() 165 | self.conv = FirstOctaveConv(in_channels,out_channels,kernel_size, alpha,stride,padding,dilation,groups,bias) 166 | self.bn_h = norm_layer(int(out_channels * (1 - alpha))) 167 | self.bn_l = norm_layer(int(out_channels * alpha)) 168 | self.relu = nn.ReLU(inplace=True) 169 | 170 | def forward(self, x): 171 | x_h, x_l = self.conv(x) 172 | x_h = self.bn_h(x_h) 173 | x_l = self.bn_l(x_l) 174 | return x_h, x_l 175 | 176 | 177 | class LastOCtaveCB(nn.Module): 178 | def __init__(self, in_channels, out_channels, kernel_size, alpha=0.5, stride=1, padding=1, dilation=1, 179 | groups=1, bias=False, norm_layer=nn.BatchNorm2d): 180 | super(LastOCtaveCB, self).__init__() 181 | self.conv = LastOctaveConv( in_channels, out_channels, kernel_size, alpha, stride, padding, dilation, groups, bias) 182 | self.bn_h = norm_layer(out_channels) 183 | self.relu = nn.ReLU(inplace=True) 184 | 185 | def forward(self, x): 186 | x_h = self.conv(x) 187 | x_h = self.bn_h(x_h) 188 | return x_h 189 | 190 | 191 | if __name__ == '__main__': 192 | # nn.Conv2d 193 | high = torch.Tensor(1, 64, 32, 32).cuda() 194 | low = torch.Tensor(1, 192, 16, 16).cuda() 195 | # test Oc conv 196 | OCconv = OctaveConv(kernel_size=(3,3),in_channels=256,out_channels=512,bias=False,stride=2,alpha=0.75).cuda() 197 | i = high,low 198 | x_out,y_out = OCconv(i) 199 | print(x_out.size()) 200 | print(y_out.size()) 201 | 202 | i = torch.Tensor(1, 3, 512, 512).cuda() 203 | FOCconv = FirstOctaveConv(kernel_size=(3, 3), in_channels=3, out_channels=128).cuda() 204 | x_out, y_out = FOCconv(i) 205 | print("First: ", x_out.size(), y_out.size()) 206 | # test last Octave Cov 207 | LOCconv = LastOctaveConv(kernel_size=(3, 3), in_channels=256, out_channels=128, alpha=0.75).cuda() 208 | i = high, low 209 | out = LOCconv(i) 210 | print("Last: ", out.size()) 211 | 212 | # test OCB 213 | ocb = OctaveCB(in_channels=256, out_channels=128, alpha=0.75).cuda() 214 | i = high, low 215 | x_out_h, y_out_l = ocb(i) 216 | print("OCB:",x_out_h.size(),y_out_l.size()) 217 | 218 | # test last OCB 219 | ocb_last = LastOCtaveCBR(256, 128, alpha=0.75).cuda() 220 | i = high, low 221 | x_out_h = ocb_last(i) 222 | print("Last OCB", x_out_h.size()) 223 | -------------------------------------------------------------------------------- /Res2Net Convotion.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import math 3 | import torch.utils.model_zoo as model_zoo 4 | import torch 5 | import torch.nn.functional as F 6 | __all__ = ['Res2Net', 'res2net50'] 7 | 8 | # source: https://github.com/Res2Net/Res2Net-PretrainedModels/blob/master/res2net.py 9 | 10 | model_urls = { 11 | 'res2net50_26w_4s': 'https://shanghuagao.oss-cn-beijing.aliyuncs.com/res2net/res2net50_26w_4s-06e79181.pth', 12 | 'res2net50_48w_2s': 'https://shanghuagao.oss-cn-beijing.aliyuncs.com/res2net/res2net50_48w_2s-afed724a.pth', 13 | 'res2net50_14w_8s': 'https://shanghuagao.oss-cn-beijing.aliyuncs.com/res2net/res2net50_14w_8s-6527dddc.pth', 14 | 'res2net50_26w_6s': 'https://shanghuagao.oss-cn-beijing.aliyuncs.com/res2net/res2net50_26w_6s-19041792.pth', 15 | 'res2net50_26w_8s': 'https://shanghuagao.oss-cn-beijing.aliyuncs.com/res2net/res2net50_26w_8s-2c7c9f12.pth', 16 | 'res2net101_26w_4s': 'https://shanghuagao.oss-cn-beijing.aliyuncs.com/res2net/res2net101_26w_4s-02a759a1.pth', 17 | } 18 | 19 | 20 | class Bottle2neck(nn.Module): 21 | expansion = 4 22 | 23 | def __init__(self, inplanes, planes, stride=1, downsample=None, baseWidth=26, scale = 4, stype='normal'): 24 | """ Constructor 25 | Args: 26 | inplanes: input channel dimensionality 27 | planes: output channel dimensionality 28 | stride: conv stride. Replaces pooling layer. 29 | downsample: None when stride = 1 30 | baseWidth: basic width of conv3x3 31 | scale: number of scale. 32 | type: 'normal': normal set. 'stage': first block of a new stage. 33 | """ 34 | super(Bottle2neck, self).__init__() 35 | 36 | width = int(math.floor(planes * (baseWidth/64.0))) 37 | self.conv1 = nn.Conv2d(inplanes, width*scale, kernel_size=1, bias=False) 38 | self.bn1 = nn.BatchNorm2d(width*scale) 39 | 40 | if scale == 1: 41 | self.nums = 1 42 | else: 43 | self.nums = scale -1 44 | if stype == 'stage': 45 | self.pool = nn.AvgPool2d(kernel_size=3, stride = stride, padding=1) 46 | convs = [] 47 | bns = [] 48 | for i in range(self.nums): 49 | convs.append(nn.Conv2d(width, width, kernel_size=3, stride = stride, padding=1, bias=False)) 50 | bns.append(nn.BatchNorm2d(width)) 51 | self.convs = nn.ModuleList(convs) 52 | self.bns = nn.ModuleList(bns) 53 | 54 | self.conv3 = nn.Conv2d(width*scale, planes * self.expansion, kernel_size=1, bias=False) 55 | self.bn3 = nn.BatchNorm2d(planes * self.expansion) 56 | 57 | self.relu = nn.ReLU(inplace=True) 58 | self.downsample = downsample 59 | self.stype = stype 60 | self.scale = scale 61 | self.width = width 62 | 63 | def forward(self, x): 64 | residual = x 65 | 66 | out = self.conv1(x) 67 | out = self.bn1(out) 68 | out = self.relu(out) 69 | 70 | spx = torch.split(out, self.width, 1) 71 | for i in range(self.nums): 72 | if i==0 or self.stype=='stage': 73 | sp = spx[i] 74 | else: 75 | sp = sp + spx[i] 76 | sp = self.convs[i](sp) 77 | sp = self.relu(self.bns[i](sp)) 78 | if i==0: 79 | out = sp 80 | else: 81 | out = torch.cat((out, sp), 1) 82 | if self.scale != 1 and self.stype=='normal': 83 | out = torch.cat((out, spx[self.nums]),1) 84 | elif self.scale != 1 and self.stype=='stage': 85 | out = torch.cat((out, self.pool(spx[self.nums])),1) 86 | 87 | out = self.conv3(out) 88 | out = self.bn3(out) 89 | 90 | if self.downsample is not None: 91 | residual = self.downsample(x) 92 | 93 | out += residual 94 | out = self.relu(out) 95 | 96 | return out 97 | -------------------------------------------------------------------------------- /ResNeSt Block.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Components 3 | ''' 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | 8 | # source: https://github.com/STomoya/ResNeSt/blob/master/resnest/layers.py 9 | 10 | ''' 11 | basic layers 12 | ''' 13 | 14 | class GlobalAvgPool2d(nn.Module): 15 | ''' 16 | global average pooling 2D class 17 | ''' 18 | def __init__(self): 19 | super(GlobalAvgPool2d, self).__init__() 20 | def forward(self, x): 21 | return F.adaptive_avg_pool2d(x, 1).view(x.size(0), -1) 22 | 23 | 24 | class ConvBlock(nn.Module): 25 | ''' 26 | convolution block class 27 | convolution 2D -> batch normalization -> ReLU 28 | ''' 29 | def __init__(self, 30 | in_channels, 31 | out_channels, 32 | kernel_size, 33 | stride, 34 | padding 35 | ): 36 | super(ConvBlock, self).__init__() 37 | 38 | self.block = nn.Sequential( 39 | nn.Conv2d( 40 | in_channels=in_channels, 41 | out_channels=out_channels, 42 | kernel_size=kernel_size, 43 | stride=stride, 44 | padding=padding, 45 | bias=False, 46 | ), 47 | nn.BatchNorm2d(out_channels), 48 | nn.ReLU(inplace=True) 49 | ) 50 | 51 | def forward(self, x): 52 | x = self.block(x) 53 | return x 54 | 55 | 56 | ''' 57 | Split Attention 58 | ''' 59 | 60 | class rSoftMax(nn.Module): 61 | ''' 62 | (radix-majorize) softmax class 63 | input is cardinal-major shaped tensor. 64 | transpose to radix-major 65 | ''' 66 | def __init__(self, 67 | groups=1, 68 | radix=2 69 | ): 70 | super(rSoftMax, self).__init__() 71 | 72 | self.groups = groups 73 | self.radix = radix 74 | 75 | def forward(self, x): 76 | B = x.size(0) 77 | # transpose to radix-major 78 | x = x.view(B, self.groups, self.radix, -1).transpose(1, 2) 79 | x = F.softmax(x, dim=1) 80 | x = x.view(B, -1, 1, 1) 81 | 82 | return x 83 | 84 | class SplitAttention(nn.Module): 85 | ''' 86 | split attention class 87 | ''' 88 | def __init__(self, 89 | in_channels, 90 | channels, 91 | kernel_size, 92 | stride=1, 93 | padding=0, 94 | dilation=1, 95 | groups=1, 96 | bias=True, 97 | radix=2, 98 | reduction_factor=4 99 | ): 100 | super(SplitAttention, self).__init__() 101 | 102 | self.radix = radix 103 | 104 | self.radix_conv = nn.Sequential( 105 | nn.Conv2d( 106 | in_channels=in_channels, 107 | out_channels=channels*radix, 108 | kernel_size=kernel_size, 109 | stride=stride, 110 | padding=padding, 111 | dilation=dilation, 112 | groups=groups*radix, 113 | bias=bias 114 | ), 115 | nn.BatchNorm2d(channels*radix), 116 | nn.ReLU(inplace=True) 117 | ) 118 | 119 | inter_channels = max(32, in_channels*radix//reduction_factor) 120 | 121 | self.attention = nn.Sequential( 122 | nn.Conv2d( 123 | in_channels=channels, 124 | out_channels=inter_channels, 125 | kernel_size=1, 126 | groups=groups 127 | ), 128 | nn.BatchNorm2d(inter_channels), 129 | nn.ReLU(inplace=True), 130 | nn.Conv2d( 131 | in_channels=inter_channels, 132 | out_channels=channels*radix, 133 | kernel_size=1, 134 | groups=groups 135 | ) 136 | ) 137 | 138 | self.rsoftmax = rSoftMax( 139 | groups=groups, 140 | radix=radix 141 | ) 142 | 143 | def forward(self, x): 144 | 145 | # NOTE: comments are ugly... 146 | 147 | ''' 148 | input : | in_channels | 149 | ''' 150 | 151 | ''' 152 | radix_conv : | radix 0 | radix 1 | ... | radix r | 153 | | group 0 | group 1 | ... | group k | group 0 | group 1 | ... | group k | ... | group 0 | group 1 | ... | group k | 154 | ''' 155 | x = self.radix_conv(x) 156 | 157 | ''' 158 | split : [ | group 0 | group 1 | ... | group k |, | group 0 | group 1 | ... | group k |, ... ] 159 | sum : | group 0 | group 1 | ...| group k | 160 | ''' 161 | B, rC = x.size()[:2] 162 | splits = torch.split(x, rC // self.radix, dim=1) 163 | gap = sum(splits) 164 | 165 | ''' 166 | !! becomes cardinal-major !! 167 | attention : | group 0 | group 1 | ... | group k | 168 | | radix 0 | radix 1| ... | radix r | radix 0 | radix 1| ... | radix r | ... | radix 0 | radix 1| ... | radix r | 169 | ''' 170 | att_map = self.attention(gap) 171 | 172 | ''' 173 | !! transposed to radix-major in rSoftMax !! 174 | rsoftmax : same as radix_conv 175 | ''' 176 | att_map = self.rsoftmax(att_map) 177 | 178 | ''' 179 | split : same as split 180 | sum : same as sum 181 | ''' 182 | att_maps = torch.split(att_map, rC // self.radix, dim=1) 183 | out = sum([att_map*split for att_map, split in zip(att_maps, splits)]) 184 | 185 | 186 | ''' 187 | output : | group 0 | group 1 | ...| group k | 188 | concatenated tensors of all groups, 189 | which split attention is applied 190 | ''' 191 | 192 | return out.contiguous() 193 | 194 | 195 | ''' 196 | Bottleneck Block 197 | ''' 198 | 199 | class BottleneckBlock(nn.Module): 200 | ''' 201 | bottleneck block class 202 | ''' 203 | expansion = 4 204 | def __init__(self, 205 | in_channels, 206 | channels, 207 | stride=1, 208 | dilation=1, 209 | downsample=None, 210 | radix=2, 211 | groups=1, 212 | bottleneck_width=64, 213 | is_first=False 214 | ): 215 | super(BottleneckBlock, self).__init__() 216 | group_width = int(channels * (bottleneck_width / 64.)) * groups 217 | 218 | layers = [ 219 | ConvBlock( 220 | in_channels=in_channels, 221 | out_channels=group_width, 222 | kernel_size=1, 223 | stride=1, 224 | padding=0 225 | ), 226 | SplitAttention( 227 | in_channels=group_width, 228 | channels=group_width, 229 | kernel_size=3, 230 | stride=stride, 231 | padding=dilation, 232 | dilation=dilation, 233 | groups=groups, 234 | bias=False, 235 | radix=radix 236 | ) 237 | ] 238 | 239 | if stride > 1 or is_first: 240 | layers.append( 241 | nn.AvgPool2d( 242 | kernel_size=3, 243 | stride=stride, 244 | padding=1 245 | ) 246 | ) 247 | 248 | layers += [ 249 | nn.Conv2d( 250 | group_width, 251 | channels*4, 252 | kernel_size=1, 253 | bias=False 254 | ), 255 | nn.BatchNorm2d(channels*4) 256 | ] 257 | 258 | self.block = nn.Sequential(*layers) 259 | self.downsample = downsample 260 | 261 | def forward(self, x): 262 | residual = x 263 | if self.downsample: 264 | residual = self.downsample(x) 265 | out = self.block(x) 266 | out += residual 267 | 268 | return F.relu(out) 269 | 270 | 271 | if __name__ == "__main__": 272 | m = BottleneckBlock(256, 64) 273 | x = torch.randn(3, 256, 4, 4) 274 | print(m(x).size()) 275 | -------------------------------------------------------------------------------- /Self-Calibrated Convolution.py: -------------------------------------------------------------------------------- 1 | 2 | ##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 | ## Created by: Jiang-Jiang Liu 4 | ## Email: j04.liu@gmail.com 5 | ## Copyright (c) 2020 6 | ## 7 | ## LICENSE file in the root directory of this source tree 8 | ##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 | 10 | """SCNet variants""" 11 | import torch 12 | import torch.nn as nn 13 | import torch.nn.functional as F 14 | import torch.utils.model_zoo as model_zoo 15 | 16 | __all__ = ['SCNet', 'scnet50', 'scnet101', 'scnet50_v1d', 'scnet101_v1d'] 17 | 18 | model_urls = { 19 | 'scnet50': 'https://backseason.oss-cn-beijing.aliyuncs.com/scnet/scnet50-dc6a7e87.pth', 20 | 'scnet50_v1d': 'https://backseason.oss-cn-beijing.aliyuncs.com/scnet/scnet50_v1d-4109d1e1.pth', 21 | 'scnet101': 'https://backseason.oss-cn-beijing.aliyuncs.com/scnet/scnet101-44c5b751.pth', 22 | # 'scnet101_v1d': coming soon... 23 | } 24 | 25 | class SCConv(nn.Module): 26 | def __init__(self, inplanes, planes, stride, padding, dilation, groups, pooling_r, norm_layer): 27 | super(SCConv, self).__init__() 28 | self.k2 = nn.Sequential( 29 | nn.AvgPool2d(kernel_size=pooling_r, stride=pooling_r), 30 | nn.Conv2d(inplanes, planes, kernel_size=3, stride=1, 31 | padding=padding, dilation=dilation, 32 | groups=groups, bias=False), 33 | norm_layer(planes), 34 | ) 35 | self.k3 = nn.Sequential( 36 | nn.Conv2d(inplanes, planes, kernel_size=3, stride=1, 37 | padding=padding, dilation=dilation, 38 | groups=groups, bias=False), 39 | norm_layer(planes), 40 | ) 41 | self.k4 = nn.Sequential( 42 | nn.Conv2d(inplanes, planes, kernel_size=3, stride=stride, 43 | padding=padding, dilation=dilation, 44 | groups=groups, bias=False), 45 | norm_layer(planes), 46 | ) 47 | 48 | def forward(self, x): 49 | identity = x 50 | 51 | out = torch.sigmoid(torch.add(identity, F.interpolate(self.k2(x), identity.size()[2:]))) # sigmoid(identity + k2) 52 | out = torch.mul(self.k3(x), out) # k3 * sigmoid(identity + k2) 53 | out = self.k4(out) # k4 54 | 55 | return out 56 | --------------------------------------------------------------------------------