Pytorch: [PyTorch] [طلب الميزة] Label Smoothing for CrossEntropyLoss

تم إنشاؤها على ١٠ مايو ٢٠١٨  ·  22تعليقات  ·  مصدر: pytorch/pytorch

اهلا ياجماعة. نوع الهدف torch.LongTensor سيعيق التنفيذ مثل بعض الطرق في المرجع . فهل هناك إمكانية لإضافة Arg: label_smoothing مقابل torch.nn.CrossEntropyLoss() ، أو ربما ببساطة قم بإضافة المستندات لإظهار كيفية تحويل target إلى one-hot vector للعمل معها torch.nn.CrossEntropyLoss() معًا ، أم أي طرق أخرى بسيطة؟ شكرا.

سم مكعبezyanggchanan @ zou3519bdhirshalbanDmruberry

enhancement high priority loss nn triage review triaged

التعليق الأكثر فائدة

هنا هو تطبيق بلدي

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))

ال 22 كومينتر

تضمين التغريدة
بالنسبة لـ label_smoothing ، تنظر القط إلى تطبيق NJUNMT-pytorch

في الفصل NMTCritierion

https://github.com/whr94621/NJUNMT-pytorch/blob/aff968c0da9273dc42eabbb8ac4e459f9195f6e4/src/modules/criterions.py#L131

انظر https://discuss.pytorch.org/t/cross-entropy-with-one-hot-targets/13580/5. يجب أن تعمل الوظيفة cross_entropy() التي تظهر هناك مع تسميات متجانسة لها نفس أبعاد مخرجات الشبكة.

لا أعتقد أن CrossEntropyLoss() يجب أن يدعم خيار label_smoothing ، نظرًا لأن تجانس الملصق يمكن أن يتم بعدة طرق مختلفة ويمكن أن يتم التنعيم نفسه يدويًا بسهولة بواسطة المستخدم. لكنني أوافق على أنه يجب على الأقل ذكره في المستندات حول كيفية التعامل مع الأهداف التي لا يمكن تمثيلها بقيم عددية ، أو إضافة دعم لتمرير الأهداف (k-hot / السلس) إلى CrossEntropyLoss .

ربما نحتاج شيئًا مثل NonSparseCrossEntropy ؟ (حسنًا .. من الصعب تسميتها)

هنا هو تطبيق بلدي

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))

أنا أتفق معmdraw
الخيار الجيد هو القيام بذلك في خطوتين:

  1. استخدم وظيفة للحصول على تسمية ناعمة
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
  1. اجعل CrossEntropyLoss يدعم أهداف k-hot / المتجانسة.

ثم يمكننا استخدامه مثل

Loss = CrossEntropyLoss(NonSparse=True, ...)
. . .
data = ...
labels = ...

outputs = model(data)

smooth_label = smooth_one_hot(labels, ...)
loss = (outputs, smooth_label)
...

بالمناسبة اختبرت تطبيقي على ImageNet ، يبدو الأمر جيدًا

| النموذج | العصور | نوع dtype | حجم الدفعة * | gpus | lr | الحيل | top1 / top5 | تحسين |
|: ----: |: -----: |: -----: |: ---------: |: ----: |: ---: |: ------: |: ---------: |: ------: |
| resnet50 | 120 | FP16 | 128 | 8 | 0.4 | - | 77.35 / - | خط الأساس |
| resnet50 | 120 | FP16 | 128 | 8 | 0.4 | تجانس Lable | 77.78 / 93.80 | +0.43 |

أعتقد أن @ zhangguanheng66 قال أن هذا شيء قد يكون قادرًا على النظر إليه في المستقبل.

فقط استخدم torch.nn.KLDivLoss. نفس الشيء.


التحديث: ليس هو نفسه.

أعتقد أن هذا مشابه لما طبقه Snorkel lib الجديد:
https://snorkel.readthedocs.io/en/master/packages/_autosummary/classification/snorkel.classification.cross_entropy_with_probs.html

فقط بعض المعلومات الإضافية حول كيفية تعامل الأشخاص مع هذه المشكلة

راجع https://github.com/NVIDIA/DeepLearningExamples/tree/master/PyTorch/Classification/RN50v1.5 للتعرف على كيفية قيام Nvidia بهذا الأمر الذي قد يساعد؟

suanrong شكرا جزيلا.

====
وربما يكون هذا مفيدًا للآخرين الذين قرأوا هذا العدد

لاحظ أن الانتروبيا المتقاطعة للتسميات غير 0/1 ليست متماثلة ، مما قد يكون تفسيرًا لضعف الأداء.
https://discuss.pytorch.org/t/cross-entropy-for-soft-label/16093/2

التنفيذ المقترح:

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

لقد تحققت مما يلي:
(1) عند التنعيم = 0.0 ، يكون الناتج هو نفسه nn.CrossEntropyLoss ضمن الدقة 1e-5 .
(2) عند التسوية> 0.0 ، فإن مجموع الأوزان على الفئات المختلفة weight.sum(dim=-1) تكون دائمًا 1.

تطبيقات هنا تفتقر ميزة الأوزان الصفية.
((

فقط استخدم torch.nn.KLDivLoss. نفس الشيء.

هل يمكنك توضيح المزيد من فضلك

فقط استخدم torch.nn.KLDivLoss. نفس الشيء.

هل يمكنك توضيح المزيد من فضلك

بافتراض أن لديك بالفعل ملصق مصقول ، يمكنك فقط استخدام torch.nn.KLDivLoss لأن الفرق بينهما هو إنتروبيا الملصق وهو ثابت.

PistonY لماذا لا تستخدم هذه الطريقة في

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

تطبيقات هنا تفتقر ميزة الأوزان الصفية.

هل يمكنني ضرب أوزان الفصل على موتر الملصق المصقول؟

def Smooth_one_hot (true_labels: torch.Tensor ، الفئات: int ، التنعيم = 0.0):
""
إذا كان التنعيم == 0 ، فهذه طريقة واحدة ساخنة
إذا كانت 0 <التنعيم <1 ، فهي طريقة سلسة

"""
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

""

تكمن مشكلة هذا التطبيق في أنه حساس جدًا لعدد الفئات

عندما تكون n_classes 2 ، فإن أي تجانس أعلى من 0.5 سيعكس الملصقات ، وأنا متأكد من أن الشخص لا يريده ؛ عندما تكون n_classes 3 فذلك يعني تجانس أعلى من 2/3 و 0.75 لـ 4 فصول. لذلك ربما:

assert 0 <= smoothing < (classes-1)/classes سيواجه هذه المشكلة ، لكني أشعر أن التنعيم يحتاج إلى أخذ عدد الفئات في الاعتبار؟

def Smooth_one_hot (true_labels: torch.Tensor ، الفئات: int ، التنعيم = 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

""

تكمن مشكلة هذا التطبيق في أنه حساس جدًا لعدد الفئات

عندما تكون n_classes 2 ، فإن أي تجانس أعلى من 0.5 سيعكس الملصقات ، وأنا متأكد من أن الشخص لا يريده ؛ عندما تكون n_classes 3 فذلك يعني تجانس أعلى من 2/3 و 0.75 لـ 4 فصول. لذلك ربما:

assert 0 <= smoothing < (classes-1)/classes سيواجه هذه المشكلة ، لكني أشعر أن التنعيم يحتاج إلى أخذ عدد الفئات في الاعتبار؟

أعتقد أنها فكرة حكيمة.

شكرا على المناقشة. هناك بعض النقاط التي لا تزال غير واضحة ويبدو لي أنها أخطاء:

  • موتر الوزن في تطبيقPistonY
  • التكافؤ بين اختلاف KL وتجانس الملصق ( suanrong )

عن الأوزان:

تنص ورقة تجانس الملصق على y_k = smoothing / n_classes + (1 - smoothing) * y_{one hot} . لذا فإن قيمة الوزن هي smoothing / n_classes للمؤشرات بخلاف الهدف ، وهي smoothing / n_classes + (1 - smoothing) للفئة المستهدفة. ومع ذلك ، في تطبيقPistonY ، تقوم الوظيفة torch.scatter_ بالكتابة فوق قيمة الهدف إلى (1 - smoothing) (ويختفي المصطلح الثابت).
علاوة على ذلك ، لا أفهم حقًا سبب استخدامنا n_classes -= 1 في الحساب (؟)

حول التكافؤ بين اختلاف KL وتجانس التسمية:

تقرأ خسارة الانتروبيا عبر الملصق ، مع y الأوزان المذكورة أعلاه ،

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)

حيث يستخدم السطر الثالث إلى الرابع حقيقة أن sum_k y[k] = smoothing / n_classes * n_classes + (1 - smoothing) = 1 .

تقرأ خسارة التباعد KL ،

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.

لذا في النهاية لدينا LS(x, y) = KL(x, y) + log-sum-exp(x) + Const. ، حيث Const. هو المصطلح الثابت المقابل للإنتروبيا y ، وهو بالفعل ثابت في إعدادات الطبقات المتعددة. ولكن ماذا عن مصطلح log-sum-exp؟

لقد أجريت بعض العمليات الحسابية باستخدام دالة إنتروبيا مخصصة تقبل الأهداف اللينة ، وتوضح أنها تساوي بالفعل خسارة KLDiv بالإضافة إلى log-sum-exp ، حتى المصطلح الثابت المقابل لنتروبيا y . هل هناك أي افتراض في السجلات يجعل من المعقول إسقاط هذا المصطلح؟

شكرا جزيلا على التوضيحات.
هتافات !

شكرا antrec !

انت على حق. لقد تجاهلت وظيفة logsoftmax وأخطأت.

تنفيذ تسمية تنعيم دالة فقدان الانتروبيا:

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)

الارتطام إلى hi-pri بناءً على النشاط

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات