Prompt engineering
Optimiser LLM avec DSPy : Un guide étape par étape pour construire, optimiser et évaluer les systèmes d’IA Spécifier les clés d’entrée Dans DSPy, les objets Example ont une méthode with_inputs() pour marquer des champs spécifiques comme entrées : Les valeurs peuvent être accessibles en utilisant l’opérateur de point, et les méthodes comme inputs() et labels() renvoient de nouveaux objets Example contenant uniquement les clés d’entrée ou non d’entrée, respectivement. Optimiseurs dans DSPy Un optimiseur DSPy ajuste les paramètres d’un programme DSPy (c’est-à-dire les invites et/ou les poids LM) pour maximiser les métriques spécifiées. DSPy propose divers optimiseurs intégrés, chacun employant des stratégies différentes. Optimiseurs disponibles BootstrapFewShot : Génère des exemples à quelques coups en utilisant les points de données étiquetés d’entrée et de sortie fournis. BootstrapFewShotWithRandomSearch : Applique BootstrapFewShot plusieurs fois avec une recherche aléatoire sur les démonstrations générées. COPRO : Génère et affine de nouvelles instructions pour chaque étape, en les optimisant avec une montée de coordonnées. MIPRO : Optimise les instructions et les exemples à quelques coups en utilisant l’optimisation bayésienne. Choisir un optimiseur Si vous êtes incertain sur où commencer, utilisez BootstrapFewShotWithRandomSearch : Pour très peu de données (10 exemples), utilisez BootstrapFewShot. Pour un peu plus de données (50 exemples), utilisez BootstrapFewShotWithRandomSearch. Pour des jeux de données plus importants (300+ exemples), utilisez MIPRO.
À mesure que les capacités des grands modèles de langage (LLM) continuent de s’étendre, développer des systèmes d’IA robustes qui tirent parti de leur potentiel est devenu de plus en plus complexe. Les approches conventionnelles impliquent souvent des techniques d’invocation intriquées, la génération de données pour le fine-tuning et une guidance manuelle pour assurer le respect des contraintes spécifiques au domaine. Cependant, ce processus peut être fastidieux, sujet à des erreurs et fortement dépendant de l’intervention humaine.
Entrez DSPy, un cadre révolutionnaire conçu pour rationaliser le développement de systèmes d’IA alimentés par les LLM. DSPy introduit une approche systématique pour optimiser les invites LM et les poids, permettant aux développeurs de construire des applications sophistiquées avec un minimum d’efforts manuels.
Dans ce guide complet, nous allons explorer les principes fondamentaux de DSPy, son architecture modulaire et la gamme de fonctionnalités puissantes qu’il propose. Nous allons également plonger dans des exemples pratiques, démontrant comment DSPy peut transformer la façon dont vous développez des systèmes d’IA avec les LLM.
Qu’est-ce que DSPy, et pourquoi en avez-vous besoin ?
DSPy est un cadre qui sépare le flux de votre programme (modules) des paramètres (invites LM et poids) de chaque étape. Cette séparation permet l’optimisation systématique des invites LM et des poids, vous permettant de construire des systèmes d’IA complexes avec une plus grande fiabilité, prévisibilité et respect des contraintes spécifiques au domaine.
Traditionnellement, le développement de systèmes d’IA avec les LLM impliquait un processus fastidieux de décomposition du problème en étapes, d’élaboration d’invites intriquées pour chaque étape, de génération d’exemples synthétiques pour le fine-tuning et de guidance manuelle pour que les LM respectent des contraintes spécifiques. Cette approche n’était pas seulement chronophage mais également sujette à des erreurs, car même de petits changements dans le pipeline, le LM ou les données pouvaient nécessiter une refonte extensive des invites et des étapes de fine-tuning.
DSPy répond à ces défis en introduisant un nouveau paradigme : les optimiseurs. Ces algorithmes basés sur les LM peuvent ajuster les invites et les poids de vos appels LM, étant donné une métrique que vous souhaitez maximiser. En automatisant le processus d’optimisation, DSPy permet aux développeurs de construire des systèmes d’IA robustes avec une intervention manuelle minimale, améliorant ainsi la fiabilité et la prévisibilité des sorties LM.
Architecture modulaire de DSPy
Au cœur de DSPy se trouve une architecture modulaire qui facilite la composition de systèmes d’IA complexes. Le cadre propose un ensemble de modules intégrés qui abstraient diverses techniques d’invocation, telles que dspy.ChainOfThought et dspy.ReAct. Ces modules peuvent être combinés et composés en programmes plus grands, permettant aux développeurs de construire des pipelines intriqués adaptés à leurs besoins spécifiques.
Chaque module encapsule des paramètres apprenables, y compris les instructions, les exemples à quelques coups et les poids LM. Lorsqu’un module est invoqué, les optimiseurs de DSPy peuvent affiner ces paramètres pour maximiser la métrique souhaitée, garantissant que les sorties LM respectent les contraintes et les exigences spécifiées.
Optimisation avec DSPy
DSPy introduit une gamme d’optimiseurs puissants conçus pour améliorer les performances et la fiabilité de vos systèmes d’IA. Ces optimiseurs utilisent des algorithmes basés sur les LM pour ajuster les invites et les poids de vos appels LM, maximisant la métrique spécifiée tout en respectant les contraintes spécifiques au domaine.
Certains des optimiseurs clés disponibles dans DSPy incluent :
- BootstrapFewShot : Cet optimiseur étend la signature en générant et en incluant automatiquement des exemples optimisés dans l’invite envoyée au modèle, mettant en œuvre l’apprentissage à quelques coups.
- BootstrapFewShotWithRandomSearch : Applique
BootstrapFewShotplusieurs fois avec une recherche aléatoire sur les démonstrations générées, sélectionnant le meilleur programme sur l’optimisation. - MIPRO : Génère des instructions et des exemples à quelques coups à chaque étape, avec la génération d’instructions étant consciente des données et des démonstrations. Il utilise l’optimisation bayésienne pour effectuer une recherche efficace dans l’espace des instructions de génération et des démonstrations à travers vos modules.
- BootstrapFinetune : Distille un programme DSPy basé sur les invites en mises à jour de poids pour les LLM plus petits, vous permettant de fine-tuner les LLM sous-jacents pour une efficacité améliorée.
En utilisant ces optimiseurs, les développeurs peuvent optimiser systématiquement leurs systèmes d’IA, garantissant des sorties de haute qualité tout en respectant les contraintes et les exigences spécifiques au domaine.
Commencer avec DSPy
Pour illustrer la puissance de DSPy, décomposons un exemple pratique de construction d’un système de génération assistée par récupération (RAG) pour la question-réponse.
Étape 1 : Configurer le modèle de langage et le modèle de récupération
La première étape consiste à configurer le modèle de langage (LM) et le modèle de récupération (RM) dans DSPy.
Pour installer DSPy, exécutez :
pip install dspy-ai
DSPy prend en charge plusieurs API LM et RM, ainsi que l’hébergement de modèles locaux, ce qui facilite l’intégration de vos modèles préférés.
import dspy # Configurez le LM et le RM turbo = dspy.OpenAI(model='gpt-3.5-turbo') colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts') dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)
Étape 2 : Charger le jeu de données
Ensuite, nous allons charger le jeu de données HotPotQA, qui contient une collection de paires question-réponse complexes généralement répondues de manière multihop.
from dspy.datasets import HotPotQA
# Charger le jeu de données
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)
# Spécifier le champ 'question' comme entrée
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]
Étape 3 : Construire des signatures
DSPy utilise des signatures pour définir le comportement des modules. Dans cet exemple, nous allons définir une signature pour la tâche de génération de réponses, en spécifiant les champs d’entrée (contexte et question) et le champ de sortie (réponse).
class GenerateAnswer(dspy.Signature): """Répondez aux questions avec des réponses factuelles courtes.""" context = dspy.InputField(desc="peut contenir des faits pertinents") question = dspy.InputField() answer = dspy.OutputField(desc="souvent entre 1 et 5 mots")
Étape 4 : Construire le pipeline
Nous allons construire notre pipeline RAG en tant que module DSPy, qui consiste en une méthode d’initialisation (__init__) pour déclarer les sous-modules (dspy.Retrieve et dspy.ChainOfThought) et une méthode forward (forward) pour décrire le flux de contrôle de la réponse à la question en utilisant ces modules.
class RAG(dspy.Module): def __init__(self, num_passages=3): super().__init__() self.retrieve = dspy.Retrieve(k=num_passages) self.generate_answer = dspy.ChainOfThought(GenerateAnswer) def forward(self, question): context = self.retrieve(question).passages prediction = self.generate_answer(context=context, question=question) return dspy.Prediction(context=context, answer=prediction.answer)
Étape 5 : Optimiser le pipeline
Avec le pipeline défini, nous pouvons maintenant l’optimiser en utilisant les optimiseurs de DSPy. Dans cet exemple, nous allons utiliser l’optimiseur BootstrapFewShot, qui génère et sélectionne des invites efficaces pour nos modules en fonction d’un jeu de formation et d’une métrique de validation.
from dspy.teleprompt import BootstrapFewShot # Métrique de validation def validate_context_and_answer(example, pred, trace=None): answer_EM = dspy.evaluate.answer_exact_match(example, pred) answer_PM = dspy.evaluate.answer_passage_match(example, pred) return answer_EM and answer_PM # Configurer l'optimiseur teleprompter = BootstrapFewShot(metric=validate_context_and_answer) # Compiler le programme compiled_rag = teleprompter.compile(RAG(), trainset=trainset)
Étape 6 : Évaluer le pipeline
Après avoir compilé le programme, il est essentiel de l’évaluer sur un jeu de développement pour garantir qu’il répond aux niveaux de précision et de fiabilité souhaités.
from dspy.evaluate import Evaluate
# Configurer l'évaluateur
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)
# Évaluer le programme RAG compilé
evaluation_result = evaluate(compiled_rag)
print(f"Résultat de l'évaluation : {evaluation_result}")
Étape 7 : Inspecter l’historique du modèle
Pour une compréhension plus approfondie des interactions du modèle, vous pouvez examiner les générations les plus récentes en inspectant l’historique du modèle.
# Inspecter l'historique du modèle turbo.inspect_history(n=1)
Étape 8 : Faire des prédictions
Avec le pipeline optimisé et évalué, vous pouvez maintenant l’utiliser pour faire des prédictions sur de nouvelles questions.
# Exemple de question
question = "Quel prix le premier livre de Gary Zukav a-t-il reçu ?"
# Faire une prédiction en utilisant le programme RAG compilé
prediction = compiled_rag(question)
print(f"Question : {question}")
print(f"Réponse : {prediction.answer}")
print(f"Contextes récupérés : {prediction.context}")
Exemple minimal fonctionnel avec DSPy
Maintenant, décomposons un autre exemple minimal fonctionnel en utilisant le jeu de données GSM8K et le modèle OpenAI GPT-3.5-turbo pour simuler des tâches d’invocation dans DSPy.
Configuration
Tout d’abord, assurez-vous que votre environnement est correctement configuré :
import dspy from dspy.datasets.gsm8k import GSM8K, gsm8k_metric # Configurer le LM turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250) dspy.settings.configure(lm=turbo) # Charger les questions mathématiques du jeu de données GSM8K gsm8k = GSM8K() gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10] print(gsm8k_trainset)
Le jeu de données gsm8k_trainset et gsm8k_devset contient une liste d’exemples avec chaque exemple ayant un champ question et réponse.
Définir le module
Ensuite, définissez un programme personnalisé en utilisant le module ChainOfThought pour la raisonnement étape par étape :
class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("question -> answer")
def forward(self, question):
return self.prog(question=question)
Compiler et évaluer le modèle
Maintenant, compilez-le avec le téléprompteur BootstrapFewShot :
from dspy.teleprompt import BootstrapFewShot # Configurer l'optimiseur config = dict(max_bootstrapped_demos=4, max_labeled_demos=4) # Optimiser en utilisant la métrique gsm8k_metric teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config) optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset) # Configurer l'évaluateur from dspy.evaluate import Evaluate evaluate = Evaluate(devset=gsm8k_devset, metric=gsm8k_metric, num_threads=4, display_progress=True, display_table=0) evaluate(optimized_cot) # Inspecter l'historique du modèle turbo.inspect_history(n=1)
Cet exemple démontre comment configurer votre environnement, définir un module personnalisé, compiler un modèle et évaluer rigoureusement ses performances en utilisant le jeu de données et la configuration du téléprompteur fournis.
Gestion des données dans DSPy
DSPy fonctionne avec des ensembles de formation, de développement et de test. Pour chaque exemple dans vos données, vous avez généralement trois types de valeurs : les entrées, les étiquettes intermédiaires et les étiquettes finales. Bien que les étiquettes intermédiaires ou finales soient facultatives, avoir quelques exemples d’entrée est essentiel.
Créer des objets d’exemple
Les objets d’exemple dans DSPy sont similaires aux dictionnaires Python mais viennent avec des utilitaires utiles :
qa_pair = dspy.Example(question="C'est une question ?", answer="C'est une réponse.") print(qa_pair) print(qa_pair.question) print(qa_pair.answer)












