Potenziare l'ingegneria rapida tramite la ricerca simbolica del programma |  di Tobias Schnabel |  Aprile 2024

 | Intelligenza-Artificiale

Apetta un minuto. Potremmo avere un modo per rappresentare e modificare i prompt adesso, ma ci manca ancora un processo per ottimizzarli automaticamente.

Una volta che gli chef comprendono l'astrazione e i componenti di una ricetta, proveranno molte varianti, perfezionando il gusto, il costo o la presentazione, finché non sembreranno giusti. Per fare lo stesso con le astrazioni immediate, abbiamo bisogno di un algoritmo di ricerca, di un obiettivo e di una serie di campioni etichettati per sapere che stiamo facendo progressi.

Sembra molto da implementare? Incontrare Sammouna libreria Python per creare e ottimizzare programmi con prompt simbolici.

Per illustrare il flusso di lavoro principale di SAMMO, mostreremo ora come ottimizzare la parte delle istruzioni del nostro esempio di richiesta dall'alto. Una volta che avremo elaborato questo esempio giocattolo, saremo pronti a discutere applicazioni più avanzate, come l'ottimizzazione o la compressione RAG.

I passaggi chiave sono

  1. Definire il prompt iniziale
  2. Preparare i dati: bastano poche centinaia di esempi etichettati.
  3. Definire l'obiettivo
  4. Scelta di un insieme di mutatori
  5. Esecuzione dell'ottimizzazione

Passaggio 1: definizione del prompt di avvio

Lo abbiamo già fatto più o meno sopra. SAMMO si aspetta una funzione, quindi dovremo racchiuderla in una. Se desideri memorizzare informazioni aggiuntive, avvolgile in un file Chiamabile Invece. Lo inseriremo anche in un componente Output per eseguirlo.

def starting_prompt():
instructions = MetaPrompt(
Paragraph(text="Instructions: "),
Paragraph(
id="instructions",
text="Does Speaker 2's answer mean yes or no?",
),
Paragraph(id="labels", text="Output labels: yes, no"),
InputData(),
Paragraph(text="Output: "),
)
return Output(instructions.with_extractor())

Passaggio 2: preparare i dati

SAMMO utilizza una semplice struttura dati chiamata DataTable per accoppiare input con output (etichette). Questo ci aiuterà nella valutazione e nella contabilità.

mydata = DataTable.from_records(
records, # list of {"input": <>, "output": <>}
constants={"instructions": default_instructions},
)

Fase 3: definizione dell'obiettivo

Siamo interessati a ottimizzare la precisione, quindi è ciò che stiamo implementando di seguito:

def accuracy(y_true: DataTable, y_pred: DataTable) -> EvaluationScore:
y_true = y_true.outputs.normalized_values()
y_pred = y_pred.outputs.normalized_values()
n_correct = sum((y_p == y_t for y_p, y_t in zip(y_pred, y_true)))
return EvaluationScore(n_correct / len(y_true))

Passaggio 4: scelta di un set di mutatori

Qui è dove puoi essere creativo quanto desideri. Puoi implementare i tuoi operatori che generano nuove varianti di prompt o semplicemente fare affidamento sugli operatori di mutazione predefiniti offerti da SAMMO.

Di seguito, facciamo quest'ultimo e optiamo per un mix di parafrasi e istruzioni indotte da alcuni esempi etichettati, essenzialmente implementando Ingegneria rapida automatica (APE).

mutation_operators = BagOfMutators(
starting_prompt=StartingPrompt(d_train),
InduceInstructions({"id": "instructions"}, d_train),
Paraphrase({"id": "instructions"}),
)

Passaggio 5: esecuzione dell'ottimizzazione

runner = OpenAIChat(
model_id="gpt-3.5-turbo-16k",
api_config={"api_key": YOUR_KEY},
cache="cache.tsv",
)
prompt_optimizer = BeamSearch(runner, mutation_operators, accuracy, depth=6)
transformed = prompt_optimizer.fit_transform(d_train)

Il prompt di esempio introduttivo è stato effettivamente preso da Compito implicature di BigBench che utilizzeremo per eseguire questo esperimento. Se esegui l'ottimizzazione con 100 campioni per formazione e test e un budget di 48 valutazioni dei candidati, vedrai che SAMMO migliora la precisione del prompt iniziale da 0,56 A 0,77 – UN 37,5% miglioramento. Quali istruzioni hanno funzionato meglio?

...
Paragraph(
"Consider the dialogue, context, and background "
"information provided to determine the most suitable output label",
id="instructions",
)
...

È interessante notare che diversi LLM preferiscono istruzioni abbastanza diverse. GPT-3.5 ha apprezzato di più le istruzioni generiche, come visto sopra. Il miglior suggerimento di Llama-2 selezionato da SAMMO con la stessa formazione e impostazione del budget utilizzava una stringa vuota nella parte delle istruzioni:

...
Paragraph(
"",
id="instructions",
)
...

Mostreremo ora come convertire una pipeline RAG in un programma simbolico e ottimizzarlo con SAMMO. Utilizzeremo l'analisi semantica come attività applicativa in cui vogliamo tradurre le query dell'utente in costrutti di linguaggio specifico del dominio (DSL), ad esempio, per interrogare alcuni database o chiamare un'API esterna.

Per creare il prompt iniziale, includiamo un elenco di tutti gli operatori, utilizziamo un retriever basato sull'incorporamento per ottenere cinque esempi di manyshot e quindi istruiamo il LLM a restituire la sua risposta nello stesso formato degli esempi.

class RagStartingPrompt:
def __init__(self, dtrain, examples, embedding_runner):
self._examples = examples
self._dtrain = dtrain
self._embedding_runner = embedding_runner

def __call__(self, return_raw=False):
structure = (
Section("Syntax", self._dtrain.constants("list_of_operators")),
Section(
"Examples",
EmbeddingFewshotExamples(
self._embedding_runner, self._examples, 5
),
),
Section(
"Complete and output in the same format as above",
InputData(),
),
)
instructions = MetaPrompt(
structure,
render_as="markdown",
data_formatter=JSONDataFormatter(),
)
return Output(
instructions.with_extractor(),
on_error="empty_result",
)

Ora che abbiamo un programma simbolico, diventiamo creativi. Per le mutazioni, esploriamo

  • un numero variabile di esempi di pochi scatti
  • diversi formati (XML, JSON, riga per riga) per gli esempi di pochi scatti
  • fornire o meno informazioni aggiuntive sulla DSL
  • mostrando coppie input-output o gruppi di input e output

Eseguendo SAMMO con questi e un budget totale di 24 candidati da provare, possiamo vedere una tendenza chiara. Di seguito sono riportate le accuratezze dei set di test per tre diversi set di dati in quattro diversi LLM. Nella stragrande maggioranza dei casi, possiamo vedere che SAMMO può aumentare sostanzialmente le prestazioni, anche per i LLM con le prestazioni più elevate.

Anche con un budget limitato di 24 valutazioni dei candidati possiamo ottenere notevoli miglioramenti nelle prestazioni. Immagine dell'autore.

Convertire i tuoi prompt in programmi simbolici è un'idea davvero potente per esplorare un ampio spazio di progettazione di possibili prompt e impostazioni. Proprio come uno chef professionista decostruisce e reinterpreta le ricette per creare innovazioni culinarie, la programmazione simbolica ci consente di applicare lo stesso livello di creatività e sperimentazione all’ingegneria immediata automatica.

SAMMO implementa la ricerca simbolica del programma attraverso una serie di operatori di mutazione e routine di ricerca. Empiricamente, ciò può tradursi in grandi miglioramenti nella precisione sia per l'ottimizzazione delle istruzioni che per l'ottimizzazione RAG, indipendentemente dal LLM backend.

Puoi estendere SAMMO con operatori di mutazione personalizzati per includere le tue tecniche di prompt engineering preferite o implementare obiettivi che vadano oltre la precisione (ad esempio, il costo). Buona cucina pronta!

Disclaimer: Sono l'autore di SAMMO.

Risorse

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *