EfficientFormerV2:全新的轻量级视觉Transformer

news2024/9/30 9:29:58

期刊:2023 IEEE/CVF International Conference on Computer Vision (ICCV)

标题:Rethinking(重新审视) Vision Transformers(ViT) for MobileNet Size and Speed(MobileNet的规模和速度)

                                                                                                                                                  ——重新审视ViT能否达到MobileNet的规模和速度

论文:https://arxiv.org/pdf/2212.08059.pdf

源码:GitHub - snap-research/EfficientFormer: EfficientFormerV2 [ICCV 2023] & EfficientFormer [NeurIPs 2022]

一、摘要 

研究背景:随着视觉Transformers(ViTs)在计算机视觉任务中的成功,最近的技术试图优化ViT的性能和复杂性,以实现在移动设备上的高效部署。研究人员提出了多种方法来加速注意力机制,改进低效设计,或结合mobile-friendly的轻量级卷积来形成混合架构

研究问题:然而,ViT及其变体仍然比轻量级的CNNs具有更高的延迟或更多的参数,即使对于多年前的MobileNet也是如此。实际上,延迟和大小对于资源受限硬件上的高效部署都至关重要

主要工作

  • 1. 重新审视了ViT的设计选择,并提出了一种具有低延迟和高参数效率的改进型超网络。
  • 2. 进一步引入了一种细粒度联合搜索策略,该策略可以通过同时优化延迟和参数量来找到有效的架构。

研究成果:所提出的模型EfficientFormerV2在ImageNet-1K上实现了比MobileNetV2和MobileNetV1高约4%的top-1精度,具有相似的延迟和参数。论文证明,适当设计和优化的ViT可以以MobileNet级别的大小和速度实现高性能。

  

  

二、网络架构

EfficientFormerV2网络架构如下:

 

3.1. Token Mixers vs. Feed Forward Network(FFN代替Token Mixers)

前人工作:PoolFormer和EfficientFormer采用3 x 3个平均池化层作为local token(局部标记)混合器。(local token混合器的作用)增强局部信息可以提高性能,并使ViT在没有显式位置嵌入的情况下更加鲁棒。

动机1:用相同内核大小的深度卷积(DWCONV)替换这些层不会引入延迟开销,而性能提高了0.6%,额外参数可以忽略不计(0.02M)。

动机2:此外,最近的工作表明,在ViT中的前馈网络(FFN)中添加局部信息捕获层也是有益的,以提高性能,同时开销较小。

改进:基于这些观察,删除了显式残差连接的局部标记混合器,并将深度方向的 3 × 3 CONV 移动到FFN中,以获得启用局部性的统一FFN。将统一的 FFN 应用于网络的所有阶段。这种设计修改将网络架构简化为只有两种类型的块 (Local FFN 和 Gobel Attention),并在相同延迟下将准确率提高到 80.3%,参数开销很小(0.1M)。

代码如下:

FFN深度卷积前馈网络:

# 深度卷积前馈网络
class FFN(nn.Module):
    def __init__(self, dim, pool_size=3, mlp_ratio=4.,
                 act_layer=nn.GELU,
                 drop=0., drop_path=0.,
                 use_layer_scale=True, layer_scale_init_value=1e-5):
        super().__init__()

        mlp_hidden_dim = int(dim * mlp_ratio)       # 隐藏层维度
        self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim,
                       act_layer=act_layer, drop=drop, mid_conv=True)       # 多层感知机

        self.drop_path = DropPath(drop_path) if drop_path > 0. \
            else nn.Identity()
        self.use_layer_scale = use_layer_scale
        if use_layer_scale:
            self.layer_scale_2 = nn.Parameter(
                layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)   # 缩放因子

    def forward(self, x):
        if self.use_layer_scale:
            x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))    # 残差连接
        else:
            x = x + self.drop_path(self.mlp(x))
        return x
# 多层感知机
class Mlp(nn.Module):
    """
    Implementation of MLP with 1*1 convolutions.
    Input: tensor with shape [B, C, H, W]
    """

    def __init__(self, in_features, hidden_features=None,
                 out_features=None, act_layer=nn.GELU, drop=0., mid_conv=False):
        super().__init__()
        out_features = out_features or in_features
        hidden_features = hidden_features or in_features
        self.mid_conv = mid_conv
        self.fc1 = nn.Conv2d(in_features, hidden_features, 1)
        self.act = act_layer()
        self.fc2 = nn.Conv2d(hidden_features, out_features, 1)
        self.drop = nn.Dropout(drop)
        self.apply(self._init_weights)

        if self.mid_conv:
            self.mid = nn.Conv2d(hidden_features, hidden_features, kernel_size=3, stride=1, padding=1,
                                 groups=hidden_features)
            self.mid_norm = nn.BatchNorm2d(hidden_features)

        self.norm1 = nn.BatchNorm2d(hidden_features)
        self.norm2 = nn.BatchNorm2d(out_features)

    def _init_weights(self, m):
        if isinstance(m, nn.Conv2d):
            trunc_normal_(m.weight, std=.02)
            if m.bias is not None:
                nn.init.constant_(m.bias, 0)

    def forward(self, x):
        x = self.fc1(x)     # 1x1卷积
        x = self.norm1(x)
        x = self.act(x)     # 激活层,GELU激活

        if self.mid_conv:
            x_mid = self.mid(x)     # 3x3卷积
            x_mid = self.mid_norm(x_mid)
            x = self.act(x_mid)
        x = self.drop(x)

        x = self.fc2(x)     # 1x1卷积
        x = self.norm2(x)

        x = self.drop(x)
        return x

3.2 MHSA Improvements

多头自注意力MHSA

class Attention4D(torch.nn.Module):
    def __init__(self, dim=384, key_dim=32, num_heads=8,
                 attn_ratio=4,
                 resolution=7,
                 act_layer=nn.ReLU,
                 stride=None):
        super().__init__()
        self.num_heads = num_heads
        self.scale = key_dim ** -0.5
        self.key_dim = key_dim
        self.nh_kd = nh_kd = key_dim * num_heads

        if stride is not None:
            self.resolution = math.ceil(resolution / stride)
            self.stride_conv = nn.Sequential(nn.Conv2d(dim, dim, kernel_size=3, stride=stride, padding=1, groups=dim),
                                             nn.BatchNorm2d(dim), )
            self.upsample = nn.Upsample(scale_factor=stride, mode='bilinear')
        else:
            self.resolution = resolution
            self.stride_conv = None
            self.upsample = None

        self.N = self.resolution ** 2
        self.N2 = self.N
        self.d = int(attn_ratio * key_dim)
        self.dh = int(attn_ratio * key_dim) * num_heads
        self.attn_ratio = attn_ratio
        h = self.dh + nh_kd * 2
        self.q = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.key_dim, 1),
                               nn.BatchNorm2d(self.num_heads * self.key_dim), )
        self.k = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.key_dim, 1),
                               nn.BatchNorm2d(self.num_heads * self.key_dim), )
        self.v = nn.Sequential(nn.Conv2d(dim, self.num_heads * self.d, 1),
                               nn.BatchNorm2d(self.num_heads * self.d),
                               )
        self.v_local = nn.Sequential(nn.Conv2d(self.num_heads * self.d, self.num_heads * self.d,
                                               kernel_size=3, stride=1, padding=1, groups=self.num_heads * self.d),
                                     nn.BatchNorm2d(self.num_heads * self.d), )
        self.talking_head1 = nn.Conv2d(self.num_heads, self.num_heads, kernel_size=1, stride=1, padding=0)
        self.talking_head2 = nn.Conv2d(self.num_heads, self.num_heads, kernel_size=1, stride=1, padding=0)

        self.proj = nn.Sequential(act_layer(),
                                  nn.Conv2d(self.dh, dim, 1),
                                  nn.BatchNorm2d(dim), )

        points = list(itertools.product(range(self.resolution), range(self.resolution)))
        N = len(points)
        attention_offsets = {}
        idxs = []
        for p1 in points:
            for p2 in points:
                offset = (abs(p1[0] - p2[0]), abs(p1[1] - p2[1]))
                if offset not in attention_offsets:
                    attention_offsets[offset] = len(attention_offsets)
                idxs.append(attention_offsets[offset])
        self.attention_biases = torch.nn.Parameter(
            torch.zeros(num_heads, len(attention_offsets)))
        self.register_buffer('attention_bias_idxs',
                             torch.LongTensor(idxs).view(N, N))

    @torch.no_grad()
    def train(self, mode=True):
        super().train(mode)
        if mode and hasattr(self, 'ab'):
            del self.ab
        else:
            self.ab = self.attention_biases[:, self.attention_bias_idxs]

    def forward(self, x):  # x (B,N,C)
        B, C, H, W = x.shape
        if self.stride_conv is not None:
            x = self.stride_conv(x)

        q = self.q(x).flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 3, 2)     # 降维为2维,转置
        k = self.k(x).flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 2, 3)
        v = self.v(x)
        v_local = self.v_local(v)       # 通过一个3x3的卷积将局部信息注入V中
        v = v.flatten(2).reshape(B, self.num_heads, -1, self.N).permute(0, 1, 3, 2)     # 降维为2维,转置

        attn = (
                (q @ k) * self.scale    # 矩阵乘法,计算相似度
                +
                (self.attention_biases[:, self.attention_bias_idxs]     # 融合一个位置编码
                 if self.training else self.ab)
        )
        # attn = (q @ k) * self.scale
        attn = self.talking_head1(attn)     # 1x1卷积->全连接,实现注意力头部之间的通信
        attn = attn.softmax(dim=-1)
        attn = self.talking_head2(attn)     # 同上

        x = (attn @ v)      # 注意力融合

        out = x.transpose(2, 3).reshape(B, self.dh, self.resolution, self.resolution) + v_local     # 最后再与v_local融合
        if self.upsample is not None:
            out = self.upsample(out)

        out = self.proj(out)    # 输出再进行激活 + 卷积 + 正则
        return out

Local Global模块(AttnFFN) 

class AttnFFN(nn.Module):
    def __init__(self, dim, mlp_ratio=4.,
                 act_layer=nn.ReLU, norm_layer=nn.LayerNorm,
                 drop=0., drop_path=0.,
                 use_layer_scale=True, layer_scale_init_value=1e-5,
                 resolution=7, stride=None):

        super().__init__()

        self.token_mixer = Attention4D(dim, resolution=resolution, act_layer=act_layer, stride=stride)      # MHSA多头自注意力
        mlp_hidden_dim = int(dim * mlp_ratio)       # 隐藏层维度
        self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim,
                       act_layer=act_layer, drop=drop, mid_conv=True)       # 深度卷积

        self.drop_path = DropPath(drop_path) if drop_path > 0. \
            else nn.Identity()      # drop_path概率
        self.use_layer_scale = use_layer_scale
        if use_layer_scale:     # 缩放因子
            self.layer_scale_1 = nn.Parameter(
                layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)
            self.layer_scale_2 = nn.Parameter(
                layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)

    def forward(self, x):
        if self.use_layer_scale:
            x = x + self.drop_path(self.layer_scale_1 * self.token_mixer(x))    # 多头自注意力 + 残差连接
            x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))    # mlp深度卷积 + 残差连接

        else:
            x = x + self.drop_path(self.token_mixer(x))
            x = x + self.drop_path(self.mlp(x))
        return x


# 深度卷积前馈网络
class FFN(nn.Module):
    def __init__(self, dim, pool_size=3, mlp_ratio=4.,
                 act_layer=nn.GELU,
                 drop=0., drop_path=0.,
                 use_layer_scale=True, layer_scale_init_value=1e-5):
        super().__init__()

        mlp_hidden_dim = int(dim * mlp_ratio)       # 隐藏层维度
        self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim,
                       act_layer=act_layer, drop=drop, mid_conv=True)       # 多层感知机

        self.drop_path = DropPath(drop_path) if drop_path > 0. \
            else nn.Identity()
        self.use_layer_scale = use_layer_scale
        if use_layer_scale:
            self.layer_scale_2 = nn.Parameter(
                layer_scale_init_value * torch.ones(dim).unsqueeze(-1).unsqueeze(-1), requires_grad=True)   # 缩放因子

    def forward(self, x):
        if self.use_layer_scale:
            x = x + self.drop_path(self.layer_scale_2 * self.mlp(x))    # 残差连接
        else:
            x = x + self.drop_path(self.mlp(x))
        return x

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机组成原理-存储器概念

计算机组成原理-存储器 存储系统的基本概念 1.层次结构 可以直接被CPU读取: 高速缓存:cache主存储器: 主存和内存 辅助存储器: 辅存和外存 2.分类 1.按层次结构划分 如上面所示 2.按存储介质 半导体存储器磁表面存储器光存储器 3.按信息可更改性 r/w存储器ROM(只读存储器) 4…

Vue2 + Echarts实现3D地图下钻

一、npm安装组件&#xff1a; "echarts": "5.4.0","echarts-gl": "^2.0.9","element-china-area-data": "^5.0.2", 二、Vue页面 <template><div class"Map3D" id"Map3D" ref"…

Python基础入门例程9-NP9 十六进制数字的大小

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 示例1 解答&#xff1a; 说明&#xff1a; 描述 计算的世界&#xff0c;除了二进制与十进制&#xff0c;使用最多的就是十六进制了&#xff0c;现在使用input读入一个十六进制的数字&#xff0c;输出它的十进制数字…

30 个常用的 Linux 命令!

作者&#xff1a;JackTian 来源&#xff1a;公众号「杰哥的IT之旅」 ID&#xff1a;Jake_Internet 链接&#xff1a;30 个常用的 Linux 命令&#xff01; 命令 1&#xff1a;last用于显示用户最近登录信息&#xff0c;包括用户名、登录时间、登录来源等信息 单独执行last命令&…

开发直播带货APP:用户体验设计策略

在当今数字化时代&#xff0c;直播带货APP已经成为了电子商务领域的一股重要力量。这种形式的电子商务结合了实时直播和购物&#xff0c;吸引了数百万用户。然而&#xff0c;为了确保直播带货APP的成功&#xff0c;关键在于提供出色的用户体验。本文将探讨开发直播带货APP的用户…

iframe嵌入报表滚动条问题

当在iframe中嵌入报表时&#xff0c;可能会遇到滚动条的问题。下面是一个详细的介绍 1. 了解iframe&#xff1a; - iframe是HTML中的元素&#xff0c;用于在当前页面中嵌入另一个页面。 - 嵌入报表时常使用iframe&#xff0c;以便将报表以独立的方式展示&#xff0c;并与其他页…

MT4教程新手指南:一步步开启你的金融交易之旅!

本文将为您详细介绍如何使用MT4(MetaTrader 4)平台进行金融交易。MT4是全球最受欢迎的在线交易平台之一&#xff0c;它拥有强大的功能&#xff0c;包括图表分析工具、交易执行、订单管理等&#xff0c;可以帮助你更好地理解和参与金融市场。那么&#xff0c;让我们开始吧! **步…

基于正余弦算法的无人机航迹规划-附代码

基于正余弦算法的无人机航迹规划 文章目录 基于正余弦算法的无人机航迹规划1.正余弦搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用正余弦算法来优化无人机航迹规划。 1.正余弦…

31一维信号滤波(限幅滤波、中值滤波、均值滤波、递推平均滤波),MATLAB程序已调通,可直接运行。

一维信号滤波&#xff08;限幅滤波、中值滤波、均值滤波、递推平均滤波&#xff09;&#xff0c;MATLAB程序已调通&#xff0c;可直接运行。 31matlab、中值滤波、信号处理 (xiaohongshu.com)

structs2 重构成SpringBoot架构

# 目录 structs2 重构成SpringBoot架构 1.1 structs2架构&#xff1a; 1.2 springboot 架构 1.3 演化要点&#xff1a; 1.基于前端的展示层不需要修改 2.HttpServlet 将会有SpringBoot annotation 来处理 3.构建前置的Structs url 转发器&#xff0c;适配 4.ActionSupport将由…

9篇论文速览股票预测高分经典方案

作为一直以来的烫门&#xff0c;股票预测因其非线性、高度波动性和复杂性等原因&#xff0c;成为了金融量化领域的一大难题。以往的解决方案主要围绕机器学习展开&#xff0c;如今&#xff0c;基于深度学习的股票预测方法有了许多新的突破。 为了帮助大家更深入地了解股票预测…

防止消息丢失与消息重复——Kafka可靠性分析及优化实践

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 防止消息丢失与消息重复——Kafka可…

ToDesk等远程软件连接主机无法更改分辨率 - 解决方案

问题 使用ToDesk等远程软件连接自己的Linux或Windows主机时&#xff0c;若主机已连接显示器&#xff0c;则可通过系统设置更改显示分辨率。但如果主机没有连接显示器或显示器的电源关闭&#xff0c;则无法正常调整分辨率。下文介绍解决方案。 解决方案 方案1&#xff1a;连接…

多跳推理真的可解释吗?10.24

多跳推理真的可解释吗 摘要1 引言2 相关工作2.1 多跳推理2.2 基于规则的推理2.3 可解释性评估 3 基础知识4 基准测试4.1 数据集构建4.2 评估框架4.3 近似可解释性评分4.4 Benchmark with Manual Annotation4.5 使用挖掘规则的基准 实验 摘要 近年来&#xff0c;多跳推理在获取…

BUUCTF wireshark 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 黑客通过wireshark抓到管理员登陆网站的一段流量包&#xff08;管理员的密码即是答案) 密文&#xff1a; 下载附件&#xff0c;解压后得到一个.pcap文件。 解题思路&#xff1a; 1、双击文件&#xff0c;在wires…

测试用例的设计方法(全):边界值分析方法

一.方法简介 1.定义&#xff1a;边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充&#xff0c;这种情况下&#xff0c;其测试用例来自等价类的边界。 2.与等价划分的区别 1)边界值分析不是从某等价类中随便挑…

Mysql主从集群同步延迟问题怎么解决

主从复制 复制过程分为几个步骤&#xff1a; 主库的更新事件(update、insert、delete)被写到binlog 从库发起连接&#xff0c;连接到主库。 此时主库创建一个 binlog dump thread&#xff0c;把 binlog 的内容发送到从库。 从库启动之后&#xff0c;创建一个 I/O 线程&#xff…

用VLOOKUP快速合并两个表格

一、前言 上周五微信收到运营提过来的需求&#xff0c;第一句话&#xff1a;帮我提取一下1号门店的库存数据&#xff0c;马上登录系统下载一份库存数据给到他然后专心读代码&#xff0c;过一会微信第二句话&#xff1a;帮我提取一下1号门店商品半年/一年的销量数据&#xff0c…

常用linux命令 linux_cmd_sheet

查看文件大小 ls -al 显示每个文件的kb大小 查看系统日志 dmesg -T | tail 在 top 命令中&#xff0c;RES 和 VIRT&#xff08;或者 total-vm&#xff09;是用来表示进程内存使用的两个不同指标&#xff0c;它们之间有以下区别&#xff1a; RES&#xff08;Resident Set Size…

使用ruoyi框架遇到的问题修改记录

使用ruoyi框架遇到的问题修改记录 文章目录 使用ruoyi框架遇到的问题修改记录上传后文件名改变上传时设置单多文件及其他选项附件显示文件名&#xff0c;点击下载附件直接显示图片表格固定列查询数据库作为下拉选项值字典使用加入json递归注解&#xff0c;防止无限递归内存溢出…