Révéler le potentiel des LLM : L’art de l’optimisation des prompts avec DSPy

MFG Labs
7 min readJul 15, 2024

--

Image par Dmitry sur Pexels

Introduction

Les “Large Language Models” (LLM) sont de plus en plus utilisés dans divers domaines nécessitant une compréhension approfondie et la génération de textes complexes. Ces modèles, formés sur de vastes corpus de textes, sont capables d’accomplir une multitude de tâches linguistiques, allant de la traduction à la génération de texte en passant par le résumé et la réponse aux questions. L’essor des LLMs est dû à leur capacité à comprendre le contexte, à utiliser une grande variété de structures de phrases, de vocabulaire et de styles d’écriture ainsi qu’à produire des réponses.

L’optimisation des prompts envoyés à ces LLMs est un processus crucial qui permet de spécialiser et d’affiner les résultats obtenus.

DSPy est un Framework qui vise à faciliter ce processus, se démarquant par son utilisation d’exemples en “few-shot”. Cependant, l’optimisation de prompt par les humains présente certaines limites : la performance est étroitement liée au modèle utilisé et il est souvent difficile de déterminer si le prompt est vraiment “optimal”. DSPy propose une solution à ce problème en offrant une approche systématique pour l’optimisation des LLMs. Cette approche comporte plusieurs étapes, allant de la sélection des exemples en “few shot learning”, à la modification de prompt, jusqu’au fine tuning du modèle lui-même.

Un exemple significatif concerne les prompts dits “multi-hops”. Dans ce cas, si l’on souhaite générer des exemples “few shot”, on est contraint de fournir des informations détaillées pour chaque étape intermédiaire, là où DSPy se contente du texte en entrée (input) et du résultat attendu (output).

Ainsi DSPy offre une méthode plus structurée, plus efficace et moins laborieuse pour l’optimisation de prompt.

Le principe du “few-shot exemples” que nous allons utiliser dans cet article consiste à donner quelques exemples dans le prompt qui servent de cadre au LLM pour répondre.

Afin de démontrer l’intérêt de l’optimisation de prompt et la simplicité de mise en place grâce à DSPy, nous allons essayer de traduire des phrases en Schtroumpf !?

Les règles du langage Schtroumpf sont relativement complexes et floues mais communément admises. Il est donc plus simple de donner quelques exemples que d’essayer de les détailler.

Nous allons commencer par utiliser un prompt volontairement très succinct (et perfectible), nous allons ensuite tâcher d’améliorer les résultats de notre modèle grâce à la fonctionnalité d’optimisation de prompt de DSPy.

Création d’un RAG avec DSPy

DSPY est un Framework qui permet d’interagir avec LLM de manière simple et algorithmique. Il est axé principalement autour des données et des intentions. Les composantes principales de DSPy sont les modules et les optimiseurs. Cette architecture est inspirée par les couches de réseaux de neurones de Pytorch. Le module Predict de DSPy est le module fondamental du Framework et les autres sont construit à partir de ce module.

Les modules correspondent aux différents blocs de notre application et peuvent être composés entre eux pour produire le résultat final. Ils intègrent une “Signature” dans un prompt pré-structuré.

Différents optimiseurs sont mis à disposition par DSPy pour permettre au système de s’optimiser lui-même.

Fonctionnement de DSPy @source : https://www.theaidream.com/post/dspy-a-revolutionary-framework-for-programming-llms

Créer une “Signature” qui représente votre prompt

Dans le contexte de DSPy, la “Signature” fait référence à la directive de notre prompt. La doc-string de la classe est utilisée comme directive principale, qui est ensuite composée de différents inputs et outputs.

class TranslateToSchtroumpf(dspy.Signature):
"""Translate an input text into schtroumpf langage."""
input_text = dspy.InputField(desc="The sentence to translate.")

answer = dspy.OutputField(
desc="Translate the given sentence into schtroumpf."
)

Créer un “Module” qui utilise cette “Signature

Dans notre cas, nous allons créer un module très simple qui va créer un prompt en se basant sur un modèle de type “Chain of Thought” adaptée à notre Signature précédemment définie.

class Rag(dspy.Module):

def __init__(self):
super().__init__()
self.schtroumpf = dspy.ChainOfThought(TranslateToSchtroumpf)

def forward(self, sentence: str):
return self.schtroumpf(input_text=sentence)

Créer un jeu de données et une méthode d’évaluation pour évaluer votre modèle

Afin de pouvoir évaluer la pertinence de notre modèle, nous devons fournir à DSPy un ensemble d’input / output et une méthode pour évaluer les réponses prédites par le modèle par rapport à celles données en exemple.

Notre jeu de données

La méthode d’évaluation : dans notre cas, nous voulons juste vérifier si la prédiction correspond exactement à notre exemple.

def validation_method(example, prediction, trace=None):
return example.answer == prediction.answer

Évaluation des résultats
Tout d’abord, on utilise notre module pour créer notre RAG.

llm = dspy.OpenAI(
model=os.getenv('MODEL_NAME'),
api_key=os.getenv('OPENAI_API_KEY')
)
dspy.settings.configure(lm=llm)
rag = Rag()

Ensuite, on charge nos exemples dans un tableau d’”Exemple” au sens DSPy afin que les exemples puissent matcher notre Signature.

def create_examples_from_csv(file_path):
dt = pandas.read_csv(file_path)
samples = []
for _, row in dt.iterrows():
samples.append(dspy.Example(
sentence=row["input"],
answer=row["output"]
).with_inputs("sentence"))
return samples

samples = create_examples_from_csv("./samples.csv")

Enfin, nous établissons la méthode d’évaluation et nous l’appelons à l’aide de notre fonction de validation.

evaluate_on_train_set = Evaluate(devset=samples, num_threads=5, display_progress=True, display=True)
evaluate_on_train_set(rag, metric=validation_method)

Résultats

Average Metric: 0 / 9 (0.0%)

Comme nous aurions pu nous y attendre, aucune prédiction ne correspond exactement à nos exemples.

Fournir plus de traductions possibles

Comme il n’y a pas une seule traduction possible pour une phrase donnée, autorisons plusieurs “traductions schtroumpf” dans nos exemples pour augmenter nos chances de correspondances.

Voilà notre fichier d’exemples mis à jour dans lequel nous proposons jusqu’à 3 réponses possibles pour chaque phrase.

Voici la nouvelle méthode de chargement des exemples afin de prendre en compte les réponses alternatives proposées.

def create_examples_from_csv(file_path):
dt = pandas.read_csv(file_path)
samples = []
for _, row in dt.iterrows():
answers = []
for i in range(1, 4):
if row[f"output_{i}"] != "":
answers.append(row[f"output_{i}"])
samples.append(dspy.Example(
sentence=row["input"],
alternatives=answers[1:],
answer=answers[0]
).with_inputs("sentence"))
return samples

Voici la méthode de validation mise à jour pour prendre en compte ces nouveaux types d’exemples.

def validation_method(example, prediction, trace=None):
return prediction.answer == example.answer
or prediction.answer in example.alternatives

Résultats

Average Metric: 2 / 9 (22.2%)

C’est un peu mieux, mais il y a encore une grande marge d’amélioration. Nous allons donc optimiser notre prompt en utilisant DSPY.

Optimisation du prompt

Préparer un jeu de données d’optimisation

De la même façon que nous avons établi un ensemble de données pour la validation, nous allons procéder à la constitution d’un ensemble de données destiné à l’optimisation. Il convient de noter que cet ensemble ne servira pas à l’entraînement car nous n’avons pas l’intention de ré-entraîner le modèle dans son intégralité à partir de ces exemples.

Notre approche consistera plutôt à permettre à DSPy de sélectionner les exemples les plus pertinents afin d’optimiser les résultats obtenus sur notre ensemble de données de validation.

Voici le jeu de données que nous allons utiliser pour l’optimisation.

Nous chargeons ces données de la même manière que précédemment.

train_set = create_examples_from_csv("./train.csv")

Compilation et optimisation

Nous allons utiliser l’optimiseur “BootstrapFewShotWithRandomSearch”. Il en existe de nombreux autres mais ils se comportent de manière relativement similaire.

teleprompter = BootstrapFewShotWithRandomSearch(
max_bootstrapped_demos=12,
max_labeled_demos=8,
num_candidate_programs=8,
num_threads=6,
stop_at_score=70,
metric=multi_output_validation_method,
teacher_settings=dict(llm=llm)
)

compiled_rag = teleprompter.compile(Rag(), trainset=train_set)

Cet optimiseur va choisir aléatoirement des exemples parmi ceux du jeu d’entraînement, puis évaluer la prédiction sur l’intégralité des exemples de ce même jeu de données. Si le pourcentage de bonnes prédictions dépasse 70%, le processus d’optimisation sera arrêté.

Voilà les résultats de l’optimisations (les scores obtenus correspondent au pourcentage de prédiction correctes) :

> Scores so far: [22.22, 50.0, 61.11, 55.56, 61.11, 61.11, 61.11, 44.44, 55.56, 50.0, 66.67] Best score: 66.67

Évaluation du RAG compilé

Une fois compilé, le RAG peut être sauvegardé au format JSON pour une réutilisation ultérieure sans avoir à refaire toutes ces étapes.

Ensuite, nous pouvons évaluer ce RAG sur notre jeu de données d’exemple.

evaluate_on_train_set = Evaluate(devset=dev_set, num_threads=2, display_progress=True, display_table=10)
evaluate_on_train_set(compiled_rag, metric=multi_output_validation_method)

Résultats

Average Metric: 4 / 9 (44.4%)

Conclusion

DSPy offre une interface simple pour des tâches complexes, le rendant idéal pour l’optimisation des prompts pour les LLMs en utilisant des exemples “few-shot”.

L’optimisation de prompts est cruciale pour améliorer les performances des LLMs. En optimisant les prompts efficacement, nous pouvons mieux guider les réponses du modèle et obtenir de meilleurs résultats sans pour autant devoir ré-entraîner entièrement le modèle ou définir des règles précises qui peuvent être complexes et floues.

Cette technique d’optimisation comprend l’utilisation d’exemples “few-shot”. Ce sont quelques exemples donnés au début du prompt pour guider les réponses du modèle. À l’aide de ces exemples, le modèle peut comprendre le contexte et la tâche spécifique à accomplir, améliorant ainsi la qualité de ses réponses. Bien que les résultats de notre exemple ne soient pas exceptionnels, ils montrent néanmoins une amélioration notable suite à l’optimisation. Si la demande que vous adressez à votre LLM est complexe et que vous avez de nombreux exemples, cette pratique pourrait être la solution idéale pour vous !

--

--

MFG Labs
MFG Labs

No responses yet