You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
6.2 KiB
146 lines
6.2 KiB
"""
|
|
ECA module from ECAnet
|
|
|
|
paper: ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks
|
|
https://arxiv.org/abs/1910.03151
|
|
|
|
Original ECA model borrowed from https://github.com/BangguWu/ECANet
|
|
|
|
Modified circular ECA implementation and adaption for use in timm package
|
|
by Chris Ha https://github.com/VRandme
|
|
|
|
Original License:
|
|
|
|
MIT License
|
|
|
|
Copyright (c) 2019 BangguWu, Qilong Wang
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
"""
|
|
import math
|
|
from torch import nn
|
|
import torch.nn.functional as F
|
|
|
|
|
|
from .create_act import create_act_layer
|
|
from .helpers import make_divisible
|
|
|
|
|
|
class EcaModule(nn.Module):
|
|
"""Constructs an ECA module.
|
|
|
|
Args:
|
|
channels: Number of channels of the input feature map for use in adaptive kernel sizes
|
|
for actual calculations according to channel.
|
|
gamma, beta: when channel is given parameters of mapping function
|
|
refer to original paper https://arxiv.org/pdf/1910.03151.pdf
|
|
(default=None. if channel size not given, use k_size given for kernel size.)
|
|
kernel_size: Adaptive selection of kernel size (default=3)
|
|
gamm: used in kernel_size calc, see above
|
|
beta: used in kernel_size calc, see above
|
|
act_layer: optional non-linearity after conv, enables conv bias, this is an experiment
|
|
gate_layer: gating non-linearity to use
|
|
"""
|
|
def __init__(
|
|
self, channels=None, kernel_size=3, gamma=2, beta=1, act_layer=None, gate_layer='sigmoid',
|
|
rd_ratio=1/8, rd_channels=None, rd_divisor=8, use_mlp=False):
|
|
super(EcaModule, self).__init__()
|
|
if channels is not None:
|
|
t = int(abs(math.log(channels, 2) + beta) / gamma)
|
|
kernel_size = max(t if t % 2 else t + 1, 3)
|
|
assert kernel_size % 2 == 1
|
|
padding = (kernel_size - 1) // 2
|
|
if use_mlp:
|
|
# NOTE 'mlp' mode is a timm experiment, not in paper
|
|
assert channels is not None
|
|
if rd_channels is None:
|
|
rd_channels = make_divisible(channels * rd_ratio, divisor=rd_divisor)
|
|
act_layer = act_layer or nn.ReLU
|
|
self.conv = nn.Conv1d(1, rd_channels, kernel_size=1, padding=0, bias=True)
|
|
self.act = create_act_layer(act_layer)
|
|
self.conv2 = nn.Conv1d(rd_channels, 1, kernel_size=kernel_size, padding=padding, bias=True)
|
|
else:
|
|
self.conv = nn.Conv1d(1, 1, kernel_size=kernel_size, padding=padding, bias=False)
|
|
self.act = None
|
|
self.conv2 = None
|
|
self.gate = create_act_layer(gate_layer)
|
|
|
|
def forward(self, x):
|
|
y = x.mean((2, 3)).view(x.shape[0], 1, -1) # view for 1d conv
|
|
y = self.conv(y)
|
|
if self.conv2 is not None:
|
|
y = self.act(y)
|
|
y = self.conv2(y)
|
|
y = self.gate(y).view(x.shape[0], -1, 1, 1)
|
|
return x * y.expand_as(x)
|
|
|
|
|
|
EfficientChannelAttn = EcaModule # alias
|
|
|
|
|
|
class CecaModule(nn.Module):
|
|
"""Constructs a circular ECA module.
|
|
|
|
ECA module where the conv uses circular padding rather than zero padding.
|
|
Unlike the spatial dimension, the channels do not have inherent ordering nor
|
|
locality. Although this module in essence, applies such an assumption, it is unnecessary
|
|
to limit the channels on either "edge" from being circularly adapted to each other.
|
|
This will fundamentally increase connectivity and possibly increase performance metrics
|
|
(accuracy, robustness), without significantly impacting resource metrics
|
|
(parameter size, throughput,latency, etc)
|
|
|
|
Args:
|
|
channels: Number of channels of the input feature map for use in adaptive kernel sizes
|
|
for actual calculations according to channel.
|
|
gamma, beta: when channel is given parameters of mapping function
|
|
refer to original paper https://arxiv.org/pdf/1910.03151.pdf
|
|
(default=None. if channel size not given, use k_size given for kernel size.)
|
|
kernel_size: Adaptive selection of kernel size (default=3)
|
|
gamm: used in kernel_size calc, see above
|
|
beta: used in kernel_size calc, see above
|
|
act_layer: optional non-linearity after conv, enables conv bias, this is an experiment
|
|
gate_layer: gating non-linearity to use
|
|
"""
|
|
|
|
def __init__(self, channels=None, kernel_size=3, gamma=2, beta=1, act_layer=None, gate_layer='sigmoid'):
|
|
super(CecaModule, self).__init__()
|
|
if channels is not None:
|
|
t = int(abs(math.log(channels, 2) + beta) / gamma)
|
|
kernel_size = max(t if t % 2 else t + 1, 3)
|
|
has_act = act_layer is not None
|
|
assert kernel_size % 2 == 1
|
|
|
|
# PyTorch circular padding mode is buggy as of pytorch 1.4
|
|
# see https://github.com/pytorch/pytorch/pull/17240
|
|
# implement manual circular padding
|
|
self.padding = (kernel_size - 1) // 2
|
|
self.conv = nn.Conv1d(1, 1, kernel_size=kernel_size, padding=0, bias=has_act)
|
|
self.gate = create_act_layer(gate_layer)
|
|
|
|
def forward(self, x):
|
|
y = x.mean((2, 3)).view(x.shape[0], 1, -1)
|
|
# Manually implement circular padding, F.pad does not seemed to be bugged
|
|
y = F.pad(y, (self.padding, self.padding), mode='circular')
|
|
y = self.conv(y)
|
|
y = self.gate(y).view(x.shape[0], -1, 1, 1)
|
|
return x * y.expand_as(x)
|
|
|
|
|
|
CircularEfficientChannelAttn = CecaModule
|