Slim官方教程【转载】
TensorFlow-Slim API 官方教程
TensorFlow 版本: 1.12.0
文章目录
- TF-Slim
- 1. Slim 模块的导入
- 2. Slim 模块的优点
- 3. Slim 模块的组成
- 4. 使用 Slim 构建模型
- 4.1 Slim 变量(Variables)
- 4.2 Slim 层(Layers)
- 4.3 Slim 作用域(Scopes)
- 4.4 实例:创建VGG网络(Working Example: Specifying the VGG16 Layers)
- 5. 使用 Slim 训练模型(Training Models)
- 5.1 Slim 损失函数(Losses)
- 5.2 Slim 训练 Loop(Training Loop)
- 5.3 实例:训练 VGG 模型(Working Example: Training the VGG16 Model)
- 6. 现有模型的微调(Fine-Tuning Existing Models)
- 6.1 从ckpt中恢复变量的简介(Brief Recap on Restoring Variables from a Checkpoint)
- 6.2 部分地恢复模型(Partially Restoring Models)
- 6.3 不同变量名称的模型的恢复(Restoring models with different variable names)
- 6.4 在一个不同的任务上微调模型(Fine-Tuning a Model on a different task)
- 7. 使用 Slim 评估模型(Evaluating Models)
- 7.1 Slim 评价指标(Metrics)
- 7.2 实例:追踪多个评价指标(Working example: Tracking Multiple Metrics)
- 7.3 评估Loop(Evaluation Loop)
- 8. 作者(Authors)
- 9. 参考资料:
TF-Slim
TF-Slim 模块是 TensorFlow 中最好用的 API 之一。尤其是里面引入的 arg_scope、model_variables、repeat、stack。
TF-Slim 是 TensorFlow 中一个用来构建、训练、评估复杂模型的轻量化库。TF-Slim 模块可以和 TensorFlow 中其它API混合使用。
1. Slim 模块的导入
import tensorflow.contrib.slim as slim
- 1
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')
- 1
- 2
- 3
- 4
- 5
注意:在原生的 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()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
内部是怎么实现的呢?当你通过 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)
- 1
- 2
- 3
- 4
4.2 Slim 层(Layers)
虽然 TensorFlow 的操作集合相当广泛,但神经网络的开发人员通常会在更高的层次上考虑模型,比如:“layers”、“losses”、“metrics” 和 “networks”。layer
(例如conv层、fc层、bn层)比 TensorFlow op
更加抽象,并且layer
通常涉及多个 op。
更进一步,layer
通常(但不总是)有很多与之相关的variable
(可调参数(tunable parameters
)),这一点与大多数的基本操作区别很大。例如,神经网络中的一个 conv 层由很多低级的 op 组成:
- 创建权重和偏差
viriable
- 对权重和输入进行卷积(输入来自前一层)
- 卷积结果加上偏差
- 应用一个激活函数
仅使用基础(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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
为了避免代码的重复。Slim 提供了很多方便的神经网络 layers 的高层 op。例如:与上面的代码对应的 Slim 版的代码:
input = ...
net = slim.conv2d(input, 128, [3, 3], scope='conv1_1')
- 1
- 2
对于构建神经网络的大量部件,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')
- 1
- 2
- 3
- 4
- 5
一个减少代码重复的方法是使用 for 循环:
net = ...
for i in range(3):
net = slim.conv2d(net, 256, [3, 3], scope='conv3_%d' % (i+1))
net = slim.max_pool2d(net, [2, 2], scope='pool2')
- 1
- 2
- 3
- 4
使用slim.repeat
可以使上面的代码变得更清晰明了:
net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
net = slim.max_pool2d(net, [2, 2], scope='pool2')
- 1
- 2
注意:slim.repeat
不仅对repeated
单元采用相同的参数,而且它对 repeated
单元的scope
采用更好的命名方式(加下划线,再加迭代序号)。具体来说,上面例子中的scopes
将会命名为 ‘conv3/conv3_1’,‘conv3/conv3_2’,‘conv3/conv3_3’
更进一步,Slim 的slim.stack
允许去重复多个操作with
不同的参数,从而创建一个多层的堆叠结构。slim.stack
也为每一个创建的 op 创造了一个新的tf.variable_scope
。例如,创建一个多层感知器(Multi-Layer Perceptron (MLP))的一个简单方式:
# Verbose way: 冗长的方式
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')
# Equivalent, TF-Slim way using slim.stack:
x = slim.stack(x, slim.fully_connected, [32, 64, 128], scope='fc')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
在这个例子中,slim.stack
调用 slim.fully_connected
三次,并将函数上一次调用的输出传递给下一次调用。但是,在每个调用中,隐形单元(hidden units)的数量分别为 32,64,128。相似地,我们可以使用 stack 去简化多层卷积的堆叠:
# Verbose way: 冗长的方式
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')
# Using stack:
x = slim.stack(x, slim.conv2d, [(32, [3, 3]), (32, [1, 1]), (64, [3, 3]), (64, [1, 1])], scope='core')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4.3 Slim 作用域(Scopes)
除了 TensorFlow 中的scope
机制的几种类型(name_scope,variable_scope)
,slim 增加了一个名为 arg_scope
的新scope
机制。这个新scope
允许用户去给一个或多个 op 指定一套默认参数,这些默认参数将被传给 arg_scope
里使用的的每一个 op。这个功能最好通过例子来说明。考虑一下代码段:
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')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
很明显,这三个卷积层共享很多相同的超参数。两个有相同的 padding
,三个都有相同的 weights_initializer
和weight_regularizer
。这段代码很难读,并且包含了很多重复的值。一个解决方案是使用变量指定默认值:
padding = 'SAME'
initializer = tf.truncated_normal_initializer(stddev=0.01)
regularizer = slim.l2_regularizer(0.0005)
net = slim.conv2d(inputs, 64, [11, 11], 4,
padding=padding,
weights_initializer=initializer,
weights_regularizer=regularizer,
scope='conv1')
net = slim.conv2d(net, 128, [11, 11],
padding='VALID',
weights_initializer=initializer,
weights_regularizer=regularizer,
scope='conv2')
net = slim.conv2d(net, 256, [11, 11],
padding=padding,
weights_initializer=initializer,
weights_regularizer=regularizer,
scope='conv3')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
这个解决方案保证了三个卷积层拥有相同的参数值,但代码仍不够清晰。通过使用一个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')
- 1
- 2
- 3
- 4
- 5
- 6
如上例所示,使用 arg_scope
使代码更清晰、简单并且容易去维护。注意,在 arg_scope
内部指定op的参数值时,指定的参数将取代默认参数。具体来讲,当 padding
参数的默认值被设置为 ‘SAME’
时,第二个卷积的 padding
参数被指定为‘VALID’
。
我们也可以嵌套地使用 arg_scope
,并且在同一个 scope
中可以使用多个op
。例如:
with slim.arg_scope([slim.conv2d, slim.fully_connected],
activation_fn=tf.nn.relu,
weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
with slim.arg_scope([slim.conv2d], stride=1, padding='SAME'):
net = slim.conv2d(inputs, 64, [11, 11], 4, padding='VALID', scope='conv1')
net = slim.conv2d(net, 256, [5, 5],
weights_initializer=tf.truncated_normal_initializer(stddev=0.03),
scope='conv2')
net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在这个例子中,第一个arg_scope
中对 conv2d、fully_connected
层使用相同的 weights_initializer
。在第二 arg_scope
中,给 conv2d
的其它默认参数进行了指定。
4.4 实例:创建VGG网络(Working Example: Specifying the VGG16 Layers)
结合 Slim 模块的variable、operation、scope
,我们能够用很少行的代码实现非常复杂的网络。例如,整个 VGG 架构可以使用下面的代码段实现:
def vgg16(inputs):
with slim.arg_scope([slim.conv2d, slim.fully_connected],
activation_fn=tf.nn.relu,
weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1')
net = slim.max_pool2d(net, [2, 2], scope='pool1')
net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2')
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
net = slim.max_pool2d(net, [2, 2], scope='pool3')
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4')
net = slim.max_pool2d(net, [2, 2], scope='pool4')
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5')
net = slim.max_pool2d(net, [2, 2], scope='pool5')
net = slim.fully_connected(net, 4096, scope='fc6')
net = slim.dropout(net, 0.5, scope='dropout6')
net = slim.fully_connected(net, 4096, scope='fc7')
net = slim.dropout(net, 0.5, scope='dropout7')
net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc8')
return net
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
5. 使用 Slim 训练模型(Training Models)
模型的训练需要一个model
、一个 loss function
、gradient computation
和 一个training routine
(迭代地计算模型的loss
关于权重的梯度,并且根据梯度对权重进行更新)。Slim 提供了常见的loss
函数和一系列训练、评估需要的函数。
5.1 Slim 损失函数(Losses)
根据官方提示,slim.losses
模块将被去除,请使用tf.losses
模块,两者功能完全一致loss
函数定义了一个我们想要优化的量。对于分类问题,loss
一般是正确的类别分布(true distribution)
和预测的类别分布(predicted probability distribution across classes)
之间的交叉熵(cross entropy)
。对于回归问题,loss
一般是
预测值和真实值之间差值的平方和。
一些模型(比如多任务学习模型)需要同时使用多个loss
函数。换言之,loss
函数最终最小化的量是使用的多个loss
函数的和。例如,在一个模型中,同时预测一张图片的场景(the type of scene in an image)和每个像素的景深(the depth from the camera of each pixel)。这个模型的loss
函数将是分类 loss
和depth prediction loss
的和。
Slim 通过losses
模块提供了一个易用的 定义、追踪loss
函数的方法。我们以 VGG 网络的训练为一个简单的例子来说明其的使用:
import tensorflow as tf
import tensorflow.contrib.slim.nets as nets
vgg = nets.vgg
# Load the images and labels.
images, labels = ...
# Create the model.
predictions, _ = vgg.vgg_16(images)
# Define the loss functions and get the total loss.
loss = slim.losses.softmax_cross_entropy(predictions, labels)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在这个例子中,我们首先创建model
(使用 slim.nets.vgg
来实现),并且添加标准的分类损失(loss)。现在,让我们研究下多目标模型(产生多个输出)的情况:
# Load the images and labels.
images, scene_labels, depth_labels = ...
# Create the model.
scene_predictions, depth_predictions = CreateMultiTaskModel(images)
# Define the loss functions and get the total loss.
classification_loss = slim.losses.softmax_cross_entropy(scene_predictions, scene_labels)
sum_of_squares_loss = slim.losses.sum_of_squares(depth_predictions, depth_labels)
# The following two lines have the same effect:
total_loss = classification_loss + sum_of_squares_loss
total_loss = slim.losses.get_total_loss(add_regularization_losses=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在这个例子中,我们有两个loss(slim.losses.softmax_cross_entropy 和 slim.losses.sum_of_squares)
。我们可以通过将两个loss
加起来或者调用slim.losses.get_total_loss()
来得到总的loss(total_loss)
。slim.losses.get_total_loss
的工作原理:当用slim
创建一个loss
函数时,slim
会把loss
添加到一个特定的容器中。这使得我们既可以手动管理总的loss
,也可以使用slim
来管理总loss
。
在有一个自定义的loss
的情况下,如果想让 slim
来管理 losses
,怎么办呢?loss_ops.py
也有一个函数去将自定义的loss
添加到slim
的容器中。例如:
# Load the images and labels.
images, scene_labels, depth_labels, pose_labels = ...
# Create the model.
scene_predictions, depth_predictions, pose_predictions = CreateMultiTaskModel(images)
# Define the loss functions and get the total loss.
classification_loss = slim.losses.softmax_cross_entropy(scene_predictions, scene_labels)
sum_of_squares_loss = slim.losses.sum_of_squares(depth_predictions, depth_labels)
pose_loss = MyCustomLossFunction(pose_predictions, pose_labels)
slim.losses.add_loss(pose_loss) # Letting TF-Slim know about the additional loss.
# The following two ways to compute the total loss are equivalent:
regularization_loss = tf.add_n(slim.losses.get_regularization_losses())
total_loss1 = classification_loss + sum_of_squares_loss + pose_loss + regularization_loss
# (Regularization Loss is included in the total loss by default).
total_loss2 = slim.losses.get_total_loss()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
在这个例子中,我们既可以手动地产生这个总的loss
,也可以让slim
知道额外的loss
并处理losses
。
5.2 Slim 训练 Loop(Training Loop)
Slim 为模型的训练提供了很多简单但强有力的工具(见learning.py
中)。这包含了一个训练函数(重复地计算loss
、计算梯度、将模型保存到 disk)和很多操纵梯度的函数。例如,一旦我们我们已经指定模型、loss
函数、训练方案,我们能够调用 slim.learning.create_train_op 和 slim.learning.train
去执行优化:
g = tf.Graph()
# Create the model and specify the losses...
...
total_loss = slim.losses.get_total_loss()
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
# create_train_op ensures that each time we ask for the loss, the update_ops
# are run and the gradients being computed are applied too.
train_op = slim.learning.create_train_op(total_loss, optimizer)
logdir = ... # Where checkpoints are stored.
slim.learning.train(
train_op,
logdir,
number_of_steps=1000,
save_summaries_secs=300,
save_interval_secs=600)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
在这个例子中,slim.learning.train
中的train_op
主要进行两个操作:(a) 计算loss
;(b) 进行梯度更新。logdir
指定了checkpoint
和event
文件保存的目录。我们可以指定梯度下降步的数量。在这个例子中,我们指定只执行 1000 步梯度下降。save_summaries_secs=300
指定每5分钟计算一次summaries
。save_interval_secs=600
指定每10分钟保存一个model checkpoint
。
5.3 实例:训练 VGG 模型(Working Example: Training the VGG16 Model)
为了说明 Slim 的用法,我们研究下 VGG 网络的训练:
import tensorflow as tf
import tensorflow.contrib.slim.nets as nets
slim = tf.contrib.slim
vgg = nets.vgg
...
train_log_dir = ...
if not tf.gfile.Exists(train_log_dir):
tf.gfile.MakeDirs(train_log_dir)
with tf.Graph().as_default():
# Set up the data loading:
images, labels = ...
# Define the model:
predictions = vgg.vgg_16(images, is_training=True)
# Specify the loss function:
slim.losses.softmax_cross_entropy(predictions, labels)
total_loss = slim.losses.get_total_loss()
tf.summary.scalar('losses/total_loss', total_loss)
# Specify the optimization scheme:
optimizer = tf.train.GradientDescentOptimizer(learning_rate=.001)
# create_train_op that ensures that when we evaluate it to get the loss,
# the update_ops are done and the gradient updates are computed.
train_tensor = slim.learning.create_train_op(total_loss, optimizer)
# Actually runs training.
slim.learning.train(train_tensor, train_log_dir)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
6. 现有模型的微调(Fine-Tuning Existing Models)
6.1 从ckpt中恢复变量的简介(Brief Recap on Restoring Variables from a Checkpoint)
在一个模型训练完毕后,能够使用 tf.train.Saver()
从一个给定的 checkpoint
中恢复Variables
。很多情况下,tf.train.Saver()
提供了一个简单的恢复所有或一小部分变量的方法。
# Create some variables.
v1 = tf.Variable(..., name="v1")
v2 = tf.Variable(..., name="v2")
...
# Add ops to restore all the variables.
restorer = tf.train.Saver()
# Add ops to restore some variables.
restorer = tf.train.Saver([v1, v2])
# Later, launch the model, use the saver to restore variables from disk, and
# do some work with the model.
with tf.Session() as sess:
# Restore variables from disk.
restorer.restore(sess, "/tmp/model.ckpt")
print("Model restored.")
# Do some work with the model
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
更多细节详见Restoring Variables 、Choosing which Variables to Save and Restore及Variables。
6.2 部分地恢复模型(Partially Restoring Models)
很多时候,我们想去在一个新数据集或甚至一个新的任务上微调(fine-tune)一个已经训练好的网络。在这些情况下,我们能够使用slim的辅助函数去选择一部分的变量来进行恢复:
# Create some variables.
v1 = slim.variable(name="v1", ...)
v2 = slim.variable(name="nested/v2", ...)
...
# Get list of variables to restore (which contains only 'v2'). These are all
# equivalent methods:
variables_to_restore = slim.get_variables_by_name("v2")
# or
variables_to_restore = slim.get_variables_by_suffix("2")
# or
variables_to_restore = slim.get_variables(scope="nested")
# or
variables_to_restore = slim.get_variables_to_restore(include=["nested"])
# or
variables_to_restore = slim.get_variables_to_restore(exclude=["v1"])
# Create the saver which will be used to restore the variables.
restorer = tf.train.Saver(variables_to_restore)
with tf.Session() as sess:
# Restore variables from disk.
restorer.restore(sess, "/tmp/model.ckpt")
print("Model restored.")
# Do some work with the model
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
6.3 不同变量名称的模型的恢复(Restoring models with different variable names)
当从一个checkpoint
中恢复variables
时,Saver
会在checkpoint
中寻找variable
的name
,并将它们映射到当前图中的variables
。上面,我们在创建一个saver
的时候,指定了要恢复的Variable
。在这种情况下,会自动调用var.op.name
来获得variables
的name
,然后映射。
当checkpoint
文件中的variable names
和当前图(graph
)中的variable names
匹配时,恢复过程很简单。但有时checkpoint
中的变量和当前图中的变量有不同的name
。在这种情况下,我们必须为Saver
提供一个字典,这个字典将checkpoint
中的variable name
映射到图中的variable
。在下面的例子中,我们用了一个简单的函数来获取checkpoint
中的variables names
:
# Assuming than 'conv1/weights' should be restored from 'vgg16/conv1/weights'
def name_in_checkpoint(var):
return 'vgg16/' + var.op.name
# Assuming than 'conv1/weights' and 'conv1/bias' should be restored from 'conv1/params1' and 'conv1/params2'
def name_in_checkpoint(var):
if "weights" in var.op.name:
return var.op.name.replace("weights", "params1")
if "bias" in var.op.name:
return var.op.name.replace("bias", "params2")
variables_to_restore = slim.get_model_variables()
variables_to_restore = {name_in_checkpoint(var):var for var in variables_to_restore}
restorer = tf.train.Saver(variables_to_restore)
with tf.Session() as sess:
# Restore variables from disk.
restorer.restore(sess, "/tmp/model.ckpt")
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
6.4 在一个不同的任务上微调模型(Fine-Tuning a Model on a different task)
上面的例子中,我们有一个预训练好(pre-trained)的VGG16模型。这个模型是在1000类的ImageNet数据集上训练的。但是,我们想要将它应用到只有20类的Pascal VOC数据集上。为了达到这个目的,我们可以用预训练好的模型的参数来初始化我们的新模型(除了最后一层):
# Load the Pascal VOC data
image, label = MyPascalVocDataLoader(...)
images, labels = tf.train.batch([image, label], batch_size=32)
# Create the model
predictions = vgg.vgg_16(images)
train_op = slim.learning.create_train_op(...)
# Specify where the Model, trained on ImageNet, was saved.
model_path = '/path/to/pre_trained_on_imagenet.checkpoint'
# Specify where the new model will live:
log_dir = '/path/to/my_pascal_model_dir/'
# Restore only the convolutional layers:
variables_to_restore = slim.get_variables_to_restore(exclude=['fc6', 'fc7', 'fc8'])
init_fn = slim.assign_from_checkpoint_fn(model_path, variables_to_restore)
# Start training.
slim.learning.train(train_op, log_dir, init_fn=init_fn)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
7. 使用 Slim 评估模型(Evaluating Models)
一旦我们训练完一个模型(或者甚至在模型训练过程中),我们想看看模型在实践中的表现如何。这可以通过选择一组评价指标(evaluation metrics)来实现,这将对模型的性能(performance)进行评估(grade),并且评估代码会真正地加载数据(actually loads the data),进行推理(performs inference),将推理结果和真实情况(ground truth)进行比较,记录评估分数(records the evaluation scores)。该步骤可以执行一次或者周期性重复执行(repeated periodically)。
7.1 Slim 评价指标(Metrics)
我们定义了一个评价指标来作为性能的衡量,这个评价指标不是一个loss
函数(在训练过程中,losses
是被优化的量),但是这个评价指标对于我们模型的评估十分重要。例如,我们可能想要去最小化对数损失函数(log loss
),但是我们的想要的评价指标可能是F1 score (test accuracy)
或者IoU (Intersection Over Union score)
(这是不可微分的,所以不能被用作losses)
slim提供了很多评价指标操作(metric operation
),这些op使得模型的评估变得容易。理论上,计算评价指标的值能够被分为三部分:
初始化(Initialization):初始化评价指标相关的一些variables
聚合(Aggregation):执行很多计算评价指标需要的操作(sum等)
完成(Finalization):(可选) 执行任何计算评价指标的最终操作。例如,计算均值(means)、最小值(mins)、最大值(maxes)等。
例如,为了计算mean_absolute_error
,count
和total
两个变量被初始化为0。在聚合过程中,我们观测(observe)一些predictions
和labels
,计算误差的绝对值,并且对其求和total
。每一次,我们观察另一个值,count
就增加一点。最后,在完成阶段,total
除以count
从而获得误差绝对值的均值。
下面的例子说明了定义metrics的API的使用方法。因为metrics通常在测试数据集上计算,而测试集与训练集(通常loss是在训练集上计算)是不同的,我们将假设正在使用测试数据:
images, labels = LoadTestData(...)
predictions = MyModel(images)
mae_value_op, mae_update_op = slim.metrics.streaming_mean_absolute_error(predictions, labels)
mre_value_op, mre_update_op = slim.metrics.streaming_mean_relative_error(predictions, labels)
pl_value_op, pl_update_op = slim.metrics.percentage_less(mean_relative_errors, 0.3)
- 1
- 2
- 3
- 4
- 5
- 6
正如例子所述,创建一个metric
会返回两个值:一个value_op
一个update_op
。value_op
是一个返回metric
当前值的idempotent op
。update_op
执行上面提及的聚合步骤(aggregation step
)同时返回metric
的值。
追踪每一个value_op
及update_op
是非常费力的。为了处理这个问题,slim提供了两个很方便的函数:
# Aggregates the value and update ops in two lists:
value_ops, update_ops = slim.metrics.aggregate_metrics(
slim.metrics.streaming_mean_absolute_error(predictions, labels),
slim.metrics.streaming_mean_squared_error(predictions, labels))
# Aggregates the value and update ops in two dictionaries:
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
"eval/mean_absolute_error": slim.metrics.streaming_mean_absolute_error(predictions, labels),
"eval/mean_squared_error": slim.metrics.streaming_mean_squared_error(predictions, labels),
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
7.2 实例:追踪多个评价指标(Working example: Tracking Multiple Metrics)
把所有的代码放在一起:
import tensorflow as tf
import tensorflow.contrib.slim.nets as nets
slim = tf.contrib.slim
vgg = nets.vgg
# Load the data
images, labels = load_data(...)
# Define the network
predictions = vgg.vgg_16(images)
# Choose the metrics to compute:
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
"eval/mean_absolute_error": slim.metrics.streaming_mean_absolute_error(predictions, labels),
"eval/mean_squared_error": slim.metrics.streaming_mean_squared_error(predictions, labels),
})
# Evaluate the model using 1000 batches of data:
num_batches = 1000
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
sess.run(tf.local_variables_initializer())
for batch_id in range(num_batches):
sess.run(names_to_updates.values())
metric_values = sess.run(names_to_values.values())
for metric, value in zip(names_to_values.keys(), metric_values):
print('Metric %s has value: %f' % (metric, value))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
注意:metric_ops.py
可以在不使用 layers.py
和loss_ops.py
的情况下单独使用。
7.3 评估Loop(Evaluation Loop)
slim提供了一个评估模块(evaluation.py),这个模块包含了编写模型评估脚本(scripts
)的辅助函数(这些函数定义在metric_ops.py
模块)。这些函数包括周期性运行评估、在batch上计算metrics
、print
和summarizing metric
结果。例如:
import tensorflow as tf
slim = tf.contrib.slim
# Load the data
images, labels = load_data(...)
# Define the network
predictions = MyModel(images)
# Choose the metrics to compute:
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
'accuracy': slim.metrics.accuracy(predictions, labels),
'precision': slim.metrics.precision(predictions, labels),
'recall': slim.metrics.recall(mean_relative_errors, 0.3),
})
# Create the summary ops such that they also print out to std output:
summary_ops = []
for metric_name, metric_value in names_to_values.iteritems():
op = tf.summary.scalar(metric_name, metric_value)
op = tf.Print(op, [metric_value], metric_name)
summary_ops.append(op)
num_examples = 10000
batch_size = 32
num_batches = math.ceil(num_examples / float(batch_size))
# Setup the global step.
slim.get_or_create_global_step()
output_dir = ... # Where the summaries are stored.
eval_interval_secs = ... # How often to run the evaluation.
slim.evaluation.evaluation_loop(
'local',
checkpoint_dir,
log_dir,
num_evals=num_batches,
eval_op=names_to_updates.values(),
summary_op=tf.summary.merge(summary_ops),
eval_interval_secs=eval_interval_secs)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
8. 作者(Authors)
Sergio Guadarrama and Nathan Silberman
9. 参考资料:
tensorflow.contrib.slim 模块官方说明文件: README.md
作者:黑暗星球
来源:CSDN
原文:https://blog.csdn.net/u014061630/article/details/80632736
版权声明:本文为博主原创文章,转载请附上博文链接!
TensorFlow-Slim API 官方教程
TensorFlow 版本: 1.12.0
文章目录
- TF-Slim
- 1. Slim 模块的导入
- 2. Slim 模块的优点
- 3. Slim 模块的组成
- 4. 使用 Slim 构建模型
- 4.1 Slim 变量(Variables)
- 4.2 Slim 层(Layers)
- 4.3 Slim 作用域(Scopes)
- 4.4 实例:创建VGG网络(Working Example: Specifying the VGG16 Layers)
- 5. 使用 Slim 训练模型(Training Models)
- 5.1 Slim 损失函数(Losses)
- 5.2 Slim 训练 Loop(Training Loop)
- 5.3 实例:训练 VGG 模型(Working Example: Training the VGG16 Model)
- 6. 现有模型的微调(Fine-Tuning Existing Models)
- 6.1 从ckpt中恢复变量的简介(Brief Recap on Restoring Variables from a Checkpoint)
- 6.2 部分地恢复模型(Partially Restoring Models)
- 6.3 不同变量名称的模型的恢复(Restoring models with different variable names)
- 6.4 在一个不同的任务上微调模型(Fine-Tuning a Model on a different task)
- 7. 使用 Slim 评估模型(Evaluating Models)
- 7.1 Slim 评价指标(Metrics)
- 7.2 实例:追踪多个评价指标(Working example: Tracking Multiple Metrics)
- 7.3 评估Loop(Evaluation Loop)
- 8. 作者(Authors)
- 9. 参考资料:
TF-Slim
TF-Slim 模块是 TensorFlow 中最好用的 API 之一。尤其是里面引入的 arg_scope、model_variables、repeat、stack。
TF-Slim 是 TensorFlow 中一个用来构建、训练、评估复杂模型的轻量化库。TF-Slim 模块可以和 TensorFlow 中其它API混合使用。
1. Slim 模块的导入
import tensorflow.contrib.slim as slim
- 1
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')
- 1
- 2
- 3
- 4
- 5
注意:在原生的 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()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
内部是怎么实现的呢?当你通过 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)
- 1
- 2
- 3
- 4
4.2 Slim 层(Layers)
虽然 TensorFlow 的操作集合相当广泛,但神经网络的开发人员通常会在更高的层次上考虑模型,比如:“layers”、“losses”、“metrics” 和 “networks”。layer
(例如conv层、fc层、bn层)比 TensorFlow op
更加抽象,并且layer
通常涉及多个 op。
更进一步,layer
通常(但不总是)有很多与之相关的variable
(可调参数(tunable parameters
)),这一点与大多数的基本操作区别很大。例如,神经网络中的一个 conv 层由很多低级的 op 组成:
- 创建权重和偏差
viriable
- 对权重和输入进行卷积(输入来自前一层)
- 卷积结果加上偏差
- 应用一个激活函数
仅使用基础(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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
为了避免代码的重复。Slim 提供了很多方便的神经网络 layers 的高层 op。例如:与上面的代码对应的 Slim 版的代码:
input = ...
net = slim.conv2d(input, 128, [3, 3], scope='conv1_1')
- 1
- 2
对于构建神经网络的大量部件,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')
- 1
- 2
- 3
- 4
- 5
一个减少代码重复的方法是使用 for 循环:
net = ...
for i in range(3):
net = slim.conv2d(net, 256, [3, 3], scope='conv3_%d' % (i+1))
net = slim.max_pool2d(net, [2, 2], scope='pool2')
- 1
- 2
- 3
- 4
使用slim.repeat
可以使上面的代码变得更清晰明了:
net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
net = slim.max_pool2d(net, [2, 2], scope='pool2')
- 1
- 2
注意:slim.repeat
不仅对repeated
单元采用相同的参数,而且它对 repeated
单元的scope
采用更好的命名方式(加下划线,再加迭代序号)。具体来说,上面例子中的scopes
将会命名为 ‘conv3/conv3_1’,‘conv3/conv3_2’,‘conv3/conv3_3’
更进一步,Slim 的slim.stack
允许去重复多个操作with
不同的参数,从而创建一个多层的堆叠结构。slim.stack
也为每一个创建的 op 创造了一个新的tf.variable_scope
。例如,创建一个多层感知器(Multi-Layer Perceptron (MLP))的一个简单方式:
# Verbose way: 冗长的方式
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')
# Equivalent, TF-Slim way using slim.stack:
x = slim.stack(x, slim.fully_connected, [32, 64, 128], scope='fc')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
在这个例子中,slim.stack
调用 slim.fully_connected
三次,并将函数上一次调用的输出传递给下一次调用。但是,在每个调用中,隐形单元(hidden units)的数量分别为 32,64,128。相似地,我们可以使用 stack 去简化多层卷积的堆叠:
# Verbose way: 冗长的方式
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')
# Using stack:
x = slim.stack(x, slim.conv2d, [(32, [3, 3]), (32, [1, 1]), (64, [3, 3]), (64, [1, 1])], scope='core')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4.3 Slim 作用域(Scopes)
除了 TensorFlow 中的scope
机制的几种类型(name_scope,variable_scope)
,slim 增加了一个名为 arg_scope
的新scope
机制。这个新scope
允许用户去给一个或多个 op 指定一套默认参数,这些默认参数将被传给 arg_scope
里使用的的每一个 op。这个功能最好通过例子来说明。考虑一下代码段:
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')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
很明显,这三个卷积层共享很多相同的超参数。两个有相同的 padding
,三个都有相同的 weights_initializer
和weight_regularizer
。这段代码很难读,并且包含了很多重复的值。一个解决方案是使用变量指定默认值:
padding = 'SAME'
initializer = tf.truncated_normal_initializer(stddev=0.01)
regularizer = slim.l2_regularizer(0.0005)
net = slim.conv2d(inputs, 64, [11, 11], 4,
padding=padding,
weights_initializer=initializer,
weights_regularizer=regularizer,
scope='conv1')
net = slim.conv2d(net, 128, [11, 11],
padding='VALID',
weights_initializer=initializer,
weights_regularizer=regularizer,
scope='conv2')
net = slim.conv2d(net, 256, [11, 11],
padding=padding,
weights_initializer=initializer,
weights_regularizer=regularizer,
scope='conv3')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
这个解决方案保证了三个卷积层拥有相同的参数值,但代码仍不够清晰。通过使用一个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')
- 1
- 2
- 3
- 4
- 5
- 6
如上例所示,使用 arg_scope
使代码更清晰、简单并且容易去维护。注意,在 arg_scope
内部指定op的参数值时,指定的参数将取代默认参数。具体来讲,当 padding
参数的默认值被设置为 ‘SAME’
时,第二个卷积的 padding
参数被指定为‘VALID’
。
我们也可以嵌套地使用 arg_scope
,并且在同一个 scope
中可以使用多个op
。例如:
with slim.arg_scope([slim.conv2d, slim.fully_connected],
activation_fn=tf.nn.relu,
weights_initializer=tf.truncated_normal_initializer(stddev=0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
with slim.arg_scope([slim.conv2d], stride=1, padding='SAME'):
net = slim.conv2d(inputs, 64, [11, 11], 4, padding='VALID', scope='conv1')
net = slim.conv2d(net, 256, [5, 5],
weights_initializer=tf.truncated_normal_initializer(stddev=0.03),
scope='conv2')
net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在这个例子中,第一个arg_scope
中对 conv2d、fully_connected
层使用相同的 weights_initializer
。在第二 arg_scope
中,给 conv2d
的其它默认参数进行了指定。
4.4 实例:创建VGG网络(Working Example: Specifying the VGG16 Layers)
结合 Slim 模块的variable、operation、scope
,我们能够用很少行的代码实现非常复杂的网络。例如,整个 VGG 架构可以使用下面的代码段实现:
def vgg16(inputs):
with slim.arg_scope([slim.conv2d, slim.fully_connected],
activation_fn=tf.nn.relu,
weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1')
net = slim.max_pool2d(net, [2, 2], scope='pool1')
net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2')
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
net = slim.max_pool2d(net, [2, 2], scope='pool3')
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4')
net = slim.max_pool2d(net, [2, 2], scope='pool4')
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5')
net = slim.max_pool2d(net, [2, 2], scope='pool5')
net = slim.fully_connected(net, 4096, scope='fc6')
net = slim.dropout(net, 0.5, scope='dropout6')
net = slim.fully_connected(net, 4096, scope='fc7')
net = slim.dropout(net, 0.5, scope='dropout7')
net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc8')
return net
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
5. 使用 Slim 训练模型(Training Models)
模型的训练需要一个model
、一个 loss function
、gradient computation
和 一个training routine
(迭代地计算模型的loss
关于权重的梯度,并且根据梯度对权重进行更新)。Slim 提供了常见的loss
函数和一系列训练、评估需要的函数。
5.1 Slim 损失函数(Losses)
根据官方提示,slim.losses
模块将被去除,请使用tf.losses
模块,两者功能完全一致loss
函数定义了一个我们想要优化的量。对于分类问题,loss
一般是正确的类别分布(true distribution)
和预测的类别分布(predicted probability distribution across classes)
之间的交叉熵(cross entropy)
。对于回归问题,loss
一般是
预测值和真实值之间差值的平方和。
一些模型(比如多任务学习模型)需要同时使用多个loss
函数。换言之,loss
函数最终最小化的量是使用的多个loss
函数的和。例如,在一个模型中,同时预测一张图片的场景(the type of scene in an image)和每个像素的景深(the depth from the camera of each pixel)。这个模型的loss
函数将是分类 loss
和depth prediction loss
的和。
Slim 通过losses
模块提供了一个易用的 定义、追踪loss
函数的方法。我们以 VGG 网络的训练为一个简单的例子来说明其的使用:
import tensorflow as tf
import tensorflow.contrib.slim.nets as nets
vgg = nets.vgg
# Load the images and labels.
images, labels = ...
# Create the model.
predictions, _ = vgg.vgg_16(images)
# Define the loss functions and get the total loss.
loss = slim.losses.softmax_cross_entropy(predictions, labels)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在这个例子中,我们首先创建model
(使用 slim.nets.vgg
来实现),并且添加标准的分类损失(loss)。现在,让我们研究下多目标模型(产生多个输出)的情况:
# Load the images and labels.
images, scene_labels, depth_labels = ...
# Create the model.
scene_predictions, depth_predictions = CreateMultiTaskModel(images)
# Define the loss functions and get the total loss.
classification_loss = slim.losses.softmax_cross_entropy(scene_predictions, scene_labels)
sum_of_squares_loss = slim.losses.sum_of_squares(depth_predictions, depth_labels)
# The following two lines have the same effect:
total_loss = classification_loss + sum_of_squares_loss
total_loss = slim.losses.get_total_loss(add_regularization_losses=False)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在这个例子中,我们有两个loss(slim.losses.softmax_cross_entropy 和 slim.losses.sum_of_squares)
。我们可以通过将两个loss
加起来或者调用slim.losses.get_total_loss()
来得到总的loss(total_loss)
。slim.losses.get_total_loss
的工作原理:当用slim
创建一个loss
函数时,slim
会把loss
添加到一个特定的容器中。这使得我们既可以手动管理总的loss
,也可以使用slim
来管理总loss
。
在有一个自定义的loss
的情况下,如果想让 slim
来管理 losses
,怎么办呢?loss_ops.py
也有一个函数去将自定义的loss
添加到slim
的容器中。例如:
# Load the images and labels.
images, scene_labels, depth_labels, pose_labels = ...
# Create the model.
scene_predictions, depth_predictions, pose_predictions = CreateMultiTaskModel(images)
# Define the loss functions and get the total loss.
classification_loss = slim.losses.softmax_cross_entropy(scene_predictions, scene_labels)
sum_of_squares_loss = slim.losses.sum_of_squares(depth_predictions, depth_labels)
pose_loss = MyCustomLossFunction(pose_predictions, pose_labels)
slim.losses.add_loss(pose_loss) # Letting TF-Slim know about the additional loss.
# The following two ways to compute the total loss are equivalent:
regularization_loss = tf.add_n(slim.losses.get_regularization_losses())
total_loss1 = classification_loss + sum_of_squares_loss + pose_loss + regularization_loss
# (Regularization Loss is included in the total loss by default).
total_loss2 = slim.losses.get_total_loss()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
在这个例子中,我们既可以手动地产生这个总的loss
,也可以让slim
知道额外的loss
并处理losses
。
5.2 Slim 训练 Loop(Training Loop)
Slim 为模型的训练提供了很多简单但强有力的工具(见learning.py
中)。这包含了一个训练函数(重复地计算loss
、计算梯度、将模型保存到 disk)和很多操纵梯度的函数。例如,一旦我们我们已经指定模型、loss
函数、训练方案,我们能够调用 slim.learning.create_train_op 和 slim.learning.train
去执行优化:
g = tf.Graph()
# Create the model and specify the losses...
...
total_loss = slim.losses.get_total_loss()
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
# create_train_op ensures that each time we ask for the loss, the update_ops
# are run and the gradients being computed are applied too.
train_op = slim.learning.create_train_op(total_loss, optimizer)
logdir = ... # Where checkpoints are stored.
slim.learning.train(
train_op,
logdir,
number_of_steps=1000,
save_summaries_secs=300,
save_interval_secs=600)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
在这个例子中,slim.learning.train
中的train_op
主要进行两个操作:(a) 计算loss
;(b) 进行梯度更新。logdir
指定了checkpoint
和event
文件保存的目录。我们可以指定梯度下降步的数量。在这个例子中,我们指定只执行 1000 步梯度下降。save_summaries_secs=300
指定每5分钟计算一次summaries
。save_interval_secs=600
指定每10分钟保存一个model checkpoint
。
5.3 实例:训练 VGG 模型(Working Example: Training the VGG16 Model)
为了说明 Slim 的用法,我们研究下 VGG 网络的训练:
import tensorflow as tf
import tensorflow.contrib.slim.nets as nets
slim = tf.contrib.slim
vgg = nets.vgg
...
train_log_dir = ...
if not tf.gfile.Exists(train_log_dir):
tf.gfile.MakeDirs(train_log_dir)
with tf.Graph().as_default():
# Set up the data loading:
images, labels = ...
# Define the model:
predictions = vgg.vgg_16(images, is_training=True)
# Specify the loss function:
slim.losses.softmax_cross_entropy(predictions, labels)
total_loss = slim.losses.get_total_loss()
tf.summary.scalar('losses/total_loss', total_loss)
# Specify the optimization scheme:
optimizer = tf.train.GradientDescentOptimizer(learning_rate=.001)
# create_train_op that ensures that when we evaluate it to get the loss,
# the update_ops are done and the gradient updates are computed.
train_tensor = slim.learning.create_train_op(total_loss, optimizer)
# Actually runs training.
slim.learning.train(train_tensor, train_log_dir)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
6. 现有模型的微调(Fine-Tuning Existing Models)
6.1 从ckpt中恢复变量的简介(Brief Recap on Restoring Variables from a Checkpoint)
在一个模型训练完毕后,能够使用 tf.train.Saver()
从一个给定的 checkpoint
中恢复Variables
。很多情况下,tf.train.Saver()
提供了一个简单的恢复所有或一小部分变量的方法。
# Create some variables.
v1 = tf.Variable(..., name="v1")
v2 = tf.Variable(..., name="v2")
...
# Add ops to restore all the variables.
restorer = tf.train.Saver()
# Add ops to restore some variables.
restorer = tf.train.Saver([v1, v2])
# Later, launch the model, use the saver to restore variables from disk, and
# do some work with the model.
with tf.Session() as sess:
# Restore variables from disk.
restorer.restore(sess, "/tmp/model.ckpt")
print("Model restored.")
# Do some work with the model
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
更多细节详见Restoring Variables 、Choosing which Variables to Save and Restore及Variables。
6.2 部分地恢复模型(Partially Restoring Models)
很多时候,我们想去在一个新数据集或甚至一个新的任务上微调(fine-tune)一个已经训练好的网络。在这些情况下,我们能够使用slim的辅助函数去选择一部分的变量来进行恢复:
# Create some variables.
v1 = slim.variable(name="v1", ...)
v2 = slim.variable(name="nested/v2", ...)
...
# Get list of variables to restore (which contains only 'v2'). These are all
# equivalent methods:
variables_to_restore = slim.get_variables_by_name("v2")
# or
variables_to_restore = slim.get_variables_by_suffix("2")
# or
variables_to_restore = slim.get_variables(scope="nested")
# or
variables_to_restore = slim.get_variables_to_restore(include=["nested"])
# or
variables_to_restore = slim.get_variables_to_restore(exclude=["v1"])
# Create the saver which will be used to restore the variables.
restorer = tf.train.Saver(variables_to_restore)
with tf.Session() as sess:
# Restore variables from disk.
restorer.restore(sess, "/tmp/model.ckpt")
print("Model restored.")
# Do some work with the model
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
6.3 不同变量名称的模型的恢复(Restoring models with different variable names)
当从一个checkpoint
中恢复variables
时,Saver
会在checkpoint
中寻找variable
的name
,并将它们映射到当前图中的variables
。上面,我们在创建一个saver
的时候,指定了要恢复的Variable
。在这种情况下,会自动调用var.op.name
来获得variables
的name
,然后映射。
当checkpoint
文件中的variable names
和当前图(graph
)中的variable names
匹配时,恢复过程很简单。但有时checkpoint
中的变量和当前图中的变量有不同的name
。在这种情况下,我们必须为Saver
提供一个字典,这个字典将checkpoint
中的variable name
映射到图中的variable
。在下面的例子中,我们用了一个简单的函数来获取checkpoint
中的variables names
:
# Assuming than 'conv1/weights' should be restored from 'vgg16/conv1/weights'
def name_in_checkpoint(var):
return 'vgg16/' + var.op.name
# Assuming than 'conv1/weights' and 'conv1/bias' should be restored from 'conv1/params1' and 'conv1/params2'
def name_in_checkpoint(var):
if "weights" in var.op.name:
return var.op.name.replace("weights", "params1")
if "bias" in var.op.name:
return var.op.name.replace("bias", "params2")
variables_to_restore = slim.get_model_variables()
variables_to_restore = {name_in_checkpoint(var):var for var in variables_to_restore}
restorer = tf.train.Saver(variables_to_restore)
with tf.Session() as sess:
# Restore variables from disk.
restorer.restore(sess, "/tmp/model.ckpt")
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
6.4 在一个不同的任务上微调模型(Fine-Tuning a Model on a different task)
上面的例子中,我们有一个预训练好(pre-trained)的VGG16模型。这个模型是在1000类的ImageNet数据集上训练的。但是,我们想要将它应用到只有20类的Pascal VOC数据集上。为了达到这个目的,我们可以用预训练好的模型的参数来初始化我们的新模型(除了最后一层):
# Load the Pascal VOC data
image, label = MyPascalVocDataLoader(...)
images, labels = tf.train.batch([image, label], batch_size=32)
# Create the model
predictions = vgg.vgg_16(images)
train_op = slim.learning.create_train_op(...)
# Specify where the Model, trained on ImageNet, was saved.
model_path = '/path/to/pre_trained_on_imagenet.checkpoint'
# Specify where the new model will live:
log_dir = '/path/to/my_pascal_model_dir/'
# Restore only the convolutional layers:
variables_to_restore = slim.get_variables_to_restore(exclude=['fc6', 'fc7', 'fc8'])
init_fn = slim.assign_from_checkpoint_fn(model_path, variables_to_restore)
# Start training.
slim.learning.train(train_op, log_dir, init_fn=init_fn)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
7. 使用 Slim 评估模型(Evaluating Models)
一旦我们训练完一个模型(或者甚至在模型训练过程中),我们想看看模型在实践中的表现如何。这可以通过选择一组评价指标(evaluation metrics)来实现,这将对模型的性能(performance)进行评估(grade),并且评估代码会真正地加载数据(actually loads the data),进行推理(performs inference),将推理结果和真实情况(ground truth)进行比较,记录评估分数(records the evaluation scores)。该步骤可以执行一次或者周期性重复执行(repeated periodically)。
7.1 Slim 评价指标(Metrics)
我们定义了一个评价指标来作为性能的衡量,这个评价指标不是一个loss
函数(在训练过程中,losses
是被优化的量),但是这个评价指标对于我们模型的评估十分重要。例如,我们可能想要去最小化对数损失函数(log loss
),但是我们的想要的评价指标可能是F1 score (test accuracy)
或者IoU (Intersection Over Union score)
(这是不可微分的,所以不能被用作losses)
slim提供了很多评价指标操作(metric operation
),这些op使得模型的评估变得容易。理论上,计算评价指标的值能够被分为三部分:
初始化(Initialization):初始化评价指标相关的一些variables
聚合(Aggregation):执行很多计算评价指标需要的操作(sum等)
完成(Finalization):(可选) 执行任何计算评价指标的最终操作。例如,计算均值(means)、最小值(mins)、最大值(maxes)等。
例如,为了计算mean_absolute_error
,count
和total
两个变量被初始化为0。在聚合过程中,我们观测(observe)一些predictions
和labels
,计算误差的绝对值,并且对其求和total
。每一次,我们观察另一个值,count
就增加一点。最后,在完成阶段,total
除以count
从而获得误差绝对值的均值。
下面的例子说明了定义metrics的API的使用方法。因为metrics通常在测试数据集上计算,而测试集与训练集(通常loss是在训练集上计算)是不同的,我们将假设正在使用测试数据:
images, labels = LoadTestData(...)
predictions = MyModel(images)
mae_value_op, mae_update_op = slim.metrics.streaming_mean_absolute_error(predictions, labels)
mre_value_op, mre_update_op = slim.metrics.streaming_mean_relative_error(predictions, labels)
pl_value_op, pl_update_op = slim.metrics.percentage_less(mean_relative_errors, 0.3)
- 1
- 2
- 3
- 4
- 5
- 6
正如例子所述,创建一个metric
会返回两个值:一个value_op
一个update_op
。value_op
是一个返回metric
当前值的idempotent op
。update_op
执行上面提及的聚合步骤(aggregation step
)同时返回metric
的值。
追踪每一个value_op
及update_op
是非常费力的。为了处理这个问题,slim提供了两个很方便的函数:
# Aggregates the value and update ops in two lists:
value_ops, update_ops = slim.metrics.aggregate_metrics(
slim.metrics.streaming_mean_absolute_error(predictions, labels),
slim.metrics.streaming_mean_squared_error(predictions, labels))
# Aggregates the value and update ops in two dictionaries:
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
"eval/mean_absolute_error": slim.metrics.streaming_mean_absolute_error(predictions, labels),
"eval/mean_squared_error": slim.metrics.streaming_mean_squared_error(predictions, labels),
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
7.2 实例:追踪多个评价指标(Working example: Tracking Multiple Metrics)
把所有的代码放在一起:
import tensorflow as tf
import tensorflow.contrib.slim.nets as nets
slim = tf.contrib.slim
vgg = nets.vgg
# Load the data
images, labels = load_data(...)
# Define the network
predictions = vgg.vgg_16(images)
# Choose the metrics to compute:
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
"eval/mean_absolute_error": slim.metrics.streaming_mean_absolute_error(predictions, labels),
"eval/mean_squared_error": slim.metrics.streaming_mean_squared_error(predictions, labels),
})
# Evaluate the model using 1000 batches of data:
num_batches = 1000
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
sess.run(tf.local_variables_initializer())
for batch_id in range(num_batches):
sess.run(names_to_updates.values())
metric_values = sess.run(names_to_values.values())
for metric, value in zip(names_to_values.keys(), metric_values):
print('Metric %s has value: %f' % (metric, value))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
注意:metric_ops.py
可以在不使用 layers.py
和loss_ops.py
的情况下单独使用。
7.3 评估Loop(Evaluation Loop)
slim提供了一个评估模块(evaluation.py),这个模块包含了编写模型评估脚本(scripts
)的辅助函数(这些函数定义在metric_ops.py
模块)。这些函数包括周期性运行评估、在batch上计算metrics
、print
和summarizing metric
结果。例如:
import tensorflow as tf
slim = tf.contrib.slim
# Load the data
images, labels = load_data(...)
# Define the network
predictions = MyModel(images)
# Choose the metrics to compute:
names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({
'accuracy': slim.metrics.accuracy(predictions, labels),
'precision': slim.metrics.precision(predictions, labels),
'recall': slim.metrics.recall(mean_relative_errors, 0.3),
})
# Create the summary ops such that they also print out to std output:
summary_ops = []
for metric_name, metric_value in names_to_values.iteritems():
op = tf.summary.scalar(metric_name, metric_value)
op = tf.Print(op, [metric_value], metric_name)
summary_ops.append(op)
num_examples = 10000
batch_size = 32
num_batches = math.ceil(num_examples / float(batch_size))
# Setup the global step.
slim.get_or_create_global_step()
output_dir = ... # Where the summaries are stored.
eval_interval_secs = ... # How often to run the evaluation.
slim.evaluation.evaluation_loop(
'local',
checkpoint_dir,
log_dir,
num_evals=num_batches,
eval_op=names_to_updates.values(),
summary_op=tf.summary.merge(summary_ops),
eval_interval_secs=eval_interval_secs)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
8. 作者(Authors)
Sergio Guadarrama and Nathan Silberman
9. 参考资料:
tensorflow.contrib.slim 模块官方说明文件: README.md
作者:黑暗星球
来源:CSDN
原文:https://blog.csdn.net/u014061630/article/details/80632736
版权声明:本文为博主原创文章,转载请附上博文链接!