Pytorch之shuffleNet图像分类( 五 )


可以看见上表中报告了不同变体的运行时间 。观察到 , 在移除ReLU和后 , GPU和ARM都获得了大约20%的加速 。这里主要突出的是 , 这些操作会比我们想象当中的要耗时 。
总结:基于上述准则和实证研究 , 作者总结出一个高效的网络架构应该:
①要使用“平衡”卷积 , 使输入特征矩阵和输出矩阵的相等或者接近
②注意分组卷积的计算成本 , 增大组数能降低参数,但是它会增加计算成本
③降低网络的碎片程度 , 不要涉及多分支结构
④尽可能减少使逐元素操作(-wise )
2. 网络结构
如下图所示 , 图(a)和图(b)是 V1,其中(a)是DW卷积步距为1时的block , (b)是DW卷积步距为2时的 block 。右边的图(c)和图(d) , 是 V2中对应步距为1和2的block 。
在V1上做出了一些改进 , 如图( c )所示 , 在每个单元的开始 , 通过一个 Split操作将输入特征矩阵划分为两部分 , 一部分是分支 , 另一部分对应于主分支(在中这里是对均分成两半) 。根据G3 , 不能使用太多的分支 , 所以其中一个分支不作改变 , 另外的一个分支由三个卷积组成 , 它们具有相同的输入和输出通道以满足G1 。
在主分支上 , 两个1 × 1卷积不再是组卷积 , 而改变为普通的1x1卷积操作 , 这是为了遵循G2(需要考虑组的代价) 。卷积后 , 两个分支被拼接 , 而不是相加(G4) 。因此 , 通道的数量保持不变(G1) 。
然后使用与 V1中相同的 操作来启用两个分支之间的信息通信 。需要注意 ,  v1中的“Add”操作不再存在 。ReLU和这样的元素操作只存在于一个分支中 。
对于三个连续的-wise操作 , , 以及下一个block的 Split , 这三个操作可以合并为一个-wise  , 这样减少了-wise操作的个数 , 这就满足G4准则 。
对于为2降采样图(d) ,  v1使用了3x3的平均池化 , 而 v2使用了一个3x3 DW卷积和一个1x1的普通卷积 。不存在 split操作 , 移除通道分离操作符 , 输出通道的数量增加了一倍 。
所提出的block( c )( d )以及由此产生的网络称为 V2 。基于上述分析 , 该体系结构设计是高效的 , 因为它遵循了所有的指导原则 。积木重复堆叠 , 构建整个网络 。V2网络结构如下表所示 。
总体网络结构类似于 v1 , 唯一的区别是在全局平均池化前增加了一个1 × 1的卷积层来混合特性 。每个block中的通道数量被缩放 , 生成不同复杂度的网络 , 标记为0.5x , 1x , 1.5x , 2x 。对于每个stage , 它的第一个block是需要进行翻倍的 , 步距strip都是等于2的 。
v2不仅高效 , 而且准确 , 主要有两个原因:
①每个block的高效率使使用更多的特征通道和更大的网络容量成为可能
②在每个block中 , 有一半的特征通道直接穿过该块并加入下一个块, 可以看作是一种特性复用 , 与和的思想一样
三、网络实现 1.构建网络
# 通道洗牌def channel_shuffle(x: Tensor, groups: int) -> Tensor:batch_size, num_channels, height, width = x.size()# [n,c,h,w]channels_per_group = num_channels // groups# groups划分组数# reshape# [batch_size, num_channels, height, width] -> [batch_size, groups, channels_per_group, height, width]x = x.view(batch_size, groups, channels_per_group, height, width)x = torch.transpose(x, 1, 2).contiguous()# 1 , 2维度交换# flattenx = x.view(batch_size, -1, height, width)return x# ShuffleNetV2 blockclass InvertedResidual(nn.Module):def __init__(self, input_c: int, output_c: int, stride: int):super(InvertedResidual, self).__init__()# 参数判断if stride not in [1, 2]:raise ValueError("illegal stride value.")self.stride = strideassert output_c % 2 == 0# 判断 output_c = in‘ + in’ = 2*in'branch_features = output_c // 2# 均分# 当stride为1时 , input_channel应该是branch_features的两倍# python中 '