Lorsque vous essayez d'obtenir une entropie croisée avec la fonction d'activation sigmoïde, il existe une différence entre
loss1 = -tf.reduce_sum(p*tf.log(q), 1)
loss2 = tf.reduce_sum(tf.nn.sigmoid_cross_entropy_with_logits(labels=p, logits=logit_q),1)
Mais ils sont les mêmes avec la fonction d'activation de softmax.
Voici l'exemple de code:
import tensorflow as tf
sess2 = tf.InteractiveSession()
p = tf.placeholder(tf.float32, shape=[None, 5])
logit_q = tf.placeholder(tf.float32, shape=[None, 5])
q = tf.nn.sigmoid(logit_q)
sess.run(tf.global_variables_initializer())
feed_dict = {p: [[0, 0, 0, 1, 0], [1,0,0,0,0]], logit_q: [[0.2, 0.2, 0.2, 0.2, 0.2], [0.3, 0.3, 0.2, 0.1, 0.1]]}
loss1 = -tf.reduce_sum(p*tf.log(q),1).eval(feed_dict)
loss2 = tf.reduce_sum(tf.nn.sigmoid_cross_entropy_with_logits(labels=p, logits=logit_q),1).eval(feed_dict)
print(p.eval(feed_dict), "\n", q.eval(feed_dict))
print("\n",loss1, "\n", loss2)
Vous confondez l'entropie croisée pour les problèmes binary et multi-class.
La formule que vous utilisez est correcte et correspond directement à tf.nn.softmax_cross_entropy_with_logits
:
_-tf.reduce_sum(p * tf.log(q), axis=1)
_
p
et q
devraient être des distributions de probabilité sur N classes. En particulier, N peut être 2, comme dans l'exemple suivant:
_p = tf.placeholder(tf.float32, shape=[None, 2])
logit_q = tf.placeholder(tf.float32, shape=[None, 2])
q = tf.nn.softmax(logit_q)
feed_dict = {
p: [[0, 1],
[1, 0],
[1, 0]],
logit_q: [[0.2, 0.8],
[0.7, 0.3],
[0.5, 0.5]]
}
prob1 = -tf.reduce_sum(p * tf.log(q), axis=1)
prob2 = tf.nn.softmax_cross_entropy_with_logits(labels=p, logits=logit_q)
print(prob1.eval(feed_dict)) # [ 0.43748799 0.51301527 0.69314718]
print(prob2.eval(feed_dict)) # [ 0.43748799 0.51301527 0.69314718]
_
Notez que q
est en train de calculer tf.nn.softmax
, c’est-à-dire une distribution de probabilité. Donc, c'est toujours la formule multi-classe à entropie croisée, seulement pour N = 2.
Cette fois la bonne formule est
_p * -tf.log(q) + (1 - p) * -tf.log(1 - q)
_
Bien que mathématiquement il s’agisse d’un cas partiel du cas multi-classes, le signification de p
et q
est différent. Dans le cas le plus simple, chaque p
et q
est un nombre, correspondant à une probabilité de la classe A.
Important : Ne soyez pas dérouté par la partie commune p * -tf.log(q)
et la somme. Previous p
était un vecteur one-hot, maintenant c'est un nombre, zéro ou un. Même chose pour q
- c'était une distribution de probabilité, maintenant c'est un nombre (probabilité).
Si p
est un vecteur, chaque composant individuel est considéré comme un classification binaire indépendante. Voir cette réponse qui décrit la différence entre les fonctions softmax et sigmoïde dans tensorflow. Donc, la définition _p = [0, 0, 0, 1, 0]
_ ne signifie pas un vecteur one-hot, mais 5 caractéristiques différentes, dont 4 sont désactivées et 1 est activée. La définition _q = [0.2, 0.2, 0.2, 0.2, 0.2]
_ signifie que chacune des 5 fonctions est activée avec une probabilité de 20%.
Ceci explique l'utilisation de la fonction sigmoid
avant l'entropie croisée: son objectif est de réduire le logit à un intervalle _[0, 1]
_.
La formule ci-dessus reste valable pour plusieurs fonctionnalités indépendantes, et c'est exactement ce que tf.nn.sigmoid_cross_entropy_with_logits
calcule:
_p = tf.placeholder(tf.float32, shape=[None, 5])
logit_q = tf.placeholder(tf.float32, shape=[None, 5])
q = tf.nn.sigmoid(logit_q)
feed_dict = {
p: [[0, 0, 0, 1, 0],
[1, 0, 0, 0, 0]],
logit_q: [[0.2, 0.2, 0.2, 0.2, 0.2],
[0.3, 0.3, 0.2, 0.1, 0.1]]
}
prob1 = -p * tf.log(q)
prob2 = p * -tf.log(q) + (1 - p) * -tf.log(1 - q)
prob3 = p * -tf.log(tf.sigmoid(logit_q)) + (1-p) * -tf.log(1-tf.sigmoid(logit_q))
prob4 = tf.nn.sigmoid_cross_entropy_with_logits(labels=p, logits=logit_q)
print(prob1.eval(feed_dict))
print(prob2.eval(feed_dict))
print(prob3.eval(feed_dict))
print(prob4.eval(feed_dict))
_
Vous devriez voir que les trois derniers tenseurs sont égaux, alors que le _prob1
_ est seulement une partie de l'entropie croisée, il ne contient donc la valeur correcte que lorsque p
est _1
_:
_[[ 0. 0. 0. 0.59813893 0. ]
[ 0.55435514 0. 0. 0. 0. ]]
[[ 0.79813886 0.79813886 0.79813886 0.59813887 0.79813886]
[ 0.5543552 0.85435522 0.79813886 0.74439669 0.74439669]]
[[ 0.7981388 0.7981388 0.7981388 0.59813893 0.7981388 ]
[ 0.55435514 0.85435534 0.7981388 0.74439663 0.74439663]]
[[ 0.7981388 0.7981388 0.7981388 0.59813893 0.7981388 ]
[ 0.55435514 0.85435534 0.7981388 0.74439663 0.74439663]]
_
Maintenant, il devrait être clair que prendre une somme de -p * tf.log(q)
le long de _axis=1
_ n'a pas de sens dans ce paramètre, bien que ce soit une formule valide dans le cas multi-classe.