From 1a517ecd4564f1436c79e1945bb991b2e8423973 Mon Sep 17 00:00:00 2001
From: Tiago Freitas Pereira <tiagofrepereira@gmail.com>
Date: Fri, 11 Nov 2016 17:08:33 +0100
Subject: [PATCH] DOcumenting

---
 bob/learn/tensorflow/datashuffler/Memory.py   |   2 +-
 .../tensorflow/datashuffler/SiameseDisk.py    |   7 +-
 .../tensorflow/datashuffler/TripletDisk.py    |  10 +-
 .../tensorflow/network/SequenceNetwork.py     |   4 +-
 bob/learn/tensorflow/network/VGG16_mod.py     |   8 +-
 bob/learn/tensorflow/test/test_cnn.py         |  69 ++++--------
 bob/learn/tensorflow/test/test_cnn_load.py    |  44 ++++++++
 .../test/test_cnn_pretrained_model.py         |  47 ++++++--
 bob/learn/tensorflow/test/test_cnn_scratch.py |   7 +-
 .../tensorflow/test/test_datashuffler.py      |   2 +-
 .../test/test_datashuffler_augmentation.py    |   2 +-
 bob/learn/tensorflow/test/test_dnn.py         |  50 ++++-----
 .../tensorflow/trainers/SiameseTrainer.py     |  28 ++---
 bob/learn/tensorflow/trainers/Trainer.py      |  13 +--
 .../tensorflow/trainers/TripletTrainer.py     |  40 +++----
 bob/learn/tensorflow/utils/session.py         |   3 -
 doc/py_api.rst                                |   2 +
 doc/user_guide.rst                            | 105 +++++++++++++++---
 18 files changed, 274 insertions(+), 169 deletions(-)
 create mode 100644 bob/learn/tensorflow/test/test_cnn_load.py

diff --git a/bob/learn/tensorflow/datashuffler/Memory.py b/bob/learn/tensorflow/datashuffler/Memory.py
index 45e64404..1b9509e0 100644
--- a/bob/learn/tensorflow/datashuffler/Memory.py
+++ b/bob/learn/tensorflow/datashuffler/Memory.py
@@ -60,7 +60,7 @@ class Memory(Base):
         indexes = numpy.array(range(self.data.shape[0]))
         numpy.random.shuffle(indexes)
 
-        selected_data = self.data[indexes[0:self.batch_size], :, :, :]
+        selected_data = self.data[indexes[0:self.batch_size], ...]
         selected_labels = self.labels[indexes[0:self.batch_size]]
 
         # Applying the data augmentation
diff --git a/bob/learn/tensorflow/datashuffler/SiameseDisk.py b/bob/learn/tensorflow/datashuffler/SiameseDisk.py
index 03b4423d..52f64ee4 100644
--- a/bob/learn/tensorflow/datashuffler/SiameseDisk.py
+++ b/bob/learn/tensorflow/datashuffler/SiameseDisk.py
@@ -75,13 +75,10 @@ class SiameseDisk(Siamese, Disk):
         genuine = True
         for i in range(self.shape[0]):
             file_name, file_name_p = self.get_genuine_or_not(self.data, self.labels, genuine=genuine)
-            sample_l[i, ...] = self.load_from_file(str(file_name))
-            sample_r[i, ...] = self.load_from_file(str(file_name_p))
+            sample_l[i, ...] = self.normalize_sample(self.load_from_file(str(file_name)))
+            sample_r[i, ...] = self.normalize_sample(self.load_from_file(str(file_name_p)))
 
             labels_siamese[i] = not genuine
             genuine = not genuine
 
-        sample_l = self.normalize_sample(sample_l)
-        sample_r = self.normalize_sample(sample_r)
-
         return sample_l, sample_r, labels_siamese
diff --git a/bob/learn/tensorflow/datashuffler/TripletDisk.py b/bob/learn/tensorflow/datashuffler/TripletDisk.py
index 2df05c08..fd117190 100644
--- a/bob/learn/tensorflow/datashuffler/TripletDisk.py
+++ b/bob/learn/tensorflow/datashuffler/TripletDisk.py
@@ -78,12 +78,8 @@ class TripletDisk(Triplet, Disk):
 
         for i in range(self.shape[0]):
             file_name_a, file_name_p, file_name_n = self.get_one_triplet(self.data, self.labels)
-            sample_a[i, ...] = self.load_from_file(str(file_name_a))
-            sample_p[i, ...] = self.load_from_file(str(file_name_p))
-            sample_n[i, ...] = self.load_from_file(str(file_name_n))
-
-        sample_a = self.normalize_sample(sample_a)
-        sample_p = self.normalize_sample(sample_p)
-        sample_n = self.normalize_sample(sample_n)
+            sample_a[i, ...] = self.normalize_sample(self.load_from_file(str(file_name_a)))
+            sample_p[i, ...] = self.normalize_sample(self.load_from_file(str(file_name_p)))
+            sample_n[i, ...] = self.normalize_sample(self.load_from_file(str(file_name_n)))
 
         return [sample_a, sample_p, sample_n]
diff --git a/bob/learn/tensorflow/network/SequenceNetwork.py b/bob/learn/tensorflow/network/SequenceNetwork.py
index fd727c1d..49af83b5 100644
--- a/bob/learn/tensorflow/network/SequenceNetwork.py
+++ b/bob/learn/tensorflow/network/SequenceNetwork.py
@@ -322,8 +322,8 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
         session = Session.instance().session
 
         self.sequence_net = pickle.loads(open(path+"_sequence_net.pickle").read())
-        #saver = tf.train.import_meta_graph(path + ".meta", clear_devices=clear_devices)
-        saver = tf.train.import_meta_graph(path + ".meta")
+        saver = tf.train.import_meta_graph(path + ".meta", clear_devices=clear_devices)
+        #saver = tf.train.import_meta_graph(path + ".meta")
         saver.restore(session, path)
         self.inference_graph = tf.get_collection("inference_graph")[0]
         self.inference_placeholder = tf.get_collection("inference_placeholder")[0]
diff --git a/bob/learn/tensorflow/network/VGG16_mod.py b/bob/learn/tensorflow/network/VGG16_mod.py
index a3b7f187..bdfa4f89 100644
--- a/bob/learn/tensorflow/network/VGG16_mod.py
+++ b/bob/learn/tensorflow/network/VGG16_mod.py
@@ -71,10 +71,13 @@ class VGG16_mod(SequenceNetwork):
                  default_feature_layer="fc8",
 
                  seed=10,
+
+                 do_dropout=True,
+
                  use_gpu=False):
 
         super(VGG16_mod, self).__init__(default_feature_layer=default_feature_layer,
-                                              use_gpu=use_gpu)
+                                        use_gpu=use_gpu)
 
         # First convolutional block
         self.conv1_1_kernel_size = conv1_1_kernel_size
@@ -223,6 +226,9 @@ class VGG16_mod(SequenceNetwork):
                         ))
         self.add(AveragePooling(name="pooling5", strides=[1, 2, 2, 1]))
 
+        if do_dropout:
+            self.add(Dropout(name="dropout", keep_prob=0.4))
+
         self.add(FullyConnected(name="fc8", output_dim=n_classes,
                                 activation=None,
                                 weights_initialization=Xavier(seed=seed, use_gpu=self.use_gpu),
diff --git a/bob/learn/tensorflow/test/test_cnn.py b/bob/learn/tensorflow/test/test_cnn.py
index 43f0ff5e..257258e1 100644
--- a/bob/learn/tensorflow/test/test_cnn.py
+++ b/bob/learn/tensorflow/test/test_cnn.py
@@ -8,12 +8,11 @@ from bob.learn.tensorflow.datashuffler import Memory, SiameseMemory, TripletMemo
 from bob.learn.tensorflow.network import Chopra
 from bob.learn.tensorflow.loss import BaseLoss, ContrastiveLoss, TripletLoss
 from bob.learn.tensorflow.trainers import Trainer, SiameseTrainer, TripletTrainer, constant
+from .test_cnn_scratch import validate_network
 
-# from ..analyzers import ExperimentAnalizer, SoftmaxAnalizer
-from bob.learn.tensorflow.util import load_mnist
+from bob.learn.tensorflow.utils import load_mnist
 import tensorflow as tf
 import bob.io.base
-import os
 import shutil
 from scipy.spatial.distance import cosine
 import bob.measure
@@ -28,7 +27,7 @@ iterations = 50
 seed = 10
 
 
-def dummy_experiment(data_s, architecture, session):
+def dummy_experiment(data_s, architecture):
     """
     Create a dummy experiment and return the EER
     """
@@ -38,12 +37,12 @@ def dummy_experiment(data_s, architecture, session):
 
     # Extracting features for enrollment
     enroll_data, enroll_labels = data_shuffler.get_batch()
-    enroll_features = architecture(enroll_data, session=session)
+    enroll_features = architecture(enroll_data)
     del enroll_data
 
     # Extracting features for probing
     probe_data, probe_labels = data_shuffler.get_batch()
-    probe_features = architecture(probe_data, session=session)
+    probe_features = architecture(probe_data)
     del probe_data
 
     # Creating models
@@ -102,26 +101,14 @@ def test_cnn_trainer():
                       prefetch=False,
                       temp_dir=directory)
     trainer.train(train_data_shuffler)
-    del trainer #Just to clean tf.variables
 
-    with tf.Session() as session:
+    accuracy = validate_network(validation_data, validation_labels, architecture)
 
-        # Testing
-        chopra = Chopra(seed=seed, fc1_output=10)
-        chopra.load(session, os.path.join(directory, "model.ckp"))
-
-        validation_data_shuffler = Memory(validation_data, validation_labels,
-                                          input_shape=[28, 28, 1],
-                                          batch_size=validation_batch_size)
-
-        [data, labels] = validation_data_shuffler.get_batch()
-        predictions = chopra(data, session=session)
-        accuracy = 100. * numpy.sum(numpy.argmax(predictions, 1) == labels) / predictions.shape[0]
-
-        # At least 80% of accuracy
-        assert accuracy > 80.
-        shutil.rmtree(directory)
-        del chopra
+    # At least 80% of accuracy
+    assert accuracy > 80.
+    shutil.rmtree(directory)
+    del trainer
+    del architecture
 
 
 def test_siamesecnn_trainer():
@@ -155,19 +142,15 @@ def test_siamesecnn_trainer():
                              temp_dir=directory)
 
     trainer.train(train_data_shuffler)
-    del trainer  # Just to clean tf.variables
 
-    with tf.Session() as session:
-        # Testing
-        chopra = Chopra(seed=seed, fc1_output=10)
-        chopra.load(session, os.path.join(directory, "model.ckp"))
+    eer = dummy_experiment(validation_data_shuffler, architecture)
 
-        eer = dummy_experiment(validation_data_shuffler, chopra, session)
+    # At least 80% of accuracy
+    assert eer < 0.25
+    shutil.rmtree(directory)
 
-        # At least 80% of accuracy
-        assert eer < 0.25
-        shutil.rmtree(directory)
-        del chopra
+    del architecture
+    del trainer  # Just to clean tf.variables
 
 
 def test_tripletcnn_trainer():
@@ -201,17 +184,13 @@ def test_tripletcnn_trainer():
                              temp_dir=directory)
 
     trainer.train(train_data_shuffler)
-    del trainer  # Just to clean tf.variables
 
-    with tf.Session() as session:
+    # Testing
+    eer = dummy_experiment(validation_data_shuffler, architecture)
 
-        # Testing
-        chopra = Chopra(seed=seed, fc1_output=10)
-        chopra.load(session, os.path.join(directory, "model.ckp"))
+    # At least 80% of accuracy
+    assert eer < 0.25
+    shutil.rmtree(directory)
 
-        eer = dummy_experiment(validation_data_shuffler, chopra, session)
-
-        # At least 80% of accuracy
-        assert eer < 0.25
-        shutil.rmtree(directory)
-        del chopra
+    del architecture
+    del trainer  # Just to clean tf.variables
diff --git a/bob/learn/tensorflow/test/test_cnn_load.py b/bob/learn/tensorflow/test/test_cnn_load.py
new file mode 100644
index 00000000..02553e40
--- /dev/null
+++ b/bob/learn/tensorflow/test/test_cnn_load.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
+# @date: Thu 13 Oct 2016 13:35 CEST
+
+
+"""
+Some unit tests that create networks on the fly
+"""
+
+
+import numpy
+import pkg_resources
+from bob.learn.tensorflow.utils import load_mnist
+from bob.learn.tensorflow.network import SequenceNetwork
+from bob.learn.tensorflow.datashuffler import Memory
+
+
+def validate_network(validation_data, validation_labels, network):
+    # Testing
+    validation_data_shuffler = Memory(validation_data, validation_labels,
+                                      input_shape=[28, 28, 1],
+                                      batch_size=400)
+
+    [data, labels] = validation_data_shuffler.get_batch()
+    predictions = network.predict(data)
+    accuracy = 100. * numpy.sum(predictions == labels) / predictions.shape[0]
+
+    return accuracy
+
+"""
+def test_load_test_cnn():
+
+    _, _, validation_data, validation_labels = load_mnist()
+
+    # Creating datashufflers
+    validation_data = numpy.reshape(validation_data, (validation_data.shape[0], 28, 28, 1))
+    network = SequenceNetwork()
+    network.load(pkg_resources.resource_filename(__name__, 'data/cnn_mnist/model.ckp'))
+
+    accuracy = validate_network(validation_data, validation_labels, network)
+    assert accuracy > 80
+    del network
+"""
diff --git a/bob/learn/tensorflow/test/test_cnn_pretrained_model.py b/bob/learn/tensorflow/test/test_cnn_pretrained_model.py
index b27a1e23..e8d4e90c 100644
--- a/bob/learn/tensorflow/test/test_cnn_pretrained_model.py
+++ b/bob/learn/tensorflow/test/test_cnn_pretrained_model.py
@@ -9,7 +9,10 @@ import os
 from bob.learn.tensorflow.datashuffler import Memory, ImageAugmentation
 from bob.learn.tensorflow.loss import BaseLoss
 from bob.learn.tensorflow.trainers import Trainer, constant
-from bob.learn.tensorflow.util import load_mnist
+from bob.learn.tensorflow.utils import load_mnist
+from bob.learn.tensorflow.network import SequenceNetwork
+from bob.learn.tensorflow.layers import Conv2D, FullyConnected
+
 import tensorflow as tf
 import shutil
 
@@ -22,10 +25,36 @@ validation_batch_size = 400
 iterations = 50
 seed = 10
 
-from test_cnn_scratch import scratch_network, validate_network
+
+def scratch_network():
+    # Creating a random network
+    scratch = SequenceNetwork(default_feature_layer="fc1")
+    scratch.add(Conv2D(name="conv1", kernel_size=3,
+                       filters=10,
+                       activation=tf.nn.tanh,
+                       batch_norm=False))
+    scratch.add(FullyConnected(name="fc1", output_dim=10,
+                               activation=None,
+                               batch_norm=False
+                               ))
+
+    return scratch
+
+
+def validate_network(validation_data, validation_labels, network):
+    # Testing
+    validation_data_shuffler = Memory(validation_data, validation_labels,
+                                      input_shape=[28, 28, 1],
+                                      batch_size=validation_batch_size)
+
+    [data, labels] = validation_data_shuffler.get_batch()
+    predictions = network.predict(data)
+    accuracy = 100. * numpy.sum(predictions == labels) / predictions.shape[0]
+
+    return accuracy
 
 
-def test_cnn_trainer_scratch():
+def test_cnn_pretrained():
     train_data, train_labels, validation_data, validation_labels = load_mnist()
     train_data = numpy.reshape(train_data, (train_data.shape[0], 28, 28, 1))
 
@@ -55,8 +84,7 @@ def test_cnn_trainer_scratch():
                       learning_rate=constant(0.05, name="lr"),
                       temp_dir=directory)
     trainer.train(train_data_shuffler)
-
-    accuracy = validate_network(validation_data, validation_labels, directory)
+    accuracy = validate_network(validation_data, validation_labels, scratch)
     assert accuracy > 85
 
     del scratch
@@ -77,7 +105,12 @@ def test_cnn_trainer_scratch():
 
     trainer.train(train_data_shuffler)
 
-    accuracy = validate_network(validation_data, validation_labels, directory2)
-    assert accuracy > 85
+    accuracy = validate_network(validation_data, validation_labels, scratch)
+    assert accuracy > 90
     shutil.rmtree(directory)
     shutil.rmtree(directory2)
+
+    del scratch
+    del loss
+    del trainer
+
diff --git a/bob/learn/tensorflow/test/test_cnn_scratch.py b/bob/learn/tensorflow/test/test_cnn_scratch.py
index 242eac52..580985e7 100644
--- a/bob/learn/tensorflow/test/test_cnn_scratch.py
+++ b/bob/learn/tensorflow/test/test_cnn_scratch.py
@@ -7,12 +7,11 @@ import numpy
 import bob.io.base
 import os
 from bob.learn.tensorflow.datashuffler import Memory, ImageAugmentation
-from bob.learn.tensorflow.initialization import Xavier, Constant
 from bob.learn.tensorflow.network import SequenceNetwork
 from bob.learn.tensorflow.loss import BaseLoss
 from bob.learn.tensorflow.trainers import Trainer
 from bob.learn.tensorflow.utils import load_mnist
-from bob.learn.tensorflow.layers import Conv2D, FullyConnected, MaxPooling
+from bob.learn.tensorflow.layers import Conv2D, FullyConnected
 import tensorflow as tf
 import shutil
 
@@ -33,13 +32,9 @@ def scratch_network():
     scratch.add(Conv2D(name="conv1", kernel_size=3,
                        filters=10,
                        activation=tf.nn.tanh,
-                       weights_initialization=Xavier(seed=seed, use_gpu=False),
-                       bias_initialization=Constant(use_gpu=False),
                        batch_norm=False))
     scratch.add(FullyConnected(name="fc1", output_dim=10,
                                activation=None,
-                               weights_initialization=Xavier(seed=seed, use_gpu=False),
-                               bias_initialization=Constant(use_gpu=False),
                                batch_norm=False
                                ))
 
diff --git a/bob/learn/tensorflow/test/test_datashuffler.py b/bob/learn/tensorflow/test/test_datashuffler.py
index fb13e422..648fdbed 100644
--- a/bob/learn/tensorflow/test/test_datashuffler.py
+++ b/bob/learn/tensorflow/test/test_datashuffler.py
@@ -6,7 +6,7 @@
 import numpy
 from bob.learn.tensorflow.datashuffler import Memory, SiameseMemory, TripletMemory, Disk, SiameseDisk, TripletDisk
 import pkg_resources
-from ..util import load_mnist
+from bob.learn.tensorflow.utils import load_mnist
 import os
 
 """
diff --git a/bob/learn/tensorflow/test/test_datashuffler_augmentation.py b/bob/learn/tensorflow/test/test_datashuffler_augmentation.py
index 67de430d..ed1281dd 100644
--- a/bob/learn/tensorflow/test/test_datashuffler_augmentation.py
+++ b/bob/learn/tensorflow/test/test_datashuffler_augmentation.py
@@ -6,7 +6,7 @@
 import numpy
 from bob.learn.tensorflow.datashuffler import Memory, SiameseMemory, TripletMemory, Disk, SiameseDisk, TripletDisk, ImageAugmentation
 import pkg_resources
-from ..util import load_mnist
+from bob.learn.tensorflow.utils import load_mnist
 import os
 
 """
diff --git a/bob/learn/tensorflow/test/test_dnn.py b/bob/learn/tensorflow/test/test_dnn.py
index 613b744c..ddb5b0ff 100644
--- a/bob/learn/tensorflow/test/test_dnn.py
+++ b/bob/learn/tensorflow/test/test_dnn.py
@@ -8,13 +8,9 @@ from bob.learn.tensorflow.datashuffler import Memory
 from bob.learn.tensorflow.network import MLP
 from bob.learn.tensorflow.loss import BaseLoss
 from bob.learn.tensorflow.trainers import Trainer, constant
-# from ..analyzers import ExperimentAnalizer, SoftmaxAnalizer
-from bob.learn.tensorflow.util import load_mnist
+from bob.learn.tensorflow.utils import load_mnist
 import tensorflow as tf
-import bob.io.base
-import os
 import shutil
-import bob.measure
 
 """
 Some unit tests for the datashuffler
@@ -26,14 +22,25 @@ iterations = 200
 seed = 10
 
 
+def validate_network(validation_data, validation_labels, network):
+    # Testing
+    validation_data_shuffler = Memory(validation_data, validation_labels,
+                                      input_shape=[784],
+                                      batch_size=validation_batch_size)
+
+    [data, labels] = validation_data_shuffler.get_batch()
+    predictions = network.predict(data)
+    accuracy = 100. * numpy.sum(predictions == labels) / predictions.shape[0]
+
+    return accuracy
+
+
 def test_dnn_trainer():
     train_data, train_labels, validation_data, validation_labels = load_mnist()
-    train_data = numpy.reshape(train_data, (train_data.shape[0], 28, 28, 1))
-    validation_data = numpy.reshape(validation_data, (validation_data.shape[0], 28, 28, 1))
 
     # Creating datashufflers
     train_data_shuffler = Memory(train_data, train_labels,
-                                 input_shape=[28, 28, 1],
+                                 input_shape=[784],
                                  batch_size=batch_size)
 
     directory = "./temp/dnn"
@@ -53,21 +60,12 @@ def test_dnn_trainer():
                       learning_rate=constant(0.05, name="dnn_lr"),
                       temp_dir=directory)
     trainer.train(train_data_shuffler)
-    del trainer# Just to clean the variables
-
-    with tf.Session() as session:
-        # Testing
-        mlp = MLP(10, hidden_layers=[15, 20])
-        mlp.load(session, os.path.join(directory, "model.ckp"))
-        validation_data_shuffler = Memory(validation_data, validation_labels,
-                                          input_shape=[28, 28, 1],
-                                          batch_size=validation_batch_size)
-
-        [data, labels] = validation_data_shuffler.get_batch()
-        predictions = mlp(data, session=session)
-        accuracy = 100. * numpy.sum(numpy.argmax(predictions, 1) == labels) / predictions.shape[0]
-
-        # At least 50% of accuracy for the DNN
-        assert accuracy > 50.
-        shutil.rmtree(directory)
-        session.close()
+
+    accuracy = validate_network(validation_data, validation_labels, architecture)
+
+    # At least 50% of accuracy for the DNN
+    assert accuracy > 50.
+    shutil.rmtree(directory)
+
+    del architecture
+    del trainer  # Just to clean the variables
diff --git a/bob/learn/tensorflow/trainers/SiameseTrainer.py b/bob/learn/tensorflow/trainers/SiameseTrainer.py
index ad7b558a..7b3b3764 100644
--- a/bob/learn/tensorflow/trainers/SiameseTrainer.py
+++ b/bob/learn/tensorflow/trainers/SiameseTrainer.py
@@ -49,9 +49,6 @@ class SiameseTrainer(Trainer):
                  temp_dir="cnn",
 
                  # Learning rate
-                 #base_learning_rate=0.001,
-                 #weight_decay=0.9,
-                 #decay_steps=1000,
                  learning_rate=constant(),
 
                  ###### training options ##########
@@ -76,9 +73,6 @@ class SiameseTrainer(Trainer):
             temp_dir=temp_dir,
 
             # Learning rate
-            #base_learning_rate=base_learning_rate,
-            #weight_decay=weight_decay,
-            #decay_steps=decay_steps,
             learning_rate=learning_rate,
 
             ###### training options ##########
@@ -207,7 +201,7 @@ class SiameseTrainer(Trainer):
 
         return feed_dict
 
-    def fit(self, session, step):
+    def fit(self, step):
         """
         Run one iteration (`forward` and `backward`)
 
@@ -217,19 +211,19 @@ class SiameseTrainer(Trainer):
 
         """
         if self.prefetch:
-            _, l, bt_class, wt_class, lr, summary = session.run([self.optimizer,
-                                             self.training_graph, self.between_class_graph_train, self.within_class_graph_train,
-                                             self.learning_rate, self.summaries_train])
+            _, 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 = session.run([self.optimizer,
+            _, 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)
 
         logger.info("Loss training set step={0} = {1}".format(step, l))
         self.train_summary_writter.add_summary(summary, step)
 
-    def compute_validation(self, session, data_shuffler, step):
+    def compute_validation(self, data_shuffler, step):
         """
         Computes the loss in the validation set
 
@@ -245,9 +239,9 @@ class SiameseTrainer(Trainer):
 
         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 = session.run([self.validation_graph,
-                                             self.between_class_graph_validation, self.within_class_graph_validation],
-                                             feed_dict=feed_dict)
+        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)))
@@ -268,7 +262,7 @@ class SiameseTrainer(Trainer):
         tf.scalar_summary('lr', self.learning_rate, name="train")
         return tf.merge_all_summaries()
 
-    def load_and_enqueue(self, session):
+    def load_and_enqueue(self):
         """
         Injecting data in the place holder queue
 
@@ -285,4 +279,4 @@ class SiameseTrainer(Trainer):
                          placeholder_right_data: batch_right,
                          placeholder_label: labels}
 
-            session.run(self.enqueue_op, feed_dict=feed_dict)
+            self.session.run(self.enqueue_op, feed_dict=feed_dict)
diff --git a/bob/learn/tensorflow/trainers/Trainer.py b/bob/learn/tensorflow/trainers/Trainer.py
index 47e4f54f..c648c7e4 100644
--- a/bob/learn/tensorflow/trainers/Trainer.py
+++ b/bob/learn/tensorflow/trainers/Trainer.py
@@ -306,7 +306,7 @@ class Trainer(object):
 
 
         """
-        saver = self.architecture.load(self.session, self.model_from_file)
+        saver = self.architecture.load(self.model_from_file)
 
         # Loading training graph
         self.training_graph = tf.get_collection("training_graph")[0]
@@ -357,21 +357,16 @@ class Trainer(object):
 
         logger.info("Initializing !!")
 
-        config = tf.ConfigProto(log_device_placement=True,
-                                gpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.333))
-        config.gpu_options.allow_growth = True
-
         # Pickle the architecture to save
         self.architecture.pickle_net(train_data_shuffler.deployment_shape)
 
-        #with tf.Session(config=config) as session:
-
+        Session.create()
         self.session = Session.instance().session
 
         # Loading a pretrained model
         if self.model_from_file != "":
             logger.info("Loading pretrained model from {0}".format(self.model_from_file))
-            saver = self.bootstrap_graphs_fromfile(self.session, train_data_shuffler, validation_data_shuffler)
+            saver = self.bootstrap_graphs_fromfile(train_data_shuffler, validation_data_shuffler)
         else:
             # Bootstraping all the graphs
             self.bootstrap_graphs(train_data_shuffler, validation_data_shuffler)
@@ -408,7 +403,7 @@ class Trainer(object):
         for step in range(self.iterations):
 
             start = time.time()
-            self.fit(self.session, step)
+            self.fit(step)
             end = time.time()
             summary = summary_pb2.Summary.Value(tag="elapsed_time", simple_value=float(end-start))
             self.train_summary_writter.add_summary(summary_pb2.Summary(value=[summary]), step)
diff --git a/bob/learn/tensorflow/trainers/TripletTrainer.py b/bob/learn/tensorflow/trainers/TripletTrainer.py
index 093bd45c..4c287a72 100644
--- a/bob/learn/tensorflow/trainers/TripletTrainer.py
+++ b/bob/learn/tensorflow/trainers/TripletTrainer.py
@@ -49,9 +49,6 @@ class TripletTrainer(Trainer):
                  temp_dir="cnn",
 
                  # Learning rate
-                 #base_learning_rate=0.001,
-                 #weight_decay=0.9,
-                 #decay_steps=1000,
                  learning_rate=constant(),
 
                  ###### training options ##########
@@ -76,9 +73,6 @@ class TripletTrainer(Trainer):
             temp_dir=temp_dir,
 
             # Learning rate
-            #base_learning_rate=base_learning_rate,
-            #weight_decay=weight_decay,
-            #decay_steps=decay_steps,
             learning_rate=learning_rate,
 
             ###### training options ##########
@@ -213,7 +207,7 @@ class TripletTrainer(Trainer):
 
         return feed_dict
 
-    def fit(self, session, step):
+    def fit(self, step):
         """
         Run one iteration (`forward` and `backward`)
 
@@ -223,21 +217,21 @@ class TripletTrainer(Trainer):
 
         """
         if self.prefetch:
-            _, l, bt_class, wt_class, lr, summary = session.run([self.optimizer,
-                                                                 self.training_graph, self.between_class_graph_train,
-                                                                 self.within_class_graph_train, self.learning_rate,
-                                                                 self.summaries_train])
+            _, 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 = 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)
+            _, 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)
         logger.info("Loss training set step={0} = {1}".format(step, l))
         self.train_summary_writter.add_summary(summary, step)
 
-    def compute_validation(self, session, data_shuffler, step):
+    def compute_validation(self, data_shuffler, step):
         """
         Computes the loss in the validation set
 
@@ -250,13 +244,13 @@ class TripletTrainer(Trainer):
 
         if self.validation_summary_writter is None:
             self.validation_summary_writter = tf.train.SummaryWriter(os.path.join(self.temp_dir, 'validation'),
-                                                                     session.graph)
+                                                                     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 = session.run([self.validation_graph,
-                                             self.between_class_graph_validation, self.within_class_graph_validation],
-                                            feed_dict=feed_dict)
+        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)))
@@ -277,7 +271,7 @@ class TripletTrainer(Trainer):
         tf.scalar_summary('lr', self.learning_rate, name="train")
         return tf.merge_all_summaries()
 
-    def load_and_enqueue(self, session):
+    def load_and_enqueue(self):
         """
         Injecting data in the place holder queue
 
@@ -294,4 +288,4 @@ class TripletTrainer(Trainer):
                          placeholder_positive_data: batch_positive,
                          placeholder_negative_data: batch_negative}
 
-            session.run(self.enqueue_op, feed_dict=feed_dict)
+            self.session.run(self.enqueue_op, feed_dict=feed_dict)
diff --git a/bob/learn/tensorflow/utils/session.py b/bob/learn/tensorflow/utils/session.py
index cd123bd5..a539eb32 100644
--- a/bob/learn/tensorflow/utils/session.py
+++ b/bob/learn/tensorflow/utils/session.py
@@ -15,6 +15,3 @@ class Session(object):
                                 gpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.333))
         config.gpu_options.allow_growth = True
         self.session = tf.Session()
-
-    #def __del__(self):
-    #    self.session.close()
\ No newline at end of file
diff --git a/doc/py_api.rst b/doc/py_api.rst
index 576cedf4..99b9b0c7 100644
--- a/doc/py_api.rst
+++ b/doc/py_api.rst
@@ -2,6 +2,8 @@
 .. Tiago de Freitas Pereira <laurent.el-shafey@idiap.ch>
 .. Tue 28 Aug 2012 18:09:40 CEST
 
+.. _py_api:
+
 ============
  Python API
 ============
diff --git a/doc/user_guide.rst b/doc/user_guide.rst
index e5732845..6f4fb7de 100644
--- a/doc/user_guide.rst
+++ b/doc/user_guide.rst
@@ -8,8 +8,8 @@
 ===========
 
 
-Getting started
----------------
+Quick start
+-----------
 
 Before explain the base elements of this library, lets first do a simple example.
 The example consists in training a very simple **CNN** with `MNIST` dataset in 4 steps.
@@ -48,18 +48,19 @@ The example consists in training a very simple **CNN** with `MNIST` dataset in 4
 
 .. doctest::
 
-    >>> with tf.Session() as session:
-    >>>    # Loading the model
-    >>>    architecture = bob.learn.tensorflow.network.SequenceNetwork()
-    >>>    architecture.load(session, "./cnn/model.ckp")
-    >>>    # Predicting
-    >>>    predictions = scratch.predict(validation_data, session=session)
-    >>>    # Computing an awesome accuracy for a simple network and 100 iterations
-    >>>    accuracy = 100. * numpy.sum(predictions == validation_labels) / predictions.shape[0]
-    >>>    print accuracy
+    >>> # Loading the model
+    >>> architecture = bob.learn.tensorflow.network.SequenceNetwork()
+    >>> architecture.load("./cnn/model.ckp")
+    >>> # Predicting
+    >>> predictions = scratch.predict(validation_data, session=session)
+    >>> # Computing an awesome accuracy for a simple network and 100 iterations
+    >>> accuracy = 100. * numpy.sum(predictions == validation_labels) / predictions.shape[0]
+    >>> print accuracy
     90.4714285714
 
-Now lets describe each step in detail.
+
+Understanding what you have done
+--------------------------------
 
 
 Preparing your input data
@@ -81,9 +82,9 @@ number of channels.
 Creating the architecture
 .........................
 
-Architectures are assembled as a :py:class:`bob.learn.tensorflow.network.SequenceNetwork` object.
-Once the objects are created it is necessary to fill it up with `Layers`_.
-The library has already some crafted networks implemented in `Architectures`_
+Architectures are assembled in the :py:class:`bob.learn.tensorflow.network.SequenceNetwork` object.
+Once the objects are created it is necessary to fill it up with `Layers <py_api.html#layers>`_
+The library has already some crafted networks implemented in `Architectures <py_api.html#architectures>`_
 
 
 Defining a loss and training
@@ -99,6 +100,80 @@ As for the loss, we have specific trainers for Siamese (:py:class:`bob.learn.ten
 nd Triplet networks (:py:class:`bob.learn.tensorflow.trainers.TripletTrainer`).
 
 
+Components in detail
+--------------------
+
+If you have reached this point it means that you want to understand a little bit more on how this library works.
+The next sections give some details of each element.
+
+Data Shufflers
+..............
+
+As mentioned before, datasets are wrapped in **data shufflers**.
+Data shufflers were designed to shuffle the input data for stochastic training.
+It has one basic functionality which is :py:meth:`bob.learn.tensorflow.datashuffler.Base.get_batch` functionality.
+
+The shufflers are categorized with respect to:
+ 1. How the data is fetched
+ 2. The type of the trainer
+ 3. How the data is sampled
+
+How do you want to fetch your data?
+```````````````````````````````````
+
+The data can be fetched either from the memory (:py:class:`bob.learn.tensorflow.datashuffler.Memory`), as in out example, or from
+disk (:py:class:`bob.learn.tensorflow.datashuffler.Disk`).
+To train networks fetched from the disk, your training data must be a list of paths like in the example below:
+
+.. doctest::
+
+    >>> train_data = ['./file/id1_0.jpg', './file/id1_1.jpg', './file/id2_1.jpg']
+    >>> train_labels = [0, 0, 1]
+
+With disk data shufflers, the data is loaded on the fly.
+
+
+How is the shape of your trainer?
+`````````````````````````````````
+
+Here we have one data shuffler for each type of the trainer.
+
+You will see in the section `Trainers`_ that we have three types of trainer.
+The first one is the regular trainer, which deals with one graph only.
+The data shuflers for this type of trainer must be a direct instance of either :py:class:`bob.learn.tensorflow.datashuffler.Memory`
+or :py:class:`bob.learn.tensorflow.datashuffler.Disk`.
+
+The second one is the :py:class:`bob.learn.tensorflow.trainers.Siamese` trainer, which is designed to train Siamese networks.
+The data shuflers for this type of trainer must be a direct instance of either
+
+The third one is the :py:class:`bob.learn.tensorflow.trainers.Triplet` trainer, which is designed to train Triplet networks.
+
+
+
+Architecture
+............
+
+Trainers
+........
+
+
+Layers
+......
+
+
+Initialization
+..............
+
+
+Loss
+....
+
+Analyzers
+.........
+
+
+
+
 Sandbox
 -------
 
-- 
GitLab