【论文实操】从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。本人能力有限,路过的各位大神若发现纰漏的地方还望指教一二!感谢!希望本文能够帮助到大家。


相关文章
|
机器学习/深度学习 计算机视觉
用实验数据验证面试题:VGG使用3x3卷积核的优势
用实验数据验证面试题:VGG使用3x3卷积核的优势
469 0
用实验数据验证面试题:VGG使用3x3卷积核的优势
|
6月前
|
机器学习/深度学习 测试技术 Ruby
YOLOv8改进 | 主干篇 | 反向残差块网络EMO一种轻量级的CNN架构(附完整代码 + 修改教程)
YOLOv8改进 | 主干篇 | 反向残差块网络EMO一种轻量级的CNN架构(附完整代码 + 修改教程)
207 0
|
28天前
|
机器学习/深度学习 数据可视化 测试技术
YOLO11实战:新颖的多尺度卷积注意力(MSCA)加在网络不同位置的涨点情况 | 创新点如何在自己数据集上高效涨点,解决不涨点掉点等问题
本文探讨了创新点在自定义数据集上表现不稳定的问题,分析了不同数据集和网络位置对创新效果的影响。通过在YOLO11的不同位置引入MSCAAttention模块,展示了三种不同的改进方案及其效果。实验结果显示,改进方案在mAP50指标上分别提升了至0.788、0.792和0.775。建议多尝试不同配置,找到最适合特定数据集的解决方案。
205 0
|
2月前
|
机器学习/深度学习 PyTorch 算法框架/工具
CNN中的注意力机制综合指南:从理论到Pytorch代码实现
注意力机制已成为深度学习模型的关键组件,尤其在卷积神经网络(CNN)中发挥了重要作用。通过使模型关注输入数据中最相关的部分,注意力机制显著提升了CNN在图像分类、目标检测和语义分割等任务中的表现。本文将详细介绍CNN中的注意力机制,包括其基本概念、不同类型(如通道注意力、空间注意力和混合注意力)以及实际实现方法。此外,还将探讨注意力机制在多个计算机视觉任务中的应用效果及其面临的挑战。无论是图像分类还是医学图像分析,注意力机制都能显著提升模型性能,并在不断发展的深度学习领域中扮演重要角色。
82 10
|
6月前
|
计算机视觉
【YOLOv8改进】 AFPN :渐进特征金字塔网络 (论文笔记+引入代码).md
YOLO目标检测专栏介绍了YOLO的有效改进和实战案例,包括AFPN——一种解决特征金字塔网络信息丢失问题的新方法。AFPN通过非相邻层直接融合和自适应空间融合处理多尺度特征,提高检测性能。此外,还展示了YOLOv8中引入的MPDIoU和ASFF模块的代码实现。详情可参考提供的专栏链接。
|
6月前
|
机器学习/深度学习 存储 测试技术
【YOLOv8改进】 YOLOv8 更换骨干网络之 GhostNet :通过低成本操作获得更多特征 (论文笔记+引入代码).md
YOLO目标检测专栏探讨了卷积神经网络的创新改进,如Ghost模块,它通过低成本运算生成更多特征图,降低资源消耗,适用于嵌入式设备。GhostNet利用Ghost模块实现轻量级架构,性能超越MobileNetV3。此外,文章还介绍了SegNeXt,一个高效卷积注意力网络,提升语义分割性能,参数少但效果优于EfficientNet-L2。专栏提供YOLO相关基础解析、改进方法和实战案例。
|
6月前
|
编解码 计算机视觉 网络架构
【YOLOv8改进】BiFPN:加权双向特征金字塔网络 (论文笔记+引入代码)
该专栏深入研究了YOLO目标检测的神经网络架构优化,提出了加权双向特征金字塔网络(BiFPN)和复合缩放方法,以提升模型效率。BiFPN通过双向跨尺度连接和加权融合增强信息传递,同时具有自适应的网络拓扑结构。结合EfficientNet,构建了EfficientDet系列检测器,在效率和准确性上超越先前技术。此外,介绍了YOLOv8如何引入MPDIoU并应用BiFPN进行可学习权重的特征融合。更多详情可参考提供的专栏链接。
|
6月前
|
机器学习/深度学习 算法 计算机视觉
YOLOv8 | 卷积模块 | 提高网络的灵活性和表征能力的动态卷积【附代码+小白可上手】
本教程介绍了如何在YOLOv8中使用动态卷积提升网络性能和灵活性。动态卷积利用注意力机制动态选择和组合卷积核,适应输入数据特征,解决了轻量级CNN的局限。文中提供了详细步骤教读者如何添加和修改代码,包括在`conv.py`中添加`Dynamic_conv2d`模块,更新`init.py`、`task.py`和`yaml`配置文件。此外,还分享了完整代码和进阶技巧,帮助深度学习初学者实践目标检测。参考[YOLOv8改进](https://blog.csdn.net/m0_67647321/category_12548649.html)专栏获取更多详情。
|
6月前
|
机器学习/深度学习 计算机视觉
【YOLOv8改进-论文笔记】SCConv :即插即用的空间和通道重建卷积
该文介绍了一种针对卷积神经网络(CNN)的改进方法,名为SCConv,旨在减少计算冗余并提升特征学习效率。SCConv包含空间重构单元(SRU)和通道重构单元(CRU),分别处理空间和通道冗余。SRU利用分离-重构策略抑制空间冗余,而CRU通过分割-变换-融合策略减少通道冗余。SCConv可直接插入现有CNN架构中,实验结果显示,整合SCConv的模型能在降低复杂性和计算成本的同时保持或提高性能。此外,文章还展示了如何在YOLOv8中应用SCConv。
|
6月前
|
机器学习/深度学习 并行计算
YOLOv8改进 | ODConv卷积助力极限涨点(附修改后的C2f、Bottleneck模块代码)
YOLOv8改进 | ODConv卷积助力极限涨点(附修改后的C2f、Bottleneck模块代码)
479 0