DSPy 简介

DSPy 简介

Barry Lv6

DSPy 简介

声明式自进化语言程序(DSPy)旨在将程序流程与提示信息分离,同时基于关键指标优化提示内容。

引言与总体观察

最近几天,我一直在努力理解DSPy,特别是它与LangChain和LlamaIndex的关系。

以下是一些初步观察,任何反馈或额外见解都将非常有帮助……

术语

我们已经逐渐认可了LlamaIndex和LangChain等框架引入的固定术语。DSPy正在引入一系列新术语,如签名、原本称为Teleprompters现改为优化器(Optimisers)、模块等。

简化原则

简化原则在RAG的吸引力中扮演了重要角色,因为RAG本身并不晦涩,而是非常透明。

像LangChain这样的框架做了大量工作,以在每一步引入可检查性和可观察性,确保在应用流程中的每一步都能被观察和检查。

而DSPy试图引入编程接口并自动化和隐藏提示工程部分,这使得DSPy在很大程度上变得不透明,并未有助于简化。

提示工程

DSPy 的目标之一是抽象化提示工程,从而将编程与提示分离。用户向 DSPy 描述做什么,而不是如何做

这一切都在幕后实现,用户通过一个或多个签名来定义需求,DSPy 选择最适合当前任务的提示模板和提示策略/实践。

因此,签名可以看作是提示的简写,表达您的需求并让 DSPy 执行。

简化

大多数框架正逐渐趋同于相同的基本理念,其中许多理念实际上是简化的方法,即将特定功能抽象化。

我们所熟知的抽象包括流程构建、提示操场、分块处理等。将这些不同的抽象统一到一个数据生产力套件中是有意义的。

DSPy 并未参与这一简化过程,而是采用了一种更为程序化的方法。

可能的DSPy应用场景

探索与优化

DSPy 可以很好地作为接口来描述您的需求,分享极少量的数据,并让 DSPy 生成最优的提示、提示模板和提示策略。

为了在不花费巨资的情况下获得更好的结果,您应该尝试不同的方法,如将任务分解为更小的部分、优化提示、增加数据、微调以及选择较小的模型。真正的魔力在于这些方法协同工作时,但调整其中一种可能会影响其他方法。

图形用户界面(GUI)

DSPy 是一种编程方法,我可以想象到,对于更基础的实现,DSPy 将如何从 GUI 中获益。设想用户能够上传样本数据,用自然语言描述他们想要实现的目标,然后通过 GUI 生成带有模板等的提示策略。

使用场景

在决定DSPy是否适合您的实施方案时,需要考虑使用场景。对于所有实施方案而言,使用场景都应处于主导地位。

本质上,DSPy旨在满足那些需要轻量级自优化编程模型的场景,而非依赖预定义提示和集成。

介绍DSPy

机器学习社区正致力于让语言模型(LLMs)更好地理解提示以及通过堆叠串联它们来解决复杂任务。

手动制作模板是最常见的方法,但DSPy团队认为这种方法脆弱且不可扩展。与依赖固定提示模板和管道的流程不同,DSPy不依赖这些。

为了改进这一点,DSPy引入了一种新的开发和优化LM管道的方式。它将它们视为文本转换图,其中通过声明式模块使用LLMs。

正如你稍后将看到的,这些模块可以从演示中学习如何应用各种技术,如提示微调推理

设计了一个编译器来优化DSPy管道,以提高性能。

两个案例研究表明,DSPy能够表达和优化复杂的LM管道,用于解决数学应用题和回答复杂问题等任务。

DSPy 工作流程

参考下图,DSPy 程序可以执行多种任务,包括问答、信息提取等。无论何种任务,其通用工作流程保持不变:

DSPy 采用迭代方法。首先,您概述任务及目标优化的指标。

接着,您收集一组示例输入,通常不带标签(或仅在最终输出时根据指标需要带标签)。

然后,您通过选择可用的内置层(模块)构建管道,为每层指定特定的输入/输出规范,并根据需要在 Python 代码中整合这些层。

最后,您使用 DSPy 优化器将代码转换为高质量指令,生成自动少样本示例,或更新语言模型(LM)的权重。这一迭代过程确保任务高效执行,并最大化 DSPy 实施的效果。

签名

从DSPy到LLM的每一次调用都需要一个签名,该签名由三个元素组成,如下所示。对子任务的简要描述。一个或多个输入字段和输出字段的描述。

DSPy程序不使用自由形式的字符串提示,而是使用自然语言签名来分配工作给LM。

DSPy签名是自然语言类型的声明,可以描述为一个简短的声明性规范,告诉DSPy需要进行什么样的文本转换,而不是如何提示特定的LM实现该行为。

更正式地说,DSPy签名是一个包含以下内容的元组:

  1. 输入字段
  2. 输出字段以及
  3. 一个可选的指令

DSPy编译器将使用上下文学习来解释问题与答案的不同,并会迭代地优化这些字段的使用。

签名相对于提示的好处在于,它们可以被编译成自我改进和适应管道的提示或微调。

这主要是通过为每个签名引导有用的演示示例来实现的。此外,它们处理结构化格式和解析逻辑,以减少或避免用户程序中的脆弱字符串操作。

实际上,DSPy签名可以用简写符号表示,如问题 -> 答案,因此以下第一行是一个完整的DSPy程序,用于基本的问题回答系统(第二行说明用法,第三行是当GPT-3.5作为LM时的响应):

1
2
3
1 qa = dspy.Predict("question -> answer")
2 qa(question="Where is Guaran ́ı spoken?")
3 # Out: Prediction(answer='Guaran ́ı is spoken mainly in South America.')

提词器(现称为优化器)

在编译DSPy程序时,通常会调用一个提词器,它是一个优化器,接收(1)程序、(2)训练集和(3)指标——并返回一个经过优化的新程序。

不同的提词器采用不同的优化策略。

编译器

DSPy 表达能力的关键来源在于其能够编译或自动优化此编程模型中的任何程序。

编译依赖于提词器(Teleprompter),这是 DSPy 程序的优化器,通过提示或微调来提升模块的质量或成本,这些在 DSPy 中是统一的。

尽管 DSPy 在创建新提词器时不强制执行此流程,典型的提词器通常会经历三个阶段:

  1. 候选生成
  2. 参数优化
  3. 高阶程序优化

DSPy vs LangChain & LlamaIndex

LangChain和LlamaIndex专注于高级应用开发,提供即插即用的应用模块,能与您的数据或配置无缝集成。

如果您倾向于使用标准提示来完成如PDF问答或文本转SQL等任务,这些库提供了一个全面的生态系统供您探索。

相比之下,DSPy采取了不同的策略……

  • 它不附带为特定应用定制的预定义提示。可以想象一个拥有可搜索提示集合的提示中心。
  • 相反,DSPy引入了一套紧凑而强大的多功能模块。
  • 这些模块能够根据您的数据,在您的流程中适应和优化提示(或微调语言模型)。
  • 无论您是在修改数据集、调整程序控制流,还是切换到不同的语言模型,DSPy的编译器都能无缝生成针对您特定流程优化的提示或微调配置。

根据您的项目需求,DSPy有可能以最小的努力为您带来更出色的任务结果,特别是如果您愿意编写或扩展自己的脚本。

本质上,DSPy是为那些需要轻量级自优化编程模型的场景设计的,而不是依赖预定义提示和集成。

最小工作示例

这个最小工作示例展示了如何将如下所示的简单签名转换为DSPy的思维链提示…

在DSPy的文档 中,有一个最小工作示例,我成功地在Colab笔记本中运行了它。这是一个利用DSPy库的思维链示例。

该简短教程使用了GSM8K数据集 和OpenAI的GPT-3.5-turbo模型在DSPy中模拟提示任务。

首先导入必要的模块并配置语言模型:

1
2
3
4
5
6
7
8
9
10
11
###################################################
import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric

# 设置语言模型
turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250)
dspy.settings.configure(lm=turbo)

# 从GSM8K数据集中加载数学问题
gsm8k = GSM8K()
gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]

然后你可以打印训练集,如下所示:

1
print(gsm8k_trainset)

你可以看到示例的数量、每个问题及其答案。

现在定义一个自定义程序,利用ChainOfThought模块进行逐步推理以生成答案:

1
2
3
4
5
6
7
class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("question -> answer")

def forward(self, question):
return self.prog(question=question)

考虑到上述代码和DSPy文档中的摘录,很明显如何插入问答的签名。

现在我们继续用BootstrapFewShot提示器编译它。在最近的DSPy文档中,术语teleprompter已被替换为Optimizer

1
2
3
4
5
6
7
8
from dspy.teleprompt import BootstrapFewShot

# 设置优化器:我们希望“引导”(即自生成)4个示例的CoT程序。
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)

# 优化!在这里使用`gsm8k_metric`。通常,指标会告诉优化器它的表现如何。
teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config)
optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)

编译DSPy程序后,继续评估其在开发数据集上的性能。

1
2
3
4
5
6
7
from dspy.evaluate import Evaluate

# 设置评估器,可以多次使用。
evaluate = Evaluate(devset=gsm8k_devset, metric=gsm8k_metric, num_threads=4, display_progress=True, display_table=0)

# 评估我们的`optimized_cot`程序。
evaluate(optimized_cot)

为了更深入地理解模型的交互,通过检查模型的历史记录来审查最近的生成:

1
turbo.inspect_history(n=1)

在输出下方,注意指令和模板。

⭐️ 在LinkedIn 上关注我,获取大型语言模型的更新 ⭐️

我目前是Kore AI 首席布道师 。我探索并撰写关于AI与语言交叉领域的所有事物;范围从LLMs 聊天机器人 语音机器人 开发框架 数据中心潜空间 等。

  • 标题: DSPy 简介
  • 作者: Barry
  • 创建于 : 2024-05-28 20:28:06
  • 更新于 : 2024-08-31 06:59:45
  • 链接: https://wx.role.fun/2024/05/28/0e98afb21f454076912057df004d162e/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。