web-dev-qa-db-fra.com

Utilisation de sample_weight dans Keras pour l'étiquetage de séquence

Je travaille sur un problème d'étiquetage séquentiel avec des classes non équilibrées et j'aimerais utiliser sample_weight Pour résoudre le problème de déséquilibre. Fondamentalement, si j'entraîne le modèle pendant environ 10 époques, j'obtiens d'excellents résultats. Si je m'entraîne pour plus d'époques, val_loss Continue de baisser, mais j'obtiens de moins bons résultats. Je suppose que le modèle détecte simplement plus de la classe dominante au détriment des classes plus petites.

Le modèle a deux entrées, pour les intégrations Word et les intégrations de caractères, et l'entrée est l'une des 7 classes possibles de 0 à 6.

Avec le remplissage, la forme de ma couche d'entrée pour les intégrations Word est (3000, 150) Et la couche d'entrée pour les intégrations Word est (3000, 150, 15). J'utilise un fractionnement de 0,3 pour les données de test et de formation, ce qui signifie que X_train Pour les intégrations Word est (2000, 150) Et (2000, 150, 15) Pour les intégrations de caractères. y contient la classe correcte pour chaque mot, encodée dans un vecteur unique de dimension 7, donc sa forme est (3000, 150, 7). y est également divisé en un ensemble de formation et de test. Chaque entrée est ensuite introduite dans un LSTM bidirectionnel.

Le résultat est une matrice avec l'une des 7 catégories affectées à chaque mot des 2000 échantillons d'apprentissage, la taille est donc (2000, 150, 7).


Au début, j'ai simplement essayé de définir sample_weight Comme un np.array De longueur 7 contenant les poids pour chaque classe:

count = [list(array).index(1) for arrays in y for array in arrays]
count = dict(Counter(count))
count[0] = 0
total = sum([count[key] for key in count])
count = {k: count[key] / total for key in count}
category_weights = np.zeros(7)
for f in count:
    category_weights[f] = count[f]

Mais j'obtiens l'erreur suivante ValueError: Found a sample_weight array with shape (7,) for an input with shape (2000, 150, 7). sample_weight cannot be broadcast.

En regardant les documents, il semble que je devrais plutôt passer a 2D array with shape (samples, sequence_length). Je crée donc un tableau (3000, 150) Avec une concaténation des poids de chaque mot de chaque séquence:

weights = []

for sample in y:
    current_weight = []
    for line in sample:
        current_weight.append(frequency[list(line).index(1)])
    weights.append(current_weight)

weights = np.array(weights)

et passez cela à la fonction fit via le paramètre sample_weight après avoir ajouté l'option sample_weight_mode="temporal" dans compile().

J'ai d'abord eu une erreur en me disant que la dimension était incorrecte, mais après avoir généré les poids uniquement pour l'échantillon d'apprentissage, je me retrouve avec un tableau (2000, 150) Que je peux utiliser pour s'adapter à mon modèle.


  • Est-ce une bonne façon de définir les poids_échantillons ou est-ce que je fais tout mal? Je ne peux pas dire que j'ai remarqué des améliorations en ajoutant les poids, donc j'ai dû manquer quelque chose.
8
user2969402

Je pense que vous confondez sample_weights Et class_weights. En vérifiant les docs un peu, nous pouvons voir les différences entre eux:

sample_weights Est utilisé pour fournir un poids pour chaque échantillon d'entraînement. Cela signifie que vous devez réussir un tableau 1D avec le même nombre d'éléments que vos échantillons d'entraînement (indiquant le poids de chacun de ces échantillons). Dans le cas où vous utilisez des données temporelles, vous pouvez à la place passer un tableau 2D, vous permettant de donner du poids à chaque pas de temps de chaque échantillon.

class_weights Est utilisé pour fournir un poids ou un biais pour chaque classe de sortie . Cela signifie que vous devez transmettre un poids pour chaque classe que vous essayez de classer. De plus, ce paramètre attend qu'un dictionnaire lui soit transmis (pas un tableau, c'est pourquoi vous avez cette erreur). Par exemple, considérez cette situation:

class_weight = {0 : 1. , 1: 50.}

Dans ce cas (un problème de classification binaire), vous donnez 50 fois plus de poids (ou de "pertinence") à vos échantillons de classe 1 Par rapport à la classe 0. De cette façon, vous pouvez compenser les jeux de données déséquilibrés. Voici un autre post utile expliquant plus à ce sujet et d'autres options à considérer lorsque vous traitez avec des ensembles de données déséquilibrés.

Si je m'entraîne pour plus d'époques, val_loss continue de chuter, mais j'obtiens de moins bons résultats.

Vous êtes probablement sur-ajusté, et quelque chose qui pourrait y contribuer, ce sont les classes déséquilibrées de votre ensemble de données, comme vous le soupçonniez correctement. La compensation des poids de classe devrait aider à atténuer cela, mais il peut y avoir d'autres facteurs qui peuvent entraîner un sur-ajustement qui échappent à la portée de cette question/réponse (alors assurez-vous de faire attention à ceux après avoir résolu cette question).


À en juger par votre message, il me semble que ce dont vous avez besoin est d'utiliser class_weight Pour équilibrer votre jeu de données pour la formation, pour lequel vous devrez passer un dictionnaire indiquant les ratios de poids entre vos 7 cours. Envisagez d'utiliser sample_weight Uniquement si vous souhaitez donner à chaque échantillon un poids personnalisé à prendre en considération.

Si vous voulez une comparaison plus détaillée entre ces deux, pensez à vérifier cette réponse J'ai posté une question connexe. Spoiler: sample_weight Remplace class_weight, Vous devez donc utiliser l'un ou l'autre, mais pas les deux, alors soyez attention à ne pas les mélanger.


Mise à jour: Au moment de cette modification (27 mars 2020), en regardant le code source de la fonction training_utils.standardize_weights() nous pouvons voir qu'il prend désormais en charge les deuxclass_weights et sample_weights:

Tout est normalisé en un seul tableau de poids en fonction de l'échantillon (ou du temps). Si à la fois sample_weights Et class_weights Sont fournis, les poids sont multipliés ensemble.

11
DarkCygnus