PyTorch torch.optim Optimizer-Modul

Optimierer sind Kernkomponenten im Deep Learning und dafür verantwortlich, Modellparameter auf Grundlage des Gradienten der Verlustfunktion anzupassen, wodurch sich das Modell schrittweise der optimalen Lösung nähert.

In PyTorch stellt das Modul torch.optim Implementierungen verschiedener Optimierungsalgorithmen bereit und ist ein unverzichtbares Werkzeug zum Trainieren neuronaler Netzwerke.


Warum Optimierer benötigt werden

Optimierer spielen im Deep Learning eine entscheidende Rolle und lösen das mühsame Problem der manuellen Aktualisierung von Parametern.

  • Automatische Parameteraktualisierungen: Manuelles Berechnen und Aktualisieren jedes Parameters ist sehr mühsam, und der Optimierer erledigt dies automatisch.
  • Beschleunigte Konvergenz: Die Verwendung von Optimierungsalgorithmen findet die optimale Lösung schneller als reiner Gradientenabstieg.
  • Lokale Optima vermeiden: Einige Optimierer können lokalen Optima entkommen.

Gängige Optimierer-Typen

Verschiedene Optimierer eignen sich für verschiedene Szenarien. Die Wahl des richtigen Optimierers kann Trainingsergebnisse erheblich verbessern.

Optimierer-NameHauptfunktionenGeeignete Szenarien
SGDEinfach und grundlegend, unterstützt MomentumGrundlegende Lehre, einfache Modelle, CNN
AdamAdaptive LernrateDie meisten Deep-Learning-Aufgaben
AdamWAdam + entkoppelter Weight DecayAufgaben, die L2-Regularisierung erfordern
RMSpropAdaptive LernrateRNN-Netzwerke, Spracherkennung
AdagradLernrate pro ParameterSparse-Daten, Textverarbeitung
AdadeltaAdaptive LernrateLangfristige Trainingsaufgaben

Zentrale Optimizer-API

Die Beherrschung des grundlegenden Nutzungsablaufs von Optimierern ist der erste Schritt im Deep Learning.

Grundlegender Nutzungsablauf

Die Verwendung von Optimierern folgt einem festen Muster: Instanz erstellen → Gradienten nullen → Backward-Pass → Parameter aktualisieren.

Beispiel

import torch
import torch.nn as nn
import torch.optim as optim

# 1. Ein einfaches Modell definieren
class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Linear(784, 10)

    def forward(self, x):
        return self.fc(x)

model = SimpleNet()

# 2. Optimizer-Instanz erstellen
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 3. Trainingsschleife
for epoch in range(epochs):
    # Vorwärtsdurchlauf
    outputs = model(inputs)
    loss = criterion(outputs, labels)

    # Rückwärtsdurchlauf
    optimizer.zero_grad()  # Den Gradienten-Cache auf Null setzen, um Gradientenakkumulation zu vermeiden
    loss.backward()        # Gradienten berechnen

    # Parameteraktualisierung
    optimizer.step()       # Parameter aktualisieren

Erklärung der wichtigsten Methoden

Der Optimierer bietet mehrere zentrale Methoden zur Verwaltung des Parameteraktualisierungsprozesses.

  • zero_grad(set_to_none=True): Setzt den Parameter-Gradienten-Cache auf null. Wenn auf True gesetzt, werden Gradienten auf None gesetzt, was mehr Speicher spart, als sie auf 0 zu setzen.
  • step(): Führt eine einzelne Parameteraktualisierung durch, wobei Modellparameter anhand von Gradienten und Lernrate aktualisiert werden.
  • state_dict(): Ruft das Zustands-Dictionary des Optimierers ab, das zum Speichern von Checkpoints verwendet werden kann.
  • load_state_dict(state_dict): Lädt den Optimiererzustand, um das Training fortzusetzen.
  • add_param_group(param_group): Fügt dynamisch eine Parametergruppe hinzu.

Hinweis: Sie müssen zero_grad() vor jedem Backward-Pass aufrufen, da sich sonst Gradienten ansammeln und zu instabilem Training führen. Es wird empfohlen, zero_grad(set_to_none=True) zu verwenden, um Speicher zu sparen.

Speichern und Laden des Optimiererzustands

Beim Fortsetzen des Trainings müssen sowohl der Modell- als auch der Optimiererzustand gespeichert werden.

Beispiel

# Checkpoint speichern (Modell, Optimierer und Scheduler zusammen speichern)
checkpoint = {
    'epoch': epoch,
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'scheduler_state_dict': scheduler.state_dict(),
    'loss': loss,
}
torch.save(checkpoint, 'checkpoint.pth')

# Checkpoint laden
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
start_epoch = checkpoint['epoch'] + 1

Detaillierte Erklärung gängiger Optimierer

SGD (Stochastic Gradient Descent)

SGD ist der grundlegendste Optimierungsalgorithmus; er aktualisiert Parameter, indem Gradienten aus einer einzelnen Stichprobe oder einem Mini-Batch von Stichproben berechnet werden. Er ist der Eckpfeiler der Deep-Learning-Optimierung, und viele fortgeschrittene Optimierer basieren auf SGD.

Beispiel

# Parameterbeschreibung des SGD-Optimierers
# params: Zu optimierende Parameter (normalerweise aus model.parameters())
# lr: Lernrate, steuert die Schrittweite der Parameteraktualisierungen, Standard 0.01
# momentum: Momentum-Faktor, wird verwendet, um die Konvergenz zu beschleunigen und Oszillationen zu reduzieren, Standard 0
# weight_decay: L2-Regularisierungskoeffizient, wird verwendet, um Overfitting zu verhindern, Standard 0
# dampening: Momentum-Dämpfung, steuert die Berechnung des Momentum-Terms, Standard 0
# nesterov: ob Nesterov-Momentum verwendet werden soll, Standard False
optimizer = optim.SGD(
    params=model.parameters(),
    lr=0.01,           # Lernrate
    momentum=0.9,      # Momentum-Faktor
    weight_decay=1e-4, # L2-Regularisierung
    nesterov=True      # Nesterov-Momentum aktivieren
)

Beschreibung der Kernparameter:

  • lr (float): Lernrate, steuert die Schrittweite der Parameteraktualisierungen
  • momentum (float): Momentum-Faktor, wird verwendet, um die Konvergenz zu beschleunigen und Oszillationen zu reduzieren, üblicher Wert 0.9
  • weight_decay (float): L2-Regularisierungskoeffizient, wird verwendet, um Überanpassung zu verhindern, üblicher Wert 1e-4
  • nesterov (bool): ob Nesterov-Momentum verwendet werden soll; die Aktivierung kann Oszillationen reduzieren

Funktionen:

  • Einfach zu implementieren, es ist der grundlegende Algorithmus der Deep-Learning-Optimierung
  • Das Hinzufügen eines Momentum-Terms kann die Konvergenz beschleunigen und die Trainingsstabilität verbessern
  • Langsamere Konvergenzgeschwindigkeit, aber die endgültige Genauigkeit kann höher sein
  • Geeignet als Benchmark für den Vergleich mit anderen Optimierern

Obwohl SGD einfach ist, kann es mit geeigneten Hyperparametern oft sehr gute Ergebnisse erzielen, was es zu einem guten Ausgangspunkt für das Erlernen von Optimierungsalgorithmen macht. Bei Bildklassifikationsaufgaben bleibt SGD mit Momentum eine gängige Wahl.

Adam (Adaptive Moment Estimation)

Adam ist einer der am häufigsten verwendeten Optimierer und kombiniert die Vorteile von Momentum und adaptiven Lernraten. Er passt die Lernrate für jeden Parameter adaptiv an, indem er Schätzungen des ersten und zweiten Moments der Gradienten berechnet.

Beispiel

# Parameterbeschreibung des Adam-Optimierers
# params: zu optimierende Parameter
# lr: Lernrate, Standardwert 0.001 (empfohlener Wert)
# betas: Koeffizienten des gleitenden Durchschnitts zur Berechnung von Gradienten und quadrierten Gradienten (beta1, beta2)
# beta1 steuert die Schätzung des ersten Moments (Momentum), Standardwert 0.9
# beta2 steuert die Schätzung des zweiten Moments (Varianz), Standardwert 0.999
# eps: Term für numerische Stabilität, verhindert Division durch null, Standardwert 1e-8
# weight_decay: L2-Regularisierungskoeffizient, Standardwert 0
# amsgrad: ob die AMSGrad-Variante verwendet werden soll, Standardwert False
optimizer = optim.Adam(
    params=model.parameters(),
    lr=0.001,                      # Es wird empfohlen, eine kleinere Lernrate zu verwenden
    betas=(0.9, 0.999),            # Häufig verwendete Momentum-Parameter
    eps=1e-8,                      # Term zur numerischen Stabilität
    weight_decay=1e-4,             # L2-Regularisierung
    amsgrad=False                  # Ob AMSGrad verwendet werden soll
)

Beschreibung der Kernparameter:

  • betas (Tuple[float, float]): steuern den exponentiellen gleitenden Durchschnitt von Gradienten und quadrierten Gradienten
  • eps (float): Term zur numerischen Stabilität, verhindert, dass der Nenner null wird
  • amsgrad (bool): ob die AMSGrad-Variante verwendet werden soll; ihre Verwendung kann Konvergenz garantieren

Funktionen:

  • Adaptive Lernrate: passt die Lernrate automatisch anhand des historischen Gradienten des Parameters an
  • Integriert das Momentum-Konzept: verwendet eine Schätzung des ersten Moments, um die Konvergenz zu beschleunigen
  • Hohe Robustheit: relativ unempfindlich gegenüber der Wahl der Hyperparameter
  • Schnelle Konvergenzgeschwindigkeit, geeignet für schnelles Prototyping

Adam ist die Standardwahl für die meisten Deep-Learning-Aufgaben, aber in bestimmten spezifischen Szenarien (wie GAN, Reinforcement Learning) muss man möglicherweise andere Optimierer ausprobieren.

AdamW(Adam mit Weight Decay)

AdamW ist eine verbesserte Version von Adam, die Weight Decay von Gradienten-Updates entkoppelt, was theoretisch förderlicher für die Konvergenz ist. In praktischen Anwendungen schneidet AdamW normalerweise besser ab als Adam.

Beispiel

# AdamW-Optimierer
# Hauptunterschied zu Adam: andere Implementierung von weight_decay
# Der Weight Decay von AdamW ist korrekter und wirkt sich nicht auf die Gradientenberechnung aus
optimizer = optim.AdamW(
    params=model.parameters(),
    lr=0.001,
    betas=(0.9, 0.999),
    weight_decay=0.01,    # Weight-Decay-Koeffizient, normalerweise größer eingestellt als bei Adam
    amsgrad=False
)

# Empfohlene Konfiguration: AdamW verwendet normalerweise einen weight_decay von 0.01
# Während Adam normalerweise 0.001 verwendet

Wenn Ihre Aufgabe Weight Decay (L2-Regularisierung) erfordert, wird dringend empfohlen, AdamW anstelle von Adam zu verwenden.

RMSprop

RMSprop ist ein Optimierer mit adaptiver Lernrate, der besonders für die Behandlung nicht-stationärer Ziele und rekurrenter neuronaler Netze geeignet ist.

Beispiel

# RMSprop-Optimierer
# Normalisiert die Lernrate durch Division durch den exponentiell gewichteten Durchschnitt der Gradienten
optimizer = optim.RMSprop(
    params=model.parameters(),
    lr=0.01,               # Lernrate
    alpha=0.99,            # Exponentielle Abklingrate für quadrierte Gradienten
    eps=1e-8,              # Term zur numerischen Stabilität
    weight_decay=0,        # L2-Regularisierung
    momentum=0,            # Momentum-Faktor
    centered=False         # Ob die Gradienten zentriert werden sollen
)

Adagrad

Adagrad eignet sich für die Verarbeitung von dünnbesetzten Daten; es passt die Lernrate adaptiv für jeden Parameter an.

Beispiel

# Adagrad-Optimierer
# Geeignet für die Optimierung dünnbesetzter Daten, verwendet eine kleinere Lernrate für häufig aktualisierte Parameter
optimizer = optim.Adagrad(
    params=model.parameters(),
    lr=0.01,               # Lernrate
    lr_decay=0,            # Lernratenabfall
    weight_decay=0,       # L2-Regularisierung
    initial_accumulator_value=0  # Anfangswert des Akkumulators
)

Fortgeschrittene Optimierer-Techniken

Lernratenplanung

Die Lernratenplanung ermöglicht es, die Lernrate während des Trainings dynamisch anzupassen, was häufig die Modellkonvergenz deutlich verbessern kann.

Beispiel: Mehrere Lernraten-Scheduler

from torch.optim.lr_scheduler import (
    StepLR,                # Stufenweiser Abfall
    MultiStepLR,           # Mehrstufiger Abfall
    ExponentialLR,         # Exponentieller Abfall
    CosineAnnealingLR,     # Kosinus-Annealing
    ReduceLROnPlateau,     # Automatische Anpassung anhand von Metriken
)

# Methode 1: StepLR - wird alle 30 Epochen verringert
optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)

# Methode 2: MultiStepLR - wird zu festgelegten Epochen verringert
optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = MultiStepLR(optimizer, milestones=[30, 60, 80], gamma=0.1)

# Methode 3: CosineAnnealingLR - Cosinus-Kurven-Annealing
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = CosineAnnealingLR(optimizer, T_max=50, eta_min=1e-6)

# Methode 4: ReduceLROnPlateau - passt sich automatisch anhand der überwachten Metrik an
optimizer = optim.Adam(model.parameters(), lr=0.001)
scheduler = ReduceLROnPlateau(
    optimizer, mode='min',     # Verlust überwachen
    factor=0.5,                 # Abklingfaktor
    patience=5,                 # Anzahl der Epochen zum Warten
    verbose=True                # Informationen ausgeben
)

# Trainingsschleife
for epoch in range(100):
    train_loss = train(...)
    val_loss = validate(...)

    # StepLR und andere Scheduler
    scheduler.step()

    # ReduceLROnPlateau erfordert, dass die überwachte Metrik übergeben wird
    scheduler.step(val_loss)

Der Lernraten-Scheduler muss zusammen mit dem Optimierer verwendet werden; step() muss nach optimizer.step() aufgerufen werden, andernfalls kann die Aktualisierung der Lernrate fehlerhaft sein.

Parametergruppen-Optimierung

Durch die Parametergruppierung können für verschiedene Schichten unterschiedliche Lernraten festgelegt werden, was insbesondere beim Transferlernen nützlich ist.

Beispiel

# Beispiel für Parametergruppierungs-Optimierung
# Unterschiedliche Lernraten für verschiedene Schichten festlegen
# Normalerweise: Das Backbone-Netzwerk verwendet eine kleinere Lernrate, und der Klassifizierungskopf verwendet eine größere Lernrate
optimizer = optim.SGD([
    {'params': model.base.parameters(), 'lr': 1e-3},      # Basisschichten: große Lernrate
    {'params': model.classifier.parameters(), 'lr': 1e-2} # Klassifizierungsschicht: große Lernrate
], lr=1e-4)  # Globale Standard-Lernrate (verwendet, wenn keine Parametergruppe angegeben ist)

# Gebräuchlichere Notation in praktischen Anwendungen
optimizer = optim.Adam([
    {'params': model.fc.parameters(), 'lr': 1e-3},       # Klassifizierungskopf
    {'params': [p for n, p in model.named_parameters()    # Backbone-Netzwerk
                if not n.startswith('fc')],
     'lr': 1e-5},
])

Gradienten-Clipping

Gradienten-Clipping kann Gradientenexplosionen verhindern und die Trainingsstabilität verbessern. Es ist besonders nützlich in tiefen Netzwerken wie RNN und LSTM.

Beispiel

import torch.nn as nn

# Beispiel für Gradienten-Clipping
# max_norm: die maximale Norm der Gradienten; Gradienten, die diesen Wert überschreiten, werden skaliert
# norm_type: Normtyp, Standard ist 2 (L2-Norm)
nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

# Position in der Trainingsschleife
for epoch in range(epochs):
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss.backward()

    # Gradienten nach loss.backward() und vor optimizer.step() clippen
    nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

    optimizer.step()

# Eine weitere Möglichkeit des Gradienten-Clippings: Clipping nach Wert
for param in model.parameters():
    if param.grad is not None:
        param.grad.data.clamp_(min=-1.0, max=1.0)

Gradienten-Clipping ist eine gängige Technik zum Trainieren tiefer neuronaler Netzwerke (insbesondere RNN, LSTM usw.) und kann Trainingsabstürze, die durch Gradientenexplosion verursacht werden, effektiv verhindern.

Gradientenakkumulation

Wenn der Grafikspeicher nicht ausreicht, kann die Gradientenakkumulation verwendet werden, um den Trainingseffekt einer großen Batchgröße zu simulieren.

Beispiel

# Beispiel für Gradientenakkumulation
# Tatsächliche batch_size = batch_size * accumulation_steps
accumulation_steps = 4   # 4 kleine Batches akkumulieren

optimizer.zero_grad()

for i, (inputs, labels) in enumerate(train_loader):
    outputs = model(inputs)
    loss = criterion(outputs, labels)

    # Den Verlust durch die Akkumulationsschritte teilen, um eine Mittelung zu erreichen
    loss = loss / accumulation_steps
    loss.backward()

    # Parameter nach der angegebenen Anzahl von Akkumulationsschritten einmal aktualisieren
    if (i + 1) % accumulation_steps == 0:
        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        optimizer.step()
        optimizer.zero_grad()

# Die verbleibenden Gradienten verarbeiten
if (i + 1) % accumulation_steps != 0:
    optimizer.step()
    optimizer.zero_grad()

Vollständiges Trainingsbeispiel

Das Folgende ist eine vollständige Trainingspipeline, die Best Practices für Optimierer demonstriert.

Beispiel

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim.lr_scheduler import CosineAnnealingLR

# Konfiguration
DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
EPOCHS = 100
BATCH_SIZE = 32
LR = 1e-3

# Modell erstellen und auf Gerät verschieben
model = SimpleNet().to(DEVICE)

# Verlustfunktion und Optimierer
criterion = nn.CrossEntropyLoss()
optimizer = optim.AdamW(model.parameters(), lr=LR, weight_decay=0.01)

# Lernraten-Scheduler (Cosinus-Annealing)
scheduler = CosineAnnealingLR(optimizer, T_max=EPOCHS, eta_min=1e-6)

# Trainingsschleife
best_acc = 0.0
for epoch in range(EPOCHS):
    model.train()
    total_loss = 0.0
    correct = 0

    for inputs, labels in train_loader:
        inputs = inputs.to(DEVICE)
        labels = labels.to(DEVICE)

        # Gradienten zurücksetzen (empfohlen: set_to_none=True)
        optimizer.zero_grad(set_to_none=True)

        # Vorwärtsdurchlauf
        outputs = model(inputs)
        loss = criterion(outputs, labels)

        # Rückwärtsdurchlauf
        loss.backward()

        # Gradientenclipping (Gradientenexplosion verhindern)
        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

        # Parameter aktualisieren
        optimizer.step()

        # Statistiken
        total_loss += loss.item()
        correct += (outputs.argmax(1) == labels).sum().item()

    # Lernrate aktualisieren
    scheduler.step()

    # Trainingsinformationen ausgeben
    avg_loss = total_loss / len(train_loader)
    accuracy = correct / len(train_loader.dataset)
    current_lr = scheduler.get_last_lr()[0]
    print(f"Epoche {epoch+1}/{EPOCHS} | Verlust: {avg_loss:.4f} | "
          f"Genauigkeit: {accuracy:.4f} | Lernrate: {current_lr:.6f}")

    # Bestes Modell speichern
    if accuracy > best_acc:
        best_acc = accuracy
        torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'scheduler_state_dict': scheduler.state_dict(),
            'best_acc': best_acc,
        }, 'best_model.pth')

print(f"Training abgeschlossen, beste Genauigkeit: {best_acc:.4f}")

Leitfaden zur Optimierer-Auswahl

Die Wahl des richtigen Optimierers muss anhand der konkreten Aufgabe, der Datenmerkmale und der Trainingsphase erfolgen.

Auswahl nach Aufgabe

Verschiedene Aufgabentypen haben unterschiedliche Optimierer-Empfehlungen.

AufgabentypEmpfohlener OptimiererEmpfohlene LernrateHinweise
Bildklassifizierung (CNN)SGD + Momentum0.01 ~ 0.1Langsame Konvergenz, aber hohe Genauigkeit
Bildklassifizierung (CNN)AdamW0.001Schnelle Konvergenz
NLP / TransformerAdamW1e-5 ~ 1e-4Kleinere Lernrate
RNN / LSTMRMSprop / Adam0.001Adaptive Lernrate
GANAdam (G) / Adam (D)0.0001Kleinere Lernrate
Bestärkendes LernenAdam / RMSprop0.0001 ~ 0.001Hängt von der jeweiligen Aufgabe ab
Schnelle ExperimentierungAdam / AdamW0.001Schnelle Konvergenz

Leistungsvergleich

OptimiererKonvergenzgeschwindigkeitSpeichernutzungHyperparameter-EmpfindlichkeitEndgültige Genauigkeit
SGD + MomentumLangsamNiedrigHochHoch
AdamSchnellMittelNiedrigMittel
AdamWSchnellMittelNiedrigHoch
RMSpropMittelMittelMittelMittel
AdagradMittelHochMittelNiedrig

Häufige Probleme und Lösungen

  • Instabiles Training (Loss-Oszillation): Lernrate reduzieren, Gradientenclipping hinzufügen
  • Konvergenz zu langsam: Adam oder AdamW verwenden, Lernrate erhöhen
  • Overfitting: weight_decay erhöhen, Regularisierung verwenden
  • Unzureichender Videospeicher: batch_size reduzieren, Gradientenakkumulation verwenden

Die Wahl des Optimierers ist nicht absolut; es wird empfohlen, mit Adam oder AdamW zu beginnen und andere Optimierer in Betracht zu ziehen, wenn die Ergebnisse nicht zufriedenstellend sind. Für spezifische Aufgaben sind möglicherweise Experimente erforderlich, um die optimale Wahl zu bestimmen.

Für ausführlichere Informationen sieheoffizielle PyTorch-Dokumentation。

Weitere Erweiterungen