Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet

声明:

  1. 翻译自Quick complete Tensorflow tutorial to understand and run Alexnet, VGG, Inceptionv3, Resnet and squeezeNet networks
  2. 虽然原文中有第3部分,使用预训练网络,但是文章中没有贴代码,所以本文没有翻译这一个部分。

TensorFlow卷积网络教程

本文包括2部分:

  1. 介绍一些成功的卷积网络,如AlexNet,Resnet等。
  2. 简要介绍一个轻量级高层TensorFlow库——TensorFlow-Slim(TF-Slim)。

数据集:Imagenet

1.1 Alexnet

Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet_第1张图片

Alexnet在第一层使用的11*11卷积核后来被证明过大,以后的卷积网络的设计都是使用较小的卷积核。同时,Alexnet通过使用dropout层、ReLU激活函数和一系列数据增强的手段提升网络性能,这些方法被证明十分有效并沿用至今。

1.2 VGG

Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet_第2张图片

图中每一列表示VGG的一个版本,其中vgg16和vgg19最常用。

VGG用3*3的卷积核代替Alexnet11*11的卷积核,网络更深,准确率更高。同时由于其简单的结构一度被广泛使用。它的缺点是参数量太大(主要是全连接层的参数)。一个预训练的VGG模型会占用500M以上的空间。

3. Inceptioin

越深的网络具有越强的capacity,这也是深度学习的由来。在深度学习的发展史上,Inception网络是第一个不仅仅在网络深度上进行改进的模型。

在第一层,对于输入图片,使用多个不同大小的卷积核(1*1, 3*3, 5*5),然后合并卷积结果以获得更robust的图片表示。
Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet_第3张图片
很多时候会在卷积层后面再加一个1*1的卷积层来调节通道的深度。
Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet_第4张图片
Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet_第5张图片

4. Resnet

2015年Imagenet的最佳结果来自于152层的残差网络Resnet。它通过残差的形式解决了深层网络难以训练的问题。
Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet_第6张图片

5. Squeezenet

squeezenet的优点在于大大减少了网络中参数的个数,一个预训练的squeezenet只有5M,更便于在实际中应用。
Tensorflow卷积网络教程——Alexnet, VGG, Inceptionv3, Resnet and squeezeNet_第7张图片

2. TensorFlow-slim tutorial

TensorFlow-slim是一个封装好的轻量级API,可以帮我们省去很多底层编码。下面通过构建InceptionV1来学习如何使用TF-Slim。

2.1 From Tensorflow version1.0 and above

TF-Slim在1.0或以上的版本中上线。在shell中运行以下命令检查TF-Slim是否存在。

python -c "import tensorflow.contrib.slim as slim; eval=slim.evaluation.evaluate_once"

2.2 TensorFlow-slim tutorial

import TF-Slim

import tf.contrib.slim as slim

2.2.1 Quick Slim variables

weights = slim.variable('weights', shape=[10, 10, 3, 3], initializer=tf.truncated_normal_initializer(stddev=0.1), regularizer=slim.l2_regularizer(0.05), device='/GPU:0')

一行代码,定义了:

  1. 变量名
  2. 变量shape
  3. 初始化方式
  4. 正则化方式
  5. 部署设备号

获取模型变量的方式:model_variables = slim.get_model_variables().
与之对应的TensorFlow底层方式:tf.get_global_variables().
获取全部变量的方式,包括步长变量(global_step):regular_variables_and_model_variables = slim.get_variables().

每次通过TF-Slim创建了变量,都会添加到tf.GraphKeys.MODEL_VARIABLES集合中。

2.2.2 Higher level layers

创建二维卷积层,128个3*3的卷积核:

input = ...
net = slim.conv2d(input, 128, [3, 3], scope='conv1_1')

跟Keras很像

要实现相同功能,底层TensorFlow:

input = ...
with tf.name_scope('conv1_1') as scope:
    kernel = tf.Variable(tf.truncated_normal([3, 3, 64, 128], dtype=tf.float32, stddev=0.1), name='weights')
    conv = tf.nn.conv2d(input, kernel, [1, 1, 1, 1], padding='SAME')
    biases = tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32), trainable=True, name='biases')
    bias = tf.nn.bias_add(conv, biases)
    conv1 = tf.nn.relu(bias, name=scope)

可以看出TF-Slim确实可以大大减少代码量,如果有多层卷积层:

net = ...
net = slim.conv2d(net, 256, [3, 3], scope='conv3_1')
net = slim.conv2d(net, 256, [3, 3], scope='conv3_2')
net = slim.conv2d(net, 256, [3, 3], scope='conv3_3')

或者更简单的,用一句话表示3个卷积层:

net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')

跟进一步,如果我们的网络由参数不同(比如卷积核个数不同)的全连接层层构成:

x = slim.fully_connected(x, 32, scope='fc/fc_1')
x = slim.fully_connected(x, 64, scope='fc/fc_2')
x = slim.fully_connected(x, 128, scope='fc/fc_3')
slim.stack(x, slim.fully_connected, [32, 64, 128], scope='fc')

对于卷积层,也是同样的道理:

# 方案一:不使用stack
x = slim.conv2d(x, 32,[3, 3], scope='core/core_1')
x = slim.conv2d(x, 32,[1, 1], scope='core/core_2')
x = slim.conv2d(x, 64,[3, 3], scope='core/core_3')
x = slim.conv2d(x, 64,[1, 1], scope='core/core_4')

# 方案二:使用stack
slim.stack(x, slim.conv2d,[(32,[3, 3]),(32,[1, 1]),(64,[3, 3]),(64,[1, 1])], scope='core')

2.2.3 Arg scope and using it

还记得前面使用底层TensorFlow函数构建卷积层,里面使用了name_scope。把源代码再次贴出来:

input = ...
with tf.name_scope('conv1_1') as scope:
    kernel = tf.Variable(tf.truncated_normal([3, 3, 64, 128], dtype=tf.float32, stddev=0.1), name='weights')
    conv = tf.nn.conv2d(input, kernel, [1, 1, 1, 1], padding='SAME')
    biases = tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32), trainable=True, name='biases')
    bias = tf.nn.bias_add(conv, biases)
    conv1 = tf.nn.relu(bias, name=scope)

name_scope起到一个命名的前缀的作用,在上述代码中,kernel.name == 'conv1_1/weights:0

基于name_scope的思想,TF-Slim有一个arg_scope的功能。对于3个相似的卷积层:

net = slim.conv2d(inputs, 64,[11, 11], 4, padding='SAME',
                  weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
                  weights_regularizer=slim.l2_regularizer(0.0005), scope='conv1')
net = slim.conv2d(net, 128,[11, 11], padding='VALID',
                  weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
                  weights_regularizer=slim.l2_regularizer(0.0005), scope='conv2')
net = slim.conv2d(net, 256,[11, 11], padding='SAME',
                  weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
                  weights_regularizer=slim.l2_regularizer(0.0005), scope='conv3')

通过arg_scope可以改为:

with slim.arg_scope([slim.conv2d], padding='SAME', weights_initializer=tf.truncated_normal_initializer(stddev=0.01), weights_regularizer=slim.l2_regularizer(0.0005)):
    net = slim.conv2d(inputs, 64, [11, 11], scope='conv1')
    net = slim.conv2d(net, 128, [11, 11], padding='VALID', scope='conv2')
    net = slim.conv2d(net, 256, [11, 11], scope='conv3')

你可能感兴趣的:(TensorFlow)