Halo kawan-kawan. Jenis torch.LongTensor
dari target akan menghambat implementasi seperti beberapa metode dalam referensi . Jadi apakah ada kemungkinan untuk menambahkan Arg: label_smoothing
untuk torch.nn.CrossEntropyLoss()
, atau mungkin cukup menambahkan dokumen untuk menunjukkan cara mengonversi target
menjadi one-hot vector
agar berfungsi torch.nn.CrossEntropyLoss()
bersama-sama, atau cara sederhana lainnya? Terima kasih.
cc @ezyang @gchanan @zou3519 @bdhirsh @albanD @mruberry
@KaiyuYue
Untuk label_smoothing, perhatikan implementasi NJUNMT-pytorch
Di kelas NMTCritierion
Lihat https://discuss.pytorch.org/t/cross-entropy-with-one-hot-targets/13580/5. Fungsi cross_entropy()
yang ditampilkan di sana harus bekerja dengan label yang dihaluskan yang memiliki dimensi yang sama dengan keluaran jaringan.
Saya tidak berpikir CrossEntropyLoss()
harus secara langsung mendukung opsi label_smoothing
, karena perataan label dapat dilakukan dengan berbagai cara dan pemulusan itu sendiri dapat dengan mudah dilakukan secara manual oleh pengguna. Tapi saya setuju setidaknya harus disebutkan dalam dokumen bagaimana menangani target yang tidak dapat diwakili oleh nilai skalar, atau menambahkan dukungan untuk meneruskan target (k-hot/smoothed) ke CrossEntropyLoss
.
Mungkin kita perlu sth seperti NonSparseCrossEntropy
? (yah.. sulit untuk menyebutkannya)
Ini alat saya
class LabelSmoothingLoss(nn.Module):
def __init__(self, classes, smoothing=0.0, dim=-1):
super(LabelSmoothingLoss, self).__init__()
self.confidence = 1.0 - smoothing
self.smoothing = smoothing
self.cls = classes
self.dim = dim
def forward(self, pred, target):
pred = pred.log_softmax(dim=self.dim)
with torch.no_grad():
# true_dist = pred.data.clone()
true_dist = torch.zeros_like(pred)
true_dist.fill_(self.smoothing / (self.cls - 1))
true_dist.scatter_(1, target.data.unsqueeze(1), self.confidence)
return torch.mean(torch.sum(-true_dist * pred, dim=self.dim))
Saya setuju dengan @mdraw
Pilihan yang baik adalah melakukannya dalam dua langkah:
def smooth_one_hot(true_labels: torch.Tensor, classes: int, smoothing=0.0):
"""
if smoothing == 0, it's one-hot method
if 0 < smoothing < 1, it's smooth method
"""
assert 0 <= smoothing < 1
confidence = 1.0 - smoothing
label_shape = torch.Size((true_labels.size(0), classes))
with torch.no_grad():
true_dist = torch.empty(size=label_shape, device=true_labels.device)
true_dist.fill_(smoothing / (classes - 1))
true_dist.scatter_(1, true_labels.data.unsqueeze(1), confidence)
return true_dist
CrossEntropyLoss
mendukung k-hot/smoothed.Kemudian kita bisa menggunakannya seperti
Loss = CrossEntropyLoss(NonSparse=True, ...)
. . .
data = ...
labels = ...
outputs = model(data)
smooth_label = smooth_one_hot(labels, ...)
loss = (outputs, smooth_label)
...
Omong-omong, saya menguji alat saya di ImageNet, kelihatannya bagus
|model | zaman| dtype |ukuran batch*|gpus | lr | trik|top1/top5 |perbaiki |
|:----:|:-----:|:------:|:---------:|:----:|:---:|: ------:|:---------:|:------:|
|resnet50|120 |FP16 |128 | 8 |0,4 | - |77.35/- |dasar|
|resnet50|120 |FP16 |128 | 8 |0,4 |Perataan label|77,78/93,80| +0.43 |
Saya percaya @zhangguanheng66 mengatakan bahwa ini adalah sesuatu yang mungkin bisa dia lihat di masa depan.
Cukup gunakan torch.nn.KLDivLoss. Sama.
Pembaruan: tidak sama.
Saya percaya ini mirip dengan apa yang diterapkan lib Snorkel baru:
https://snorkel.readthedocs.io/en/master/packages/_autosummary/classification/snorkel.classification.cross_entropy_with_probs.html
Hanya beberapa info tambahan tentang bagaimana orang-orang mengatasi masalah ini
lihat https://github.com/NVIDIA/DeepLearningExamples/tree/master/PyTorch/Classification/RN50v1.5 untuk mengetahui bagaimana Nvidia melakukannya yang mungkin membantu?
@suanrong Terima kasih banyak.
====
Dan mungkin ini bermanfaat bagi orang lain yang membaca masalah ini
Perhatikan bahwa entropi silang untuk label non 0/1 tidak simetris, yang bisa menjadi penjelasan untuk kinerja yang buruk.
https://discuss.pytorch.org/t/cross-entropy-for-soft-label/16093/2
Implementasi yang disarankan:
class LabelSmoothLoss(nn.Module):
def __init__(self, smoothing=0.0):
super(LabelSmoothLoss, self).__init__()
self.smoothing = smoothing
def forward(self, input, target):
log_prob = F.log_softmax(input, dim=-1)
weight = input.new_ones(input.size()) * \
self.smoothing / (input.size(-1) - 1.)
weight.scatter_(-1, target.unsqueeze(-1), (1. - self.smoothing))
loss = (-weight * log_prob).sum(dim=-1).mean()
return loss
Saya telah memeriksa bahwa:
(1) Saat menghaluskan=0.0, outputnya sama dengan nn.CrossEntropyLoss
dalam presisi 1e-5
.
(2) Saat menghaluskan>0,0, jumlah bobot pada kelas yang berbeda weight.sum(dim=-1)
selalu 1.
Implementasi di sini kekurangan fitur bobot kelas.
((
Cukup gunakan torch.nn.KLDivLoss. Sama.
bisa tolong lebih di perjelas
Cukup gunakan torch.nn.KLDivLoss. Sama.
bisa tolong lebih di perjelas
Asumsikan Anda sudah memiliki label yang dihaluskan, Anda bisa menggunakan torch.nn.KLDivLoss karena perbedaan di antara keduanya adalah entropi label dan konstanta.
@PistonY mengapa tidak menggunakan cara ini lebih sederhana:
with torch.no_grad():
confidence = 1.0 - smoothing_factor
true_dist = torch.mul(labels, confidence)
true_dist = torch.add(true_dist, smoothing_factor / (classNum - 1))
print(true_dist)
return true_dist
Implementasi di sini kekurangan fitur bobot kelas.
Bisakah saya mengalikan bobot kelas pada tensor label yang dihaluskan?
def smooth_one_hot(true_labels: torch.Tensor, class: int, smoothing=0.0):
"""
jika menghaluskan == 0, itu metode satu-panas
jika 0 < smoothing < 1, itu metode smooth""" assert 0 <= smoothing < 1 confidence = 1.0 - smoothing label_shape = torch.Size((true_labels.size(0), classes)) with torch.no_grad(): true_dist = torch.empty(size=label_shape, device=true_labels.device) true_dist.fill_(smoothing / (classes - 1)) true_dist.scatter_(1, true_labels.data.unsqueeze(1), confidence) return true_dist
```
Masalah dengan implementasi ini adalah sangat sensitif terhadap jumlah kelas
Di mana n_classes adalah 2, perataan apa pun di atas 0,5 akan membalikkan label, yang saya yakin tidak diinginkan orang tersebut; ketika n_classes adalah 3 itu smoothing di atas 2/3, dan 0,75 untuk 4 kelas. Jadi mungkin:
assert 0 <= smoothing < (classes-1)/classes
akan menangkap masalah ini, tetapi saya merasa perataan perlu mempertimbangkan jumlah kelas?
def smooth_one_hot(true_labels: torch.Tensor, class: int, smoothing=0.0):
"""
if smoothing == 0, it's one-hot method
if 0 < smoothing < 1, it's smooth method
"""
assert 0 <= smoothing < 1
confidence = 1.0 - smoothing
label_shape = torch.Size((true_labels.size(0), classes))
with torch.no_grad():
true_dist = torch.empty(size=label_shape, device=true_labels.device)
true_dist.fill_(smoothing / (classes - 1))
true_dist.scatter_(1, true_labels.data.unsqueeze(1), confidence)
return true_dist
```
Masalah dengan implementasi ini adalah sangat sensitif terhadap jumlah kelas
Di mana n_classes adalah 2, perataan apa pun di atas 0,5 akan membalikkan label, yang saya yakin tidak diinginkan orang tersebut; ketika n_classes adalah 3 itu smoothing di atas 2/3, dan 0,75 untuk 4 kelas. Jadi mungkin:
assert 0 <= smoothing < (classes-1)/classes
akan menangkap masalah ini, tetapi saya merasa perataan perlu mempertimbangkan jumlah kelas?
Itu ide yang bijak menurut saya.
Terima kasih atas diskusinya. Ada beberapa poin yang masih belum jelas dan terlihat seperti kesalahan bagi saya:
Kertas penghalus label menyatakan y_k = smoothing / n_classes + (1 - smoothing) * y_{one hot}
. Jadi nilai bobotnya adalah smoothing / n_classes
untuk indeks selain target, dan smoothing / n_classes + (1 - smoothing)
untuk kelas target. Namun dalam implementasi @PistonY , fungsi torch.scatter_
menimpa nilai target menjadi (1 - smoothing)
(dan istilah konstan menghilang).
Selain itu, saya tidak begitu mengerti mengapa kita menggunakan n_classes -= 1
dalam perhitungan (?)
Kehilangan lintas-entropi perataan label berbunyi, dengan y
bobot yang disebutkan di atas,
LS(x, y) = - sum_k {y[k] * log-prob(x)}
= - sum_k {y[k] * log(exp(x[k]) / (sum_j exp(x[j])))}
= - sum_k {y[k] * (x[k] - log-sum-exp(x))}
= - sum_k {y[k] * x[k]} + log-sum-exp(x)
di mana baris ketiga hingga keempat menggunakan fakta bahwa sum_k y[k] = smoothing / n_classes * n_classes + (1 - smoothing) = 1
.
Kerugian divergensi KL berbunyi,
KL(x, y) = - sum_k {y[k] * x[k] - y[k] * log(y[k])
= - sum_k {y[k] * x[k]} - sum_k {y[k] * log(y[k])}
= - sum_k {y[k] * x[k]} - Const.
Jadi pada akhirnya kita memiliki LS(x, y) = KL(x, y) + log-sum-exp(x) + Const.
, di mana Const.
adalah suku konstan yang sesuai dengan entropi y
, yang memang konstan dalam pengaturan multikelas. Tapi bagaimana dengan istilah log-sum-exp ?
Saya melakukan beberapa perhitungan menggunakan fungsi lintas entropi khusus yang menerima target lunak , dan itu menunjukkan bahwa itu memang sama dengan kerugian KLDiv
ditambah log-sum-exp
, hingga istilah konstan yang sesuai dengan entropi y
. Apakah ada asumsi pada log yang membuatnya masuk akal untuk menjatuhkan istilah ini?
Terima kasih banyak atas klarifikasinya.
Bersulang !
Terima kasih @antrec !
Kamu benar. Saya mengabaikan fungsi logsoftmax dan membuat kesalahan.
Implementasi fungsi kehilangan lintas entropi perataan label:
import torch.nn.functional as F
def linear_combination(x, y, epsilon):
return epsilon*x + (1-epsilon)*y
def reduce_loss(loss, reduction='mean'):
return loss.mean() if reduction=='mean' else loss.sum() if reduction=='sum' else loss
class LabelSmoothingCrossEntropy(nn.Module):
def __init__(self, epsilon:float=0.1, reduction='mean'):
super().__init__()
self.epsilon = epsilon
self.reduction = reduction
def forward(self, preds, target):
n = preds.size()[-1]
log_preds = F.log_softmax(preds, dim=-1)
loss = reduce_loss(-log_preds.sum(dim=-1), self.reduction)
nll = F.nll_loss(log_preds, target, reduction=self.reduction)
return linear_combination(loss/n, nll, self.epsilon)
Menabrak hi-pri berdasarkan aktivitas
Komentar yang paling membantu
Ini alat saya