From 7fabc04e762d8c8966c9257768d6f0a706c1a291 Mon Sep 17 00:00:00 2001 From: Amir MOHAMMADI <amir.mohammadi@idiap.ch> Date: Fri, 7 Feb 2020 16:11:39 +0100 Subject: [PATCH] Add more utils --- bob/learn/tensorflow/utils/__init__.py | 3 + bob/learn/tensorflow/utils/math.py | 77 ++++++++++++++++++++++ bob/learn/tensorflow/utils/network.py | 19 +++--- bob/learn/tensorflow/utils/reproducible.py | 6 +- bob/learn/tensorflow/utils/util.py | 35 ++++++++-- 5 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 bob/learn/tensorflow/utils/math.py diff --git a/bob/learn/tensorflow/utils/__init__.py b/bob/learn/tensorflow/utils/__init__.py index c7b56d5d..aab52096 100644 --- a/bob/learn/tensorflow/utils/__init__.py +++ b/bob/learn/tensorflow/utils/__init__.py @@ -6,3 +6,6 @@ from .eval import * from .keras import * from .train import * from .graph import * +from .network import * +from .math import * +from .reproducible import * diff --git a/bob/learn/tensorflow/utils/math.py b/bob/learn/tensorflow/utils/math.py new file mode 100644 index 00000000..64ed7349 --- /dev/null +++ b/bob/learn/tensorflow/utils/math.py @@ -0,0 +1,77 @@ +import tensorflow as tf + + +def gram_matrix(input_tensor): + """Computes the gram matrix + + Parameters + ---------- + input_tensor : object + The input tensor. Usually it's the activation of a conv layer. The input shape + must be ``BHWC``. + + Returns + ------- + object + The computed gram matrix as a tensor. + + Example + ------- + >>>> gram_matrix(tf.zeros((32, 4, 6, 12))) + <tf.Tensor: id=53, shape=(32, 12, 12), dtype=float32, numpy= + array([[[0., 0., 0., ..., 0., 0., 0.], + [0., 0., 0., ..., 0., 0., 0.], + [0., 0., 0., ..., 0., 0., 0.], + ..., + [0., 0., 0., ..., 0., 0., 0.], + [0., 0., 0., ..., 0., 0., 0.], + [0., 0., 0., ..., 0., 0., 0.]], + """ + result = tf.linalg.einsum("bijc,bijd->bcd", input_tensor, input_tensor) + input_shape = tf.shape(input_tensor) + num_locations = tf.cast(input_shape[1] * input_shape[2], tf.float32) + return result / (num_locations) + + +def upper_triangle_and_diagonal(A): + """Returns a flat version of upper triangle of a 2D array (including diagonal). + + This function is useful to be applied on gram matrices since they contain duplicate + information. + + Parameters + ---------- + A + A two dimensional array. + + Returns + ------- + object + The flattened upper triangle of array + + Example + ------- + >>> A = [ + ... [1, 2, 3], + ... [4, 5, 6], + ... [7, 8, 9], + ... ] + >>> upper_triangle_and_diagonal(A) + [1,2,3,5,6,9] + """ + ones = tf.ones_like(A) + # Upper triangular matrix of 0s and 1s (including diagonal) + mask = tf.matrix_band_part(ones, 0, -1) + upper_triangular_flat = tf.boolean_mask(A, mask) + return upper_triangular_flat + + +def upper_triangle(A): + ones = tf.ones_like(A) + # Upper triangular matrix of 0s and 1s (including diagonal) + mask_a = tf.matrix_band_part(ones, 0, -1) + # Diagonal + mask_b = tf.matrix_band_part(ones, 0, 0) + mask = tf.cast(mask_a - mask_b, dtype=tf.bool) + upper_triangular_flat = tf.boolean_mask(A, mask) + return upper_triangular_flat diff --git a/bob/learn/tensorflow/utils/network.py b/bob/learn/tensorflow/utils/network.py index 2a4f1ff9..fa56276e 100644 --- a/bob/learn/tensorflow/utils/network.py +++ b/bob/learn/tensorflow/utils/network.py @@ -2,19 +2,21 @@ import tensorflow as tf import tensorflow.contrib.slim as slim -def append_logits(graph, - n_classes, - reuse=False, - l2_regularizer=5e-05, - weights_std=0.1, trainable_variables=None, - name='Logits'): +def append_logits( + graph, + n_classes, + reuse=False, + l2_regularizer=5e-05, + weights_std=0.1, + trainable_variables=None, + name="Logits", +): trainable = is_trainable(name, trainable_variables) return slim.fully_connected( graph, n_classes, activation_fn=None, - weights_initializer=tf.truncated_normal_initializer( - stddev=weights_std), + weights_initializer=tf.truncated_normal_initializer(stddev=weights_std), weights_regularizer=slim.l2_regularizer(l2_regularizer), scope=name, reuse=reuse, @@ -47,4 +49,3 @@ def is_trainable(name, trainable_variables, mode=tf.estimator.ModeKeys.TRAIN): # Here is my choice to shutdown the whole scope return name in trainable_variables - diff --git a/bob/learn/tensorflow/utils/reproducible.py b/bob/learn/tensorflow/utils/reproducible.py index b620cf93..9331704c 100644 --- a/bob/learn/tensorflow/utils/reproducible.py +++ b/bob/learn/tensorflow/utils/reproducible.py @@ -9,7 +9,7 @@ from tensorflow.core.protobuf import rewriter_config_pb2 def set_seed( seed=0, python_hash_seed=0, log_device_placement=False, allow_soft_placement=False, - arithmetic_optimization=None, + arithmetic_optimization=None, allow_growth=None, ): """Sets the seeds in python, numpy, and tensorflow in order to help training reproducible networks. @@ -68,6 +68,10 @@ def set_seed( off = rewriter_config_pb2.RewriterConfig.OFF session_config.graph_options.rewrite_options.arithmetic_optimization = off + if allow_growth is not None: + session_config.gpu_options.allow_growth = allow_growth + session_config.gpu_options.per_process_gpu_memory_fraction = 0.8 + # The below tf.set_random_seed() will make random number generation # in the TensorFlow backend have a well-defined initial state. # For further details, see: diff --git a/bob/learn/tensorflow/utils/util.py b/bob/learn/tensorflow/utils/util.py index 1c81613b..263a9ec0 100644 --- a/bob/learn/tensorflow/utils/util.py +++ b/bob/learn/tensorflow/utils/util.py @@ -35,6 +35,35 @@ def compute_euclidean_distance(x, y): return d +def pdist_safe(A, metric="sqeuclidean"): + if metric != "sqeuclidean": + raise NotImplementedError() + r = tf.reduce_sum(A * A, 1) + r = tf.reshape(r, [-1, 1]) + D = r - 2 * tf.matmul(A, A, transpose_b=True) + tf.transpose(r) + return D + + +def cdist(A, B, metric="sqeuclidean"): + if metric != "sqeuclidean": + raise NotImplementedError() + M1, M2 = tf.shape(A)[0], tf.shape(B)[0] + # code from https://stackoverflow.com/a/43839605/1286165 + p1 = tf.matmul( + tf.expand_dims(tf.reduce_sum(tf.square(A), 1), 1), tf.ones(shape=(1, M2)) + ) + p2 = tf.transpose( + tf.matmul( + tf.reshape(tf.reduce_sum(tf.square(B), 1), shape=[-1, 1]), + tf.ones(shape=(M1, 1)), + transpose_b=True, + ) + ) + + D = tf.add(p1, p2) - 2 * tf.matmul(A, B, transpose_b=True) + return D + + def load_mnist(perc_train=0.9): numpy.random.seed(0) import bob.db.mnist @@ -184,7 +213,7 @@ def pdist(A): Compute a pairwise euclidean distance in the same fashion as in scipy.spation.distance.pdist """ - with tf.variable_scope("Pairwisedistance"): + with tf.name_scope("Pairwisedistance"): ones_1 = tf.reshape(tf.cast(tf.ones_like(A), tf.float32)[:, 0], [1, -1]) p1 = tf.matmul(tf.expand_dims(tf.reduce_sum(tf.square(A), 1), 1), ones_1) @@ -406,9 +435,7 @@ def bytes2human(n, format="%(value).1f %(symbol)s", symbols="customary"): return format % dict(symbol=symbols[0], value=n) -def random_choice_no_replacement( - one_dim_input, num_indices_to_drop=3, sort=False -): +def random_choice_no_replacement(one_dim_input, num_indices_to_drop=3, sort=False): """Similar to np.random.choice with no replacement. Code from https://stackoverflow.com/a/54755281/1286165 """ -- GitLab