3838import torch .nn .functional as F
3939
4040
41+ from .create_act import create_act_layer
42+
43+
4144class EcaModule (nn .Module ):
4245 """Constructs an ECA module.
4346
@@ -48,20 +51,27 @@ class EcaModule(nn.Module):
4851 refer to original paper https://arxiv.org/pdf/1910.03151.pdf
4952 (default=None. if channel size not given, use k_size given for kernel size.)
5053 kernel_size: Adaptive selection of kernel size (default=3)
54+ gamm: used in kernel_size calc, see above
55+ beta: used in kernel_size calc, see above
56+ act_layer: optional non-linearity after conv, enables conv bias, this is an experiment
57+ gate_layer: gating non-linearity to use
5158 """
52- def __init__ (self , channels = None , kernel_size = 3 , gamma = 2 , beta = 1 ):
59+ def __init__ (self , channels = None , kernel_size = 3 , gamma = 2 , beta = 1 , act_layer = None , gate_layer = 'sigmoid' ):
5360 super (EcaModule , self ).__init__ ()
54- assert kernel_size % 2 == 1
5561 if channels is not None :
5662 t = int (abs (math .log (channels , 2 ) + beta ) / gamma )
5763 kernel_size = max (t if t % 2 else t + 1 , 3 )
58-
59- self .conv = nn .Conv1d (1 , 1 , kernel_size = kernel_size , padding = (kernel_size - 1 ) // 2 , bias = False )
64+ assert kernel_size % 2 == 1
65+ has_act = act_layer is not None
66+ self .conv = nn .Conv1d (1 , 1 , kernel_size = kernel_size , padding = (kernel_size - 1 ) // 2 , bias = has_act )
67+ self .act = create_act_layer (act_layer ) if has_act else nn .Identity ()
68+ self .gate = create_act_layer (gate_layer )
6069
6170 def forward (self , x ):
6271 y = x .mean ((2 , 3 )).view (x .shape [0 ], 1 , - 1 ) # view for 1d conv
6372 y = self .conv (y )
64- y = y .view (x .shape [0 ], - 1 , 1 , 1 ).sigmoid ()
73+ y = self .act (y ) # NOTE: usually a no-op, added for experimentation
74+ y = self .gate (y ).view (x .shape [0 ], - 1 , 1 , 1 )
6575 return x * y .expand_as (x )
6676
6777
@@ -86,27 +96,35 @@ class CecaModule(nn.Module):
8696 refer to original paper https://arxiv.org/pdf/1910.03151.pdf
8797 (default=None. if channel size not given, use k_size given for kernel size.)
8898 kernel_size: Adaptive selection of kernel size (default=3)
99+ gamm: used in kernel_size calc, see above
100+ beta: used in kernel_size calc, see above
101+ act_layer: optional non-linearity after conv, enables conv bias, this is an experiment
102+ gate_layer: gating non-linearity to use
89103 """
90104
91- def __init__ (self , channels = None , kernel_size = 3 , gamma = 2 , beta = 1 ):
105+ def __init__ (self , channels = None , kernel_size = 3 , gamma = 2 , beta = 1 , act_layer = None , gate_layer = 'sigmoid' ):
92106 super (CecaModule , self ).__init__ ()
93- assert kernel_size % 2 == 1
94107 if channels is not None :
95108 t = int (abs (math .log (channels , 2 ) + beta ) / gamma )
96109 kernel_size = max (t if t % 2 else t + 1 , 3 )
110+ has_act = act_layer is not None
111+ assert kernel_size % 2 == 1
97112
98113 # PyTorch circular padding mode is buggy as of pytorch 1.4
99114 # see https://github.com/pytorch/pytorch/pull/17240
100115 # implement manual circular padding
101- self .conv = nn .Conv1d (1 , 1 , kernel_size = kernel_size , padding = 0 , bias = False )
102116 self .padding = (kernel_size - 1 ) // 2
117+ self .conv = nn .Conv1d (1 , 1 , kernel_size = kernel_size , padding = 0 , bias = has_act )
118+ self .act = create_act_layer (act_layer ) if has_act else nn .Identity ()
119+ self .gate = create_act_layer (gate_layer )
103120
104121 def forward (self , x ):
105122 y = x .mean ((2 , 3 )).view (x .shape [0 ], 1 , - 1 )
106123 # Manually implement circular padding, F.pad does not seemed to be bugged
107124 y = F .pad (y , (self .padding , self .padding ), mode = 'circular' )
108125 y = self .conv (y )
109- y = y .view (x .shape [0 ], - 1 , 1 , 1 ).sigmoid ()
126+ y = self .act (y ) # NOTE: usually a no-op, added for experimentation
127+ y = self .gate (y ).view (x .shape [0 ], - 1 , 1 , 1 )
110128 return x * y .expand_as (x )
111129
112130
0 commit comments