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?
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))
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.
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?)
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).
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)
Où data
est l'ensemble de données que vous essayez d'appliquer des poids à.