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

parent a8b287cb
......@@ -2,7 +2,7 @@
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
from DataShuffler import *
from .util import *
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
......@@ -6,9 +6,9 @@
"""
Neural net work error rates analizer
"""
import tensor
import numpy
import bob.measure
from scipy.spatial.distance import cosine
class Analizer:
"""
......@@ -21,50 +21,63 @@ class Analizer:
"""
def __init__(self, analizer_architecture_file, n_classes, extractor_file, snapshot, mu,
single_batch=False, end_cnn="feature"):
def __init__(self, data_shuffler, graph, feature_placeholder, session):
"""
Use the CNN as feature extractor for a n-class classification
** Parameters **
temp_model: Caffe model
analizer_architecture_file: prototxt with the architecture
n_classes: Number of classes
single_batch: Do the forwar in a single batch? For huge architectures will consume a lot of memory
data_shuffler:
graph:
"""
self.temp_model = None
self.analizer_architecture_file = analizer_architecture_file
self.n_classes = n_classes
self.single_batch = single_batch
self.data_shuffler = data_shuffler
self.graph = graph
self.feature_placeholder = feature_placeholder
self.session = session
# Statistics
self.eer = []
self.far10 = []
self.far100 = []
self.far1000 = []
self.validation_loss = []
self.end_cnn = end_cnn
self.extractor_file = extractor_file
self.snapshot = snapshot
self.mu = mu
def update_model(self, model):
self.temp_model = model
def save_stats(self, it):
# Saving statistics
hdf5 = bob.io.base.HDF5File(self.extractor_file, "w")
hdf5.set("iterations", it)
hdf5.set("snapshot", self.snapshot)
#hdf5.set("validationloss", loss)
hdf5.set("eer", self.eer)
hdf5.set("far10", self.far10)
hdf5.set("far100", self.far100)
hdf5.set("far1000", self.far1000)
hdf5.set("mu", self.mu)
del hdf5
def extract_features(self):
data, labels = self.data_shuffler.get_batch(train_dataset=False)
feed_dict = {self.feature_placeholder: data}
return self.session.run([self.graph], feed_dict=feed_dict)[0], labels
def __call__(self):
# Extracting features
enroll_features, enroll_labels = self.extract_features()
probe_features, probe_labels = self.extract_features()
# Creating models
models = []
for i in range(self.data_shuffler.total_labels):
indexes_model = numpy.where(enroll_labels == i)[0]
models.append(numpy.mean(enroll_features[indexes_model, :], axis=0))
# Probing
positive_scores = numpy.zeros(shape=0)
negative_scores = numpy.zeros(shape=0)
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):
"""
......@@ -102,62 +115,4 @@ class Analizer:
far, frr = bob.measure.farfrr(negative_scores, positive_scores, threshold)
self.far1000.append(frr)
def compute_features(self, net, data):
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]
print eer
# 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 @@
# @date: Wed 11 May 2016 09:39:36 CEST
import numpy
import tensorflow as tf
from .DataShuffler import DataShuffler
def scale_mean_norm(data, scale=0.00390625):
mean = numpy.mean(data)
......@@ -13,91 +13,22 @@ def scale_mean_norm(data, scale=0.00390625):
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):
"""
Some base functions for neural networks
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
super(PairDataShuffler, self).__init__(data, labels,
perc_train=perc_train,
scale=scale,
train_batch_size=train_batch_size*2,
validation_batch_size=validation_batch_size)
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]]
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):
def get_pair(self, is_target_set_train=True, zero_one_labels=True):
"""
Get a random pair of samples
......@@ -146,7 +77,7 @@ class DataShuffler(object):
target_data = self.validation_data
target_labels = self.validation_labels
total_data = n_pair * 2
total_data = self.train_batch_size
c = target_data.shape[3]
w = target_data.shape[1]
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):
def create_variables(self, input_layer):
self.input_layer = input_layer
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],
seed=self.seed, name="W_" + str(self.name), use_gpu=self.use_gpu)
#if self.activation is not None:
......
......@@ -14,8 +14,9 @@ class BaseLoss(object):
One exam
"""
def __init__(self, loss):
def __init__(self, loss, operation):
self.loss = loss
self.operation = operation
def __call__(self):
return self.loss
def __call__(self, graph, label):
return self.operation(self.loss(graph, label))
......@@ -6,7 +6,9 @@
import logging
logger = logging.getLogger("bob.learn.tensorflow")
import tensorflow as tf
from .Baseloss import Baseloss
from .BaseLoss import BaseLoss
from bob.learn.tensorflow.util import compute_euclidean_distance
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
OR MAYBE THAT
L = 0.5 * (1-Y) * D^2 + 0.5 * (Y) * {max(0, margin - D)}^2
**Parameters**
left_feature: First element of the pair
right_feature: Second element of the pair
......@@ -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"):
label = tf.to_float(label)
one = tf.constant(1.0)
d = bob.learn.tensorflow.util.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)
d = compute_euclidean_distance(left_feature, right_feature)
between_class = tf.exp(tf.mul(one - label, tf.square(d))) # (1-Y)*(d^2)
max_part = tf.square(tf.maximum(margin - d, 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
__path__ = extend_path(__path__, __name__)
from .BaseLoss import BaseLoss
from .ContrastiveLoss import ContrastiveLoss
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
......
......@@ -41,7 +41,7 @@ class Lenet(SequenceNetwork):
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(MaxPooling(name="pooling1"))
......
......@@ -21,7 +21,7 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
Base class to create architectures using TensorFlow
"""
def __init__(self):
def __init__(self, feature_layer=None):
"""
Base constructor
......@@ -30,13 +30,14 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
"""
self.sequence_net = OrderedDict()
self.feature_layer = feature_layer
def add(self, layer):
if not isinstance(layer, Layer):
raise ValueError("Input `layer` must be an instance of `bob.learn.tensorflow.layers.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
for k in self.sequence_net.keys():
......@@ -44,4 +45,7 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
current_layer.create_variables(input_offset)
input_offset = current_layer.get_graph()
if cut and k == self.feature_layer:
return input_offset
return input_offset
......@@ -21,9 +21,11 @@ from docopt import docopt
import tensorflow as tf
from .. import util
SEED = 10
from ..DataShuffler import *
from bob.learn.tensorflow.data import DataShuffler
from bob.learn.tensorflow.network import Lenet
from bob.learn.tensorflow.trainers import Trainer
from bob.learn.tensorflow.loss import BaseLoss
import numpy
def main():
......@@ -38,12 +40,12 @@ def main():
# Loading data
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_shuffler = DataShuffler(data, labels)
data_shuffler = DataShuffler(data, labels, train_batch_size=BATCH_SIZE, validation_batch_size=BATCH_SIZE*100)
# Preparing the architecture
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.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
import logging
logger = logging.getLogger("bob.learn.tensorflow")
import tensorflow as tf
from ..analyzers import Analizer
from ..network import SequenceNetwork
class SiameseTrainer(object):
def __init__(self,
architecture=None,
use_gpu=False,
loss=None,
###### training options ##########
convergence_threshold = 0.01,
iterations=5000,
base_lr=0.001,
momentum=0.9,
weight_decay=0.95,
# The learning rate policy
snapshot=100):
self.loss = loss
self.loss_instance = None
self.optimizer = None
self.architecture = architecture
self.use_gpu = use_gpu
self.iterations = iterations
self.snapshot = snapshot
self.base_lr = base_lr
self.momentum = momentum
self.weight_decay = weight_decay
self.convergence_threshold = convergence_threshold
def train(self, data_shuffler):
"""
Do the loop forward --> backward --|
^--------------------|
"""
train_placeholder_left_data, train_placeholder_labels = data_shuffler.get_placeholders(name="train_left")
train_placeholder_right_data, _ = data_shuffler.get_placeholders(name="train_right")
feature_placeholder, _ = data_shuffler.get_placeholders(name="feature", train_dataset=False)
#validation_placeholder_data, validation_placeholder_labels = data_shuffler.get_placeholders(name="validation",
# train_dataset=False)
# 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`")
train_left_graph = self.architecture.compute_graph(train_placeholder_left_data)
train_right_graph = self.architecture.compute_graph(train_placeholder_right_data)
feature_graph = self.architecture.compute_graph(feature_placeholder, cut=True)
loss_train, between_class, within_class = self.loss(train_placeholder_labels,
train_left_graph,
train_right_graph,
0.2)
batch = tf.Variable(0)
learning_rate = tf.train.exponential_decay(
self.base_lr, # Learning rate
batch * data_shuffler.train_batch_size,
data_shuffler.train_data.shape[0],
self.weight_decay # Decay step
)
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_train,
global_step=batch)
#train_prediction = tf.nn.softmax(train_graph)
#validation_prediction = tf.nn.softmax(validation_graph)
print("Initializing !!")
# Training
with tf.Session() as session:
analizer = Analizer(data_shuffler, feature_graph, feature_placeholder, session)
train_writer = tf.train.SummaryWriter('./LOGS/train',
session.graph)
# Tensorboard data
tf.scalar_summary('loss', loss_train)
tf.scalar_summary('between_class', between_class)
tf.scalar_summary('within_class', within_class)
tf.scalar_summary('lr', learning_rate)
merged = tf.merge_all_summaries()
tf.initialize_all_variables().run()
for step in range(self.iterations):
batch_left, batch_right, labels = data_shuffler.get_pair()
feed_dict = {train_placeholder_left_data: batch_left,
train_placeholder_right_data: batch_right,
train_placeholder_labels: labels}