最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

python神經(jīng)網(wǎng)絡(luò)Keras?GhostNet模型的實現(xiàn)

 更新時間:2022年05月07日 16:15:21   作者:Bubbliiiing  
這篇文章主要為大家介紹了python神經(jīng)網(wǎng)絡(luò)Keras?GhostNet模型的復現(xiàn)詳解,

什么是GhostNet模型

GhostNet是華為諾亞方舟實驗室提出來的一個非常有趣的網(wǎng)絡(luò),我們一起來學習一下。

2020年,華為新出了一個輕量級網(wǎng)絡(luò),命名為GhostNet。

在優(yōu)秀CNN模型中,特征圖存在冗余是非常重要的。如圖所示,這個是對ResNet-50第一個殘差塊特征圖進行可視化的結(jié)果,當我們給一個神經(jīng)網(wǎng)絡(luò)輸入一張圖片時,我們可以獲得特別多的特征圖。

利用小扳手連接起來的兩幅特征圖,它們的相似性就特別高,這個就是神經(jīng)網(wǎng)絡(luò)中存在的特征圖冗雜的情況。

作者將相似的特征圖認為是彼此的Ghost,所以這個網(wǎng)絡(luò)就叫做GhostNet(誤)。

在GhostNet這篇論文里面,作者認為可以使用一些計算量更低(Cheap Operations)的操作去生成這些冗余的特征圖,這樣就可以在保證良好檢測效果的情況下,減少模型的參數(shù)量與提高模型的執(zhí)行速度。

源碼下載

GhostNet模型的實現(xiàn)思路

1、Ghost Module

通過上述的介紹,我們了解到了,GhostNet的核心思想就是使用一些計算量更低(Cheap Operations)的操作去生成這些冗余的特征圖。

在論文中,作者設(shè)計了一個名為Ghost Module的模塊,他的功能是代替普通卷積。

Ghost Module將普通卷積分為兩部分,首先進行一個普通的1x1卷積,這是一個少量卷積,比如正常使用32通道的卷積,這里就用16通道的卷積,這個1x1卷積的作用類似于特征整合,生成輸入特征層的特征濃縮。

然后我們再進行深度可分離卷積,這個深度可分離卷積是逐層卷積,它也就是我們上面提到的Cheap Operations。它利用上一步獲得的特征濃縮生成Ghost特征圖。

因此,如果我們從整體上去看這個Ghost Module,它其實就是兩步簡單思想的匯總:

1、利用1x1卷積獲得輸入特征的必要特征濃縮。

2、利用深度可分離卷積獲得特征濃縮的相似特征圖(Ghost)。

Ghost Module的實現(xiàn)代碼如下:

def _ghost_module(inputs, exp, kernel, dw_kernel, ratio, strides=1,
                    padding='same',use_bias=False, relu=True):
    output_channels = math.ceil(exp * 1.0 / ratio)
    x = Conv2D(output_channels, kernel, strides=strides, padding=padding, use_bias=use_bias)(inputs)
    x = BatchNormalization()(x)
    if relu:
        x = Activation('relu')(x)
    dw = DepthwiseConv2D(dw_kernel, strides, padding=padding, depth_multiplier=ratio-1, use_bias=use_bias)(x)
    dw = BatchNormalization()(dw)
    if relu:
        dw = Activation('relu')(dw)
    x = Concatenate(axis=-1)([x,dw])
    x = Lambda(slices, arguments={'n':exp})(x)
    return x

2、Ghost Bottlenecks

Ghost Bottlenecks是由Ghost Module組成的瓶頸結(jié)構(gòu),就像這樣。

其實本質(zhì)上就是用Ghost Module,來代替瓶頸結(jié)構(gòu)里面的普通卷積。

Ghost Bottlenecks可以分為兩個部分,分別是主干部分和殘差邊部分,包含Ghost Module的,我們稱它為主干部分。

Ghost Bottlenecks有兩個種類,如下圖所示,當我們需要對特征層的寬高進行壓縮的時候,我們會設(shè)置這個Ghost Bottlenecks的Stride=2,即步長為2。

此時我們會Bottlenecks里面多添加一些卷積層,在主干部分里,我們會在兩個Ghost Module中添加一個步長為2x2的深度可分離卷積進行特征層的寬高壓縮。在殘差邊部分,我們也會添加上一個步長為2x2的深度可分離卷積和1x1的普通卷積。

Ghost Bottlenecks的實現(xiàn)代碼如下:

def _ghost_bottleneck(inputs, output_channel, hidden_channel, kernel, ghost_kernel, strides, ratio, squeeze):
    input_shape = K.int_shape(inputs)       # 獲取輸入張量的尺寸
    x = _ghost_module(inputs, hidden_channel, [1,1], ghost_kernel, ratio)
    if strides > 1:
        x = DepthwiseConv2D(kernel, strides, padding='same', depth_multiplier=1, use_bias=False)(x)
        x = BatchNormalization()(x)
    if squeeze:
        x = _squeeze(x, hidden_channel, 4)
    x = _ghost_module(x, output_channel, [1,1], ghost_kernel, ratio, relu=False)
    if strides == 1 and input_shape[-1] == output_channel:
        res = inputs
    else:
        res = DepthwiseConv2D(kernel, strides=strides, padding='same', depth_multiplier=1, use_bias=False)(inputs)
        res = BatchNormalization()(res)
        res = Conv2D(output_channel, (1, 1), padding='same', strides=(1, 1), use_bias=False)(res)
        res = BatchNormalization()(res)
    x = Add()([res, x])
    return x

3、Ghostnet的構(gòu)建

整個Ghostnet的構(gòu)建方式如列表所示:

可以看到,整個Ghostnet都是由Ghost Bottlenecks進行組成的。

當一張圖片輸入到Ghostnet當中時,我們首先進行一個16通道的普通1x1卷積塊(卷積+標準化+激活函數(shù))。

之后我們就開始Ghost Bottlenecks的堆疊了,利用Ghost Bottlenecks,我們最終獲得了一個7x7x160的特征層(當輸入是224x224x3的時候)。

然后我們會利用一個1x1的卷積塊進行通道數(shù)的調(diào)整,此時我們可以獲得一個7x7x960的特征層。

之后我們進行一次全局平均池化,然后再利用一個1x1的卷積塊進行通道數(shù)的調(diào)整,獲得一個1x1x1280的特征層。

然后平鋪后進行全連接就可以進行分類了。

GhostNet的代碼構(gòu)建

1、模型代碼的構(gòu)建

GhostNet的實現(xiàn)代碼如下,該代碼是Ghostnet在YoloV4上的應用,可以參考一下:

import math
import warnings
import numpy as np
import tensorflow as tf
from keras import backend as K
from keras.applications.imagenet_utils import decode_predictions
from keras.initializers import random_normal
from keras.layers import (Activation, Add, BatchNormalization, Concatenate,
                          Conv2D, DepthwiseConv2D, GlobalAveragePooling2D, 
                          Lambda, Multiply, Reshape)
def slices(dw, n):
    return dw[:,:,:,:n]
def _make_divisible(v, divisor, min_value=None):
    if min_value is None:
        min_value = divisor
    new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
    if new_v < 0.9 * v:
        new_v += divisor
    return new_v
def _squeeze(inputs, hidden_channel, ratio, block_id, sub_block_id):
    x = GlobalAveragePooling2D()(inputs)
    x = Reshape((1,1,-1))(x)
    x = Conv2D(_make_divisible(hidden_channel/ratio, 4), (1,1), strides=(1,1), padding='same', kernel_initializer=random_normal(stddev=0.02),
            name="blocks."+str(block_id)+"."+str(sub_block_id)+".se.conv_reduce")(x)
    x = Activation('relu')(x)
    x = Conv2D(hidden_channel, (1,1),strides=(1,1), padding='same', kernel_initializer=random_normal(stddev=0.02),
            name="blocks."+str(block_id)+"."+str(sub_block_id)+".se.conv_expand")(x)
    x = Activation('hard_sigmoid')(x)
    x = Multiply()([inputs, x])    # inputs和x逐元素相乘
    return x
def _ghost_module(inputs, exp, ratio, block_id, sub_block_id, part, kernel_size=1, dw_size=3, stride=1, relu=True):
    output_channels = math.ceil(exp * 1.0 / ratio)
    x = Conv2D(output_channels, kernel_size, strides=stride, padding="same", use_bias=False, kernel_initializer=random_normal(stddev=0.02),
            name="blocks."+str(block_id)+"."+str(sub_block_id)+".ghost"+str(part)+".primary_conv.0")(inputs)
    x = BatchNormalization(name="blocks."+str(block_id)+"."+str(sub_block_id)+".ghost"+str(part)+".primary_conv.1")(x)
    if relu:
        x = Activation('relu')(x)
    dw = DepthwiseConv2D(dw_size, 1, padding="same", depth_multiplier=ratio-1, use_bias=False, depthwise_initializer=random_normal(stddev=0.02), 
            name="blocks."+str(block_id)+"."+str(sub_block_id)+".ghost"+str(part)+".cheap_operation.0")(x)
    dw = BatchNormalization(name="blocks."+str(block_id)+"."+str(sub_block_id)+".ghost"+str(part)+".cheap_operation.1")(dw)
    if relu:
        dw = Activation('relu')(dw)
    x = Concatenate(axis=-1)([x,dw])
    x = Lambda(slices, arguments={'n':exp})(x)
    return x
def _ghost_bottleneck(inputs, output_channel, hidden_channel, kernel, strides, ratio, squeeze, block_id, sub_block_id):
    input_shape = K.int_shape(inputs)
    x = _ghost_module(inputs, hidden_channel, ratio, block_id, sub_block_id, 1)
    if strides > 1:
        x = DepthwiseConv2D(kernel, strides, padding='same', depth_multiplier=1, use_bias=False, depthwise_initializer=random_normal(stddev=0.02),
                name="blocks."+str(block_id)+"."+str(sub_block_id)+".conv_dw")(x)
        x = BatchNormalization(name="blocks."+str(block_id)+"."+str(sub_block_id)+".bn_dw")(x)
    if squeeze:
        x = _squeeze(x, hidden_channel, 4, block_id, sub_block_id)
    x = _ghost_module(x, output_channel, ratio, block_id, sub_block_id, 2, relu=False)
    if strides == 1 and input_shape[-1] == output_channel:
        res = inputs
    else:
        res = DepthwiseConv2D(kernel, strides=strides, padding='same', depth_multiplier=1, use_bias=False, depthwise_initializer=random_normal(stddev=0.02),
                name="blocks."+str(block_id)+"."+str(sub_block_id)+".shortcut.0")(inputs)
        res = BatchNormalization(name="blocks."+str(block_id)+"."+str(sub_block_id)+".shortcut.1")(res)
        res = Conv2D(output_channel, (1, 1), padding='same', strides=(1, 1), use_bias=False, kernel_initializer=random_normal(stddev=0.02),
                name="blocks."+str(block_id)+"."+str(sub_block_id)+".shortcut.2")(res)
        res = BatchNormalization(name="blocks."+str(block_id)+"."+str(sub_block_id)+".shortcut.3")(res)
    x = Add()([res, x])
    return x
def Ghostnet(inputs):
    x = Conv2D(16, (3, 3), padding="same", strides=(2, 2), use_bias=False, kernel_initializer=random_normal(stddev=0.02), name="conv_stem")(inputs)
    x = BatchNormalization(name="bn1")(x)
    x = Activation('relu')(x)
    x = _ghost_bottleneck(x, 16, 16, (3, 3), strides=1, ratio=2, squeeze=False, block_id=0, sub_block_id=0)
    x = _ghost_bottleneck(x, 24, 48, (3, 3), strides=2, ratio=2, squeeze=False, block_id=1, sub_block_id=0)
    x = _ghost_bottleneck(x, 24, 72, (3, 3), strides=1, ratio=2, squeeze=False, block_id=2, sub_block_id=0)
    x = _ghost_bottleneck(x, 40, 72, (5, 5), strides=2, ratio=2, squeeze=True, block_id=3, sub_block_id=0)
    x = _ghost_bottleneck(x, 40, 120, (5, 5), strides=1, ratio=2, squeeze=True, block_id=4, sub_block_id=0)
    feat1 = x
    x = _ghost_bottleneck(x, 80, 240, (3, 3), strides=2, ratio=2, squeeze=False, block_id=5, sub_block_id=0)
    x = _ghost_bottleneck(x, 80, 200, (3, 3), strides=1, ratio=2, squeeze=False, block_id=6, sub_block_id=0)
    x = _ghost_bottleneck(x, 80, 184, (3, 3), strides=1, ratio=2, squeeze=False, block_id=6, sub_block_id=1)
    x = _ghost_bottleneck(x, 80, 184, (3, 3), strides=1, ratio=2, squeeze=False, block_id=6, sub_block_id=2)
    x = _ghost_bottleneck(x, 112, 480, (3, 3), strides=1, ratio=2, squeeze=True, block_id=6, sub_block_id=3)
    x = _ghost_bottleneck(x, 112, 672, (3, 3), strides=1, ratio=2, squeeze=True, block_id=6, sub_block_id=4)
    feat2 = x
    x = _ghost_bottleneck(x, 160, 672, (5, 5), strides=2, ratio=2, squeeze=True, block_id=7, sub_block_id=0)
    x = _ghost_bottleneck(x, 160, 960, (5, 5), strides=1, ratio=2, squeeze=False, block_id=8, sub_block_id=0)
    x = _ghost_bottleneck(x, 160, 960, (5, 5), strides=1, ratio=2, squeeze=True, block_id=8, sub_block_id=1)
    x = _ghost_bottleneck(x, 160, 960, (5, 5), strides=1, ratio=2, squeeze=False, block_id=8, sub_block_id=2)
    x = _ghost_bottleneck(x, 160, 960, (5, 5), strides=1, ratio=2, squeeze=True, block_id=8, sub_block_id=3)
    feat3 = x
    return feat1,feat2,feat3

2、Yolov4上的應用

作為一個輕量級網(wǎng)絡(luò),我把Ghostnet和Mobilenet放在一起,作為Yolov4的主干網(wǎng)絡(luò)進行特征提取。

對于yolov4來講,我們需要利用主干特征提取網(wǎng)絡(luò)獲得的三個有效特征進行加強特征金字塔的構(gòu)建。

我們通過上述代碼可以取出三個有效特征層,我們可以利用這三個有效特征層替換原來yolov4主干網(wǎng)絡(luò)CSPdarknet53的有效特征層。

為了進一步減少參數(shù)量,我們可以使用深度可分離卷積代替yoloV3中用到的普通卷積。

最終Ghostnet-Yolov4的構(gòu)建代碼如下:

from functools import wraps
from keras import backend as K
from keras.initializers import random_normal
from keras.layers import (Activation, BatchNormalization, Concatenate, Conv2D,
                          DepthwiseConv2D, Input, Lambda, MaxPooling2D,
                          UpSampling2D)
from keras.layers.normalization import BatchNormalization
from keras.models import Model
from keras.regularizers import l2
from utils.utils import compose
from nets.ghostnet import Ghostnet
from nets.mobilenet_v1 import MobileNetV1
from nets.mobilenet_v2 import MobileNetV2
from nets.mobilenet_v3 import MobileNetV3
from nets.yolo_training import yolo_loss
def relu6(x):
    return K.relu(x, max_value=6)
#------------------------------------------------------#
#   單次卷積DarknetConv2D
#   如果步長為2則自己設(shè)定padding方式。
#------------------------------------------------------#
@wraps(Conv2D)
def DarknetConv2D(*args, **kwargs):
    darknet_conv_kwargs = {'kernel_initializer' : random_normal(stddev=0.02), 'kernel_regularizer': l2(5e-4)}
    darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same'
    darknet_conv_kwargs.update(kwargs)
    return Conv2D(*args, **darknet_conv_kwargs)
#---------------------------------------------------#
#   卷積塊 -> 卷積 + 標準化 + 激活函數(shù)
#   DarknetConv2D + BatchNormalization + Relu6
#---------------------------------------------------#
def DarknetConv2D_BN_Leaky(*args, **kwargs):
    no_bias_kwargs = {'use_bias': False}
    no_bias_kwargs.update(kwargs)
    return compose( 
        DarknetConv2D(*args, **no_bias_kwargs),
        BatchNormalization(),
        Activation(relu6))
#---------------------------------------------------#
#   深度可分離卷積塊
#   DepthwiseConv2D + BatchNormalization + Relu6
#---------------------------------------------------#
def _depthwise_conv_block(inputs, pointwise_conv_filters, alpha = 1,
                          depth_multiplier=1, strides=(1, 1)):
    pointwise_conv_filters = int(pointwise_conv_filters * alpha)
    x = DepthwiseConv2D((3, 3), depthwise_initializer=random_normal(stddev=0.02),
                        padding='same',
                        depth_multiplier=depth_multiplier,
                        strides=strides,
                        use_bias=False)(inputs)
    x = BatchNormalization()(x)
    x = Activation(relu6)(x)
    x = DarknetConv2D(pointwise_conv_filters, (1, 1), 
                    padding='same',
                    use_bias=False,
                    strides=(1, 1))(x)
    x = BatchNormalization()(x)
    return Activation(relu6)(x)
#---------------------------------------------------#
#   進行五次卷積
#---------------------------------------------------#
def make_five_convs(x, num_filters):
    # 五次卷積
    x = DarknetConv2D_BN_Leaky(num_filters, (1,1))(x)
    x = _depthwise_conv_block(x, num_filters*2,alpha=1)
    x = DarknetConv2D_BN_Leaky(num_filters, (1,1))(x)
    x = _depthwise_conv_block(x, num_filters*2,alpha=1)
    x = DarknetConv2D_BN_Leaky(num_filters, (1,1))(x)
    return x
#---------------------------------------------------#
#   Panet網(wǎng)絡(luò)的構(gòu)建,并且獲得預測結(jié)果
#---------------------------------------------------#
def yolo_body(input_shape, anchors_mask, num_classes, backbone="mobilenetv1", alpha=1):
    inputs = Input(input_shape)
    #---------------------------------------------------#   
    #   生成mobilnet的主干模型,獲得三個有效特征層。
    #---------------------------------------------------#
    if backbone=="mobilenetv1":
        #---------------------------------------------------#   
        #   52,52,256;26,26,512;13,13,1024
        #---------------------------------------------------#
        feat1,feat2,feat3 = MobileNetV1(inputs, alpha=alpha)
    elif backbone=="mobilenetv2":
        #---------------------------------------------------#   
        #   52,52,32;26,26,92;13,13,320
        #---------------------------------------------------#
        feat1,feat2,feat3 = MobileNetV2(inputs, alpha=alpha)
    elif backbone=="mobilenetv3":
        #---------------------------------------------------#   
        #   52,52,40;26,26,112;13,13,160
        #---------------------------------------------------#
        feat1,feat2,feat3 = MobileNetV3(inputs, alpha=alpha)
    elif backbone=="ghostnet":
        #---------------------------------------------------#   
        #   52,52,40;26,26,112;13,13,160
        #---------------------------------------------------#
        feat1,feat2,feat3 = Ghostnet(inputs)
    else:
        raise ValueError('Unsupported backbone - `{}`, Use mobilenetv1, mobilenetv2, mobilenetv3, ghostnet.'.format(backbone))
    P5 = DarknetConv2D_BN_Leaky(int(512* alpha), (1,1))(feat3)
    P5 = _depthwise_conv_block(P5, int(1024* alpha))
    P5 = DarknetConv2D_BN_Leaky(int(512* alpha), (1,1))(P5)
    maxpool1 = MaxPooling2D(pool_size=(13,13), strides=(1,1), padding='same')(P5)
    maxpool2 = MaxPooling2D(pool_size=(9,9), strides=(1,1), padding='same')(P5)
    maxpool3 = MaxPooling2D(pool_size=(5,5), strides=(1,1), padding='same')(P5)
    P5 = Concatenate()([maxpool1, maxpool2, maxpool3, P5])
    P5 = DarknetConv2D_BN_Leaky(int(512* alpha), (1,1))(P5)
    P5 = _depthwise_conv_block(P5, int(1024* alpha))
    P5 = DarknetConv2D_BN_Leaky(int(512* alpha), (1,1))(P5)
    P5_upsample = compose(DarknetConv2D_BN_Leaky(int(256* alpha), (1,1)), UpSampling2D(2))(P5)
    P4 = DarknetConv2D_BN_Leaky(int(256* alpha), (1,1))(feat2)
    P4 = Concatenate()([P4, P5_upsample])
    P4 = make_five_convs(P4,int(256* alpha))
    P4_upsample = compose(DarknetConv2D_BN_Leaky(int(128* alpha), (1,1)), UpSampling2D(2))(P4)
    P3 = DarknetConv2D_BN_Leaky(int(128* alpha), (1,1))(feat1)
    P3 = Concatenate()([P3, P4_upsample])
    P3 = make_five_convs(P3,int(128* alpha))
    #---------------------------------------------------#
    #   第三個特征層
    #   y3=(batch_size,52,52,3,85)
    #---------------------------------------------------#
    P3_output = _depthwise_conv_block(P3, int(256* alpha))
    P3_output = DarknetConv2D(len(anchors_mask[0])*(num_classes+5), (1,1))(P3_output)
    P3_downsample = _depthwise_conv_block(P3, int(256* alpha), strides=(2,2))
    P4 = Concatenate()([P3_downsample, P4])
    P4 = make_five_convs(P4,int(256* alpha))
    #---------------------------------------------------#
    #   第二個特征層
    #   y2=(batch_size,26,26,3,85)
    #---------------------------------------------------#
    P4_output = _depthwise_conv_block(P4, int(512* alpha))
    P4_output = DarknetConv2D(len(anchors_mask[1])*(num_classes+5), (1,1))(P4_output)
    P4_downsample = _depthwise_conv_block(P4, int(512* alpha), strides=(2,2))
    P5 = Concatenate()([P4_downsample, P5])
    P5 = make_five_convs(P5,int(512* alpha))
    #---------------------------------------------------#
    #   第一個特征層
    #   y1=(batch_size,13,13,3,85)
    #---------------------------------------------------#
    P5_output = _depthwise_conv_block(P5, int(1024* alpha))
    P5_output = DarknetConv2D(len(anchors_mask[2])*(num_classes+5), (1,1))(P5_output)
    return Model(inputs, [P5_output, P4_output, P3_output])

以上就是python神經(jīng)網(wǎng)絡(luò)Keras GhostNet模型的復現(xiàn)詳解的詳細內(nèi)容,更多關(guān)于Keras GhostNet模型復現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

永川市| 类乌齐县| 黎城县| 股票| 苏尼特左旗| 车险| 连云港市| 博野县| 吉水县| 沈阳市| 尼玛县| 江达县| 德惠市| 彩票| 通许县| 平泉县| 富阳市| 福建省| 克拉玛依市| 手游| 文化| 民乐县| 西贡区| 四平市| 桦川县| 四子王旗| 万州区| 县级市| 古交市| 乌什县| 衡阳县| 呈贡县| 湟中县| 铜山县| 吴旗县| 蒙阴县| 武城县| 庄浪县| 九龙坡区| 阳春市| 锡林郭勒盟|