-
图的a图是一个带有深度可分离卷积的残差模块,这里的1×1是逐点的卷积。相比深度可分离卷积,1×1计算量较大。
-
图的b图则是基本的ShuffleNet基本单元,可以看到1×1卷积采用的是组卷积,然后进行通道的混洗,这两步可以取代1×1的逐点卷积,并且大大降低了计算量。3×3卷积仍然采用深度可分离的方式。
-
图的c图是带有降采样的ShuffleNet单元,在旁路中使用了步长为2的3×3平均池化进行降采样,在主路中3×3卷积步长为2实现降采样。另外,由于降采样时通常要伴有通道数的增加,ShuffleNet直接将两分支拼接在一起来实现了通道数的增加,而不是常规的逐点相加。
得益于组卷积与通道混洗,ShuffleNet的基本单元可以很高效地进行计算。在该基本单元的基础上,ShuffleNet的整体网络结构如下表所示。
关于ShuffleNet的整体结构,有以下3点需要注意:
-
g代表组卷积的组数,以控制卷积连接的稀疏性。组数越多,计算量越少,因此在相同的计算资源,可以使用更多的卷积核以获取更多的通道数。
-
ShuffleNet在3个阶段内使用了其特殊的基本单元,这3个阶段的第一个Block的步长为2以完成降采样,下一个阶段的通道数是上一个的两倍。
-
深度可分离卷积虽然可以有效降低计算量,但其存储访问效率较差,因此第一个卷积并没有使用ShuffleNet基本单元,而是只在后续3个阶段使用。
下面以g=3为例,从代码层面讲解ShuffleNet网络的构建。(Pytorch实现)
from torch import nn
import torch.nn.functional as F
class ShuffleNet(nn.Module):
def init(self, groups=3, in_channels=3, num_classes=1000):
super(ShuffleNet, self).init()
self.groups = groups
# 3个阶段的Block数量
self.stage_repeats = [3, 7, 3]
self.in_channels = in_channels
self.num_classes = num_classes
# g为3时,每一个阶段输出特征的通道数
self.stage_out_channels = [-1, 24, 240, 480, 960]
self.conv1 = conv3x3(self.in_channels,
self.stage_out_channels[1], # stage 1
stride=2)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
# 单独构建3个阶段
self.stage2 = self._make_stage(2)
self.stage3 = self._make_stage(3)
self.stage4 = self._make_stage(4)
# 全连接层,当前模型用于物体分类
num_inputs = self.stage_out_channels[-1]
self.fc = nn.Linear(num_inputs, self.num_classes)
def _make_stage(self, stage):
modules = OrderedDict()
stage_name = “ShuffleUnit_Stage{}”.format(stage)
# 在第二个阶段之后开始使用组卷积
grouped_conv = stage > 2
# 每个阶段的第一个模块使用的是Concat,因此需要单独构建
first_module = ShuffleUnit(
self.stage_out_channels[stage-1],
self.stage_out_channels[stage],
groups=self.groups,
grouped_conv=grouped_conv,
combine=‘concat’
)
modules[stage_name+“_0”] = first_module
# 重复构建每个阶段剩余的Block模块
for i in range(self.stage_repeats[stage-2]):
name = stage_name + “_{}”.format(i+1)
module = ShuffleUnit(
self.stage_out_channels[stage],
self.stage_out_channels[stage],
groups=self.groups,
grouped_conv=True,
combine=‘add’
)
modules[name] = module
return nn.Sequential(modules)
def forward(self, x):
x = self.conv1(x)
x = self.maxpool(x)
x = self.stage2(x)
x = self.stage3(x)
x = self.stage4(x)
# 全局的平均池化层
x = F.avg_pool2d(x, x.data.size()[-2:])
# 将特征平展,并送入全连接层与Softmax,得到最终预测概率
x = x.view(x.size(0), -1)
x = self.fc(x)
return F.log_softmax(x, dim=1)
总体上,ShuffleNet提出了一个巧妙的通道混洗模块,在几乎不影响准确率的前提下,进一步地降低了计算量,性能优于ResNet和Xception等网络,因此也更适合部署在移动设备上。
ShuffleNet v2网络结构:
==================
在2018年,旷视的团队进一步升级了ShuffleNet,提出了新的版本ShuffleNet v2。相比于ShuffleNet v1,ShuffleNet v2进一步分析了影响模型速度的因素,提出了新的规则,并基于此规则,改善了原版本的不足。 原有的一些轻量化方法在衡量模型性能时,通常使用浮点运算量FLOPs(Floating Point Operations)作为主要指标。FLOPs是指模型在进行一次前向传播时所需的浮点计算次数,其单位为FLOP,可以用来衡量模型的复杂度。 然而,通过一系列实验发现ShuffleNet v2仅仅依赖FLOPs是有问题的,FLOPs近似的网络会存在不同的速度,还有另外两个重要的指标:内存访问时间(Memory Access Cost,MAC)与网络的并行度。 以此作为出发点,ShuffleNet v2做了大量的实验,分析影响网络运行速度的原因,提出了建立高性能网络的4个基本规则: (1)卷积层的输入特征与输出特征通道数相等时,MAC最小,此时模型速度最快。 (2)过多的组卷积会增加MAC,导致模型的速度变慢。 (3)网络的碎片化会降低可并行度,这表明模型中分支数量越少,模型速度会越快。 (4)逐元素(Element Wise)操作虽然FLOPs值较低,但其MAC较高,因此也应当尽可能减少逐元素操作。 以这4个规则为基础,可以看出ShuffleNet v1有3点违反了此规则:
-
在Bottleneck中使用了1×1组卷积与1×1的逐点卷积,导致输入输出通道数不同,违背了规则1与规则2。
-
整体网络中使用了大量的组卷积,造成了太多的分组,违背了规则3。
-
网络中存在大量的逐点相加操作,违背了规则4。
针对v1的问题,ShuffleNet v2提出了一种新的网络基本单元,具体如下图所示。图a与图b为v1版本的基础结构,图c与图d是v2提出的新结构。
ShuffleNet v2的基本单元有如下3点新特性:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

总结
在清楚了各个大厂的面试重点之后,就能很好的提高你刷题以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
8rx-1712068345592)]
[外链图片转存中…(img-92grDmha-1712068345592)]
[外链图片转存中…(img-lBQUDBXL-1712068345592)]
[外链图片转存中…(img-8Zw0KSK1-1712068345593)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!