Knowledge Distillation ist eine Technik, um Wissen von großen Modellen (Teacher) auf kleinere Modelle (Student) zu übertragen.
Kernidee
Ein großes Modell (GPT-4, 70B Llama) trainiert ein kleines Modell (7B Llama), indem es ihm nicht nur "richtige Antworten" gibt, sondern auch seine "Intuition" zeigt.
Teacher (Großes Modell):
Input: "Was ist die Hauptstadt Deutschlands?"
Output: Wahrscheinlichkeiten
- Berlin: 0.95
- München: 0.02
- Hamburg: 0.01
- Köln: 0.01
- (weitere 96 Länder mit <0.01)
Student (Kleines Modell):
Lernt nicht nur dass Berlin richtig ist,
sondern auch WARUM es andere Städte gibt und warum nicht!
Teacher-Student Framework
Standard Training (falsch)
# Teacher macht vorhersagen
teacher_predictions = teacher_model(input) # [0, 1, 0, 0, ...]
# Student trainiert nur auf richtige Labels
student_predictions = student_model(input)
loss = CrossEntropyLoss(student_predictions, true_labels)
Problem: Student sieht nicht die "Intuition" des Teachers.
Knowledge Distillation (richtig)
# Teacher macht Vorhersagen
teacher_logits = teacher_model(input, output_hidden_states=True)
# Student trainiert auf ZWEI Dinge:
student_logits = student_model(input)
# 1. Distillation Loss (Wissen vom Teacher)
distillation_loss = KL_Divergence(
softmax(student_logits / temperature),
softmax(teacher_logits / temperature)
)
# 2. Task Loss (Original-Labels)
task_loss = CrossEntropyLoss(student_logits, true_labels)
# Kombiniere beide
total_loss = (1 - alpha) * task_loss + alpha * distillation_loss
# alpha typisch: 0.3-0.7
Soft Labels und Temperature
Temperature ist der Schlüssel zur Distillation.
Ohne Temperature (T=1)
Teacher Ausgabe für "Berlin ist Hauptstadt":
Berlin: 0.99 (sehr sicher)
Alle anderen: ~0.0001 (sehr unsicher)
Student sieht:
"Berlin ist richtig, alles andere ist Mist"
→ Keine neuen Informationen von anderen Optionen!
Mit Temperature (T=5)
Gleiche Teacher Ausgabe mit T=5 "weicher gemacht":
Berlin: 0.85 (sicher)
München: 0.04 (könnte auch sein)
Hamburg: 0.03
Köln: 0.03
Andere: 0.02
...
Student sieht:
"Berlin ist sehr likely, aber München/Hamburg/Köln könnten auch sinnvoll sein"
→ Student lernt die Intuition des Teachers!
Mathematik
Soft Label Berechnung:
p_soft = softmax(z / T)
T = 1: Normale Softmax (sharp, one-hot)
T = 5: Weicher, mehr Wahrscheinlichkeit für "falsche" Labels
T = 20: Noch weicher, fast uniform
T = ∞: Komplett uniform (zu weich, keine Info)
Typischer Wert: T = 3-7
Praktischer Code
import torch
import torch.nn.functional as F
def distillation_loss(student_logits, teacher_logits, temperature=4.0, alpha=0.7):
"""
student_logits: Shape (batch, num_classes)
teacher_logits: Shape (batch, num_classes)
"""
# Soft Targets vom Teacher
soft_targets = F.softmax(teacher_logits / temperature, dim=-1)
# Student probabilities mit gleicher Temperature
student_log_probs = F.log_softmax(student_logits / temperature, dim=-1)
# KL Divergence
kl_loss = F.kl_div(student_log_probs, soft_targets, reduction='batchmean')
return kl_loss
Distillation für LLMs
Distillation von großen Language Models auf kleinere.
Szenario
Teacher: GPT-4 oder Llama-70B
Student: Llama-7B oder Mistral-7B
Ziel: Kleine Model mit großem Modell Qualität
Implementierung
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
# Modelle laden
teacher = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-70b")
student = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b")
# Beide auf GPU
device = "cuda:0"
teacher = teacher.to(device)
student = student.to(device)
teacher.eval() # Teacher in eval mode
# Training Loop
optimizer = torch.optim.AdamW(student.parameters(), lr=1e-5)
for batch_text in training_data:
inputs = tokenizer(batch_text, return_tensors="pt").to(device)
# Teacher generiert Logits (kein Gradient)
with torch.no_grad():
teacher_outputs = teacher(**inputs, output_hidden_states=True)
teacher_logits = teacher_outputs.logits # (batch, seq_len, vocab_size)
# Student generiert Logits
student_outputs = student(**inputs)
student_logits = student_outputs.logits
# Distillation Loss
T = 4.0
soft_targets = F.softmax(teacher_logits / T, dim=-1)
student_log_probs = F.log_softmax(student_logits / T, dim=-1)
kl_loss = F.kl_div(student_log_probs, soft_targets, reduction='batchmean')
# Backward
kl_loss.backward()
optimizer.step()
optimizer.zero_grad()
Praktische Ergebnisse
Llama-2-70B Teacher
MMLU: 63.4%
Hellaswag: 81.3%
Llama-2-7B (vortrainiert, ohne Distillation)
MMLU: 46.0%
Hellaswag: 71.2%
Llama-2-7B (mit Distillation vom 70B)
MMLU: 52.5% ← +6.5 Punkte!
Hellaswag: 76.1% ← +4.9 Punkte
Distillation vs Alternatives
Wann Distillation verwenden
✅ DISTILLATION SINNVOLL:
- Du hast einen großen Teacher (GPT-4, 70B)
- Student muss schneller sein
- Qualität ist wichtig, aber nicht kritisch
- Budget begrenzt (Inference-Kosten)
❌ NICHT SINNVOLL:
- Kein guter Teacher vorhanden
- Qualität muss gleich wie Teacher sein
- Training-Daten sehr begrenzt (<10K Beispiele)
Vergleich: Distillation vs Fine-Tuning vs Quantization
| Methode | Modellgröße | Speed | Qualität | Komplexität | Kosten |
|---|---|---|---|---|---|
| Original | 70B | 1x | 100% | - | $$$$$ |
| Distillation | 7B | 10x | 85-90% | Hoch | $$$ |
| Fine-Tuning | 7B | 10x | 75-80% | Niedrig | $$ |
| Quantization | 70B | 3-4x | 95-98% | Niedrig | $$ |
| Distill + Quantize | 7B | 15x | 80-85% | Mittel | $ |
Advanced Distillation Techniques
1. Feature Distillation
Nicht nur Outputs distillieren, sondern auch versteckte Layer.
# Standard: Nur letzte Layer
distillation_loss = KL_Divergence(student_logits, teacher_logits)
# Feature Distillation: Versteckte Layer matchen
def feature_distillation_loss(student_hiddens, teacher_hiddens):
"""
student_hiddens: List[Tensors] → 13 Layers
teacher_hiddens: List[Tensors] → 13 Layers
Matche intermediate Representations
"""
loss = 0
for student_h, teacher_h in zip(student_hiddens, teacher_hiddens):
# Projiziere auf gleiche Dimension
student_proj = projection_layer(student_h) # (batch, seq, dim)
teacher_proj = teacher_h
# MSE Loss zwischen Features
loss += F.mse_loss(student_proj, teacher_proj)
return loss
2. Attention Transfer
Matche die Attention Patterns zwischen Teacher und Student.
def attention_transfer_loss(student_attentions, teacher_attentions):
"""
Achte darauf, WO der Teacher Aufmerksamkeit gibt
"""
loss = 0
for student_att, teacher_att in zip(student_attentions, teacher_attentions):
# student_att shape: (batch, heads, seq_len, seq_len)
# Summiere über heads um Aufmerksamkeits-Map zu bekommen
student_att_map = student_att.sum(dim=1) # (batch, seq_len, seq_len)
teacher_att_map = teacher_att.sum(dim=1)
# KL Loss zwischen Attention Distributions
loss += F.kl_div(
F.log_softmax(student_att_map, dim=-1),
F.softmax(teacher_att_map, dim=-1)
)
return loss
3. Mixed Batch Distillation
Teacher und Student trainieren gemeinsam auf Batch.
# Batch: 32 Samples
# Vordere 16: Real data
# Hintere 16: Teacher-generated data (synthetisch)
# Student sieht auch "künstliche" Beispiele vom Teacher
# → Besser auf unsichtbare Daten generalisiert
Praktische Implementierung: DistilBERT
DistilBERT ist ein gutes Real-World Beispiel.
BERT (Teacher): 110M Parameter, 12 Layers
DistilBERT (Student): 66M Parameter, 6 Layers
Training:
- Distillation Loss: KL zwischen Logits
- Task Loss: Classification/MLM auf echten Daten
- Feature Loss: Layer-Matching auf intermediate Representations
Ergebnis:
- 40% kleiner
- 60% schneller
- 97% der Qualität
Code
from transformers import AutoModelForSequenceClassification, DistilBertForSequenceClassification
# Teacher
teacher = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased")
# Student (bereits DistilBERT)
student = DistilBertForSequenceClassification.from_pretrained("distilbert-base-uncased")
# Trainieren mit Distillation
def distil_forward(student, teacher, input_ids, attention_mask, labels):
# Teacher
teacher_outputs = teacher(input_ids, attention_mask=attention_mask)
teacher_logits = teacher_outputs.logits
# Student
student_outputs = student(input_ids, attention_mask=attention_mask, labels=labels)
student_logits = student_outputs.logits
task_loss = student_outputs.loss
# Distillation
T = 3.0
soft_targets = F.softmax(teacher_logits / T, dim=-1)
student_log_probs = F.log_softmax(student_logits / T, dim=-1)
kl_loss = F.kl_div(student_log_probs, soft_targets)
# Kombiniert
loss = 0.3 * task_loss + 0.7 * kl_loss
return loss
Häufige Fehler
1. Temperature zu niedrig/hoch
❌ T=1: Zu sharp → Student lernt nur "richtige Antwort"
✅ T=3-7: Balance
❌ T=50: Zu soft → Student lernt nichts neues
2. Falsches Alpha-Verhältnis
❌ alpha=0 (nur Task Loss): Wartet nicht auf Teacher
❌ alpha=1 (nur Distillation): Ignoriert echte Labels
✅ alpha=0.3-0.7: Balance zwischen beiden
3. Student zu klein
❌ Teacher 70B → Student 1B: Zu großer Gap
Student kann nicht lernen, zu viel Differenz
✅ Teacher 70B → Student 7B: Machbar
✅ Teacher 7B → Student 1B: Besser
Regel: Student sollte >= 10% der Größe sein
4. Zu lange Training
❌ 100 Epochen auf Distillation: Overfitting
✅ 5-10 Epochen: Reicht meist
Best Practices
1. Teacher Qualität ist kritisch
# Guter Teacher
teacher = load_gpt4_api() # oder lokales großes Modell
teacher.eval()
# vs. Mittelmäßiger Teacher
teacher = load_pretrained_7b()
# → Kleine Verbesserungen nur
2. Separate Validation Sets
# Nicht auf Teacher Daten validieren!
train_dataset = teacher_generated_data # Mit Distillation
val_dataset = original_data # Task-Spezifisch
# Validiere auf val_dataset!
3. Wärm-Up Phase
# Erste 1000 Steps: Nur Task Loss
# Dann: Mix mit Distillation Loss
# Grund: Student braucht Basis-Knowledge
for step, batch in enumerate(train_loader):
if step < 1000:
loss = task_loss # Nur Task
else:
loss = 0.3 * task_loss + 0.7 * kl_loss # Mix
