Wie setze ich Klassengewichte für unausgeglichene Klassen in Keras?

130

Ich weiß, dass es in Keras eine Möglichkeit gibt, das class_weightsParameterwörterbuch anzupassen, aber ich konnte kein Beispiel finden. Würde jemand so freundlich sein, einen zur Verfügung zu stellen?

Übrigens ist es in diesem Fall die angemessene Praxis, die Minderheitenklasse proportional zu ihrer Unterrepräsentation zu gewichten?

Hendrik
quelle
Gibt es eine neue aktualisierte Methode mit Keras? Warum besteht das Wörterbuch aus drei Klassen und für die Klasse: 0: 1.0 1: 50.0 2: 2.0 ???? sollte nicht auch: 2: 1.0 sein?
Chuck

Antworten:

112

Wenn Sie über den regulären Fall sprechen, in dem Ihr Netzwerk nur eine Ausgabe erzeugt, ist Ihre Annahme korrekt. Um Ihren Algorithmus zu zwingen, jede Instanz von Klasse 1 als 50 Instanzen von Klasse 0 zu behandeln , müssen Sie:

  1. Definieren Sie ein Wörterbuch mit Ihren Bezeichnungen und den dazugehörigen Gewichten

    class_weight = {0: 1.,
                    1: 50.,
                    2: 2.}
  2. Füttere das Wörterbuch als Parameter:

    model.fit(X_train, Y_train, nb_epoch=5, batch_size=32, class_weight=class_weight)

BEARBEITEN: "Behandle jede Instanz von Klasse 1 als 50 Instanzen von Klasse 0 " bedeutet, dass Sie in Ihrer Verlustfunktion diesen Instanzen einen höheren Wert zuweisen. Daher wird der Verlust zu einem gewichteten Durchschnitt, wobei das Gewicht jeder Stichprobe durch class_weight und die entsprechende Klasse angegeben wird.

Aus Keras-Dokumenten: class_weight : Optionales Wörterbuch, das Klassenindizes (Ganzzahlen) einem Gewichtungswert (float) , der zur Gewichtung der Verlustfunktion verwendet wird (nur während des Trainings).

Layser
quelle
1
Auch einen Blick auf github.com/fchollet/keras/issues/3653 , wenn Sie mit 3D - Daten arbeiten.
Herve
Für mich gibt es einen Fehler dic hat keine Formattribute.
Flávio Filho
Ich glaube , Keras könnte die Art und Weise funktioniert dies sich ändern, dies für die Version von August 2016. ich für Sie in einer Woche überprüfen wird
layser
4
@layser Funktioniert dies nur bei Verlust von 'category_crossentropy'? Wie kann man Keras für den Verlust von 'Sigmoid' und 'Binary_crossentropy' ein Klassengewicht zuweisen?
Naman
1
@layser Können Sie erklären, "um jede Instanz von Klasse 1 als 50 Instanzen von Klasse 0 zu behandeln"? Ist es so, dass die Zeile im Trainingssatz, die der Klasse 1 entspricht, 50-mal dupliziert wird, um sie auszugleichen, oder folgt ein anderer Prozess?
Divyanshu Shekhar
122

Sie könnten einfach implementieren die class_weightaus sklearn:

  1. Lassen Sie uns zuerst das Modul importieren

    from sklearn.utils import class_weight
  2. Um das Klassengewicht zu berechnen, gehen Sie wie folgt vor

    class_weights = class_weight.compute_class_weight('balanced',
                                                     np.unique(y_train),
                                                     y_train)
  3. Drittens und zuletzt zum Modellfitting hinzufügen

    model.fit(X_train, y_train, class_weight=class_weights)

Achtung : Ich habe diesen Beitrag bearbeitet und den Variablennamen von class_weight in class_weight s geändert, um das importierte Modul nicht zu überschreiben. Passen Sie dies an, wenn Sie den Code aus den Kommentaren kopieren.

PSc
quelle
21
class_weight.compute_class_weight Produziert für mich ein Array, muss ich es in ein Diktat ändern, um mit Keras zu arbeiten. Verwenden class_weight_dict = dict(enumerate(class_weight))
Sie
5
Das funktioniert bei mir nicht. Für ein Drei-Klassen-Problem in Keras y_trainist (300096, 3)Numpy Array. Die class_weight=Zeile gibt mir also TypeError: unhashable type: 'numpy.ndarray'
Lembik
3
@Lembik Ich hatte ein ähnliches Problem, bei dem jede Zeile von y ein one-hot-codierter Vektor des Klassenindex ist. I fixed es von der one-hot - Darstellung in einen int wie diese Umwandlung: y_ints = [y.argmax() for y in y_train].
Tkocmathla
3
Was ist, wenn ich mehrere Klassen beschrifte, sodass meine y_true-Vektoren mehrere Einsen enthalten: [1 0 0 0 1 0 0], wobei einige x die Bezeichnungen 0 und 4 haben labels ist nicht ausgewogen. Wie würde ich damit Klassengewichte verwenden?
Aalok
22

Ich benutze diese Art von Regel für class_weight:

import numpy as np
import math

# labels_dict : {ind_label: count_label}
# mu : parameter to tune 

def create_class_weight(labels_dict,mu=0.15):
    total = np.sum(labels_dict.values())
    keys = labels_dict.keys()
    class_weight = dict()

    for key in keys:
        score = math.log(mu*total/float(labels_dict[key]))
        class_weight[key] = score if score > 1.0 else 1.0

    return class_weight

# random labels_dict
labels_dict = {0: 2813, 1: 78, 2: 2814, 3: 78, 4: 7914, 5: 248, 6: 7914, 7: 248}

create_class_weight(labels_dict)

math.logglättet die Gewichte für sehr unausgeglichene Klassen! Dies ergibt:

{0: 1.0,
 1: 3.749820767859636,
 2: 1.0,
 3: 3.749820767859636,
 4: 1.0,
 5: 2.5931008483842453,
 6: 1.0,
 7: 2.5931008483842453}
J. Guillaumin
quelle
3
Warum sollte log verwendet werden, anstatt nur die Anzahl der Stichproben für eine Klasse durch die Gesamtzahl der Stichproben zu dividieren? Ich gehe davon aus, dass es etwas gibt, das ich nicht verstehe. Es geht in den Parameter class_weight auf model.fit_generator (...)
startoftext
@startoftext So habe ich es gemacht, aber ich glaube, du hast es invertiert. Ich habe n_total_samples / n_class_samplesfür jede Klasse verwendet.
Colllin
2
In Ihrem Beispiel haben Klasse 0 (mit 2813 Beispielen) und Klasse 6 (mit 7914 Beispielen) genau ein Gewicht von 1,0. Warum ist das so? Die Klasse 6 ist einige Male größer! Sie möchten, dass Klasse 0 hoch- und Klasse 6 herunterskaliert wird, um sie auf das gleiche Niveau zu bringen.
Vladislavs Dovgalecs
9

HINWEIS: Siehe Kommentare, diese Antwort ist veraltet.

Um alle Klassen gleich zu gewichten, können Sie class_weight jetzt einfach wie folgt auf "auto" setzen:

model.fit(X_train, Y_train, nb_epoch=5, batch_size=32, class_weight = 'auto')
David Groppe
quelle
1
Ich konnte weder class_weight='auto'in der Keras-Dokumentation noch im Quellcode einen Verweis auf finden . Können Sie uns zeigen, wo Sie das gefunden haben?
Fábio Perez
2
Diese Antwort ist wahrscheinlich falsch. Überprüfen Sie diese Ausgabe: github.com/fchollet/keras/issues/5116
Fábio Perez
Ungerade. Ich habe class_balanced = 'auto' verwendet, als ich den Kommentar gepostet habe, aber ich kann jetzt keinen Verweis darauf finden. Vielleicht wurde es geändert, als sich Keras rasant weiterentwickelte.
David Groppe
Wie in dem oben angegebenen Keras-Problem angegeben , können Sie eine beliebige Zeichenfolge als übergeben class_weight, ohne dass dies Auswirkungen hat. Diese Antwort ist daher nicht richtig.
Ncasas
3

class_weight ist in Ordnung, aber wie @Aalok sagte, funktioniert dies nicht, wenn Sie mehrfach beschriftete Klassen mit One-Hot-Codierung verwenden. In diesem Fall verwenden Sie sample_weight :

sample_weight: Optionales Array mit der gleichen Länge wie x, das die Gewichte enthält, die auf den Verlust des Modells für jede Stichprobe angewendet werden. Bei zeitlichen Daten können Sie ein 2D-Array mit Form (Samples, sequence_length) übergeben, um jedem Zeitschritt jedes Samples ein anderes Gewicht zuzuweisen. In diesem Fall sollten Sie unbedingt sample_weight_mode = "temporal" in compile () angeben.

sample_weights wird verwendet, um ein Gewicht für jede Trainingsprobe bereitzustellen . Das bedeutet, dass Sie ein 1D-Array mit der gleichen Anzahl von Elementen wie Ihre Trainingsmuster übergeben sollten (wobei das Gewicht für jedes dieser Muster angegeben wird).

class_weights wird verwendet, um eine Gewichtung oder eine Verzerrung für jede Ausgabeklasse bereitzustellen . Das bedeutet, dass Sie für jede Klasse, die Sie klassifizieren möchten, eine Gewichtung übergeben sollten.

sample_weight muss ein numpy-Array zugewiesen werden, da seine Form ausgewertet wird.

Siehe auch diese Antwort: https://stackoverflow.com/questions/48315094/using-sample-weight-in-keras-for-sequence-labelling

Charly Empereur-mot
quelle
2

Hinzufügen zur Lösung unter https://github.com/keras-team/keras/issues/2115 . Wenn Sie mehr als eine Klassengewichtung benötigen, bei der Sie unterschiedliche Kosten für falsch-positive und falsch-negative Ergebnisse wünschen. Mit der neuen Keras-Version können Sie nun die unten angegebene Verlustfunktion überschreiben. Beachten Sie, dass dies weightseine quadratische Matrix ist.

from tensorflow.python import keras
from itertools import product
import numpy as np
from tensorflow.python.keras.utils import losses_utils

class WeightedCategoricalCrossentropy(keras.losses.CategoricalCrossentropy):

    def __init__(
        self,
        weights,
        from_logits=False,
        label_smoothing=0,
        reduction=losses_utils.ReductionV2.SUM_OVER_BATCH_SIZE,
        name='categorical_crossentropy',
    ):
        super().__init__(
            from_logits, label_smoothing, reduction, name=f"weighted_{name}"
        )
        self.weights = weights

    def call(self, y_true, y_pred):
        weights = self.weights
        nb_cl = len(weights)
        final_mask = keras.backend.zeros_like(y_pred[:, 0])
        y_pred_max = keras.backend.max(y_pred, axis=1)
        y_pred_max = keras.backend.reshape(
            y_pred_max, (keras.backend.shape(y_pred)[0], 1))
        y_pred_max_mat = keras.backend.cast(
            keras.backend.equal(y_pred, y_pred_max), keras.backend.floatx())
        for c_p, c_t in product(range(nb_cl), range(nb_cl)):
            final_mask += (
                weights[c_t, c_p] * y_pred_max_mat[:, c_p] * y_true[:, c_t])
        return super().call(y_true, y_pred) * final_mask
Praveen Kulkarni
quelle
0

Ich habe das folgende Beispiel für die Codierung von Klassengewichten in der Verlustfunktion mithilfe des Minist-Datasets gefunden. Siehe Link hier: https://github.com/keras-team/keras/issues/2115

def w_categorical_crossentropy(y_true, y_pred, weights):
    nb_cl = len(weights)
    final_mask = K.zeros_like(y_pred[:, 0])
    y_pred_max = K.max(y_pred, axis=1)
    y_pred_max = K.reshape(y_pred_max, (K.shape(y_pred)[0], 1))
    y_pred_max_mat = K.equal(y_pred, y_pred_max)
    for c_p, c_t in product(range(nb_cl), range(nb_cl)):
        final_mask += (weights[c_t, c_p] * y_pred_max_mat[:, c_p] * y_true[:, c_t])
    return K.categorical_crossentropy(y_pred, y_true) * final_mask
CathyQian
quelle
0
from collections import Counter
itemCt = Counter(trainGen.classes)
maxCt = float(max(itemCt.values()))
cw = {clsID : maxCt/numImg for clsID, numImg in itemCt.items()}

Dies funktioniert mit einem Generator oder Standard. Ihre größte Klasse hat eine Gewichtung von 1, während die anderen Werte größer als 1 in Bezug auf die größte Klasse haben.

Klassengewichte akzeptieren eine Eingabe vom Typ Wörterbuch

Allie
quelle