TF-Slim
文章来源:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/slim
1-
允许用户通过消除样板代码来更紧凑地定义模型。这是通过使用参数范围和许多高级层和变量来实现的。这些工具提高了可读性和可维护性,降低了复制和粘贴超参数值的错误发生的可能性,并简化了超参数调整。
2-
通过提供常用的正则化器使开发模型变得简单。
3-
已经开发了几种广泛使用的计算机视觉模型(例如,VGG,AlexNet),并且可供用户使用。这些可以用作黑盒子,或者可以以各种方式扩展,例如,通过向不同的内层添加“多个头”。
4-
Slim可以轻松扩展复杂模型,并通过使用预先存在的模型检查点来热启动训练算法。
2-定义模型
结合variables, layers and scopes,可以使用TF-Slim简洁地定义模型。这些元素中的每一个都在下面定义。
Variable
在原tensorflow中创建变量需要预定义值或初始化机制(例如,从高斯随机采样)。此外,如果需要在特定设备(如GPU)上创建变量,则必须明确规范。为了减轻变量创建所需的代码,TF-Slim在variables.py中提供了一组瘦包装函数,允许调用者轻松定义变量。
例如,要创建权重变量,使用截断的正态分布对其进行初始化,使用l2_loss对其进行规范化并将其放在CPU上,只需要声明以下内容:
请注意,在TensorFlow中,有两种类型的变量:常规变量和本地(瞬态)变量。绝大多数变量都是常规变量:一旦创建,就可以使用保护程序将它们保存到磁盘。局部变量是仅在会话期间存在且不保存到磁盘的变量。
TF-Slim通过定义模型变量进一步区分变量,模型变量是表示模型参数的变量。模型变量在学习期间被训练或微调,并在评估或推理期间从checkpoint加载。示例包括slim.fully_connected或slim.conv2d层创建的变量。非模型变量是在学习或评估期间使用的所有其他变量,但不是实际执行推理所必需的。例如,global_step是在学习和评估期间使用的变量,但它实际上不是模型的一部分。同样,移动平均变量可能反映模型变量,但移动平均值本身不是模型变量。
这个怎么用?当您通过TF-Slim的图层创建模型变量或直接通过slim.model_variable函数创建模型变量时,TF-Slim会将变量添加到tf.GraphKeys.MODEL_VARIABLES集合中。如果您有自己的自定义图层或变量创建例程但仍希望TF-Slim管理或了解您的模型变量,该怎么办? TF-Slim提供了一个方便的功能,用于将模型变量添加到其集合中:
Layer:
虽然TensorFlow操作集非常广泛,但神经网络的开发人员通常会根据更高级别的概念来考虑模型列如:”layers”, “losses”, “metrics”, and “networks”诸如卷积层,完全连接层或BatchNorm层之类的层比单个TensorFlow操作更抽象,并且通常涉及多个操作。此外,与更原始的操作不同,层通常(但不总是)具有与之关联的变量(可调参数)。例如,神经网络中的卷积层由几个低级操作组成:
1) 创建weights和biases
2) 使用前一层的输入来计算权重
3) 将biases加到卷积的结果中
4) 使用激活函数
仅使用普通的TensorFlow代码,这可能相当累赘:
为了减少重复复制此代码的需要,TF-Slim提供了许多在更抽象的神经网络层定义的方便操作。例如,将上面的代码与相应TF-Slim代码的调用进行比较:
TF-Slim还提供了两个称为repeat和stack的元操作,允许用户重复执行相同的操作。例如,请考虑VGG网络中的以下片段,其层在池化层之间连续执行多个卷积:
1-减少此代码重复的一种方法是通过for循环:
2-使用TF-Slim的repeat操作可以使这更清洁:
请注意,slim.repeat不仅在线应用相同的参数,而且还足够智能地展开范围,以便为slim.conv2d的每个后续调用分配的范围附加下划线和迭代编号。更具体地说,上面示例中的范围将命名为“conv3 / conv3_1”,“conv3 / conv3_2”和“conv3 / conv3_3”。
3-此外,TF-Slim的slim.stack运算符允许调用者使用不同的参数重复应用相同的操作来创建堆栈或层塔。 slim.stack还为每个创建的操作创建一个新的tf.variable_scope。例如,创建多层感知器(MLP)的简单方法:
在这个例子中,slim.stack调用slim.fully_connected三次,将函数的一次调用的输出传递给下一次。但是,每次调用中隐藏单元的数量从32变为64到128.同样,可以使用堆栈来简化多个卷积的塔:
Scopes
1-除了TensorFlow(name_scope,variable_scope)中的作用域机制类型之外,TF-Slim还添加了一个名为arg_scope的新作用域机制。这个新范围允许用户指定一个或多个操作以及一组参数,这些参数将传递给arg_scope中定义的每个操作。通过示例可以最好地说明此功能。请考虑以下代码段:
2-应该清楚的是,这三个卷积层共享许多相同的超参数。两个具有相同的填充,所有三个具有相同的weights_initializer和weight_regularizer。这段代码难以阅读,并且包含许多应该被考虑的重复值。一种解决方案是使用变量指定默认值
3-此解决方案可确保所有三个卷积共享完全相同的参数值,但不会完全减少代码混乱。通过使用arg_scope,我们可以确保每个层使用相同的值并简化代码:
如示例所示,使用arg_scope使代码更清晰,更简单,更易于维护。请注意,虽然参数值在arg_scope中指定,但它们可以在本地覆盖。特别是,当padding参数设置为’SAME’时,第二个卷积将使用’VALID’的值覆盖它
4-也可以嵌套arg_scopes并在同一范围内使用多个操作。例如:
在此示例中,第一个arg_scope将相同的weights_initializer和weights_regularizer参数应用于其范围内的conv2d和fully_connected层。在第二个arg_scope中,仅指定了conv2d的其他默认参数。
=====================EXAMPLE================================
Working Example: Specifying the VGG16 Layers
1-通过组合TF-Slim变量,操作和范围,我们可以编写一个通常非常复杂的网络,只需很少的代码行。例如,可以使用以下代码段定义整个VGG体系结构
Training Models
训练Tensorflow模型需要模型,损失函数,梯度计算和训练例程,迭代地计算相对于损失的模型权重的梯度并相应地更新权重。 TF-Slim提供常见的损失函数和一组辅助函数,用于运行训练和评估例程
Losses
损失函数定义了我们想要最小化的数量。对于分类问题,这通常是真实分布与跨类别的预测概率分布之间的交叉熵。对于回归问题,这通常是预测值和真值之间的平方和差异。
某些模型,例如多任务学习模型,需要同时使用多个损失函数。换句话说,最终最小化的损失函数是各种其他损失函数的总和。例如,考虑一个模型,该模型预测图像中的场景类型以及每个像素的相机深度。该模型的损失函数将是分类损失和深度预测损失的总和
TF-Slim提供了一种易于使用的机制,用于通过损耗模块定义和跟踪损耗函数。考虑我们想要训练VGG网络的简单情况
在本例中,我们首先创建模型(使用TF-Slim的VGG实现),然后添加标准分类损失。现在,让我们转向我们有一个产生多个输出的多任务模型的情况:
在这个例子中,我们通过调用slim.losses.softmax_cross_entropy和slim.losses.sum_of_squares来添加两个损失。我们可以通过将它们加在一起(total_loss)或通过调用slim.losses.get_total_loss()来获得总损失。这是怎么回事?当您通过TF-Slim创建损失函数时,TF-Slim会将损失添加到特殊的TensorFlow损失函数集合中。这使您可以手动管理总损失,或允许TF-Slim为您管理它们。
如果您想让TF-Slim为您管理损失但具有自定义丢失功能,该怎么办? loss_ops.py还有一个函数可以将这种损失添加到TF-Slims集合中。例如:
在这个例子中,我们可以再次手动产生总损耗函数或让TF-Slim知道额外的损失并让TF-Slim处理损失。
Training Loop
TF-Slim为learning.py中的训练模型提供了一套简单但功能强大的工具。这些功能包括重复测量损耗的Train功能,计算渐变并将模型保存到磁盘,以及用于操作渐变的几个便利功能。例如,一旦我们指定了模型,损失函数和优化方案,我们就可以调用slim.learning.create_train_op和slim.learning.train来执行优化:
在这个例子中,slim.learning.train提供了train_op,用于(a)计算损失和(b)应用梯度步骤。 logdir指定存储检查点和事件文件的目录。我们可以将所采用的梯度步数限制为任意数量。在这种情况下,我们要求采取1000步骤。最后,save_summaries_secs = 300表示我们将每5分钟计算一次摘要,save_interval_secs = 600表示我们将每10分钟保存一次模型检查点。
Working Example: Training the VGG16 Model
为了说明这一点,我们来看一下训练VGG网络的以下示例
Fine-Tuning Existing Models(微调现有模型)
从检查点恢复变量的简要回顾
在训练模型之后,可以使用tf.train.Saver()恢复模型,该模式从给定的检查点恢复变量。在许多情况下,tf.train.Saver()提供了一种简单的机制来恢复所有或仅几个变量。
Partially Restoring Models(部分恢复模型)
通常需要在全新数据集甚至新任务上微调预训练模型。在这些情况下,可以使用TF-Slim的辅助函数来选择要恢复的变量子集:
Restoring models with different variable names(
使用不同的变量名恢复模型)
从检查点恢复变量时,Saver在检查点文件中定位变量名称,并将它们映射到当前图形中的变量。上面,我们通过传递一个变量列表来创建一个saver。在这种情况下,从每个提供的变量的var.op.name中隐式获取要在检查点文件中定位的变量的名称。 当检查点文件中的变量名称与图中的变量名称匹配时,这很有效。但是,有时,我们希望从检查点恢复模型,该检查点的变量与当前图形中的变量具有不同的名称。在这种情况下,我们必须为Saver提供一个字典,该字典从每个检查点变量名称映射到每个图形变量。考虑以下示例,其中检查点变量名称是通过一个简单的函数获得的:
Fine-Tuning a Model on a different task(
在不同的任务上微调模型)
考虑我们拥有预先培训的VGG16模型的情况。该模型在ImageNet数据集上进行了训练,该数据集有1000个类。但是,我们希望将其应用于只有20个类的Pascal VOC数据集。为此,我们可以使用预先训练的模型的值来初始化我们的新模型,不包括最后一层:
Evaluating Models(评估模型)
一旦我们训练了模型(或者甚至在模型忙于训练时),我们希望看到模型在实践中的表现如何。这是通过选择一组评估指标来实现的,评估指标将对模型性能进行评级,实际加载数据的评估代码,执行推理,将结果与基础事实进行比较并记录评估得分。该步骤可以执行一次或周期性地重复。
Metrics(度量)
我们将度量定义为不是损失函数的性能度量(损失在培训期间直接优化),但我们仍然对评估模型的目的感兴趣。例如,我们可能希望最大限度地减少对数损失,但我们感兴趣的指标可能是F1得分(测试准确度)或交叉点联合得分(不可区分,因此不能用作损失)
TF-Slim提供了一组度量操作,可以轻松评估模型。抽象地,计算度量值可以分为三个部分:
1- 初始化:初始化用于计算指标的变量。
2- 聚合:执行用于计算指标的操作(总和等)。
3- 完成:(可选)执行任何最终操作以计算度量值。例如,计算方式,分钟,最大值等。
例如,要计算mean_absolute_error,将两个变量(count和total)初始化为零。在聚合期间,我们观察了一组预测和标签,计算它们的绝对差异并将总数加到总数中。每次我们观察另一个值时,计数会递增。最后,在最终确定期间,将总数除以计数以获得平均值。
以下示例演示了用于声明度量标准的API。由于度量通常在与训练集(计算损失)不同的测试集上进行评估,因此我们假设我们正在使用测试数据:
如示例所示,度量的创建返回两个值:value_op和update_op。 value_op是一个幂等操作,它返回度量的当前值。 update_op是执行上述聚合步骤以及返回度量值的操作。
跟踪每个value_op和update_op可能很费力。为了解决这个问题,TF-Slim提供了两个便利功能:
Working example: Tracking Multiple Metrics(
工作示例:跟踪多个度量标准)
把它们放在一起:
Evaluation Loop(评估循环)
TF-Slim提供了一个评估模块(evaluation.py),它包含帮助函数,用于使用metric_ops.py模块中的指标编写模型评估脚本。其中包括定期运行评估,评估批量数据的指标以及打印和汇总度量结果的功能。例如: