Qu'est-ce que logits, softmax et softmax_cross_entropy_with_logits?

351

Je parcourais les documents de l'API tensorflow ici . Dans la documentation tensorflow, ils ont utilisé un mot-clé appelé logits. Qu'Est-ce que c'est? Dans de nombreuses méthodes des documents API, il est écrit comme

tf.nn.softmax(logits, name=None)

Si ce qui est écrit est que ce logitssont seulement Tensors, pourquoi garder un nom différent comme logits?

Une autre chose est qu'il y a deux méthodes que je ne pouvais pas différencier. Ils étaient

tf.nn.softmax(logits, name=None)
tf.nn.softmax_cross_entropy_with_logits(logits, labels, name=None)

Quelles sont les différences entre eux? Les documents ne sont pas clairs pour moi. Je sais ce que tf.nn.softmaxça fait. Mais pas l'autre. Un exemple sera vraiment utile.

Shubhashis
la source

Réponses:

427

Logits signifie simplement que la fonction fonctionne sur la sortie non mise à l'échelle des couches précédentes et que l'échelle relative pour comprendre les unités est linéaire. Cela signifie, en particulier, que la somme des entrées peut ne pas être égale à 1, que les valeurs ne sont pas des probabilités (vous pouvez avoir une entrée de 5).

tf.nn.softmaxproduit juste le résultat de l'application de la fonction softmax à un tenseur d'entrée. Le softmax "écrase" les entrées pour que sum(input) = 1: c'est une façon de normaliser. La forme de sortie d'un softmax est la même que l'entrée: elle normalise juste les valeurs. Les sorties de softmax peuvent être interprétées comme des probabilités.

a = tf.constant(np.array([[.1, .3, .5, .9]]))
print s.run(tf.nn.softmax(a))
[[ 0.16838508  0.205666    0.25120102  0.37474789]]

En revanche, tf.nn.softmax_cross_entropy_with_logitscalcule l'entropie croisée du résultat après avoir appliqué la fonction softmax (mais il le fait tous ensemble d'une manière mathématiquement plus prudente). Il est similaire au résultat de:

sm = tf.nn.softmax(x)
ce = cross_entropy(sm)

L'entropie croisée est une métrique récapitulative: elle résume les éléments. La sortie de tf.nn.softmax_cross_entropy_with_logitssur un [2,5]tenseur de forme est de forme [2,1](la première dimension est traitée comme le lot).

Si vous souhaitez faire une optimisation pour minimiser l'entropie croisée ET que vous effectuez un softmaxing après votre dernière couche, vous devez utiliser tf.nn.softmax_cross_entropy_with_logitsau lieu de le faire vous-même, car il couvre les cas d'angle numériquement instables de la bonne manière mathématique. Sinon, vous finirez par le pirater en ajoutant de petits epsilons ici et là.

Modifié le 07/02/2016: Si vous avez des étiquettes à classe unique, où un objet ne peut appartenir qu'à une seule classe, vous pouvez désormais envisager d'utiliser tf.nn.sparse_softmax_cross_entropy_with_logitsafin de ne pas avoir à convertir vos étiquettes en un tableau dense à une seule zone. Cette fonction a été ajoutée après la version 0.6.0.

dga
la source
1
À propos de softmax_cross_entropy_with_logits, je ne sais pas si je l'utilise correctement. Le résultat n'est pas si stable dans mon code. Le même code s'exécute deux fois, la précision totale passe de 0,6 à 0,8. cross_entropy = tf.nn.softmax_cross_entropy_with_logits(tf.nn.softmax(tf.add(tf.matmul(x,W),b)),y) cost=tf.reduce_mean(cross_entropy). Mais quand j'utilise une autre façon, pred=tf.nn.softmax(tf.add(tf.matmul(x,W),b)) cost =tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred),reduction_indices=1))le résultat est stable et meilleur.
Rida
15
Vous êtes double softmaxing dans votre première ligne. softmax_cross_entropy_with_logits attend des logits non mis à l'échelle, pas la sortie de tf.nn.softmax. Vous voulez juste tf.nn.softmax_cross_entropy_with_logits(tf.add(tf.matmul(x, W, b))dans votre cas.
dga
7
@dga Je pense que vous avez une faute de frappe dans votre code, les bbesoins doivent être en dehors du support,tf.nn.softmax_cross_entropy_with_logits(tf.add(tf.matmul(x, W), b)
jrieke
1
qu'est-ce que "que l'échelle relative pour comprendre les unités est linéaire." une partie de votre première phrase signifie?
Charlie Parker
5
Voté - mais votre réponse est légèrement incorrecte lorsque vous dites que "[l] a forme de sortie d'un softmax est la même que l'entrée - elle normalise simplement les valeurs". Softmax ne fait pas que "écraser" les valeurs pour que leur somme soit égale à 1. Il les redistribue également, et c'est probablement la principale raison pour laquelle il est utilisé. Voir stackoverflow.com/questions/17187507/… , en particulier la réponse de Piotr Czapla.
Paolo Perrotta
282

Version courte:

Supposons que vous ayez deux tenseurs, où y_hatcontient les scores calculés pour chaque classe (par exemple, à partir de y = W * x + b) et y_truecontient les véritables étiquettes codées à chaud.

y_hat  = ... # Predicted label, e.g. y = tf.matmul(X, W) + b
y_true = ... # True label, one-hot encoded

Si vous interprétez les scores y_hatcomme des probabilités logarithmiques non normalisées, ce sont des logits .

De plus, la perte totale d'entropie croisée calculée de cette manière:

y_hat_softmax = tf.nn.softmax(y_hat)
total_loss = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), [1]))

est essentiellement équivalent à la perte d'entropie croisée totale calculée avec la fonction softmax_cross_entropy_with_logits():

total_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))

Version longue:

Dans la couche de sortie de votre réseau de neurones, vous calculerez probablement un tableau qui contient les scores de classe pour chacune de vos instances d'entraînement, par exemple à partir d'un calcul y_hat = W*x + b. Pour servir d'exemple, ci-dessous, j'ai créé un y_hattableau 2 x 3, où les lignes correspondent aux instances de formation et les colonnes correspondent aux classes. Il y a donc ici 2 instances de formation et 3 classes.

import tensorflow as tf
import numpy as np

sess = tf.Session()

# Create example y_hat.
y_hat = tf.convert_to_tensor(np.array([[0.5, 1.5, 0.1],[2.2, 1.3, 1.7]]))
sess.run(y_hat)
# array([[ 0.5,  1.5,  0.1],
#        [ 2.2,  1.3,  1.7]])

Notez que les valeurs ne sont pas normalisées (c'est-à-dire que les lignes ne totalisent pas 1). Afin de les normaliser, nous pouvons appliquer la fonction softmax, qui interprète l'entrée comme des probabilités de log non normalisées (alias logits ) et génère des probabilités linéaires normalisées.

y_hat_softmax = tf.nn.softmax(y_hat)
sess.run(y_hat_softmax)
# array([[ 0.227863  ,  0.61939586,  0.15274114],
#        [ 0.49674623,  0.20196195,  0.30129182]])

Il est important de bien comprendre ce que la sortie softmax dit. Ci-dessous, j'ai montré un tableau qui représente plus clairement la sortie ci-dessus. On peut voir que, par exemple, la probabilité que l'instance de formation 1 soit de "classe 2" est de 0,619. Les probabilités de classe pour chaque instance de formation sont normalisées, la somme de chaque ligne est donc de 1,0.

                      Pr(Class 1)  Pr(Class 2)  Pr(Class 3)
                    ,--------------------------------------
Training instance 1 | 0.227863   | 0.61939586 | 0.15274114
Training instance 2 | 0.49674623 | 0.20196195 | 0.30129182

Nous avons donc maintenant des probabilités de classe pour chaque instance de formation, où nous pouvons prendre l'argmax () de chaque ligne pour générer une classification finale. De ci-dessus, nous pouvons générer que l'instance de formation 1 appartient à la "classe 2" et l'instance de formation 2 appartient à la "classe 1".

Ces classifications sont-elles correctes? Nous devons mesurer par rapport aux véritables étiquettes de l'ensemble de formation. Vous aurez besoin d'un y_truetableau codé à chaud , où encore les lignes sont des instances d'apprentissage et les colonnes sont des classes. Ci-dessous, j'ai créé un exemple d' y_trueun tableau à chaud où la véritable étiquette pour l'instance de formation 1 est "Classe 2" et la véritable étiquette pour l'instance de formation 2 est "Classe 3".

y_true = tf.convert_to_tensor(np.array([[0.0, 1.0, 0.0],[0.0, 0.0, 1.0]]))
sess.run(y_true)
# array([[ 0.,  1.,  0.],
#        [ 0.,  0.,  1.]])

La distribution de probabilité est-elle y_hat_softmaxproche de la distribution de probabilité de y_true? Nous pouvons utiliser la perte d'entropie croisée pour mesurer l'erreur.

Formule pour la perte d'entropie croisée

Nous pouvons calculer la perte d'entropie croisée par ligne et voir les résultats. Ci-dessous, nous pouvons voir que l'instance de formation 1 a une perte de 0,479, tandis que l'instance de formation 2 a une perte plus élevée de 1,200. Ce résultat est logique parce que dans notre exemple ci-dessus, a y_hat_softmaxmontré que la probabilité la plus élevée de l'instance de formation 1 était pour "Classe 2", qui correspond à l'instance de formation 1 dans y_true; cependant, la prévision pour l'instance de formation 2 a montré une probabilité la plus élevée pour "Classe 1", qui ne correspond pas à la vraie classe "Classe 3".

loss_per_instance_1 = -tf.reduce_sum(y_true * tf.log(y_hat_softmax), reduction_indices=[1])
sess.run(loss_per_instance_1)
# array([ 0.4790107 ,  1.19967598])

Ce que nous voulons vraiment, c'est la perte totale sur toutes les instances de formation. Nous pouvons donc calculer:

total_loss_1 = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), reduction_indices=[1]))
sess.run(total_loss_1)
# 0.83934333897877944

Utilisation de softmax_cross_entropy_with_logits ()

Nous pouvons plutôt calculer la perte d'entropie croisée totale en utilisant la tf.nn.softmax_cross_entropy_with_logits()fonction, comme indiqué ci-dessous.

loss_per_instance_2 = tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true)
sess.run(loss_per_instance_2)
# array([ 0.4790107 ,  1.19967598])

total_loss_2 = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))
sess.run(total_loss_2)
# 0.83934333897877922

Notez cela total_loss_1et total_loss_2produisez des résultats essentiellement équivalents avec quelques petites différences dans les tout derniers chiffres. Cependant, vous pourriez aussi bien utiliser la deuxième approche: elle prend une ligne de code en moins et accumule moins d'erreur numérique car le softmax est fait pour vous à l'intérieur de softmax_cross_entropy_with_logits().

stackoverflowuser2010
la source
Je confirme tout ce qui précède. Le code simple: M = tf.random.uniform([100, 10], minval=-1.0, maxval=1.0); labels = tf.one_hot(tf.random.uniform([100], minval=0, maxval=10 , dtype='int32'), 10); tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=M) - tf.reduce_sum(-tf.nn.log_softmax(M)*tf.one_hot(labels, 10), -1)retourne presque nul partout
Sami A. Haija
51

tf.nn.softmaxcalcule la propagation directe à travers une couche softmax. Vous l'utilisez lors de l' évaluation du modèle lorsque vous calculez les probabilités de sortie du modèle.

tf.nn.softmax_cross_entropy_with_logitscalcule le coût d'une couche softmax. Il n'est utilisé que pendant l' entraînement .

Les logits sont les probabilités logarithmiques non normalisées générées par le modèle (les valeurs générées avant que la normalisation softmax ne leur soit appliquée).

Ian Goodfellow
la source
2
J'ai compris. Pourquoi ne pas appeler la fonction, tf.nn.softmax_cross_entropy_sans_normalization?
auro
8
@auro car il normalise les valeurs (en interne) lors du calcul d'entropie croisée. Il tf.nn.softmax_cross_entropy_with_logitss'agit d'évaluer dans quelle mesure le modèle s'écarte des étiquettes en or, et non de fournir une sortie normalisée.
erickrf
1
Dans le cas de l'utilisation de tf.nn.sparse_softmax_cross_entropy_with_logits () calcule le coût d'une couche softmax clairsemée, et ne doit donc être utilisé pendant la formation que ce qui serait l'alternative lors de l'exécution du modèle avec de nouvelles données, est-il possible d'obtenir des probabilités à partir de cela une.
SerialDev
2
@SerialDev, il n'est pas possible d'obtenir des probabilités à partir de tf.nn.sparse_softmax_cross_entropy_with_logits. Pour obtenir des probabilités, utilisez tf.nn.softmax.
Nandeesh
4

Les réponses ci-dessus ont suffisamment de description pour la question posée.

De plus, Tensorflow a optimisé le fonctionnement de l'application de la fonction d'activation, puis le calcul des coûts à l'aide de sa propre activation suivie des fonctions de coût. Par conséquent, c'est une bonne pratique à utiliser: tf.nn.softmax_cross_entropy()surtf.nn.softmax(); tf.nn.cross_entropy()

Vous pouvez trouver une différence importante entre eux dans un modèle gourmand en ressources.

Abish
la source
1
la réponse ci-dessus n'a clairement pas lu la question .. Ils disent tous les mêmes choses, qui sont connues, mais ne répondent pas à la question elle
Euler_Salter
@abhish Voulez-vous dire, tf.nn.softmaxsuivi de tf.losses.softmax_cross_entropy?
ankurrc
4

Ce qui va toujours au softmaxlogit, c'est ce que J. Hinton répète tout le temps dans les vidéos coursera.

prosti
la source
1

Réponse compatible Tensorflow 2.0 : Les explications dgaet stackoverflowuser2010les détails de Logits et des fonctions associées sont très détaillés.

Toutes ces fonctions, lorsqu'elles sont utilisées dans Tensorflow 1.x, fonctionneront correctement, mais si vous migrez votre code de 1.x (1.14, 1.15, etc)vers 2.x (2.0, 2.1, etc..), l'utilisation de ces fonctions entraînera une erreur.

Par conséquent, en spécifiant les appels compatibles 2.0 pour toutes les fonctions, nous avons discuté ci-dessus, si nous migrons de 1.x to 2.x, au profit de la communauté.

Fonctions en 1.x :

  1. tf.nn.softmax
  2. tf.nn.softmax_cross_entropy_with_logits
  3. tf.nn.sparse_softmax_cross_entropy_with_logits

Fonctions respectives lors de la migration de 1.x vers 2.x :

  1. tf.compat.v2.nn.softmax
  2. tf.compat.v2.nn.softmax_cross_entropy_with_logits
  3. tf.compat.v2.nn.sparse_softmax_cross_entropy_with_logits

Pour plus d'informations sur la migration de 1.x vers 2.x, veuillez consulter ce Guide de migration .

Prise en charge de Tensorflow
la source
0

Une dernière chose que je voudrais certainement souligner car logit est juste une sortie brute, généralement la sortie de la dernière couche. Cela peut également être une valeur négative. Si nous l'utilisons tel qu'il est pour l'évaluation de «l'entropie croisée» comme mentionné ci-dessous:

-tf.reduce_sum(y_true * tf.log(logits))

alors ça ne marchera pas. Le journal de -ve n'est pas défini. Ainsi, l'utilisation de l'activation o softmax résoudra ce problème.

C'est ma compréhension, veuillez me corriger si je me trompe.

vipin bansal
la source