Organized the siamese network (Still buggy) and sketched the analyser

parent a8b287cb
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
from pkgutil import extend_path from pkgutil import extend_path
__path__ = extend_path(__path__, __name__) __path__ = extend_path(__path__, __name__)
from DataShuffler import * from .util import *
# gets sphinx autodoc done right - don't remove it # gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')] __all__ = [_ for _ in dir() if not _.startswith('_')]
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
""" """
Neural net work error rates analizer Neural net work error rates analizer
""" """
import tensor
import numpy import numpy
import bob.measure import bob.measure
from scipy.spatial.distance import cosine
class Analizer: class Analizer:
""" """
...@@ -21,50 +21,63 @@ class Analizer: ...@@ -21,50 +21,63 @@ class Analizer:
""" """
def __init__(self, analizer_architecture_file, n_classes, extractor_file, snapshot, mu, def __init__(self, data_shuffler, graph, feature_placeholder, session):
single_batch=False, end_cnn="feature"):
""" """
Use the CNN as feature extractor for a n-class classification Use the CNN as feature extractor for a n-class classification
** Parameters ** ** Parameters **
temp_model: Caffe model
analizer_architecture_file: prototxt with the architecture data_shuffler:
n_classes: Number of classes graph:
single_batch: Do the forwar in a single batch? For huge architectures will consume a lot of memory
""" """
self.temp_model = None self.data_shuffler = data_shuffler
self.analizer_architecture_file = analizer_architecture_file self.graph = graph
self.n_classes = n_classes self.feature_placeholder = feature_placeholder
self.single_batch = single_batch self.session = session
# Statistics # Statistics
self.eer = [] self.eer = []
self.far10 = [] self.far10 = []
self.far100 = [] self.far100 = []
self.far1000 = [] self.far1000 = []
self.validation_loss = []
self.end_cnn = end_cnn def extract_features(self):
data, labels = self.data_shuffler.get_batch(train_dataset=False)
self.extractor_file = extractor_file feed_dict = {self.feature_placeholder: data}
self.snapshot = snapshot return self.session.run([self.graph], feed_dict=feed_dict)[0], labels
self.mu = mu
def __call__(self):
def update_model(self, model):
self.temp_model = model # Extracting features
enroll_features, enroll_labels = self.extract_features()
def save_stats(self, it): probe_features, probe_labels = self.extract_features()
# Saving statistics
hdf5 = bob.io.base.HDF5File(self.extractor_file, "w") # Creating models
hdf5.set("iterations", it) models = []
hdf5.set("snapshot", self.snapshot) for i in range(self.data_shuffler.total_labels):
#hdf5.set("validationloss", loss) indexes_model = numpy.where(enroll_labels == i)[0]
hdf5.set("eer", self.eer) models.append(numpy.mean(enroll_features[indexes_model, :], axis=0))
hdf5.set("far10", self.far10)
hdf5.set("far100", self.far100) # Probing
hdf5.set("far1000", self.far1000) positive_scores = numpy.zeros(shape=0)
hdf5.set("mu", self.mu) negative_scores = numpy.zeros(shape=0)
del hdf5 for i in range(self.data_shuffler.total_labels):
# Positive scoring
indexes = probe_labels == i
positive_data = probe_features[indexes, :]
p = [cosine(models[i], positive_data[j]) for j in range(positive_data.shape[0])]
positive_scores = numpy.hstack((positive_scores, p))
# negative scoring
indexes = probe_labels != i
negative_data = probe_features[indexes, :]
n = [cosine(models[i], negative_data[j]) for j in range(negative_data.shape[0])]
negative_scores = numpy.hstack((negative_scores, n))
self.compute_stats((-1)*negative_scores, (-1) * positive_scores)
def compute_stats(self, negative_scores, positive_scores): def compute_stats(self, negative_scores, positive_scores):
""" """
...@@ -102,62 +115,4 @@ class Analizer: ...@@ -102,62 +115,4 @@ class Analizer:
far, frr = bob.measure.farfrr(negative_scores, positive_scores, threshold) far, frr = bob.measure.farfrr(negative_scores, positive_scores, threshold)
self.far1000.append(frr) self.far1000.append(frr)
def compute_features(self, net, data): print eer
if self.single_batch:
features = net.forward(data=data)[self.end_cnn]
else:
features = numpy.zeros(shape=0, dtype='float32')
for d in data:
d = numpy.reshape(d,(1,d.shape[0],d.shape[1],d.shape[2]))
if features.shape[0] == 0:
features = net.forward(data=d, end=self.end_cnn)[self.end_cnn][0]
else:
features = numpy.vstack((features, net.forward(data=d, end=self.end_cnn)[self.end_cnn][0]))
return features
def __call__(self, data_train, labels_train, data_validation, labels_validation, it):
from scipy.spatial.distance import cosine
net = caffe.Net(self.analizer_architecture_file, self.temp_model, caffe.TEST)
labels_train = numpy.reshape(labels_train[:], (labels_train.shape[0],))
labels_validation = numpy.reshape(labels_validation[:], (labels_validation.shape[0],))
# Projecting the data to train the models
features = self.compute_features(net, data_train)
# Creating client models
models = []
for i in range(self.n_classes):
indexes = labels_train == i
models.append(numpy.mean(features[indexes, :], axis=0))
# Projecting the data for probing
del features
features = self.compute_features(net, data_validation)
# Probing
positive_scores = numpy.zeros(shape=0)
negative_scores = numpy.zeros(shape=0)
for i in range(self.n_classes):
# Positive scoring
indexes = labels_validation == i
positive_data = features[indexes, :]
p = [cosine(models[i], positive_data[j]) for j in range(positive_data.shape[0])]
positive_scores = numpy.hstack((positive_scores, p))
# negative scoring
indexes = labels_validation != i
negative_data = features[indexes, :]
n = [cosine(models[i], negative_data[j]) for j in range(negative_data.shape[0])]
negative_scores = numpy.hstack((negative_scores, n))
#Computing performance based on EER
negative_scores = (-1) * negative_scores
positive_scores = (-1) * positive_scores
self.compute_stats(negative_scores, positive_scores)
self.save_stats(it)
return self.eer[-1]
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
from .Analizer import Analizer
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
# @date: Wed 11 May 2016 09:39:36 CEST
import numpy
import tensorflow as tf
def scale_mean_norm(data, scale=0.00390625):
mean = numpy.mean(data)
data = (data - mean) * scale
return data, mean
class DataShuffler(object):
def __init__(self, data, labels, perc_train=0.9, scale=True, train_batch_size=1, validation_batch_size=300):
"""
The class provide some functionalities for shuffling data
**Parameters**
data:
"""
self.perc_train = perc_train
self.scale = True
self.scale_value = 0.00390625
self.train_batch_size = train_batch_size
self.validation_batch_size = validation_batch_size
self.data = data
self.labels = labels # From O to N-1
self.total_labels = max(labels) + 1
self.n_samples = self.data.shape[0]
self.width = self.data.shape[1]
self.height = self.data.shape[2]
self.channels = self.data.shape[3]
self.start_shuffler()
def get_placeholders(self, name="", train_dataset=True):
"""
"""
batch = self.train_batch_size if train_dataset else self.validation_batch_size
data = tf.placeholder(tf.float32, shape=(batch, self.width,
self.height, self.channels), name=name)
labels = tf.placeholder(tf.int64, shape=batch)
return data, labels
def start_shuffler(self):
"""
Some base functions for neural networks
**Parameters**
data:
"""
indexes = numpy.array(range(self.n_samples))
numpy.random.shuffle(indexes)
# Spliting train and validation
train_samples = int(round(self.n_samples * self.perc_train))
validation_samples = self.n_samples - train_samples
self.train_data = self.data[indexes[0:train_samples], :, :, :]
self.train_labels = self.labels[indexes[0:train_samples]]
self.validation_data = self.data[indexes[train_samples:train_samples + validation_samples], :, :, :]
self.validation_labels = self.labels[indexes[train_samples:train_samples + validation_samples]]
if self.scale:
# data = scale_minmax_norm(data,lower_bound = -1, upper_bound = 1)
self.train_data, self.mean = scale_mean_norm(self.train_data)
self.validation_data = (self.validation_data - self.mean) * self.scale_value
def get_batch(self, train_dataset=True):
if train_dataset:
n_samples = self.train_batch_size
else:
n_samples = self.validation_batch_size
if train_dataset:
data = self.train_data
label = self.train_labels
else:
data = self.validation_data
label = self.validation_labels
# Shuffling samples
indexes = numpy.array(range(data.shape[0]))
numpy.random.shuffle(indexes)
selected_data = data[indexes[0:n_samples], :, :, :]
selected_labels = label[indexes[0:n_samples]]
return selected_data.astype("float32"), selected_labels
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# @date: Wed 11 May 2016 09:39:36 CEST # @date: Wed 11 May 2016 09:39:36 CEST
import numpy import numpy
import tensorflow as tf from .DataShuffler import DataShuffler
def scale_mean_norm(data, scale=0.00390625): def scale_mean_norm(data, scale=0.00390625):
mean = numpy.mean(data) mean = numpy.mean(data)
...@@ -13,91 +13,22 @@ def scale_mean_norm(data, scale=0.00390625): ...@@ -13,91 +13,22 @@ def scale_mean_norm(data, scale=0.00390625):
return data, mean return data, mean
class DataShuffler(object): class PairDataShuffler(DataShuffler):
def __init__(self, data, labels, perc_train=0.9, scale=True, train_batch_size=1, validation_batch_size=300): def __init__(self, data, labels, perc_train=0.9, scale=True, train_batch_size=1, validation_batch_size=300):
""" """
Some base functions for neural networks The class provide some functionalities for shuffling data
**Parameters** **Parameters**
data: data:
""" """
self.perc_train = perc_train super(PairDataShuffler, self).__init__(data, labels,
self.scale = True perc_train=perc_train,
self.scale_value = 0.00390625 scale=scale,
self.train_batch_size = train_batch_size train_batch_size=train_batch_size*2,
self.validation_batch_size = validation_batch_size validation_batch_size=validation_batch_size)
self.data = data
self.labels =labels
self.n_samples = self.data.shape[0] def get_pair(self, is_target_set_train=True, zero_one_labels=True):
self.width = self.data.shape[1]
self.height = self.data.shape[2]
self.channels = self.data.shape[3]
self.start_shuffler()
def get_placeholders(self, name="", train_dataset=True):
batch = self.train_batch_size if train_dataset else self.validation_batch_size
data = tf.placeholder(tf.float32, shape=(batch, self.width,
self.height, self.channels), name=name)
labels = tf.placeholder(tf.int64, shape=batch)
return data, labels
def start_shuffler(self):
"""
Some base functions for neural networks
**Parameters**
data:
"""
indexes = numpy.array(range(self.n_samples))
numpy.random.shuffle(indexes)
# Spliting train and validation
train_samples = int(round(self.n_samples * self.perc_train))
validation_samples = self.n_samples - train_samples
self.train_data = self.data[indexes[0:train_samples], :, :, :]
self.train_labels = self.labels[indexes[0:train_samples]]
self.validation_data = self.data[indexes[train_samples:train_samples + validation_samples], :, :, :]
self.validation_labels = self.labels[indexes[train_samples:train_samples + validation_samples]]
self.total_labels = 10
if self.scale:
# data = scale_minmax_norm(data,lower_bound = -1, upper_bound = 1)
self.train_data, self.mean = scale_mean_norm(self.train_data)
self.validation_data = (self.validation_data - self.mean) * self.scale_value
def get_batch(self, train_dataset=True):
if train_dataset:
n_samples = self.train_batch_size
else:
n_samples = self.validation_batch_size
if train_dataset:
data = self.train_data
label = self.train_labels
else:
data = self.validation_data
label = self.validation_labels
# Shuffling samples
indexes = numpy.array(range(data.shape[0]))
numpy.random.shuffle(indexes)
selected_data = data[indexes[0:n_samples], :, :, :]
selected_labels = label[indexes[0:n_samples]]
return selected_data.astype("float32"), selected_labels
def get_pair(self, n_pair=1, is_target_set_train=True, zero_one_labels=True):
""" """
Get a random pair of samples Get a random pair of samples
...@@ -146,7 +77,7 @@ class DataShuffler(object): ...@@ -146,7 +77,7 @@ class DataShuffler(object):
target_data = self.validation_data target_data = self.validation_data
target_labels = self.validation_labels target_labels = self.validation_labels
total_data = n_pair * 2 total_data = self.train_batch_size
c = target_data.shape[3] c = target_data.shape[3]
w = target_data.shape[1] w = target_data.shape[1]
h = target_data.shape[2] h = target_data.shape[2]
......
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
from .DataShuffler import DataShuffler
from .PairDataShuffler import PairDataShuffler
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
...@@ -40,8 +40,7 @@ class FullyConnected(Layer): ...@@ -40,8 +40,7 @@ class FullyConnected(Layer):
def create_variables(self, input_layer): def create_variables(self, input_layer):
self.input_layer = input_layer self.input_layer = input_layer
if self.W is None: if self.W is None:
input_dim = reduce(mul, self.input_layer.get_shape().as_list()) input_dim = reduce(mul, self.input_layer.get_shape().as_list()[1:])
self.W = create_weight_variables([input_dim, self.output_dim], self.W = create_weight_variables([input_dim, self.output_dim],
seed=self.seed, name="W_" + str(self.name), use_gpu=self.use_gpu) seed=self.seed, name="W_" + str(self.name), use_gpu=self.use_gpu)
#if self.activation is not None: #if self.activation is not None:
......
...@@ -14,8 +14,9 @@ class BaseLoss(object): ...@@ -14,8 +14,9 @@ class BaseLoss(object):
One exam One exam
""" """
def __init__(self, loss): def __init__(self, loss, operation):
self.loss = loss self.loss = loss
self.operation = operation
def __call__(self): def __call__(self, graph, label):
return self.loss return self.operation(self.loss(graph, label))
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
import logging import logging
logger = logging.getLogger("bob.learn.tensorflow") logger = logging.getLogger("bob.learn.tensorflow")
import tensorflow as tf import tensorflow as tf
from .Baseloss import Baseloss
from .BaseLoss import BaseLoss
from bob.learn.tensorflow.util import compute_euclidean_distance
class ContrastiveLoss(BaseLoss): class ContrastiveLoss(BaseLoss):
...@@ -17,10 +19,6 @@ class ContrastiveLoss(BaseLoss): ...@@ -17,10 +19,6 @@ class ContrastiveLoss(BaseLoss):
L = 0.5 * (Y) * D^2 + 0.5 * (1-Y) * {max(0, margin - D)}^2 L = 0.5 * (Y) * D^2 + 0.5 * (1-Y) * {max(0, margin - D)}^2
OR MAYBE THAT
L = 0.5 * (1-Y) * D^2 + 0.5 * (Y) * {max(0, margin - D)}^2
**Parameters** **Parameters**
left_feature: First element of the pair left_feature: First element of the pair
right_feature: Second element of the pair right_feature: Second element of the pair
...@@ -29,21 +27,20 @@ class ContrastiveLoss(BaseLoss): ...@@ -29,21 +27,20 @@ class ContrastiveLoss(BaseLoss):
""" """
def __init__(self, label, left_feature, right_feature, margin): def __init__(self):
return
def __call__(self, label, left_feature, right_feature, margin):
with tf.name_scope("contrastive_loss"): with tf.name_scope("contrastive_loss"):
label = tf.to_float(label) label = tf.to_float(label)
one = tf.constant(1.0) one = tf.constant(1.0)
d = bob.learn.tensorflow.util.compute_euclidean_distance(left_feature, right_feature) d = compute_euclidean_distance(left_feature, right_feature)
# first_part = tf.mul(one - label, tf.square(d)) # (Y-1)*(d^2)
# first_part = tf.mul(label, tf.square(d)) # (Y-1)*(d^2)
between_class = tf.exp(tf.mul(one - label, tf.square(d))) # (1-Y)*(d^2) between_class = tf.exp(tf.mul(one - label, tf.square(d))) # (1-Y)*(d^2)
max_part = tf.square(tf.maximum(margin - d, 0)) max_part = tf.square(tf.maximum(margin - d, 0))
within_class = tf.mul(label, max_part) # (Y) * max((margin - d)^2, 0) within_class = tf.mul(label, max_part) # (Y) * max((margin - d)^2, 0)
# second_part = tf.mul(one-label, max_part) # (Y) * max((margin - d)^2, 0)
self.loss = 0.5 * tf.reduce_mean(within_class + between_class) loss = 0.5 * tf.reduce_mean(within_class + between_class)
self.within_class = tf.reduce_mean(within_class), tf.reduce_mean(between_class) return tf.reduce_mean(loss), tf.reduce_mean(between_class), tf.reduce_mean(within_class)
...@@ -3,6 +3,7 @@ from pkgutil import extend_path ...@@ -3,6 +3,7 @@ from pkgutil import extend_path
__path__ = extend_path(__path__, __name__) __path__ = extend_path(__path__, __name__)
from .BaseLoss import BaseLoss from .BaseLoss import BaseLoss
from .ContrastiveLoss import ContrastiveLoss
# gets sphinx autodoc done right - don't remove it # gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')] __all__ = [_ for _ in dir() if not _.startswith('_')]
......
...@@ -41,7 +41,7 @@ class Lenet(SequenceNetwork): ...@@ -41,7 +41,7 @@ class Lenet(SequenceNetwork):
seed = 10 seed = 10
""" """
super(Lenet, self).__init__() super(Lenet, self).__init__(feature_layer="fc2")
self.add(Conv2D(name="conv1", kernel_size=conv1_kernel_size, filters=conv1_output, activation=tf.nn.tanh)) self.add(Conv2D(name="conv1", kernel_size=conv1_kernel_size, filters=conv1_output, activation=tf.nn.tanh))
self.add(MaxPooling(name="pooling1")) self.add(MaxPooling(name="pooling1"))
......
...@@ -21,7 +21,7 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -21,7 +21,7 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
Base class to create architectures using TensorFlow Base class to create architectures using TensorFlow
""" """
def __init__(self): def __init__(self, feature_layer=None):
""" """
Base constructor Base constructor
...@@ -30,13 +30,14 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -30,13 +30,14 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
""" """
self.sequence_net = OrderedDict() self.sequence_net = OrderedDict()
self.feature_layer = feature_layer
def add(self, layer): def add(self, layer):
if not isinstance(layer, Layer): if not isinstance(layer, Layer):
raise ValueError("Input `layer` must be an instance of `bob.learn.tensorflow.layers.Layer`") raise ValueError("Input `layer` must be an instance of `bob.learn.tensorflow.layers.Layer`")
self.sequence_net[layer.name] = layer self.sequence_net[layer.name] = layer
def compute_graph(self, input_data): def compute_graph(self, input_data, cut=False):
input_offset = input_data input_offset = input_data
for k in self.sequence_net.keys(): for k in self.sequence_net.keys():
...@@ -44,4 +45,7 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)): ...@@ -44,4 +45,7 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
current_layer.create_variables(input_offset) current_layer.create_variables(input_offset)
input_offset = current_layer.get_graph() input_offset = current_layer.get_graph()
if cut and k == self.feature_layer:
return input_offset
return input_offset return input_offset
...@@ -21,9 +21,11 @@ from docopt import docopt ...@@ -21,9 +21,11 @@ from docopt import docopt
import tensorflow as tf import tensorflow as tf
from .. import util from .. import util
SEED = 10 SEED = 10
from ..DataShuffler import * from bob.learn.tensorflow.data import DataShuffler
from bob.learn.tensorflow.network import Lenet from bob.learn.tensorflow.network import Lenet
from bob.learn.tensorflow.trainers import Trainer from bob.learn.tensorflow.trainers import Trainer
from bob.learn.tensorflow.loss import BaseLoss
import numpy import numpy
def main(): def main():
...@@ -38,12 +40,12 @@ def main(): ...@@ -38,12 +40,12 @@ def main():
# Loading data # Loading data
data, labels = util.load_mnist(data_dir="./src/bob.db.mnist/bob/db/mnist/") data, labels = util.load_mnist(data_dir="./src/bob.db.mnist/bob/db/mnist/")
data = numpy.reshape(data, (data.shape[0], 28, 28, 1)) data = numpy.reshape(data, (data.shape[0], 28, 28, 1))
data_shuffler = DataShuffler(data, labels) data_shuffler = DataShuffler(data, labels, train_batch_size=BATCH_SIZE, validation_batch_size=BATCH_SIZE*100)
# Preparing the architecture # Preparing the architecture
lenet = Lenet() lenet = Lenet()
loss = tf.nn.sparse_softmax_cross_entropy_with_logits loss = BaseLoss(tf.nn.sparse_softmax_cross_entropy_with_logits, tf.reduce_mean)
trainer = Trainer(architecture=lenet, loss=loss, iterations=ITERATIONS) trainer = Trainer(architecture=lenet, loss=loss, iterations=ITERATIONS)
trainer.train(data_shuffler) trainer.train(data_shuffler)
......
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
# @date: Tue 09 Aug 2016 15:25:22 CEST