Planificateur de taux d'apprentissage PyTorch

Le taux d'apprentissage (Learning Rate) est l'un des hyperparamètres les plus importants dans l'entraînement des réseaux de neurones. Si le taux d'apprentissage est trop grand, l'entraînement oscille ou même diverge ; s'il est trop petit, la convergence est extrêmement lente et il est facile de tomber dans des optima locaux.

Planificateur de taux d'apprentissageEn ajustant dynamiquement le taux d'apprentissage pendant l'entraînement, il équilibre une convergence rapide au début et un réglage fin en fin d'entraînement.


1. Concepts de base et modes d'utilisation

Flux d'utilisation standard

)

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. Créer le planificateur, en passant l'optimiseur
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

for epoch in range(100):
    # 2. Entraîner
    train(model, optimizer)

    # 3. Après chaque époque, appeler scheduler.step()
    scheduler.step()

    # 4. Afficher le taux d'apprentissage actuel
    current_lr = scheduler.get_last_lr()[0]
    print(f"Epoch {epoch+1}, LR: {current_lr:.6f}")

Quand appeler step()

Exemple

# Correct : optimizer.step() d'abord, scheduler.step() ensuite
optimizer.step()
scheduler.step()

# Incorrect : scheduler.step() avant optimizer.step()
# Après PyTorch 1.1.0, un avertissement sera généré, et certains schedulers se comporteront anormalement
scheduler.step()
optimizer.step()

Remarque :optimizer.step()Doit êtrescheduler.step()appelé avant.

Affichage et sauvegarde de l'état du taux d'apprentissage

Exemple

# Obtenir le taux d'apprentissage actuel
current_lr = optimizer.param_groups[0]['lr']
current_lr = scheduler.get_last_lr()[0]   # Le LR après le step() précédent

# Sauvegarder le checkpoint (il faut aussi sauvegarder l'état du scheduler)
torch.save({
    'epoch': epoch,
    'model': model.state_dict(),
    'optimizer': optimizer.state_dict(),
    'scheduler': scheduler.state_dict(),   # Ne manquez pas ceci
}, 'checkpoint.pth')

# Restaurer le point de contrôle
ckpt = torch.load('checkpoint.pth')
model.load_state_dict(ckpt['model'])
optimizer.load_state_dict(ckpt['optimizer'])
scheduler.load_state_dict(ckpt['scheduler'])

2. Planificateurs à décroissance fixe

2.1 StepLR Décroissance par paliers

À chaquestep_sizenombre fixe d'époques, multiplier le taux d'apprentissage pargamma. C'est l'un des planificateurs les plus simples et les plus couramment utilisés.

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

Exemple

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.StepLR(
    optimizer,
    step_size=30,   # Décroissance toutes les 30 époques
    gamma=0.1       # Multiplier par 0.1 à chaque fois (c'est-à-dire réduire à 1/10 de la valeur d'origine)
)

# Changements du LR :
# Époque 0-29 : 0.1
# Époque 30-59 : 0.01
# Époque 60-89 : 0.001
# Époque 90+ : 0.0001

Scénarios d'application :Tâches avec un rythme d'entraînement fixe et des étapes claires, telles que l'entraînement ResNet sur ImageNet (90 époques, avec décroissance aux 30e et 60e époques).


2.2 MultiStepLR Décroissance multi-jalons

Réduit le taux d'apprentissage à plusieurs époques spécifiées (jalons), plus flexible que StepLR.

Exemple

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.MultiStepLR(
    optimizer,
    milestones=[30, 60, 80],   # Décroissance aux 30e, 60e et 80e époques
    gamma=0.1
)

# Changements du LR :
# Époque 0-29 : 0.1
# Époque 30-59 : 0.01
# Époque 60-79 : 0.001
# Époque 80+ : 0.0001

Scénarios d'application :Lorsqu'on sait à quelles époques le modèle nécessite un ajustement fin, comme la phase de convergence fine dans les phases intermédiaires et tardives des modèles de classification.


2.3 ExponentialLR Décroissance exponentielle

Le taux d'apprentissage décroît à chaque époque et continue de diminuer exponentiellement, offrant une décroissance plus douce.

Formule : lr = lr_base * gamma^epoch

Exemple

optimizer = optim.Adam(model.parameters(), lr=0.01)
scheduler = optim.lr_scheduler.ExponentialLR(
    optimizer,
    gamma=0.95   # Multiplier par 0.95 à chaque époque
)

# Modifications du LR (5 premières époques) :
# Époque 1 : 0.0100
# Époque 2 : 0.0095
# Époque 3 : 0.0090
# Époque 4 : 0.0086
# Époque 5 : 0.0081

gammaS'il est défini trop petit (par ex., 0.5), le taux d'apprentissage se rapprochera rapidement de zéro ; il est généralement défini entre 0.9 et 0.99.


3. Planificateurs adaptatifs

3.1 Décroissance basée sur la métrique ReduceLROnPlateau

L'un des planificateurs les plus intelligents: surveillez une certaine métrique (par ex., la perte de validation), et réduisez automatiquement le taux d'apprentissage lorsque la métrique cesse de s'améliorer. Il n'est pas nécessaire de savoir à l'avance à quelle époque effectuer la décroissance.

Exemple

optimizer = optim.Adam(model.parameters(), lr=0.01)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer,
    mode='min',       # 'min' : plus la valeur surveillée est faible, mieux c'est (perte) ; 'max' : plus elle est élevée, mieux c'est (précision)
    factor=0.1,       # Lorsqu'il est déclenché, lr = lr × factor
    patience=10,      # Nombre d'époques pendant lesquelles la métrique peut stagner ; si dépassé, décroissance
    threshold=1e-4,   # Une amélioration inférieure à cette valeur est considérée comme inexistante
    min_lr=1e-6,      # Borne inférieure du taux d'apprentissage ; il ne sera pas inférieur à cette valeur
    verbose=True      # Affiche les informations de décroissance
)

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

    # Contrairement aux autres planificateurs, passez ici la métrique surveillée
    scheduler.step(val_loss)

Style d'écriture pour la surveillance de l'exactitude :

Exemple

scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer,
    mode='max',       # Plus l'exactitude est élevée, mieux c'est
    factor=0.5,
    patience=5,
)

scheduler.step(val_accuracy)

Scénarios applicables :Le premier choix par défaut pour presque toutes les tâches, en particulier lorsque vous ne savez pas combien d'époques entraîner ou lorsque l'entraînement est instable.


3.2 CosineAnnealingLR Recuit cosinusoïdal

Le taux d'apprentissage suit unecourbe en cosinusdepuis la valeur initiale en descendant progressivement jusqu'à la valeur minimale (eta_min), en évitant les changements brusques de la décroissance par paliers.

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

Exemple

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.CosineAnnealingLR(
    optimizer,
    T_max=100,     # Durée d'un demi-cycle (généralement fixé au nombre total d'époques)
    eta_min=1e-6   # Taux d'apprentissage minimal (0 par défaut)
)

# Trajectoire d'évolution du LR (schéma lorsque T_max=10) :
# 0.1 -> 0.095 -> 0.079 -> 0.055 -> 0.026 -> 0.001
# (descente douce de la courbe de cosinus)

Scénarios applicables :Scénarios avec un nombre fixe d'époques d'entraînement, largement utilisés dans des articles tels que Vision Transformer et ResNet, avec une bonne qualité de convergence.


3.3 CosineAnnealingWarmRestarts

La version améliorée du recuit cosinus prend en chargeles redémarrages périodiques à chaud(Warm Restarts) : après la fin de chaque cycle, le taux d'apprentissage est réinitialisé à sa valeur initiale et entame un nouveau cycle de décroissance cosinus. Cela permet au modèle d'échapper aux optima locaux.

Exemple

optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(
    optimizer,
    T_0=10,       # Longueur du premier cycle (nombre d'époques)
    T_mult=2,     # Multiplicateur pour la longueur du cycle après chaque redémarrage (1=longueur égale, 2=augmentation progressive)
    eta_min=1e-6
)

# Longueurs des cycles lorsque T_mult=2 : 10 -> 20 -> 40 -> 80 ...
# Variations du LR (T_0=10, T_mult=1) :
# 0.1 -> ... -> 0 -> 0.1 -> ... -> 0 -> 0.1 (redémarrage toutes les 10 époques)

Scénarios applicables :Lors de l'entraînement de grands modèles, ou lorsque vous souhaitez que le modèle choisisse le meilleur parmi plusieurs points de convergence, il fonctionne efficacement avec Snapshot Ensemble.


4. Planificateurs de warmup

Warmupconsiste à augmenter progressivement le taux d'apprentissage d'une valeur très faible jusqu'à la valeur cible au cours des premières étapes de l'entraînement. L'entraînement avec de grands lots et les modèles de type Transformer nécessitent presque tous un warmup ; sinon, les mises à jour initiales du gradient sont trop agressives et le modèle est difficile à stabiliser.

4.1 LinearLR : planification linéaire

Modifier linéairement le taux d'apprentissage pendant un nombre d'époques spécifié (peut être utilisé pour un warmup linéaire ou une décroissance linéaire).

Exemple

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

# Warmup linéaire : au cours des 5 premières époques, lr augmente linéairement de 0.01×0.1=0.001 à 0.01
warmup_scheduler = optim.lr_scheduler.LinearLR(
    optimizer,
    start_factor=0.1,   # lr initial = base_lr × start_factor
    end_factor=1.0,     # lr final = base_lr × end_factor
    total_iters=5       # Terminé après 5 époques
)

# Évolution du LR : 0.001 -> 0.003 -> 0.005 -> 0.007 -> 0.009 -> 0.01

4.2 ConstantLR : phase constante

Pendant un nombre spécifié d’époques, fixez le taux d’apprentissage àbase_lr × factor, puis restaurez la valeur d’origine.

Exemple

# Utiliser base_lr × 0.5 pour les 5 premières époques, puis restaurer la valeur normale
scheduler = optim.lr_scheduler.ConstantLR(
    optimizer,
    factor=0.5,
    total_iters=5
)

4.3 Planification combinée SequentialLR

Combiner plusieurs planificateursen séquence, ce qui est l’approche standard pour mettre en œuvre la stratégie de combinaison « warmup + decay ».

Exemple

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

# Phase 1 : warmup (5 premières époques, LR augmente linéairement de 0.001 à 0.01)
warmup = optim.lr_scheduler.LinearLR(
    optimizer, start_factor=0.1, end_factor=1.0, total_iters=5
)

# Phase 2 : recuit cosinus (95 époques restantes)
cosine = optim.lr_scheduler.CosineAnnealingLR(
    optimizer, T_max=95, eta_min=1e-6
)

# Combiné : exécuter d'abord le warmup, puis passer à cosine après la 5e époque
scheduler = optim.lr_scheduler.SequentialLR(
    optimizer,
    schedulers=[warmup, cosine],
    milestones=[5]          # Basculer à la 5e époque
)

# L'utilisation est exactement la même que celle d'un planificateur normal
for epoch in range(100):
    train(...)
    scheduler.step()

Scénarios applicables :Configuration standard pour l'entraînement de Transformers (warmup + cosine annealing) ; les pré-entraînements de BERT, GPT et ViT utilisent tous cette stratégie.


5. Planificateurs cycliques

5.1 CyclicLR Taux d'apprentissage cyclique

Le taux d'apprentissagebase_lretmax_lrentre lescycles périodiquement, ce qui aide le modèle à explorer un espace de paramètres plus large et à échapper aux points selles.

Exemple

optimizer = optim.SGD(model.parameters(), lr=0.01)
scheduler = optim.lr_scheduler.CyclicLR(
    optimizer,
    base_lr=0.001,          # Borne inférieure du taux d'apprentissage
    max_lr=0.01,            # Borne supérieure du taux d'apprentissage
    step_size_up=2000,      # Nombre d'itérations pour monter de base_lr à max_lr
    step_size_down=2000,    # Nombre d'itérations pour descendre de max_lr à base_lr (par défaut step_size_up)
    mode='triangular',      # Cycle triangulaire (amplitude constante)
    # mode='triangular2' # l'amplitude diminue de moitié à chaque cycle
    # mode='exp_range' # décroissance exponentielle de l'amplitude
)

# CyclicLR est appelé à chaque étape (batch), non par époque
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()    # Appeler après chaque batch

Comparaison des trois modes :

modeVariation d'amplitudeCaractéristique
triangularConstanteExploration stable, adaptée aux premières étapes
triangular2Diminue de moitié à chaque cycleExplorer d'abord, puis converger
exp_rangeDécroissance exponentielleFinalement, converge en douceur

5.2 OneCycleLR Stratégie en un cycle

L'un des planificateurs les plus performants, proposé par fastai comme politique 1-Cycle. L'ensemble de l'entraînement ne comporte qu'un seul cycle : le taux d'apprentissage augmente d'abord puis diminue, et le momentum évolue en sens inverse.

L'entraînement est plus rapide, ne nécessitant généralement que1/5~1/10le nombre d'époques de l'entraînement traditionnel.

Exemple

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,                              # Taux d'apprentissage maximal
    steps_per_epoch=steps_per_epoch,         # Nombre d'étapes par époque
    epochs=10,                               # Nombre total d'époques
    pct_start=0.3,                           # Les 30% premiers utilisés pour la montée en warmup
    anneal_strategy='cos',                   # Stratégie de décroissance ('cos' ou 'linear')
    div_factor=25,                           # lr initial = max_lr / div_factor
    final_div_factor=1e4                     # lr final = max_lr / final_div_factor
)

# lr initial = 0.1 / 25 = 0.004
# lr maximal = 0.1 (à 30%)
# lr final = 0.1 / 10000 = 0.00001

# Aussi appelé par batch
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()    # Appeler après chaque batch

Scénarios applicables :Premier choix lorsque les ressources de calcul sont limitées et que vous devez valider rapidement des idées ;max_lrIl est recommandé d'utiliser l'outil LR Finder pour déterminer la valeur optimale.


6. Planificateurs personnalisés

6.1 Personnalisation fonctionnelle de LambdaLR

Personnalisez entièrement la stratégie de planification en passant une fonction Lambda (qui accepte le numéro d'époque et renvoie un multiplicateur de taux d'apprentissage).

Exemple

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

# Exemple 1 : stratégie de warmup classique de 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          # Éviter la division par zéro
    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)


# Exemple 2 : décroissance polynomiale (lr diminue linéairement/polynomialement de la valeur initiale à 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)


# Exemple 3 : taux d'apprentissage groupés (stratégies différentes pour différents groupes de paramètres)
optimizer = optim.Adam([
    {'params': model.backbone.parameters(), 'lr': 1e-4},
    {'params': model.head.parameters(),     'lr': 1e-3},
])

# Une fonction lambda par groupe de paramètres
scheduler = optim.lr_scheduler.LambdaLR(
    optimizer,
    lr_lambda=[
        lambda epoch: 0.95 ** epoch,    # backbone : décroissance lente
        lambda epoch: 0.85 ** epoch,    # head : décroissance rapide
    ]
)

6.2 Héritage de LRScheduler

Lorsqu’une logique plus complexe est nécessaire, héritezLRScheduler(PyTorch >= 2.0, dans les versions plus anciennes_LRScheduler) pour implémenter une personnalisation complète :

Exemple

from torch.optim.lr_scheduler import LRScheduler
import math

class WarmupCosineScheduler(LRScheduler):
    """
Planificateur combiné warmup + cosine annealing (version implémentée manuellement)
- Warmup linéaire pendant les warmup_epochs premières époques
- Ensuite, recuit cosinus jusqu’à 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

        # Phase de préchauffage : augmentation linéaire
        if epoch < self.warmup_epochs:
            warmup_factor = (epoch + 1) / self.warmup_epochs
            return [base_lr * warmup_factor for base_lr in self.base_lrs]

        # Phase de recuit cosinus
        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. Comparaison de visualisation des planificateurs

Le code suivant peut tracer les courbes de variation du LR de chaque planificateur pour une comparaison intuitive :

Exemple

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

def simulate_lr(scheduler_fn, epochs=100, steps_per_epoch=None):
    """Simuler et enregistrer les variations du taux d'apprentissage du planificateur"""
    model     = torch.nn.Linear(1, 1)
    optimizer = optim.SGD(model.parameters(), lr=0.1)
    scheduler = scheduler_fn(optimizer)
    lrs = []

    if steps_per_epoch:
        # Planificateur appelé à chaque étape
        for _ in range(epochs):
            for _ in range(steps_per_epoch):
                optimizer.step()
                scheduler.step()
                lrs.append(optimizer.param_groups[0]['lr'])
    else:
        # Planificateur appelé à chaque époque
        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('Époque')
plt.ylabel('Taux d'apprentissage')
plt.title('Comparaison des planificateurs de taux d'apprentissage PyTorch')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('lr_schedulers.png', dpi=150)
plt.show()

8. Gabarit complet d'entraînement

Un gabarit d'entraînement de qualité production intégrant le warmup + recuit cosinus, la sauvegarde et la restauration du modèle :

Exemple

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

# Hyperparamètres
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')

# Modèle / Optimiseur / Planificateur
model     = MyModel().to(device)
optimizer = optim.AdamW(model.parameters(), lr=BASE_LR, weight_decay=1e-4)
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)

# Échauffement 5 époques + recuit cosinusoïdal 95 époques
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])

# Restaurer le point de contrôle
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"Reprise depuis l'époque {start_epoch}, meilleure précision {best_acc:.4f}")
except FileNotFoundError:
    print("Entraînement à partir de zéro")

# Boucle d'entraînement
history = {'train_loss': [], 'val_acc': [], 'lr': []}

for epoch in range(start_epoch, EPOCHS):
    # Entraîner
    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)  # Écrêtage du gradient
        optimizer.step()
        total_loss += loss.item()

    # Valider
    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]

    # Enregistrer & afficher
    history['train_loss'].append(avg_loss)
    history['val_acc'].append(val_acc)
    history['lr'].append(cur_lr)
    print(f"Époque {epoch+1:3d}/{EPOCHS} | "
          f"Perte: {avg_loss:.4f} | Préc: {val_acc:.4f} | LR: {cur_lr:.2e}")

    # Mise à jour du scheduler
    scheduler.step()

    # Enregistrer le meilleur modèle
    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" Sauvegarde du meilleur modèle, Précision : {best_acc:.4f}")

print(f"\n"Entraînement terminé, meilleure précision de validation : {best_acc:.4f}")

9. Guide de sélection du planificateur

Recommandations par type de tâche

ScénarioPlanificateur recommandéRaison
Expériences rapides / validation de prototypeReduceLROnPlateauAucun réglage d'hyperparamètres nécessaire, décroissance adaptative
Classification d'images (époques fixes)CosineAnnealingLRDécroissance douce, bonne qualité de convergence
Transformer / BERTLinearLR + CosineAnnealingLRLe warmup est essentiel
Ressources limitées, entraînement rapideOneCycleLRAtteindre la même précision en 1/5 du temps
Fine-tuning de grands modèles (fine-tune)LinearLR(warmup) +ConstantLRFaible taux d'apprentissage pour un fine-tuning stable
Nombre d'époques incertainReduceLROnPlateauRéagit automatiquement, ne dépend pas d'un planning fixe
Détection d'objets (YOLO/Faster-RCNN)MultiStepLR / OneCycleLRA des points de décroissance clairs ou vise la vitesse
Vouloir échapper aux optima locauxCosineAnnealingWarmRestartsRedémarrage périodique pour explorer l'espace des paramètres

Combinaisons de configurations courantes

Exemple

# Combinaison 1 : Warmup + recuit cosinus (standard pour Transformer)
SequentialLR([LinearLR(5 epochs), CosineAnnealingLR(95 époques)])

# Combinaison 2 : surveiller l'ensemble de validation + décroissance automatique (universellement préférée)
ReduceLROnPlateau(mode='min', patience=5, factor=0.5)

# Combinaison 3 : OneCycleLR (préférée pour un entraînement rapide)
OneCycleLR(max_lr=0.1, total_steps=total_steps, pct_start=0.3)

# Combinaison 4 : décroissance par paliers traditionnelle (classique en CV)
MultiStepLR(milestones=[30, 60, 90], gamma=0.1)

Référence rapide des notes

ProblèmeSolution
Le planificateur ne prend pas effetVérifierscheduler.step()s'il s'agit deoptimizer.step() aprèsappel
CyclicLR / OneCycleLR ne change pasCes deux sont basés surbatchappels, pas par époque
ReduceLROnPlateau ne se déclenche pasVérifiermodes'il est correctement défini (utiliser loss pour'min', utiliser accuracy pour'max')
LR est incorrect après la reprise de l'entraînementVérifiez s'il a été sauvegardé et chargéscheduler.state_dict()
Paramètres LR pour plusieurs groupes de paramètresUtiliserLambdaLRPassez une liste, un lambda par groupe de paramètres
Autres extensions