参考 : https://blog.csdn.net/hongbin_xu/article/details/82957426
原文: https://blog.csdn.net/just_sort/article/details/79901885
一,翻译
参考翻译文献:https://baijiahao.baidu.com/s?id=1566004753349359&wfr=spider&for=pc
文中不懂的知识现在用:粗体标注了
摘要
我们为移动和嵌入式视觉应用提供了一类名为MobileNets的高效模型。 MobileNets基于流线型架构,使用深度可分离卷积来构建轻量级深度神经网络。 我们介绍了两个简单的全局超参数,可以在时间和准确性之间进行有效折中。 这些超参数允许模型构建者根据问题的约束为其应用程序选择合适大小的模型。 我们在资源和精度折中方面提出了大量实验,并且与ImageNet分类上的其他流行模型相比显示出强大的性能。 然后,我们演示MobileNets在广泛的应用和用例(包括对象检测,精细分类,人脸属性提取和大规模地理定位)中的有效性。
1.Introduction(介绍)
自从AlexNet赢得ImageNet挑战赛(ILSVRC2012)推广深度卷积神经网络以后,卷积神经网络在计算机视觉领域变得普遍存在。总得趋势是使用更深层更复杂的网络来实现更高的准确度。然而,这些提高准确率的进步并不一定会使网络在尺寸和速度方面更有效率。在许多现实世界的应用中,例如机器人,自动驾驶汽车和AR技术,识别任务需要及时地在有限计算资源的平台上进行。本文介绍了一种高效的网络结构和两个超参数,以便构建非常小的,快速度的模型,可以轻松匹配移动和嵌入式视觉应用的设计要求。第二节回顾了之前在构建小模型的工作。第三节描述了MobileNets架构和两个超参数(widthmultiplier和resolutionmultiplier),以便定义更小更高效的MobileNets。第四节介绍了ImageNet上的实验以及各种不同的应用和用例。第五节以汇总和结论来结束论文。
2. Prior Work(先验工作)
获取小型网络的另一个办法是缩小,分解或者压缩训练好的模型。文献中基于压缩的方法有product quantization,哈希法与pruning,vector quantization和霍夫曼编码压缩。此外,各种各样的因式分解方法被提出来用于加速训练好的网络。训练小型网络的另一种方法是distillation(蒸馏法),使用更大的网络来教授小一点的网络。它在我们方法的补充,并在第四节的一些使用案例中有所涉及。另一种新兴的方法是low bit networks。
图1:MobileNet模型可以应用于各种识别任务,以实现设备智能化。
3.MobileNet Architecture 结构
在本节中,我们首先描述MobileNet构建的核心层,它们是深度可分离的过滤器。然后我们介绍MobileNet网络结构,并以两个模型缩小参数(widthmultiplier和resolutionmultiplier)作为总结。
3.1 Deep-wise Separabe 深度可分离卷积
图2:标准卷积的分解过程。
是假设正方形核的空间空间维度,M是输入通道数量,N是输出通道数量。如果假设卷积步幅是1并考虑padding,那么标准化卷积操作的输出特征图谱为:
Gk,l,m=∑i,j,mKi,j,m,n∗Fk+i−1,l+j−1,mGk,l,m=∑i,j,mKi,j,m,n∗Fk+i−1,l+j−1,m
在第四节中,MobileNets使用3x3的深度可分离卷积比标准的卷积减少了8-9倍的计算复杂度,准确率只减少了一点点。空间维度的因式分解不会节省大量的计算复杂度,因为depthwise convolutions的计算复杂度本来就很小。
3.2 Network Structure and Training (网络结构和训练)
使用类似于InceptionV3的异步梯度下降的RMSprop,MobileNet模型在TensorFlow中进行训练。然而,与训练大模型相反,我们较少地使用正则化和数据增加技术,因为小模型不容易过拟合。当训练MobileNets时,我们不使用sideheads或者labelsmoothing,通过限制croping的尺寸来减少图片扭曲。另外,我们发现重要的是在depthwise滤波器上放置很少或没有重量衰减(L2正则化),因为它们参数很少。在下一节ImageNet的benchmark中,所有的模型通过同样的参数进行训练,但不考虑模型的大小。
表2:每种层的分布
3.3. Width Multiplier: Thinner Models(alpha参数:更小的模型)
α∈(0,1],通常设为1,0.75,0.5和0.25。α=1表示基准MobileNet,而α<1则表示瘦身的MobileNets。Width multiplier有减少计算复杂度和参数数量(大概α二次方)的作用。Width multiplier可以应用于任何模型结构,以定义一个具有合理准确性,延迟和尺寸的新的较小的模型。它用于定义新的简化结构,但需要重新进行训练。
3.4. Resolution Multiplier: Reduced Representation
降低神经网络的第二个超参数是resolution multiplier ρ。我们将其应用于输入图像,并且每个层的内部特征随后被减去相同的乘数。实际上,我们通过设置输入分辨率隐式设置ρ。我们现在可以将网络核心层的计算复杂度表示为具有width multiplier α和resolution multiplier ρ的深度可分离卷积:ρ∈(0,1],通常设为224,192,160或者128。ρ=1是基本MobileNets而ρ<1示瘦身的MobileNets。Resolutionmultiplier可以减少计算复杂度ρ的平方。作为一个例子,我们可以看一下MobileNet中的一个典型的卷积层,看看深度可分离卷积,width multiplier和resolution multiplier如何降低计算复杂度和参数。表3显示了不同的架构收缩方法应用于该层,而表示的计算复杂度和参数数量。第一行显示了Multi-Adds和参数,其中Multi-Adds具有14x14x512的输入特征图,内核K的大小为3x3x512x512。我们将在下一节中详细介绍模型资源和准确率的折中。
表3
4. Experiments(实验)
在本节中,我们首先研究depthwise convolutions和收缩网络的影响,收缩网络时我们选择减小网络宽度而不是减少层数。然后,我们展示基于两个超参数而瘦身网络的相关结果:width multiplier和resolution multiplier,然后和一系列流行模型对比。接着我们将MobileNets应用于各种不同的应用
4.1ModelChoices
表9比较了瘦身(widthmultiplier α=0.5,分辨率160×160)的MobileNet。瘦身的MobileNet准确率比AlexNet好大约4%,但模型大小小了45倍,计算复杂度也小了9.4倍。在模型尺寸差不多的情况下,它比Squeezenet有更高的准确率,而计算复杂度降低了22倍。
4.3. Fine Grained Recognition(精细分类)
我们在StanfordDogs数据集上训练细粒度识别的MobileNet模型。我们扩展了【18】的方法,并从网络收集比【18】更大但更嘈杂的训练集。我们使用嘈杂的网络数据预先训练一个细粒度的狗识别模型,然后在StanfordDog训练集上对模型进行微调。StanfordDogs测试集结果见表10。MobileNet几乎可以实现最先进的结果【18】,并大大减少了计算量和大小。
4.4. Large Scale Geolocalizaton(大规模地理定位)
PlaNet将如何确定在哪里拍照作为分类问题。该方法将地球划分为一个用作目标类的地理单元格,并在数百万个地理标记的照片上训练卷积神经网络。PlaNet已被证明可以成功确认各种照片的地点,并优于处理相同任务的Im2GPS。我们使用MobileNet架构在相同的数据集上重新训练PlaNet。整个基于InceptionV3架构的PlaNet模型有5200万参数和57.4亿多mult-adds。而MobileNet模型只有1300万参数(3百万用在主架构,1000万用在最后一层)和5.8亿mult-adds。如表11所示,MobileNet版本与PlaNet相比,性能略有降低,但更加小。此外,它仍然大幅度优于Im2GPS。
4.5. Face Attributes(人脸属性分类)
MobileNet的另一个用例是压缩具有未知或深奥的训练过程的大型系统。在面部属性分类任务中,我们展示了MobileNet与distillation的关系,这是深度网络的迁移学习的知识。我们寻求减少7500万个参数和1600万次Mult-Adds的大属性分类器。分类器对类似于YFFC100M的多属性训练集进行了训练。我们使用MobileNet架构提取一个面部属性分类器。Distillation通过仿真大模型的输出(而不是实际标签)来训练分类器,从而可以用大量(可能是无限的)未标记的数据进行训练。综合distillation训练的可扩展性和MobileNet的简约参数化,终端系统不仅不需要正规化(权值降低和早期停止),而且还表现出加强性能。从表12显而易见的是,基于MobileNet的分类器具有强烈弹性的模型收缩性:它在不同的属性达到了近似的平均精度(meanAP)却只有1%的计算量。
表12:用MobileNet进行人脸属性分类
4.6. Object Detection (目标检测)
MobileNet也可以作为有效地基础网络部署在现代物体检测系统中。根据最近获得2016COCO冠军的工作,我们报告了针对COCO数据集进行目标检测的MobileNet训练结果。在表13中,使用相同Faster-RCNN和SSD框架,MobileNet和VGG与InceptionV2进行了比较。在我们的实验中,SSD使用了300的输入分辨率(SSD300),而Faster-RCNN使用了600和300的分辨率(Faster-RCNN300,Faster-RCNN600)。这个Faster-RCNN模型每张图考虑300PRN建议框。模型通过COCO训练集和测试集训练(排除8000张minival图),然后再minival集中测试。对于这两个框架,MobileNet可以实现与其他网络的相似结果,但计算复杂度和模型大小差很多。
图6:使用MobileNetSSD进行目标检测的例子
4.7. Face Embeddings(人脸识别)
FaceNet模型时最先进的面部识别模型。它基于tripletloss构建人脸特征。为了构建一个移动端的FaceNet模型,我们使用distillation方法去训练模型,训练时尽量最小化FaceNet和MobileNet在训练集上的输出平方差。小MobileNet模型的结果可以在表14中找到。
表14:FaceNet中提取MobileNet
5 结论
我们提出了一种基于深度可分离卷积的新型模型架构,称为MobileNets。我们讨论了一些可以得到高效模型的重要设计决策。然后,我们演示了如何通过使用widthmultiplier和resolutionmultiplier来构建更小更快的MobileNets,以减少合理的精度来减少模型尺寸和延迟。然后,我们将不同的MobileNets与流行的模型进行比较,展示了尺寸,速度和精度。我们最终通过展示MobileNet在应用于各种任务时的有效性来得出结论。作为帮助采用和探索MobileNets的下一步,我们计划在TensorFlow中发布模型。
二,总结
用mobilenet实现对mnist数据集进行训练, 代码如下:
import tensorflow as tf
from tensorflow.python.training import moving_averages
UPDATE_OPS_COLLECTION = "_update_ops_"
# create variable
def create_variable(name, shape, initializer,
dtype=tf.float32, trainable=True):
return tf.get_variable(name, shape=shape, dtype=dtype,
initializer=initializer, trainable=trainable)
"""
公式如下:
y=γ(x-μ)/σ+β
其中x是输入,y是输出,μ是均值,σ是方差,γ和β是缩放(scale)、偏移(offset)系数。
一般来讲,这些参数都是基于channel来做的,比如输入x是一个16*32*32*128(NWHC格式)的feature map,
那么上述参数都是128维的向量。其中γ和β是可有可无的,有的话,就是一个可以学习的参数(参与前向后向),
没有的话,就简化成y=(x-μ)/σ。而μ和σ,在训练的时候,使用的是batch内的统计值,测试/预测的时候,采用的是训练时计算出的滑动平均值。
"""
# batchnorm layer
def bacthnorm(inputs, scope, epsilon=1e-05, momentum=0.99, is_training=True):
inputs_shape = inputs.get_shape().as_list()#inputs代表输入,scope代表命名
params_shape = inputs_shape[-1:]#得到numpy数组的维度,例如[3,4]
axis = list(range(len(inputs_shape) - 1))#得到最后一个维度[4]
with tf.variable_scope(scope):
beta = create_variable("beta", params_shape,
initializer=tf.zeros_initializer())
gamma = create_variable("gamma", params_shape,
initializer=tf.ones_initializer())
# for inference
moving_mean = create_variable("moving_mean", params_shape,
initializer=tf.zeros_initializer(), trainable=False)
moving_variance = create_variable("moving_variance", params_shape,
initializer=tf.ones_initializer(), trainable=False)
if is_training:
mean, variance = tf.nn.moments(inputs, axes=axis)
update_move_mean = moving_averages.assign_moving_average(moving_mean,
mean, decay=momentum)
update_move_variance = moving_averages.assign_moving_average(moving_variance,
variance, decay=momentum)
else:
mean, variance = moving_mean, moving_variance
return tf.nn.batch_normalization(inputs, mean, variance, beta, gamma, epsilon)#低级的操作函数,调用者需要自己处理张量的平均值和方差。
# depthwise conv2d layer
def depthwise_conv2d(inputs, scope, filter_size=3, channel_multiplier=1, strides=1):
inputs_shape = inputs.get_shape().as_list()
in_channels = inputs_shape[-1]
with tf.variable_scope(scope):
filter = create_variable("filter", shape=[filter_size, filter_size,
in_channels, channel_multiplier],
initializer=tf.truncated_normal_initializer(stddev=0.01))
return tf.nn.depthwise_conv2d(inputs, filter, strides=[1, strides, strides, 1],
padding="SAME", rate=[1, 1])
# conv2d layer
def conv2d(inputs, scope, num_filters, filter_size=1, strides=1):
inputs_shape = inputs.get_shape().as_list()
in_channels = inputs_shape[-1]
with tf.variable_scope(scope):
filter = create_variable("filter", shape=[filter_size, filter_size,
in_channels, num_filters],
initializer=tf.truncated_normal_initializer(stddev=0.01))
return tf.nn.conv2d(inputs, filter, strides=[1, strides, strides, 1],
padding="SAME")
# avg pool layer
def avg_pool(inputs, pool_size, scope):
with tf.variable_scope(scope):
return tf.nn.avg_pool(inputs, [1, pool_size, pool_size, 1],
strides=[1, pool_size, pool_size, 1], padding="VALID")
# fully connected layer
def fc(inputs, n_out, scope, use_bias=True):
inputs_shape = inputs.get_shape().as_list()
n_in = inputs_shape[-1]
with tf.variable_scope(scope):
weight = create_variable("weight", shape=[n_in, n_out],
initializer=tf.random_normal_initializer(stddev=0.01))
if use_bias:
bias = create_variable("bias", shape=[n_out,],
initializer=tf.zeros_initializer())
return tf.nn.xw_plus_b(inputs, weight, bias)
return tf.matmul(inputs, weight)
class MobileNet(object):
def __init__(self, inputs, labels, num_classes=10, is_training=True,
width_multiplier=0.5, scope="MobileNet", ):
self.inputs = inputs
self.num_classes = num_classes
self.is_training = is_training
self.width_multiplier = width_multiplier
# construct model
with tf.variable_scope(scope):
# conv1
net = conv2d(inputs, "conv_1", round(32 * width_multiplier), filter_size=1,
strides=2) # ->[N, 14, 14, 1]
net = tf.nn.relu(bacthnorm(net, "conv_1/bn", is_training=self.is_training))
net = self._depthwise_separable_conv2d(net, 64, self.width_multiplier,
"ds_conv_2") # ->[N, 14, 14, 64]
net = self._depthwise_separable_conv2d(net, 128, self.width_multiplier,
"ds_conv_3") # ->[N, 14, 14, 128]
net = self._depthwise_separable_conv2d(net, 128, self.width_multiplier,
"ds_conv_4") # ->[N, 14, 14, 128]
net = self._depthwise_separable_conv2d(net, 256, self.width_multiplier,
"ds_conv_5") # ->[N, 14, 14, 256]
net = self._depthwise_separable_conv2d(net, 256, self.width_multiplier,
"ds_conv_6") # ->[N, 14, 14, 256]
net = self._depthwise_separable_conv2d(net, 512, self.width_multiplier,
"ds_conv_7") # ->[N, 14, 14, 512]
net = self._depthwise_separable_conv2d(net, 512, self.width_multiplier,
"ds_conv_8") # ->[N, 14, 14, 512]
net = self._depthwise_separable_conv2d(net, 512, self.width_multiplier,
"ds_conv_9") # ->[N, 14, 14, 512]
net = self._depthwise_separable_conv2d(net, 512, self.width_multiplier,
"ds_conv_10") # ->[N, 14, 14, 512]
net = self._depthwise_separable_conv2d(net, 512, self.width_multiplier,
"ds_conv_11") # ->[N, 14, 14, 512]
net = self._depthwise_separable_conv2d(net, 512, self.width_multiplier,
"ds_conv_12") # ->[N, 14, 14, 512]
net = self._depthwise_separable_conv2d(net, 1024, self.width_multiplier,
"ds_conv_13", downsample=True) # ->[N, 7, 7, 1024]
net = self._depthwise_separable_conv2d(net, 1024, self.width_multiplier,
"ds_conv_14") # ->[N, 7, 7, 1024]
net = avg_pool(net, 7, "avg_pool_15")
net = tf.squeeze(net, [1, 2], name="SpatialSqueeze")
self.logits = fc(net, self.num_classes, "fc_16")
self.predictions = tf.nn.softmax(self.logits)
self.loss = -tf.reduce_mean(labels * tf.log(self.predictions))
def _depthwise_separable_conv2d(self, inputs, num_filters, width_multiplier,
scope, downsample=False):
"""depthwise separable convolution 2D function"""
num_filters = round(num_filters * width_multiplier)
strides = 2 if downsample else 1
with tf.variable_scope(scope):
# depthwise conv2d
dw_conv = depthwise_conv2d(inputs, "depthwise_conv", strides=strides)
# batchnorm
bn = bacthnorm(dw_conv, "dw_bn", is_training=self.is_training)
# relu
relu = tf.nn.relu(bn)
# pointwise conv2d (1x1)
pw_conv = conv2d(relu, "pointwise_conv", num_filters)
# bn
bn = bacthnorm(pw_conv, "pw_bn", is_training=self.is_training)
return tf.nn.relu(bn)
if __name__ == "__main__":
# test data
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
x = tf.placeholder("float", [None, 784])
y_ = tf.placeholder("float", [None, 10])
x_image = tf.reshape(x, [-1, 28, 28, 1])
x_label = tf.reshape(y_, [-1, 10])
mobileNet = MobileNet(x_image, x_label)
train_step = tf.train.AdamOptimizer(1e-4).minimize(mobileNet.loss)
keep_prob = tf.placeholder("float")
correct_prediction = tf.equal(tf.arg_max(mobileNet.predictions, 1), tf.arg_max(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for i in range(10000):
batch = mnist.train.next_batch(50)
if i % 10 == 0:
train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})
print("step %d, training accurary %g" % (i, train_accuracy))
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
tf.nn.separable_conv2d()