网络编程
位置:首页>> 网络编程>> Python编程>> python神经网络MobileNetV2模型的复现详解

python神经网络MobileNetV2模型的复现详解

作者:Bubbliiiing  发布时间:2023-08-19 16:20:44 

标签:python,神经网络,MobileNetV2,模型,复现

什么是MobileNetV2模型

MobileNet它哥MobileNetV2也是很不错的呢

MobileNet模型是Google针对手机等嵌入式设备提出的一种轻量级的深层神经网络,其使用的核心思想便是depthwise separable convolution。

MobileNetV2是MobileNet的升级版,它具有两个特征点:

1、Inverted residuals,在ResNet50里我们认识到一个结构,bottleneck design结构,在3x3网络结构前利用1x1卷积降维,在3x3网络结构后,利用1x1卷积升维,相比直接使用3x3网络卷积效果更好,参数更少,先进行压缩,再进行扩张。而在MobileNetV2网络部分,其采用Inverted residuals结构,在3x3网络结构前利用1x1卷积升维,在3x3网络结构后,利用1x1卷积降维,先进行扩张,再进行压缩。

2、Linear bottlenecks,为了避免Relu对特征的破坏,在在3x3网络结构前利用1x1卷积升维,在3x3网络结构后,再利用1x1卷积降维后,不再进行Relu6层,直接进行残差网络的加法。

python神经网络MobileNetV2模型的复现详解

整体网络结构如下:(其中bottleneck进行的操作就是上述的创新操作)

python神经网络MobileNetV2模型的复现详解

MobileNetV2网络部分实现代码

#-------------------------------------------------------------#
#   MobileNetV2的网络部分
#-------------------------------------------------------------#
import math
import numpy as np
import tensorflow as tf
from tensorflow.keras import backend
from keras import backend as K
from keras.preprocessing import image
from keras.models import Model
from keras.layers.normalization import BatchNormalization
from keras.layers import Conv2D, Add, ZeroPadding2D, GlobalAveragePooling2D, Dropout, Dense
from keras.layers import MaxPooling2D,Activation,DepthwiseConv2D,Input,GlobalMaxPooling2D
from keras.applications import imagenet_utils
from keras.applications.imagenet_utils import decode_predictions
from keras.utils.data_utils import get_file
# TODO Change path to v1.1
BASE_WEIGHT_PATH = ('https://github.com/JonathanCMitchell/mobilenet_v2_keras/'
                   'releases/download/v1.1/')
# relu6!
def relu6(x):
   return K.relu(x, max_value=6)
# 用于计算padding的大小
def correct_pad(inputs, kernel_size):
   img_dim = 1
   input_size = backend.int_shape(inputs)[img_dim:(img_dim + 2)]
   if isinstance(kernel_size, int):
       kernel_size = (kernel_size, kernel_size)
   if input_size[0] is None:
       adjust = (1, 1)
   else:
       adjust = (1 - input_size[0] % 2, 1 - input_size[1] % 2)
   correct = (kernel_size[0] // 2, kernel_size[1] // 2)
   return ((correct[0] - adjust[0], correct[0]),
           (correct[1] - adjust[1], correct[1]))
# 使其结果可以被8整除,因为使用到了膨胀系数α
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 MobileNetV2(input_shape=[224,224,3],
               alpha=1.0,
               include_top=True,
               weights='imagenet',
               classes=1000):
   rows = input_shape[0]
   img_input = Input(shape=input_shape)
   # stem部分
   # 224,224,3 -> 112,112,32
   first_block_filters = _make_divisible(32 * alpha, 8)
   x = ZeroPadding2D(padding=correct_pad(img_input, 3),
                            name='Conv1_pad')(img_input)
   x = Conv2D(first_block_filters,
                     kernel_size=3,
                     strides=(2, 2),
                     padding='valid',
                     use_bias=False,
                     name='Conv1')(x)
   x = BatchNormalization(epsilon=1e-3,
                                 momentum=0.999,
                                 name='bn_Conv1')(x)
   x = Activation(relu6, name='Conv1_relu')(x)
   # 112,112,32 -> 112,112,16
   x = _inverted_res_block(x, filters=16, alpha=alpha, stride=1,
                           expansion=1, block_id=0)
   # 112,112,16 -> 56,56,24
   x = _inverted_res_block(x, filters=24, alpha=alpha, stride=2,
                           expansion=6, block_id=1)
   x = _inverted_res_block(x, filters=24, alpha=alpha, stride=1,
                           expansion=6, block_id=2)
   # 56,56,24 -> 28,28,32
   x = _inverted_res_block(x, filters=32, alpha=alpha, stride=2,
                           expansion=6, block_id=3)
   x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1,
                           expansion=6, block_id=4)
   x = _inverted_res_block(x, filters=32, alpha=alpha, stride=1,
                           expansion=6, block_id=5)
   # 28,28,32 -> 14,14,64
   x = _inverted_res_block(x, filters=64, alpha=alpha, stride=2,
                           expansion=6, block_id=6)
   x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1,
                           expansion=6, block_id=7)
   x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1,
                           expansion=6, block_id=8)
   x = _inverted_res_block(x, filters=64, alpha=alpha, stride=1,
                           expansion=6, block_id=9)
   # 14,14,64 -> 14,14,96
   x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1,
                           expansion=6, block_id=10)
   x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1,
                           expansion=6, block_id=11)
   x = _inverted_res_block(x, filters=96, alpha=alpha, stride=1,
                           expansion=6, block_id=12)
   # 14,14,96 -> 7,7,160
   x = _inverted_res_block(x, filters=160, alpha=alpha, stride=2,
                           expansion=6, block_id=13)
   x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1,
                           expansion=6, block_id=14)
   x = _inverted_res_block(x, filters=160, alpha=alpha, stride=1,
                           expansion=6, block_id=15)
   # 7,7,160 -> 7,7,320
   x = _inverted_res_block(x, filters=320, alpha=alpha, stride=1,
                           expansion=6, block_id=16)
   if alpha > 1.0:
       last_block_filters = _make_divisible(1280 * alpha, 8)
   else:
       last_block_filters = 1280
   # 7,7,320 -> 7,7,1280
   x = Conv2D(last_block_filters,
                     kernel_size=1,
                     use_bias=False,
                     name='Conv_1')(x)
   x = BatchNormalization(epsilon=1e-3,
                                 momentum=0.999,
                                 name='Conv_1_bn')(x)
   x = Activation(relu6, name='out_relu')(x)
   x = GlobalAveragePooling2D()(x)
   x = Dense(classes, activation='softmax',
                       use_bias=True, name='Logits')(x)
   inputs = img_input
   model = Model(inputs, x, name='mobilenetv2_%0.2f_%s' % (alpha, rows))
   # Load weights.
   if weights == 'imagenet':
       if include_top:
           model_name = ('mobilenet_v2_weights_tf_dim_ordering_tf_kernels_' +
                         str(alpha) + '_' + str(rows) + '.h5')
           weight_path = BASE_WEIGHT_PATH + model_name
           weights_path = get_file(
               model_name, weight_path, cache_subdir='models')
       else:
           model_name = ('mobilenet_v2_weights_tf_dim_ordering_tf_kernels_' +
                         str(alpha) + '_' + str(rows) + '_no_top' + '.h5')
           weight_path = BASE_WEIGHT_PATH + model_name
           weights_path = get_file(
               model_name, weight_path, cache_subdir='models')
       model.load_weights(weights_path)
   elif weights is not None:
       model.load_weights(weights)
   return model
def _inverted_res_block(inputs, expansion, stride, alpha, filters, block_id):
   in_channels = backend.int_shape(inputs)[-1]
   pointwise_conv_filters = int(filters * alpha)
   pointwise_filters = _make_divisible(pointwise_conv_filters, 8)
   x = inputs
   prefix = 'block_{}_'.format(block_id)
   # part1 数据扩张
   if block_id:
       # Expand
       x = Conv2D(expansion * in_channels,
                         kernel_size=1,
                         padding='same',
                         use_bias=False,
                         activation=None,
                         name=prefix + 'expand')(x)
       x = BatchNormalization(epsilon=1e-3,
                                     momentum=0.999,
                                     name=prefix + 'expand_BN')(x)
       x = Activation(relu6, name=prefix + 'expand_relu')(x)
   else:
       prefix = 'expanded_conv_'
   if stride == 2:
       x = ZeroPadding2D(padding=correct_pad(x, 3),
                                name=prefix + 'pad')(x)
   # part2 可分离卷积
   x = DepthwiseConv2D(kernel_size=3,
                              strides=stride,
                              activation=None,
                              use_bias=False,
                              padding='same' if stride == 1 else 'valid',
                              name=prefix + 'depthwise')(x)
   x = BatchNormalization(epsilon=1e-3,
                                 momentum=0.999,
                                 name=prefix + 'depthwise_BN')(x)
   x = Activation(relu6, name=prefix + 'depthwise_relu')(x)
   # part3压缩特征,而且不使用relu函数,保证特征不被破坏
   x = Conv2D(pointwise_filters,
                     kernel_size=1,
                     padding='same',
                     use_bias=False,
                     activation=None,
                     name=prefix + 'project')(x)
   x = BatchNormalization(epsilon=1e-3,
                                 momentum=0.999,
                                 name=prefix + 'project_BN')(x)
   if in_channels == pointwise_filters and stride == 1:
       return Add(name=prefix + 'add')([inputs, x])
   return x

图片预测

建立网络后,可以用以下的代码进行预测。

def preprocess_input(x):
   x /= 255.
   x -= 0.5
   x *= 2.
   return x
if __name__ == '__main__':
   model = MobileNetV2(input_shape=(224, 224, 3))
   model.summary()
   img_path = 'elephant.jpg'
   img = image.load_img(img_path, target_size=(224, 224))
   x = image.img_to_array(img)
   x = np.expand_dims(x, axis=0)
   x = preprocess_input(x)
   print('Input image shape:', x.shape)
   preds = model.predict(x)
   print(np.argmax(preds))
   print('Predicted:', decode_predictions(preds, 1))

预测所需的已经训练好的MobileNetV2模型会在运行时自动下载,下载后的模型位于C:\Users\Administrator.keras\models文件夹内。

可以修改MobileNetV2内不同的alpha值实现不同depth的MobileNetV2模型。可选的alpha值有:

 Top-1Top-510-5SizeStem
MobileNetV2(alpha=0.35)39.91417.56815.4221.7M0.4M
MobileNetV2(alpha=0.50)34.80613.93811.9762.0M0.7M
MobileNetV2(alpha=0.75)30.46810.8249.1882.7M1.4M
MobileNetV2(alpha=1.0)28.6649.8588.3223.5M2.3M
MobileNetV2(alpha=1.3)25.3207.8786.7285.4M3.8M

来源:https://blog.csdn.net/weixin_44791964/article/details/102851214

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com