Commit e12fdb85 authored by Amir MOHAMMADI's avatar Amir MOHAMMADI
Browse files

Merge branch 'logits' into 'master'

Updates to the logits estimator

See merge request !57
parents d9b4b431 6fee5638
Pipeline #21782 passed with stages
in 46 minutes and 25 seconds
......@@ -78,6 +78,8 @@ def append_image_augmentation(image,
# Casting to float32
image = tf.cast(image, tf.float32)
# FORCING A SEED FOR THE RANDOM OPERATIONS
tf.set_random_seed(0)
if output_shape is not None:
assert len(output_shape) == 2
......
......@@ -25,7 +25,7 @@ def shuffle_data_and_labels_image_augmentation(filenames,
extension=None):
"""
Dump random batches from a list of image paths and labels:
The list of files and labels should be in the same order e.g.
filenames = ['class_1_img1', 'class_1_img2', 'class_2_img1']
labels = [0, 0, 1]
......@@ -34,28 +34,28 @@ def shuffle_data_and_labels_image_augmentation(filenames,
filenames:
List containing the path of the images
labels:
List containing the labels (needs to be in EXACT same order as filenames)
data_shape:
Samples shape saved in the tf-record
data_type:
tf data type(https://www.tensorflow.org/versions/r0.12/resources/dims_types#data_types)
batch_size:
Size of the batch
epochs:
Number of epochs to be batched
buffer_size:
Size of the shuffle bucket
gray_scale:
Convert to gray scale?
output_shape:
If set, will randomly crop the image given the output shape
......@@ -79,7 +79,7 @@ def shuffle_data_and_labels_image_augmentation(filenames,
extension:
If None, will load files using `tf.image.decode..` if set to `hdf5`, will load with `bob.io.base.load`
"""
dataset = create_dataset_from_path_augmentation(
......@@ -118,23 +118,23 @@ def create_dataset_from_path_augmentation(filenames,
extension=None):
"""
Create dataset from a list of tf-record files
**Parameters**
filenames:
List containing the path of the images
labels:
List containing the labels (needs to be in EXACT same order as filenames)
data_shape:
Samples shape saved in the tf-record
data_type:
tf data type(https://www.tensorflow.org/versions/r0.12/resources/dims_types#data_types)
feature:
"""
parser = partial(
......@@ -151,7 +151,7 @@ def create_dataset_from_path_augmentation(filenames,
per_image_normalization=per_image_normalization,
extension=extension)
dataset = tf.contrib.data.Dataset.from_tensor_slices((filenames, labels))
dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.map(parser)
return dataset
......
......@@ -25,43 +25,43 @@ def shuffle_data_and_labels_image_augmentation(filenames,
extension=None):
"""
Dump random batches for siamese networks from a list of image paths and labels:
The list of files and labels should be in the same order e.g.
filenames = ['class_1_img1', 'class_1_img2', 'class_2_img1']
labels = [0, 0, 1]
The batches returned with tf.Session.run() with be in the following format:
**data** a dictionary containing the keys ['left', 'right'], each one representing
**data** a dictionary containing the keys ['left', 'right'], each one representing
one element of the pair and **labels** which is [0, 1] where 0 is the genuine pair
and 1 is the impostor pair.
**Parameters**
filenames:
List containing the path of the images
labels:
List containing the labels (needs to be in EXACT same order as filenames)
data_shape:
Samples shape saved in the tf-record
data_type:
tf data type(https://www.tensorflow.org/versions/r0.12/resources/dims_types#data_types)
batch_size:
Size of the batch
epochs:
Number of epochs to be batched
buffer_size:
Size of the shuffle bucket
gray_scale:
Convert to gray scale?
output_shape:
If set, will randomly crop the image given the output shape
......@@ -79,10 +79,10 @@ def shuffle_data_and_labels_image_augmentation(filenames,
random_rotate:
Randomly rotate face images between -5 and 5 degrees
per_image_normalization:
Linearly scales image to have zero mean and unit norm.
Linearly scales image to have zero mean and unit norm.
extension:
If None, will load files using `tf.image.decode..` if set to `hdf5`, will load with `bob.io.base.load`
"""
......@@ -122,33 +122,33 @@ def create_dataset_from_path_augmentation(filenames,
extension=None):
"""
Create dataset from a list of tf-record files
**Parameters**
filenames:
List containing the path of the images
labels:
List containing the labels (needs to be in EXACT same order as filenames)
data_shape:
Samples shape saved in the tf-record
data_type:
tf data type(https://www.tensorflow.org/versions/r0.12/resources/dims_types#data_types)
batch_size:
Size of the batch
epochs:
Number of epochs to be batched
buffer_size:
Size of the shuffle bucket
gray_scale:
Convert to gray scale?
output_shape:
If set, will randomly crop the image given the output shape
......@@ -168,11 +168,11 @@ def create_dataset_from_path_augmentation(filenames,
Randomly rotate face images between -10 and 10 degrees
per_image_normalization:
Linearly scales image to have zero mean and unit norm.
Linearly scales image to have zero mean and unit norm.
extension:
If None, will load files using `tf.image.decode..` if set to `hdf5`, will load with `bob.io.base.load`
"""
parser = partial(
......@@ -191,7 +191,7 @@ def create_dataset_from_path_augmentation(filenames,
left_data, right_data, siamese_labels = siamease_pairs_generator(
filenames, labels)
dataset = tf.contrib.data.Dataset.from_tensor_slices(
dataset = tf.data.Dataset.from_tensor_slices(
(left_data, right_data, siamese_labels))
dataset = dataset.map(parser)
return dataset
......
......@@ -25,41 +25,41 @@ def shuffle_data_and_labels_image_augmentation(filenames,
extension=None):
"""
Dump random batches for triplee networks from a list of image paths and labels:
The list of files and labels should be in the same order e.g.
filenames = ['class_1_img1', 'class_1_img2', 'class_2_img1']
labels = [0, 0, 1]
The batches returned with tf.Session.run() with be in the following format:
**data** a dictionary containing the keys ['anchor', 'positive', 'negative'].
**Parameters**
filenames:
List containing the path of the images
labels:
List containing the labels (needs to be in EXACT same order as filenames)
data_shape:
Samples shape saved in the tf-record
data_type:
tf data type(https://www.tensorflow.org/versions/r0.12/resources/dims_types#data_types)
batch_size:
Size of the batch
epochs:
Number of epochs to be batched
buffer_size:
Size of the shuffle bucket
gray_scale:
Convert to gray scale?
output_shape:
If set, will randomly crop the image given the output shape
......@@ -80,10 +80,10 @@ def shuffle_data_and_labels_image_augmentation(filenames,
per_image_normalization:
Linearly scales image to have zero mean and unit norm.
extension:
If None, will load files using `tf.image.decode..` if set to `hdf5`, will load with `bob.io.base.load`
"""
dataset = create_dataset_from_path_augmentation(
......@@ -123,23 +123,23 @@ def create_dataset_from_path_augmentation(filenames,
extension=None):
"""
Create dataset from a list of tf-record files
**Parameters**
filenames:
List containing the path of the images
labels:
List containing the labels (needs to be in EXACT same order as filenames)
data_shape:
Samples shape saved in the tf-record
data_type:
tf data type(https://www.tensorflow.org/versions/r0.12/resources/dims_types#data_types)
feature:
"""
parser = partial(
......@@ -159,7 +159,7 @@ def create_dataset_from_path_augmentation(filenames,
anchor_data, positive_data, negative_data = triplets_random_generator(
filenames, labels)
dataset = tf.contrib.data.Dataset.from_tensor_slices(
dataset = tf.data.Dataset.from_tensor_slices(
(anchor_data, positive_data, negative_data))
dataset = dataset.map(parser)
return dataset
......
......@@ -99,7 +99,8 @@ class Logits(estimator.Estimator):
validation_batch_size=None,
params=None,
extra_checkpoint=None,
apply_moving_averages=True):
apply_moving_averages=True,
add_histograms=None):
self.architecture = architecture
self.optimizer = optimizer
......@@ -114,64 +115,17 @@ class Logits(estimator.Estimator):
check_features(features)
data = features['data']
key = features['key']
# Configure the Training Op (for TRAIN mode)
if mode == tf.estimator.ModeKeys.TRAIN:
# Building the training graph
# Checking if we have some variables/scope that we may want to shut down
trainable_variables = get_trainable_variables(
self.extra_checkpoint)
prelogits = self.architecture(
data, mode=mode,
trainable_variables=trainable_variables)[0]
logits = append_logits(prelogits, n_classes)
if self.extra_checkpoint is not None:
tf.contrib.framework.init_from_checkpoint(
self.extra_checkpoint["checkpoint_path"],
self.extra_checkpoint["scopes"])
global_step = tf.train.get_or_create_global_step()
# Compute the moving average of all individual losses and the total loss.
if apply_moving_averages:
variable_averages = tf.train.ExponentialMovingAverage(
0.9999, global_step)
variable_averages_op = variable_averages.apply(
tf.trainable_variables())
else:
variable_averages_op = tf.no_op(name='noop')
with tf.control_dependencies([variable_averages_op]):
# Compute Loss (for both TRAIN and EVAL modes)
self.loss = self.loss_op(logits=logits, labels=labels)
# Compute the moving average of all individual losses and the total loss.
loss_averages = tf.train.ExponentialMovingAverage(
0.9, name='avg')
loss_averages_op = loss_averages.apply(
tf.get_collection(tf.GraphKeys.LOSSES))
for l in tf.get_collection(tf.GraphKeys.LOSSES):
tf.summary.scalar(l.op.name + "_averaged",
loss_averages.average(l))
global_step = tf.train.get_or_create_global_step()
train_op = tf.group(
self.optimizer.minimize(
self.loss, global_step=global_step),
variable_averages_op, loss_averages_op)
return tf.estimator.EstimatorSpec(
mode=mode, loss=self.loss, train_op=train_op)
# Building the training graph for PREDICTION OR VALIDATION
prelogits = self.architecture(data, mode=mode)[0]
# Checking if we have some variables/scope that we may want to shut
# down
trainable_variables = get_trainable_variables(
self.extra_checkpoint, mode=mode)
prelogits = self.architecture(
data, mode=mode, trainable_variables=trainable_variables)[0]
logits = append_logits(prelogits, n_classes)
if self.embedding_validation:
if self.embedding_validation and mode != tf.estimator.ModeKeys.TRAIN:
# Compute the embeddings
embeddings = tf.nn.l2_normalize(prelogits, 1)
predictions = {
......@@ -179,13 +133,13 @@ class Logits(estimator.Estimator):
"key": key,
}
else:
probabilities = tf.nn.softmax(logits, name="softmax_tensor")
predictions = {
# Generate predictions (for PREDICT and EVAL mode)
"classes": tf.argmax(input=logits, axis=1),
# Add `softmax_tensor` to the graph. It is used for PREDICT
# and by the `logging_hook`.
"probabilities": probabilities,
"probabilities": tf.nn.softmax(
logits, name="softmax_tensor"),
"key": key,
}
......@@ -193,31 +147,88 @@ class Logits(estimator.Estimator):
return tf.estimator.EstimatorSpec(
mode=mode, predictions=predictions)
# IF Validation
self.loss = self.loss_op(logits=logits, labels=labels)
if self.embedding_validation:
if self.embedding_validation and mode != tf.estimator.ModeKeys.TRAIN:
predictions_op = predict_using_tensors(
predictions["embeddings"],
labels,
num=validation_batch_size)
eval_metric_ops = {
"accuracy":
tf.metrics.accuracy(
labels=labels, predictions=predictions_op)
}
return tf.estimator.EstimatorSpec(
mode=mode, loss=self.loss, eval_metric_ops=eval_metric_ops)
else:
# Add evaluation metrics (for EVAL mode)
eval_metric_ops = {
"accuracy":
tf.metrics.accuracy(
labels=labels, predictions=predictions["classes"])
}
predictions_op = predictions["classes"]
accuracy = tf.metrics.accuracy(
labels=labels, predictions=predictions_op)
metrics = {'accuracy': accuracy}
if mode == tf.estimator.ModeKeys.EVAL:
self.loss = self.loss_op(logits=logits, labels=labels)
return tf.estimator.EstimatorSpec(
mode=mode, loss=self.loss, eval_metric_ops=eval_metric_ops)
mode=mode,
predictions=predictions,
loss=self.loss,
train_op=None,
eval_metric_ops=metrics)
# restore the model from an extra_checkpoint
if extra_checkpoint is not None:
tf.train.init_from_checkpoint(
ckpt_dir_or_file=extra_checkpoint["checkpoint_path"],
assignment_map=extra_checkpoint["scopes"],
)
global_step = tf.train.get_or_create_global_step()
# Compute the moving average of all individual losses and the
# total loss.
if apply_moving_averages:
variable_averages = tf.train.ExponentialMovingAverage(
0.9999, global_step)
variable_averages_op = variable_averages.apply(
tf.trainable_variables())
else:
variable_averages_op = tf.no_op(name='noop')
# Some layer like tf.layers.batch_norm need this:
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies([variable_averages_op] + update_ops):
# Calculate Loss
self.loss = self.loss_op(logits=logits, labels=labels)
# Compute the moving average of all individual losses
# and the total loss.
loss_averages = tf.train.ExponentialMovingAverage(
0.9, name='avg')
loss_averages_op = loss_averages.apply(
tf.get_collection(tf.GraphKeys.LOSSES))
train_op = tf.group(
self.optimizer.minimize(
self.loss, global_step=global_step),
variable_averages_op, loss_averages_op)
# Log accuracy and loss
with tf.name_scope('train_metrics'):
tf.summary.scalar('accuracy', accuracy[1])
tf.summary.scalar('loss', self.loss)
for l in tf.get_collection(tf.GraphKeys.LOSSES):
tf.summary.scalar(l.op.name + "_averaged",
loss_averages.average(l))
# add histograms summaries
if add_histograms == 'all':
for v in tf.all_variables():
tf.summary.histogram(v.name, v)
elif add_histograms == 'train':
for v in tf.trainable_variables():
tf.summary.histogram(v.name, v)
return tf.estimator.EstimatorSpec(
mode=mode,
predictions=predictions,
loss=self.loss,
train_op=train_op,
eval_metric_ops=metrics)
super(Logits, self).__init__(
model_fn=_model_fn,
......
......@@ -13,7 +13,8 @@ def check_features(features):
return True
def get_trainable_variables(extra_checkpoint):
def get_trainable_variables(extra_checkpoint,
mode=tf.estimator.ModeKeys.TRAIN):
"""
Given the extra_checkpoint dictionary provided to the estimator,
extract the content of "trainable_variables" e.
......@@ -24,15 +25,20 @@ def get_trainable_variables(extra_checkpoint):
Parameters
----------
extra_checkpoint: dict
The `extra_checkpoint dictionary provided to the estimator
extra_checkpoint : dict
The `extra_checkpoint dictionary provided to the estimator
mode
The estimator mode. TRAIN, EVAL, and PREDICT. If not TRAIN, None is
returned.
Returns
-------
Returns `None` if `trainable_variables` is not in extra_checkpoint;
otherwise returns the content of `extra_checkpoint
Returns `None` if `trainable_variables` is not in extra_checkpoint;
otherwise returns the content of `extra_checkpoint
"""
if mode != tf.estimator.ModeKeys.TRAIN:
return None
# If you don't set anything, everything is trainable
if extra_checkpoint is None or "trainable_variables" not in extra_checkpoint:
......
......@@ -75,7 +75,7 @@ def input_fn(mode, batch_size=1):
# Map example_parser over dataset, and batch results by up to batch_size
dataset = dataset.map(
example_parser, num_threads=1, output_buffer_size=batch_size)
example_parser, num_parallel_calls=1).prefetch(batch_size)
dataset = dataset.batch(batch_size)
images, labels, keys = dataset.make_one_shot_iterator().get_next()
......
......@@ -4,13 +4,7 @@ import numpy as np
import tensorflow as tf
def model_fn(features, labels, mode, params, config):
"""The model function for join face and patch PAD. The input to the model
is 160x160 faces."""
faces = features['data']
key = features['key']
def architecture(faces, mode, **kwargs):
# construct patches inside the model
ksizes = strides = [1, 28, 28, 1]
rates = [1, 1, 1, 1]
......@@ -19,18 +13,12 @@ def model_fn(features, labels, mode, params, config):
# n_blocks should be 25 for 160x160 faces
patches = tf.reshape(patches, [-1, n_blocks, 28, 28, 3])
# organize the parameters
params = params or {}
learning_rate = params.get('learning_rate', 1e-4)