转自Tensorflow slim库使用小记
看fensorflow的书发现使用的是slim库,那就要研究slim的常用函数,这个文章写的很好,转一下哈。
slim库的导入:
import tensorflow as tf
import tensorflow.contrib.slim as slim
常用函数:
与tensorflow自带的函数相比,slim能够让我们不用重复写函数的参数。那么函数的参数写在哪里呢?核心方法就是slim.arg_scope。
slim.arg_scope
def arg_scope(list_ops_or_scope, **kwargs)
list_ops_or_scope:要用的函数的作用域,可以在需要使用的地方用@add_arg_scope 声明
**kwargs: keyword=value 定义了list_ops中要使用的变量
也就是说可以通过这个函数将不想重复写的参数通过这个函数自动赋值。
示例:
import tensorflow.contrib.slim as slim
@slim.add_arg_scope
def hh(name, add_arg):
print("name:", name)
print("add_arg:", add_arg)
with slim.arg_scope([hh], add_arg='this is add'):
hh('test')
#结果:
#name: test
#add_arg: this is add
进入add_arg_scope函数查看代码可知:
def add_arg_scope(func):
"""Decorates a function with args so it can be used within an arg_scope.
Args:
func: function to decorate.
Returns:
A tuple with the decorated function func_with_args().
"""
def func_with_args(*args, **kwargs):
current_scope = _current_arg_scope()
current_args = kwargs
key_func = _key_op(func)
if key_func in current_scope:
current_args = current_scope[key_func].copy()
current_args.update(kwargs)
return func(*args, **current_args)
_add_op(func)
setattr(func_with_args, '_key_op', _key_op(func))
return tf_decorator.make_decorator(func, func_with_args)
其实就是看看你调用的是那个函数,给参数中添加你之前赋值的参数。
之后是使用slim构建神经网络常用的函数。
slim.conv2d
slim.conv2d是对tf.conv2d的进一步封装。常见调用方式:
net = slim.conv2d(inputs, 256, [3, 3], stride=1, scope='conv1_1')
源代码:
@add_arg_scope
def convolution(inputs,num_outputs,
kernel_size,
stride=1,
padding='SAME',
data_format=None,
rate=1,
activation_fn=nn.relu,
normalizer_fn=None,
normalizer_params=None,
weights_initializer=initializers.xavier_initializer(),
weights_regularizer=None,
biases_initializer=init_ops.zeros_initializer(),
biases_regularizer=None,
reuse=None,
variables_collections=None,
outputs_collections=None,
trainable=True,
scope=None)
常用的有:
padding : 补零的方式,例如'SAME'
activation_fn : 激活函数,默认是nn.relu
normalizer_fn : 正则化函数,默认为None,这里可以设置为batch normalization,函数用slim.batch_norm
normalizer_params : slim.batch_norm中的参数,以字典形式表示
weights_initializer : 权重的初始化器,initializers.xavier_initializer()
weights_regularizer : 权重的正则化器,一般不怎么用到
biases_initializer : 如果之前有batch norm,那么这个及下面一个就不用管了
biases_regularizer :
trainable : 参数是否可训练,默认为True
scope:你绘制的网络结构图中它属于那个范围内
slim.max_pool2d
net = slim.max_pool2d(net, [2, 2], scope='pool1')
前两个参数分别为网络输入、输出的神经元数量,第三个同上。
slim.batch_norm
def batch_norm(inputs,
decay=0.999,
center=True,
scale=False,
epsilon=0.001,
activation_fn=None,
param_initializers=None,
param_regularizers=None,
updates_collections=ops.GraphKeys.UPDATE_OPS,
is_training=True,
reuse=None,
variables_collections=None,
outputs_collections=None,
trainable=True,
batch_weights=None,
fused=False,
data_format=DATA_FORMAT_NHWC,
zero_debias_moving_mean=False,
scope=None,
renorm=False,
renorm_clipping=None,
renorm_decay=0.99):
这个我没有理解。以下是原博客说的。
接下来说我在用slim.batch_norm时踩到的坑。slim.batch_norm里有moving_mean和moving_variance两个量,分别表示每个批次的均值和方差。在训练时还好理解,但在测试时,moving_mean和moving_variance的含义变了。在训练时,有一些语句是必不可少的:
# 定义占位符,X表示网络的输入,Y表示真实值label
X = tf.placeholder("float", [None, 224, 224, 3])
Y = tf.placeholder("float", [None, 100])
#调用含batch_norm的resnet网络,其中记得is_training=True
logits = model.resnet(X, 100, is_training=True)
cross_entropy = -tf.reduce_sum(Y*tf.log(logits))
#训练的op一定要用slim的slim.learning.create_train_op,只用tf.train.MomentumOptimizer.minimize()是不行的
opt = tf.train.MomentumOptimizer(lr_rate, 0.9)
train_op = slim.learning.create_train_op(cross_entropy, opt, global_step=global_step)
#更新操作,具体含义不是很明白,直接套用即可
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
if update_ops:
updates = tf.group(*update_ops)
cross_entropy = control_flow_ops.with_dependencies([updates], cross_entropy)
之后的训练都和往常一样了,导出模型后,在测试阶段调用相同的网络,参数is_training一定要设置成False。```
logits = model.resnet(X, 100, is_training=False)
否则,可能会出现这种情况:所有的单个图像分类,最后几乎全被归为同一类。这可能就是训练模式设置反了的问题。