Tensorflow Ajustement de la fonction de coût pour les données déséquilibrées

12

J'ai un problème de classification avec des données très déséquilibrées. J'ai lu que le suréchantillonnage et le sous-échantillonnage ainsi que la modification du coût des extrants catégoriels sous-représentés conduiront à un meilleur ajustement. Avant cela, tensorflow catégoriserait chaque entrée comme le groupe majoritaire (et gagnerait plus de 90% de précision, aussi insignifiant que cela soit).

J'ai remarqué que le logarithme du pourcentage inverse de chaque groupe a fait le meilleur multiplicateur que j'ai essayé. Existe-t-il une manipulation plus standard pour la fonction de coût? Est-ce mis en œuvre correctement?

from collections import Counter
counts = Counter(category_train)
weightsArray =[]
for i in range(n_classes):
    weightsArray.append(math.log(category_train.shape[0]/max(counts[i],1))+1)

class_weight = tf.constant(weightsArray)
weighted_logits = tf.mul(pred, class_weight)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(weighted_logits, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
Cole
la source
Avez-vous des références scientifiques sur la façon dont vous choisissez idéalement les poids pour la fonction de perte? Non pas que je ne te crois pas, mais je te trouvais très inspiré par quelqu'un d'autre?
Gerhard Hagerer
Et comme davidparks21 l'a déjà demandé, les résultats de votre approche seraient très intéressants :).
Gerhard Hagerer

Réponses:

4

Cela semble être une bonne solution pour la fonction de perte. J'ai eu du succès avec une approche similaire récemment, mais je pense que vous voudriez réorganiser où vous vous multipliez dans le class_weight.

En y réfléchissant logiquement, le class_weightsera une constante par rapport à la sortie, donc il sera transporté et appliqué au gradient de la même manière qu'il est appliqué à la fonction de coût. Mais il y a un problème.

La façon dont vous l'avez, class_weightaffecterait la valeur de prédiction. Mais vous voulez qu'il affecte l'échelle du dégradé. Si je ne me trompe pas, je pense que vous voudriez inverser l'ordre des opérations:

# Take the cost like normal
error = tf.nn.softmax_cross_entropy_with_logits(pred, y)

# Scale the cost by the class weights
scaled_error = tf.mul(error, class_weight)

# Reduce
cost = tf.reduce_mean(scaled_error)

Je serais très intéressé de savoir comment cela fonctionne par rapport au simple suréchantillonnage de la classe sous-représentée, ce qui est plus typique. Donc, si vous obtenez un aperçu, postez-en à ce sujet! :)

Fait intéressant, j'ai récemment utilisé avec succès une technique très similaire dans un autre domaine problématique (ce qui m'a amené à ce poste):

Apprentissage multitâche, recherche d'une fonction de perte qui "ignore" certains échantillons

davidparks21
la source
2

Commander tf.nn.weighted_cross_entropy_with_logits():

Calcule une entropie croisée pondérée.

C'est comme sigmoid_cross_entropy_with_logits () sauf que pos_weight, permet de faire un compromis entre le rappel et la précision en augmentant ou diminuant le coût d'une erreur positive par rapport à une erreur négative.

Cela devrait vous permettre de faire ce que vous voulez.

marcos pozzi
la source
0

J'ai 2 implémentations différentes:

  1. avec softmax 'normal' avec logits: tf.nn.softmax_cross_entropy_with_logits

Lorsque le poids_classe est un espace réservé, je remplis chaque itération par lots.

self.class_weight  = tf.placeholder(tf.float32, shape=self.batch_size,self._num_classes], name='class_weight')    
self._final_output = tf.matmul(self._states,self._weights["linear_layer"]) + self._biases["linear_layer"] 
self.scaled_logits = tf.multiply(self._final_output, self.class_weight)
self.softmax = tf.nn.softmax_cross_entropy_with_logits(logits=self.scaled_logits,labels= self._labels)
  1. avec tf.nn.softmax_cross_entropy_with_logits

Où j'utilise la fonction tensorflow implémentée mais je dois calculer les poids pour le lot. Les documents sont un peu déroutants. Il y a 2 façons de le faire avec tf.gather ou comme ceci:

self.scaled_class_weights=tf.reduce_sum(tf.multiply(self._labels,self.class_weight),1)
self.softmax = tf.losses.softmax_cross_entropy(logits=self._final_output,
                                                   onehot_labels=self._labels,weights=self.scaled_class_weights)

ici, il y a une belle discussion à ce sujet

Et finalement, comme je ne voulais pas me marier à aucune des implémentations de façon permanente, j'ai ajouté un petit tf.case et je transmets au temps de formation la stratégie que je veux utiliser.

self.sensitive_learning_strategy = tf.placeholder(tf.int32 , name='sensitive_learning_strategy')
self.softmax =tf.case([
            (tf.equal(self.sensitive_learning_strategy, 0), lambda: self.softmax_0),
            (tf.equal(self.sensitive_learning_strategy, 1), lambda: self.softmax_1),
            (tf.equal(self.sensitive_learning_strategy, 2), lambda: self.softmax_2)
AI4U.ai
la source