2

J'ai écrit le programme de classification binaire suivant dans tensorflow qui est buggé. Le coût revient à zéro tout le temps, peu importe ce que l'entrée est. J'essaye de déboguer un plus grand programme qui n'apprend rien des données. J'ai réduit au moins un bug à la fonction de coût retournant toujours zéro. Le programme donné utilise des entrées aléatoires et rencontre le même problème. self.X_train et self.y_train est à l'origine censé lire des fichiers et la fonction self.predict() a plus de couches formant un réseau neuronal feedforward.Fonction de coût retournant toujours zéro pour une classification binaire en tensorflow

import numpy as np 
import tensorflow as tf 

class annClassifier(): 

    def __init__(self): 

     with tf.variable_scope("Input"): 
      self.X = tf.placeholder(tf.float32, shape=(100, 11)) 

     with tf.variable_scope("Output"): 
      self.y = tf.placeholder(tf.float32, shape=(100, 1)) 

     self.X_train = np.random.rand(100, 11) 
     self.y_train = np.random.randint(0,2, size=(100, 1)) 

    def predict(self): 

     with tf.variable_scope('OutputLayer'): 
      weights = tf.get_variable(name='weights', 
             shape=[11, 1], 
             initializer=tf.contrib.layers.xavier_initializer()) 
      bases = tf.get_variable(name='bases', 
            shape=[1], 
            initializer=tf.zeros_initializer()) 
      final_output = tf.matmul(self.X, weights) + bases 

     return final_output 

    def train(self): 

     prediction = self.predict() 
     cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=self.y)) 

     with tf.Session() as sess: 
      sess.run(tf.global_variables_initializer())   
      print(sess.run(cost, feed_dict={self.X:self.X_train, self.y:self.y_train})) 


with tf.Graph().as_default(): 
    classifier = annClassifier() 
    classifier.train() 

Si quelqu'un pourrait s'il vous plaît comprendre ce que je fais mal dans ce domaine, je peux essayer de faire le même changement dans mon programme original. Merci beaucoup!

Répondre

5

Le seul problème est le coût invalide utilisé. softmax_cross_entropy_with_logits doit être utilisé si vous avez plus que deux classes, comme softmax d'une seule sortie retourne toujours 1, comme il est défini comme:

softmax(x)_i = exp(x_i)/SUM_j exp(x_j) 

donc pour un seul numéro (une sortie dimensionnelle)

softmax(x) = exp(x)/exp(x) = 1 

En outre, pour la sortie softmax TF attend encodées un chaud étiquettes, donc si vous fournissez seulement 0 ou 1, il y a deux possibilités:

  1. étiquette Vrai est 0, donc le coût est -0*log(1) = 0
  2. étiquette vrai est 1, donc le coût est -1*log(1) = 0

tensorflow a une fonction distincte pour gérer la classification binaire qui applique sigmoïde au lieu (note que la même fonction pour plus d'une sortie s'appliquerait sigmoïde indépendamment sur chaque dimension qui est ce que la classification multi-étiquette s'attendrait):

tf.sigmoid_cross_entropy_with_logits 

juste passer à ce coût et vous êtes bon pour aller, vous n'avez rien à coder comme one-hot plus non plus, car cette fonction est designe d uniquement à utiliser pour votre cas d'utilisation.

Le seul bit manquant est que .... votre code n'a pas de routine d'entraînement Vous devez définir l'optimiseur, lui demander de minimiser une perte, puis exécuter une opération de train dans la boucle. Dans votre configuration actuelle, vous essayez de prédire encore et encore, avec le réseau qui ne change jamais.

En particulier, s'il vous plaît se référer à Cross Entropy Jungle question on SO qui fournit une description plus détaillée de toutes ces différentes fonctions d'assistance dans TF (et d'autres bibliothèques), qui ont des exigences différentes/cas d'utilisation.

+0

La perte sigmoïde mesure l'erreur de probabilité dans les tâches de classification discrètes dans lesquelles chaque classe est indépendante et non mutuellement exclusive. Cette perte ne sera pas efficace pour un problème de classification binaire. –

+0

@Ishant, vous semblez être des termes confus - pour une ** classification binaire ** il n'y a que deux options, soit vous êtes un membre de classe 1 ou 2, c'est pourquoi vous pouvez modéliser ** une ** probabilité avec sigmoïde . P (y = 1 | x) = sigmoïde (f (x)), comme par la définition même de la probabilité P (y = 2 | x) = 1 - P (y = 1 | x). Il n'y a pas de problème ici avec l'exclusivité mutuelle. Cependant, si vous avez plus de 2 classes alors vous avez raison, sigmoïde ne peut pas être appliqué. C'est exactement comme cela (entre autres modèles) que la régression logistique est dérivée. – lejlot

+1

Le mal n'est pas grand, mais ce n'est pas "pas de mal" - vous allouez plus de mémoire (peu importe si la dernière couche est petite, mais cela pourrait être important), vous gaspillez des calculs (car il n'y a rien l'apprentissage peut en bénéficier, mais nous devons calculer un gradient supplémentaire), enfin pour les réseaux neuronaux, il n'est pas clair si cette redondance n'affectera pas la dynamique d'apprentissage (puisque nous avons encore très peu de compréhension des surfaces de perte des réseaux profonds). une meilleure stratégie pour éviter toute complexité inutile. – lejlot

3

Le softmax_cross_entropy_with_logits est essentiellement une mise en œuvre stable des 2 parties:

softmax = tf.nn.softmax(prediction) 
cost = -tf.reduce_mean(labels * tf.log(softmax), 1) 

Maintenant, dans votre exemple, la prédiction est une valeur unique, donc lorsque vous appliquez softmax sur elle, ça va être toujours 1 quel que soit la valeur (exp(prediction)/exp(prediction) = 1), et ainsi le terme tf.log(softmax) devient 0. C'est pourquoi vous obtenez toujours votre coût zéro.

Soit appliquer sigmoid pour obtenir vos probabilités entre 0 ou 1 ou si vous utilisez souhaitez utiliser softmax obtenir les étiquettes comme [1, 0] pour la classe 0 et [0, 1] pour la classe 1.