Température et Sampling RAG : Contrôler la créativité du LLM
Guide complet sur les paramètres de sampling pour systèmes RAG : température, top-p, top-k, frequency penalty. Optimisez la balance entre créativité et fidélité.
TL;DR
Les paramètres de sampling (température, top-p, top-k) contrôlent le niveau de créativité et de déterminisme des réponses LLM. En RAG, ces paramètres sont critiques : trop de créativité = hallucinations, trop de déterminisme = réponses robotiques. Ce guide vous apprend à calibrer ces paramètres selon votre cas d'usage.
Comprendre les paramètres de sampling
Qu'est-ce que le sampling ?
Quand un LLM génère du texte, il prédit une distribution de probabilités sur tous les tokens possibles pour le prochain mot. Le sampling détermine comment choisir parmi ces candidats.
DEVELOPERpython# Exemple simplifié de distribution de probabilités next_token_probs = { "rapidement": 0.35, "vite": 0.25, "promptement": 0.15, "immédiatement": 0.12, "instantanément": 0.08, "...": 0.05 } # Sans sampling (greedy) : toujours "rapidement" # Avec sampling : peut choisir "vite", "promptement", etc.
Vue d'ensemble des paramètres
| Paramètre | Plage | Effet | Usage RAG typique |
|---|---|---|---|
| Temperature | 0.0 - 2.0 | Contrôle la "chaleur" de la distribution | 0.1 - 0.5 |
| Top-p | 0.0 - 1.0 | Nucleus sampling | 0.9 - 1.0 |
| Top-k | 1 - 100+ | Limite les candidats | 40 - 80 |
| Frequency penalty | -2.0 - 2.0 | Pénalise les répétitions | 0.0 - 0.5 |
| Presence penalty | -2.0 - 2.0 | Encourage la diversité | 0.0 - 0.3 |
La température en détail
Fonctionnement mathématique
La température modifie la distribution softmax des probabilités :
DEVELOPERpythonimport numpy as np def apply_temperature(logits: np.array, temperature: float) -> np.array: """ Applique la température aux logits. - temperature < 1 : distribution plus piquée (déterministe) - temperature = 1 : distribution originale - temperature > 1 : distribution plus plate (aléatoire) """ if temperature == 0: # Greedy: retourne un vecteur one-hot sur le max 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) # Exemple 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] - presque déterministe print("Temp 1.0:", apply_temperature(logits, 1.0)) # [0.42, 0.26, 0.15, 0.09, 0.07] - distribution originale print("Temp 2.0:", apply_temperature(logits, 2.0)) # [0.29, 0.23, 0.19, 0.16, 0.14] - plus uniforme
Impact visuel
Température basse (0.1) Température haute (1.5)
│ │
│ ████████████ │ ██████
│ ██ │ █████
│ █ │ ████
│ │ ███
│ │ ██
└──────────────── └────────────────
Token 1 domine Distribution plate
Recommandations par cas d'usage RAG
| Cas d'usage | Température | Justification |
|---|---|---|
| Support client factuel | 0.1 - 0.2 | Précision maximale, pas de créativité |
| FAQ automatisée | 0.2 - 0.3 | Variations légères acceptables |
| Assistant e-commerce | 0.3 - 0.5 | Un peu de personnalité |
| Rédaction assistée | 0.5 - 0.7 | Créativité contrôlée |
| Brainstorming | 0.7 - 1.0 | Idées variées bienvenues |
Top-p (Nucleus Sampling)
Comment ça marche
Top-p sélectionne les tokens dont les probabilités cumulées atteignent p :
DEVELOPERpythondef top_p_sampling(probs: dict, p: float) -> list: """ Retourne les tokens dont la probabilité cumulée atteint p. """ # Trier par probabilité décroissante 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 # Exemple 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)] # Cumul: 0.90 - les autres sont exclus
Top-p vs Température
| Critère | Température | Top-p |
|---|---|---|
| Contrôle | Global sur toute la distribution | Coupe les tokens improbables |
| Risque | Peut sélectionner des tokens très improbables | Garantit des tokens raisonnables |
| Usage | Ajuster la "confiance" | Éviter les aberrations |
Combinaison recommandée pour RAG
DEVELOPERpython# Configuration recommandée pour un chatbot support rag_config = { "temperature": 0.3, # Peu de créativité "top_p": 0.95, # Garde 95% de la masse probabiliste "top_k": 50, # Maximum 50 candidats } # La température basse rend la distribution piquée # Top-p élimine les tokens avec < 5% de masse cumulative restante # Top-k met une limite dure sur le nombre de candidats
Top-k sampling
Principe
Top-k garde uniquement les k tokens les plus probables :
DEVELOPERpythondef top_k_sampling(probs: dict, k: int) -> dict: """ Garde les k tokens les plus probables. """ sorted_tokens = sorted(probs.items(), key=lambda x: x[1], reverse=True) top_k_tokens = dict(sorted_tokens[:k]) # Renormaliser total = sum(top_k_tokens.values()) return {t: p/total for t, p in top_k_tokens.items()} # Exemple 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 et E sont exclus, les autres renormalisés
Quand utiliser Top-k
- k petit (10-20) : Réponses très conservatrices
- k moyen (40-60) : Bon équilibre (recommandé pour RAG)
- k grand (100+) : Presque pas de filtrage
Frequency et Presence Penalties
Frequency Penalty
Pénalise les tokens proportionnellement à leur fréquence dans le texte généré :
DEVELOPERpythondef apply_frequency_penalty( logits: dict, generated_tokens: list, penalty: float ) -> dict: """ Réduit la probabilité des tokens fréquemment utilisés. """ 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
Usage en RAG : Évite les réponses répétitives comme "comme mentionné précédemment..."
Presence Penalty
Pénalise tout token déjà apparu, indépendamment de la fréquence :
DEVELOPERpythondef apply_presence_penalty( logits: dict, generated_tokens: set, penalty: float ) -> dict: """ Réduit la probabilité des tokens déjà utilisés. """ 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
Usage en RAG : Encourage l'utilisation de synonymes et la variété lexicale.
Configuration optimale par modèle
OpenAI GPT-4
DEVELOPERpythonfrom openai import OpenAI client = OpenAI() # Configuration RAG recommandée 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 utilise seulement temperature et top_p 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}"} ] )
Modèles open-source (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") # Configuration RAG generation_config = { "temperature": 0.3, "top_p": 0.95, "top_k": 50, "repetition_penalty": 1.1, # Équivalent frequency_penalty "do_sample": True, "max_new_tokens": 500 } outputs = model.generate( input_ids, **generation_config )
Stratégies avancées
1. Sampling adaptatif
Ajuster les paramètres dynamiquement selon le contexte :
DEVELOPERpythonclass AdaptiveSampler: def __init__(self): self.base_temperature = 0.3 def get_config(self, query_type: str, context_quality: float) -> dict: """ Ajuste les paramètres selon le contexte. - query_type: "factual", "creative", "mixed" - context_quality: 0-1, score de pertinence du contexte """ if query_type == "factual": # Questions factuelles : très déterministe temp = 0.1 elif query_type == "creative": # Questions créatives : plus de liberté temp = 0.7 else: temp = self.base_temperature # Si le contexte est de mauvaise qualité, être plus conservateur if context_quality < 0.5: temp *= 0.5 # Réduire la température return { "temperature": temp, "top_p": 0.95 if context_quality > 0.7 else 0.85, "frequency_penalty": 0.2 } # Utilisation sampler = AdaptiveSampler() config = sampler.get_config( query_type="factual", context_quality=0.85 )
2. Température variable par section
Utiliser différentes températures selon la partie de la réponse :
DEVELOPERpythonasync def generate_structured_response(query: str, context: str): """ Génère une réponse avec des paramètres différents par section. """ # Section 1: Réponse factuelle (temp basse) factual_part = await llm.generate( prompt=f"Réponds factuellement: {query}\nContext: {context}", temperature=0.1, max_tokens=200 ) # Section 2: Explication (temp moyenne) explanation = await llm.generate( prompt=f"Explique pourquoi: {factual_part}", temperature=0.4, max_tokens=150 ) # Section 3: Suggestion (temp plus haute) suggestion = await llm.generate( prompt=f"Suggère des alternatives ou compléments", temperature=0.6, max_tokens=100 ) return { "answer": factual_part, "explanation": explanation, "suggestions": suggestion }
3. A/B Testing des paramètres
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)
Erreurs courantes
1. Température trop haute pour du factuel
DEVELOPERpython# ❌ Mauvais : température haute pour du support response = llm.generate( prompt="Quel est le délai de retour ?", temperature=1.0 # Risque d'hallucinations ! ) # ✅ Bon : température basse pour du factuel response = llm.generate( prompt="Quel est le délai de retour ?", temperature=0.2 )
2. Ignorer le context quality
DEVELOPERpython# ❌ Mauvais : même température peu importe le contexte config = {"temperature": 0.5} # ✅ Bon : adapter selon la qualité du contexte context_score = retriever.get_relevance_score(query, documents) config = { "temperature": 0.2 if context_score < 0.6 else 0.4 }
3. Combiner de façon incohérente
DEVELOPERpython# ❌ Incohérent : température basse + top_p très bas config = { "temperature": 0.1, "top_p": 0.5 # Double restriction inutile } # ✅ Cohérent : une restriction principale config = { "temperature": 0.2, "top_p": 0.95 # Juste pour éviter les aberrations }
Intégration avec Ailog
Ailog permet de configurer les paramètres de sampling directement dans l'interface :
DEVELOPERpythonfrom ailog import AilogClient client = AilogClient(api_key="your-key") # Configuration via l'interface ou l'API channel_config = { "generation": { "temperature": 0.3, "top_p": 0.95, "frequency_penalty": 0.2, "adaptive_sampling": True # Ajustement automatique } } client.update_channel_config("support-widget", channel_config)
Conclusion
Les paramètres de sampling sont des leviers puissants mais subtils. En RAG :
- Température basse (0.1-0.3) pour les réponses factuelles
- Top-p autour de 0.95 pour filtrer les aberrations
- Frequency penalty léger (0.1-0.3) pour éviter les répétitions
- Adaptation dynamique selon la qualité du contexte
- A/B testing pour trouver la configuration optimale
Ressources complémentaires
- Introduction au RAG - Fondamentaux
- Génération LLM pour RAG - Guide parent
- Prompt Engineering RAG - Optimiser les prompts
- Chain-of-Thought RAG - Raisonnement étape par étape
Besoin d'une configuration optimale sans vous casser la tête ? Essayez Ailog - paramètres pré-optimisés par cas d'usage, ajustement adaptatif inclus.
FAQ
Tags
Articles connexes
Génération RAG : Choisir et optimiser son LLM
Guide complet pour sélectionner et configurer votre LLM dans un système RAG : prompting, température, tokens et optimisation des réponses.
Agents RAG : Orchestrer des systemes multi-agents
Architecturez des systemes RAG multi-agents : orchestration, specialisation, collaboration et gestion des echecs pour des assistants complexes.
RAG Conversationnel : Memoire et contexte multi-sessions
Implementez un RAG avec memoire conversationnelle : gestion du contexte, historique multi-sessions et personnalisation des reponses.