(二)ShuffleNet_v1----2017论文解读

ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices

ShuffleNet:一种用于移动设备的极其高效的卷积神经网络

Abstract

我们介绍了一种名为ShuffleNet的计算效率极高的CNN架构,该架构是专为计算能力非常有限(例如10-150 MFLOP)的移动设备设计的。新架构利用了两个新的操作,逐点组卷积和通道混洗,可以在保持准确性的同时大大降低计算成本。 ImageNet分类和MS COCO对象检测的实验证明了ShuffleNet优于其他结构的性能,例如在40个MFLOP的计算预算下,比最近的MobileNet [12]在ImageNet分类任务上的top-1错误要低(绝对7.8%)。在基于ARM的移动设备上,ShuffleNet的实际速度是AlexNet的13倍,同时保持了相当的准确性。

1 Introduction

建立更深,更大的卷积神经网络(CNN)是解决主要视觉识别任务的主要趋势[21,9,33,5,28,24]。最精确的CNN通常具有数百个层和数千个通道[9、34、32、40],因此需要数十亿个FLOP进行计算。本报告探讨了相反的极端情况:在非常有限的计算预算中以数十或数百个MFLOP追求最佳准确性,重点放在无人机,机器人和智能手机等常见的移动平台上。请注意,许多现有的作品[16,22,43,42,38,27]专注于修剪,压缩或代表“基本”网络体系结构的低位。在这里,我们旨在探索一种专为我们所需的计算范围而设计的高效基本架构。

我们注意到,由于代价高昂的密集1×1卷积,诸如Xception [3]和ResNeXt [40]之类的最新基础架构在极小的网络中的效率降低。我们建议使用逐点分组卷积来减少1×1卷积的计算复杂度。 为了克服群卷积带来的副作用,我们提出了一种新颖的通道随机操作,以帮助信息流过特征通道。基于这两种技术,我们构建了一种高效的体系结构,称为ShuffleNet。 与流行的结构[30,9,40]相比,在给定的计算复杂度预算的情况下,我们的ShuffleNet允许更多的特征映射通道,这有助于编码更多的信息,并且对于超小型网络的性能尤为关键。

我们根据具有挑战性的ImageNet分类[4,29]和MS COCO对象检测[23]任务评估我们的模型。一系列受控实验显示了我们设计原理的有效性以及优于其他结构的性能。与最先进的架构MobileNet [12]相比,ShuffleNet可以显着提高性能,例如。在40个MFLOP级别上,ImageNet top-1错误绝对降低了7.8%。

我们还检查了实际硬件(即基于ARM的现成计算核心)上的加速情况。 ShuffleNet模型比AlexNet [21]达到了约13倍的实际提速(理论提速为18倍),同时保持了相当的精度。

2 Related Work

在这里插入图片描述
​ 图1.具有两个堆叠的组卷积的通道混洗。 GConv代表组卷积。 a)具有相同组数的两个堆叠的卷积层。每个输出通道仅与组中的输入通道相关。 b)当GConv2在GConv1之后从不同组获取数据时,输入和输出通道完全相关; c)与b)使用通道混洗等效的实现。

高效的模型设计最近几年,深层神经网络在计算机视觉任务中取得了成功[21、36、28],其中模型设计发挥了重要作用。在嵌入式设备上运行高质量深度神经网络的需求不断增长,鼓励了对有效模型设计的研究[8]。例如,与简单堆叠卷积层相比,GoogLeNet [33]以较低的复杂性增加了网络的深度。 SqueezeNet [14]在保持精度的同时显着减少了参数和计算量。 ResNet [9,10]利用高效的瓶颈结构来实现令人印象深刻的性能。 SENet [13]引入了一种架构单元,它以较低的计算成本提高了性能。与我们同时进行的是一项非常近期的工作[46],它采用强化学习和模型搜索来探索有效的模型设计。 拟议的移动NASNet模型在性能上与我们的ShuffleNet模型相当(对于ImageNet分类错误,其@ 564 MFLOP为26.0%,而524 MFLOP为26.3%)。 但是[46]没有报告极小的模型的结果(例如,复杂度小于150 MFLOP),也没有评估移动设备上的实际推理时间。

分组卷积 分组卷积的概念最早出现在AlexNet [21]中,用于在两个GPU上分配模型,已在ResNeXt [40]中得到了充分证明。 Xception [3]中提出的深度可分离卷积概括了Inception系列[34,32]中的可分离卷积的思想。最近,MobileNet [12]利用深度可分离卷积并获得了轻量模型中的最新结果。我们的工作以一种新颖的形式概括了群卷积和深度可分离卷积。

据我们所知,尽管在CNN库cuda-convnet [20]支持“随机稀疏卷积”层(相当于随机信道)的前提下,在有效模型设计的先前工作中很少提及信道随机化操作的想法。洗牌,然后是组卷积层。这种“随机混洗”操作具有不同的目的,以后很少被利用。最近,另一项并行的工作[41]也采用了这种思想进行两阶段卷积。然而,[41]没有专门研究信道改组本身的有效性及其在微型模型设计中的使用。

模型加速 此方向旨在加快推理速度,同时保持预训练模型的准确性。修剪网络连接[6、7]或通道[38]可在保持性能的同时减少预训练模型中的冗余连接。在文献中提出了量化[31、27、39、45、44]和因式分解[22、16、18、37],以减少计算中的冗余以加速推理。在不修改参数的情况下,通过FFT [25,35]和其他方法[2]实现的优化卷积算法可以减少实践中的时间消耗。提炼[11]将知识从大型模型转移到小型模型,这使得对小型模型的训练更加容易。

3 Approach

3.1. Channel Shuffle for Group Convolutions

现代卷积神经网络[30、33、34、32、9、10]通常由具有相同结构的重复构建块组成。其中,最先进的网络,例如Xception [3]和ResNeXt [40],将有效的深度可分离卷积或组卷积引入到构建块中,从而在表示能力和计算成本之间取得了很好的折衷。但是,我们注意到,两种设计都没有完全考虑1×1卷积(在[12]中也称为点状卷积),这需要相当大的复杂性。例如,在ResNeXt [40]中,只有3×3层配备了组卷积。结果,对于ResNeXt中的每个残差单元,逐点卷积占据93.4%的乘积(基数= 32,如[40]中所建议)。在小型网络中,昂贵的逐点卷积导致通道数量有限,无法满足复杂性约束,这可能会严重影响精度。
在这里插入图片描述
​ 图2. ShuffleNet单元。 a)具有深度卷积(DWConv)[3,12]的瓶颈单元[9]; b)具有按点分组卷积(GConv)和通道混洗的ShuffleNet单元; c)步幅= 2的ShuffleNet单元

为了解决这个问题,一个简单的解决方案是在1×1层上应用通道稀疏连接,例如组卷积。 通过确保每个卷积仅在相应的输入通道组上运行,组卷积显着降低了计算成本。 但是,如果多个组卷积堆叠在一起,则会产生一个副作用:某个通道的输出仅从一小部分输入通道派生。图1(a)展示了两个堆叠的组卷积层的情况。 显然,某个组的输出仅与该组内的输入有关。 此属性阻止通道组之间的信息流并削弱表示。

如果我们允许组卷积从不同组中获取输入数据(如图1(b)所示),则输入和输出通道将完全相关。具体来说,对于从上一个组层生成的特征图,我们可以先将每个组中的通道划分为几个子组,然后再将下一个层中的每个组提供给不同的子组。这可以通过通道混洗操作有效而优雅地实现(图1(c)):假设一个具有g个组的卷积层,其输出具有g×n个通道;我们首先将输出通道的尺寸调整为(g,n),进行转置,然后再变平,作为下一层的输入。请注意,即使两个卷积具有不同数量的组,该操作仍然会生效。此外,信道混洗也是可区分的,这意味着可以将其嵌入到网络结构中以进行端到端训练。

通道混洗操作可以构建具有多个组卷积层的更强大的结构。在下一个小节中,我们将介绍一个具有信道混洗和组卷积的高效网络单元。

3.2. ShuffleNet Unit

利用信道随机播放操作的优势,我们提出了一种专门为小型网络设计的新型ShuffleNet单元。我们从图2(a)中的瓶颈单元[9]的设计原理开始。这是一个剩余的块。在其剩余分支中,对于3×3层,我们在瓶颈特征图上应用了经济的3×3深度卷积计算方法[3]。然后,我们将第一个1×1层替换为逐点组卷积,然后进行通道随机混合操作,以形成ShuffleNet单元,如图2(b)所示。第二次逐点分组卷积的目的是恢复通道尺寸以匹配快捷方式路径。为简单起见,我们在第二个逐点层之后不应用额外的通道随机播放操作,因为它会产生可比的得分。批处理归一化(BN)[15]和非线性的用法与[9,40]相似,不同之处在于我们不按[3]的建议在深度卷积后使用ReLU。对于ShuffleNet跨步应用的情况,我们只需进行两个修改(见图2(c)):(i)在快捷路径上添加3×3平均池; (ii)用通道级联替换逐元素加法,这使得扩展通道尺寸变得容易,而额外的计算成本却很少。
在这里插入图片描述
由于具有通道混洗的逐点分组卷积,可以高效地计算ShuffleNet单元中的所有分量。 与ResNet [9](瓶颈设计)和ResNeXt [40]相比,我们的结构在相同设置下具有较低的复杂性。 例如,给定输入大小c×h×w和瓶颈通道m,ResNet单位需要hw(2cm + 9m2)FLOP,ResNeXt则需要hw(2cm + 9m2 / g)FLOP,而我们的ShuffleNet单位仅需要hw(2cm / g + 9m)FLOP,其中g表示卷积的组数。 换句话说,给定计算预算,ShuffleNet可以使用更宽的特征图。 我们发现这对于小型网络至关重要,因为小型网络通常没有足够的通道来处理信息。

此外,在ShuffleNet中,深度卷积仅对瓶颈特征图执行。尽管深度卷积通常具有非常低的理论复杂度,但我们发现很难在低功率移动设备上有效实现,这可能是由于与其他密集操作相比更差的计算/内存访问比所致。在[3]中也提到了这种缺点,它具有基于TensorFlow [1]的运行时库。在ShuffleNet单元中,我们故意仅在瓶颈上使用深度卷积,以尽可能避免开销。

3.3. Network Architecture

在ShuffleNet单元的基础上,我们在表1中介绍了整个ShuffleNet架构。所提议的网络主要由分成三个阶段的ShuffleNet单元堆栈组成。在步幅= 2的情况下应用第一个构建块级。阶段中的其他超参数保持不变,并且对于下一个阶段,输出通道加倍。类似于[9],我们将每个ShuffleNet单元的瓶颈通道数设置为输出通道的1/4。我们的目的是提供尽可能简单的参考设计,尽管我们发现进一步的超参数调整可能会产生更好的结果。

在ShuffleNet单元中,组号g控制逐点卷积的连接稀疏性。表1探索了不同的组号,我们调整了输出通道以确保总体计算成本大致不变(〜140 MFLOP)。显然,对于给定的复杂性约束,较大的组数会导致更多的输出通道(因此,需要更多的卷积滤波器),这有助于编码更多信息,尽管由于受限的相应输入通道,这也可能导致单个卷积滤波器的性能下降。在第4.1.1节中,我们将研究此数字在不同计算约束下的影响。

为了将网络定制为所需的复杂度,我们可以简单地在通道数上应用比例因子s。例如,我们在表1中将网络表示为“ ShuffleNet 1×”,然后“ ShuffleNet s×”表示将ShuffleNet 1×中的过滤器数量缩放s倍,因此总体复杂度将约为ShuffleNet 1×s^2倍。

4 Experiments

我们主要在ImageNet 2012分类数据集上评估模型[29,4]。 我们遵循[40]中使用的大多数训练设置和超参数,但有两个例外:(i)将权重衰减设置为4e-5而不是1e-4,并使用线性衰减学习率策略(从0.5降低 至0); (ii)我们在数据预处理中使用略微较少的积极规模扩展。 在[12]中也引用了类似的修改,因为这样的小型网络通常遭受欠拟合而不是过度拟合的困扰。 在4个GPU上训练3×105迭代的模型需要1或2天的时间,其批处理大小设置为1024。为进行基准测试,我们在ImageNet验证集上比较了单作物top-1性能。 从256×输入图像中裁剪224×224中心视图并评估分类准确性。 我们对所有模型使用完全相同的设置,以确保公平地进行比较。
在这里插入图片描述

4.1. Ablation Study

ShuffleNet的核心思想在于逐点群组卷积和通道随机操作。在本小节中,我们分别评估它们。

4.1.1 Pointwise Group Convolutions

为了评估逐点群卷积的重要性,我们比较了复杂度相同的ShuffleNet模型,其群数范围为1到8。如果群数等于1,则不涉及逐点群卷积,然后ShuffleNet单元变为“ Xception-喜欢” [3]结构。为了更好地理解,我们还将网络的宽度缩放到3个不同的复杂度,并分别比较它们的分类性能。结果如表2所示。

从结果可以看出,具有组卷积(g> 1)的模型的性能始终优于没有逐点组卷积(g = 1)的模型。较小的模型往往会从团体中受益更多。例如,对于ShuffleNet 1x,最佳条目(g = 8)比同类条目好1.2%,而对于ShuffleNet 0.5x和0.25x,差距分别变为3.5%和4.4%。请注意,对于给定的复杂性约束,组卷积允许更多的特征图通道,因此我们假设性能增益来自有助于编码更多信息的更宽的特征图。另外,较小的网络涉及较薄的特征图,这意味着它从扩大的特征图中受益更多。

表2还显示,对于某些模型(例如ShuffleNet 0.5x),当组数变得相对较大(例如g = 8)时,分类得分会饱和甚至下降。随着组数的增加(因此,特征图会更宽),每个卷积滤波器的输入通道会越来越少,这可能会损害表示能力。有趣的是,我们还注意到,对于ShuffleNet等较小的模型,如0.25×较大的组号,往往会始终如一地获得更好的结果,这表明较宽的特征图为较小的模型带来了更多好处。

4.1.2 Channel Shuffle vs. No Shuffle

随机操作的目的是为多个组卷积层启用跨组信息流。表3比较了带/不带通道混洗的ShuffleNet结构(例如,组号设置为3或8)的性能。评估是在三种不同的复杂度范围内进行的。显然,频道改组可以持续提高不同设置的分类得分。特别地,当组数相对较大(例如,g = 8)时,具有信道混洗的模型在性能上优于对应的模型,这表明了跨组信息交换的重要性。

4.2. Comparison with Other Structure Units

VGG [30],ResNet [9],GoogleNet [33],ResNeXt [40]和Xception [3]中最近的领先卷积单元已经在大型模型(例如≥1GFLOP)上追求了最新的结果,但是没有充分探索低复杂性条件。在本节中,我们调查了各种构建基块,并在相同的复杂性约束下与ShuffleNet进行了比较。
在这里插入图片描述
为了公平比较,我们使用表1所示的总体网络架构。我们将Stage 2-4中的ShuffleNet单元替换为其他结构,然后调整通道数以确保复杂度保持不变。我们探索的结构包括:

VGG-like。遵循VGG网络的设计原理[30],我们使用两层3×3卷积作为基本构建块。与[30]不同,我们在每个卷积之后添加了一个批处理归一化层[15],以简化端到端的训练。

Xception-like。 [3]中提出的原始结构涉及不同阶段的精美设计或超参数,我们发现很难在小模型上进行公平比较。取而代之的是,我们从ShuffleNet(也等效于g = 1的ShuffleNet)中删除了逐点分组卷积和通道shuffle操作。派生结构与[3]中的“深度可分离卷积”想法相同,在此称为Xception-like结构。

ResNeXt。如[40]中所建议的,我们使用基数= 16和瓶颈比率= 1:2的设置。我们还将探索其他设置,例如瓶颈比率= 1:4,并获得相似的结果。

我们使用完全相同的设置来训练这些模型。结果如表4所示。在不同的复杂度下,我们的ShuffleNet模型要比其他大多数模型大得多。 有趣的是,我们发现了特征图通道与分类精度之间的经验关系。 例如,在38个MFLOP的复杂性下,类VGG,ResNet,ResNeXt,类Xception,ShuffleNet的第4阶段(请参见表1)的输出通道分别为50192、192、288、576,与 提高准确性。 由于ShuffleNet的高效设计,对于给定的计算预算,我们可以使用更多的通道,因此通常可以获得更好的性能。

请注意,以上比较不包括GoogleNet或Inception系列[33、34、32]。我们发现将这样的Inception结构生成到小型网络并非易事,因为Inception模块的原始设计涉及太多的超参数。作为参考,第一个GoogleNet版本[33]具有31.3%的top-1错误,但代价是1.5 GFLOP(请参见表6)。然而,更复杂的盗版版本[34,32]更加准确,但是复杂度也大大增加。最近,金等提出了一种轻量级的网络结构,称为PVANET [19],它采用了Inception单元。我们重新实现的PV ANET(输入大小为224×224)具有29.7%的分类错误,计算复杂度为557 MFLOP,而我们的ShuffleNet 2x模型(g = 3)在524 MFLOP的情况下获得了26.3%(参见表6)。

4.3. Comparison with MobileNets and Other Frameworks

最近霍华德等。已经提出了MobileNets [12],其主要侧重于移动设备的有效网络架构。 MobileNet从[3]中采用了深度可分离卷积的思想,并在小型模型上获得了最新的结果。
在这里插入图片描述
表5比较了各种复杂性级别下的分类得分。 显然,我们的ShuffleNet模型在所有复杂性方面都优于MobileNet。 尽管我们的ShuffleNet网络是专为小型机型(<150 MFLOP)设计的,但我们发现它在计算成本方面比MobileNet更好,例如, 在500个MFLOP的代价下,其精度比MobileNet 1×高3.1%。 对于较小的网络(约40个MFLOP),ShuffleNet比MobileNet超出7.8%。 请注意,我们的ShuffleNet架构包含50层,而MobileNet仅包含28层。 为了更好地理解,我们还通过在阶段2-4中删除一半的块来尝试在26层体系结构上使用ShuffleNet(请参阅表5中的“ ShuffleNet 0.5×浅(g = 3)”)。 结果表明,较浅的模型仍然比相应的MobileNet更好,这表明ShuffleNet的有效性主要来自其有效的结构,而不是深度。

表6比较了我们的ShuffleNet和一些流行的模型。结果表明,以相似的精度ShuffleNet比其他的效率更高。例如,ShuffleNet 0.5×理论上比具有类似分类分数的AlexNet [21]快18倍。我们将在4.5节中评估实际的运行时间。

还值得注意的是,简单的体系结构设计使向ShuffeNets轻松配备诸如[13,26]之类的最新进展。例如,在[13]中,作者提出了挤压和激发(SE)块,该块可以在大型ImageNet模型上获得最新的结果。我们发现SE模块也可以与主链ShuffleNets结合使用,例如,将ShuffleNet 2×的top-1误差提高到24.7%(如表5所示)。有趣的是,尽管理论复杂性的增加可以忽略不计,但我们发现带有SE模块的ShuffleNets通常比移动设备上的“原始” ShuffleNets慢25%到40%,这意味着实际的加速评估对于低成本架构设计至关重要。在第4.5节中,我们将进一步讨论。

4.4. Generalization Ability

为了评估迁移学习的泛化能力,我们在MS COCO对象检测任务上测试了ShuffleNet模型[23]。我们采用Faster-RCNN [28]作为检测框架,并使用公开发布的Caffe代码[28,17]进行默认设置的训练。与[12]类似,模型在不包括5000个最小图像的COCO序列+ val数据集上进行训练,我们对最小集合进行测试。表7显示了在两种输入分辨率下训练和评估的结果的比较。将ShuffleNet 2x和MobileNet的复杂度相媲美(524 vs. 569 MFLOP),我们的ShuffleNet 2x在两个分辨率上都远远超过MobileNet。我们的ShuffleNet 1×在600×分辨率下也可以与MobileNet取得可比的结果,但复杂度降低了约4倍。我们猜想,这一重大收益部分是由于ShuffleNet的架构设计简单而没有花哨。

4.5. Actual Speedup Evaluation

最后,我们评估具有ARM平台的移动设备上ShuffleNet模型的实际推理速度。尽管具有较大组号(例如g = 4或g = 8)的ShuffleNets通常具有更好的性能,但我们发现它在当前的实现中效率较低。根据经验,g = 3通常会在准确性和实际推理时间之间取得适当的折衷。如表8所示,测试使用了三种输入分辨率。由于内存访问和其他开销,我们发现理论上每降低4倍的理论复杂度通常会导致实际速度提高2.6倍。尽管如此,与AlexNet [21]相比,我们的ShuffleNet 0.5×模型在可比的分类精度下(理论上的速度为18倍)仍可实现约13倍的实际速度,这比以前的AlexNet级别的模型或诸如[14]的速度方法要快得多。[14、16、22、42、43、38]。

pytorch源码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import math

import torch
import torch.nn as nn
import torch.nn.functional as F


class Bottleneck(nn.Module):  # shuffle Net 模仿的是resnet bottleblock的结构
    def __init__(self, in_planes, out_planes, stride, groups):
        super(Bottleneck, self).__init__()
        self.stride = stride

        mid_planes = out_planes // 4  # 每个ShuffleNet unit的bottleneck通道数为输出的1/4(和ResNet设置一致)
        self.groups = 1 if in_planes == 24 else groups  # 第一层卷积之后是24,所以不必group
        self.conv1 = nn.Conv2d(in_planes, mid_planes, kernel_size=1, groups=self.groups, bias=False)
        self.bn1 = nn.BatchNorm2d(mid_planes)
        self.conv2 = nn.Conv2d(mid_planes, mid_planes, kernel_size=3, stride=stride, padding=1, groups=mid_planes, bias=False)  # 这里应该用dw conv的
        self.bn2 = nn.BatchNorm2d(mid_planes)
        self.conv3 = nn.Conv2d(mid_planes, out_planes, kernel_size=1, groups=groups, bias=False)
        self.bn3 = nn.BatchNorm2d(out_planes)
        if stride == 2:
            self.shortcut = nn.Sequential(nn.AvgPool2d(3, stride=2, padding=1))  # 每个阶段第一个block步长是2,下个阶段通道翻倍

    @staticmethod
    def shuffle_channels(x, groups):
        '''Channel shuffle: [N,C,H,W] -> [N,g,C/g,H,W] -> [N,C/g,g,H,W] -> [N,C,H,W]'''
        '''一共C个channel要分成g组混合的channel,先把C reshape成(g, C/g)的形状,然后转置成(C/g, g)最后平坦成C组channel'''
        N, C, H, W = x.size()
        return x.view(N, groups, C // groups, H, W).permute(0, 2, 1, 3, 4).contiguous().view(N, C, H, W)  # 因为x之前view过了,他的内存不连续了,需要contiguous来规整一下

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.shuffle_channels(out, self.groups)
        out = F.relu(self.bn2(self.conv2(out)))
        out = self.bn3(self.conv3(out))

        if self.stride == 2:
            res = self.shortcut(x)
            out = F.relu(torch.cat([out, res], 1))  # shuffle-net 对做了下采样的res用的是cat而不是+
        else:
            out = F.relu(out + x)
        return out


class ShuffleNet(nn.Module):

    def __init__(self, out_planes, num_blocks, groups, num_classes=None, depth_multiplier=1.):  # depth_multiplier是控制通道数的缩放因子
        super(ShuffleNet, self).__init__()
        self.num_classes = num_classes
        self.in_planes = int(24 * depth_multiplier)  # 通常第一个卷积的输出为24
        self.out_planes = [int(depth_multiplier * x) for x in out_planes]

        self.conv1 = nn.Conv2d(3, self.in_planes, kernel_size=3, stride=2, bias=False)
        self.bn1 = nn.BatchNorm2d(self.in_planes)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        # self.bn1 = nn.BatchNorm2d(24)
        # self.in_planes = 24
        layers = []
        for out_plane, num_block in zip(out_planes, num_blocks):
            layers.append(self._make_layer(out_plane, num_block, groups))
        self.layers = nn.ModuleList(layers)  # 把list里面的每一个元素变成一个module
        if num_classes is not None:
            self.avgpool = nn.AdaptiveMaxPool2d(1)
            self.fc = nn.Linear(out_planes[-1], num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    def _make_layer(self, out_planes, num_blocks, groups):
        layers = []
        for i in range(num_blocks):
            stride = 2 if i == 0 else 1  # 如果是第一个block就是2 否则就是1
            cat_planes = self.in_planes if i == 0 else 0  # 因为第一个要下采样并且cat,所以为了下一个block加的时候能够通道匹配,要先减掉cat的通道数
            layers.append(Bottleneck(self.in_planes, out_planes - cat_planes, stride=stride, groups=groups))
            self.in_planes = out_planes  # 第一个过后input就都是out_planes
        return nn.Sequential(*layers)

    @property
    def layer_channels(self):
        return self.out_planes

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = self.maxpool(x)

        c = []
        for i in range(len(self.layers)):
            x = self.layers[i](x)
            c.append(x)

        if self.num_classes is not None:
            x = self.avgpool(x)
            x = x.view(x.size(0), -1)
            x = self.fc(x)
            return x
        else:  # 返回每一个阶段的特征
            return c


def shufflenet(**kwargs):  # group = 3 论文实验中效果最好的
    planes = [240, 480, 960]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=3, **kwargs)
    return model


def shufflenet_4(**kwargs):  # group = 4
    planes = [272, 544, 1088]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=4, **kwargs)
    return model


def shufflenet_2(**kwargs):
    planes = [200, 400, 800]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=2, **kwargs)
    return model


def shufflenet_1(**kwargs):
    planes = [144, 288, 576]
    layers = [4, 8, 4]
    model = ShuffleNet(planes, layers, groups=1, **kwargs)
    return model


源码链接:https://blog.csdn.net/weixin_44538273/article/details/88856239

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值