CVPR | CNN融合注意力机制,芜湖起飞!

**标题:**On the Integration of Self-Attention and Convolution
**论文链接:**https://arxiv.org/pdf/2111.14556
**代码链接:**https://github.com/LeapLabTHU/ACmix

创新点

1. 揭示卷积和自注意力的内在联系

文章通过重新分解卷积和自注意力模块的操作,发现它们在第一阶段(特征投影)都依赖于 1×1 卷积操作,并且这一阶段占据了大部分的计算复杂度(与通道数的平方成正比)。这一发现为整合两种模块提供了理论基础。

2. 提出 ACmix 模型

基于上述发现,作者提出了 ACmix 模型,它通过共享 1×1 卷积操作来同时实现卷积和自注意力的功能。具体来说:
**第一阶段:**输入特征通过 1×1 卷积投影,生成中间特征。
**第二阶段:**这些中间特征分别用于卷积路径(通过移位和聚合操作)和自注意力路径(计算注意力权重并聚合值)。最终,两条路径的输出通过可学习的权重加权求和,得到最终输出。

3. 改进的移位和聚合操作

文章还提出了一种改进的移位操作,通过使用 固定卷积核的分组卷积 来替代传统的张量移位操作。这种方法不仅提高了计算效率,还允许卷积核的可学习性,进一步增强了模型的灵活性。

4. 适应性路径权重

ACmix 引入了两个可学习的标量参数(α 和 β),用于动态调整卷积路径和自注意力路径的权重。这种设计不仅提高了模型的灵活性,还允许模型在不同深度上自适应地选择更适合的特征提取方式。实验表明,这种设计在模型的不同阶段表现出不同的偏好,例如在早期阶段更倾向于卷积,在后期阶段更倾向于自注意力。

整体结构

第一阶段:特征投影

在第一阶段,输入特征通过三个1×1卷积进行投影,分别生成查询(query)、键(key)和值(value)特征映射。这些特征映射随后被重塑为N块,形成一个包含3×N特征映射的中间特征集。

第二阶段:特征聚合

在第二阶段,中间特征集被分为两个路径进行处理:

  • **自注意力路径:**将中间特征集分为N组,每组包含三个特征映射(分别对应查询、键和值)。这些特征映射按照传统的多头自注意力机制进行处理,计算注意力权重并聚合值。
  • **卷积路径:**通过轻量级的全连接层生成k²个特征映射(k为卷积核大小)。这些特征映射通过移位和聚合操作,以类似传统卷积的方式处理输入特征,从局部感受野收集信息。

输出整合

最后,自注意力路径和卷积路径的输出通过两个可学习的标量参数(α和β)加权求和,得到最终的输出。

改进的移位和聚合操作

为了提高计算效率,ACmix模型采用了改进的移位操作,通过固定卷积核的分组卷积来替代传统的张量移位操作。这种方法不仅提高了计算效率,还允许卷积核的可学习性,进一步增强了模型的灵活性。

模型的灵活性和泛化能力

ACmix模型不仅适用于标准的自注意力机制,还可以与各种变体(如Patchwise Attention、Window Attention和Global Attention)结合使用。这种设计使得ACmix能够适应不同的任务需求,具有广泛的适用性。

消融实验

1. 结合两个路径的输出

消融实验探索了卷积和自注意力输出的不同组合方式对模型性能的影响。实验结果表明:

  • **卷积和自注意力的组合优于单一路径:**使用卷积和自注意力模块的组合始终优于仅使用单一路径(如仅卷积或仅自注意力)的模型。
  • **可学习参数的灵活性:**通过引入可学习的参数(如α和β)来动态调整卷积和自注意力路径的权重,ACmix能够根据网络中不同位置的需求自适应地调整路径强度,从而获得更高的灵活性和性能。

2. 组卷积核的选择

实验还对组卷积核的设计进行了验证,结果表明:

  • **用组卷积替代张量位移:**通过使用组卷积替代传统的张量位移操作,显著提高了模型的推理速度。
  • **可学习卷积核和初始化:**使用可学习的卷积核并结合精心设计的初始化方法,进一步增强了模型的灵活性,并有助于提升最终性能。

3. 不同路径的偏好

ACmix模型引入了两个可学习标量α和β,用于动态调整卷积和自注意力路径的权重。通过平行实验,观察到以下趋势:

  • **早期阶段偏好卷积:**在Transformer模型的早期阶段,卷积作为特征提取器表现更好。
  • **中间阶段混合使用:**在网络的中间阶段,模型倾向于混合使用两种路径,并逐渐增加对卷积的偏好。
  • **后期阶段偏好自注意力:**在网络的最后阶段,自注意力表现优于卷积。

4. 对模型性能的影响

这些消融实验结果表明,ACmix模型通过合理结合卷积和自注意力的优势,并优化计算路径,不仅在多个视觉任务上取得了显著的性能提升,还保持了较高的计算效率

ACmix模块的作用

1. 融合卷积和自注意力的优势

ACmix模块通过结合卷积的局部特征提取能力和自注意力的全局感知能力,实现了一种高效的特征融合策略。这种设计使得模型能够同时利用卷积的局部感受野特性和自注意力的灵活性。

2. 优化计算路径

ACmix通过优化计算路径和减少重复计算,提高了整体模块的计算效率。具体来说,它通过1×1卷积对输入特征图进行投影,生成中间特征,然后根据不同的范式(卷积和自注意力)分别重用和聚合这些中间特征。这种设计不仅减少了计算开销,还提升了模型性能。

3. 改进的位移与求和操作

在卷积路径中,ACmix采用深度可分离卷积(depthwise convolution)来替代低效的张量位移操作,从而提高了实际推理效率。

4. 动态调整路径权重

ACmix引入了两个可学习的标量参数(α和β),用于动态调整卷积和自注意力路径的权重。这种设计使得模型能够根据网络中不同位置的需求自适应地调整路径强度,从而获得更高的灵活性。

5. 广泛的应用潜力

ACmix在多个视觉任务(如图像分类、语义分割和目标检测)上均显示出优于单一机制(仅卷积或仅自注意力)的性能,展示了其广泛的应用潜力。

6. 实验验证

实验结果表明,ACmix在保持较低计算开销的同时,能够显著提升模型的性能。例如,在ImageNet分类任务中,ACmix模型在相同的FLOPs或参数数量下表现出色,并且在与竞争对手的基准比较中取得了持续的改进。此外,ACmix在ADE20K语义分割任务和COCO目标检测任务中也显示出明显的改进

代码实现

import torch
import torch.nn as nn


def position(H, W, is_cuda=True):
    if is_cuda:
        loc_w = torch.linspace(-1.0, 1.0, W).cuda().unsqueeze(0).repeat(H, 1)
        loc_h = torch.linspace(-1.0, 1.0, H).cuda().unsqueeze(1).repeat(1, W)
    else:
        loc_w = torch.linspace(-1.0, 1.0, W).unsqueeze(0).repeat(H, 1)
        loc_h = torch.linspace(-1.0, 1.0, H).unsqueeze(1).repeat(1, W)
    loc = torch.cat([loc_w.unsqueeze(0), loc_h.unsqueeze(0)], 0).unsqueeze(0)
    return loc


def stride(x, stride):
    b, c, h, w = x.shape
    return x[:, :, ::stride, ::stride]


def init_rate_half(tensor):
    if tensor is not None:
        tensor.data.fill_(0.5)


def init_rate_0(tensor):
    if tensor is not None:
        tensor.data.fill_(0.)


class ACmix(nn.Module):
    def __init__(self, in_planes, out_planes, kernel_att=7, head=4, kernel_conv=3, stride=1, dilation=1):
        super(ACmix, self).__init__()
        self.in_planes = in_planes
        self.out_planes = out_planes
        self.head = head
        self.kernel_att = kernel_att
        self.kernel_conv = kernel_conv
        self.stride = stride
        self.dilation = dilation
        self.rate1 = torch.nn.Parameter(torch.Tensor(1))
        self.rate2 = torch.nn.Parameter(torch.Tensor(1))
        self.head_dim = self.out_planes // self.head

        self.conv1 = nn.Conv2d(in_planes, out_planes, kernel_size=1)
        self.conv2 = nn.Conv2d(in_planes, out_planes, kernel_size=1)
        self.conv3 = nn.Conv2d(in_planes, out_planes, kernel_size=1)
        self.conv_p = nn.Conv2d(2, self.head_dim, kernel_size=1)

        self.padding_att = (self.dilation * (self.kernel_att - 1) + 1) // 2
        self.pad_att = torch.nn.ReflectionPad2d(self.padding_att)
        self.unfold = nn.Unfold(kernel_size=self.kernel_att, padding=0, stride=self.stride)
        self.softmax = torch.nn.Softmax(dim=1)

        self.fc = nn.Conv2d(3 * self.head, self.kernel_conv * self.kernel_conv, kernel_size=1, bias=False)
        self.dep_conv = nn.Conv2d(self.kernel_conv * self.kernel_conv * self.head_dim, out_planes,
                                  kernel_size=self.kernel_conv, bias=True, groups=self.head_dim, padding=1,
                                  stride=stride)

        self.reset_parameters()

    def reset_parameters(self):
        init_rate_half(self.rate1)
        init_rate_half(self.rate2)
        kernel = torch.zeros(self.kernel_conv * self.kernel_conv, self.kernel_conv, self.kernel_conv)
        for i in range(self.kernel_conv * self.kernel_conv):
            kernel[i, i // self.kernel_conv, i % self.kernel_conv] = 1.
        kernel = kernel.squeeze(0).repeat(self.out_planes, 1, 1, 1)
        self.dep_conv.weight = nn.Parameter(data=kernel, requires_grad=True)
        self.dep_conv.bias = init_rate_0(self.dep_conv.bias)

    def forward(self, x):
        q, k, v = self.conv1(x), self.conv2(x), self.conv3(x)
        scaling = float(self.head_dim) ** -0.5
        b, c, h, w = q.shape
        h_out, w_out = h // self.stride, w // self.stride

        # ### att
        # ## positional encoding
        pe = self.conv_p(position(h, w, x.is_cuda))

        q_att = q.view(b * self.head, self.head_dim, h, w) * scaling
        k_att = k.view(b * self.head, self.head_dim, h, w)
        v_att = v.view(b * self.head, self.head_dim, h, w)

        if self.stride > 1:
            q_att = stride(q_att, self.stride)
            q_pe = stride(pe, self.stride)
        else:
            q_pe = pe

        unfold_k = self.unfold(self.pad_att(k_att)).view(b * self.head, self.head_dim,
                                                         self.kernel_att * self.kernel_att, h_out,
                                                         w_out) # b*head, head_dim, k_att^2, h_out, w_out
        unfold_rpe = self.unfold(self.pad_att(pe)).view(1, self.head_dim, self.kernel_att * self.kernel_att, h_out,
                                                        w_out) # 1, head_dim, k_att^2, h_out, w_out

        att = (q_att.unsqueeze(2) * (unfold_k + q_pe.unsqueeze(2) - unfold_rpe)).sum(
            1) # (b*head, head_dim, 1, h_out, w_out) * (b*head, head_dim, k_att^2, h_out, w_out) -> (b*head, k_att^2, h_out, w_out)
        att = self.softmax(att)

        out_att = self.unfold(self.pad_att(v_att)).view(b * self.head, self.head_dim, self.kernel_att * self.kernel_att,
                                                        h_out, w_out)
        out_att = (att.unsqueeze(1) * out_att).sum(2).view(b, self.out_planes, h_out, w_out)

        ## conv
        f_all = self.fc(torch.cat(
            [q.view(b, self.head, self.head_dim, h * w), k.view(b, self.head, self.head_dim, h * w),
             v.view(b, self.head, self.head_dim, h * w)], 1))
        f_conv = f_all.permute(0, 2, 1, 3).reshape(x.shape[0], -1, x.shape[-2], x.shape[-1])

        out_conv = self.dep_conv(f_conv)

        return self.rate1 * out_att + self.rate2 * out_conv


#输入 B C H W, 输出 B C H W
if __name__ == '__main__':
    block = ACmix(in_planes=64, out_planes=64)
    input = torch.rand(3, 64, 32, 32)
    output = block(input)
    print(input.size(), output.size())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/965053.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ollama部署deepseek实操记录

1. 安装 ollama 1.1 下载并安装 官网 https://ollama.com/ Linux安装命令 https://ollama.com/download/linux curl -fsSL https://ollama.com/install.sh | sh安装成功截图 3. 开放外网访问 1、首先停止ollama服务:systemctl stop ollama 2、修改ollama的servic…

鸟哥Linux私房菜笔记(三)

鸟哥Linux私房菜笔记(三) 该第三部分和第四部分主要为原书的第十一章(正则表达式与文件格式化处理),第十二章学习shell脚本,第十六章(进程管理与SElinux初探部分),第十七…

WordPressAI自动生成发布文章免费插件,SEO,定时任务,生成长尾关键词、根据网站主题内容全自动化后台生成发布文章

一款可以自动发布文章的WordPress插件 wordpress 自动生成文章发布插件下载地址: 点击下载 插件基础功能免费无限制 1、插件后台可输入想要的文章主题或文章构成思路 2、ai自动生成并发布 3、可自定义发布后的文章状态和分类 4、可根据已有的长尾关键词生成文章 5、可对a…

R语言 | 使用 ComplexHeatmap 绘制热图,分区并给对角线分区加黑边框

目的:画热图,分区,给对角线分区添加黑色边框 建议直接看0和4。 0. 准备数据 # 安装并加载必要的包 #install.packages("ComplexHeatmap") # 如果尚未安装 library(ComplexHeatmap)# 使用 iris 数据集 #data(iris)# 选择数值列&a…

机器学习基本概念(附代码)

一、算法与模型的关系 在机器学习领域,算法和模型是两个核心概念。算法是一种偏抽象的概念,它定义了计算机解决问题的步骤和流程。而模型则是更加具体的概念,通过代码实现特定算法来完成任务。数学上,一个简单的模型可以表示为 y…

OpenCV:特征检测总结

目录 一、什么是特征检测? 二、OpenCV 中的常见特征检测方法 1. Harris 角点检测 2. Shi-Tomasi 角点检测 3. Canny 边缘检测 4. SIFT(尺度不变特征变换) 5. ORB 三、特征检测的应用场景 1. 图像匹配 2. 运动检测 3. 自动驾驶 4.…

深度学习系列--01.入门

一.深度学习概念 深度学习(Deep Learning)是机器学习的分支,是指使用多层的神经网络进行机器学习的一种手法抖音百科。它学习样本数据的内在规律和表示层次,最终目标是让机器能够像人一样具有分析学习能力,能够识别文字…

Vue3.5常用特性整理

Vue3.5 发布已近半年&#xff0c;抽空整理下常用的新增/改动特性 响应式 Props 解构 Vue3.5 中 Props 正式支持解构了&#xff0c;并添加了响应式跟踪 设置默认值 使用 JavaScript 原生的默认值语法声明 props 默认值 以前 const props withDefaults(defineProps<{ co…

Windows程序设计10:文件指针及目录的创建与删除

文章目录 前言一、文件指针是什么&#xff1f;二、设置文件指针的位置&#xff1a;随机读写&#xff0c;SetFilePointer函数1.函数说明2.函数实例 三、 目录的创建CreateDirectory四、目录的删除RemoveDirectory总结 前言 Windows程序设计10&#xff1a;文件指针及目录的创建与…

【Linux系统编程】进程间通信(管道:匿名管道、命名管道、实战练习)

知其然&#xff0c;知其所以然 什么是进程间通信&#xff1a; 进程间通信是不同进程间交换信息的一种机制。进程可能在同一台计算机上&#xff0c;也可能在网络中的不同计算机上。那我们为什么要有这种机制&#xff1a; 为什么进程间要通信&#xff1a; ①数据共享&#xff…

K8S ReplicaSet 控制器

一、理论介绍 今天我们来实验 ReplicaSet 控制器&#xff08;也叫工作负载&#xff09;。官网描述如下&#xff1a; 1、是什么&#xff1f; ReplicaSet 副本集&#xff0c; 维护一组稳定的副本 Pod 集合。 2、为什么需要&#xff1f; 解决 pod 被删除了&#xff0c;不能自我恢…

【C语言】自定义类型讲解

文章目录 一、前言二、结构体2.1 概念2.2 定义2.2.1 通常情况下的定义2.2.2 匿名结构体 2.3 结构体的自引用和嵌套2.4 结构体变量的定义与初始化2.5 结构体的内存对齐2.6 结构体传参2.7 结构体实现位段 三、枚举3.1 概念3.2 定义3.3 枚举的优点3.3.1 提高代码的可读性3.3.2 防止…

VUE2双向绑定的原理

文章目录 VUE2双向绑定的原理1. 什么是双向绑定2. 双向绑定的原理2.1 ViewModel的重要作用2.2 双向绑定的流程 3. 双向绑定的实现3.1 data响应化处理3.2 Compile编译3.3 依赖收集 VUE2双向绑定的原理 1. 什么是双向绑定 讲双向绑定先讲单项绑定&#xff0c;啥叫单项绑定&…

入行FPGA设计工程师需要提前学习哪些内容?

FPGA作为一种灵活可编程的硬件平台&#xff0c;广泛应用于嵌入式系统、通信、数据处理等领域。很多人选择转行FPGA设计工程师&#xff0c;但对于新手来说&#xff0c;可能在学习过程中会遇到一些迷茫和困惑。为了帮助大家更好地准备&#xff0c;本文将详细介绍入行FPGA设计工程…

Mac M1 ComfyUI 中 AnyText插件安装问题汇总?

Q1&#xff1a;NameError: name ‘PreTrainedTokenizer’ is not defined ? 该项目最近更新日期为2024年12月&#xff0c;该时间段的transformers 版本由PyPI 上的 transformers 页面 可知为4.47.1. A1: transformers 版本不满足要求&#xff0c;必须降级transformors &#…

深度学习 Pytorch 神经网络的学习

本节将从梯度下降法向外拓展&#xff0c;介绍更常用的优化算法&#xff0c;实现神经网络的学习和迭代。在本节课结束将完整实现一个神经网络训练的全流程。 对于像神经网络这样的复杂模型&#xff0c;可能会有数百个 w w w的存在&#xff0c;同时如果我们使用的是像交叉熵这样…

Java 大视界 -- 深度洞察 Java 大数据安全多方计算的前沿趋势与应用革新(52)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Docker使用指南(二)——容器相关操作详解(实战案例教学,创建/使用/停止/删除)

目录 1.容器操作相关命令​编辑 案例一&#xff1a; 案例二&#xff1a; 容器常用命令总结&#xff1a; 1.查看容器状态&#xff1a; 2.删除容器&#xff1a; 3.进入容器&#xff1a; 二、Docker基本操作——容器篇 1.容器操作相关命令 下面我们用两个案例来具体实操一…

【C++】STL——list的使用

目录 &#x1f495;1.带头双向链表List &#x1f495;2.list用法介绍 &#x1f495;3.list的初始化 &#x1f495;4.size函数与resize函数 &#x1f495;5.empty函数 &#x1f495;6.front函数与back函数 &#x1f495;7.push_front,push_back,pop_front,pop_back函数…

Java面试题集合篇5:10道基础面试题

文章目录 前言41、多线程使用 ArrayList42、List 和 Set 区别43、HashSet 实现原理44、HashSet检查重复和保证数据不可重复45、BlockingQueue46、Map接口46.1、HashMap实现原理46.2、HashMap在JDK1.7和JDK1.8中不同点46.3、JDK1.7 VS JDK1.8 比较 47、HashMap的put方法流程48、…