MnasNet 网络原理与 Tensorflow2.0 实现(with SE module)

介绍

设计移动设备上的 CNN 模型具有挑战性,需要保证模型小速度快且准确率高,人为地权衡这三方面很困难,有太多种可能结构需要考虑。Google 大脑 AutoML 组提出了一种用于设计资源受限的移动 CNN 模型的神经网络结构搜索方法,将时间延迟信息明确地整合到主要目标中,这样搜索模型可以识别一个网络是否很好地平衡了准确率和时间延迟。

在《MnasNet: Platform-Aware Neural Architecture Search for Mobile》一文中,作者探索了一种使用强化学习设计移动端模型的自动化神经架构搜索方法。为了处理移动端速度限制,将速度信息纳入搜索算法的主要奖励函数中,以便搜索可以识别一个在准确率和速度之间实现良好平衡的模型。如此,MnasNet 能够找到运行速度比 MobileNet V2 快 1.5 倍、比 NASNet 快 2.4 倍的型号,同时达到同样的 ImageNet top-1 准确率。

网络介绍

总体流程

相比于之前的搜索策略,在这里使用的搜索方法有两点不同:

  • 1、在这里我们使用的是同时考虑准确率和延迟的多目标优化;
  • 2、直接测量实际中在手机端的推理延迟而不是使用 FLOPS 间接表示。

总体流程主要包括三个部分:一个基于 RNN 的学习和采样模型架构控制器,一个建立和训练模型以获得准确率的训练器,以及一个使用 TensorFlow Lite 测量真实手机上模型速度的推理引擎,作者制定了一个多目标优化问题,旨在实现高准确率和高速,并利用带有定制奖励函数的强化学习算法来寻找帕累托最优解。
在这里插入图片描述

  • 首先我们计算 latency (来自于训练时候的时间) 以及网络准确度,共同得到一个 reward,这个 reward 即代表了我们要权衡的两个东西,运算时间和网络准确度;
  • 然后将 reward 返回到一个 controller 中, 这个 controller 应该就是决定了如何对网络进行重构和择优。

优化目标

一般我们会选定一个目标延迟(最大延迟),在延迟不超过这个最大延迟的情况下尽可能提高所选模型的准确率,即:
在这里插入图片描述
但是这种方法只是最大化了单一的变量而没有提供多变量的帕累托最优解,因此,我们将优化目标定义为:
在这里插入图片描述
α 和 β 的确定方法:力求在不同的准确率-延迟情况下达到(近乎)相同的 reward。

举例来说,假设 M1 模型的延迟为 l,准确率为 a;M2 模型的延迟为 2l,准确率为 a(1+5%),那么我们应该满足:
R e w a r d ( M 2 ) = a ( 1 + 5 % ) ( 2 l T ) β ≈ R e w a r d ( M 1 ) = a ( l T ) β Reward(M2)=a(1+5\%)(\frac{2l}{T})^{\beta}\approx Reward(M1)=a(\frac{l}{T})^{\beta} Reward(M2)=a(1+5%)(T2l)βReward(M1)=a(Tl)β
解得 β=-0.07。在 MnasNet 论文中,作者使用的是 α=-0.07、β=-0.07 的情况。

α=0、β=-1 时:当 LAT(m)>T 时,reward 不可能有 ACC(m),即不可能大于 LAT(m)≤T 的情况,此时我们称延迟约束为硬约束。
α=-0.07、β=-0.07时:称延迟约束为软约束。

应用神经架构搜索

在这里插入图片描述
在搜索最优神经架构过程中,我们对以下几个方面进行了寻优操作(基于强化学习):

  • 卷积操作:标准卷积 or 深度级卷积 or MobileNet V2 中的 BottleNeck block;
  • 卷积核尺寸:3x3 or 5x5;
  • 跳跃操作:无跳跃 or 池化跳跃 or 残差跳跃;
  • SE Ratio:0 or 0.25;
  • 卷积核个数:基于 MobileNet V2,是 MobileNet V2 中的 {0.75 or 1.0 or 1.25} 倍;
  • 每个模块内的层数:基于 MobileNet V2,相比于 MobileNet V2 中的层数 {+1 or +0 or -1}。

Mnasnet 结构

在这里插入图片描述
值得注意的地方有:

  • 1、上图中 MBConv 后面跟的数字表示扩展系数(3 或 6),即经过第一个 Conv 1x1 后的通道数与经过前的通道数之比。
  • 2、与 MobileNet V2 类似,当 MBConv 模块是第一次出现时,其中 DwiseConv 的步长可以是任意的(通常是 1 或 2),但后面重复该模块时步长必须设为 1。(步长是 2 时会对 feature map 的形状产生影响)
  • 3、和 MobileNet V2 不同的是,每个 MBConv 模块中的第二个 Conv 1x1 中的卷积核个数并不需要等于输入的通道数,从 (a) 图中也能看出这一点。
  • 4、和 MobileNet V2 一样,Mnasnet 用的激活函数也是 ReLU6(除了之后提及的 SE 模块),且 MBConv 模块中的第二个 Conv 1x1 之后不设置激活函数。

含有 SE 模块的 Mnasnet 结构

开发者在 ImageNet 分类和 COCO 物体检测上测试了这种方法的有效性。下图所示为该网络在 ImageNet 上的结果。
在这里插入图片描述
在相同的准确度下,MnasNet 模型的运行速度比手工设计的最先进的 MobileNetV2 模型快 1.5 倍,并且比 NASNet 快 2.4 倍,而 NASNet 也是使用架构搜索的方法。在应用压缩和激活优化方法后,MnasNet+SE 模型实现了 76.1% 的 ResNet-50 level top-1 准确率,并且参数数量是 MnasNet 的 1/19,乘加运算数量是 MnasNet 的 1/10。

MnasNet+SE 模型的结构如下图所示:
在这里插入图片描述
【注】这里用的激活函数都是 ReLU。

代码实现

import tensorflow as tf

def conv_bn(x, filters, kernel_size, strides=1, activation=True):
    x = tf.keras.layers.Conv2D(filters=filters, 
                               kernel_size=kernel_size, 
                               strides=strides, 
                               padding='SAME')(x)
    x = tf.keras.layers.BatchNormalization()(x)
    if activation:
        x = tf.keras.layers.Activation('relu')(x)
        
    return x

def depthwiseConv_bn(x, kernel_size, strides):

    x = tf.keras.layers.DepthwiseConv2D(kernel_size, 
                                        padding='same', 
                                        strides=strides)(x)
    x = tf.keras.layers.BatchNormalization()(x)
    x = tf.keras.layers.Activation('relu')(x)

    return x

def sepConv_bn_noskip(x, filters, kernel_size, strides=1):
    
    x = depthwiseConv_bn(x, kernel_size=kernel_size, strides=strides)
    x = conv_bn(x, filters=filters, kernel_size=1, strides=1)
    
    return x

def Squeeze_excitation_layer(x):
    
    inputs = x
    squeeze = inputs.shape[-1]/2
    excitation = inputs.shape[-1]
    x = tf.keras.layers.GlobalAveragePooling2D()(x)
    x = tf.keras.layers.Dense(squeeze)(x)
    x = tf.keras.layers.Activation('relu')(x)
    x = tf.keras.layers.Dense(excitation)(x)
    x = tf.keras.layers.Activation('sigmoid')(x)
    x = tf.keras.layers.Reshape((1, 1, excitation))(x)
    x = inputs * x

    return x

def MBConv_idskip(x, filters, kernel_size, strides, t, SE=False):
    
    x_input = x
    
    x = conv_bn(x, filters=x.shape[-1] * t, kernel_size=1, strides=1)
    x = depthwiseConv_bn(x, kernel_size=kernel_size, strides=strides)
    if SE:
        x = Squeeze_excitation_layer(x)
    x = conv_bn(x, filters=filters, kernel_size=1, strides=1, activation=False)
    
    if strides==1 and x.shape[3] == x_input.shape[3]:
        return  tf.keras.layers.add([x_input, x])
    else: 
        return x

def MBConv(x, filters, kernel_size, strides, t, n, SE=False):
    
    x = MBConv_idskip(x, filters, kernel_size, strides, t, SE)
    
    for _ in range(1, n):
        x = MBConv_idskip(x, filters, kernel_size, strides=1, t=t, SE=SE)
        
    return x

def MnasNet(x, n_classes=1000):
    
    x = conv_bn(x, 32, kernel_size=3, strides=2)
    x = sepConv_bn_noskip(x, filters=16, kernel_size=3, strides=1)
    x = MBConv(x, filters=24, kernel_size=3, strides=2, t=6, n=2)
    x = MBConv(x, filters=40, kernel_size=5, strides=2, t=3, n=3, SE=True)
    x = MBConv(x, filters=80, kernel_size=3, strides=2, t=6, n=4)
    x = MBConv(x, filters=96, kernel_size=3, strides=1, t=6, n=2, SE=True)
    x = MBConv(x, filters=192, kernel_size=5, strides=2, t=6, n=3, SE=True)
    x = MBConv(x, filters=320, kernel_size=3, strides=1, t=6, n=1)
    
    x = conv_bn(x, filters=1152, kernel_size=1, strides=1)
    x = tf.keras.layers.GlobalAveragePooling2D()(x)
    predictions = tf.keras.layers.Dense(n_classes, activation='softmax')(x)

    return predictions

inputs = np.zeros((1, 224, 224, 3), np.float32)
MnasNet(inputs).shape
TensorShape([1, 1000])

参考资料

Mnasnet论文解析及开源实现

Circle Loss[^1] 是一种改进的深度学习损失函数,特别是在人脸识别领域,它相对于传统的Triplet Loss[^2]在收敛过程和性能上有优势。Circle Loss的设计旨在解决Triplet Loss中容易产生的"easy negatives"(即距离较远但分类标签相同的样本)问题,通过限制正负样本之间的角度,使得模型更加关注那些难以区分的样本。 参数`m`通常代表margin,控制了正负样本间的界限,`gamma`则是指数衰减因子,影响决策边界的变化速度。对于 CircleLoss(m=0.25, gamma=32),这意味着选择了一个较小的margin值(0.25),这样可以使模型对更小的角度变化敏感,而较大的gamma值则可能导致决策边界更陡峭。 具体实现时,CircleLoss的计算公式如引用[1]所示: \[ L_{ce} = -\log\frac{exp(y_i)}{\sum_{i=1}^{i=n}exp(y_i)} \] 其中,\(y_i\)是每个样本对应的标签,如果样本是正样本,则标签接近1,如果是负样本则靠近-1。当使用CircleLoss时,网络会尝试最大化这些标签的差异,使正样本聚类在一起,而远离负样本,形成一个理想的圆形分布。 如果你想要在实际项目中使用CircleLoss,你可以通过以下Python代码片段实现,假设你已经安装了相应的库(如PyTorch或Keras): ```python import torch.nn as nn class CircleLoss(nn.Module): def __init__(self, margin=0.25, gamma=32): super(CircleLoss, self).__init__() self.margin = margin self.gamma = gamma def forward(self, anchor, positive, negative): # 假设anchor, positive, negative是预定义的特征张量,它们的维度应该包括类别信息 euclidean_distance = F.pairwise_distance(anchor, positive) pdist = euclidean_distance - self.margin pos_loss = torch.clamp(self.gamma * (1 + pdist), min=0).detach().sigmoid() neg_loss = torch.clamp(self.gamma * (-pdist), min=0).detach().sigmoid() num_positives = torch.sum(pos_loss > 0.5) num_negatives = torch.sum(neg_loss > 0.5) return -(num_positives * torch.log(pos_loss) + num_negatives * torch.log(1 - neg_loss)) ``` 请注意,上述代码仅作为概念展示,实际应用可能需要根据具体框架和数据处理方式进行调整。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cofisher

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

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

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

打赏作者

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

抵扣说明:

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

余额充值