web-dev-qa-db-fra.com

Comment calculer les poids non équilibrés pour BCEWithLogitsloss dans Pytorch

J'essaie de résoudre un problème multilabel avec des étiquettes 270 Et j'ai converti des étiquettes cible en une forme codée à chaud. J'utilise BCEWithLogitsLoss(). Étant donné que les données de formation sont déséquilibrées, j'utilise pos_weight Argument mais je suis un peu confus.

pos_weight (Tensor, facultatif) - un poids d'exemples positifs. Doit être un vecteur avec une longueur égale au nombre de classes.

Dois-je donner le nombre total de valeurs positives de chaque étiquette comme un tenseur ou signifie quelque chose d'autre en poids?

8
Naresh

Solution Pytorch

Eh bien, en fait, j'ai traversé des docs et vous pouvez simplement utiliser pos_weight En effet.

Cet argument donne du poids à un échantillon positif pour chaque classe, donc si vous avez 270 classes que vous devriez passer torch.Tensor avec la forme (270,) Définir le poids pour chaque classe.

Voici un extrait de modification marginalement modifié de documentation :

# 270 classes, batch size = 64    
target = torch.ones([64, 270], dtype=torch.float32)  
# Logits outputted from your network, no activation
output = torch.full([64, 270], 0.9)
# Weights, each being equal to one. You can input your own here.
pos_weight = torch.ones([270])
criterion = torch.nn.BCEWithLogitsLoss(pos_weight=pos_weight)
criterion(output, target)  # -log(sigmoid(0.9))

Solution auto-fabriquée

En ce qui concerne la pondération, il n'y a pas de solution intégrée, mais vous pouvez le coder un soi-même facilement:

import torch

class WeightedMultilabel(torch.nn.Module):
    def __init__(self, weights: torch.Tensor):
        self.loss = torch.nn.BCEWithLogitsLoss()
        self.weights = weights.unsqueeze()

    def forward(outputs, targets):
        return self.loss(outputs, targets) * self.weights

Tensor doit avoir de la même longueur que le nombre de classes de votre classification Multilabel (270), chacun donnant du poids à votre exemple spécifique.

Calcul des poids

Vous simplement ajouter des étiquettes de chaque échantillon dans votre jeu de données, diviser par la valeur minimale et l'inverse à la fin.

Sorte d'extrait:

weights = torch.zeros_like(dataset[0])
for element in dataset:
    weights += element

weights = 1 / (weights / torch.min(weights))

L'utilisation de cette classe d'approche se produisant le moins de la perte normale, tandis que d'autres auront des poids plus petits que 1.

Cela peut entraîner une certaine instabilité lors de la formation, vous pouvez donc vouloir expérimenter ces valeurs un peu (peut-être log transformer au lieu de linéaire?)

Autre approche

Vous pouvez penser à upsampling/descendant (bien que cette opération soit compliquée, vous pouvez également ajouter/supprimer des autres classes, de sorte que les heuristiques avancées seraient nécessaires, je pense).

2
Szymon Maszke

Juste pour fournir une révision rapide sur la réponse de @ Crypdick, cette mise en œuvre de la fonction a fonctionné pour moi:

def calculate_pos_weights(class_counts,data):
    pos_weights = np.ones_like(class_counts)
    neg_counts = [len(data)-pos_count for pos_count in class_counts]
    for cdx, (pos_count, neg_count) in enumerate(Zip(class_counts,  neg_counts)):
        pos_weights[cdx] = neg_count / (pos_count + 1e-5)

    return torch.as_tensor(pos_weights, dtype=torch.float)

data est l'ensemble de données que vous essayez d'appliquer des poids à.

0
aesthetic420