注意:本文代码为自己理解之后实现,与原论文代码原理相同但并不完全一样,主要是输入张量的形状不同,若更想了解原文代码,可以访问:https://blog.csdn.net/weixin_45084253/article/details/124282580
(加入超链接不太行,)
假设 b a t c h s i z e batchsize batchsize为 1024 1024 1024,每个元素是一个序列式的特征,比如是一个长为 23 23 23的序列,单个序列元素特征为 708 708 708,则输入进入 E C A ECA ECA模块的张量形状为 [ 1024 , 23 , 708 ] [1024,23,708] [1024,23,708],而 E C A ECA ECA内部的结构为:
def call(self, inputs, mask=None):
nn = tf.keras.layers.GlobalAveragePooling1D()(inputs, mask=mask)
nn = tf.expand_dims(nn, -1)
nn = self.conv(nn)
nn = tf.squeeze(nn, -1)
nn = tf.nn.sigmoid(nn)
nn = nn[:,None,:]
return inputs * nn
首先经过一个全局池化层,则张量 [ 1024 , 23 , 708 ] [1024,23,708] [1024,23,708]的形状变为 [ 1024 , 708 ] [1024,708] [1024,708]
E C A ECA ECA与普通的 C A CA CA不同的地方在于:
普通的 C A CA CA是对通道先进行降采样,然后进行上采样,而 E C A ECA ECA作者认为这种做法学不到通道之间的关系,而应该转为学习邻居通道之间的关系,这个是比较有道理,因为我这里面的特征是人脸的 E Y E + N O S E + L I P EYE+NOSE+LIP EYE+NOSE+LIP和手的所有特征,相邻特征之间学习起来更合理,所以作者采用的是一维卷积的方法:
之后由于要进行一维卷积,所以作者对其最后一个维度进行维度扩展:
nn = tf.expand_dims(nn,-1)
也就是张量的形状变成 [ 1024 , 708 , 1 ] [1024,708,1] [1024,708,1],经过一维卷积之后:
conv = tf.keras.layers.Conv1D(1,kernel_size=kernel_size,strides=1,padding="same",use_bias=False)
由于这块做了等维的 p a d pad pad,所以张量的形状变成了 [ 1024 , 708 , 1 ] [1024,708,1] [1024,708,1],注意是对倒数第 2 2 2个进行卷积。到这块其实注意力权重就已经算出来了,接下来需要把最后一维的 1 1 1放到第 2 2 2维上(按照索引来说应该是第 1 1 1维),也就是:
>>> nn = tf.squeeze(nn,-1)
>>> nn.shape
TensorShape([1024, 708])
>>> nn = nn[:,None,:]
>>> nn.shape
TensorShape([1024, 1, 708])
不要忘记做 s i g m o i d sigmoid sigmoid操作:
nn = tf.squeeze(nn, -1)
nn = tf.nn.sigmoid(nn)
nn = nn[:,None,:]