学习笔记——轻量型神经网络

前言

因为以后会用到,所以这块尝试看一下轻量型神经网络的代码,同样是写给小白的,不深究只教大家大致如何去看。而且MobileNetV3有两个版本,我们以small为例。

代码文件结构

在这里插入图片描述
其中MobileNetV3—small和large是网络结构搭建,MobileNetV3—factory是将两个结构整合到一起。layers是各个子模块的构建。其他是训练和测试文件。

MobileNetV3—small结构

在这里插入图片描述

MobileNetV3—small代码

整体代码

# Copyright 2019 Bisonai Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Implementation of paper Searching for MobileNetV3, https://arxiv.org/abs/1905.02244

MobileNetV3 Small
"""
import tensorflow as tf

from layers import ConvNormAct
from layers import Bneck
from layers import LastStage
from utils import _make_divisible
from utils import LayerNamespaceWrapper


class MobileNetV3(tf.keras.Model):
    def __init__(
            self,
            num_classes: int=1001,
            width_multiplier: float=1.0,
            name: str="MobileNetV3_Small",
            divisible_by: int=8,
            l2_reg: float=1e-5,
    ):
        super().__init__(name=name)

        # First layer
        self.first_layer = ConvNormAct(
            16,
            kernel_size=3,
            stride=2,
            padding=1,
            norm_layer="bn",
            act_layer="hswish",
            use_bias=False,
            l2_reg=l2_reg,
            name="FirstLayer",
        )

        # Bottleneck layers
        self.bneck_settings = [
            # k   exp   out  SE      NL         s
            [ 3,  16,   16,  True,   "relu",    2 ],
            [ 3,  72,   24,  False,  "relu",    2 ],
            [ 3,  88,   24,  False,  "relu",    1 ],
            [ 5,  96,   40,  True,   "hswish",  2 ],
            [ 5,  240,  40,  True,   "hswish",  1 ],
            [ 5,  240,  40,  True,   "hswish",  1 ],
            [ 5,  120,  48,  True,   "hswish",  1 ],
            [ 5,  144,  48,  True,   "hswish",  1 ],
            [ 5,  288,  96,  True,   "hswish",  2 ],
            [ 5,  576,  96,  True,   "hswish",  1 ],
            [ 5,  576,  96,  True,   "hswish",  1 ],
        ]

        self.bneck = tf.keras.Sequential(name="Bneck")
        for idx, (k, exp, out, SE, NL, s) in enumerate(self.bneck_settings):
            out_channels = _make_divisible(out * width_multiplier, divisible_by)
            exp_channels = _make_divisible(exp * width_multiplier, divisible_by)

            self.bneck.add(
                LayerNamespaceWrapper(
                    Bneck(
                        out_channels=out_channels,
                        exp_channels=exp_channels,
                        kernel_size=k,
                        stride=s,
                        use_se=SE,
                        act_layer=NL,
                    ),
                    name=f"Bneck{idx}")
            )

        # Last stage
        penultimate_channels = _make_divisible(576 * width_multiplier, divisible_by)
        last_channels = _make_divisible(1_280 * width_multiplier, divisible_by)

        self.last_stage = LastStage(
            penultimate_channels,
            last_channels,
            num_classes,
            l2_reg=l2_reg,
        )

    def call(self, input):
        x = self.first_layer(input)
        x = self.bneck(x)
        x = self.last_stage(x)
        return x

这段代码看着比较长,其实只是分三部分,因为子模块在layers都定义好了,直接调用就行了。所以只是类的重写,其实就是传参。下面我们挑主要介绍一下。

 self.first_layer = ConvNormAct(
            16,
            kernel_size=3,
            stride=2,
            padding=1,
            norm_layer="bn",
            act_layer="hswish",
            use_bias=False,
            l2_reg=l2_reg,
            name="FirstLayer",
        )

这是一层的定义,对应结构的:
在这里插入图片描述

 self.bneck_settings = [
            # k   exp   out  SE      NL         s
            [ 3,  16,   16,  True,   "relu",    2 ],
            [ 3,  72,   24,  False,  "relu",    2 ],
            [ 3,  88,   24,  False,  "relu",    1 ],
            [ 5,  96,   40,  True,   "hswish",  2 ],
            [ 5,  240,  40,  True,   "hswish",  1 ],
            [ 5,  240,  40,  True,   "hswish",  1 ],
            [ 5,  120,  48,  True,   "hswish",  1 ],
            [ 5,  144,  48,  True,   "hswish",  1 ],
            [ 5,  288,  96,  True,   "hswish",  2 ],
            [ 5,  576,  96,  True,   "hswish",  1 ],
            [ 5,  576,  96,  True,   "hswish",  1 ],
        ]

     

这块是对这几层的定义:
在这里插入图片描述
但是请注意这并不是真正的层的定义,只不过是把好多层参数写在一起,方便后面一起传参,或者说类的重写。

 self.bneck = tf.keras.Sequential(name="Bneck")
        for idx, (k, exp, out, SE, NL, s) in enumerate(self.bneck_settings):
            out_channels = _make_divisible(out * width_multiplier, divisible_by)
            exp_channels = _make_divisible(exp * width_multiplier, divisible_by)

这块大家可能有点费解,这是一个重要参数:宽度因子。

MobileNet本身的网络结构已经比较小并且执行延迟较低,但为了适配更定制化的场景,MobileNet提供了称为宽度因子(Width Multiplier)的超参数给我们调整。宽度因子在MobileNetV1、V2、V3都可以运用。
通过宽度因子,可以调整神经网络中间产生的特征的大小,调整的是特征数据通道数大小,从而调整了运算量的大小。
其实就是一个调节参数量的因子。用它乘以各个参数就能调节网络大小。
然后是这几层的定义:

 self.bneck.add(
                LayerNamespaceWrapper(
                    Bneck(
                        out_channels=out_channels,
                        exp_channels=exp_channels,
                        kernel_size=k,
                        stride=s,
                        use_se=SE,
                        act_layer=NL,
                    ),
                    name=f"Bneck{idx}")
            )

然后是最后几层的定义:
)

 penultimate_channels = _make_divisible(576 * width_multiplier, divisible_by)
        last_channels = _make_divisible(1_280 * width_multiplier, divisible_by)

        self.last_stage = LastStage(
            penultimate_channels,
            last_channels,
            num_classes,
            l2_reg=l2_reg,
        )

然后就是让整体网络一步步计算:

 def call(self, input):
        x = self.first_layer(input)
        x = self.bneck(x)
        x = self.last_stage(x)
        return x

最后

轻量型网络的MobileNetV3—small就写到这了,希望能帮到大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值