|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| """Contains convenience wrappers for typical Neural Network TensorFlow layers.
|
|
|
| Ops that have different behavior during training or eval have an is_training
|
| parameter.
|
| """
|
| from __future__ import absolute_import
|
| from __future__ import division
|
| from __future__ import print_function
|
|
|
|
|
| import numpy as np
|
| import tensorflow as tf
|
|
|
|
|
| arg_scope = tf.contrib.framework.arg_scope
|
|
|
|
|
| def variable(name, shape, dtype, initializer, trainable):
|
| """Returns a TF variable with the passed in specifications."""
|
| var = tf.get_variable(
|
| name,
|
| shape=shape,
|
| dtype=dtype,
|
| initializer=initializer,
|
| trainable=trainable)
|
| return var
|
|
|
|
|
| def global_avg_pool(x, scope=None):
|
| """Average pools away spatial height and width dimension of 4D tensor."""
|
| assert x.get_shape().ndims == 4
|
| with tf.name_scope(scope, 'global_avg_pool', [x]):
|
| kernel_size = (1, int(x.shape[1]), int(x.shape[2]), 1)
|
| squeeze_dims = (1, 2)
|
| result = tf.nn.avg_pool(
|
| x,
|
| ksize=kernel_size,
|
| strides=(1, 1, 1, 1),
|
| padding='VALID',
|
| data_format='NHWC')
|
| return tf.squeeze(result, squeeze_dims)
|
|
|
|
|
| def zero_pad(inputs, in_filter, out_filter):
|
| """Zero pads `input` tensor to have `out_filter` number of filters."""
|
| outputs = tf.pad(inputs, [[0, 0], [0, 0], [0, 0],
|
| [(out_filter - in_filter) // 2,
|
| (out_filter - in_filter) // 2]])
|
| return outputs
|
|
|
|
|
| @tf.contrib.framework.add_arg_scope
|
| def batch_norm(inputs,
|
| decay=0.999,
|
| center=True,
|
| scale=False,
|
| epsilon=0.001,
|
| is_training=True,
|
| reuse=None,
|
| scope=None):
|
| """Small wrapper around tf.contrib.layers.batch_norm."""
|
| return tf.contrib.layers.batch_norm(
|
| inputs,
|
| decay=decay,
|
| center=center,
|
| scale=scale,
|
| epsilon=epsilon,
|
| activation_fn=None,
|
| param_initializers=None,
|
| updates_collections=tf.GraphKeys.UPDATE_OPS,
|
| is_training=is_training,
|
| reuse=reuse,
|
| trainable=True,
|
| fused=True,
|
| data_format='NHWC',
|
| zero_debias_moving_mean=False,
|
| scope=scope)
|
|
|
|
|
| def stride_arr(stride_h, stride_w):
|
| return [1, stride_h, stride_w, 1]
|
|
|
|
|
| @tf.contrib.framework.add_arg_scope
|
| def conv2d(inputs,
|
| num_filters_out,
|
| kernel_size,
|
| stride=1,
|
| scope=None,
|
| reuse=None):
|
| """Adds a 2D convolution.
|
|
|
| conv2d creates a variable called 'weights', representing the convolutional
|
| kernel, that is convolved with the input.
|
|
|
| Args:
|
| inputs: a 4D tensor in NHWC format.
|
| num_filters_out: the number of output filters.
|
| kernel_size: an int specifying the kernel height and width size.
|
| stride: an int specifying the height and width stride.
|
| scope: Optional scope for variable_scope.
|
| reuse: whether or not the layer and its variables should be reused.
|
| Returns:
|
| a tensor that is the result of a convolution being applied to `inputs`.
|
| """
|
| with tf.variable_scope(scope, 'Conv', [inputs], reuse=reuse):
|
| num_filters_in = int(inputs.shape[3])
|
| weights_shape = [kernel_size, kernel_size, num_filters_in, num_filters_out]
|
|
|
|
|
| n = int(weights_shape[0] * weights_shape[1] * weights_shape[3])
|
| weights_initializer = tf.random_normal_initializer(
|
| stddev=np.sqrt(2.0 / n))
|
|
|
| weights = variable(
|
| name='weights',
|
| shape=weights_shape,
|
| dtype=tf.float32,
|
| initializer=weights_initializer,
|
| trainable=True)
|
| strides = stride_arr(stride, stride)
|
| outputs = tf.nn.conv2d(
|
| inputs, weights, strides, padding='SAME', data_format='NHWC')
|
| return outputs
|
|
|
|
|
| @tf.contrib.framework.add_arg_scope
|
| def fc(inputs,
|
| num_units_out,
|
| scope=None,
|
| reuse=None):
|
| """Creates a fully connected layer applied to `inputs`.
|
|
|
| Args:
|
| inputs: a tensor that the fully connected layer will be applied to. It
|
| will be reshaped if it is not 2D.
|
| num_units_out: the number of output units in the layer.
|
| scope: Optional scope for variable_scope.
|
| reuse: whether or not the layer and its variables should be reused.
|
|
|
| Returns:
|
| a tensor that is the result of applying a linear matrix to `inputs`.
|
| """
|
| if len(inputs.shape) > 2:
|
| inputs = tf.reshape(inputs, [int(inputs.shape[0]), -1])
|
|
|
| with tf.variable_scope(scope, 'FC', [inputs], reuse=reuse):
|
| num_units_in = inputs.shape[1]
|
| weights_shape = [num_units_in, num_units_out]
|
| unif_init_range = 1.0 / (num_units_out)**(0.5)
|
| weights_initializer = tf.random_uniform_initializer(
|
| -unif_init_range, unif_init_range)
|
| weights = variable(
|
| name='weights',
|
| shape=weights_shape,
|
| dtype=tf.float32,
|
| initializer=weights_initializer,
|
| trainable=True)
|
| bias_initializer = tf.constant_initializer(0.0)
|
| biases = variable(
|
| name='biases',
|
| shape=[num_units_out,],
|
| dtype=tf.float32,
|
| initializer=bias_initializer,
|
| trainable=True)
|
| outputs = tf.nn.xw_plus_b(inputs, weights, biases)
|
| return outputs
|
|
|
|
|
| @tf.contrib.framework.add_arg_scope
|
| def avg_pool(inputs, kernel_size, stride=2, padding='VALID', scope=None):
|
| """Wrapper around tf.nn.avg_pool."""
|
| with tf.name_scope(scope, 'AvgPool', [inputs]):
|
| kernel = stride_arr(kernel_size, kernel_size)
|
| strides = stride_arr(stride, stride)
|
| return tf.nn.avg_pool(
|
| inputs,
|
| ksize=kernel,
|
| strides=strides,
|
| padding=padding,
|
| data_format='NHWC')
|
|
|
|
|