PyTorch-Lernratenplaner

Die Lernrate (Learning Rate) ist einer der wichtigsten Hyperparameter beim Training neuronaler Netze. Ist die Lernrate zu groß, oszilliert das Training oder divergiert sogar; ist sie zu klein, ist die Konvergenz extrem langsam und es ist leicht, in lokale Optima zu geraten.

Lernraten-SchedulerDurch die dynamische Anpassung der Lernrate während des Trainings gleicht er schnelle Konvergenz in der frühen Phase und Feinabstimmung in der späten Phase aus.


1. Grundlegende Konzepte und Nutzungsmuster

Standard-Nutzungsablauf

)

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

model     = nn.Linear(10, 1)
optimizer = optim.SGD(model.parameters(), lr=0.1)

# 1. Scheduler erstellen und den Optimizer übergeben
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, Gamma=0.1)

for epoch in range(100):
    # 2. Trainieren
    train(model, optimizer)

    # 3. Nach jeder Epoche scheduler.step() aufrufen
    scheduler.step()

    # 4. Aktuelle Lernrate anzeigen
    current_lr = scheduler.get_last_lr()[0]
    print(f"Epoche {epoch+1}, LR: {current_lr:.6f}")

Wann step() aufgerufen werden sollte

Beispiel

# Richtig: zuerst optimizer.step(), danach scheduler.step()
optimizer.step()
scheduler.step()

# Falsch: scheduler.step() vor optimizer.step()
# Nach PyTorch 1.1.0 wird eine Warnung erzeugt, und einige Scheduler werden sich ungewöhnlich verhalten
scheduler.step()
optimizer.step()

Hinweis:optimizer.step()Muss seinscheduler.step()zuvor aufgerufen.

Anzeigen und Speichern des Lernratenstatus

Beispiel

# Aktuelle Lernrate abrufen
current_lr = optimizer.param_groups[0]['lr']
current_lr = scheduler.get_last_lr()[0]   # Die LR nach dem vorherigen step()

# Checkpoint speichern (der Scheduler-Zustand muss ebenfalls gespeichert werden)
torch.save({
    'epoch': epoch,
    'model': model.state_dict(),
    'optimizer': optimizer.state_dict(),
    'scheduler': scheduler.state_dict(),   # Nicht verpassen
}, 'checkpoint.pth')

# Checkpoint wiederherstellen
ckpt = torch.load('checkpoint.pth')
model.load_state_dict(ckpt['model'])
optimizer.load_state_dict(ckpt['optimizer'])
scheduler.load_state_dict(ckpt['scheduler'])

2. Scheduler mit festem Decay

2.1 StepLR Schrittweiser Abfall

Nach jeweils einer festenstep_sizeAnzahl von Epochen multipliziere die Lernrate mitgamma. Er ist einer der einfachsten und am häufigsten verwendeten Scheduler.

Formel: lr = lr_base * gamma^(floor(step / step_size))

Beispiel

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.StepLR(
    optimizer,
    step_size=30,   # Alle 30 Epochen verringern
    gamma=0.1       # Jedes Mal mit 0.1 multiplizieren (d. h. auf 1/10 des ursprünglichen Werts reduzieren)
)

# LR-Änderungen:
# Epoche 0-29: 0.1
# Epoche 30-59: 0.01
# Epoche 60-89: 0.001
# Epoche 90+: 0.0001

Anwendbare Szenarien:Aufgaben mit einem festen Trainingsrhythmus und klaren Phasen, wie das ResNet-Training auf ImageNet (90 Epochen, mit Reduzierung in der 30. und 60. Epoche).


2.2 MultiStepLR Multi-Milestone-Zerfall

Die Lernrate bei mehreren festgelegten Epochen (Meilensteinen) absenken, flexibler als StepLR.

Beispiel

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.MultiStepLR(
    optimizer,
    milestones=[30, 60, 80],   # Zerfall bei der 30., 60. und 80. Epoche
    Gamma=0.1
)

# LR-Änderungen:
# Epoche 0-29: 0.1
# Epoche 30-59: 0.01
# Epoche 60-79: 0.001
# Epoche 80+: 0.0001

Anwendbare Szenarien:Wenn bekannt ist, in welchen Epochen das Modell eine Feinabstimmung benötigt, wie etwa die feine Konvergenzphase in der mittleren und späten Phase von Klassifikationsmodellen.


2.3 ExponentialLR Exponentieller Zerfall

Die Lernrate zerfällt in jeder Epoche und nimmt weiter exponentiell ab, was einen glatteren Zerfall ergibt.

Formel: lr = lr_base * gamma^epoch

Beispiel

optimizer = optim.Adam(model.parameters(), lr=0.01)
Scheduler = optim.lr_scheduler.ExponentialLR(
    optimizer,
    gamma=0.95   # Jede Epoche mit 0.95 multiplizieren
)

# LR-Änderungen (erste 5 Epochen):
# Epoche 1: 0.0100
# Epoche 2: 0.0095
# Epoche 3: 0.0090
# Epoche 4: 0.0086
# Epoche 5: 0.0081

gammaWenn ein zu kleiner Wert eingestellt wird (z. B. 0.5), geht die Lernrate schnell gegen null; üblicherweise wird ein Wert zwischen 0.9 und 0.99 eingestellt.


3. Adaptive Scheduler

3.1 ReduceLROnPlateau Metrikbasierte Verringerung

Einer der intelligentesten Scheduler: überwacht eine bestimmte Metrik (z. B. Validierungsverlust) und reduziert automatisch die Lernrate, wenn die Metrik sich nicht mehr verbessert. Es ist nicht nötig, im Voraus zu wissen, in welcher Epoche verringert werden soll.

Beispiel

optimizer = optim.Adam(model.parameters(), lr=0.01)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer,
    mode='min',       # 'min': je kleiner der überwachte Wert, desto besser (Verlust); 'max': je größer, desto besser (Genauigkeit)
    factor=0.1,       # Wenn ausgelöst, gilt lr = lr × factor
    patience=10,      # Anzahl der Epochen, in denen die Metrik stagnieren darf; wird sie überschritten, verringern
    threshold=1e-4,   # Eine Verbesserung, die kleiner als dieser Wert ist, gilt als keine Verbesserung
    min_lr=1e-6,      # Untergrenze der Lernrate; sie wird nicht unter diesen Wert fallen
    verbose=True      # Informationen zur Verringerung ausgeben
)

for epoch in range(100):
    train_loss = train(model, optimizer)
    val_loss   = evaluate(model)

    # Anders als bei anderen Schedulern wird hier die überwachte Metrik übergeben
    scheduler.step(val_loss)

Schreibweise zur Überwachung von Accuracy:

Beispiel

scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer,
    mode='max',       # Je größer die Accuracy, desto besser
    factor=0.5,
    patience=5,
)

scheduler.step(val_accuracy)

Anwendbare Szenarien:Die standardmäßige erste Wahl für fast alle Aufgaben, insbesondere wenn Sie sich nicht sicher sind, wie viele Epochen trainiert werden sollen oder wenn das Training instabil ist.


3.2 CosineAnnealingLR Kosinus-Annealing

Die Lernrate folgt einerKosinus-Kurvevom Anfangswert sanft bis zum Minimalwert (eta_min), wodurch abrupte Änderungen des Step-Decay vermieden werden.

Formel: lr_t = eta_min + 0.5 * (eta_max - eta_min) * (1 + cos(t * pi / T_max)

Beispiel

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.CosineAnnealingLR(
    optimizer,
    T_max=100,     # Länge eines halben Zyklus (normalerweise auf die Gesamtzahl der Epochen gesetzt)
    eta_min=1e-6   # Minimale Lernrate (Standard 0)
)

# LR-Änderungsverlauf (schematisch bei T_max=10):
# 0.1 -> 0.095 -> 0.079 -> 0.055 -> 0.026 -> 0.001
# (sanfter Abstieg der Kosinuskurve)

Anwendbare Szenarien:Szenarien mit einer festen Anzahl von Trainingsepochen, weit verbreitet in Veröffentlichungen wie Vision Transformer und ResNet, mit guter Konvergenzqualität.


3.3 CosineAnnealingWarmRestarts

Die verbesserte Version des Kosinus-Annealings unterstütztperiodische Warm Restarts(Warm Restarts): Nach Ende jedes Zyklus wird die Lernrate auf den Anfangswert zurückgesetzt und beginnt eine neue Runde des Kosinus-Decays. Dadurch kann das Modell lokale Optima verlassen.

Beispiel

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(
    optimizer,
    T_0=10,       # Länge des ersten Zyklus (Anzahl der Epochen)
    T_mult=2,     # Multiplikator für die Zykluslänge nach jedem Neustart (1=gleiche Länge, 2=schrittweise zunehmend)
    eta_min=1e-6
)

# Zykluslängen bei T_mult=2: 10 -> 20 -> 40 -> 80 ...
# LR-Änderungen (T_0=10, T_mult=1):
# 0.1 -> ... -> 0 -> 0.1 -> ... -> 0 -> 0.1 (Neustart alle 10 Epochen)

Anwendbare Szenarien:Beim Training großer Modelle oder wenn das Modell aus mehreren Konvergenzpunkten den besten auswählen soll, lässt es sich effektiv mit Snapshot Ensemble einsetzen.


4. Warmup-Scheduler

Warmupbezeichnet das schrittweise Erhöhen der Lernrate von einem sehr kleinen Wert auf den Zielwert innerhalb der ersten Trainingsschritte. Training mit großer Batch-Größe und Transformer-ähnliche Modelle benötigen fast alle Warmup; andernfalls sind die anfänglichen Gradienten-Updates zu aggressiv und das Modell lässt sich nur schwer stabilisieren.

4.1 LinearLR Lineare Zeitplanung

Ändert die Lernrate linear innerhalb einer angegebenen Anzahl von Epochen (kann für lineares Warmup oder lineare Abnahme verwendet werden).

Beispiel

optimizer = optim.Adam(model.parameters(), lr=0.01)

# Lineares Warmup: In den ersten 5 Epochen steigt lr linear von 0.01×0.1=0.001 auf 0.01
warmup_scheduler = optim.lr_scheduler.LinearLR(
    optimizer,
    start_factor=0.1,   # anfängliche lr = base_lr × start_factor
    end_factor=1.0,     # finale lr = base_lr × end_factor
    total_iters=5       # Nach 5 Epochen abgeschlossen
)

# LR-Änderungen: 0.001 -> 0.003 -> 0.005 -> 0.007 -> 0.009 -> 0.01

4.2 ConstantLR Konstante Phase

Innerhalb einer bestimmten Anzahl von Epochen wird die Lernrate festgelegt aufbase_lr × factor, und danach wird der ursprüngliche Wert wiederhergestellt.

Beispiel

# Verwende base_lr × 0.5 für die ersten 5 Epochen und setze dann auf den Normalwert zurück.
scheduler = optim.lr_scheduler.ConstantLR(
    optimizer,
    factor=0.5,
    total_iters=5
)

4.3 SequentialLR Kombinierte Zeitplanung

Kombiniere mehrere Schedulersequenziell, was der Standardansatz zur Implementierung der "Warmup + Decay"-Kombinationsstrategie ist.

Beispiel

optimizer = optim.Adam(model.parameters(), lr=0.01)

# Phase 1: Warmup (erste 5 Epochen, LR steigt linear von 0.001 auf 0.01)
warmup = optim.lr_scheduler.LinearLR(
    optimizer, start_factor=0.1, end_factor=1.0, total_iters=5
)

# Phase 2: Kosinus-Annealing (verbleibende 95 Epochen)
cosine = optim.lr_scheduler.CosineAnnealingLR(
    optimizer, T_max=95, eta_min=1e-6
)

# Kombiniert: zuerst Warmup ausführen, nach der 5. Epoche auf Cosinus umschalten
scheduler = optim.lr_scheduler.SequentialLR(
    optimizer,
    schedulers=[warmup, cosine],
    milestones=[5]          # Umschalten bei der 5. Epoche
)

# Die Verwendung ist genau dieselbe wie bei einem normalen Scheduler
for epoch in range(100):
    train(...)
    scheduler.step()

Anwendbare Szenarien:Standardkonfiguration für das Transformer-Training (Warmup + Cosine Annealing); BERT-, GPT- und ViT-Pretraining verwenden allesamt diese Strategie.


5. Zyklische Scheduler

5.1 CyclicLR Zyklische Lernrate

Die Lernratebase_lrundmax_lrzwischen denZyklen periodisch, was dem Modell hilft, einen größeren Parameterraum zu erkunden und Sattelpunkten zu entkommen.

Beispiel

optimizer = optim.SGD(model.parameters(), lr=0.01)
scheduler = optim.lr_scheduler.CyclicLR(
    optimizer,
    base_lr=0.001,          # Untere Grenze der Lernrate
    max_lr=0.01,            # Obere Grenze der Lernrate
    step_size_up=2000,      # Anzahl der Iterationen, um von base_lr auf max_lr zu steigen
    step_size_down=2000,    # Anzahl der Iterationen, um von max_lr auf base_lr zu fallen (standardmäßig step_size_up)
    mode='triangular',      # Dreieckszyklus (konstante Amplitude)
    # mode='triangular2' # Die Amplitude halbiert sich pro Zyklus
    # mode='exp_range' # exponentieller Amplitudenabfall
)

# CyclicLR wird pro Schritt (Batch) aufgerufen, nicht pro Epoche
for epoch in range(num_epochs):
    for inputs, labels in train_loader:
        optimizer.zero_grad()
        loss = criterion(model(inputs), labels)
        loss.backward()
        optimizer.step()
        scheduler.step()    # Nach jedem Batch aufrufen

Vergleich der drei Modi:

modeAmplitudenänderungEigenschaft
triangularKonstanteStabile Exploration, geeignet für frühe Phasen
triangular2Halbiert sich pro ZyklusErst erkunden, dann konvergieren
exp_rangeExponentieller ZerfallKonvergiert schließlich sanft

5.2 OneCycleLR One-Cycle-Strategie

Einer der leistungsstärksten Scheduler, vorgeschlagen von fastai als 1-Cycle-Policy. Das gesamte Training hat nur einen Zyklus: Die Lernrate steigt zuerst an und fällt dann, und das Momentum ändert sich in die entgegengesetzte Richtung.

Das Training ist schneller und benötigt typischerweise nur1/5~1/10die Anzahl der Epochen des traditionellen Trainings.

Beispiel

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

steps_per_epoch = len(train_loader)

scheduler = optim.lr_scheduler.OneCycleLR(
    optimizer,
    max_lr=0.1,                              # Maximale Lernrate
    steps_per_epoch=steps_per_epoch,         # Anzahl der Schritte pro Epoche
    epochs=10,                               # Gesamtzahl der Epochen
    pct_start=0.3,                           # Erste 30% werden für den Warmup-Anstieg verwendet
    anneal_strategy='cos',                   # Abklingstrategie ('cos' oder 'linear')
    div_factor=25,                           # Anfängliche lr = max_lr / div_factor
    final_div_factor=1e4                     # Endgültige lr = max_lr / final_div_factor
)

# Anfängliche lr = 0.1 / 25 = 0.004
# Spitzen-lr = 0.1 (bei 30%)
# Endgültige lr = 0.1 / 10000 = 0.00001

# Wird auch pro Batch genannt
for epoch in range(10):
    for inputs, labels in train_loader:
        optimizer.zero_grad()
        loss = criterion(model(inputs), labels)
        loss.backward()
        optimizer.step()
        scheduler.step()    # Nach jedem Batch aufrufen

Anwendbare Szenarien:Erste Wahl, wenn Rechenressourcen begrenzt sind und Ideen schnell validiert werden müssen;max_lrEs wird empfohlen, das LR-Finder-Tool zu verwenden, um den optimalen Wert zu bestimmen.


6. Benutzerdefinierte Scheduler

6.1 Funktionale Anpassung von LambdaLR

Passe die Scheduling-Strategie vollständig an, indem du eine Lambda-Funktion übergibst (die die Epochennummer annimmt und einen Lernraten-Multiplikator zurückgibt).

Beispiel

optimizer = optim.Adam(model.parameters(), lr=0.01)

# Beispiel 1: Klassische Warmup-Strategie für Transformer
# lr ∝ min(step^-0.5, step × warmup_steps^-1.5)
def transformer_lr(epoch, warmup_epochs=10, d_model=512):
    if epoch == 0:
        return 1e-7 / 0.01          # Division durch null vermeiden
    step = epoch + 1
    warmup = warmup_epochs
    return (d_model ** -0.5) * min(step ** -0.5, step * warmup ** -1.5) / 0.01

scheduler = optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=transformer_lr)


# Beispiel 2: polynomiale Abnahme (lr sinkt linear/polynomiell vom Anfangswert auf 0)
def polynomial_decay(epoch, total_epochs=100, power=1.0):
    return max((1 - epoch / total_epochs) ** power, 0.0)

scheduler = optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=polynomial_decay)


# Beispiel 3: gruppierte Lernraten (verschiedene Strategien für verschiedene Parametergruppen)
optimizer = optim.Adam([
    {'params': model.backbone.parameters(), 'lr': 1e-4},
    {'params': model.head.parameters(),     'lr': 1e-3},
])

# Eine Lambda-Funktion pro Parametergruppe
scheduler = optim.lr_scheduler.LambdaLR(
    optimizer,
    lr_lambda=[
        lambda epoch: 0.95 ** epoch,    # backbone: langsamer Zerfall
        lambda epoch: 0.85 ** epoch,    # head: schneller Zerfall
    ]
)

6.2 LRScheduler erben

Wenn komplexere Logik benötigt wird, erbenLRScheduler(PyTorch >= 2.0, in älteren Versionen_LRScheduler) um vollständige Anpassung zu implementieren:

Beispiel

from torch.optim.lr_scheduler import LRScheduler
import math

class WarmupCosineScheduler(LRScheduler):
    """
Warmup + Cosine Annealing kombinierter Scheduler (manuell implementierte Version)
- Lineares Warmup für die ersten warmup_epochs Epochen
- Danach Cosine Annealing bis min_lr
    """

    def __init__(self, optimizer, warmup_epochs, total_epochs,
                 min_lr=1e-6, last_epoch=-1):
        self.warmup_epochs = warmup_epochs
        self.total_epochs  = total_epochs
        self.min_lr        = min_lr
        super().__init__(optimizer, last_epoch)

    def get_lr(self):
        epoch = self.last_epoch

        # Aufwärmphase: lineare Erhöhung
        if epoch < self.warmup_epochs:
            warmup_factor = (epoch + 1) / self.warmup_epochs
            return [base_lr * warmup_factor for base_lr in self.base_lrs]

        # Kosinus-Annealing-Phase
        progress = (epoch - self.warmup_epochs) / (
            self.total_epochs - self.warmup_epochs
        )
        cosine_factor = 0.5 * (1 + math.cos(math.pi * progress))
        return [
            self.min_lr + (base_lr - self.min_lr) * cosine_factor
            for base_lr in self.base_lrs
        ]


optimizer = optim.Adam(model.parameters(), lr=0.01)
scheduler = WarmupCosineScheduler(
    optimizer,
    warmup_epochs=10,
    total_epochs=100,
    min_lr=1e-6
)

7. Vergleich der Scheduler-Visualisierung

Der folgende Code kann die LR-Änderungskurven der einzelnen Scheduler zum intuitiven Vergleich darstellen:

Beispiel

import torch
import torch.optim as optim
import matplotlib.pyplot as plt

def simulate_lr(scheduler_fn, epochs=100, steps_per_epoch=None):
    """Simuliere und protokolliere die Änderungen der Lernrate des Schedulers"""
    model     = torch.nn.Linear(1, 1)
    optimizer = optim.SGD(model.parameters(), lr=0.1)
    scheduler = scheduler_fn(optimizer)
    lrs = []

    if steps_per_epoch:
        # Scheduler wird pro Schritt aufgerufen
        for _ in range(epochs):
            for _ in range(steps_per_epoch):
                optimizer.step()
                scheduler.step()
                lrs.append(optimizer.param_groups[0]['lr'])
    else:
        # Scheduler wird pro Epoche aufgerufen
        for _ in range(epochs):
            optimizer.step()
            scheduler.step()
            lrs.append(optimizer.param_groups[0]['lr'])
    return lrs

schedulers = {
    'StepLR(step=30, gamma=0.1)':
        lambda opt: optim.lr_scheduler.StepLR(opt, 30, 0.1),
    'CosineAnnealingLR':
        lambda opt: optim.lr_scheduler.CosineAnnealingLR(opt, T_max=100),
    'ExponentialLR(gamma=0.95)':
        lambda opt: optim.lr_scheduler.ExponentialLR(opt, 0.95),
    'CosineWarmRestarts(T0=25)':
        lambda opt: optim.lr_scheduler.CosineAnnealingWarmRestarts(opt, T_0=25),
}

plt.figure(figsize=(12, 5))
for name, fn in schedulers.items():
    plt.plot(simulate_lr(fn), label=name)

plt.xlabel('Epoche')
plt.ylabel('Lernrate')
plt.title('PyTorch LR-Scheduler-Vergleich')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('lr_schedulers.png', dpi=150)
plt.show()

8. Vollständige Trainingsvorlage

Eine produktionsreife Trainingsvorlage, die Warmup + Cosine Annealing, Modellspeicherung und -wiederherstellung integriert:

Beispiel

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim.lr_scheduler import SequentialLR, LinearLR, CosineAnnealingLR

# Hyperparameter
EPOCHS       = 100
WARMUP_EPOCHS = 5
BASE_LR      = 1e-3
MIN_LR       = 1e-6
SAVE_PATH    = 'best_model.pth'

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# Modell / Optimierer / Scheduler
model     = MyModel().to(device)
optimizer = optim.AdamW(model.parameters(), lr=BASE_LR, weight_decay=1e-4)
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)

# Warmup 5 Epochen + Kosinus-Annealing 95 Epochen
warmup_sched = LinearLR(optimizer, start_factor=0.1, total_iters=WARMUP_EPOCHS)
cosine_sched = CosineAnnealingLR(optimizer, T_max=EPOCHS - WARMUP_EPOCHS, eta_min=MIN_LR)
scheduler    = SequentialLR(optimizer, [warmup_sched, cosine_sched], milestones=[WARMUP_EPOCHS])

# Checkpoint wiederherstellen
start_epoch = 0
best_acc    = 0.0

try:
    ckpt = torch.load(SAVE_PATH, map_location=device)
    model.load_state_dict(ckpt['model'])
    optimizer.load_state_dict(ckpt['optimizer'])
    scheduler.load_state_dict(ckpt['scheduler'])
    start_epoch = ckpt['epoch'] + 1
    best_acc    = ckpt['best_acc']
    print(f"Fortgesetzt ab Epoche {start_epoch}, beste Genauigkeit {best_acc:.4f}")
except FileNotFoundError:
    print("Training von Grund auf")

# Trainingsschleife
history = {'train_loss': [], 'val_acc': [], 'lr': []}

for epoch in range(start_epoch, EPOCHS):
    # Trainieren
    model.train()
    total_loss = 0.0
    for inputs, labels in train_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        loss = criterion(model(inputs), labels)
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)  # Gradienten-Clipping
        optimizer.step()
        total_loss += loss.item()

    # Validieren
    model.eval()
    correct = 0
    with torch.no_grad():
        for inputs, labels in val_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            correct += (model(inputs).argmax(1) == labels).sum().item()

    avg_loss = total_loss / len(train_loader)
    val_acc  = correct / len(val_loader.dataset)
    cur_lr   = scheduler.get_last_lr()[0]

    # Aufzeichnen & Ausgeben
    history['train_loss'].append(avg_loss)
    history['val_acc'].append(val_acc)
    history['lr'].append(cur_lr)
    print(f"Epoch {epoch+1:3d}/{EPOCHS} | "
          f"Loss: {avg_loss:.4f} | Acc: {val_acc:.4f} | LR: {cur_lr:.2e}")

    # Scheduler-Aktualisierung
    scheduler.step()

    # Bestes Modell speichern
    if val_acc > best_acc:
        best_acc = val_acc
        torch.save({
            'epoch': epoch,
            'model': model.state_dict(),
            'optimizer': optimizer.state_dict(),
            'scheduler': scheduler.state_dict(),
            'best_acc': best_acc,
        }, SAVE_PATH)
        print(f" Bestes Modell wird gespeichert, Acc: {best_acc:.4f}")

print(f"\n"Training abgeschlossen, beste Validierungsgenauigkeit: {best_acc:.4f}")

9. Leitfaden zur Scheduler-Auswahl

Empfehlungen nach Aufgabentyp

SzenarioEmpfohlener SchedulerGrund
Schnelle Experimente / Prototyp-ValidierungReduceLROnPlateauKeine Hyperparameter-Abstimmung nötig, adaptive Abklingrate
Bildklassifikation (feste Epochen)CosineAnnealingLRSanftes Abklingen, gute Konvergenzqualität
Transformer / BERTLinearLR + CosineAnnealingLRWarmup ist essenziell
Begrenzte Ressourcen, schnelles TrainingOneCycleLRGleiche Genauigkeit in 1/5 der Zeit erreichen
Fine-Tuning großer Modelle (fine-tune)LinearLR(warmup) +ConstantLRNiedrige Lernrate für stabiles Fine-Tuning
Ungewisse Anzahl an EpochenReduceLROnPlateauReagiert automatisch, verlässt sich nicht auf einen festen Zeitplan
Objekterkennung (YOLO/Faster-RCNN)MultiStepLR / OneCycleLRHat klare Decay-Punkte oder strebt Geschwindigkeit an
Wollen lokale Optima verlassenCosineAnnealingWarmRestartsPeriodischer Neustart zur Erkundung des Parameterraums

Häufige Konfigurationskombinationen

Beispiel

# Kombination 1: Warmup + Cosinus-Annealing (Standard für Transformer)
SequentialLR([LinearLR(5 epochs), CosineAnnealingLR(95 epochs)])

# Kombination 2: Validierungsdatensatz überwachen + automatische Abnahme (allgemein bevorzugt)
ReduceLROnPlateau(mode='min', patience=5, factor=0.5)

# Kombination 3: OneCycleLR (bevorzugt für schnelles Training)
OneCycleLR(max_lr=0.1, total_steps=total_steps, pct_start=0.3)

# Kombination 4: traditioneller Step-Decay (CV-Klassiker)
MultiStepLR(milestones=[30, 60, 90], gamma=0.1)

Kurzübersicht der Hinweise

ProblemLösung
Scheduler wird nicht wirksamPrüfenscheduler.step()ob es sich umoptimizer.step() nachAufruf
CyclicLR / OneCycleLR ändert sich nichtDiese beiden basieren aufBatch-Aufrufen, nicht pro Epoche
ReduceLROnPlateau wird nicht ausgelöstPrüfenmodeob es korrekt gesetzt ist (Verwenden Sie Loss für'min', verwenden Sie Accuracy für'max')
LR ist nach Wiederaufnahme des Trainings falschPrüfen Sie, ob es gespeichert und geladen wurdescheduler.state_dict()
LR-Einstellungen für mehrere ParametergruppenVerwenden SieLambdaLRÜbergeben Sie eine Liste, eine Lambda-Funktion pro Parametergruppe
Weitere Erweiterungen