Commit b8b71630 authored by Tiago Pereira's avatar Tiago Pereira
Browse files

Migrated Siamease net

parent f11d453f
Pipeline #8651 failed with stages
in 20 minutes and 50 seconds
......@@ -20,42 +20,22 @@ class Siamese(Base):
def __init__(self, **kwargs):
super(Siamese, self).__init__(**kwargs)
self.data2_placeholder = None
def set_placeholders(self, data, data2, label):
self.data_placeholder = data
self.data2_placeholder = data2
self.label_placeholder = label
def get_placeholders(self, name=""):
"""
Returns a place holder with the size of your batch
def create_placeholders(self):
"""
if self.data_placeholder is None:
self.data_placeholder = tf.placeholder(tf.float32, shape=self.shape, name=name+"_right")
if self.data2_placeholder is None:
self.data2_placeholder = tf.placeholder(tf.float32, shape=self.shape, name=name+"_left")
if self.label_placeholder is None:
self.label_placeholder = tf.placeholder(tf.int64, shape=self.shape[0], name=name+"_label")
return [self.data_placeholder, self.data2_placeholder, self.label_placeholder]
Create place holder instances
def get_placeholders_forprefetch(self, name=""):
:return:
"""
Returns a place holder with the size of your batch
"""
if self.data_placeholder is None:
self.data_placeholder = tf.placeholder(tf.float32, shape=tuple([None] + list(self.shape[1:])), name=name)
if self.data2_placeholder is None:
self.data2_placeholder = tf.placeholder(tf.float32, shape=tuple([None] + list(self.shape[1:])), name=name)
if self.label_placeholder is None:
self.label_placeholder = tf.placeholder(tf.int64, shape=[None, ])
return [self.data_placeholder, self.data2_placeholder, self.label_placeholder]
with tf.name_scope("Input"):
self.data_ph = {}
self.data_ph['left'] = tf.placeholder(tf.float32, shape=self.input_shape, name="left")
self.data_ph['right'] = tf.placeholder(tf.float32, shape=self.input_shape, name="right")
self.label_ph = tf.placeholder(tf.int64, shape=[None], name="label")
# If prefetch, setup the queue to feed data
if self.prefetch:
raise ValueError("There is no prefetch for siamease networks")
def get_genuine_or_not(self, input_data, input_labels, genuine=True):
......
......@@ -45,12 +45,13 @@ class SiameseMemory(Siamese, Memory):
"""
def __init__(self, data, labels,
input_shape,
input_dtype="float",
batch_size=1,
input_shape=[None, 28, 28, 1],
input_dtype="float32",
batch_size=32,
seed=10,
data_augmentation=None,
normalizer=Linear()):
normalizer=Linear()
):
super(SiameseMemory, self).__init__(
data=data,
......@@ -64,7 +65,6 @@ class SiameseMemory(Siamese, Memory):
)
# Seting the seed
numpy.random.seed(seed)
self.data = self.data.astype(input_dtype)
def get_batch(self, zero_one_labels=True):
......@@ -76,12 +76,15 @@ class SiameseMemory(Siamese, Memory):
**Return**
"""
sample_l = numpy.zeros(shape=self.shape, dtype='float')
sample_r = numpy.zeros(shape=self.shape, dtype='float')
labels_siamese = numpy.zeros(shape=self.shape[0], dtype='float')
shape = [self.batch_size] + list(self.input_shape[1:])
sample_l = numpy.zeros(shape=shape, dtype='float32')
sample_r = numpy.zeros(shape=shape, dtype='float32')
labels_siamese = numpy.zeros(shape=shape[0], dtype='float32')
genuine = True
for i in range(self.shape[0]):
for i in range(shape[0]):
sample_l[i, ...], sample_r[i, ...] = self.get_genuine_or_not(self.data, self.labels, genuine=genuine)
if zero_one_labels:
labels_siamese[i] = not genuine
......
......@@ -23,27 +23,26 @@ Some unit tests for the datashuffler
"""
batch_size = 32
validation_batch_size = 32
validation_batch_size = 400
iterations = 300
seed = 10
def dummy_experiment(data_s, architecture):
def dummy_experiment(data_s, embedding):
"""
Create a dummy experiment and return the EER
"""
data_shuffler = object.__new__(Memory)
data_shuffler.__dict__ = data_s.__dict__.copy()
# Extracting features for enrollment
enroll_data, enroll_labels = data_shuffler.get_batch()
enroll_features = architecture(enroll_data)
enroll_features = embedding(enroll_data)
del enroll_data
# Extracting features for probing
probe_data, probe_labels = data_shuffler.get_batch()
probe_features = architecture(probe_data)
probe_features = embedding(probe_data)
del probe_data
# Creating models
......@@ -90,12 +89,6 @@ def test_cnn_trainer():
data_augmentation=data_augmentation,
normalizer=ScaleFactor())
validation_data_shuffler = Memory(validation_data, validation_labels,
input_shape=[None, 28, 28, 1],
batch_size=batch_size,
data_augmentation=data_augmentation,
normalizer=ScaleFactor())
directory = "./temp/cnn"
# Loss for the softmax
......@@ -138,45 +131,40 @@ def test_siamesecnn_trainer():
# Creating datashufflers
train_data_shuffler = SiameseMemory(train_data, train_labels,
input_shape=[28, 28, 1],
batch_size=batch_size)
input_shape=[None, 28, 28, 1],
batch_size=batch_size,
normalizer=ScaleFactor())
validation_data_shuffler = SiameseMemory(validation_data, validation_labels,
input_shape=[28, 28, 1],
batch_size=validation_batch_size)
input_shape=[None, 28, 28, 1],
batch_size=validation_batch_size,
normalizer=ScaleFactor())
directory = "./temp/siamesecnn"
# Preparing the architecture
architecture = Chopra(seed=seed, fc1_output=10)
inputs = {}
inputs['left'] = tf.placeholder(tf.float32, shape=[None, 28, 28, 1], name="input_left")
inputs['right'] = tf.placeholder(tf.float32, shape=[None, 28, 28, 1], name="input_right")
inputs['label'] = tf.placeholder(tf.int64, shape=[None], name="label")
graph = {}
graph['left'] = architecture(inputs['left'])
graph['right'] = architecture(inputs['right'])
# Loss for the Siamese
loss = ContrastiveLoss(contrastive_margin=4.)
# One graph trainer
trainer = SiameseTrainer(inputs=inputs,
graph=graph,
loss=loss,
input_pl = train_data_shuffler("data")
graph = {}
graph['left'] = architecture(input_pl['left'])
graph['right'] = architecture(input_pl['right'])
trainer = SiameseTrainer(train_data_shuffler,
iterations=iterations,
prefetch=False,
analizer=None,
learning_rate=constant(0.05, name="siamese_lr"),
optimizer=tf.train.AdamOptimizer(name="adam_siamese"),
temp_dir=directory
)
trainer.train(train_data_shuffler)
temp_dir=directory)
eer = dummy_experiment(validation_data_shuffler, architecture)
trainer.create_network_from_scratch(graph=graph,
loss=loss,
learning_rate=constant(0.01, name="regular_lr"),
optimizer=tf.train.GradientDescentOptimizer(0.01),)
trainer.train(train_data_shuffler)
embedding = Embedding(train_data_shuffler("data", from_queue=False)['left'], graph['left'])
eer = dummy_experiment(validation_data_shuffler, embedding)
# At least 80% of accuracy
assert eer < 0.25
assert eer < 0.15
shutil.rmtree(directory)
del architecture
......
......@@ -63,9 +63,7 @@ def test_cnn_pretrained():
data_augmentation=data_augmentation,
normalizer=ScaleFactor())
validation_data = numpy.reshape(validation_data, (validation_data.shape[0], 28, 28, 1))
directory = "./temp/cnn"
directory2 = "./temp/cnn2"
# Creating a random network
input_pl = train_data_shuffler("data", from_queue=True)
......
......@@ -5,12 +5,13 @@
import tensorflow as tf
from tensorflow.core.framework import summary_pb2
from ..analyzers import ExperimentAnalizer, SoftmaxAnalizer
from ..network import SequenceNetwork
from .Trainer import Trainer
from ..analyzers import SoftmaxAnalizer
from .learning_rate import constant
import os
import logging
from bob.learn.tensorflow.utils.session import Session
import bob.core
logger = logging.getLogger("bob.learn")
......@@ -61,215 +62,109 @@ class SiameseTrainer(Trainer):
"""
def __init__(self,
inputs,
graph,
optimizer=tf.train.AdamOptimizer(),
use_gpu=False,
loss=None,
temp_dir="cnn",
# Learning rate
learning_rate=None,
train_data_shuffler,
###### training options ##########
convergence_threshold=0.01,
iterations=5000,
snapshot=500,
prefetch=False,
validation_snapshot=100,
## Analizer
analizer=ExperimentAnalizer(),
analizer=SoftmaxAnalizer(),
model_from_file="",
# Temporatu dir
temp_dir="siamese_cnn",
verbosity_level=2
):
import ipdb;
ipdb.set_trace();
self.inputs = inputs
self.graph = graph
self.loss = loss
if not isinstance(self.graph, dict) or not(('left' and 'right') in self.graph.keys()):
raise ValueError("Expected a dict with the elements `right` and `left` as input for the keywork `graph`")
self.predictor = self.loss(self.graph, inputs['label'])
self.optimizer_class = optimizer
self.use_gpu = use_gpu
self.train_data_shuffler = train_data_shuffler
self.temp_dir = temp_dir
if learning_rate is None and model_from_file == "":
self.learning_rate = constant()
else:
self.learning_rate = learning_rate
self.iterations = iterations
self.snapshot = snapshot
self.validation_snapshot = validation_snapshot
self.convergence_threshold = convergence_threshold
self.prefetch = prefetch
# Training variables used in the fit
self.optimizer = None
self.training_graph = None
self.train_data_shuffler = None
self.summaries_train = None
self.train_summary_writter = None
self.thread_pool = None
# Validation data
self.validation_graph = None
self.validation_summary_writter = None
# Analizer
self.analizer = analizer
self.thread_pool = None
self.enqueue_op = None
self.global_step = None
self.model_from_file = model_from_file
self.session = None
bob.core.log.set_verbosity_level(logger, verbosity_level)
self.between_class_graph_train = None
self.within_class_graph_train = None
self.between_class_graph_validation = None
self.within_class_graph_validation = None
def bootstrap_placeholders(self, train_data_shuffler, validation_data_shuffler):
"""
Persist the placeholders
"""
# Persisting the placeholders
if self.prefetch:
batch, batch2, label = train_data_shuffler.get_placeholders_forprefetch()
else:
batch, batch2, label = train_data_shuffler.get_placeholders()
tf.add_to_collection("train_placeholder_data", batch)
tf.add_to_collection("train_placeholder_data2", batch2)
tf.add_to_collection("train_placeholder_label", label)
# Creating validation graph
if validation_data_shuffler is not None:
batch, batch2, label = validation_data_shuffler.get_placeholders()
tf.add_to_collection("validation_placeholder_data", batch)
tf.add_to_collection("validation_placeholder_data2", batch2)
tf.add_to_collection("validation_placeholder_label", label)
def bootstrap_placeholders_fromfile(self, train_data_shuffler, validation_data_shuffler):
"""
Load placeholders from file
"""
train_data_shuffler.set_placeholders(tf.get_collection("train_placeholder_data")[0],
tf.get_collection("train_placeholder_data2")[0],
tf.get_collection("train_placeholder_label")[0])
if validation_data_shuffler is not None:
train_data_shuffler.set_placeholders(tf.get_collection("validation_placeholder_data")[0],
tf.get_collection("validation_placeholder_data2")[0],
tf.get_collection("validation_placeholder_label")[0])
self.graph = None
self.loss = None
self.predictor = None
self.optimizer_class = None
self.learning_rate = None
# Training variables used in the fit
self.optimizer = None
self.data_ph = None
self.label_ph = None
self.saver = None
def bootstrap_graphs(self, train_data_shuffler, validation_data_shuffler):
"""
Create all the necessary graphs for training, validation and inference graphs
"""
super(SiameseTrainer, self).bootstrap_graphs(train_data_shuffler, validation_data_shuffler)
bob.core.log.set_verbosity_level(logger, verbosity_level)
# Triplet specific
tf.add_to_collection("between_class_graph_train", self.between_class_graph_train)
tf.add_to_collection("within_class_graph_train", self.within_class_graph_train)
# Creating the session
self.session = Session.instance(new=True).session
self.from_scratch = True
# Creating validation graph
if validation_data_shuffler is not None:
tf.add_to_collection("between_class_graph_validation", self.between_class_graph_validation)
tf.add_to_collection("within_class_graph_validation", self.within_class_graph_validation)
bob.core.log.set_verbosity_level(logger, verbosity_level)
self.bootstrap_placeholders(train_data_shuffler, validation_data_shuffler)
def create_network_from_scratch(self,
graph,
optimizer=tf.train.AdamOptimizer(),
loss=None,
def bootstrap_graphs_fromfile(self, train_data_shuffler, validation_data_shuffler):
"""
Bootstrap all the necessary data from file
# Learning rate
learning_rate=None,
):
** Parameters **
session: Tensorflow session
train_data_shuffler: Data shuffler for training
validation_data_shuffler: Data shuffler for validation
"""
self.data_ph = self.train_data_shuffler("data")
self.label_ph = self.train_data_shuffler("label")
saver = super(SiameseTrainer, self).bootstrap_graphs_fromfile(train_data_shuffler, validation_data_shuffler)
self.graph = graph
if "left" and "right" not in self.graph:
raise ValueError("`graph` should be a dictionary with two elements (`left`and `right`)")
self.between_class_graph_train = tf.get_collection("between_class_graph_train")[0]
self.within_class_graph_train = tf.get_collection("within_class_graph_train")[0]
self.loss = loss
self.predictor = self.loss(self.label_ph,
self.graph["left"],
self.graph["right"])
self.optimizer_class = optimizer
self.learning_rate = learning_rate
if validation_data_shuffler is not None:
self.between_class_graph_validation = tf.get_collection("between_class_graph_validation")[0]
self.within_class_graph_validation = tf.get_collection("within_class_graph_validation")[0]
# TODO: find an elegant way to provide this as a parameter of the trainer
self.global_step = tf.Variable(0, trainable=False, name="global_step")
self.bootstrap_placeholders_fromfile(train_data_shuffler, validation_data_shuffler)
# Saving all the variables
self.saver = tf.train.Saver(var_list=tf.global_variables())
return saver
tf.add_to_collection("global_step", self.global_step)
def compute_graph(self, data_shuffler, prefetch=False, name="", training=True):
"""
Computes the graph for the trainer.
tf.add_to_collection("graph", self.graph)
tf.add_to_collection("predictor", self.predictor)
tf.add_to_collection("data_ph", self.data_ph)
tf.add_to_collection("label_ph", self.label_ph)
** Parameters **
# Preparing the optimizer
self.optimizer_class._learning_rate = self.learning_rate
self.optimizer = self.optimizer_class.minimize(self.predictor[0], global_step=self.global_step)
tf.add_to_collection("optimizer", self.optimizer)
tf.add_to_collection("learning_rate", self.learning_rate)
data_shuffler: Data shuffler
prefetch:
name: Name of the graph
"""
self.summaries_train = self.create_general_summary()
tf.add_to_collection("summaries_train", self.summaries_train)
# Defining place holders
if prefetch:
[placeholder_left_data, placeholder_right_data, placeholder_labels] = data_shuffler.get_placeholders_forprefetch(name=name)
# Defining a placeholder queue for prefetching
queue = tf.FIFOQueue(capacity=100,
dtypes=[tf.float32, tf.float32, tf.int64],
shapes=[placeholder_left_data.get_shape().as_list()[1:],
placeholder_right_data.get_shape().as_list()[1:],
[]])
# Fetching the place holders from the queue
self.enqueue_op = queue.enqueue_many([placeholder_left_data, placeholder_right_data, placeholder_labels])
feature_left_batch, feature_right_batch, label_batch = queue.dequeue_many(data_shuffler.batch_size)
# Creating the architecture for train and validation
if not isinstance(self.architecture, SequenceNetwork):
raise ValueError("The variable `architecture` must be an instance of "
"`bob.learn.tensorflow.network.SequenceNetwork`")
else:
[feature_left_batch, feature_right_batch, label_batch] = data_shuffler.get_placeholders(name=name)
# Creating the siamese graph
train_left_graph = self.architecture.compute_graph(feature_left_batch, training=training)
train_right_graph = self.architecture.compute_graph(feature_right_batch, training=training)
graph, between_class_graph, within_class_graph = self.loss(label_batch,
train_left_graph,
train_right_graph)
if training:
self.between_class_graph_train = between_class_graph
self.within_class_graph_train = within_class_graph
else:
self.between_class_graph_validation = between_class_graph
self.within_class_graph_validation = within_class_graph
return graph
# Creating the variables
tf.global_variables_initializer().run(session=self.session)
def get_feed_dict(self, data_shuffler):
"""
......@@ -279,13 +174,11 @@ class SiameseTrainer(Trainer):
data_shuffler:
"""
[batch_left, batch_right, labels] = data_shuffler.get_batch()
[placeholder_left_data, placeholder_right_data, placeholder_label] = data_shuffler.get_placeholders()
feed_dict = {placeholder_left_data: batch_left,
placeholder_right_data: batch_right,
placeholder_label: labels}
feed_dict = {self.data_ph['left']: batch_left,
self.data_ph['right']: batch_right,
self.label_ph: labels}
return feed_dict
......@@ -298,73 +191,25 @@ class SiameseTrainer(Trainer):
step: Iteration number
"""
if self.prefetch:
_, l, bt_class, wt_class, lr, summary = self.session.run([self.optimizer,
self.training_graph, self.between_class_graph_train, self.within_class_graph_train,
self.learning_rate, self.summaries_train])
else:
feed_dict = self.get_feed_dict(self.train_data_shuffler)
_, l, bt_class, wt_class, lr, summary = self.session.run([self.optimizer,
self.training_graph, self.between_class_graph_train, self.within_class_graph_train,
self.learning_rate, self.summaries_train], feed_dict=feed_dict)
feed_dict = self.get_feed_dict(self.train_data_shuffler)
_, l, bt_class, wt_class, lr, summary = self.session.run([
self.optimizer,
self.predictor[0], self.predictor[1],
self.predictor[2],
self.learning_rate, self.summaries_train], feed_dict=feed_dict)
logger.info("Loss training set step={0} = {1}".format(step, l))
self.train_summary_writter.add_summary(summary, step)
def compute_validation(self, data_shuffler, step):
"""
Computes the loss in the validation set
** Parameters **
session: Tensorflow session
data_shuffler: The data shuffler to be used
step: Iteration number
"""
if self.validation_summary_writter is None:
self.validation_summary_writter = tf.summary.FileWriter(os.path.join(self.temp_dir, 'validation'), self.session.graph)
self.validation_graph = self.compute_graph(data_shuffler, name="validation", training=False)
feed_dict = self.get_feed_dict(data_shuffler)
l, bt_class, wt_class = self.session.run([self.validation_graph,
self.between_class_graph_validation, self.within_class_graph_validation],
feed_dict=feed_dict)
summaries = []
summaries.append(summary_pb2.Summary.Value(tag="loss", simple_value=float(l)))
summaries.append(summary_pb2.Summary.Value(tag="between_class_loss", simple_value=float(bt_class)))
summaries.append(summary_pb2.Summary.Value(tag="within_class_loss", simple_value=float(wt_class)))
self.validation_summary_writter.add_summary(summary_pb2.Summary(value=summaries), step)
logger.info("Loss VALIDATION set step={0} = {1}".format(step, l))
def create_general_summary(self):
"""
Creates a simple tensorboard summary with the value of the loss and learning rate
"""
# Train summary
tf.summary.scalar('loss', self.training_graph)
tf.summary.scalar('between_class_loss', self.between_class_graph_train)