飞道的博客

论文解读:OMNI-DIMENSIONAL DYNAMIC CONVOLUTION

439人阅读  评论(0)


github:https://github.com/OSVAI/ODConv
pdf:https://openreview.net/pdf?id=DmpCfq6Mg39
发表年份:2022
普通的卷积神经网络的卷积核是静态的,最近的动态卷积表明对卷积核权重的线性组合实现conv对输入数据的注意力加权,可以显著提升轻量级cnn的准确性,同时保持高速的推理。ODCONV认为现有的动态卷积(CondConv和DyConv)仅关注到conv-kernel数量的动态性,而忽略了spatial,input-channel,output-chanel的动态性。基于此,使用SE注意力实现全维度动态卷积(channel、filer、spatial、kernel)。ODConv可以插入到现行的许多CNN网络中。ODConv为各种流行的CNN骨干带来了坚实的精度提高,包括轻量级的和大型的骨干,例如,在ImageNet数据集上对MobivleNetV2|ResNet家族的3.77%∼5.71%|1.86%∼3.72%绝对前改进。由于其改进的特性学习能力,即使是一个内核的ODConv也可以与现有的动态卷积对应的多内核竞争或超越,大大减少了额外的参数。此外,ODConv在调制输出特征或卷积权值方面也优于其他注意模块。

对类似工作的评价

CondConv和DyConv的动态特性来自于计算作为输入特征的函数的卷积内核。它们通过核空间的1个维度(关于卷积核数)赋予了卷积核的动态特性,而其他三个维度(关于空间大小、输入通道数和输出通道数)则被忽略了;DyConv|CondConv与ODConv的对比如下所示:

CondConv和DyConv对于所有的输入,卷积核都是享有相同的attention scalar,这限制了其对模型上下文的捕捉能力;
现有的动态卷积是卷积的参数量增量了很多,极大程度上的增加模型大小。
CondConv和DyConv在实测中仅是替换了现有CNN中的最后几个block中的conv层和最后一个全连接层

现有工作对动态卷积的特性未进行充分有效的探索,ODConv可以用于提升轻量化网络的性能。odconv在mobilenet中除了第一个conv都进行了替换,在resnet中替换了所有的conv层。

理论层次实现

基本概念

常规的卷积层有一个静态的卷积核,它适用于所有的输入样本。对于动态卷积层,它使用n个卷积核的线性组合与注意机制进行动态加权,使卷积操作依赖于输入。在数学上,动态卷积运算可以定义为:
y = ( α w 1 W 1 + … + α w n W n ) ∗ x (1) y=\left(\alpha_{w 1} W_{1}+\ldots+\alpha_{w n} W_{n}\right) * x \tag{1} y=(αw1W1++αwnWn)x(1)
其中 x x x ( h , w , c i n ) (h,w,c_{in}) (h,w,cin)的格式表示输入数据, y y y ( h , w , c o u t ) (h,w,c_{out}) (h,w,cout)的格式表示输出数据。 W i W_{i} Wi表示第i个输出滤波器核(其中 W i W_{i} Wi的数据格式为$ (k,k,c_{in}) $ ; $ \alpha_{w i} 为 为 W_{i}$的attention scalar系数,这是由attention函数根据输入数据所计算出来的。

CondConv和DyConv的区别在主要是在计算attention map、模型训练策略和应用动态卷积的层位置上;虽然两者都选用SE结构实现注意力,CondConv使用sigmoid函数,DyConv使用softmax函数(并选用temperature参数抑制softmax的过热输出)。

深入讨论

根据公式1的定义,动态卷积拥有两个特性:1、包含卷积核W,2、具有attention函数和对W的ateention系数$ \alpha_{w i}$。然而卷积核的详细参数有:n个filter、kxk的核参数、输入channel、输出channel。然而CondConv和DyConv仅在filter上进行动态attention设计,忽视了其他在conv上可调控的参数,且使卷积参数增加了n倍(虽然n可控,CondConv中n=8,DyConv中n=4)。通过实验发现,CondConv和DyConv在移除注意力机制后精度增益极其有限,这表明注意力机制在动态卷积中起到了关键作用。

关键定义

ODConv对于动态卷积的定义实现如下所示,其中 α w i \alpha_{w i} αwi表示对卷积核 W i W_i Wi的注意力, α s i \alpha_{s i} αsi表示对kxk卷积核空间上的注意力, α c i \alpha_{c i} αci表示对输入channel的注意力, α f i \alpha_{f i} αfi表示对输出channel的注意力。这里每个注意力的实现方式是有细微差距的。
y = ( α w 1 ⊙ α f 1 ⊙ α c 1 ⊙ α s 1 ⊙ W 1 + … + α w n ⊙ α f n ⊙ α c n ⊙ α s n ⊙ W n ) ∗ x (2) y=\left(\alpha_{w 1} \odot \alpha_{f 1} \odot \alpha_{c 1} \odot \alpha_{s 1} \odot W_{1}+\ldots+\alpha_{w n} \odot \alpha_{f n} \odot \alpha_{c n} \odot \alpha_{s n} \odot W_{n}\right) * x \tag{2} y=(αw1αf1αc1αs1W1++αwnαfnαcnαsnWn)x(2)

在ODConv中,对于卷积核Wi: (1) α s i \alpha_{s i} αsi在空间位置的卷积参数(每个滤波器);(2) α c i \alpha_{c i} αci为每个卷积滤波器Wi m的cin通道分配不同的注意标量;(3) α f i \alpha_{f i} αfi将不同的注意标量分配给卷积滤波器;(4) α w i \alpha_{w i} αwi给整个卷积核分配一个注意标量,下图显示了这四种注意力乘n个卷积核的过程。原则上,这四种类型的注意是互补的,并以位置、通道、滤波器和核的顺序逐步将它们乘以卷积核Wi,使得卷积操作为不同的输入x的所有空间位置、所有输入通道、所有过滤器和所有内核,为捕获丰富的上下文线索提供了性能保证

此外,具有单一卷积核的ODConv比标准的CondConv和DyConv更好,在最终的模型引入更少的额外参数。作者提供了大量的实验来验证这些优势。通过比较等式1和等式2,可以清楚地看到,ODConv是一个更广义的动态卷积。此外,当设置n = 1和所有组件(asi、aci、awi)为1,ODConv只有filter注意αf1将减少为:应用SE变体条件的输入特性卷积过滤器,然后是卷积操作(注意原始SE是基于输出特性,并用于重新校准输出特性本身)。这种SE变量是ODConv的一种特例。

具体实现

Attention实现

对于ODConv,一个关键的问题是如何计算卷积核Wi的四种注意类型 α s i \alpha_{s i} αsi α c i \alpha_{c i} αci α f i \alpha_{f i} αfi α w i \alpha_{w i} αwi。ODConv使用SE类型的注意模块,但以多头的形式实现(每个注意力都是SE类型,主干结构均为同一个GAP+FC+BN+Relu+注意力单独fc,输出头为不同的conv映射),其中FC是使用conv1x1实现的。根据消融实验,FC层对特征的压缩率r为1/16(避免了模型过高的复杂度,在代码实现中r由最小值限制)。

四种注意类型的核心实现代码如下所示:

    def forward(self, x):
        x = self.avgpool(x)
        x = self.fc(x)
        x = self.bn(x)
        x = self.relu(x)
        return self.func_channel(x), self.func_filter(x), self.func_spatial(x), self.func_kernel(x)

各个注意力fc的实现如下,每个注意力都使用了temperature参数,其输出分别为k × k, c i n c_{in} cin × 1, c o u t c_{out} cout × 1 和 n × 1。需要注意的是kernel_attention是使用softmax作为激活函数

    def get_channel_attention(self, x):
        channel_attention = paddle.nn.functional.sigmoid(self.channel_fc(x).reshape((x.shape[0], -1, 1, 1)) / self.temperature)
        return channel_attention

    def get_filter_attention(self, x):
        filter_attention = paddle.nn.functional.sigmoid(self.filter_fc(x).reshape((x.shape[0], -1, 1, 1)) / self.temperature)
        return filter_attention

    def get_spatial_attention(self, x):
        spatial_attention = self.spatial_fc(x).reshape((x.shape[0], 1, 1, 1, self.kernel_size, self.kernel_size))#x.size(0) view
        spatial_attention = paddle.nn.functional.sigmoid(spatial_attention / self.temperature)
        return spatial_attention

    def get_kernel_attention(self, x):
        kernel_attention = self.kernel_fc(x).reshape((x.shape[0], -1, 1, 1, 1, 1))
        kernel_attention = F.softmax(kernel_attention / self.temperature, axis=1)
        return kernel_attention

 

ODConv2D实现

ODConv实现中需要注意的是_forward_impl_common与_forward_impl_pw1x。
当ksize和输出通道为1时:仅对输入数据chanel_attention,其conv参数为F.conv2d中可训练的self.weight;关键代码如下

    #对于conv1x1只进行输入数据chanel_attention
    def _forward_impl_pw1x(self, x):
        channel_attention, filter_attention, spatial_attention, kernel_attention = self.attention(x)
        x = x * channel_attention
        output = F.conv2d(x, weight=self.weight.squeeze(axis=0), bias=None, stride=self.stride, padding=self.padding,
                          dilation=self.dilation, groups=self.groups)
        output = output * filter_attention
        return output

当ksize不为1或输出通常不为1时:使用_forward_impl_common,在所有维度进行attention。channel_attention(输入channel的attention)作用于输入数据x,filter_attention(输出chanel的attention)作用于输出输出x‘;spatial_attention * kernel_attention作用于卷积核中可训练参数self.weight。
关键代码如下,其中在细节上使用了分组卷积(将batch信息与channel信息合并,强行将batch设为1,一个group对于一个batch)

    def _forward_impl_common(self, x):
        # Multiplying channel attention (or filter attention) to weights and feature maps are equivalent,
        # while we observe that when using the latter method the models will run faster with less gpu memory cost.
        channel_attention, filter_attention, spatial_attention, kernel_attention = self.attention(x)
        batch_size, in_planes, height, width = x.shape
        x = x * channel_attention
        x = x.reshape([1, -1, height, width])
        aggregate_weight = spatial_attention * kernel_attention * self.weight.unsqueeze(axis=0)
        aggregate_weight = paddle.sum(aggregate_weight, axis=1).reshape(
            [-1, self.in_planes // self.groups, self.kernel_size, self.kernel_size])
        output = F.conv2d(x, weight=aggregate_weight, bias=None, stride=self.stride, padding=self.padding,
                          dilation=self.dilation, groups=self.groups * batch_size)
        output = output.reshape((batch_size, self.out_planes, output.shape[-2], output.shape[-1]))
        output = output * filter_attention
        return output

 

ODConv2D的完整实现代码如下,其中两个标记为pass的函数在前文已有所实现

class ODConv2D(nn.Layer):
    def __init__(self, in_planes, out_planes, kernel_size, stride=1, padding=0, dilation=1, groups=1,
                 reduction=0.0625, kernel_num=4):
        super(ODConv2D, self).__init__()
        self.in_planes = in_planes
        self.out_planes = out_planes
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding
        self.dilation = dilation
        self.groups = groups
        self.kernel_num = kernel_num
        self.attention = Attention(in_planes, out_planes, kernel_size, groups=groups,
                                   reduction=reduction, kernel_num=kernel_num)
        #self.weight = nn.Parameter(torch.randn(kernel_num, out_planes, in_planes//groups, kernel_size, kernel_size),requires_grad=True)
        self.weight = paddle.create_parameter((kernel_num, out_planes, in_planes//groups, kernel_size, kernel_size),np.float32,default_initializer=nn.initializer.KaimingNormal())
        #self.weight在paddle.create_parameter时已经进行了KaimingNormal初始化,故不需要调用_initialize_weights
        #self._initialize_weights()

        if self.kernel_size == 1 and self.kernel_num == 1:
            self._forward_impl = self._forward_impl_pw1x
        else:
            self._forward_impl = self._forward_impl_common

    def _initialize_weights(self):
        for i in range(self.kernel_num):
            #nn.init.kaiming_normal_(self.weight[i], mode='fan_out', nonlinearity='relu')
            pass

    def update_temperature(self, temperature):
        self.attention.update_temperature(temperature)

    def _forward_impl_common(self, x):
        pass
    
    #对于conv1x1只进行输入数据chanel_attention
    def _forward_impl_pw1x(self, x):
        pass

    def forward(self, x):
        return self._forward_impl(x)

 

ImageNet实验

实验细节

为了进行公平的比较,采用了社区中流行的训练设置,用所有的方法来训练各自的主干模型。特别是,对于ResNet18、ResNet50和ResNet101,所有模型都使用SGD进行100次训练。我们将批处理大小设置为256,权重衰减设置为0.0001,动量设置为0.9。学习速率从0.1开始,每30个时代除以10个。在DyConv(Chen等人,2020年)之后,对于ODConv,也对ResNet18使用了0.1的droprate。ODConv使用ResNet101和ResNet50使用drop rate为0.2。对于MobileNetV2(1.0×,0.75×,0.5×),所有模型都用SGD进行150周期训练(我们在附录中也进行了300时代训练所有模型的实验)。将批处理大小设置为256,权重衰减设置为0.00004,动量设置为0.9。学习速率从0.05开始,并计划在单个余弦周期内达到零。

参考DyConv,ODConv使用MobileNetV2的dropout rate为0.2(1.0×),使用MobileNetV2(0.75×,0.5×)的dropout rate为(0.1,0)。对于DyConv和ODConv的温度退火策略,所有模型的前10个epoch的温度从30个线性降低到1个。所有实验都在有8个gpu的服务器上进行。我们遵循标准协议来训练和评估每个CNN主干。为了进行训练,首先将图像的大小调整到256×256,然后从调整大小的图像中随机抽取224×224的corp,或将其水平翻转与每通道均值和标准差值归一化。为了进行评估,使用中心图像corp测试了top1和top5准确率。

基本实验

表1显示了在MobileNetV2(1.0×,0.75×,0.5×)骨干上的结果比较。由于CondConv和DyConv主要是为了提高高效cnn的性能,它们都为轻量级的MobileNetV2(1.0×,0.75×,0.5×)骨干带来了良好的顶级收益。相比之下,我们的单个卷积内核的ODConv(1×)的性能优于8个卷积内核的CondConv(8×),其性能也与4个卷积内核的DyConv(4×)相当。请注意,ODConv(1×)的这些竞争结果是在额外参数的情况下获得的,这验证了我们的ODConv可以在模型精度和大小之间进行更好的权衡。此外,ODConv(1×)在大多数情况下也优于DCD。 ODConv (4×)取得了最佳结果与训练时期数量的增加(从150到300)的比较可以在附录中找到,从中我们可以观察到类似的性能趋势。

表2显示了在ResNet18和ResNet50骨干上的结果比较,它们比MobileNetV2骨干要大得多。我们可以得到以下观察结果: (1)在ResNet18主干上,动态卷积方法(CondConv、DyConv、DCD和我们的ODConv)和卷积权重修改方法(CGC、加权网和WE)大多比输出特征重新校准方法(SE、CBAM和ECA)具有更好的性能,尽管它们都使用注意机制。相比之下,我们的单卷积核的ODConv(1×)在模型精度和大小上都优于其他方法,为基线模型带来了2.85%的增益。ODConv(4×)得到最好的结果,获得3.72%;(2)然而,在较大的ResNet50主干上,CondConv、DyConv和DCD的结果比大多数其他方法更差,尽管它们的参数数量显著增加。

巧妙的设计在简单的网络上增益较大,在复杂的网络上增益效果有所下降

由于参数冗余的增加,与小网络相比,在大网络中添加更多参数在提高模型精度方面效率较差。由于提出的多维注意机制,ODConv可以更好地解决这一问题,在更大的骨干和轻量级骨干的模型精度和尺寸上都具有优越的性能。为了进一步验证ODConv在非常深和大型网络上的性能,我们将ODConv应用于ResNet101骨干,结果比较如表3所示。同样,ODConv显示了非常有希望的结果,使用ODConv(1×)产生了1.57%的前1名收益。

MS-COCO实验效果

实验细节

为探索ODConv在下游任务中的应用,在COCO数据集上进行实验。2017年版本的MS-COCO数据集包含11.8w张训练图像和5k张包含80个类别的验证图像。
基于MMdetection实现,使用预训练的ResNet50和MobileNetV2做backbone,使用FPN层做neck(里面均为正常卷积)。使用相同的超参数对CondConv和DyConv及ODConv进行重构,在8个GPU上训练,batchsize为2。在MS-COCO训练集上调整这些检测器,这表明总共有12个epoch,在第8个、第10个和第11个epoch将学习率除以10。参考DyConv,在MS-COCO数据集的下游实验中没有使用温度退火策略,因为它容易产生更糟糕的结果。在验证中,报告了在IOU阈值从0.5到0.95下的标准平均精度(AP),增量为0.05。同时还保留了小、中、大物体的AP分数。

实验结果

从表4所示的结果中,我们可以观察到与ImageNet数据集类似的性能改进趋势。更快的R-CNN|面具R-CNN预先训练ResNet50骨干模型,CondConv(8×)和DyCOnv(4×)在基线模型上将AP改善了0.9%|0.8%和1.1%|1.2%,而ODConv(1×)在单一卷积核上改进了1.8%|1.9%。使用预先训练的MobileNetV2(1.0×)骨干模型,我们的ODConv(1×)的性能明显优于CondConv(8×),其性能与DyConv(4×)相同,因为它们的AP差别只有0.2%,取得了更好的模型精度和效率的权衡。在两种探测器上,通过三种方法也获得了对小、中、大物体的AP分数的类似提高。ODConv(4×)总是能达到最好的AP分数。

消融实验

常规消融实验

缩放比例r的实验 在resnet18上r为1/16时效果最佳

注意力的互补性测试 使用4个注意力是精度最佳

推理速度测试 作者说ODConv是speed最佳的。但是无法理解speed是越大越好还是越小越好

ODConv添加位置

将动态卷积应用于更少的层比应用于更多的层会导致更快的运行时速度,表9给出了在MobileNetV2(0.5×)主干的不同层中添加ODConv的性能比较,显示了我们的默认设置(即添加到除第一层外的所有卷积层中的ODConv)具有最大的增益。这表明将ODConv添加到更多的卷积层往往具有更高识别精度的模型

温度策略研究

参考DyConv使用温度退火策略探索ODConv的模型训练过程。为了研究其效果,在ImageNet数据集上进行了一组消融实验,对基于ODConv的ResNet18主干进行训练,无论是否使用温度退火策略。从表10所示的结果可以看出,温度退火策略非常重要,这给基于ODCOonv(1×)|ODConv(4×)的ResNet18模型带来了1.06%|1.04% top-1的改进

激活函数设计

使用两种不同的激活函数(sigmoid函数或Softmax函数)来计算四种类型的注意αsi、αci、αf i和αwi。比较ODConv的不同激活函数的性能也也至关重要。由于SE和DyConv的论文分别讨论了通道维数和卷积核维数的激活函数选择,因此我们采用了他们对ODConv中定义的三个相关注意事项的建议。在这里,我们进行了另一组实验,重点关注空间维度的激活函数的选择。利用ImageNet数据集,使用ResNet18作为测试骨干网络,探索不同激活函数对计算核空间空间维度上ODConv注意力的影响。表11总结了结果,可以看到sigmoid函数比Softmax函数表现更好,因此使用sigmoid函数来计算ODConv空间维数的注意标量

注意力分享策略

从表12中的消融结果可以看出,对每个加性卷积核分别训练αsi、αci和αfi,可以进一步提高模型的精度。也就是说,当你更喜欢获得更准确的模型时,注意力分享策略将会被删除(默认使用共享,有利于提升速度)。

Epoch数调整

将ODConv与MobileNetV2上的CondConv、DyConv和DCD(1.0×,0.75×,0.5×)进行了比较,遵循社区中流行的训练设置,其中所有模型都进行了150个时代的训练。为了进一步验证我们的ODConv的有效性,我们还进行了另一组实验,使用了DyConv所采用的增加数量的训练时期(300而不是150)。所有其他的训练设置与主论文中描述的实验设置保持相同。为了对所有方法进行干净的比较,我们不使用先进的训练技巧,如mixup(Zhang等人,2018a)和标签平滑(Szegedy等人,2016)。结果总结在表13中,其中对于150个时期的模型训练的性能提高显示出与表1中相似的趋势。图3中的结果进一步表明,与CondConv和DyConv相比,即使在轻量级的MobileNetV2骨干上,我们的ODConv在模型精度和尺寸之间得到了更好的权衡。

训练耗时对比

从下表中可以看出,训练成本的趋势与表8中报告的运行时推理速度有些相似。一般来说,动态卷积方法的训练成本明显比基线模型高(约2×到4×),这主要是由于加性卷积核的时间密集的反向传播过程。

更多实验

此外还有更多的消融实验记录,建议查看论文原文 https://openreview.net/pdf?id=DmpCfq6Mg39


转载:https://blog.csdn.net/a486259/article/details/128881748
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场