python神经网络MobileNetV2模型的复现详解
作者:Bubbliiiing
什么是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层,直接进行残差网络的加法。
整体网络结构如下:(其中bottleneck进行的操作就是上述的创新操作)
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-1 | Top-5 | 10-5 | Size | Stem | |
---|---|---|---|---|---|
MobileNetV2(alpha=0.35) | 39.914 | 17.568 | 15.422 | 1.7M | 0.4M |
MobileNetV2(alpha=0.50) | 34.806 | 13.938 | 11.976 | 2.0M | 0.7M |
MobileNetV2(alpha=0.75) | 30.468 | 10.824 | 9.188 | 2.7M | 1.4M |
MobileNetV2(alpha=1.0) | 28.664 | 9.858 | 8.322 | 3.5M | 2.3M |
MobileNetV2(alpha=1.3) | 25.320 | 7.878 | 6.728 | 5.4M | 3.8M |
以上就是python神经网络MobileNetV2模型的复现详解的详细内容,更多关于MobileNetV2模型复现的资料请关注脚本之家其它相关文章!