TensorFlow 版本: 1.12.0
TF-Slim 模块是 TensorFlow 中最好用的 API 之一。
尤其是里面引入的 arg_scope
、model_variables
、repeat
、stack
。
文章目录
TF-Slim ¶
TF-Slim 是 TensorFlow 中一个用来构建、训练、评估复杂模型的轻量化库。TF-Slim 模块可以和 TensorFlow 中其它API混合使用。
1. Slim 模块的导入 ¶
import tensorflow as tf
slim = tf.contrib.slim
2. Slim 模块的优点 ¶
Slim 模块可以使模型的构建、训练、评估变得简单:
-
允许用户用紧凑的代码定义模型。这主要由
arg_scope
、大量的高级layers
和variables
来实现。这些工具增加了代码的可读性和维护性,减少了复制、粘贴超参数值出错的可能性,并且简化了超参数的调整。 -
通过提供常用的 regularizers 来简化模型的开发。很多常用的计算机视觉模型(例如 VGG、AlexNet)在 Slim 里面已经有了实现。这些模型开箱可用,并且能够以多种方式进行扩展(例如,给内部的不同层添加 multiple heads)。
-
Slim使得 “复杂模型的扩展” 及 “从一些现存的模型 ckpt 开始训练” 变得容易。
3. Slim 模块的组成 ¶
slim是独由几个独立的模块组成。
arg_scope
:允许用户对该 scope 内的操作定义默认参数。data
:包含了 Slim 模块的 dataset definition、data providers、parallel_reader 及 decoding utilities。evaluation
:评估模型需要的一些东西。layers
:构建模型需要的一些高级 layers。learning
:训练模型需要的一些东西。.losses
:常见的 loss 函数。metrics
:常见的评估指标。nets
:常见的深度网络(例如 VGG、AlexNet)。注意:最新的 Slim 中已经没有nets
了!!!queues
:提供一个容易、简单的开始和关闭 QueueRunners的 content manager。regularizers
:常见的权重 regularizer。variables
:provides convenience wrappers for variable creation and manipulation.
4. 使用 Slim 构建模型 ¶
可以用 slim、variables、layers 和 scopes 来十分简洁地定义模型。下面对各个部分进行了详细描述:
4.1 Slim 变量(Variables) ¶
在原生的tensorflow中创建 Variables 需要一个预定义的值或者一个初始化机制(例如,从高斯分布随机采样)。更近一步,如果需要在一个指定的设备上创建一个 variable,必须进行显式指定。为了减少创建variable需要的代码,slim模块在 variable.py 内提供了一系列的wrapper函数,从而使得变量的定义更加容易。
例如,要创建一个权重 variable,用一个截断的正态分布初始化它,用 l2_loss 进行正则,并将它放在 CPU 上。只需要进行如下的声明即可。
weights = slim.variable('weights',
shape=[10, 10, 3 , 3],
initializer=tf.truncated_normal_initializer(stddev=0.1),
regularizer=slim.l2_regularizer(0.05),
device='/CPU:0')
注意:在原生的 TensorFlow 中,有两种类型的 variables:一般 variables 和 local(transient)variables。绝大数的变量是一般 variables;一旦被创建,他们能够用一个 saver 保存到 disk。local variables 只存在于一个 session 中,不保存到 disk。
Slim 进一步区分了 variables 通过定义 model variables,这些变量代表一个模型的参数。Model variables are trained or fine-tuned during learning and are loaded from a checkpoint during evaluation or inference(例如,由 slim.fully_connected
和 slim.conv2d
创建的 variable)。Non-model 变量指训练、评估过程中需要但推理过程不需要的变量(例如,global_step
训练评估中需要,推理时不需要)。同样,moving average variables might mirror model variables, but the moving averages are not themselves model variables。
通过 Slim 创建和索引(retrieved)model variables 和一般的 variables 很容易:
# Model Variables
weights = slim.model_variable('weights',
shape=[10, 10, 3 , 3],
initializer=tf.truncated_normal_initializer(stddev=0.1),
regularizer=slim.l2_regularizer(0.05),
device='/CPU:0')
model_variables = slim.get_model_variables()
# Regular variables
my_var = slim.variable('my_var',
shape=[20, 1],
initializer=tf.zeros_initializer())
regular_variables_and_model_variables = slim.get_variables()
内部是怎么实现的呢?当你通过 Slim 的 layers 或者直接通过 slim.model_variable
函数创建 model variables 时,Slim 将 variable 添加到了 tf.GrapghKeys.MODEL_VARIABLES
容器中。如果你有自定义的 layers 或者 variable 创建 routine,但是仍然想要使用 Slim 去管理或者想让 Slim 知道你的 model variables,Slim 模块提供了一个很方便的添加 model variable 到对应的容器中的函数:
my_model_variable = CreateViaCustomCode()
# Letting TF-Slim know about the additional variable.
slim.add_model_variable(my_model_variable)
4.2 Slim 层(Layers) ¶
虽然 TensorFlow 的操作集合相当广泛,但神经网络的开发人员通常会在更高的层次上考虑模型,比如:“layers”、“losses”、“metrics” 和 “networks”。layer(例如conv层、fc层、bn层)比 TensorFlow op 更加抽象,并且 layer 通常涉及多个 op。
更进一步,layer 通常(但不总是)有很多与之相关的 variable(可调参数(tunable parameters)),这一点与大多数的基本操作区别很大。例如,神经网络中的一个 conv 层由很多低级的 op 组成:
1. 创建权重和偏差 viriable
2. 对权重和输入进行卷积(输入来自前一层)
3. 卷积结果加上偏差
4. 应用一个激活函数
仅使用基础(plain)的 TensorFlow 代码,这可能相当费力:
input = ...
with tf.name_scope('conv1_1') as scope:
kernel = tf.Variable(tf.truncated_normal([3, 3, 64, 128], dtype=tf.float32,
stddev=1e-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)
为了避免代码的重复。Slim 提供了很多方便的神经网络 layers 的高层 op。例如:与上面的代码对应的 Slim 版的代码:
input = ...
net = slim.conv2d(input, 128, [3, 3], scope='conv1_1')
对于构建神经网络的大量部件,Slim 都提供了标准的实现。这些实现包括但不限于下表中的 op:
Layer | TF-Slim |
---|---|
BiasAdd | slim.bias_add |
BatchNorm | slim.batch_norm |
Conv2d | slim.conv2d |
Conv2dInPlane | slim.conv2d_in_plane |
Conv2dTranspose (Deconv) | slim.conv2d_transpose |
FullyConnected | slim.fully_connected |
AvgPool2D | slim.avg_pool2d |
Dropout | slim.dropout |
Flatten | slim.flatten |
MaxPool2D | slim.max_pool2d |
OneHotEncoding | slim.one_hot_encoding |
SeparableConv2 | slim.separable_conv2d |
UnitNorm | slim.unit_norm |
Slim 还提供了两个 meta-operations:repeat
和 stack
。 tf.contrib.layers.repeat 和 stack,普通函数可以用这两个函数。 它们允许用户去重复的进行(perform)相同的操作(operation)。例如,考虑下面的代码段(来自 VGG 网络,它的 layers 在两个 pooling 层之间进行了很多 conv):
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')
net = slim.max_pool2d(net, [2, 2], scope='pool2')
一个减少代码重复的方法是使用 for
循环:
net = ...
for i in range(3):
net = slim.conv2d(net, 256, [3, 3], scope