EfficientFormer 系列算法

1. EfficientFormer V1 模型

论文地址:https://proceedings.neurips.cc/paper_files/paper/2022/file/5452ad8ee6ea6e7dc41db1cbd31ba0b8-Paper-Conference.pdf

        EfficientFormer V1 基于 ViT 的模型中使用的网络架构和具体的算子,找到端侧低效的原因。然后引入了维度一致的 Transformer Block 作为设计范式。最后,通过网络模型搜索获得不同系列的模型 —— EfficientFormer。

        大多数现有方法通过从服务器 GPU 获得的计算复杂性(MAC)或吞吐量(图像/秒)来优化 Transformer 的推理速度。但是这些指标不能反映实际的设备延迟。为了清楚地了解哪些操作和设计选择会减慢边缘设备上 VIT 的推断,在下图中作者作者对不同模型在端侧运行进行了一些分析,主要是分为 ViT 对图像进行分块的 Patch Embedding、Transformer 中的 Attention 和 MLP,另外还有 LeViT 提出的 Reshape 和一些激活等。提出了下面几个猜想。

观察 1:在移动设备上,具有大核和步长的 patch 嵌入是一个速度瓶颈。

        patch 嵌入通常使用一个不重叠的卷积层来实现,该层具有较大的内核大小和步长。一种普遍的看法是,Transformer 网络中 patch 嵌入层的计算成本不显著或可以忽略不计。然而,在上图中比较了具有大核和大步长的 patch 嵌入模型,即 DeiT-S 和 PoolFormer-s24,以及没有它的模型,即 LeViT-256 和 EfficientFormer,结果表明,patch 嵌入反而是移动设备上的速度瓶颈。

        大多数编译器都不支持大型内核卷积,并且无法通过 Winograd 等现有算法来加速。或者,非重叠 patch 嵌入可以由一个具有快速下采样的卷积 stem 代替,该卷积 stem 由几个硬件效率高的 3×3 卷积组成。

#stem 以一些普通卷积组成
def stem(in_chs, out_chs):return nn.Sequential(nn.Conv2d(in_chs, out_chs // 2, kernel_size=3, stride=2, padding=1),nn.BatchNorm2d(out_chs // 2),nn.ReLU(),nn.Conv2d(out_chs // 2, out_chs, kernel_size=3, stride=2, padding=1),nn.BatchNorm2d(out_chs),nn.ReLU(), )

观察 2:一致的特征维度对于选择 token 混合器很重要。MHSA 不一定是速度瓶颈。

        最近的工作将基于 ViT 的模型扩展到 MetaFormer,该架构由 MLP 块和未指定的 token 混合器组成。在构建基于 ViT 的模型时,选择 token 混合器是一个重要的设计选择。token 混合器的选择有很多:传统的 MHSA 混合器,它们具有全局感受野;更复杂的移位窗口注意;或者像池化这样的非参数化操作符。

        作者将比较范围缩小到两种 token 混合器,池化和 MHSA,其中选择前者是为了简单和高效,而后者是为了更好的性能。大多数公开的移动编译器目前不支持更复杂的 token 混合器,如移位窗口,因此作者将其排除在范围之外。此外,由于本文专注于在没有轻量级卷积帮助的情况下构建体系结构,因此没有使用深度卷积来取代池化。

        为了解两个 token 混合器的延迟,作者执行以下两个比较:

        首先,通过比较 PoolFormer-s24 和 LeViT-256,作者发现 reshape 操作是 LeViT-256 的一个瓶颈。LeViT-256 的大部分是用 CONV on 4D tensor 实现的,在特征转发到 MHSA 时需要频繁的 reshape 操作,因为 MHSA 必须在 3D tensor 上进行注意(丢弃注意力头的额外尺寸)。reshape 的广泛使用限制了 LeViT 在移动设备上的速度。另一方面,当网络主要由基于 CONV 的实现组成时,池化自然适合 4D 张量,例如,CONV 1×1 作为 MLP 实现,CONV stem 用于下采样。因此,PoolFormer 具有更快的推理速度。 其次,通过比较 DeiT-S 和 LeViT-256,作者发现,如果特征尺寸一致且不需要 reshape,MHSA 不会给手机带来显著的开销。虽然计算量更大,但具有一致 3D 特征的 DeiT-S 可以达到与新 ViT 变体(即 LeViT-256)相当的速度。

观察 3:CONV-BN 比 LN-Linear 更适合延迟,准确性缺陷通常是可以接受的。

        选择 MLP 实现是另一个重要的设计选择。通常,会选择两个选项之一:带 3D 线性投影(proj)的 LayerNorm(LN)和带 BatchNorm(BN)的 CONV1×1。CONV-BN 更适合低延迟,因为 BN 可以折叠到之前的卷积用于推理加速,而 LN 仍在推理阶段收集运行统计信息,从而导致延迟。根据本文的实验结果和之前的工作,LN 引入的延迟约占整个网络延迟的 10%-20%。

观察 4:非线性延迟取决于硬件和编译器。

        最后,作者研究了非线性,包括 GeLU、ReLU 和 HardSwish。之前的工作表明 GeLU 在硬件上效率不高,会减慢推理速度。然而,作者观察到,GeLU 受到 iPhone 12 的良好支持,几乎不比它的对手 ReLU 慢。

        相反,在本文的实验中,HardSwish 的速度惊人地慢,编译器可能无法很好地支持它(LeViT-256 使用 HardSwish 的延迟为 44.5 ms,而使用 GeLU 的延迟为 11.9 ms)。作者的结论是,考虑到手头的特定硬件和编译器,非线性应该根据具体情况来确定。

1.1 EfficientFormer 结构

        基于延迟分析,作者提出了 EfficientFormer 的设计,如上图所示。该网络由 patch 嵌入(PatchEmbed)和 meta transformer 块堆栈组成,表示为 MB:

        其中 𝑋0 是 Batch 大小为 B、空间大小为 [𝐻,𝑊] 的输入图像,y 是所需输出,m 是块的总数(深度)。MB 由未指定的 token 混合器(TokenMixer)和一个 MLP 块组成,可以表示为:

        其中,X_{i|i>0} 是输入到第 𝑖 个 𝑀𝐵 的中间特征。作者进一步将 Stage(或 S)定义为多个 MetaBlocks 的堆栈,这些 MetaBlocks 处理具有相同空间大小的特征,如上图中的 𝑁1× 表示 𝑆1 具有 𝑁1 个 MetaBlocks。该网络包括 4 个阶段。在每个阶段中,都有一个嵌入操作来投影嵌入维度和下采样 token 长度,如上图所示。在上述架构中,EfficientFormer 是一个完全基于 Transformer 的模型,无需集成 MobileNet 结构。接下来,作者深入研究了网络设计的细节。 

1.2 Dimension-consistent Design

        作者提出了一种维度一致性设计,该设计将网络分割为 4D 分区,其中操作符以卷积网络样式实现(MB4D),以及一个 3D 分区,其中线性投影和注意力在 3D 张量上执行,以在不牺牲效率的情况下享受 MHSA 的全局建模能力(MB3D),如上图所示。具体来说,网络从 4D 分区开始,而 3D 分区应用于最后阶段。注意,上图只是一个实例,4D 和 3D 分区的实际长度稍后通过架构搜索指定。

        首先,输入图像由一个具有两个步长为 2,感受野为 3×3 卷积的 Conv stem 处理:

        然后,网络从 MB4D 开始,使用一个简单的池化混合器来提取低级特征:

        注意,这里作者没有在池化混合器之前使用 LN,因为 4D 分区是基于 CONV-BN 的设计,因此每个池化混合器前面都有一个 BN。

        在处理完所有 MB4D 块后,作者执行一次 reshape 以变换特征大小并进入 3D 分区。MB3D 遵循传统 ViT 结构,如上图所示:

#以 1x1 卷积为主的 MLP
class Mlp(nn.Module):def __init__(self, in_features, hidden_features=None,out_features=None, act_layer=nn.GELU, drop=0.):super().__init__()out_features = out_features or in_featureshidden_features = hidden_features or in_featuresself.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)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)x = self.norm1(x)x = self.act(x)x = self.drop(x)x = self.fc2(x)x = self.norm2(x)x = self.drop(x)return class Meta4D(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__()self.token_mixer = Pooling(pool_size=pool_size)mlp_hidden_dim = int(dim * mlp_ratio)#MLP 层self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim,act_layer=act_layer, drop=drop)#drop_path 目的是在一个 batch 里面随机去除一部分样本,起正则化作用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_1 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)self.layer_scale_2 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)def forward(self, x):if self.use_layer_scale:x = x + self.drop_path(self.layer_scale_1.unsqueeze(-1).unsqueeze(-1)* self.token_mixer(x))x = x + self.drop_path(self.layer_scale_2.unsqueeze(-1).unsqueeze(-1)* self.mlp(x))else:x = x + self.drop_path(self.token_mixer(x))x = x + self.drop_path(self.mlp(x))return x

1.3 Latency Driven Slimming

        基于维度一致性设计,作者构建了一个超网,用于搜索上图所示网络架构的有效模型(上图显示了搜索的最终网络的示例)。为了表示这样的超网,作者定义了元路径(MetaPath,MP),它是可能的块的集合:

        其中 𝐼 表示 identity path,j 表示第 𝑗 阶段,i 表示第 𝑖 个块。

        在超网的 𝑆1 和 𝑆2 中,每个块可以从 MB4D 或 I 中选择,而在 𝑆3 和 𝑆4 中,块可以是 MB3D、MB4D 或 I。出于两个原因,作者仅在最后两个阶段启用 MB3D:首先,由于 MHSA 的计算相对于 token 长度呈二次增长,因此在早期阶段对其进行集成将大大增加计算成本。其次,将全局 MHSA 应用于最后阶段符合这样一种直觉,即网络的早期阶段捕获低级特征,而后期阶段学习长期依赖性。

#Meta3D 与 Meta4D 在 MLP 有不同,Meta3D 使用如下的 LinearMLP,主要以线性层为主
class LinearMlp(nn.Module):""" MLP as used in Vision Transformer, MLP-Mixer and related networks""" def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):super().__init__()out_features = out_features or in_featureshidden_features = hidden_features or in_featuresself.fc1 = nn.Linear(in_features, hidden_features)self.act = act_layer()self.drop1 = nn.Dropout(drop)self.fc2 = nn.Linear(hidden_features, out_features)self.drop2 = nn.Dropout(drop)def forward(self, x):x = self.fc1(x)x = self.act(x)x = self.drop1(x)x = self.fc2(x)x = self.drop2(x)return xclass Meta3D(nn.Module):def __init__(self, dim, mlp_ratio=4.,act_layer=nn.GELU, norm_layer=nn.LayerNorm,drop=0., drop_path=0.,use_layer_scale=True, layer_scale_init_value=1e-5):super().__init__()self.norm1 = norm_layer(dim)self.token_mixer = Attention(dim)self.norm2 = norm_layer(dim)mlp_hidden_dim = int(dim * mlp_ratio)self.mlp = LinearMlp(in_features=dim, hidden_features=mlp_hidden_dim,act_layer=act_layer, drop=drop)self.drop_path = DropPath(drop_path) if drop_path > 0. \else nn.Identity()self.use_layer_scale = use_layer_scaleif use_layer_scale:self.layer_scale_1 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)self.layer_scale_2 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)def forward(self, x):if self.use_layer_scale:x = x + self.drop_path(self.layer_scale_1.unsqueeze(0).unsqueeze(0)* self.token_mixer(self.norm1(x)))x = x + self.drop_path(self.layer_scale_2.unsqueeze(0).unsqueeze(0)* self.mlp(self.norm2(x)))else:x = x + self.drop_path(self.token_mixer(self.norm1(x)))x = x + self.drop_path(self.mlp(self.norm2(x)))return x

        以前的硬件感知网络搜索方法通常依赖于在搜索空间中部署每个候选对象的硬件来获得延迟,这非常耗时。在这项工作中,作者提出了一种简单、快速但有效的基于梯度的搜索算法,以获得只需训练一次超网的候选网络。该算法有三个主要步骤。

        首先,作者使用 Gumble Softmax 采样对超网进行训练,以获得每个 MP 内块的重要性得分,可以表示为:

        其中,α评估 MP 中每个块的重要性,因为它表示选择块的概率;\epsilon ~U(0,1);τ是温度;n 代表 MP 中的块体类型,即对于 𝑆1 和 𝑆2,n\in{4D,I};对于 S_{3}和 S_{4},。通过训练之后,以获得经过训练的权重和结构参数α。

        其次,作者通过收集不同宽度(16 的倍数)的 MB4D 和 MB3D 的设备上延迟来构建延迟查找表。

        最后,作者使用查找表在第一步通过延迟评估获得的超网上执行网络瘦身。典型的基于梯度的搜索算法仅选择α最大的块,这不符合本文的范围,因为它无法搜索宽度 𝐶𝑗。事实上,构造一个多宽度的超网需要消耗显存,甚至是不现实的,因为在本文的设计中,每个 MP 都有几个分支。作者不在复杂的搜索空间上直接搜索,而是在单宽度超网上执行逐步瘦身,如下所示。

        作者首先将 S_{1,2}和 S_{3,4}的 𝑀𝑃𝑖 重要性得分分别定义为和 𝛼𝑖4𝐷𝛼𝑖𝐼 和 𝛼𝑖3𝐷+𝛼𝑖4𝐷𝛼𝑖𝐼。同样,每个阶段的重要性得分可以通过将该阶段内所有 MP 的得分相加得到。根据重要性得分,作者定义了包含三个选项的动作空间:1)选择 I 作为最小导入 MP,2)删除第一个 MB3D,3)减少最不重要阶段的宽度(乘以 16)。然后,通过查找表计算每个动作的延迟,并评估每个动作的准确度下降。最后,根据每延迟准确度下降 (−𝑚𝑠) 来选择操作。此过程将迭代执行,直到达到目标延迟。

        EfficientFormer 一共有 4 个阶段。每个阶段都有一个 Embeding(两个 3x3 的 Conv 组成一个 Embeding)来投影 Token 长度(可以理解为 CNN 中的 feature map)。EfficientFormer 是一个完全基于 Transformer 设计的模型,并没有集成 MobileNet 相关内容。最后通过 AUTOML 来搜索 MB_3D 和 MB_4D block 相关参数。最后堆叠 block 形成最终网络。

2. EfficientFormer V2 模型

论文地址:https://openaccess.thecvf.com/content/ICCV2023/papers/Li_Rethinking_Vision_Transformers_for_MobileNet_Size_and_Speed_ICCV_2023_paper.pdf

        论文重新审视了 ViT 的设计选择,并提出了一种具有低延迟和高参数效率的改进型超网络。论文进一步引入了一种细粒度联合搜索策略,该策略可以通过同时优化延迟和参数量来找到有效的架构。所提出的模型 EfficientFormerV2 在 ImageNet-1K 上实现了比 MobileNetV2 和 MobileNetV1 高约 4%的 top-1 精度,具有相似的延迟和参数。

        EfficientFormerV2 相对于 EfficientFormer 的主要改进如下图所示:

        结合局部信息可以提高性能,并使 ViT 在缺少显式位置嵌入的情况下更加鲁棒。PoolFormer 和 EfficientFormer 使用 3×3 平均池化层(作为 local token 混合器。用相同内核大小的 depth-wise 卷积)替换这些层不会引入耗时开销,而使用可忽略的额外参数(0.02M),性能提高了 0.6%。此外。在 ViT 中的前馈网络(FFN)中注入局部信息建模层也有利于以较小的开销提高性能。值得注意的是,通过在 FFN 中放置额外的 depth-wise 3×3 卷积来捕获局部信息,复制了原始局部混合器(池或卷积)的功能。基于这些观察,论文移除了显式残差连接的 local token 混合器,并将 depth-wise 3×3 CONV 移动到 FFN 中,以获得 locality enabled 的统一 FFN(上图(b))。论文将统一的 FFN 应用于网络的所有阶段,如上图(a,b)所示。这种设计修改将网络架构简化为仅两种类型的 block(local FFN 和 global attention),并在相同的耗时(见表 1)下将精度提高到 80.3%,参数开销较小(0.1M)。更重要的是,该修改允许直接使用模块的确切数量搜索网络深度,以提取局部和全局信息,尤其是在网络的后期阶段。

2.1 搜索空间优化 

        通过统一的 FFN 和删除残差连接的 token mixer,V2 检查来自 EfficientFormer 的搜索空间是否仍然足够,特别是在深度方面。论文改变了网络深度(每个阶段中的 block 数)和宽度(通道数),并发现更深和更窄的网络会带来更好的精度(0.2%的改进)、更少的参数(0.3M 的减少)和更少的耗时(0.1ms 的加速),如上表所示。因此,论文将此网络设置为新的基线(精度 80.5%),以验证后续的设计修改,并为架构搜索提供更深入的超网络。

        此外,具有进一步缩小的空间分辨率(1/64)的 5 阶段模型已广泛用于有效的 ViT 工作。为了证明是否应该从一个 5 阶段超网络中搜索,论文在当前的基线网络中添加了一个额外的阶段,并验证了性能增益和开销。值得注意的是,尽管考虑到小的特征分辨率,计算开销不是一个问题,但附加阶段是参数密集型的。因此需要缩小网络维度(深度或宽度),以将参数和延迟与基线模型对齐,以便进行公平比较。如上表所示,尽管节省了 MACs(0.12G),但 5 阶段模型的最佳性能在更多参数(0.39M)和延迟开销(0.2ms)的情况下意外降至 80.31%。这符合直觉,即五阶段计算效率高,但参数密集。鉴于 5 阶段网络无法在现有的规模和速度范围内引入更多潜力,论文坚持 4 阶段设计。这一分析也解释了为什么某些 ViT 在 MACs 精度方面提供了出色的 Pareto curve,但在大小上往往非常冗余。作为最重要的一点,优化单一度量很容易陷入困境。

# 深度卷积前馈网络(局部模块)
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_scaleif 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

2.2 多头注意力改进

        论文研究了在不增加模型大小和耗时的额外开销的情况下提高注意力模块性能的技术。如图(c)所示,论文研究了 MHSA 的两种方法。首先通过添加 depth-wise 3×3 CONV 将局部信息注入到 Value 矩阵(V)中,也采用了这种方法。其次通过在 head 维度上添加全连接层来实现注意力头之间的通信,如图 2(c)所示。通过这些修改,进一步将性能提高到 80.8%,与基线模型相比,具有相似的参数和延迟。

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_headsself.scale = key_dim ** -0.5self.key_dim = key_dimself.nh_kd = nh_kd = key_dim * num_headsif 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 = resolutionself.stride_conv = Noneself.upsample = Noneself.N = self.resolution ** 2self.N2 = self.Nself.d = int(attn_ratio * key_dim)self.dh = int(attn_ratio * key_dim) * num_headsself.attn_ratio = attn_ratioh = self.dh + nh_kd * 2self.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.abelse:self.ab = self.attention_biases[:, self.attention_bias_idxs]def forward(self, x):  # x (B,N,C)B, C, H, W = x.shapeif 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.scaleattn = 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
#AttnFFN(Local Global 模块)
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_scaleif 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

2.3 更高分辨率注意力

        注意机制有利于性能。然而,将其应用于高分辨率特征会损害部署效率,因为它具有与空间分辨率相对应的二次时间复杂度。论文研究了将 MHSA 有效应用于更高分辨率(早期阶段)的策略。回想一下,在当前基线网络中,MHSA 仅在输入图像空间分辨率为 1/32 的最后阶段使用。论文将额外的 MHSA 应用于具有 1/16 特征大小的倒数第二个阶段,并观察到准确度提高了 0.9%。另一方面,推理速度减慢了几乎 2.7 倍。因此,有必要适当降低注意力模块的复杂性。

        尽管一些工作提出了基于窗口的注意力,或下采样 Key 和 Value 来缓解这个问题,但论文发现它们不是最适合移动部署的选项。由于复杂的窗口划分和重新排序,基于窗口的注意力很难在移动设备上加速。对于[40]中的下采样 Key(K)和 Value(V),需要全分辨率查询(Q)来保持注意力矩阵乘法后的输出分辨率(Out):

        根据测试该模型的耗时仅下降到 2.8ms,仍然比基线模型慢 2 倍。因此,为了在网络的早期阶段执行 MHSA,论文将所有 Query、Key 和 Value 降采样到固定的空间分辨率(1/32),并将注意力的输出插值回原始分辨率,以馈送到下一层,如图所示((d)和(e))。我们称这种方法为“Stride Attention”。如表所示,这种简单的近似值将延迟从 3.5ms 显著降低到 1.5ms,并保持了具有竞争力的准确性(81.5%对 81.7%)。

class Attention4DDownsample(torch.nn.Module):def __init__(self, dim=384, key_dim=16, num_heads=8,attn_ratio=4,resolution=7,out_dim=None,act_layer=None,):super().__init__()self.num_heads = num_headsself.scale = key_dim ** -0.5self.key_dim = key_dimself.nh_kd = nh_kd = key_dim * num_headsself.resolution = resolutionself.d = int(attn_ratio * key_dim)self.dh = int(attn_ratio * key_dim) * num_headsself.attn_ratio = attn_ratioh = self.dh + nh_kd * 2if out_dim is not None:self.out_dim = out_dimelse:self.out_dim = dimself.resolution2 = math.ceil(self.resolution / 2)self.q = LGQuery(dim, self.num_heads * self.key_dim, self.resolution, self.resolution2)         # 双注意力下采样self.N = self.resolution ** 2self.N2 = self.resolution2 ** 2self.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=2, padding=1, groups=self.num_heads * self.d),nn.BatchNorm2d(self.num_heads * self.d), )self.proj = nn.Sequential(act_layer(),nn.Conv2d(self.dh, self.out_dim, 1),nn.BatchNorm2d(self.out_dim), )points = list(itertools.product(range(self.resolution), range(self.resolution)))points_ = list(itertools.product(range(self.resolution2), range(self.resolution2)))N = len(points)N_ = len(points_)attention_offsets = {}idxs = []for p1 in points_:for p2 in points:size = 1offset = (abs(p1[0] * math.ceil(self.resolution / self.resolution2) - p2[0] + (size - 1) / 2),abs(p1[1] * math.ceil(self.resolution / self.resolution2) - p2[1] + (size - 1) / 2))if offset not in attention_offsets:attention_offsets[offset] = len(attention_offsets)idxs.append(attention_offsets[offset])self.register_buffer('attention_biases', torch.zeros(num_heads, 196))self.register_buffer('attention_bias_idxs',torch.ones(49, 196).long())self.attention_biases_seg = torch.nn.Parameter(torch.zeros(num_heads, len(attention_offsets)))self.register_buffer('attention_bias_idxs_seg',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.abelse:self.ab = self.attention_biases_seg[:, self.attention_bias_idxs_seg]def forward(self, x):  # x (B,N,C)B, C, H, W = x.shapeq = self.q(x).flatten(2).reshape(B, self.num_heads, -1, H * W // 4).permute(0, 1, 3, 2)k = self.k(x).flatten(2).reshape(B, self.num_heads, -1, H * W).permute(0, 1, 2, 3)v = self.v(x)v_local = self.v_local(v)v = v.flatten(2).reshape(B, self.num_heads, -1, H * W).permute(0, 1, 3, 2)attn = (q @ k) * self.scale                     # (H * W // 4, H * W)bias = self.attention_biases_seg[:, self.attention_bias_idxs_seg] if self.training else self.abbias = torch.nn.functional.interpolate(bias.unsqueeze(0), size=(attn.size(-2), attn.size(-1)), mode='bicubic')attn = attn + biasattn = attn.softmax(dim=-1)x = (attn @ v).transpose(2, 3)                  # (H * W // 4, H * W)out = x.reshape(B, self.dh, H // 2, W // 2) + v_localout = self.proj(out)return out

2.4 注意力降采样

大多数视觉主干利用跨步卷积或池化层来执行静态和局部下采样,并形成分层结构。最近的一些研究开始探索注意力下采样。例如,LeViT 和 UniNet 建议通过注意力机制将特征分辨率减半,以实现全局感受野的上下文感知下采样。具体而言,Query 中的 token 数量减少一半,以便对注意力模块的输出进行下采样:

        然而,决定如何减少 Query 中的 token 数量是非常重要的。Graham 等人根据经验使用池化对 Query 进行下采样,而 Liu 等人建议搜索局部或全局方法。为了在移动设备上实现可接受的推理速度,将注意力下采样应用于具有高分辨率的早期阶段是不利的,这限制了以更高分辨率搜索不同下采样方法的现有工作的价值。

        相反,论文提出了一种同时使用局部性和全局依赖性的组合策略,如图(f)所示。为了获得下采样的 Query,论文使用池化层作为静态局部下采样,使用 3×3 DWCONV 作为可学习的局部下采样。此外,注意力下采样模块残差连接到 regular strided CONV,以形成 local-global 方式,类似于下采样 bottlenecks 或 inverted bottlenecks。如表所示,通过略微增加参数和耗时开销,论文进一步将注意力下采样的准确率提高到 81.8%。

class LGQuery(torch.nn.Module):def __init__(self, in_dim, out_dim, resolution1, resolution2):super().__init__()self.resolution1 = resolution1self.resolution2 = resolution2self.pool = nn.AvgPool2d(1, 2, 0)self.local = nn.Sequential(nn.Conv2d(in_dim, in_dim, kernel_size=3, stride=2, padding=1, groups=in_dim),)self.proj = nn.Sequential(nn.Conv2d(in_dim, out_dim, 1),nn.BatchNorm2d(out_dim), )def forward(self, x):B, C, H, W = x.shapelocal_q = self.local(x)pool_q = self.pool(x)q = local_q + pool_q            # 双路径下采q = self.proj(q)return q

        对于模型大小,EfficientFormerV 2-S0 比 EdgeViT-XXS 超出了 1.3%的 top-1 精度,甚至少了 0.6M 参数,比 MobileNetV 2 ×1.0 优于 3.5%的 top-1,参数数量相似。对于大模型,EfficientFormerV 2-L 模型实现了与最近的 EfficientFormerL 7 相同的精度,同时小 3.1 倍。在速度方面,在延迟相当或更低的情况下,EfficientFormerV2-S2 的性能分别优于 UniNet-B1,EdgeViT-S 和 EfficientFormerL 1,分别为 0.8%,0.6%和 2.4%。EiffcientFormer V2-S1 的效率分别比 MobileViT-XS、EdgeViT-XXS 和 EdgeViTXS 高出 4.2%、4.6%和 1.5%,其中 MES 要高得多。

3. 小结

  • EfficientFormerV1 证明了视觉的 Transformer 可以在移动设备上以 MobileNet 速度运行。经全面的延迟分析,在一系列基于 VIT 的架构中识别低效的运算符,指导新的设计范式。

  • EfficientFormerV2 进一步提出了在大小和速度上的细粒度联合搜索,并获得了轻量级和推理速度超快的模型。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高性能web服务器nginx

目录 nginx简介 服务端 I/O 流程 Nginx 进程结构 Nginx启动流程 nginx的源码编译下载 nginx命令常见参数 nginx的配置文件详解 全局配置优化 nginx的平滑升级和回滚 nginx目录匹配优先级测试(因为只支持访问文件,所有不比对匹配目录优先级&…

五、2 移位操作符赋值操作符

1、移位操作符 2、赋值操作符 “ ”赋值,“ ”判断是否相等 1)连续赋值 2)复合赋值符

C ++初阶:类和对象(上)

目录 🌞0.前言 1. 面向过程和面向对象初步认识 2..类的引入与定义 2.1类的引入 2.2类的定义 3.类的访问限定符及其封装 3.1访问限定符 3.2封装 4.类的作用域 4.1加餐和发现 5.类的实例化 6.类对象大小的计算 6.1.内部的存储方式 6.2结构体对齐规则回顾…

一、什么是 mvvm? MVC、MVP、MVVM三种模式的区别与详解

简介 MVC、MVP、MVVM都是常见的软件架构模式。 MVC(Model-View-Controller)架构模式中,将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller&…

STM32自制手持小风扇实验

1.1 介绍: 实验功能说明:功能(1)按一下按键小风扇开启,再按一下关闭。 功能(2)按一下按键小风扇一档风速,再按一下二挡,依次三挡…关闭。 按键模块说明:按下…

什么是AR、VR、MR、XR?

时代背景 近年来随着计算机图形学、显示技术等的发展,视觉虚拟化技术得到了广泛的发展,并且越来越普及化,慢慢的也走入人们的视野。目前市场上视觉虚拟化技术的主流分为这几种 VR、AR、MR、XR。这几项技术并不是最近才出现的,VR的…

路由器VLAN配置(H3C)

路由器VLAN配置(H3C) 控制页面访问 路由器默认处于192.168.1.1网段(可以短按reset重置),如果要直接使用需要设置静态IP处于同一网段; 对路由器进行配置也要将电脑IP手动设置为同一网段; 默…

执行rasa shell 遇到asyncio.exceptions.TimeoutError报错

在《树莓派3B运行rasa init和rasa shell遇到的tensorflow报错总结》一文中,我遇到的第7个报错是首次运行rasa shell时候碰到的。按照我在文中记录的解决方案,处理成功。 结果,今天我又一次遇到了asyncio - Task exception was never retrie…

91. 解码方法 -dp4

. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/decode-ways/description/ 示例 1: 输入:s &…

「字符串」前缀函数|KMP匹配:规范化next数组 / LeetCode 28(C++)

概述 为什么大家总觉得KMP难?难的根本就不是这个算法本身。 在互联网上你可以见到八十种KMP算法的next数组定义和模式串回滚策略,把一切都懂得特别混乱。很多时候初学者的难点根本不在于这个算法本身,而是它令人痛苦的百花齐放的定义。 有…

ee trade:黄金投资与股票投资的区别

黄金和股票, 是金融市场中两种常见的投资工具, 它们拥有截然不同的特点和风险, 了解它们的差异, 可以帮助投资者制定更合理的投资策略。 一、 投资性质: 避险与成长, 两种投资方向 黄金: 被视…

【C++】入门篇一

【C】入门篇一 一 .缺省参数1.缺省参数的概念2. 缺省参数分类 二. 函数重载1. 函数重载概念2.函数重载代码举例 三.引用1.引用的概念2. 引用特性3. 常引用4. 使用场景(1). 做参数(2). 做返回值 5. 传值、传引用效率比较6. 引用和指针的区别7.引用和指针的不同点 一 .缺省参数 …

如何将MySQL迁移到TiDB,完成无缝业务切换?

当 MySQL 数据库的单表数据量达到了亿级,会发生什么? 这个现象表示公司的业务上了一个台阶,随着数据量的增加,公司规模也进一步扩大了,是非常喜人的一个改变 ,然而随之而来的其他变化,就没那么…

Python | Leetcode Python题解之第354题俄罗斯套娃信封问题

题目: 题解: class Solution:def maxEnvelopes(self, envelopes: List[List[int]]) -> int:if not envelopes:return 0n len(envelopes)envelopes.sort(keylambda x: (x[0], -x[1]))f [1] * nfor i in range(n):for j in range(i):if envelopes[j]…

全液冷服务器革命:CPU、内存、PCIe高效散热新方案

在国家十四五规划大力发展数字经济的背景下,数据中心作为算力的核心载体,其基础设施成为支撑数字经济的“数字底座”,但同时也面临巨大的碳排放压力。随着芯片与服务器功耗的上升,单机柜功率密度不断增大,传统风冷散热…

深度学习设计模式之享元设计模式

文章目录 前言一、介绍二、特点三、详细介绍1.核心组成2.代码示例3.优缺点优点缺点 4.使用场景 总结 前言 享元设计模式主要用于减少创建对象的数量,以减少内存占用,提高性能。 一、介绍 享元设计模式(Flyweight Pattern)是一种…

Hexo通过GitHub设置自定义域名

本身GitHub也是支持自定义域名的,本次教程将讲解如何使用GitHub自带的自定义域名解析。 1. GitHub设置 1.1 登录GitHub账号 登录GitHub账号,找到名称为 用户名.github.io的仓库,并点击进入。 1.2 进入Settings页面 点击如图的Settings按…

【体检】程序人生之健康检查,全身体检与预防疫苗,五大传染病普筛,基因检测等

程序员养生指南之 【体检】程序人生之健康检查,全身体检项目分类,五大传染病普筛,基因检测等 文章目录 一、全身体检与预防疫苗(年检)1、实验室检测:生化全套检查2、医技检查:辅助诊疗科室3、科…

python中使用gurobi遇到强不等式约束(只有大于或者小于而不是大于等于或者小于等于的形式)的解决办法

文章目录 情况分析与解决思路数学模型严格不等式约束转化后的约束形式带入具体的 ϵ \epsilon ϵ 值 python代码总结 情况分析与解决思路 在gurobi求解数学优化问题时&#xff0c;标准的约束形式通常是大于等于&#xff08; >&#xff09;或小于等于&#xff08;<&…

Linux:网络基础概念

网络发展 独立模式: 计算机之间相互独立; 网络互联: 多台计算机连接在一起, 完成数据共享; 局域网 LAN: 计算机数量更多了, 通过交换机和路由器连接在一起; 广域网 WAN: 将远隔千里的计算机都连在一起; 所谓 "局域网" 和 "广域网" 只是一个相对的概念. 比如…