Engenharia imediata
Otimize LLM com DSPy: um guia passo a passo para construir, otimizar e avaliar sistemas de IA

À medida que as capacidades dos grandes modelos de linguagem (LLMs) continuam a expandir-se, o desenvolvimento de sistemas robustos de IA que aproveitem o seu potencial tornou-se cada vez mais complexo. As abordagens convencionais geralmente envolvem técnicas complexas de solicitação, geração de dados para ajuste fino e orientação manual para garantir a adesão às restrições específicas do domínio. No entanto, este processo pode ser tedioso, sujeito a erros e fortemente dependente da intervenção humana.
Entrar DSPy, uma estrutura revolucionária projetada para agilizar o desenvolvimento de sistemas de IA alimentados por LLMs. O DSPy introduz uma abordagem sistemática para otimizar prompts e pesos de LM, permitindo que os desenvolvedores criem aplicativos sofisticados com o mínimo de esforço manual.
Neste guia abrangente, exploraremos os princípios básicos do DSPy, sua arquitetura modular e a variedade de recursos poderosos que ele oferece. Também mergulharemos em exemplos práticos, demonstrando como o DSPy pode transformar a maneira como você desenvolve sistemas de IA com LLMs.
O que é DSPy e por que você precisa dele?
DSPy é uma estrutura que separa o fluxo do seu programa (modules
) dos parâmetros (prompts e pesos do LM) de cada etapa. Essa separação permite a otimização sistemática de prompts e pesos de LM, permitindo construir sistemas de IA complexos com maior confiabilidade, previsibilidade e adesão a restrições específicas de domínio.
Tradicionalmente, o desenvolvimento de sistemas de IA com LLMs envolvia um processo trabalhoso de dividir o problema em etapas, elaborando instruções complexas para cada etapa, gerando exemplos sintéticos para ajuste fino e orientando manualmente os LMs para aderir a restrições específicas. Essa abordagem não era apenas demorada, mas também propensa a erros, pois mesmo pequenas alterações no pipeline, no LM ou nos dados poderiam exigir um extenso retrabalho de prompts e etapas de ajuste fino.
O DSPy aborda esses desafios introduzindo um novo paradigma: otimizadores. Esses algoritmos orientados por LM podem ajustar os prompts e os pesos de suas chamadas LM, de acordo com uma métrica que você deseja maximizar. Ao automatizar o processo de otimização, o DSPy capacita os desenvolvedores a construir sistemas robustos de IA com intervenção manual mínima, aumentando a confiabilidade e a previsibilidade dos resultados do LM.
Arquitetura Modular do DSPy
No coração do DSPy está uma arquitetura modular que facilita a composição de sistemas complexos de IA. A estrutura fornece um conjunto de módulos integrados que abstraem várias técnicas de prompt, como dspy.ChainOfThought
e dspy.ReAct
. Esses módulos podem ser combinados e compostos em programas maiores, permitindo que os desenvolvedores construam pipelines complexos adaptados às suas necessidades específicas.
Cada módulo encapsula parâmetros que podem ser aprendidos, incluindo instruções, exemplos de poucas tentativas e pesos LM. Quando um módulo é invocado, os otimizadores do DSPy podem ajustar esses parâmetros para maximizar a métrica desejada, garantindo que as saídas do LM cumpram as restrições e requisitos especificados.
Otimizando com DSPy
DSPy apresenta uma gama de otimizadores poderosos projetados para aprimorar o desempenho e a confiabilidade de seus sistemas de IA. Esses otimizadores aproveitam algoritmos orientados por LM para ajustar os prompts e pesos de suas chamadas LM, maximizando a métrica especificada e ao mesmo tempo aderindo às restrições específicas do domínio.
Alguns dos principais otimizadores disponíveis no DSPy incluem:
- BootstrapFewShot: este otimizador estende a assinatura gerando e incluindo automaticamente exemplos otimizados no prompt enviado ao modelo, implementando aprendizado rápido.
- BootstrapFewShotWithRandomSearch: Aplica-se
BootstrapFewShot
diversas vezes com busca aleatória nas demonstrações geradas, selecionando o melhor programa em detrimento da otimização. - MIPRO: Gera instruções e exemplos rápidos em cada etapa, com a geração de instruções ciente de dados e de demonstração. Ele usa otimização bayesiana para pesquisar com eficácia o espaço de instruções de geração e demonstrações em seus módulos.
- BootstrapFinetune: destila um programa DSPy baseado em prompt em atualizações de peso para LMs menores, permitindo ajustar o(s) LLM(s) subjacente(s) para maior eficiência.
Ao aproveitar esses otimizadores, os desenvolvedores podem otimizar sistematicamente seus sistemas de IA, garantindo resultados de alta qualidade e, ao mesmo tempo, aderindo às restrições e requisitos específicos do domínio.
Introdução ao DSPy
Para ilustrar o poder do DSPy, vamos examinar um exemplo prático de construção de um sistema de geração aumentada de recuperação (RAG) para resposta a perguntas.
Etapa 1: Configurando o modelo de linguagem e o modelo de recuperação
A primeira etapa envolve a configuração do modelo de linguagem (LM) e do modelo de recuperação (RM) no DSPy.
Para instalar o DSPy, execute:
pip install dspy-ai
O DSPy oferece suporte a várias APIs LM e RM, bem como hospedagem de modelos locais, facilitando a integração de seus modelos preferidos.
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)
Etapa 2: Carregando o conjunto de dados
A seguir, carregaremos o conjunto de dados HotPotQA, que contém uma coleção de pares complexos de perguntas e respostas, normalmente respondidos em vários saltos.
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]
Etapa 3: Construindo Assinaturas
DSPy usa assinaturas para definir o comportamento dos módulos. Neste exemplo, definiremos uma assinatura para a tarefa de geração de resposta, especificando os campos de entrada (contexto e pergunta) e o campo de saída (resposta).
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")
Etapa 4: Construindo o Pipeline
Construiremos nosso pipeline RAG como um módulo DSPy, que consiste em um método de inicialização (__init__) para declarar os submódulos (dspy.Retrieve e dspy.ChainOfThought) e um método forward (forward) para descrever o fluxo de controle de resposta a questão usando esses módulos.
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)
Etapa 5: Otimizando o Pipeline
Com o pipeline definido, agora podemos otimizá-lo usando os otimizadores do DSPy. Neste exemplo, usaremos o otimizador BootstrapFewShot, que gera e seleciona prompts efetivos para nossos módulos com base em um conjunto de treinamento e uma métrica para validação.
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)
Etapa 6: avaliando o pipeline
Depois de compilar o programa, é essencial avaliar seu desempenho em um conjunto de desenvolvimento para garantir que atenda à precisão e confiabilidade desejadas.
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}")
Etapa 7: inspecionando o histórico do modelo
Para uma compreensão mais profunda das interações do modelo, você pode revisar as gerações mais recentes inspecionando o histórico do modelo.
# Inspect the model's history turbo.inspect_history(n=1)
Passo 8: Fazendo Previsões
Com o pipeline otimizado e avaliado, agora você pode usá-lo para fazer previsões sobre novas questões.
# 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}")
Exemplo mínimo de trabalho com DSPy
Agora, vamos examinar outro exemplo de trabalho mínimo usando o Conjunto de dados GSM8K e o modelo OpenAI GPT-3.5-turbo para simular tarefas de prompt no DSPy.
instalação
Primeiro, certifique-se de que seu ambiente esteja configurado corretamente:
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)
A gsm8k_trainset e gsm8k_devset conjuntos de dados contêm uma lista de exemplos com cada exemplo tendo um campo de perguntas e respostas.
Defina o Módulo
A seguir, defina um programa personalizado utilizando o módulo ChainOfThought para raciocínio passo a passo:
class CoT(dspy.Module): def __init__(self): super().__init__() self.prog = dspy.ChainOfThought("question -> answer") def forward(self, question): return self.prog(question=question)
Compilar e avaliar o modelo
Agora compile-o com o BootstrapFewShot teleprompter:
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)
Este exemplo demonstra como configurar seu ambiente, definir um módulo personalizado, compilar um modelo e avaliar rigorosamente seu desempenho usando o conjunto de dados fornecido e as configurações de teleprompter.
Gerenciamento de dados em DSPy
DSPy opera com conjuntos de treinamento, desenvolvimento e teste. Para cada exemplo nos seus dados, normalmente você tem três tipos de valores: entradas, rótulos intermediários e rótulos finais. Embora os rótulos intermediários ou finais sejam opcionais, é essencial ter alguns exemplos de entradas.
Criando objetos de exemplo
Objetos de exemplo em DSPy são semelhantes aos dicionários Python, mas vêm com utilitários úteis:
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)
Saída:
Example({'question': 'This is a question?', 'answer': 'This is an answer.'}) (input_keys=None) This is a question? This is an answer.
Especificando chaves de entrada
No DSPy, os objetos de exemplo têm um método with_inputs() para marcar campos específicos como entradas:
print(qa_pair.with_inputs("question")) print(qa_pair.with_inputs("question", "answer"))
Os valores podem ser acessados usando o operador ponto, e métodos como inputs() e rótulos() retornam novos objetos de exemplo contendo apenas chaves de entrada ou não, respectivamente.
Otimizadores em DSPy
Um otimizador DSPy ajusta os parâmetros de um programa DSPy (isto é, prompts e/ou pesos LM) para maximizar métricas especificadas. O DSPy oferece vários otimizadores integrados, cada um empregando estratégias diferentes.
Otimizadores disponíveis
- BootstrapFewShot: gera exemplos rápidos usando pontos de dados de entrada e saída rotulados fornecidos.
- BootstrapFewShotWithRandomSearch: aplica BootstrapFewShot várias vezes com pesquisa aleatória nas demonstrações geradas.
- COPRO: Gera e refina novas instruções para cada etapa, otimizando-as com subida coordenada.
- MIPRO: otimiza instruções e exemplos rápidos usando a otimização bayesiana.
Escolhendo um otimizador
Se você não souber por onde começar, use BootstrapFewShotWithRandomSearch:
Para poucos dados (10 exemplos), use BootstrapFewShot.
Para um pouco mais de dados (50 exemplos), use BootstrapFewShotWithRandomSearch.
Para conjuntos de dados maiores (mais de 300 exemplos), use MIPRO.
Veja como usar 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)
Salvando e carregando programas otimizados
Depois de executar um programa através de um otimizador, salve-o para uso futuro:
otimizado_program.save(SEU_SAVE_PATH)
Carregue um programa salvo:
loaded_program = YOUR_PROGRAM_CLASS() loaded_program.load(path=YOUR_SAVE_PATH)
Recursos avançados: Asserções DSPy
As asserções DSPy automatizam a aplicação de restrições computacionais em LMs, aumentando a confiabilidade, previsibilidade e correção das saídas de LM.
Usando Asserções
Definir funções de validação e declarar asserções após a respectiva geração do modelo. Por exemplo:
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)), )
Transformando Programas com Asserções
from dspy.primitives.assertions import assert_transform_module, backtrack_handler baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)
Alternativamente, ative as asserções diretamente no programa:
baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()
Otimizações baseadas em asserções
As asserções DSPy funcionam com otimizações DSPy, especialmente com BootstrapFewShotWithRandomSearch, incluindo configurações como:
- Compilação com Asserções
- Compilação + Inferência com Asserções
Conclusão
DSPy oferece uma abordagem poderosa e sistemática para otimizar modelos de linguagem e seus prompts. Seguindo as etapas descritas nestes exemplos, você pode construir, otimizar e avaliar sistemas complexos de IA com facilidade. O design modular e os otimizadores avançados do DSPy permitem a integração eficiente e eficaz de vários modelos de linguagem, tornando-o uma ferramenta valiosa para qualquer pessoa que trabalhe na área de PNL e IA.
Esteja você construindo um sistema simples de resposta a perguntas ou um pipeline mais complexo, o DSPy fornece a flexibilidade e a robustez necessárias para alcançar alto desempenho e confiabilidade.