当前位置: 首页 > 工具软件 > SLiM > 使用案例 >

学习slim(一)官方说明

许昆
2023-12-01

从11月正式开始学习DL这块,由于项目这块目前都是用tensorflow写工程,所以学习DL同时也在学习tf,感觉在tf基础上出现了很多封装好的库,比如slim,主要也是同事们在用,所以自己也学习一下。
详细说明介绍可以参考tensorflow官方提供的说明文档,本文基本上是对说明的翻译
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/slim/README.md

使用方法

导入slim模块

import tensorflow.contrib.slim as slim

Why TF-Slim?

TF-Slim是一个可以让你轻松构建、训练、评价网络的库

  • 用户可以自定义模型,方便的使用argument scoping ,layers和variable来简洁的定义网络。

  • 提供常用的regularizers让开发模型更简单

  • 多种常用机器视觉模型(如VGG,AlexNet)都在slim中已经实现并且可以提供给拥护使用。这些可以作为黑盒或者使用多种方式进行扩展,例如在不同内层中添加“multiple heads”

  • Slim 使得扩展复杂模型更加简单,并且可以通过加载预训练模型的checkpoints热启动训练算法。

TF-Slim组成结构

主要由以下几个部分组成:

定义模型(Defining Models)

Variables

Variables
在原生tensorflow中需要预定义值或者定义一个初始化机制(如 Gaussian随机采样)。并且,如果一个变量需要在特定的设备上生成,如一个GPU,则必须使用made explicit 进行声明。为了减少生成变量所需的代码两,TF-Slim提供了一套简单封装的函数variables.py 以使调用者可以简单定义变量。

例,要产生一个weight 变量,使用truncated normal distribution 来初始化,使用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中,有两种变量类型:规则变量(regulra variables)和局部(transient)变量。大部分变量都是规则变量:一旦创建,变可以使用saver保存在磁盘上,局部变量则只能在一个session期间存在,不能保存在磁盘上。

TF-Slim通过定义 model variables进一步区分变量,该变量代表模型的参数。模型变量在学习阶段被训练或微调,在评估或预测阶段可以从checkpoint中加载。例如使用slim.fully_connected或者 slim.conv2d创建变量。非模型变量都是其他变量,在学习或评估过程中使用,在预测阶段并不会起作用。例如global_step是一个在学习和预测阶段使用的变量,但是并不能真正算是模型的一部分。类似的,移动平均变量可以镜像模型变量,但是移动平均变量自身也并不能算是是模型变量。

模型变量和规则变量都可以通过TF-Slim轻松创建:

# 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()

这是如何运行的呢?当你通过TF-Slim的layer或者直接通过slim.model_variable函数创建了一个模型变量,TF-Slim将变量添加到tf.GraphKeys.MODEL_VARIABLES 这个集合中。如果你有你自己定义的网络或这变量生成程序,而你有希望TF-Slim来管理你的模型变量要怎么办呢?TF-Slim提供了一个方便的函数用来添加模型变量到该集合:

my_model_variable = CreateViaCustomCode()

# Letting TF-Slim know about the additional variable.
slim.add_model_variable(my_model_variable)

Layers

Tensorflow的操作集非常广泛,开发者通常想到使用一些更高层次的概念如“layers”,“loss”,“metrics”和“networks”。一个层,比如一个卷积层,一个全连接层或者一个BatchNorm层要比单独的一个TensorFlow操作要抽象的多,而且往往要涉及到多个操作。并且不想那些基础操作,一个网络层经常(不是总是)有很多与之相关的变量。例如,一个网络中的卷积层经常由多个低层操作组成:
1. 创建权值(weight)和偏置(bias)变量
2. 将来自上一层的数据和权值进行卷积
3. 将偏置加到卷积结果上
4. 应用激活函数

如果只用普通的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)

为了降低重复劳动以及减少重复代码,TF-Slim在更抽象的层面上提供了一系列边界操作定义神经网络。例如,对应于上述代码的TF-Slim代码如下:

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

TF-Slimg提供了标准接口用来组建神经网络,这些包含:

LayerTF-Slim
BiasAddslim.bias_add
BatchNormslim.batch_norm
Conv2dslim.conv2d
Conv2dInPlaneslim.conv2d_in_plane
Conv2dTranspose (Deconv)slim.conv2d_transpose
FullyConnectedslim.fully_connected
AvgPool2Dslim.avg_pool2d
Dropoutslim.dropout
Flattenslim.flatten
MaxPool2Dslim.max_pool2d
OneHotEncodingslim.one_hot_encoding
SeparableConv2slim.separable_conv2d
UnitNormslim.unit_norm

TF-Slim还提供了两个元操作符repeat and stack,供给用户使用重复或相同操作。如VGG 网络中的一段,网络在两个池化层只之间有多个卷积:

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='conv3_%d' % (i+1))
net = slim.max_pool2d(net, [2, 2], scope='pool2')

使用TF-Slim的repeat可以使得代码更加简洁:

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

slim.repeat不但可以在一行中使用相同的参数,而且还能智能地展开scopes,即每个后续的slim.conv2d调用所对应的scope都会追加下划线及迭代数字。更具体地讲,上面代码的scope分别为’conv3/conv3_1’, ‘conv3/conv3_2’ 和 ‘conv3/conv3_3’.

除此以外,TF-Slim的slim.stack操作允许调用者用不同的参数重复使用相同的操作符是创建一个stack或网络层塔。 slim.stack 同样也为每个操作创建了一个新的tf.variable_scope。例如,一个创建多层感知机(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:
slim.stack(x, slim.fully_connected, [32, 64, 128], scope='fc')

在这个例子中,slim.stack调用 slim.fully_connected
三次,将上一层的输出作为下一层的输入。然而,每次调用中隐含单元的数量变化从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:
slim.stack(x, slim.conv2d, [(32, [3, 3]), (32, [1, 1]), (64, [3, 3]), (64, [1, 1])], scope='core')
 类似资料: