web-dev-qa-db-fra.com

Fonction de perte pour le classifieur binaire déséquilibré de classe dans le flux tenseur

J'essaie d'appliquer l'apprentissage en profondeur à un problème de classification binaire avec un déséquilibre entre les classes cibles (500K, 31K). Je veux écrire une fonction de perte personnalisée qui devrait ressembler à: Minimiser (100 - ((Predicted_smallerclass)/(total_smallerclass))) * 100)

Appréciez les indications sur la manière dont je peux construire cette logique. 

43

Vous pouvez ajouter des poids de classe à la fonction de perte en multipliant les logits. La perte d'entropie croisée régulière est la suivante:

loss(x, class) = -log(exp(x[class]) / (\sum_j exp(x[j])))
               = -x[class] + log(\sum_j exp(x[j]))

en cas pondéré:

loss(x, class) = weights[class] * -x[class] + log(\sum_j exp(weights[class] * x[j]))

Ainsi, en multipliant les logits, vous redimensionnez les prédictions de chaque classe en fonction de son poids.

Par exemple:

ratio = 31.0 / (500.0 + 31.0)
class_weight = tf.constant([ratio, 1.0 - ratio])
logits = ... # shape [batch_size, 2]
weighted_logits = tf.mul(logits, class_weight) # shape [batch_size, 2]
xent = tf.nn.softmax_cross_entropy_with_logits(
  weighted_logits, labels, name="xent_raw")

Il existe maintenant une fonction standard de pertes qui prend en charge les poids par lot:

tf.losses.sparse_softmax_cross_entropy(labels=label, logits=logits, weights=weights)

Où les poids doivent être transformés de poids de classe en poids par exemple (avec shape [batch_size]). Voir documentation ici .

38
ilblackdragon

Le code que vous avez proposé me semble faux. La perte doit être multipliée par le poids, je suis d’accord.

Mais si vous multipliez le logit par les poids de classe, vous terminez par:

weights[class] * -x[class] + log( \sum_j exp(x[j] * weights[class]) )

Le second terme n'est pas égal à: 

weights[class] * log(\sum_j exp(x[j]))

Pour montrer cela, nous pouvons réécrire ce dernier comme suit: 

log( (\sum_j exp(x[j]) ^ weights[class] )

Alors voici le code que je propose:

ratio = 31.0 / (500.0 + 31.0)
class_weight = tf.constant([[ratio, 1.0 - ratio]])
logits = ... # shape [batch_size, 2]

weight_per_label = tf.transpose( tf.matmul(labels
                           , tf.transpose(class_weight)) ) #shape [1, batch_size]
# this is the weight for each datapoint, depending on its label

xent = tf.mul(weight_per_label
         , tf.nn.softmax_cross_entropy_with_logits(logits, labels, name="xent_raw") #shape [1, batch_size]
loss = tf.reduce_mean(xent) #shape 1
38
JL Meunier

Utilisez tf.nn.weighted_cross_entropy_with_logits() et définissez pos_weight sur 1/(rapport attendu des positifs).

10
Malay Haldar

Vous pouvez consulter les guides sur tensorflow https://www.tensorflow.org/api_guides/python/contrib.losses

...

Bien que la spécification d'une perte scalaire redimensionne la perte sur l'ensemble du lot, nous souhaitons parfois redimensionner la perte par échantillon de lot. Par exemple, si nous avons certains exemples qui nous importent davantage pour obtenir correctement, nous pourrions souhaiter une perte plus élevée que d’autres échantillons dont les erreurs importent moins. Dans ce cas, nous pouvons fournir un vecteur de poids de longueur batch_size qui entraîne la perte pour chaque échantillon du lot étant mis à l'échelle par l'élément de poids correspondant. Par exemple, prenons le cas d’un problème de classification dans lequel nous voulons maximiser notre précision mais nous souhaitons particulièrement obtenir une précision élevée pour une classe spécifique:

inputs, labels = LoadData(batch_size=3)
logits = MyModelPredictions(inputs)

# Ensures that the loss for examples whose ground truth class is `3` is 5x
# higher than the loss for all other examples.
weight = tf.multiply(4, tf.cast(tf.equal(labels, 3), tf.float32)) + 1

onehot_labels = tf.one_hot(labels, num_classes=5)
tf.contrib.losses.softmax_cross_entropy(logits, onehot_labels, weight=weight)

Je devais travailler avec un ensemble de données similaire non équilibré de plusieurs classes et voici comment je l'ai fait, espérez que cela aidera quelqu'un à la recherche d'une solution similaire:

Cela entre dans votre module de formation: 

from sklearn.utils.class_weight import compute_sample_weight
#use class weights for handling unbalanced dataset
if mode == 'INFER' #test/dev mode, not weighing loss in test mode
   sample_weights = np.ones(labels.shape)
else:
   sample_weights = compute_sample_weight(class_weight='balanced', y=labels)

Cela va dans la définition de votre classe de modèle:

#an extra placeholder for sample weights
#assuming you already have batch_size tensor
self.sample_weight = tf.placeholder(dtype=tf.float32, shape=[None],
                       name='sample_weights')
cross_entropy_loss = tf.nn.sparse_softmax_cross_entropy_with_logits(
                       labels=self.label, logits=logits, 
                       name='cross_entropy_loss')
cross_entropy_loss = tf.reduce_sum(cross_entropy_loss*self.sample_weight) / batch_size
3
bitspersecond

Avez-vous utilisé tf.nn.weighted_cross_entropy_with_logits () pour deux classes:

classes_weights = tf.constant([0.1, 1.0])
cross_entropy = tf.nn.weighted_cross_entropy_with_logits(logits=logits, targets=labels, pos_weight=classes_weights)
1
Denis Shcheglov