Aller au contenu principal

Chapitre 3 : Oubli Catastrophique

Préserver les Connaissances Pendant le Fine-Tuning​

L'oubli catastrophique est l'un des plus grands défis du fine-tuning. Le comprendre vous aide à concevoir de meilleures stratégies d'entraînement.


Qu'est-ce que l'Oubli Catastrophique ?​

L'oubli catastrophique se produit lorsqu'un réseau neuronal perd des connaissances précédemment acquises après avoir été entraîné sur de nouvelles données.

Scénario d'Exemple​

Un LLM pré-entraîné sait :

  • Comment traduire des langues
  • Connaissances générales du monde
  • Raisonnement mathématique
  • Génération de code

Après un fine-tuning sur des données de support client :

  • ✅ Excellent en support client
  • ❌ Peut avoir des difficultés avec la traduction
  • ❌ Peut perdre certaines connaissances générales
  • ❌ La qualité de la génération de code peut diminuer

Pourquoi Cela Se Produit-il ?​

Interférence des Poids​

Les réseaux neuronaux stockent les connaissances dans leurs poids. Lorsque vous vous entraînez sur de nouvelles données :

Poids originaux (W) → encodent des connaissances générales
Nouvel entraînement → met à jour W en (W')
W' optimise pour la nouvelle tâche → peut casser les représentations des anciennes tâches

Changement de Distribution​

Le modèle s'adapte à votre distribution de données spécifique, perdant potentiellement en généralisation.


Mesurer l'Oubli Catastrophique​

Évaluation Avant/Après​

from lm_eval import evaluator
from lm_eval.models.huggingface import HFLM

def evaluate_model(model_path, tasks=["hellaswag", "arc_easy", "mmlu"]):
"""Évaluer le modèle sur des benchmarks standard."""
model = HFLM(pretrained=model_path)
results = evaluator.simple_evaluate(
model=model,
tasks=tasks,
num_fewshot=0,
)
return results

# Avant le fine-tuning
baseline_results = evaluate_model("meta-llama/Llama-3.1-8B")
print("Baseline:", baseline_results)

# Après le fine-tuning
finetuned_results = evaluate_model("./my-finetuned-model")
print("Fine-tuned:", finetuned_results)

# Comparer
for task in ["hellaswag", "arc_easy", "mmlu"]:
baseline = baseline_results["results"][task]["acc"]
finetuned = finetuned_results["results"][task]["acc"]
diff = finetuned - baseline
print(f"{task}: {diff:+.2%} ({'↑' if diff > 0 else '↓'})")

Tests Spécifiques aux Tâches​

# Créer une suite de tests pour les capacités que vous souhaitez préserver
test_cases = [
{"prompt": "Translate to French: Hello, how are you?",
"expected_contains": "Bonjour"},
{"prompt": "What is 15 * 17?",
"expected_contains": "255"},
{"prompt": "Write a Python function to reverse a string",
"expected_contains": "def"},
]

def test_capabilities(model, tokenizer, test_cases):
"""Tester si le modèle conserve des capacités spécifiques."""
for test in test_cases:
output = model.generate(
tokenizer.encode(test["prompt"], return_tensors="pt"),
max_new_tokens=100
)
response = tokenizer.decode(output[0], skip_special_tokens=True)
passed = test["expected_contains"] in response
print(f"{'✅' if passed else '❌'} {test['prompt'][:50]}...")

Stratégies d'Atténuation​

1. Utiliser des Taux d'Apprentissage Plus Bas​

# Fine-tuning complet : un LR plus bas préserve plus de connaissances
training_args = TrainingArguments(
learning_rate=1e-5, # Plus bas que par défaut
# vs 2e-5 ou plus pour PEFT
)

2. Moins d'Époques d'Entraînement​

# Moins d'entraînement = moins d'oubli
training_args = TrainingArguments(
num_train_epochs=1, # Commencer avec 1
# Surveiller la performance de validation
# N'augmenter que si sous-apprentissage
)

3. Techniques de Régularisation​

Décroissance des Poids​

training_args = TrainingArguments(
weight_decay=0.01, # Pénaliser les grands changements de poids
)

Consolidation Élastique des Poids (EWC)​

# EWC ajoute une pénalité pour changer les poids importants
# (Technique de recherche, pas directement dans HF Trainer)
loss = cross_entropy_loss + lambda * sum(
importance[i] * (theta[i] - theta_star[i])^2
for i in parameters
)

4. Mélange / Relecture de Données​

# Mélanger les données de style pré-entraînement originales avec les données de fine-tuning
from datasets import concatenate_datasets

# Vos données spécifiques
finetuning_data = load_dataset("your/dataset")

# Données d'instruction générales pour maintenir les capacités
general_data = load_dataset("databricks/dolly-15k", split="train[:1000]")

# Les mélanger (par exemple, 80% spécifiques, 20% générales)
mixed_dataset = concatenate_datasets([
finetuning_data,
general_data.shuffle().select(range(len(finetuning_data) // 4))
])

5. Utiliser PEFT à la Place​

La mitigation la plus efficace : ne mettez pas à jour tous les poids

from peft import LoraConfig, get_peft_model

# Ne mettre à jour que de petits adaptateurs, préservant le modèle de base
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
)

model = get_peft_model(base_model, lora_config)
# Poids du modèle de base gelés → pas d'oubli catastrophique !

Avantage de PEFT​

Pourquoi PEFT Empêche l'Oubli​

Preuves Empiriques​

MéthodePerformance de la TâcheRétention du Benchmark
Fine-tuning Complet95%70-85%
LoRA93%95-99%
QLoRA92%95-99%

Les méthodes PEFT conservent généralement 95%+ de la performance de base sur des tâches non liées.


Stratégie d'Évaluation​

Protocole d'Évaluation Complet​

def comprehensive_evaluation(model_path, baseline_path):
"""Évaluer à la fois la performance de la tâche et la rétention des capacités."""

# 1. Évaluation spécifique à la tâche (votre objectif de fine-tuning)
task_score = evaluate_on_task(model_path, your_test_set)

# 2. Benchmarks de capacités générales
benchmarks = ["hellaswag", "arc_easy", "mmlu", "gsm8k"]
baseline_scores = {}
finetuned_scores = {}

for bench in benchmarks:
baseline_scores[bench] = evaluate_benchmark(baseline_path, bench)
finetuned_scores[bench] = evaluate_benchmark(model_path, bench)

# 3. Calculer la rétention
retention = {}
for bench in benchmarks:
retention[bench] = finetuned_scores[bench] / baseline_scores[bench]

avg_retention = sum(retention.values()) / len(retention)

print(f"Performance de la Tâche : {task_score:.2%}")
print(f"Rétention Moyenne des Capacités : {avg_retention:.2%}")

# 4. Analyse des compromis
if task_score > 0.90 and avg_retention > 0.95:
print("✅ Bon équilibre atteint")
elif task_score > 0.90 and avg_retention < 0.90:
print("⚠️ Haute performance de tâche mais oubli significatif")
else:
print("❌ Envisagez d'ajuster l'entraînement")

return {
"task_score": task_score,
"retention": retention,
"avg_retention": avg_retention
}

Résumé des Meilleures Pratiques​

Pour Minimiser l'Oubli Catastrophique :​

StratégieDifficultéEfficacité
Utiliser PEFT (LoRA/QLoRA)Facile⭐⭐⭐⭐⭐
Taux d'apprentissage plus basFacile⭐⭐⭐
Moins d'époquesFacile⭐⭐⭐
Mélange de donnéesMoyen⭐⭐⭐⭐
Décroissance des poidsFacile⭐⭐
Arrêt précoceFacile⭐⭐⭐

Approche Recommandée​

  1. Commencez avec PEFT (LoRA ou QLoRA)
  2. Si PEFT est insuffisant, essayez le fine-tuning complet avec :
    • Taux d'apprentissage bas (1e-5)
    • Époques limitées (1-2)
    • Mélange de données
    • Évaluation complète

Points Clés à Retenir​

✅ L'oubli catastrophique est lorsque les modèles perdent des connaissances après le fine-tuning

✅ Cela se produit parce que les poids sont modifiés pour s'adapter à une nouvelle distribution de données

✅ Les méthodes PEFT évitent largement l'oubli en gardant les poids de base gelés

✅ Pour le fine-tuning complet : utilisez un LR bas, peu d'époques, mélange de données

✅ Évaluez toujours à la fois la performance de la tâche ET la rétention des capacités

✅ Le compromis est : spécialisation de la tâche vs capacité générale


Prochaines Étapes​

Testez vos connaissances avec le Quiz du Module 2, puis continuez vers Module 3 : Fine-Tuning Efficace en Paramètres.


Vérification Rapide​

Avant de continuer, assurez-vous de pouvoir répondre :

  1. Qu'est-ce que l'oubli catastrophique et pourquoi se produit-il ?
  2. Comment les méthodes PEFT aident-elles à prévenir l'oubli catastrophique ?
  3. Quelles sont trois stratégies pour minimiser l'oubli dans le fine-tuning complet ?