๐ŸŽ“ A1Learner
๐Ÿ”

PyTorch Label Smoothing

Label smoothing is a setting that softens the target inside cross-entropy loss. The correct class gets most of the target, and every class keeps a small share.

A hard target, softened

The middle class is the correct one here. Smoothing takes a small share from it and spreads the share over all classes.

eps = 0.1
classes = 3
hard = [0.0, 1.0, 0.0]
soft = [(1 - eps) * v + eps / classes for v in hard]
print([round(v, 4) for v in soft])

Turning smoothing on

One argument turns smoothing on. The rest of the training loop stays the same.

loss_fn = torch.nn.CrossEntropyLoss(label_smoothing=0.1)

The cost of full confidence

This prediction is correct and confident. The smoothed loss is still much larger, because the target itself is no longer 1.0.

import torch

logits = torch.tensor([[4.0, 0.0, 0.0]])
target = torch.tensor([0])
plain = torch.nn.CrossEntropyLoss()(logits, target)
smooth = torch.nn.CrossEntropyLoss(label_smoothing=0.1)(logits, target)
print(f"Plain: {plain.item():.4f}")
print(f"Smoothed: {smooth.item():.4f}")

Example

import torch

torch.manual_seed(0)
centers = torch.tensor([[2.0, 0.0], [-1.0, 2.0], [-1.0, -2.0]])
x = torch.cat([c + torch.randn(30, 2) * 0.6 for c in centers])
y = torch.cat([torch.full((30,), i) for i in range(3)])


def train(smoothing):
    torch.manual_seed(1)
    model = torch.nn.Linear(2, 3)
    optimizer = torch.optim.Adam(model.parameters(), lr=0.05)
    loss_fn = torch.nn.CrossEntropyLoss(label_smoothing=smoothing)
    for step in range(150):
        optimizer.zero_grad()
        loss = loss_fn(model(x), y)
        loss.backward()
        optimizer.step()
    with torch.no_grad():
        probs = torch.softmax(model(x), dim=1)
        confidence = probs.max(dim=1).values.mean().item()
    return loss.item(), confidence


for smoothing in [0.0, 0.1]:
    loss, confidence = train(smoothing)
    print(f"Smoothing {smoothing}: final loss {loss:.4f}, "
          f"mean confidence {confidence:.4f}")

Key points

  • Target: the answer the loss trains the model toward, written as a spread over classes
  • Use CrossEntropyLoss(label_smoothing=...) to turn smoothing on
  • Confidence: the largest class probability a model gives one example, read with max()
  • A hard target gives the correct class a full 1.0 and every other class 0.0
  • Smoothing 0.1 with 3 classes moves the correct target to 0.9333, as the first snippet shows
  • A smoothed target stops rewarding endless confidence, so the model stays a little unsure

In this example, 90 rows form three clusters and the targets name the clusters. A train function builds the same Linear model for each smoothing value. Smoothing 0 ends with loss 0.0110 and a mean confidence of 0.9894. Smoothing 0.1 ends with loss 0.3280 and a mean confidence of 0.9087. The higher smoothed loss is expected, because its target is softer. The confidence drop is the real effect. The smoothed model stops just short of certainty.

Note

Smoothed and plain losses use different targets, so their sizes are not a fair race.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Python Playground

Exercise

What does label smoothing change?