即时工程
使用 DSPy 优化 LLM:构建、优化和评估 AI 系统的分步指南

随着大型语言模型 (LLM) 的功能不断扩展,开发能够充分发挥其潜力的强大 AI 系统变得越来越复杂。传统方法通常涉及复杂的提示技术、用于微调的数据生成以及手动指导以确保遵守特定领域的约束。然而,这个过程可能很繁琐、容易出错,并且严重依赖人工干预。
输入 DSPy,这是一个革命性的框架,旨在简化由 LLM 驱动的 AI 系统的开发。DSPy 引入了一种优化 LM 提示和权重的系统方法,使开发人员能够以最少的手动工作量构建复杂的应用程序。
在本综合指南中,我们将探索 DSPy 的核心原理、其模块化架构以及它提供的一系列强大功能。我们还将深入探讨实际示例,展示 DSPy 如何改变您使用 LLM 开发 AI 系统的方式。
什么是 DSPy?为什么需要它?
DSPy 是一个框架,它可以分离程序流程(modules
) 从每个步骤的参数(LM 提示和权重)中分离出来。这种分离允许系统地优化 LM 提示和权重,使您能够构建具有更高可靠性、可预测性和遵守特定领域约束的复杂 AI 系统。
传统上,使用 LLM 开发 AI 系统需要耗费大量时间,包括将问题分解为多个步骤、为每个步骤设计复杂的提示、生成用于微调的合成示例,以及手动引导 LM 遵守特定约束。这种方法不仅耗时,而且容易出错,因为即使对管道、LM 或数据进行微小更改也可能需要大量重新设计提示和微调步骤。
DSPy 通过引入新范式来解决这些挑战: 优化器。这些 LM 驱动的算法可以根据您想要最大化的指标调整 LM 调用的提示和权重。通过自动化优化过程,DSPy 使开发人员能够以最少的人工干预构建强大的 AI 系统,从而提高 LM 输出的可靠性和可预测性。
DSPy 的模块化架构
DSPy 的核心是模块化架构,有助于构建复杂的 AI 系统。该框架提供了一组内置模块,抽象了各种提示技术,例如 dspy.ChainOfThought
和 dspy.ReAct
。这些模块可以组合成更大的程序,让开发人员能够根据自己的特定需求构建复杂的管道。
每个模块都封装了可学习的参数,包括指令、小样本示例和 LM 权重。调用模块时,DSPy 的优化器可以微调这些参数以最大化所需指标,确保 LM 的输出符合指定的约束和要求。
使用 DSPy 进行优化
DSPy 推出了一系列功能强大的优化器,旨在提高 AI 系统的性能和可靠性。这些优化器利用 LM 驱动的算法来调整 LM 调用的提示和权重,在遵守特定领域的约束的同时最大化指定指标。
DSPy 中可用的一些关键优化器包括:
- BootstrapFewShot:该优化器通过在发送给模型的提示中自动生成并包含优化示例来扩展签名,实现小样本学习。
- BootstrapFewShotWithRandomSearch:适用
BootstrapFewShot
对生成的演示进行多次随机搜索,选择优化后的最佳程序。 - MIPRO:在每个步骤中生成指令和少量示例,指令生成具有数据感知和演示感知功能。它使用贝叶斯优化来有效地搜索模块中的生成指令和演示空间。
- BootstrapFinetune:将基于提示的 DSPy 程序提炼为较小 LM 的权重更新,使您能够微调底层 LLM 以提高效率。
通过利用这些优化器,开发人员可以系统地优化他们的人工智能系统,确保高质量的输出,同时遵守特定领域的约束和要求。
DSPy 入门
为了说明 DSPy 的强大功能,让我们通过一个构建用于问答的检索增强生成 (RAG) 系统的实际示例来介绍。
步骤 1:建立语言模型和检索模型
第一步涉及在 DSPy 中配置语言模型 (LM) 和检索模型 (RM)。
要安装 DSPy,请运行:
pip install dspy-ai
DSPy 支持多种 LM 和 RM API,以及本地模型托管,可轻松集成您喜欢的模型。
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)
第 2 步:加载数据集
接下来,我们将加载 HotPotQA 数据集,其中包含通常以多跳方式回答的复杂问答对的集合。
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]
步骤 3:构建签名
DSPy 使用签名来定义模块的行为。在此示例中,我们将为答案生成任务定义一个签名,指定输入字段(上下文和问题)和输出字段(答案)。
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")
步骤 4:构建管道
我们将把 RAG 管道构建为 DSPy 模块,它包含一个初始化方法(__init__)来声明子模块(dspy.Retrieve 和 dspy.ChainOfThought)和一个前向方法(forward)来描述使用这些模块回答问题的控制流。
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)
步骤 5:优化管道
定义好管道后,我们现在可以使用 DSPy 的优化器对其进行优化。在此示例中,我们将使用 BootstrapFewShot 优化器,该优化器根据训练集和验证指标为我们的模块生成并选择有效的提示。
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)
步骤 6:评估管道
编译程序后,必须在开发集上评估其性能,以确保其满足所需的准确性和可靠性。
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}")
步骤 7:检查模型历史
为了更深入地了解模型的交互,您可以通过检查模型的历史记录来查看最近的几代。
# Inspect the model's history turbo.inspect_history(n=1)
第五步:做出预测
通过优化和评估管道,您现在可以使用它来对新问题进行预测。
# 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}")
使用 DSPy 的最小工作示例
现在,让我们使用 GSM8K 数据集 以及 OpenAI GPT-3.5-turbo 模型来模拟 DSPy 中的提示任务。
设置
首先,确保您的环境配置正确:
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)
- gsm8k_训练集 和 gsm8k_devset 数据集包含示例列表,每个示例都有一个问题和答案字段。
定义模块
接下来,利用 ChainOfThought 模块定义一个自定义程序,进行逐步推理:
class CoT(dspy.Module): def __init__(self): super().__init__() self.prog = dspy.ChainOfThought("question -> answer") def forward(self, question): return self.prog(question=question)
编译并评估模型
现在用 BootstrapFewShot 提词器:
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)
此示例演示如何设置环境、定义自定义模块、编译模型以及使用提供的数据集和提词器配置严格评估其性能。
DSPy 中的数据管理
DSPy 使用训练、开发和测试集进行操作。对于数据中的每个示例,通常有三种类型的值:输入、中间标签和最终标签。虽然中间标签或最终标签是可选的,但拥有一些示例输入是必不可少的。
创建示例对象
DSPy 中的示例对象类似于 Python 字典,但具有实用功能:
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)
输出:
Example({'question': 'This is a question?', 'answer': 'This is an answer.'}) (input_keys=None) This is a question? This is an answer.
指定输入键
在 DSPy 中,Example 对象有一个 with_inputs() 方法,用于将特定字段标记为输入:
print(qa_pair.with_inputs("question")) print(qa_pair.with_inputs("question", "answer"))
可以使用点运算符访问值,而像inputs()和labels()这样的方法分别返回仅包含输入或非输入键的新Example对象。
DSPy 中的优化器
DSPy 优化器可调整 DSPy 程序的参数(即提示和/或 LM 权重)以最大化指定指标。DSPy 提供各种内置优化器,每种优化器都采用不同的策略。
可用的优化器
- BootstrapFewShot:使用提供的标记输入和输出数据点生成少量示例。
- BootstrapFewShotWithRandomSearch:多次应用 BootstrapFewShot 并对生成的演示进行随机搜索。
- COPRO:为每个步骤生成并细化新指令,并通过坐标上升对其进行优化。
- MIPRO:使用贝叶斯优化来优化指令和小样本示例。
选择优化器
如果你不确定从哪里开始,请使用 BootstrapFewShotWithRandomSearch:
对于非常少的数据(10 个示例),请使用 BootstrapFewShot。
对于稍多的数据(50 个示例),请使用 BootstrapFewShotWithRandomSearch。
对于更大的数据集(300 多个示例),请使用 MIPRO。
以下是使用 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)
保存和加载优化程序
通过优化器运行程序后,保存以供将来使用:
优化程序.保存(您的保存路径)
加载已保存的程序:
loaded_program = YOUR_PROGRAM_CLASS() loaded_program.load(path=YOUR_SAVE_PATH)
高级功能:DSPy 断言
DSPy 断言自动执行 LM 上的计算约束,增强 LM 输出的可靠性、可预测性和正确性。
使用断言
定义验证函数并根据相应的模型生成声明断言。例如:
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)), )
使用断言转换程序
from dspy.primitives.assertions import assert_transform_module, backtrack_handler baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)
或者,直接在程序上激活断言:
baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()
断言驱动的优化
DSPy 断言与 DSPy 优化配合使用,尤其是与 BootstrapFewShotWithRandomSearch 配合使用,包括如下设置:
- 带断言的编译
- 编译 + 断言推理
结语
DSPy 提供了一种强大而系统的方法来优化语言模型及其提示。通过遵循这些示例中概述的步骤,您可以轻松构建、优化和评估复杂的 AI 系统。DSPy 的模块化设计和高级优化器可以高效、有效地集成各种语言模型,使其成为 NLP 和 AI 领域任何工作人员的宝贵工具。
无论您构建的是简单的问答系统还是更复杂的管道,DSPy 都能提供实现高性能和可靠性所需的灵活性和稳健性。