Suivez nous sur

Ingénierie rapide

Optimiser le LLM avec DSPy : un guide étape par étape pour créer, optimiser et évaluer des systèmes d'IA

mm

Publié le

 on

DSPy est un framework pour optimiser algorithmiquement les invites et les poids LM

À mesure que les capacités des grands modèles linguistiques (LLM) continuent de se développer, le développement de systèmes d’IA robustes qui exploitent leur potentiel est devenu de plus en plus complexe. Les approches conventionnelles impliquent souvent des techniques d'incitation complexes, la génération de données pour un réglage précis et des conseils manuels pour garantir le respect des contraintes spécifiques au domaine. Cependant, ce processus peut être fastidieux, sujet aux erreurs et fortement dépendant de l’intervention humaine.

Entrer DSpy, un cadre révolutionnaire conçu pour rationaliser le développement de systèmes d'IA alimentés par des LLM. DSPy introduit une approche systématique pour optimiser les invites et les pondérations LM, permettant aux développeurs de créer des applications sophistiquées avec un minimum d'effort manuel.

Dans ce guide complet, nous explorerons les principes fondamentaux de DSPy, son architecture modulaire et la gamme de fonctionnalités puissantes qu'il offre. Nous plongerons également dans des exemples pratiques, démontrant comment DSPy peut transformer la façon dont vous développez des systèmes d'IA avec des LLM.

Qu’est-ce que DSPy et pourquoi en avez-vous besoin ?

DSPy est un framework qui sépare le flux de votre programme (modules) à partir des paramètres (invites LM et poids) de chaque étape. Cette séparation permet l'optimisation systématique des invites et des pondérations LM, vous permettant de créer 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 des LLM impliquait un processus laborieux consistant à décomposer le problème en étapes, à créer des invites complexes pour chaque étape, à générer des exemples synthétiques pour un réglage précis et à guider manuellement les LM pour qu'ils respectent des contraintes spécifiques. Cette approche prenait non seulement du temps, mais était également sujette à des erreurs, car même des modifications mineures du pipeline, du LM ou des données pouvaient nécessiter une refonte approfondie des invites et des étapes de réglage précis.

DSPy répond à ces défis en introduisant un nouveau paradigme : optimiseurs. Ces algorithmes basés sur LM peuvent ajuster les invites et les pondérations de vos appels LM, en fonction d'une métrique que vous souhaitez maximiser. En automatisant le processus d'optimisation, DSPy permet aux développeurs de créer des systèmes d'IA robustes avec une intervention manuelle minimale, améliorant ainsi la fiabilité et la prévisibilité des sorties LM.

L'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 framework fournit un ensemble de modules intégrés qui résument diverses techniques d'invite, telles que dspy.ChainOfThought et dspy.ReAct. Ces modules peuvent être combinés et composés en programmes plus vastes, permettant aux développeurs de créer des pipelines complexes adaptés à leurs besoins spécifiques.

Chaque module encapsule les paramètres apprenables, y compris les instructions, quelques exemples de prises de vue 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 ainsi que les sorties du LM respectent les contraintes et exigences spécifiées.

Optimisation avec DSpy

DSPy présente une gamme d'optimiseurs puissants conçus pour améliorer les performances et la fiabilité de vos systèmes d'IA. Ces optimiseurs exploitent des algorithmes basés sur LM pour ajuster les invites et les pondérations de vos appels LM, maximisant ainsi la métrique spécifiée tout en respectant les contraintes spécifiques au domaine.

Certains des optimiseurs clés disponibles dans DSpy incluent :

  1. 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 un apprentissage en quelques étapes.
  2. BootstrapFewShotWithRandomSearch: S'applique BootstrapFewShot plusieurs fois avec recherche aléatoire sur les démonstrations générées, en sélectionnant le meilleur programme lors de l'optimisation.
  3. MIPRO: génère des instructions et quelques exemples à chaque étape, la génération d'instructions étant tenant compte des données et de la démonstration. Il utilise l'optimisation bayésienne pour rechercher efficacement dans l'espace des instructions de génération et des démonstrations dans vos modules.
  4. BootstrapFinetune: distille un programme DSPy basé sur des invites en mises à jour de poids pour les petits LM, vous permettant d'affiner le(s) LLM sous-jacent(s) pour une efficacité accrue.

En tirant parti de ces optimiseurs, les développeurs peuvent optimiser systématiquement leurs systèmes d'IA, garantissant des résultats de haute qualité tout en respectant les contraintes et exigences spécifiques au domaine.

Premiers pas avec DSpy

Pour illustrer la puissance de DSPy, passons en revue un exemple pratique de création d'un système de génération augmentée par récupération (RAG) pour la réponse aux questions.

Étape 1 : Configuration du modèle de langage et du 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
# Configure the LM and 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 : Chargement de l'ensemble de données

Ensuite, nous chargerons l'ensemble de données HotPotQA, qui contient une collection de paires questions-réponses complexes généralement répondues de manière multi-sauts.

from dspy.datasets import HotPotQA
# Load the dataset
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)
# Specify the 'question' field as the input
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]

Étape 3 : Création de signatures

DSPy utilise des signatures pour définir le comportement des modules. Dans cet exemple, nous définirons une signature pour la tâche de génération de réponse, en spécifiant les champs de saisie (contexte et question) et le champ de sortie (réponse).

class GenerateAnswer(dspy.Signature):
"""Answer questions with short factoid answers."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")

É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 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

Une fois le pipeline défini, nous pouvons désormais l'optimiser à l'aide des optimiseurs de DSPy. Dans cet exemple, nous utiliserons l'optimiseur BootstrapFewShot, qui génère et sélectionne des invites efficaces pour nos modules en fonction d'un ensemble de formation et d'une métrique de validation.

from dspy.teleprompt import BootstrapFewShot
# Validation metric
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
# Set up the optimizer
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)
# Compile the program
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)

Étape 6 : Évaluation du pipeline

Après avoir compilé le programme, il est essentiel d'évaluer ses performances sur un ensemble de développement pour s'assurer qu'il répond à la précision et à la fiabilité souhaitées.

from dspy.evaluate import Evaluate
# Set up the evaluator
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)
# Evaluate the compiled RAG program
evaluation_result = evaluate(compiled_rag)
print(f"Evaluation Result: {evaluation_result}")

Étape 7 : Inspection de l'historique du modèle

Pour une compréhension plus approfondie des interactions du modèle, vous pouvez passer en revue les générations les plus récentes en inspectant l'historique du modèle.

# Inspect the model's history
turbo.inspect_history(n=1)

Étape 8 : Faire des prédictions

Une fois le pipeline optimisé et évalué, vous pouvez désormais l'utiliser pour faire des prédictions sur de nouvelles questions.

# Example question
question = "Which award did Gary Zukav's first book receive?"
# Make a prediction using the compiled RAG program
prediction = compiled_rag(question)
print(f"Question: {question}")
print(f"Answer: {prediction.answer}")
print(f"Retrieved Contexts: {prediction.context}")

Exemple de travail minimal avec DSpy

Passons maintenant à un autre exemple de travail minimal utilisant le Ensemble de données GSM8K et le modèle OpenAI GPT-3.5-turbo pour simuler des tâches d'invite dans DSPy.

installation

Tout d’abord, assurez-vous que votre environnement est correctement configuré :

import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric
# Set up the LM
turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250)
dspy.settings.configure(lm=turbo)
# Load math questions from the GSM8K dataset
gsm8k = GSM8K()
gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]
print(gsm8k_trainset)

Le plus gsm8k_trainset et gsm8k_devset les ensembles de données contiennent une liste d'exemples, chaque exemple ayant un champ de questions et de réponses.

Définir le module

Ensuite, définissez un programme personnalisé en utilisant le module ChainOfThought pour un 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 BootstrapFewShot téléprompteur :

from dspy.teleprompt import BootstrapFewShot
# Set up the optimizer
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)
# Optimize using the gsm8k_metric
teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config)
optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)
# Set up the evaluator
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)
# Inspect the model's history
turbo.inspect_history(n=1)

Cet exemple montre comment configurer votre environnement, définir un module personnalisé, compiler un modèle et évaluer rigoureusement ses performances à l'aide des configurations d'ensemble de données et de téléprompteur fournies.

Gestion des données dans DSpy

DSPy fonctionne avec des ensembles de formation, de développement et de test. Pour chaque exemple de vos données, vous disposez généralement de trois types de valeurs : entrées, étiquettes intermédiaires et étiquettes finales. Bien que les étiquettes intermédiaires ou finales soient facultatives, il est essentiel de disposer de quelques exemples d’entrées.

Création d'exemples d'objets

Les exemples d'objets dans DSPy sont similaires aux dictionnaires Python mais sont livrés avec des utilitaires utiles :

qa_pair = dspy.Example(question="This is a question?", answer="This is an answer.")
print(qa_pair)
print(qa_pair.question)
print(qa_pair.answer)

Sortie :

Example({'question': 'This is a question?', 'answer': 'This is an answer.'}) (input_keys=None)
This is a question?
This is an answer.

Spécification des touches de saisie

Dans DSPy, les exemples d'objets ont une méthode with_inputs() pour marquer des champs spécifiques comme entrées :

print(qa_pair.with_inputs("question"))
print(qa_pair.with_inputs("question", "answer"))

Les valeurs sont accessibles à l'aide de l'opérateur point, et des méthodes telles que inputs() et labels() renvoient de nouveaux exemples d'objets contenant uniquement des clés d'entrée ou non-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 de quelques plans à l'aide des points de données d'entrée et de sortie étiquetés 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 la remontée coordonnée.
  • MIPRO : optimise les instructions et les exemples de quelques plans à l'aide de l'optimisation bayésienne.

Choisir un optimiseur

Si vous ne savez pas par 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 ensembles de données plus volumineux (plus de 300 exemples), utilisez MIPRO.

Voici comment utiliser BootstrapFewShotWithRandomSearch :

from dspy.teleprompt import BootstrapFewShotWithRandomSearch
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4, num_candidate_programs=10, num_threads=4)
teleprompter = BootstrapFewShotWithRandomSearch(metric=YOUR_METRIC_HERE, **config)
optimized_program = teleprompter.compile(YOUR_PROGRAM_HERE, trainset=YOUR_TRAINSET_HERE)

Sauvegarde et chargement de programmes optimisés

Après avoir exécuté un programme via un optimiseur, enregistrez-le pour une utilisation ultérieure :

programme_optimisé.save (VOTRE_SAVE_PATH)

Charger un programme enregistré :

loaded_program = YOUR_PROGRAM_CLASS()
loaded_program.load(path=YOUR_SAVE_PATH)

Fonctionnalités avancées : assertions DSPy

Les assertions DSPy automatisent l'application de contraintes de calcul sur les LM, améliorant ainsi la fiabilité, la prévisibilité et l'exactitude des sorties LM.

Utiliser des assertions

Définissez les fonctions de validation et déclarez les assertions après la génération du modèle respectif. Par exemple:

dspy.Suggest(
len(query) <= 100,
"Query should be short and less than 100 characters",
)
dspy.Suggest(
validate_query_distinction_local(prev_queries, query),
"Query should be distinct from: " + "; ".join(f"{i+1}) {q}" for i, q in enumerate(prev_queries)),
)

Transformer les programmes avec des assertions

from dspy.primitives.assertions import assert_transform_module, backtrack_handler
baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)

Alternativement, activez les assertions directement sur le programme :

baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()

Optimisations basées sur les assertions

Les assertions DSPy fonctionnent avec les optimisations DSPy, en particulier avec BootstrapFewShotWithRandomSearch, y compris des paramètres tels que :

  • Compilation avec assertions
  • Compilation + Inférence avec assertions

Conclusion

DSPy propose une approche puissante et systématique pour optimiser les modèles de langage et leurs invites. En suivant les étapes décrites dans ces exemples, vous pouvez facilement créer, optimiser et évaluer des systèmes d'IA complexes. La conception modulaire de DSPy et les optimiseurs avancés permettent une intégration efficace et efficiente de divers modèles de langage, ce qui en fait un outil précieux pour toute personne travaillant dans le domaine du NLP et de l'IA.

Que vous construisiez un simple système de questions-réponses ou un pipeline plus complexe, DSPy offre la flexibilité et la robustesse nécessaires pour atteindre des performances et une fiabilité élevées.

J'ai passé les cinq dernières années à m'immerger dans le monde fascinant du Machine Learning et du Deep Learning. Ma passion et mon expertise m'ont amené à contribuer à plus de 50 projets de génie logiciel divers, avec un accent particulier sur l'IA/ML. Ma curiosité continue m'a également attiré vers le traitement automatique du langage naturel, un domaine que j'ai hâte d'explorer davantage.