MicroNet关键代码解读(Micro-block与Dynamic Shift-Max的实现代码)

论文地址:https://arxiv.org/pdf/2011.12289
中文翻译:https://hpg123.blog.csdn.net/article/details/141772832?spm=1001.2014.3001.5502
发表时间:2022
项目地址:https://github.com/liyunsheng13/micronet

在MicroNet论文中提出了Micro-block与Dynamic Shift-Max,这里对其代码实现进行深入分析。最终结论是Micro-block的定义实现十分混乱,MicroNet比Moblienet强主要是深度分离卷积的性能没有充分挖掘到位,可以替换成conv1xk_group+convkx1_group的组合,从而在低flop的约束条件下实现了5个点左右的提升;另外一点是使用了带参数的激活函数,同时激活函数中提供了group间的数据交互,故再次提升了模型精度。

1、Micro-block

1.1 模块定义

在MicroNet论文中一共有Micro-Block-A、Micro-Block-B、Micro-Block-C三种模块设计。

Micro-Block-A 采用微因式点卷积和深度卷积的精简组合(见图 2-右),它主要适用在模型的浅层。需要注意的是,微因数深度卷积扩大了通道数,而分组自适应卷积则压缩了通道数。

Micro-Block-B 用于连接 Micro-Block-A 和 Micro-Block-C。与 Micro-Block-A 不同的是,它使用的是全 Micro-Factorized 点式卷积,其中包括两个组自适应卷积(如图 5b 所示)。前者压缩了通道数,后者则扩大了通道数。

微块-C(如图 5c 所示)使用了常规组合。 深度卷积和点卷积的正则组合。它用于模型的深层,因为与精简组合相比,它在通道融合(点上)上花费的计算量更大。
在这里插入图片描述

基于下列图片可以发现,Micro-block虽然使用了group conv,但基于Φ函数的shitf操作,是可以实现数据在不同group间的交互。
在这里插入图片描述

1.2 模块效果

首先通过论文中的表1可以发现Micro-Block-B 是一直只有一个,且介于A与C之间。
在这里插入图片描述
在block级别的对比中,可以发现Micro-block具备明显的涨点效果。
在这里插入图片描述
在与MobileNet的对比中可以发现在没有Shift-Max操作时,Micro就可以比Mobile高6个点了。这主要是因为MobileNet只是将conv拆解为深度分离卷积与点卷积,conv3x3的操作没有拆解;而在Micro中,将conv拆解为了conv3x1_group+full_group_connect+conv1x3_group,这种拆解相比MobileNet更高效。同时在保证相同flop的时候能具备更深的网络结构。
在这里插入图片描述

1.3 代码实现

以下代码推测是Micro-Block-A 的实现,可以发现是 kx1+bn+1xk+ChannelShuffle操作,对比DepthSpatialSepConv函数,发现实现上高度接近。

class ChannelShuffle(nn.Module):
    def __init__(self, groups):
        super(ChannelShuffle2, self).__init__()
        self.groups = groups

    def forward(self, x):
        b, c, h, w = x.size()

        channels_per_group = c // self.groups

        # reshape
        x = x.view(b, self.groups, channels_per_group, h, w)

        x = torch.transpose(x, 1, 2).contiguous()
        out = x.view(b, -1, h, w)

        return out

######################################################################3
# part 3: new block
#####################################################################3

class SpatialSepConvSF(nn.Module):
    def __init__(self, inp, oups, kernel_size, stride):
        super(SpatialSepConvSF, self).__init__()

        oup1, oup2 = oups
        self.conv = nn.Sequential(
            nn.Conv2d(inp, oup1,
                (kernel_size, 1),
                (stride, 1),
                (kernel_size//2, 0),
                bias=False, groups=1
            ),
            nn.BatchNorm2d(oup1),
            nn.Conv2d(oup1, oup1*oup2,
                (1, kernel_size),
                (1, stride),
                (0, kernel_size//2),
                bias=False, groups=oup1
            ),
            nn.BatchNorm2d(oup1*oup2),
            ChannelShuffle(oup1),
        )

    def forward(self, x):
        out = self.conv(x)
        return out

在追溯DYMicroBlock代码中并为明确发现Micro-Block-A、Micro-Block-B、Micro-Block-C的定义。但发现了不少ChannelShuffle操作,这表明在conv_group模型中,通道间的交互是不可少的。
在这里插入图片描述

2、Dynamic Shift-Max

2.1 模块定义

在MicroNet中提到了一种新的激活函数–动态 Shift-Max函数,以增强非线性。它能动态地将输入特征图与它的环形组移动进行动态融合,以group为移动单位进行移动。Dynamic Shift-Max 还能
加强组之间的联系。这是对Micro-Factorized pointwise convolution的补充,侧重于组内连接的互补性。

其具体作用如图4所示,对channel以group为单位进行循环移动,并基于fc映射后动态输出max值。
在这里插入图片描述

2.2 模块效果

Shift-Max模块的效果如下所示,可以看到相比于不加之前,top1 acc提升了2.7%,而当使用dynamic shift-max后,top1 acc相比于不加提升了6.8%。可以发现Shift-Max模块是MicroNet的涨点关键。其对于分组卷积提供了group间的特征互动,同时相比于普通的激活函数,其是带可训练参数的模型。
在这里插入图片描述
同时在与其他同类型激活函数对比中,可以发现使用Dynamic Shift-Max时更加有效;同时Dynamic ReLU也证明了在低flop模型中也具有显著的作用,只是在group conv中略有不殆。
在这里插入图片描述

2.3 代码实现

class DYShiftMax(nn.Module):
    def __init__(self, inp, oup, reduction=4, act_max=1.0, act_relu=True, init_a=[0.0, 0.0], init_b=[0.0, 0.0], relu_before_pool=False, g=None, expansion=False):
        super(DYShiftMax, self).__init__()
        self.oup = oup
        self.act_max = act_max * 2
        self.act_relu = act_relu
        self.avg_pool = nn.Sequential(
                nn.ReLU(inplace=True) if relu_before_pool == True else nn.Sequential(),
                nn.AdaptiveAvgPool2d(1)
            )

        self.exp = 4 if act_relu else 2
        self.init_a = init_a
        self.init_b = init_b

        # determine squeeze
        squeeze = _make_divisible(inp // reduction, 4)
        if squeeze < 4:
            squeeze = 4
        print('reduction: {}, squeeze: {}/{}'.format(reduction, inp, squeeze))
        print('init-a: {}, init-b: {}'.format(init_a, init_b))

        self.fc = nn.Sequential(
                nn.Linear(inp, squeeze),
                nn.ReLU(inplace=True),
                nn.Linear(squeeze, oup*self.exp),
                h_sigmoid()
        )
        if g is None:
            g = 1
        self.g = g[1]
        if self.g !=1  and expansion:
            self.g = inp // self.g
        print('group shuffle: {}, divide group: {}'.format(self.g, expansion))
        self.gc = inp//self.g
        index=torch.Tensor(range(inp)).view(1,inp,1,1)
        index=index.view(1,self.g,self.gc,1,1)
        indexgs = torch.split(index, [1, self.g-1], dim=1)
        indexgs = torch.cat((indexgs[1], indexgs[0]), dim=1)
        indexs = torch.split(indexgs, [1, self.gc-1], dim=2)
        indexs = torch.cat((indexs[1], indexs[0]), dim=2)
        self.index = indexs.view(inp).type(torch.LongTensor)
        self.expansion = expansion

    def forward(self, x):
        x_in = x
        x_out = x

        b, c, _, _ = x_in.size()
        y = self.avg_pool(x_in).view(b, c)
        y = self.fc(y).view(b, self.oup*self.exp, 1, 1)
        y = (y-0.5) * self.act_max

        n2, c2, h2, w2 = x_out.size()
        x2 = x_out[:,self.index,:,:]

        if self.exp == 4:
            a1, b1, a2, b2 = torch.split(y, self.oup, dim=1)

            a1 = a1 + self.init_a[0]
            a2 = a2 + self.init_a[1]

            b1 = b1 + self.init_b[0]
            b2 = b2 + self.init_b[1]

            z1 = x_out * a1 + x2 * b1
            z2 = x_out * a2 + x2 * b2

            out = torch.max(z1, z2)

        elif self.exp == 2:
            a1, b1 = torch.split(y, self.oup, dim=1)
            a1 = a1 + self.init_a[0]
            b1 = b1 + self.init_b[0]
            out = x_out * a1 + x2 * b1

        return out
  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Micronet是一种轻量级神经网络架构,它专为移动和嵌入式设备设计,具有高效的计算和内存需求以及良好的图像分类性能。 使用Micronet实现图像分类,首先需要使用合适的数据集进行训练。选择适合应用场景的数据集,并标注好每张图像对应的类别标签。然后,根据网络结构设计合适的神经网络模型。 Micronet提供了一些常见的轻量级神经网络模型,如Micronet-A、Micronet-B等,可以根据实际需求选择合适的模型。接下来,使用选定的Micronet模型搭建神经网络,并加载已标注好的数据集。通过对数据集进行训练,神经网络模型将逐渐学习到图像分类的特征。 训练过程中,可以使用一些优化算法进行网络参数的更新,如随机梯度下降(SGD)等。同时,可以使用一些数据增强技术增加数据集的多样性,提升模型的泛化能力。 训练完成后,可以使用测试数据集对模型进行评估,计算准确率、精确率、召回率等指标来评估模型的性能表现。 最后,将训练好的模型部署到实际应用中,输入待分类的图像数据,通过进行前向推理,即向前传播图像的特征,最终得到图像的分类结果。 总之,使用Micronet实战图像分类,需要进行模型选择、数据集准备、模型训练、模型评估和模型部署等步骤。通过合理选择模型和适当的数据处理方法,可以实现高效准确的图像分类任务。 ### 回答2: Micronet是一种轻量级的神经网络架构,它在计算和参数数量上具有出色的效率。使用Micronet实战,我们可以实现图像分类任务。 首先,我们需要准备一个标记好的图像分类数据集,例如包含多个类别的图像样本集合。然后,我们可以使用Python编程语言和深度学习框架如TensorFlow或PyTorch来构建Micronet模型。 在构建Micronet模型的过程中,我们可以使用一些轻量级的神经网络层,例如深度可分离卷积、批标准化和全局平均池化等操作。这些层的组合可以实现较少的计算和参数数量,并在保持良好性能的同时大大减少模型大小和计算成本。 当模型构建完成后,我们可以开始训练。在训练过程中,我们可以使用编程框架提供的优化算法,如随机梯度下降(SGD)或Adam,来调整模型参数以最小化损失函数。同时,我们还可以使用数据增强技术来扩充训练数据集,如随机裁剪、水平翻转和旋转等,以提高模型的鲁棒性和泛化能力。 训练完成后,我们可以使用测试集来评估模型的性能。通常可以使用准确率、精确率、召回率和F1分数等指标来评估模型的分类效果。 总结来说,使用Micronet实战实现图像分类涉及数据集准备、模型构建、训练和评估等步骤。通过构建轻量级、高效的神经网络模型,我们可以在保持较高分类性能的同时减少模型的计算和参数数量,进而在资源受限的环境下提供可行的解决方案。 ### 回答3: Micronet是一个用于轻量级图像分类任务的模型开发工具。在实际使用中,可以按照以下步骤使用Micronet实现图像分类。 首先,我们需要收集并准备训练数据集。一个好的数据集是成功进行图像分类任务的基础,所以我们需要确保数据集包含不同类别的图像样本,并且每个图像都有正确的标签。 接下来,我们可以使用Micronet提供的一些预训练模型作为基本模型,也可以根据任务需求自定义一个新的模型。预训练模型可以通过模型的名称或者选择一个预定义的模型结构进行加载。 然后,我们可以使用Micronet提供的API来编写代码,定义我们的模型架构。我们可以使用不同的层来构建我们的模型,包括卷积、池化和全连接层。 接着,我们需要将数据集加载到我们的代码中,并进行数据预处理。数据预处理可以包括图像增强、数据标准化等操作,以提高模型的准确度和鲁棒性。 在训练之前,我们需要定义损失函数和优化器。我们可以选择适合图像分类任务的损失函数,如交叉熵损失函数,并选择适合我们的模型和数据集的优化器,如随机梯度下降法(SGD)。 然后,我们可以通过训练我们的模型来优化模型参数。训练可以由多个epoch组成,每个epoch中,我们将训练集分批次输入到模型中,并根据定义的损失函数和优化器对模型进行更新。 最后,我们可以在测试集上评估我们的模型的性能。我们可以使用准确率、精确率、召回率等指标来评估模型对图像分类任务的表现。 通过以上步骤,我们可以使用Micronet实现图像分类任务。通过不断优化我们的模型和数据集,我们可以提高模型的准确度和鲁棒性,从而实现更好地图像分类结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万里鹏程转瞬至

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值