Commit ca6f492d authored by Tiago de Freitas Pereira's avatar Tiago de Freitas Pereira
Browse files

Merge branch '23-tensorflow-1-0' into 'master'

Resolve "Tensorflow 1.0"

Closes #23

See merge request !5
parents f9ab311a 15d72123
Pipeline #8067 failed with stages
in 1 minute and 34 seconds
......@@ -193,57 +193,3 @@ docs_linux_35:
- build_linux_35
tags:
- conda-linux
# Mac OSX + Python 2.7: Builds and tests
build_macosx_27:
<<: *build_job
variables: &macosx_27_build_variables
PYTHON_VERSION: "2.7"
WHEEL_TAG: "py27"
tags:
- conda-macosx
test_macosx_27:
<<: *test_job
variables: *macosx_27_build_variables
dependencies:
- build_macosx_27
tags:
- conda-macosx
# Mac OSX + Python 3.4: Builds and tests
build_macosx_34:
<<: *build_job
variables: &macosx_34_build_variables
PYTHON_VERSION: "3.4"
WHEEL_TAG: "py3"
tags:
- conda-macosx
test_macosx_34:
<<: *test_job
variables: *macosx_34_build_variables
dependencies:
- build_macosx_34
tags:
- conda-macosx
# Mac OSX + Python 3.5: Builds and tests
build_macosx_35:
<<: *build_job
variables: &macosx_35_build_variables
PYTHON_VERSION: "3.5"
WHEEL_TAG: "py3"
tags:
- conda-macosx
test_macosx_35:
<<: *test_job
variables: *macosx_35_build_variables
dependencies:
- build_macosx_35
tags:
- conda-macosx
......@@ -44,8 +44,7 @@ class OnlineSampling(object):
if self.feature_placeholder is None:
shape = tuple([None] + list(data.shape[1:]))
self.feature_placeholder = tf.placeholder(tf.float32, shape=shape, name="feature")
embbeding = self.feature_extractor.compute_graph(self.feature_placeholder, self.feature_extractor.default_feature_layer,
training=False)
embbeding = self.feature_extractor.compute_graph(self.feature_placeholder, self.feature_extractor.default_feature_layer)
self.graph = tf.nn.l2_normalize(embbeding, 1, 1e-10, name='embeddings')
......@@ -54,4 +53,4 @@ class OnlineSampling(object):
#self.feature_placeholder.set_shape(data.shape)
feed_dict = {self.feature_placeholder: data}
return self.session.run([self.graph], feed_dict=feed_dict)[0]
\ No newline at end of file
return self.session.run([self.graph], feed_dict=feed_dict)[0]
......@@ -216,11 +216,11 @@ class TripletWithFastSelectionDisk(Triplet, Disk, OnlineSampling):
for i in range(self.shape[0]):
label = anchor_labels[i]
possible_candidates = [d if d > d_anchor_positive[i] else numpy.inf for d in d_anchor_negative[i]]
for j in numpy.argsort(possible_candidates):
# Checking if they don't have the same label
if indexes[j] != label:
if self.labels[indexes[j]] != label:
samples_n[i, ...] = temp_samples_n[j, ...]
if numpy.isinf(possible_candidates[j]):
logger.info("SEMI-HARD negative not found, took the first one")
......
......@@ -9,8 +9,11 @@ import tensorflow as tf
from .OnlineSampling import OnlineSampling
from .Memory import Memory
from .Triplet import Triplet
from scipy.spatial.distance import euclidean
from bob.learn.tensorflow.datashuffler.Normalizer import Linear
from scipy.spatial.distance import euclidean, cdist
import logging
logger = logging.getLogger("bob.learn")
class TripletWithSelectionMemory(Triplet, Memory, OnlineSampling):
......@@ -61,7 +64,6 @@ class TripletWithSelectionMemory(Triplet, Memory, OnlineSampling):
def __init__(self, data, labels,
input_shape,
input_dtype="float64",
scale=True,
batch_size=1,
seed=10,
data_augmentation=None,
......@@ -73,7 +75,6 @@ class TripletWithSelectionMemory(Triplet, Memory, OnlineSampling):
labels=labels,
input_shape=input_shape,
input_dtype=input_dtype,
scale=scale,
batch_size=batch_size,
seed=seed,
data_augmentation=data_augmentation,
......@@ -86,26 +87,7 @@ class TripletWithSelectionMemory(Triplet, Memory, OnlineSampling):
self.data = self.data.astype(input_dtype)
self.total_identities = total_identities
self.first_batch = True
def get_random_batch(self):
"""
Get a random triplet
**Parameters**
is_target_set_train: Defining the target set to get the batch
**Return**
"""
data_a = numpy.zeros(shape=self.shape, dtype='float32')
data_p = numpy.zeros(shape=self.shape, dtype='float32')
data_n = numpy.zeros(shape=self.shape, dtype='float32')
for i in range(self.shape[0]):
data_a[i, ...], data_p[i, ...], data_n[i, ...] = self.get_one_triplet(self.data, self.labels)
return [data_a, data_p, data_n]
self.batch_increase_factor = 4
def get_batch(self):
"""
......@@ -117,54 +99,27 @@ class TripletWithSelectionMemory(Triplet, Memory, OnlineSampling):
**Return**
"""
if self.first_batch:
self.first_batch = False
return self.get_random_batch()
# Selecting the classes used in the selection
indexes = numpy.random.choice(len(self.possible_labels), self.total_identities, replace=False)
samples_per_identity = self.batch_size/self.total_identities
anchor_labels = numpy.ones(samples_per_identity) * indexes[0]
samples_per_identity = numpy.ceil(self.batch_size/float(self.total_identities))
anchor_labels = numpy.ones(samples_per_identity) * self.possible_labels[indexes[0]]
for i in range(1, self.total_identities):
anchor_labels = numpy.hstack((anchor_labels,numpy.ones(samples_per_identity) * indexes[i]))
anchor_labels = numpy.hstack((anchor_labels,numpy.ones(samples_per_identity) * self.possible_labels[indexes[i]]))
anchor_labels = anchor_labels[0:self.batch_size]
data_a = numpy.zeros(shape=self.shape, dtype='float32')
data_p = numpy.zeros(shape=self.shape, dtype='float32')
data_n = numpy.zeros(shape=self.shape, dtype='float32')
# Fetching the anchors
for i in range(self.shape[0]):
data_a[i, ...] = self.get_anchor(anchor_labels[i])
features_a = self.project(data_a)
samples_a = numpy.zeros(shape=self.shape, dtype='float32')
# Computing the embedding
for i in range(self.shape[0]):
label = anchor_labels[i]
#anchor = self.get_anchor(label)
positive, distance_anchor_positive = self.get_positive(label, features_a[i])
negative = self.get_negative(label, features_a[i], distance_anchor_positive)
data_p[i, ...] = positive
data_n[i, ...] = negative
# Applying the data augmentation
if self.data_augmentation is not None:
for i in range(data_a.shape[0]):
d = self.bob2skimage(self.data_augmentation(self.skimage2bob(data_a[i, ...])))
data_a[i, ...] = d
d = self.bob2skimage(self.data_augmentation(self.skimage2bob(data_p[i, ...])))
data_p[i, ...] = d
d = self.bob2skimage(self.data_augmentation(self.skimage2bob(data_n[i, ...])))
data_n[i, ...] = d
samples_a[i, ...] = self.get_anchor(anchor_labels[i])
embedding_a = self.project(samples_a)
# Scaling
data_a = self.normalize_sample(data_a)
data_p = self.normalize_sample(data_p)
data_n = self.normalize_sample(data_n)
# Getting the positives
samples_p, embedding_p, d_anchor_positive = self.get_positives(anchor_labels, embedding_a)
samples_n = self.get_negative(anchor_labels, embedding_a, d_anchor_positive)
return data_a, data_p, data_n
return samples_a, samples_p, samples_n
def get_anchor(self, label):
"""
......@@ -175,61 +130,67 @@ class TripletWithSelectionMemory(Triplet, Memory, OnlineSampling):
indexes = numpy.where(self.labels == label)[0]
numpy.random.shuffle(indexes)
return self.data[indexes[0], ...]
return self.normalize_sample(self.data[indexes[0], ...])
def get_positive(self, label, anchor_feature):
def get_positives(self, anchor_labels, embedding_a):
"""
Get the best positive sample given the anchor.
The best positive sample for the anchor is the farthest from the anchor
Get the a random set of positive pairs
"""
samples_p = numpy.zeros(shape=self.shape, dtype='float32')
for i in range(self.shape[0]):
l = anchor_labels[i]
indexes = numpy.where(self.labels == l)[0]
numpy.random.shuffle(indexes)
samples_p[i, ...] = self.normalize_sample(self.data[indexes[0], ...])
# Projecting the anchor
#anchor_feature = self.feature_extractor(self.reshape_for_deploy(anchor), session=self.session)
indexes = numpy.where(self.labels == label)[0]
numpy.random.shuffle(indexes)
indexes = indexes[
0:self.batch_size] # Limiting to the batch size, otherwise the number of comparisons will explode
distances = []
positive_features = self.project(self.data[indexes, ...])
embedding_p = self.project(samples_p)
# Projecting the positive instances
for p in positive_features:
distances.append(euclidean(anchor_feature, p))
# Computing the distances
d_anchor_positive = []
for i in range(self.shape[0]):
d_anchor_positive.append(euclidean(embedding_a[i, :], embedding_p[i, :]))
# Geting the max
index = numpy.argmax(distances)
return self.data[indexes[index], ...], distances[index]
return samples_p, embedding_p, d_anchor_positive
def get_negative(self, label, anchor_feature, distance_anchor_positive):
def get_negative(self, anchor_labels, embedding_a, d_anchor_positive):
"""
Get the best negative sample for a pair anchor-positive
Get the the semi-hard negative
"""
# Projecting the anchor
#anchor_feature = self.feature_extractor(self.reshape_for_deploy(anchor), session=self.session)
# Selecting the negative samples
indexes = numpy.where(self.labels != label)[0]
# Shuffling all the dataset
indexes = range(len(self.labels))
numpy.random.shuffle(indexes)
indexes = indexes[
0:self.batch_size] # Limiting to the batch size, otherwise the number of comparisons will explode
negative_features = self.project(self.data[indexes, ...])
distances = []
for n in negative_features:
d = euclidean(anchor_feature, n)
negative_samples_search = self.batch_size * self.batch_increase_factor
# Limiting to the batch size, otherwise the number of comparisons will explode
indexes = indexes[0:negative_samples_search]
# Loading samples for the semi-hard search
shape = tuple([len(indexes)] + list(self.shape[1:]))
temp_samples_n = numpy.zeros(shape=shape, dtype='float32')
samples_n = numpy.zeros(shape=self.shape, dtype='float32')
for i in range(shape[0]):
temp_samples_n[i, ...] = self.normalize_sample(self.data[indexes[i], ...])
# Computing all the embeddings
embedding_temp_n = self.project(temp_samples_n)
# Semi-hard samples criteria
if d > distance_anchor_positive:
distances.append(d)
else:
distances.append(numpy.inf)
# Computing the distances
d_anchor_negative = cdist(embedding_a, embedding_temp_n, metric='euclidean')
# Selecting the negative samples
for i in range(self.shape[0]):
label = anchor_labels[i]
possible_candidates = [d if d > d_anchor_positive[i] else numpy.inf for d in d_anchor_negative[i]]
# Getting the minimum negative sample as the reference for the pair
index = numpy.argmin(distances)
for j in numpy.argsort(possible_candidates):
# if the semi-hardest is inf take the first
if numpy.isinf(distances[index]):
index = 0
# Checking if they don't have the same label
if self.labels[indexes[j]] != label:
samples_n[i, ...] = temp_samples_n[j, ...]
if numpy.isinf(possible_candidates[j]):
logger.info("SEMI-HARD negative not found, took the first one")
break
return self.data[indexes[index], ...]
return samples_n
......@@ -29,7 +29,7 @@ class Initialization(object):
tf.set_random_seed(seed)
def variable_exist(self, var):
return var in [v.name.split("/")[0] for v in tf.all_variables()]
return var in [v.name.split("/")[0] for v in tf.global_variables()]
def __call__(self, shape, name, scope, init_value=None):
NotImplementedError("Please implement this function in derived classes")
......@@ -64,7 +64,7 @@ class Layer(object):
NotImplementedError("Please implement this function in derived classes")
def variable_exist(self, var):
return var in [v.name.split("/")[0] for v in tf.all_variables()]
return var in [v.name.split("/")[0] for v in tf.global_variables()]
def batch_normalize(self, x, phase_train):
"""
......@@ -124,7 +124,7 @@ class Layer(object):
Doing this because of that https://github.com/tensorflow/tensorflow/issues/1325
"""
for v in tf.all_variables():
for v in tf.global_variables():
if (len(v.name.split("/")) > 1) and (var in v.name.split("/")[1]):
return v
......
......@@ -20,4 +20,4 @@ class BaseLoss(object):
self.name = name
def __call__(self, graph, label):
return self.operation(self.loss(graph, label), name=self.name)
return self.operation(self.loss(logits=graph, labels=label), name=self.name)
......@@ -44,10 +44,10 @@ class ContrastiveLoss(BaseLoss):
one = tf.constant(1.0)
d = compute_euclidean_distance(left_feature, right_feature)
between_class = tf.mul(one - label, tf.square(d)) # (1-Y)*(d^2)
between_class = tf.multiply(one - label, tf.square(d)) # (1-Y)*(d^2)
max_part = tf.square(tf.maximum(self.contrastive_margin - d, 0))
within_class = tf.mul(label, max_part) # (Y) * max((margin - d)^2, 0)
within_class = tf.multiply(label, max_part) # (Y) * max((margin - d)^2, 0)
loss = 0.5 * (within_class + between_class)
......
......@@ -25,9 +25,9 @@ class NegLogLoss(BaseLoss):
rank = len(shape)
flat_params = tf.reshape(params, [-1])
if rank > 2:
indices_unpacked = tf.unpack(tf.transpose(indices, [rank - 1] + range(0, rank - 1), name))
indices_unpacked = tf.unstack(tf.transpose(indices, [rank - 1] + range(0, rank - 1), name))
elif rank == 2:
indices_unpacked = tf.unpack(indices)
indices_unpacked = tf.unstack(indices)
else:
indices_unpacked = indices
flat_indices = [i * rank + indices_unpacked[i] for i in range(0, len(indices_unpacked))]
......@@ -38,6 +38,6 @@ class NegLogLoss(BaseLoss):
log_probabilities = tf.nn.log_softmax(graph)
# negative of the log-probability that correspond to the correct label
correct_probabilities = self.gather_nd(log_probabilities, label)
neg_log_prob = tf.neg(correct_probabilities)
neg_log_prob = tf.negative(correct_probabilities)
# use negative log likelihood as the loss
return self.operation(neg_log_prob)
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
# @date: Wed 10 Aug 2016 16:38 CEST
import logging
logger = logging.getLogger("bob.learn.tensorflow")
import tensorflow as tf
from .BaseLoss import BaseLoss
from bob.learn.tensorflow.utils import compute_euclidean_distance
class TripletAverageLoss(BaseLoss):
"""
Compute the triplet loss as in
Schroff, Florian, Dmitry Kalenichenko, and James Philbin.
"Facenet: A unified embedding for face recognition and clustering."
Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2015.
:math:`L = sum( |f_a - f_p|^2 - |f_a - f_n|^2 + \lambda)`
**Parameters**
left_feature:
First element of the pair
right_feature:
Second element of the pair
label:
Label of the pair (0 or 1)
margin:
Contrastive margin
"""
def __init__(self, margin=0.1):
self.margin = margin
def __call__(self, anchor_embedding, positive_embedding, negative_embedding):
with tf.name_scope("triplet_loss"):
# Normalize
anchor_embedding = tf.nn.l2_normalize(anchor_embedding, 1, 1e-10, name="anchor")
positive_embedding = tf.nn.l2_normalize(positive_embedding, 1, 1e-10, name="positive")
negative_embedding = tf.nn.l2_normalize(negative_embedding, 1, 1e-10, name="negative")
anchor_mean = tf.reduce_mean(anchor_embedding, 0)
d_positive = tf.reduce_sum(tf.square(tf.subtract(anchor_mean, positive_embedding)), 1)
d_negative = tf.reduce_sum(tf.square(tf.subtract(anchor_mean, negative_embedding)), 1)
basic_loss = tf.add(tf.subtract(d_positive, d_negative), self.margin)
loss = tf.reduce_mean(tf.maximum(basic_loss, 0.0), 0)
return loss, tf.reduce_mean(d_negative), tf.reduce_mean(d_positive)
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Tiago de Freitas Pereira <tiago.pereira@idiap.ch>
# @date: Wed 10 Aug 2016 16:38 CEST
import logging
logger = logging.getLogger("bob.learn.tensorflow")
import tensorflow as tf
from .BaseLoss import BaseLoss
from bob.learn.tensorflow.utils import compute_euclidean_distance
class TripletFisherLoss(BaseLoss):
"""
"""
def __init__(self):
pass
def __call__(self, anchor_embedding, positive_embedding, negative_embedding):
with tf.name_scope("triplet_loss"):
# Normalize
anchor_embedding = tf.nn.l2_normalize(anchor_embedding, 1, 1e-10, name="anchor")
positive_embedding = tf.nn.l2_normalize(positive_embedding, 1, 1e-10, name="positive")
negative_embedding = tf.nn.l2_normalize(negative_embedding, 1, 1e-10, name="negative")
average_class = tf.reduce_mean(anchor_embedding, 0)
average_total = tf.div(tf.add(tf.reduce_mean(anchor_embedding, axis=0),\
tf.reduce_mean(negative_embedding, axis=0)), 2)
length = anchor_embedding.get_shape().as_list()[0]
dim = anchor_embedding.get_shape().as_list()[1]
split_positive = tf.unstack(positive_embedding, num=length, axis=0)
split_negative = tf.unstack(negative_embedding, num=length, axis=0)
Sw = None
Sb = None
for s in zip(split_positive, split_negative):
positive = s[0]
negative = s[1]
buffer_sw = tf.reshape(tf.subtract(positive, average_class), shape=(dim, 1))
buffer_sw = tf.matmul(buffer_sw, tf.reshape(buffer_sw, shape=(1, dim)))
buffer_sb = tf.reshape(tf.subtract(negative, average_total), shape=(dim, 1))
buffer_sb = tf.matmul(buffer_sb, tf.reshape(buffer_sb, shape=(1, dim)))
if Sw is None:
Sw = buffer_sw
Sb = buffer_sb
else:
Sw = tf.add(Sw, buffer_sw)
Sb = tf.add(Sb, buffer_sb)
# Sw = tf.trace(Sw)
# Sb = tf.trace(Sb)
#loss = tf.trace(tf.div(Sb, Sw))
loss = tf.trace(tf.div(Sw, Sb))
return loss, tf.trace(Sb), tf.trace(Sw)
......@@ -44,14 +44,14 @@ class TripletLoss(BaseLoss):
with tf.name_scope("triplet_loss"):
# Normalize
anchor_embedding = tf.nn.l2_normalize(anchor_embedding, 1, 1e-10)
positive_embedding = tf.nn.l2_normalize(positive_embedding, 1, 1e-10)
negative_embedding = tf.nn.l2_normalize(negative_embedding, 1, 1e-10)
anchor_embedding = tf.nn.l2_normalize(anchor_embedding, 1, 1e-10, name="anchor")
positive_embedding = tf.nn.l2_normalize(positive_embedding, 1, 1e-10, name="positive")
negative_embedding = tf.nn.l2_normalize(negative_embedding, 1, 1e-10, name="negative")
d_positive = tf.reduce_sum(tf.square(tf.sub(anchor_embedding, positive_embedding)), 1)
d_negative = tf.reduce_sum(tf.square(tf.sub(anchor_embedding, negative_embedding)), 1)
d_positive = tf.reduce_sum(tf.square(tf.subtract(anchor_embedding, positive_embedding)), 1)
d_negative = tf.reduce_sum(tf.square(tf.subtract(anchor_embedding, negative_embedding)), 1)
basic_loss = tf.add(tf.sub(d_positive, d_negative), self.margin)
basic_loss = tf.add(tf.subtract(d_positive, d_negative), self.margin)
loss = tf.reduce_mean(tf.maximum(basic_loss, 0.0), 0)
return loss, tf.reduce_mean(d_negative), tf.reduce_mean(d_positive)
from .BaseLoss import BaseLoss
from .ContrastiveLoss import ContrastiveLoss
from .TripletLoss import TripletLoss
from .TripletAverageLoss import TripletAverageLoss
from .TripletFisherLoss import TripletFisherLoss
from .NegLogLoss import NegLogLoss
......@@ -22,6 +24,8 @@ __appropriate__(
BaseLoss,
ContrastiveLoss,
TripletLoss,
TripletFisherLoss,
TripletAverageLoss,
NegLogLoss,
)
__all__ = [_ for _ in dir() if not _.startswith('_')]
......
......@@ -160,13 +160,13 @@ class SequenceNetwork(six.with_metaclass(abc.ABCMeta, object)):
"""Attach a lot of summaries to a Tensor."""
with tf.name_scope('summaries'):
mean = tf.reduce_mean(var)
tf.scalar_summary('mean/' + name, mean)
tf.summary.scalar('mean/' + name, mean)
with tf.name_scope('stddev'):
stddev = tf.sqrt(tf.reduce_sum(tf.square(var - mean)))
tf.scalar_summary('sttdev/' + name, stddev)
tf.scalar_summary('max/' + name, tf.reduce_max(var))
tf.scalar_summary('min/' + name, tf.reduce_min(var))
tf.histogram_summary(name, var)
tf.summary.scalar('sttdev/' + name, stddev)
tf.summary.scalar('max/' + name, tf.reduce_max(var))
tf.summary.scalar('min/' + name, tf.reduce_min(var))
tf.summary.histogram(name, var)