web-dev-qa-db-fra.com

Implémentation de pertes contrastives et de triplets dans Tensorflow

J'ai commencé à jouer avec TensorFlow il y a deux jours et je me demande s'il y a le triplet et les pertes contrastives mis en œuvre.

J'ai consulté la documentation , mais je n'ai trouvé aucun exemple ou description à ce sujet.

37

Mise à jour (2018/03/19): J'ai écrit un message de blog détaillant comment implémenter la perte de triplet dans TensorFlow.


Vous devez implémenter vous-même la perte contrastive ou la perte de triplet, mais une fois que vous connaissez les paires ou les triplets, cela est assez facile.


Perte de contraste

Supposons que vous ayez en entrée les paires de données et leur libellé (positif ou négatif, c'est-à-dire même classe ou classe différente). Par exemple, vous avez des images en entrée de taille 28x28x1:

left = tf.placeholder(tf.float32, [None, 28, 28, 1])
right = tf.placeholder(tf.float32, [None, 28, 28, 1])
label = tf.placeholder(tf.int32, [None, 1]). # 0 if same, 1 if different
margin = 0.2

left_output = model(left)  # shape [None, 128]
right_output = model(right)  # shape [None, 128]

d = tf.reduce_sum(tf.square(left_output - right_output), 1)
d_sqrt = tf.sqrt(d)

loss = label * tf.square(tf.maximum(0., margin - d_sqrt)) + (1 - label) * d

loss = 0.5 * tf.reduce_mean(loss)

Perte de triplet

Identique à la perte contrastive, mais avec les triplets (ancre, positive, négative). Vous n'avez pas besoin d'étiquettes ici.

anchor_output = ...  # shape [None, 128]
positive_output = ...  # shape [None, 128]
negative_output = ...  # shape [None, 128]

d_pos = tf.reduce_sum(tf.square(anchor_output - positive_output), 1)
d_neg = tf.reduce_sum(tf.square(anchor_output - negative_output), 1)

loss = tf.maximum(0., margin + d_pos - d_neg)
loss = tf.reduce_mean(loss)

Le véritable problème lors de la mise en œuvre de la perte de triplets ou de la perte de contraste dans TensorFlow est comment échantillonner les triplets ou les paires . Je vais me concentrer sur la génération de triplets car il est plus difficile de générer des paires.

Le moyen le plus simple est de les générer en dehors du graphe Tensorflow, c’est-à-dire python) et de les transmettre au réseau par le biais des espaces réservés. En gros, vous sélectionnez les images 3 à la fois, les deux premières même classe et la troisième d'une autre classe. Nous effectuons ensuite un feedforward sur ces triplets et calculons la perte de triplet.

Le problème ici est que la génération de triplés est compliquée. Nous voulons qu'ils soient triplets valides, triplets avec une perte positive (sinon la perte est 0 et le réseau n'apprend pas).
Pour savoir si un triplet est bon ou pas, vous devez calculer sa perte, vous devez donc déjà créer un feed-forward sur le réseau ...

Il est clair qu’implémenter la perte de triplets dans Tensorflow est difficile et il existe des moyens de la rendre plus efficace que l’échantillonnage avec python mais leur explication exigerait un post complet sur le blog!

70
Olivier Moindrot

La perte de triolet avec une extraction négative semi-dure est maintenant implémentée dans tf.contrib, comme suit:

triplet_semihard_loss(
    labels,
    embeddings,
    margin=1.0
)

où:

Args:

  • étiquettes: 1-D tf.int32 Tenseur avec la forme [taille_batch] d'étiquettes entières multiclasses.

  • embeddings: Tensor float 2D de vecteurs d'inclusion. Les mariages doivent être l2 normalisés.

  • margin: Float, terme de marge dans la définition du perte.

Résultats:

  • triplet_loss: tf.float32 scalar.

Pour plus d'informations, consultez le lien ci-dessous:

https://www.tensorflow.org/versions/master/api_docs/python/tf/contrib/losses/metric_learning/triplet_semihard_loss

11
Corey Lynch

Tiago, je ne pense pas que tu utilises la même formule qu'Olivier a donnée. Voici le bon code (pas sûr que ça marchera bien, juste en corrigeant la formule):

def compute_euclidean_distance(x, y):
    """
    Computes the euclidean distance between two tensorflow variables
    """

    d = tf.reduce_sum(tf.square(tf.sub(x, y)),1)
    return d


def compute_contrastive_loss(left_feature, right_feature, label, margin):

    """
    Compute the contrastive loss as in


    L = 0.5 * Y * D^2 + 0.5 * (Y-1) * {max(0, margin - D)}^2

    **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

    **Returns**
     Return the loss operation

    """

    label = tf.to_float(label)
    one = tf.constant(1.0)

    d = compute_euclidean_distance(left_feature, right_feature)
    d_sqrt = tf.sqrt(compute_euclidean_distance(left_feature, right_feature))
    first_part = tf.mul(one-label, d)# (Y-1)*(d)

    max_part = tf.square(tf.maximum(margin-d_sqrt, 0))
    second_part = tf.mul(label, max_part)  # (Y) * max(margin - d, 0)

    loss = 0.5 * tf.reduce_mean(first_part + second_part)

    return loss
5
Wassim Gr