Temperatur und RAG-Sampling: Die Kreativität des LLM steuern
Umfassender Leitfaden zu Sampling-Parametern für RAG-Systeme: Temperatur, top-p, top-k, frequency penalty. Optimieren Sie das Gleichgewicht zwischen Kreativität und Treue.
TL;DR
Die paramètres de sampling (Temperatur, top-p, top-k) steuern das Niveau an Kreativität und Determinismus der Antworten eines LLM. In RAG sind diese Parameter kritisch: zu viel Kreativität = Halluzinationen, zu viel Determinismus = robotische Antworten. Dieser Leitfaden zeigt, wie Sie diese Parameter nach Ihrem Anwendungsfall kalibrieren.
Die Sampling-Parameter verstehen
Was ist Sampling?
Wenn ein LLM Text generiert, sagt es eine Wahrscheinlichkeitsverteilung über alle möglichen tokens für das nächste Wort voraus. Das sampling bestimmt, wie aus diesen Kandidaten ausgewählt wird.
DEVELOPERpython# Vereinfachtes Beispiel einer Wahrscheinlichkeitsverteilung next_token_probs = { "rapidement": 0.35, "vite": 0.25, "promptement": 0.15, "immédiatement": 0.12, "instantanément": 0.08, "...": 0.05 } # Ohne Sampling (greedy): immer "rapidement" # Mit Sampling: kann "vite", "promptement", etc. wählen
Überblick über die Parameter
| Parameter | Bereich | Wirkung | Typische RAG-Verwendung |
|---|---|---|---|
| Temperature | 0.0 - 2.0 | Steuert die "Wärme" der Verteilung | 0.1 - 0.5 |
| Top-p | 0.0 - 1.0 | Nucleus Sampling | 0.9 - 1.0 |
| Top-k | 1 - 100+ | Begrenzt die Kandidaten | 40 - 80 |
| Frequency penalty | -2.0 - 2.0 | Bestraft Wiederholungen | 0.0 - 0.5 |
| Presence penalty | -2.0 - 2.0 | Fördert Vielfalt | 0.0 - 0.3 |
Die Temperatur im Detail
Mathematische Funktionsweise
Die Temperatur verändert die Softmax-Wahrscheinlichkeitsverteilung:
DEVELOPERpythonimport numpy as np def apply_temperature(logits: np.array, temperature: float) -> np.array: """ Wendet die Temperatur auf die Logits an. - temperature < 1 : schärfere Verteilung (deterministisch) - temperature = 1 : ursprüngliche Verteilung - temperature > 1 : flachere Verteilung (zufälliger) """ if temperature == 0: # Greedy: gibt einen One-Hot-Vektor für das Maximum zurück result = np.zeros_like(logits) result[np.argmax(logits)] = 1.0 return result scaled_logits = logits / temperature exp_logits = np.exp(scaled_logits - np.max(scaled_logits)) return exp_logits / np.sum(exp_logits) # Beispiel logits = np.array([2.0, 1.5, 1.0, 0.5, 0.2]) print("Temp 0.1:", apply_temperature(logits, 0.1)) # [0.99, 0.01, 0.00, 0.00, 0.00] - fast deterministisch print("Temp 1.0:", apply_temperature(logits, 1.0)) # [0.42, 0.26, 0.15, 0.09, 0.07] - ursprüngliche Verteilung print("Temp 2.0:", apply_temperature(logits, 2.0)) # [0.29, 0.23, 0.19, 0.16, 0.14] - gleichmäßigere Verteilung
Visuelle Darstellung
Niedrige Temperatur (0.1) Hohe Temperatur (1.5)
│ │
│ ████████████ │ ██████
│ ██ │ █████
│ █ │ ████
│ │ ███
│ │ ██
└──────────────── └────────────────
Token 1 domine Distribution plate
Empfehlungen nach RAG-Anwendungsfall
| Anwendungsfall | Temperatur | Begründung |
|---|---|---|
| Faktischer Kundensupport | 0.1 - 0.2 | Maximale Präzision, keine Kreativität |
| Automatisierte FAQ | 0.2 - 0.3 | Leichte Variationen akzeptabel |
| E-Commerce-Assistent | 0.3 - 0.5 | Etwas Persönlichkeit |
| Unterstütztes Schreiben | 0.5 - 0.7 | Kontrollierte Kreativität |
| Brainstorming | 0.7 - 1.0 | Vielfältige Ideen willkommen |
Top-p (Nucleus Sampling)
Wie es funktioniert
Top-p wählt die Tokens aus, deren kumulierte Wahrscheinlichkeiten p erreichen:
DEVELOPERpythondef top_p_sampling(probs: dict, p: float) -> list: """ Gibt die Tokens zurück, deren kumulierte Wahrscheinlichkeit p erreicht. """ # Nach absteigender Wahrscheinlichkeit sortieren sorted_tokens = sorted(probs.items(), key=lambda x: x[1], reverse=True) cumulative_prob = 0.0 selected_tokens = [] for token, prob in sorted_tokens: cumulative_prob += prob selected_tokens.append((token, prob)) if cumulative_prob >= p: break return selected_tokens # Beispiel probs = { "Le": 0.40, "La": 0.25, "Un": 0.15, "Une": 0.10, "Ce": 0.05, "Cette": 0.03, "Mon": 0.02 } print(top_p_sampling(probs, 0.9)) # [("Le", 0.40), ("La", 0.25), ("Un", 0.15), ("Une", 0.10)] # Kumuliert: 0.90 - die anderen werden ausgeschlossen
Top-p vs. Temperatur
| Kriterium | Temperatur | Top-p |
|---|---|---|
| Kontrolle | Global über die gesamte Verteilung | Schneidet unwahrscheinliche Tokens ab |
| Risiko | Kann sehr unwahrscheinliche Tokens auswählen | Garantiert plausible Tokens |
| Verwendung | "Konfidenz" einstellen | Ausreißer vermeiden |
Empfohlene Kombination für RAG
DEVELOPERpython# Empfohlene Konfiguration für einen Support-Chatbot rag_config = { "temperature": 0.3, # Wenig Kreativität "top_p": 0.95, # Behält 95 % der Wahrscheinlichkeitsmasse "top_k": 50, # Maximal 50 Kandidaten } # Die niedrige Temperatur macht die Verteilung spitzer # Top-p eliminiert Tokens mit < 5% restlicher kumulativer Masse # Top-k setzt eine harte Grenze für die Anzahl der Kandidaten
Top-k sampling
Prinzip
Top-k behält nur die k wahrscheinlichsten Tokens:
DEVELOPERpythondef top_k_sampling(probs: dict, k: int) -> dict: """ Behält die k wahrscheinlichsten Tokens. """ sorted_tokens = sorted(probs.items(), key=lambda x: x[1], reverse=True) top_k_tokens = dict(sorted_tokens[:k]) # Renormalisieren total = sum(top_k_tokens.values()) return {t: p/total for t, p in top_k_tokens.items()} # Beispiel probs = {"A": 0.3, "B": 0.25, "C": 0.2, "D": 0.15, "E": 0.1} print(top_k_sampling(probs, 3)) # {"A": 0.40, "B": 0.33, "C": 0.27} # D und E werden ausgeschlossen, die anderen renormalisiert
Wann Top-k verwenden
- k klein (10-20): Sehr konservative Antworten
- k mittel (40-60): Gutes Gleichgewicht (empfohlen für RAG)
- k groß (100+): Fast keine Filterung
Frequency- und Presence-Penalty
Frequency Penalty
Bestraft Tokens proportional zu ihrer Häufigkeit im generierten Text:
DEVELOPERpythondef apply_frequency_penalty( logits: dict, generated_tokens: list, penalty: float ) -> dict: """ Reduziert die Wahrscheinlichkeit häufig verwendeter Tokens. """ token_counts = {} for token in generated_tokens: token_counts[token] = token_counts.get(token, 0) + 1 adjusted_logits = {} for token, logit in logits.items(): count = token_counts.get(token, 0) adjusted_logits[token] = logit - (penalty * count) return adjusted_logits
Verwendung in RAG: Vermeidet repetitive Antworten wie "wie bereits erwähnt..."
Presence Penalty
Bestraft jeden bereits aufgetretenen Token, unabhängig von der Häufigkeit:
DEVELOPERpythondef apply_presence_penalty( logits: dict, generated_tokens: set, penalty: float ) -> dict: """ Reduziert die Wahrscheinlichkeit bereits verwendeter Tokens. """ adjusted_logits = {} for token, logit in logits.items(): if token in generated_tokens: adjusted_logits[token] = logit - penalty else: adjusted_logits[token] = logit return adjusted_logits
Verwendung in RAG: Fördert den Einsatz von Synonymen und lexikalische Vielfalt.
Optimale Konfiguration nach Modell
OpenAI GPT-4
DEVELOPERpythonfrom openai import OpenAI client = OpenAI() # Empfohlene RAG-Konfiguration response = client.chat.completions.create( model="gpt-4", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": f"Context: {context}\n\nQuestion: {query}"} ], temperature=0.3, top_p=0.95, frequency_penalty=0.2, presence_penalty=0.1, max_tokens=500 )
Anthropic Claude
DEVELOPERpythonimport anthropic client = anthropic.Anthropic() # Claude unterstützt temperature, top_p und top_k (aber kein frequency/presence penalty) response = client.messages.create( model="claude-3-opus-20240229", max_tokens=500, temperature=0.3, top_p=0.95, messages=[ {"role": "user", "content": f"Context: {context}\n\nQuestion: {query}"} ] )
Open-Source-Modelle (Llama, Mistral)
DEVELOPERpythonfrom transformers import AutoModelForCausalLM, AutoTokenizer model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-Instruct-v0.2") tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.2") # RAG-Konfiguration generation_config = { "temperature": 0.3, "top_p": 0.95, "top_k": 50, "repetition_penalty": 1.1, # Entspricht frequency_penalty "do_sample": True, "max_new_tokens": 500 } outputs = model.generate( input_ids, **generation_config )
Fortgeschrittene Strategien
1. Adaptives Sampling
Die Parameter dynamisch je nach Kontext anpassen:
DEVELOPERpythonclass AdaptiveSampler: def __init__(self): self.base_temperature = 0.3 def get_config(self, query_type: str, context_quality: float) -> dict: """ Passt die Parameter je nach Kontext an. - query_type: "factual", "creative", "mixed" - context_quality: 0-1, Relevanzscore des Kontexts """ if query_type == "factual": # Fragen zu Fakten: sehr deterministisch temp = 0.1 elif query_type == "creative": # Kreative Fragen: mehr Freiheit temp = 0.7 else: temp = self.base_temperature # Wenn der Kontext schlecht ist, konservativer sein if context_quality < 0.5: temp *= 0.5 # Temperatur reduzieren return { "temperature": temp, "top_p": 0.95 if context_quality > 0.7 else 0.85, "frequency_penalty": 0.2 } # Verwendung sampler = AdaptiveSampler() config = sampler.get_config( query_type="factual", context_quality=0.85 )
2. Variable Temperatur pro Abschnitt
Je nach Teil der Antwort unterschiedliche Temperaturen verwenden:
DEVELOPERpythonasync def generate_structured_response(query: str, context: str): """ Generiert eine Antwort mit unterschiedlichen Parametern pro Abschnitt. """ # Abschnitt 1: Faktenantwort (niedrige Temperatur) factual_part = await llm.generate( prompt=f"Antworte sachlich: {query}\nContext: {context}", temperature=0.1, max_tokens=200 ) # Abschnitt 2: Erklärung (mittlere Temperatur) explanation = await llm.generate( prompt=f"Erkläre warum: {factual_part}", temperature=0.4, max_tokens=150 ) # Abschnitt 3: Vorschlag (höhere Temperatur) suggestion = await llm.generate( prompt=f"Schlage Alternativen oder Ergänzungen vor", temperature=0.6, max_tokens=100 ) return { "answer": factual_part, "explanation": explanation, "suggestions": suggestion }
3. A/B-Testing der Parameter
DEVELOPERpythonimport random from dataclasses import dataclass @dataclass class SamplingVariant: name: str temperature: float top_p: float frequency_penalty: float class SamplingABTester: def __init__(self): self.variants = [ SamplingVariant("conservative", 0.1, 0.9, 0.0), SamplingVariant("balanced", 0.3, 0.95, 0.2), SamplingVariant("creative", 0.5, 1.0, 0.3), ] self.results = {v.name: {"count": 0, "satisfaction": []} for v in self.variants} def get_variant(self) -> SamplingVariant: return random.choice(self.variants) def record_feedback(self, variant_name: str, satisfaction: float): self.results[variant_name]["count"] += 1 self.results[variant_name]["satisfaction"].append(satisfaction) def get_best_variant(self) -> str: avg_scores = { name: sum(data["satisfaction"]) / max(len(data["satisfaction"]), 1) for name, data in self.results.items() } return max(avg_scores, key=avg_scores.get)
Häufige Fehler
1. Zu hohe Temperatur für Faktisches
DEVELOPERpython# ❌ Schlecht: zu hohe Temperatur für Support response = llm.generate( prompt="Wie lange ist die Rückgabefrist?", temperature=1.0 # Risiko von Halluzinationen ! ) # ✅ Gut: niedrige Temperatur für Faktenfragen response = llm.generate( prompt="Wie lange ist die Rückgabefrist?", temperature=0.2 )
2. Kontextqualität ignorieren
DEVELOPERpython# ❌ Schlecht: gleiche Temperatur unabhängig vom Kontext config = {"temperature": 0.5} # ✅ Gut: an die Qualität des Kontexts anpassen context_score = retriever.get_relevance_score(query, documents) config = { "temperature": 0.2 if context_score < 0.6 else 0.4 }
3. Inkonsistente Kombination
DEVELOPERpython# ❌ Inkonsistent: niedrige Temperatur + sehr niedriges top_p config = { "temperature": 0.1, "top_p": 0.5 # unnötige doppelte Einschränkung } # ✅ Konsistent: eine hauptsächliche Einschränkung config = { "temperature": 0.2, "top_p": 0.95 # Nur um Ausreißer zu vermeiden }
Integration mit Ailog
Ailog ermöglicht die Konfiguration der Sampling-Parameter direkt in der Oberfläche:
DEVELOPERpythonfrom ailog import AilogClient client = AilogClient(api_key="your-key") # Konfiguration über die Oberfläche oder die API channel_config = { "generation": { "temperature": 0.3, "top_p": 0.95, "frequency_penalty": 0.2, "adaptive_sampling": True # automatische Anpassung } } client.update_channel_config("support-widget", channel_config)
Fazit
Die Sampling-Parameter sind mächtige, aber subtile Stellschrauben. In RAG:
- Niedrige Temperatur (0.1-0.3) für faktische Antworten
- Top-p um 0.95, um Ausreißer zu filtern
- Leichtes Frequency Penalty (0.1-0.3) zur Vermeidung von Wiederholungen
- Dynamische Anpassung je nach Kontextqualität
- A/B-Testing um die optimale Konfiguration zu finden
Ergänzende Ressourcen
- Einführung in RAG - Grundlagen
- LLM-Generierung für RAG - Übergeordneter Leitfaden
- Prompt Engineering RAG - Prompts optimieren
- Chain-of-Thought RAG - Schrittweises Reasoning
Brauchen Sie eine optimale Konfiguration ohne Kopfzerbrechen? Testen Sie Ailog - vorab optimierte Parameter nach Anwendungsfall, adaptives Tuning inklusive.
FAQ
Tags
Verwandte Artikel
RAG-Generierung: LLM auswählen und optimieren
Umfassender Leitfaden zur Auswahl und Konfiguration Ihres LLM in einem RAG-System: prompting, temperature, tokens und Optimierung der Antworten.
RAG-Agenten: Orchestrierung von Multi-Agenten-Systemen
Konzipieren Sie RAG-basierte Multi-Agenten-Systeme: Orchestrierung, Spezialisierung, Zusammenarbeit und Fehlerbehandlung für komplexe Assistenten.
Konversationelles RAG: Gedächtnis und Kontext über mehrere Sitzungen
Implementieren Sie ein RAG mit konversationellem Gedächtnis: Verwaltung des Kontexts, Verlauf über mehrere Sitzungen und Personalisierung der Antworten.