【Tensorflow】辅助工具篇——tensorflow slim(TF-Slim)介绍

一.简介

slim被放在tensorflow.contrib这个库下面,导入的方法如下:

[python]  view plain  copy
  1. import tensorflow.contrib.slim as slim  

这样我们就可以使用slim了,既然说到了,先来扒一扒tensorflow.contrib这个库,tensorflow官方对它的描述是:此目录中的任何代码未经官方支持,可能会随时更改或删除。每个目录下都有指定的所有者。它旨在包含额外功能和贡献,最终会合并到核心TensorFlow中,但其接口可能仍然会发生变化,或者需要进行一些测试,看是否可以获得更广泛的接受。所以slim依然不属于原生tensorflow。

那么什么是slim?slim到底有什么用?

slim是一个使构建,训练,评估神经网络变得简单的库。它可以消除原生tensorflow里面很多重复的模板性的代码,让代码更紧凑,更具备可读性。另外slim提供了很多计算机视觉方面的著名模型(VGG, AlexNet等),我们不仅可以直接使用,甚至能以各种方式进行扩展。


slim的子模块及功能介绍:

arg_scope: provides a new scope named arg_scope that allows a user to define default arguments for specific operations within that scope.

除了基本的namescope,variabelscope外,又加了argscope,它是用来控制每一层的默认超参数的。(后面会详细说)

data: contains TF-slim's dataset definition, data providers, parallel_reader, and decoding utilities.

貌似slim里面还有一套自己的数据定义,这个跳过,我们用的不多。

evaluation: contains routines for evaluating models.

评估模型的一些方法,用的也不多

layers: contains high level layers for building models using tensorflow.

这个比较重要,slim的核心和精髓,一些复杂层的定义

learning: contains routines for training models.

一些训练规则

losses: contains commonly used loss functions.

一些loss

metrics: contains popular evaluation metrics.

评估模型的度量标准

nets: contains popular network definitions such as VGG and AlexNet models.

包含一些经典网络,VGG等,用的也比较多

queues: provides a context manager for easily and safely starting and closing QueueRunners.

文本队列管理,比较有用。

regularizers: contains weight regularizers.

包含一些正则规则

variables: provides convenience wrappers for variable creation and manipulation.

这个比较有用,我很喜欢slim管理变量的机制

具体子库就这么多拉,接下来干货时间!


二.slim定义模型

slim中定义一个变量的示例:

[python]  view plain  copy
  1. # Model Variables  
  2. weights = slim.model_variable('weights',  
  3.                               shape=[10103 , 3],  
  4.                               initializer=tf.truncated_normal_initializer(stddev=0.1),  
  5.                               regularizer=slim.l2_regularizer(0.05),  
  6.                               device='/CPU:0')  
  7. model_variables = slim.get_model_variables()  
  8.   
  9. # Regular variables  
  10. my_var = slim.variable('my_var',  
  11.                        shape=[201],  
  12.                        initializer=tf.zeros_initializer())  
  13. regular_variables_and_model_variables = slim.get_variables()  

如上,变量分为两类:模型变量和局部变量。局部变量是不作为模型参数保存的,而模型变量会再save的时候保存下来。这个玩过tensorflow的人都会明白,诸如global_step之类的就是局部变量。slim中可以写明变量存放的设备,正则和初始化规则。还有获取变量的函数也需要注意一下,get_variables是返回所有的变量。


slim中实现一个层:

首先让我们看看tensorflow怎么实现一个层,例如卷积层:

[python]  view plain  copy
  1. input = ...  
  2. with tf.name_scope('conv1_1') as scope:  
  3.   kernel = tf.Variable(tf.truncated_normal([3364128], dtype=tf.float32,  
  4.                                            stddev=1e-1), name='weights')  
  5.   conv = tf.nn.conv2d(input, kernel, [1111], padding='SAME')  
  6.   biases = tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32),  
  7.                        trainable=True, name='biases')  
  8.   bias = tf.nn.bias_add(conv, biases)  
  9.   conv1 = tf.nn.relu(bias, name=scope)  

然后slim的实现:

[python]  view plain  copy
  1. input = ...  
  2. net = slim.conv2d(input, 128, [33], scope='conv1_1')  

但这个不是重要的,因为tenorflow目前也有大部分层的简单实现,这里比较吸引人的是slim中的repeat和stack操作:

假设定义三个相同的卷积层:

[python]  view plain  copy
  1. net = ...  
  2. net = slim.conv2d(net, 256, [33], scope='conv3_1')  
  3. net = slim.conv2d(net, 256, [33], scope='conv3_2')  
  4. net = slim.conv2d(net, 256, [33], scope='conv3_3')  
  5. net = slim.max_pool2d(net, [22], scope='pool2')  

在slim中的repeat操作可以减少代码量:

[python]  view plain  copy
  1. net = slim.repeat(net, 3, slim.conv2d, 256, [33], scope='conv3')  
  2. net = slim.max_pool2d(net, [22], scope='pool2')  

而stack是处理卷积核或者输出不一样的情况:

假设定义三层FC:

[python]  view plain  copy
  1. # Verbose way:  
  2. x = slim.fully_connected(x, 32, scope='fc/fc_1')  
  3. x = slim.fully_connected(x, 64, scope='fc/fc_2')  
  4. x = slim.fully_connected(x, 128, scope='fc/fc_3')  

使用stack操作:

[python]  view plain  copy
  1. slim.stack(x, slim.fully_connected, [3264128], scope='fc')  

同理卷积层也一样:

[python]  view plain  copy
  1. # 普通方法:  
  2. x = slim.conv2d(x, 32, [33], scope='core/core_1')  
  3. x = slim.conv2d(x, 32, [11], scope='core/core_2')  
  4. x = slim.conv2d(x, 64, [33], scope='core/core_3')  
  5. x = slim.conv2d(x, 64, [11], scope='core/core_4')  
  6.   
  7. # 简便方法:  
  8. slim.stack(x, slim.conv2d, [(32, [33]), (32, [11]), (64, [33]), (64, [11])], scope='core')  

slim中的argscope:

如果你的网络有大量相同的参数,如下:

[python]  view plain  copy
  1. net = slim.conv2d(inputs, 64, [1111], 4, padding='SAME',  
  2.                   weights_initializer=tf.truncated_normal_initializer(stddev=0.01),  
  3.                   weights_regularizer=slim.l2_regularizer(0.0005), scope='conv1')  
  4. net = slim.conv2d(net, 128, [1111], padding='VALID',  
  5.                   weights_initializer=tf.truncated_normal_initializer(stddev=0.01),  
  6.                   weights_regularizer=slim.l2_regularizer(0.0005), scope='conv2')  
  7. net = slim.conv2d(net, 256, [1111], padding='SAME',  
  8.                   weights_initializer=tf.truncated_normal_initializer(stddev=0.01),  
  9.                   weights_regularizer=slim.l2_regularizer(0.0005), scope='conv3')  


然后我们用arg_scope处理一下:

[python]  view plain  copy
  1. with slim.arg_scope([slim.conv2d], padding='SAME',  
  2.                       weights_initializer=tf.truncated_normal_initializer(stddev=0.01)  
  3.                       weights_regularizer=slim.l2_regularizer(0.0005)):  
  4.     net = slim.conv2d(inputs, 64, [1111], scope='conv1')  
  5.     net = slim.conv2d(net, 128, [1111], padding='VALID', scope='conv2')  
  6.     net = slim.conv2d(net, 256, [1111], scope='conv3')  

是不是一下子就变简洁了?这里额外说明一点,arg_scope的作用范围内,是定义了指定层的默认参数,若想特别指定某些层的参数,可以重新赋值(相当于重写),如上倒数第二行代码。那如果除了卷积层还有其他层呢?那就要如下定义:

[python]  view plain  copy
  1. with slim.arg_scope([slim.conv2d, slim.fully_connected],  
  2.                       activation_fn=tf.nn.relu,  
  3.                       weights_initializer=tf.truncated_normal_initializer(stddev=0.01),  
  4.                       weights_regularizer=slim.l2_regularizer(0.0005)):  
  5.   with slim.arg_scope([slim.conv2d], stride=1, padding='SAME'):  
  6.     net = slim.conv2d(inputs, 64, [1111], 4, padding='VALID', scope='conv1')  
  7.     net = slim.conv2d(net, 256, [55],  
  8.                       weights_initializer=tf.truncated_normal_initializer(stddev=0.03),  
  9.                       scope='conv2')  
  10.     net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc')  

写两个arg_scope就行了。

采用如上方法,定义一个VGG也就十几行代码的事了。

[python]  view plain  copy
  1. def vgg16(inputs):  
  2.   with slim.arg_scope([slim.conv2d, slim.fully_connected],  
  3.                       activation_fn=tf.nn.relu,  
  4.                       weights_initializer=tf.truncated_normal_initializer(0.00.01),  
  5.                       weights_regularizer=slim.l2_regularizer(0.0005)):  
  6.     net = slim.repeat(inputs, 2, slim.conv2d, 64, [33], scope='conv1')  
  7.     net = slim.max_pool2d(net, [22], scope='pool1')  
  8.     net = slim.repeat(net, 2, slim.conv2d, 128, [33], scope='conv2')  
  9.     net = slim.max_pool2d(net, [22], scope='pool2')  
  10.     net = slim.repeat(net, 3, slim.conv2d, 256, [33], scope='conv3')  
  11.     net = slim.max_pool2d(net, [22], scope='pool3')  
  12.     net = slim.repeat(net, 3, slim.conv2d, 512, [33], scope='conv4')  
  13.     net = slim.max_pool2d(net, [22], scope='pool4')  
  14.     net = slim.repeat(net, 3, slim.conv2d, 512, [33], scope='conv5')  
  15.     net = slim.max_pool2d(net, [22], scope='pool5')  
  16.     net = slim.fully_connected(net, 4096, scope='fc6')  
  17.     net = slim.dropout(net, 0.5, scope='dropout6')  
  18.     net = slim.fully_connected(net, 4096, scope='fc7')  
  19.     net = slim.dropout(net, 0.5, scope='dropout7')  
  20.     net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc8')  
  21.   return net  

三.训练模型

这个没什么好说的,说一下直接拿经典网络来训练吧。

[python]  view plain  copy
  1. import tensorflow as tf  
  2. vgg = tf.contrib.slim.nets.vgg  
  3.   
  4. # Load the images and labels.  
  5. images, labels = ...  
  6.   
  7. # Create the model.  
  8. predictions, _ = vgg.vgg_16(images)  
  9.   
  10. # Define the loss functions and get the total loss.  
  11. loss = slim.losses.softmax_cross_entropy(predictions, labels)  

是不是超级简单?

关于loss,要说一下定义自己的loss的方法,以及注意不要忘记加入到slim中让slim看到你的loss。

还有正则项也是需要手动添加进loss当中的,不然最后计算的时候就不优化正则目标了。

[python]  view plain  copy
  1. # Load the images and labels.  
  2. images, scene_labels, depth_labels, pose_labels = ...  
  3.   
  4. # Create the model.  
  5. scene_predictions, depth_predictions, pose_predictions = CreateMultiTaskModel(images)  
  6.   
  7. # Define the loss functions and get the total loss.  
  8. classification_loss = slim.losses.softmax_cross_entropy(scene_predictions, scene_labels)  
  9. sum_of_squares_loss = slim.losses.sum_of_squares(depth_predictions, depth_labels)  
  10. pose_loss = MyCustomLossFunction(pose_predictions, pose_labels)  
  11. slim.losses.add_loss(pose_loss) # Letting TF-Slim know about the additional loss.  
  12.   
  13. # The following two ways to compute the total loss are equivalent:  
  14. regularization_loss = tf.add_n(slim.losses.get_regularization_losses())  
  15. total_loss1 = classification_loss + sum_of_squares_loss + pose_loss + regularization_loss  
  16.   
  17. # (Regularization Loss is included in the total loss by default).  
  18. total_loss2 = slim.losses.get_total_loss()  

四.读取保存模型变量

通过以下功能我们可以载入模型的部分变量:

[python]  view plain  copy
  1. # Create some variables.  
  2. v1 = slim.variable(name="v1", ...)  
  3. v2 = slim.variable(name="nested/v2", ...)  
  4. ...  
  5.   
  6. # Get list of variables to restore (which contains only 'v2').  
  7. variables_to_restore = slim.get_variables_by_name("v2")  
  8.   
  9. # Create the saver which will be used to restore the variables.  
  10. restorer = tf.train.Saver(variables_to_restore)  
  11.   
  12. with tf.Session() as sess:  
  13.   # Restore variables from disk.  
  14.   restorer.restore(sess, "/tmp/model.ckpt")  
  15.   print("Model restored.")  

除了这种部分变量加载的方法外,我们甚至还能加载到不同名字的变量中。

假设我们定义的网络变量是conv1/weights,而从VGG加载的变量名为vgg16/conv1/weights,正常load肯定会报错(找不到变量名),但是可以这样:

[python]  view plain  copy
  1. def name_in_checkpoint(var):  
  2.   return 'vgg16/' + var.op.name  
  3.   
  4. variables_to_restore = slim.get_model_variables()  
  5. variables_to_restore = {name_in_checkpoint(var):var for var in variables_to_restore}  
  6. restorer = tf.train.Saver(variables_to_restore)  
  7.   
  8. with tf.Session() as sess:  
  9.   # Restore variables from disk.  
  10.   restorer.restore(sess, "/tmp/model.ckpt")  

通过这种方式我们可以加载不同变量名的变量!!



转载自https://blog.csdn.net/mao_xiao_feng/article/details/73409975


猜你喜欢

转载自blog.csdn.net/alawaka2018/article/details/80339461