Überspringen und zum Inhalt gehen →

30-Tage-DSPy-Challenge: Tag 10 – Der BootstrapFewShot-Optimizer im Detail

Ziel ist es, den Algorithmus zu verstehen und den Unterschied zwischen einem manuell erstellten und einem automatisch generierten Prompt praktisch aufzuzeigen.

Der Mechanismus von BootstrapFewShot

Der BootstrapFewShot-Optimizer automatisiert die Erstellung von effektiven Few-Shot-Prompts. Anstatt dass ein Entwickler manuell Beispiele (Demonstrationen) auswählen muss, führt der Optimizer einen datengesteuerten Prozess durch. Der Kernalgorithmus lässt sich in folgende Schritte unterteilen:

Iteration über den Trainingsdatensatz
Der Optimizer durchläuft jedes Beispiel im bereitgestellten Trainingsset (trainset). Jedes dieser Beispiele wird einmal als zu lösende Aufgabe behandelt.

Simulation des Lernprozesses
Für jedes Trainingsbeispiel simuliert der Optimizer, wie das zu trainierende DSPy-Modul (das „Studenten-Modell“) die Aufgabe lösen würde.

Generierung von Kandidaten-Prompts
Um dem Studenten-Modell zu helfen, wählt der Optimizer eine kleine Anzahl anderer Beispiele aus dem Trainingsset aus und formatiert sie als Demonstrationen. Diese Demonstrationen werden zusammen mit der eigentlichen Aufgabenstellung zu einem „Kandidaten-Prompt“ zusammengefügt. Dieser Prozess wird für jedes Trainingsbeispiel mit unterschiedlichen Demonstrationen wiederholt.

Evaluation der Kandidaten-Prompts
Jeder Kandidaten-Prompt wird an das Sprachmodell (LLM) gesendet. Die generierte Antwort des Modells wird anschließend mithilfe der vom Benutzer bereitgestellten Evaluationsmetrik (z. B. exakte Übereinstimmung) mit der korrekten Antwort (dem Gold-Label) aus dem Trainingsbeispiel verglichen.

Sammeln erfolgreicher Demonstrationen
Wenn ein Kandidaten-Prompt dazu führt, dass das Modell die Aufgabe korrekt löst (d. h., die Metrik erfolgreich ist), wird das Set der in diesem Prompt verwendeten Demonstrationen als „erfolgreich“ markiert und gespeichert.

Kompilierung des finalen Programms
Nach der Analyse des gesamten Trainingssets verfügt der Optimizer über eine Sammlung von hochwertigen Demonstrations-Sets für jede Signatur im Programm. Das kompilierte (optimierte) Programm wird so instruiert, dass es bei der Bearbeitung neuer, unbekannter Eingaben eine Auswahl dieser erfolgreichen Demonstrationen in seinen Prompt einfügt.

    Dieser Prozess stellt sicher, dass die für das Few-Shot-Prompting verwendeten Beispiele nachweislich dazu beitragen, die Lösungsfähigkeit des Modells zu verbessern.

    Manuelle vs. optimierte Prompts analysieren

    Die folgende Umsetzung nutzt die Sentiment-Analyse-Aufgabe aus den vorherigen Tagen, um einen einfachen Zero-Shot-Prompt mit dem Ergebnis des BootstrapFewShot-Optimizers zu vergleichen. Es wird die bereits bekannte Konfiguration verwendet: ein SimpleClassifier-Modul, ein trainset, ein devset und ein konfiguriertes Sprachmodell.

    import dspy
    import os
    
    # Konfiguration des lokalen Sprachmodells
    local_llm = dspy.LM(
        "openai/Qwen3-VL-8B-Instruct-Q4_K_M.gguf", 
        api_base="http://localhost:8080/v1", 
        api_key="no_key_needed",
        temperature=2,
        cache=False
    )
    
    dspy.configure(lm=local_llm)
    
    # Signatur und Modul (aus Tag 8/9)
    class SentimentSignature(dspy.Signature):
        """Klassifiziert den Sentiment eines gegebenen Textes als positiv, negativ oder neutral."""
        text = dspy.InputField(desc="Der zu klassifizierende Text.")
        sentiment = dspy.OutputField(desc="Das Ergebnis der Klassifizierung: positiv, negativ oder neutral.")
    
    class SimpleClassifier(dspy.Module):
        def __init__(self):
            super().__init__()
            self.predictor = dspy.Predict(SentimentSignature)
    
        def forward(self, text):
            return self.predictor(text=text)
    
    # Definition der Beispieldaten
    raw_data = [
        ("Die Bildqualität ist hervorragend und die Bedienung intuitiv.", "positiv"),
        ("Ich bin sehr zufrieden mit dem Produkt, es übertrifft meine Erwartungen.", "positiv"),
        ("Das Preis-Leistungs-Verhältnis ist unschlagbar.", "positiv"),
        ("Leider hat das Gerät nach kurzer Zeit den Geist aufgegeben.", "negativ"),
        ("Der Kundenservice war überhaupt nicht hilfreich und unfreundlich.", "negativ"),
        ("Die Akkulaufzeit ist enttäuschend kurz.", "negativ"),
        ("Das Produkt wurde pünktlich geliefert.", "neutral"),
        ("Die Verpackung war angemessen.", "neutral"),
    ]
    trainset = [dspy.Example(text=t, sentiment=s).with_inputs("text") for t, s in raw_data]
    Ein unoptimiertes DSPy-Programm verwendet einen „Zero-Shot“-Prompt. Dieser enthält nur die Anweisungen aus der Signatur, aber keine Beispiele.
    # Instanziierung des unoptimierten Modells
    unoptimized_classifier = SimpleClassifier()
    
    # Ausführung einer Vorhersage, um den Prompt zu inspizieren
    test_text = "Der Stoff fühlt sich angenehm an, aber der Schnitt ist seltsam."
    unoptimized_classifier(text=test_text)
    
    # Anzeige des letzten Prompts an das LLM
    local_llm.inspect_history(n=1) # Führen Sie dies in einer interaktiven Umgebung aus

    Resultierender Prompt
    Der generierte Prompt enthält die Systemanweisung mit der Aufgabenbeschreibung und den zu klassifizierenden Text. Er enthält keine Few-Shot-Beispiele.

     System message:
    
    Your input fields are:
    1. `text` (str): Der zu klassifizierende Text.
    Your output fields are:
    1. `sentiment` (str): Das Ergebnis der Klassifizierung: positiv, negativ oder neutral.
    All interactions will be structured in the following way, with the appropriate values filled in.
    
    [[ ## text ## ]]
    {text}
    
    [[ ## sentiment ## ]]
    {sentiment}
    
    [[ ## completed ## ]]
    In adhering to this structure, your objective is: 
            Klassifiziert den Sentiment eines gegebenen Textes als positiv, negativ oder neutral.
    
    
    User message:
    
    [[ ## text ## ]]
    Der Stoff fühlt sich angenehm an, aber der Schnitt ist seltsam.
    
    Respond with the corresponding output fields, starting with the field `[[ ## sentiment ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
    
    
    Response:
    
    [[ ## sentiment ## ]]
    neutral
    
    [[ ## completed ## ]]

    Nun wird der BootstrapFewShot-Optimizer verwendet, um ein optimiertes Programm zu kompilieren.

    from dspy.teleprompt import BootstrapFewShot
    
    # Metrik für die Optimierung
    def metric(gold, pred, trace=None):
        return gold.sentiment.lower() == pred.sentiment.lower()
    
    # Konfiguration und Ausführung des Optimizers
    optimizer = BootstrapFewShot(metric=metric, max_bootstrapped_demos=2)
    optimized_classifier = optimizer.compile(SimpleClassifier(), trainset=trainset)
    
    # Ausführung mit demselben Testtext
    optimized_classifier(text=test_text)
    
    # Anzeige des optimierten Prompts
    local_llm.inspect_history(n=1) # Führen Sie dies in einer interaktiven Umgebung aus

    Resultierender Prompt
    Der vom Optimizer erstellte Prompt ist wesentlich detaillierter. Er enthält die gleichen Anweisungen, wird aber durch erfolgreiche Demonstrationen aus dem trainset ergänzt.

    System message:
    
    Your input fields are:
    1. `text` (str): Der zu klassifizierende Text.
    Your output fields are:
    1. `sentiment` (str): Das Ergebnis der Klassifizierung: positiv, negativ oder neutral.
    All interactions will be structured in the following way, with the appropriate values filled in.
    
    [[ ## text ## ]]
    {text}
    
    [[ ## sentiment ## ]]
    {sentiment}
    
    [[ ## completed ## ]]
    In adhering to this structure, your objective is: 
            Klassifiziert den Sentiment eines gegebenen Textes als positiv, negativ oder neutral.
    
    
    User message:
    
    [[ ## text ## ]]
    Die Bildqualität ist hervorragend und die Bedienung intuitiv.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    positiv
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Ich bin sehr zufrieden mit dem Produkt, es übertrifft meine Erwartungen.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    positiv
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Das Preis-Leistungs-Verhältnis ist unschlagbar.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    positiv
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Die Akkulaufzeit ist enttäuschend kurz.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    negativ
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Das Produkt wurde pünktlich geliefert.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    neutral
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Der Kundenservice war überhaupt nicht hilfreich und unfreundlich.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    negativ
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Leider hat das Gerät nach kurzer Zeit den Geist aufgegeben.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    negativ
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Die Verpackung war angemessen.
    
    
    Assistant message:
    
    [[ ## sentiment ## ]]
    neutral
    
    [[ ## completed ## ]]
    
    
    User message:
    
    [[ ## text ## ]]
    Der Stoff fühlt sich angenehm an, aber der Schnitt ist seltsam.
    
    Respond with the corresponding output fields, starting with the field `[[ ## sentiment ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
    
    
    Response:
    
    [[ ## sentiment ## ]]
    neutral
    
    [[ ## completed ## ]]

    Ergebnis und Interpretation: Ein direkter Vergleich

    KriteriumManueller (Zero-Shot) PromptOptimierter (Few-Shot) Prompt
    StrukturMinimalistisch; enthält nur die Anweisung aus der Signatur.Strukturiert; enthält Anweisung, Trennmarker und mehrere Demonstrationen.
    InhaltRein instruktiv. Das Modell muss die Anweisung abstrakt interpretieren.Instruktiv und demonstrativ. Das Modell lernt am Beispiel (In-Context Learning).
    ZuverlässigkeitDie Leistung ist stark von der Zero-Shot-Fähigkeit des Basis-LLMs abhängig und kann inkonsistent sein.Die Leistung ist in der Regel höher und konsistenter, da die Beispiele das gewünschte Verhalten klar definieren.
    ErstellungManuell und intuitiv; basiert auf der menschlichen Annahme, was eine gute Anweisung ist.Automatisiert und datengesteuert; basiert auf der empirischen Messung, welche Beispiele die Lösungsrate verbessern.

    Die Analyse zeigt, dass der optimierte Prompt dem LLM eine wesentlich präzisere Anleitung gibt. Durch die Bereitstellung von validierten Beispielen wird das Modell auf Spur gebracht. Das Modell muss nicht mehr raten, wie das Ausgabeformat aussehen soll oder wie Grenzfälle zu behandeln sind. Es kann sich an den Mustern orientieren.

    Zusammenfassung

    Der BootstrapFewShot-Optimizer ist ein Werkzeug, das den Kern der DSPy-Philosophie – „Programming, not Prompting“ – verkörpert. Anstatt sich auf manuelle Prompt-Optimierung zu verlassen, ermöglicht der Optimizer die Erstellung von erfolgreich getesteten Prompts. Sein algorithmischer Ansatz stellt sicher, dass die ausgewählten Demonstrationen einen nachweisbaren positiven Einfluss auf die Leistung des Modells haben, was zu robusteren und genaueren LLM-Anwendungen führt.

    Veröffentlicht in Allgemein