提交 e63ec6d3 编写于 作者: 别团等shy哥发育's avatar 别团等shy哥发育

MobileNetV1/V2花朵识别

上级 18440b7d
此差异已折叠。
此差异已折叠。
......@@ -11,8 +11,10 @@ from tensorflow.keras.models import Model
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Conv2D, Add, ZeroPadding2D, GlobalAveragePooling2D, Dropout, Dense
from tensorflow.keras.layers import MaxPooling2D, Activation, DepthwiseConv2D, Input, GlobalMaxPooling2D
from tensorflow.keras.layers import ReLU
from tensorflow.keras.applications import imagenet_utils
from tensorflow.keras.applications.imagenet_utils import decode_predictions
# from tensorflow.keras.utils.data_utils import get_file
# TODO Change path to v1.1
......@@ -49,23 +51,95 @@ def correct_pad(inputs, kernel_size):
def _make_divisible(v, divisor, min_value=None):
if min_value is None:
min_value = divisor
# 与value最接近的8的倍数。若value=30,那么处理后的卷积核=((30+4)//8)*8=32
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):
def _inverted_res_block(inputs, expansion, stride, alpha, filters, block_id):
# 取输入shape的最后一维(通道数)
in_channels = backend.int_shape(inputs)[-1]
# 超参数控制逐点卷积的卷积核个数,使用int()函数取整,防止alpha是小数
pointwise_conv_filters = int(filters * alpha)
# 由于网络结构图中特征图的个数为32、16、24、64...都是8的倍数,
# 保证超参数处理过后的卷积核个数能满足网络特征图个数的要求,因此需要保证逐点卷积的卷积核个数为8的倍数
pointwise_filters = _make_divisible(pointwise_conv_filters, 8)
# 先用x表示输入,如果不执行part1的话,那么x没机会接收inputs
x = inputs
prefix = 'block_{}_'.format(block_id)
# part1 数据扩张:在通道维度上升维
if block_id:
# Expand 利用1*1卷积升维(提升通道数)
x = Conv2D(expansion * in_channels, # 卷积核个数,通道维度提升expansion倍数
kernel_size=1, # 卷积核大小1*1
strides=1, # 步长1
padding='same', # 卷积过程中特征图size不变
use_bias=False, # 有BN层就不需要计算偏置
activation=None,
name=prefix + 'expand')(x)
# 批标准化
x = BatchNormalization(epsilon=1e-3,
momentum=0.999,
name=prefix + 'expand_BN')(x)
# relu6激活函数
# x = Activation(relu6, name=prefix + 'expand_relu')(x)
x = ReLU(6.0, 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:1*1卷积降维 压缩特征,而且不使用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)
# 残差连接,输入和输出短接
# 当输入通道数=输出通道数且步长为1,进行残差边的连接
if in_channels == pointwise_filters and stride == 1:
# 将输入和输出的tensor的对应元素值相加,要求shape完全一致
return Add(name=prefix + 'add')([inputs, x])
return x
# MobileNetV2网络结构
def MobileNetV2(input_shape=(224, 224, 3),alpha=1.0,include_top=True, weights='imagenet',classes=1000):
# 规定超参数的取值范围
if alpha not in [0.5, 0.75, 1.0, 1.3]:
raise ValueError('alpha should use 0.5, 0.75, 1.0, 1.3')
rows = input_shape[0]
img_input = Input(shape=input_shape)
# stem部分
# 224,224,3 -> 112,112,32
# 超参数控制卷积核个数,先处理第一次卷积的卷积核个数(32*alpha)使它能被8整除
first_block_filters = _make_divisible(32 * alpha, 8)
x = ZeroPadding2D(padding=correct_pad(img_input, 3),
name='Conv1_pad')(img_input)
......@@ -82,34 +156,35 @@ def MobileNetV2(input_shape=(224, 224, 3),
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)
# 第一个残差卷积由于expansion=1,不用上升维度,因此不用执行第part1步的标准卷积操作,所以block_id=0
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)
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)
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)
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)
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)
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)
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)
......@@ -125,7 +200,7 @@ def MobileNetV2(input_shape=(224, 224, 3),
momentum=0.999,
name='Conv_1_bn')(x)
x = Activation(relu6, name='out_relu')(x)
# [7,7,1280]->[None,1280]
x = GlobalAveragePooling2D()(x)
x = Dense(classes, activation='softmax',
use_bias=True, name='Logits')(x)
......@@ -155,68 +230,13 @@ def MobileNetV2(input_shape=(224, 224, 3),
return model
def _inverted_res_block(inputs, expansion, stride, alpha, filters, block_id):
# 取输入shape的最后一维(通道数)
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 利用1*1卷积升维
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:1*1卷积降维 压缩特征,而且不使用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)
# 当输入通道数=输出通道数且步长为1,进行残差边的连接
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()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册