All’interno di DSPy il nuovo framework di programmazione per modelli di linguaggio di cui hai bisogno di sapere

All'interno di DSPy, nuovo framework di programmazione per modelli di linguaggio.

Creato da ricercatori di Stanford, il framework offre un’interessante alternativa a LangChain o LlamaIndex.

Creato utilizzando Midjourney

Recentemente ho avviato una newsletter educativa focalizzata sull’IA, che già conta oltre 160.000 abbonati. TheSequence è una newsletter orientata al machine learning, senza fronzoli (senza hype, senza notizie, ecc.), che richiede solo 5 minuti per essere letta. L’obiettivo è tenerti aggiornato sui progetti di machine learning, paper di ricerca e concetti. Prova a iscriverti qui di seguito:

TheSequence | Jesus Rodriguez | Substack

La migliore fonte per restare aggiornati sullo sviluppo del machine learning, dell’intelligenza artificiale e dei dati…

thesequence.substack.com

L’universo dei framework di programmazione di modelli di linguaggio (LMP) si sta espandendo rapidamente negli ultimi mesi. Framework come LangChain o LlamaIndex hanno certamente raggiunto livelli rilevanti di adozione all’interno della comunità LLM e il Kernel Semantico di Microsoft sta potenziando un impressionante set di capacità. Recentemente, è emersa una nuova alternativa nota come DSPy con un approccio unico al LMP.

DSPy è stato creato da ricercatori dell’Università di Stanford con l’obiettivo di fornire astrazioni migliorate per diverse attività di LMP. DSPy dà la priorità alla programmazione rispetto alla richiesta di prompt, cercando così di consentire la creazione di app LMP più sofisticate. Una parte delle attuali limitazioni dei LLM è che non sono molto efficaci in attività come la logica di controllo come cicli o istruzioni condizionali, e richiedono anche costrutti fondamentalmente diversi per attività come il fine-tuning o l’aumento della conoscenza. DSPy cerca di affrontare questi problemi con un approccio incentrato sulla programmazione.

L’esperienza e i principi di DSPy mostrano somiglianze con PyTorch nello spazio del deep learning. Nell’ambito della creazione di app di deep learning utilizzando PyTorch, gli scienziati dei dati modellano una rete neurale data e utilizzano strati dichiarativi o ottimizzatori per incorporare una logica desiderata. Allo stesso modo, DSPy include blocchi di costruzione come ChainOfThought o Retrieve e compila il programma, ottimizzando i prompt in base a metriche specifiche. Sebbene ciò possa sembrare un nuovo approccio in LMP, l’approccio è piuttosto tradizionale.

DSPy

DSPy dà la priorità alla programmazione rispetto alle attività di richiesta di prompt, unificando le strategie sia per i prompt che per il fine-tuning dei modelli di linguaggio (LM). Il framework potenzia queste tecniche con ragionamento e strumenti/aumento del recupero, il tutto attraverso una raccolta concisa di operazioni in stile Python che sono componibili e capaci di apprendere.

All’interno del framework di DSPy, esistono moduli componibili e dichiarativi progettati per guidare i LLM, seguendo una struttura familiare a Python. In particolare, DSPy introduce un compilatore automatico che insegna ai LLM ad eseguire fasi dichiarative all’interno di un programma. Questo compilatore traccia internamente l’esecuzione del programma e successivamente elabora prompt di alta qualità adatti a LLM di grandi dimensioni, o addirittura addestra il fine-tuning automatico per LLM più piccoli, trasmettendo conoscenze sulle sfumature del compito.

DSPy funziona come una soluzione completa per complesse attività che coinvolgono modelli di linguaggio (LM) e modelli di recupero (RM), armonizzando approcci sia per i prompt che per il fine-tuning dei LM, oltre ad accomodare metodi di ragionamento e strumenti/aumento del recupero. Attraverso DSPy, queste metodologie sono racchiuse in un insieme compatto di moduli in stile Python, che facilitano la composabilità e l’apprendimento.

Il framework di DSPy è reso possibile dalla disponibilità di moduli componibili e dichiarativi, strutturati in modo familiare agli utenti di Python. Questo avanzamento eleva le “tecniche di richiesta di prompt” come chain-of-thought e autoriflessione da tattiche di manipolazione di stringhe manuali a procedure modulari versatili in grado di adattarsi a compiti diversi.

In particolare, DSPy introduce un compilatore automatico, fornendo ai LLM la capacità di eseguire passaggi dichiarativi descritti in un programma. Il compilatore DSPy traccia metodicamente l’esecuzione del programma, generando prompt ben formulati adatti a LLM su larga scala e può anche addestrare il fine-tuning automatico per LLM più piccoli. Ciò consente a questi modelli di interiorizzare le sfumature procedurali del compito.

È importante sottolineare che il compilatore DSPy funziona senza la necessità di etichette manuali per passaggi intermedi all’interno di un programma. Ciò si contrappone alla pratica convenzionale di “ingegneria del prompt” che si basa su tecniche di manipolazione di stringhe ad hoc. Invece, DSPy apre la porta all’esplorazione di un ambito strutturato di componenti modulari e addestrabili, fornendo un approccio sistematico che sostituisce processi manuali fragili.

L’esperienza di programmazione DSPy si basa su due costrutti fondamentali: le Signature e i Teleprompter.

I. Signature: Creazione del comportamento LLM

All’interno del mondo di DSPy, quando i compiti vengono assegnati ai LMs, la specificità del comportamento viene espressa attraverso le Signature. Una Signature racchiude una rappresentazione dichiarativa di un modello comportamentale di input/output associato a un modulo DSPy.

A differenza di sforzarsi di guidare un LLM attraverso sotto-compiti, le Signature permettono agli utenti DSPy di chiarire la natura del sotto-compito. Successivamente, il compilatore DSPy si assume la responsabilità di ideare prompt intricati su misura per un grande LM o per ottimizzare un LLM più piccolo, tutto in linea con la Signature specificata, i dati forniti e il flusso di lavoro esistente.

Una Signature è composta da tre componenti fondamentali:

1) Una delineazione concisa del sotto-compito affrontato dal LM.

2) Approfondimento su uno o più campi di input (ad esempio, domanda di input) che serviranno come input per il LM.

3) Spiegazione di uno o più campi di output (ad esempio, risposta alla domanda) che ci si aspetta che vengano generati dal LM.

Il seguente codice illustra una Signature basata sul modulo ChainOfThought con diversi controlli:

class GenerateSearchQuery(dspy.Signature):    """Scrivi una semplice query di ricerca che aiuterà a rispondere a una domanda complessa."""    context = dspy.InputField(desc="può contenere fatti rilevanti")    question = dspy.InputField()    query = dspy.OutputField()### all'interno della funzione __init__ del tuo programma self.generate_answer = dspy.ChainOfThought(GenerateSearchQuery)

II. Abilitazione dell’ottimizzazione del programma tramite dspy.teleprompt.

La compilazione è al centro dell’esperienza DSPy.

La compilazione si basa su tre fattori fondamentali: un dataset di addestramento potenzialmente compatto, una metrica di validazione e la selezione di un teleprompter dal repertorio di DSPy. Questi teleprompter, incorporati in DSPy, sono ottimizzatori potenti capaci di padroneggiare l’arte di formulare prompt incisivi su misura per i moduli di qualsiasi programma. (Il prefisso “tele-” in “teleprompter” implica “a distanza”, facendo riferimento alla natura automatica del prompting.)

Notabilmente, le richieste di etichettatura di DSPy sono notevolmente modeste. Considera ad esempio una pipeline di generazione ampliata del recupero (RAG): anche un pugno di istanze contenenti domande e relative risposte annotate manualmente potrebbe essere sufficiente. Anche quando si comprendono più fasi intricate, come nell’esempio del modello di base RAG che comprende il recupero del contesto, una catena di pensiero e la risposta finale, le etichette sono obbligatorie solo per la query iniziale e la risposta finale. DSPy estrae in modo ingegnoso le etichette intermedie necessarie per supportare l’intera pipeline. Se vengono apportate modifiche alla struttura della pipeline, i dati iniziali si evolveranno dinamicamente per adeguarsi alla configurazione modificata.

Un’istanza di un set di addestramento RAG potrebbe essere strutturata come segue:

my_rag_trainset = [  dspy.Example(    question="Quale premio ha ricevuto il primo libro di Gary Zukav?",    answer="National Book Award"  ),  ...]

In seguito, è imperativo articolare i criteri di validazione. Questa logica impone vincoli sul comportamento del programma o sulle prestazioni del modulo. Ad esempio, una funzione di validazione per RAG potrebbe includere un controllo come illustrato:

def validate_context_and_answer(example, pred, trace=None):answer_match = example.answer.lower() == pred.answer.lower()context_match = any((pred.answer.lower() in c) for c in pred.context)return answer_match and context_match

Diverse opzioni di teleprompter presentano trade-off diversi legati all’ottimizzazione dei costi rispetto al miglioramento della qualità. Nel contesto di RAG, una scelta potrebbe essere il teleprompter BootstrapFewShot diretto. Questo comporta l’inizializzazione del teleprompter stesso con una funzione di validazione personalizzata (come my_rag_validation_logic) e successivamente la compilazione contro un set di addestramento designato (my_rag_trainset).

from dspy.teleprompt import BootstrapFewShotteleprompter = BootstrapFewShot(metric=my_rag_validation_logic)compiled_rag = teleprompter.compile(RAG(), trainset=my_rag_trainset)

Quando si utilizza l’istanza di RAG compilata, si entra in una fase di invocazione del LM con prompt intricati. Questi prompt incorporano dimostrazioni concise di recupero del chain-of-thought e risposta alle domande, il tutto su misura per il dataset unico in uso.

DSPy è ancora nelle prime fasi ma già evidenzia numerose idee promettenti. Un modello di programmazione semplice e coerente che assomiglia all’esperienza di PyTorch e un’esperienza modulare consentono agli sviluppatori di LMP di creare esperienze piuttosto sofisticate. Spero che DSPy si evolva da questa fase iniziale a uno stack che possa competere con LangChain e LlamaIndex, perché molte delle idee del framework sono sicuramente necessarie nello spazio LMP.