您现在的位置是:网站首页> 编程资料编程资料
python神经网络Keras GhostNet模型的实现_python_
2023-05-26
376人已围观
简介 python神经网络Keras GhostNet模型的实现_python_
什么是GhostNet模型
GhostNet是华为诺亚方舟实验室提出来的一个非常有趣的网络,我们一起来学习一下。
2020年,华为新出了一个轻量级网络,命名为GhostNet。
在优秀CNN模型中,特征图存在冗余是非常重要的。如图所示,这个是对ResNet-50第一个残差块特征图进行可视化的结果,当我们给一个神经网络输入一张图片时,我们可以获得特别多的特征图。

利用小扳手连接起来的两幅特征图,它们的相似性就特别高,这个就是神经网络中存在的特征图冗杂的情况。
作者将相似的特征图认为是彼此的Ghost,所以这个网络就叫做GhostNet(误)。
在GhostNet这篇论文里面,作者认为可以使用一些计算量更低(Cheap Operations)的操作去生成这些冗余的特征图,这样就可以在保证良好检测效果的情况下,减少模型的参数量与提高模型的执行速度。
GhostNet模型的实现思路
1、Ghost Module
通过上述的介绍,我们了解到了,GhostNet的核心思想就是使用一些计算量更低(Cheap Operations)的操作去生成这些冗余的特征图。
在论文中,作者设计了一个名为Ghost Module的模块,他的功能是代替普通卷积。
Ghost Module将普通卷积分为两部分,首先进行一个普通的1x1卷积,这是一个少量卷积,比如正常使用32通道的卷积,这里就用16通道的卷积,这个1x1卷积的作用类似于特征整合,生成输入特征层的特征浓缩。
然后我们再进行深度可分离卷积,这个深度可分离卷积是逐层卷积,它也就是我们上面提到的Cheap Operations。它利用上一步获得的特征浓缩生成Ghost特征图。
因此,如果我们从整体上去看这个Ghost Module,它其实就是两步简单思想的汇总:
1、利用1x1卷积获得输入特征的必要特征浓缩。
2、利用深度可分离卷积获得特征浓缩的相似特征图(Ghost)。

Ghost Module的实现代码如下:
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组成的瓶颈结构,就像这样。
其实本质上就是用Ghost Module,来代替瓶颈结构里面的普通卷积。
Ghost Bottlenecks可以分为两个部分,分别是主干部分和残差边部分,包含Ghost Module的,我们称它为主干部分。
Ghost Bottlenecks有两个种类,如下图所示,当我们需要对特征层的宽高进行压缩的时候,我们会设置这个Ghost Bottlenecks的Stride=2,即步长为2。
此时我们会Bottlenecks里面多添加一些卷积层,在主干部分里,我们会在两个Ghost Module中添加一个步长为2x2的深度可分离卷积进行特征层的宽高压缩。在残差边部分,我们也会添加上一个步长为2x2的深度可分离卷积和1x1的普通卷积。

Ghost Bottlenecks的实现代码如下:
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的构建
整个Ghostnet的构建方式如列表所示:

可以看到,整个Ghostnet都是由Ghost Bottlenecks进行组成的。
当一张图片输入到Ghostnet当中时,我们首先进行一个16通道的普通1x1卷积块(卷积+标准化+激活函数)。
之后我们就开始Ghost Bottlenecks的堆叠了,利用Ghost Bottlenecks,我们最终获得了一个7x7x160的特征层(当输入是224x224x3的时候)。
然后我们会利用一个1x1的卷积块进行通道数的调整,此时我们可以获得一个7x7x960的特征层。
之后我们进行一次全局平均池化,然后再利用一个1x1的卷积块进行通道数的调整,获得一个1x1x1280的特征层。
然后平铺后进行全连接就可以进行分类了。
GhostNet的代码构建
1、模型代码的构建
GhostNet的实现代码如下,该代码是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
相关内容
- 2022最新Python日志库logging总结_python_
- Python实现动态二维码生成的示例代码_python_
- python神经网络pytorch中BN运算操作自实现_python_
- python神经网络Batch Normalization底层原理详解_python_
- Python自动操作Excel文件的方法详解_python_
- 运用Python巧妙处理Word文档的方法详解_python_
- Keras搭建孪生神经网络Siamese network比较图片相似性_python_
- python多线程同步售票系统_python_
- 十个简单使用的Python自动化脚本分享_python_
- python神经网络Pytorch中Tensorboard函数使用_python_
点击排行
本栏推荐
