在 LLM 应用中,如何优化一个 pipeline 的流程一直是一个比较头疼的问题。提示词作为一个预定义字符串,往往也没有很好地优化方向。本文中的 DSPy 框架或许能在实际应用中对效果优化起到一定帮助。
当前,在 LLM 的应用中,大家都在探索 LLM 中的提示词技巧,并将其组合成解决复杂任务的流水线。不幸的是,现有的 LM 流水线通常使用硬编码的“提示词模板”(prompt template)来实现。为了提供一种更系统的方法来开发和优化 LM 流水线,本文介绍了 DSPy:一种将 LM 流水线抽象为文本转换图( text transformation graphs)的编程模型。DSPy 模块是参数化的,这意味着它们可以通过创建和收集示例来学习如何应用提示、微调、增强和推理技术的组合。案例研究表明:简洁的 DSPy 流程可以表达及优化复杂的 LLM pipeline。开源地址为:DSPY[1]。
Introduction
语言模型(LMs)使得研究人员能够以更高的抽象水平和更少的数据需求构建 NLP 系统,推动了提示技术和轻量级微调技术的发展。这些技术大多是单独探索的,但现在越来越多地用于构建多阶段管道,将复杂任务分解为更易管理的 LM 调用。然而,LM 对提示方式非常敏感,尤其是在需要多次 LM 调用的管道中,这种敏感性更加明显。现有的方法通常依赖于手工制作的“提示模板”,通过试验和错误来生成指令。这种方法虽然常见,但被认为脆弱且不可扩展,类似于手动调整分类器的权重,难以推广到不同的管道、LMs、数据域或输入。
为了设计 AI 管道的更系统方法,本文介绍了 DSPy 编程模型。DSPy 将构建新的 LM 管道从操作自由形式的字符串转向编程(组合模块化操作符以构建文本转换图),其中编译器自动从程序中生成优化的 LM 调用策略和提示。本文从神经网络抽象中获得灵感,其中许多通用层可以在任何复杂架构中模块化组合,模型权重可以使用优化器进行训练而不是手动调整。为此,本文提出了 DSPy 编程模型。首先,将基于字符串的提示技术,包括复杂且依赖任务的提示技术,如 Chain of Thought 和 ReAct,翻译为具有自然语言类型签名的声明性模块。DSPy 模块是任务自适应组件(类似于神经网络层)抽象任何特定的文本转换,如回答问题或总结论文。然后参数化每个模块,以便它可以通过在管道中迭代自举有用的演示来学习其期望行为。受 PyTorch 抽象的启发,DSPy 模块通过富表达力的 define-by-run 计算图使用。管道通过声明所需的模块并在任何逻辑控制流中使用这些模块(例如 if 语句,for 循环,异常等)以逻辑方式连接模块来表达。
然后,本文开发了 DSPy 编译器,该编译器优化任何 DSPy 程序以提高质量或降低成本。编译器输入是程序、一些带有可选标签的训练输入和验证指标。编译器在输入上模拟程序的版本,并自举每个模块的示例轨迹以进行自我改进,使用它们构建有效的 few-shot 提示或微调小 LMs 以执行管道步骤。DSPy 中的优化是高度模块化的:由 teleprompters 进行优化,这些是确定模块如何从数据中学习的通用优化策略。通过这种方式,编译器自动将声明性模块映射到高质量的提示、微调、推理和增强的组合。
像 DSPy 这样的编程模型可以从多个维度进行评估,但本文重点关注专家制作的提示在塑造系统性能中的作用。本文试图通过 DSPy 模块(例如 Chain of Thought 等流行技术的版本)和 teleprompters 减少甚至消除它们的作用。本文进行了啷个案例研究:数学文字问题(GMS8K)和多跳问答(HotPotQA),探索了 chain of thought、多链反思、多跳检索、检索增强问答和代理循环。本文的评估有效地使用了一些不同的编译策略,表明简单的 DSPy 程序优于使用手工制作提示的系统,同时也允许本文的程序有效地使用更小且更高效的 LMs。
总体而言,本文提出了第一个将提示技术转化为参数化声明性模块的编程模型,并引入了具有通用优化策略(teleprompters)的有效编译器来优化这些模块的任意管道。本文的主要贡献是实证和算法性的:借助DSPy
,本文发现可以实现非常短的程序,这些程序可以使用尽可能小的 LMs(如llama2-13b-chat
和T5-Large
(770M 参数))自举自我改进的多阶段 NLP 系统。在没有手工制作的提示且只需几分钟到几十分钟的编译时间内,DSPy 模块的组合可以将简单程序的质量从 33%提高到 82%和从 32%提高到 46%(基于GPT-3.5
),从 9%提高到 47%和从 22%提高到 41%(基于llama2-13b-chat
)。
相关工作
受到 Torch, Theano, Chainer 等在深度学习发展中通过提供强大的抽象功能以起作用的启发,本文旨在为基础模型编程提供一个坚实的概念框架和编程抽象。本文借鉴了可微编程,但应用于 LM 而非神经网络。
上下文学习(In-context learning)是基础模型编程的关键机制。越来越多的研究表明,特别是通过指令调整,可以通过提示词引出 LM 复杂的行为。同样地,通常需要任务特定启发法的弱监督形式,现在由 LM 完成。
上下文学习方法现在常常调用工具,这导致了使用检索模型、多模态基础模型和更传统的工具如 API 和计算器的 LM 流水线的出现。市面上也出现了包括LangChain
、Semantic Kernel
、LlamaIndex
以及许多其他检索和代理库。这些工具包提供了预包装的链和代理,将 LM 与众多可访问的工具连接起来。然而,它们面临着本文在 DSPy 中解决的普遍提示工程挑战:它们通过手写的提示词模板表达任务特定的行为。
研究人员开始应用离散优化和强化学习(RL)来找到有效的提示,通常针对单个逻辑 LM 调用。DSPy 旨在推广这一空间:它通过引导高质量的多阶段演示并附加约束,从高层次声明性签名优化任意流水线。 在这个框架内,DSPy 提示器可以使用诸如交叉验证等模型选择技术进行优化,原则上也可以使用涉及 RL 和 LM 反馈或学习的或贝叶斯超参数优化方法的复杂技术进行优化。
本文旨在激发 DSPy 作为一种编程模型的动机,并报告应用 DSPy 编译器的新发现。本文重点展示了 DSPy 及其编译器如何使得无需手工编写提示字符串就能构建出色的 LM 系统,而是通过真正模块化的单元,这为系统地探索在一个非常高的编程抽象级别上的丰富设计空间打开了大门。
DSPY MODEL
DSPy 将语言模型(LMs)视为用于文本生成的抽象设备,并在任意计算图中优化它们的使用。DSPy 程序用 Python 编写:每个程序接收任务输入(例如,要回答的问题或要总结的论文),并在一系列步骤后返回输出(例如,一个答案或一个摘要)。DSPy 提供了三种抽象来实现自动优化:签名、模块和提词器。
- 签名 Signature:抽象了一个模块的输入/输出行为
- 模块 module:取代了现有的手动提示技术,可以在任意流水线中组合
- 提词器 teleprompter:优化流水线中的所有模块以最大化某个指标
▶签名:抽象化 Prompting 表达
与自由格式的字符串提示不同,DSPy 程序使用自然语言签名来分配工作给语言模型 (LM)。DSPy 签名是函数的自然语言类型声明:一种简洁的声明规范,告诉 DSPy 文本转换需要做什么(例如,“接收问题并返回答案”),而不是如何提示特定的 LM 来实现该行为。以下是一些流行的 LLM 任务的签名示例:
- 问题回答:“question -> answer”
- 检索增强型问题回答:“context, question -> answer”
- 带推理的多项选择题回答:“question, choices -> reasoning, selection”
签名相比提示有两个优势:它们可以编译成自我改进且适应管道的提示或微调。这主要通过为每个签名自举有用的示例来实现。此外,它们处理结构化格式和解析逻辑,以减少(或理想情况下,避免)用户程序中脆弱的字符串操作。以下是一个签名应用的示例:
▶模块:抽象化 Prompting 技术
类似于编程语言中的类型签名,DSPy 签名只是定义了一个接口,并提供了类似类型的提示来预期行为。要使用一个签名,必须声明一个具有该签名的模块。这样的模块声明会返回一个具有该签名的函数。
Predict 模块:在 DSPy 中,使用签名的核心模块是 Predict。在内部,Predict 存储了提供的签名、一个可选的 LM(初始为 None,但可以覆盖该模块的默认 LM),以及一个用于提示的示例列表(初始为空)。类似于 PyTorch 中的层,实例化的模块表现为一个可调用的函数:它接收与签名输入字段(例如 question)对应的关键字参数,格式化一个实现签名的提示并包含适当的示例,调用 LM,并解析输出字段。当 Predict 检测到它正在编译模式下使用时,它还会在内部跟踪输入/输出的痕迹,以帮助提示器在引导示例时。
其他内置模块:DSPy 模块将提示技术转化为支持任何签名的模块化函数,这与使用任务特定细节(例如手写的少样本示例)提示 LM 的标准方法形成对比。为此,DSPy 包含了许多更复杂的模块,如 ChainOfThought、ProgramOfThought、MultiChainComparison 和 ReAct。这些模块都可以互换使用来实现 DSPy 签名。例如,仅仅将上述程序中的 Predict 更改为 ChainOfThought,就可以得到一个在提交输出字段前逐步思考的系统。
重要的是,所有这些模块都通过扩展用户定义的签名并在新签名上调用 Predict 一次或多次来实现,仅需几行代码。例如,下面展示了内置 ChainOfThought 的简化实现。这是一个完全成熟的模块,能够学习任何 LM 或任务的有效少样本提示。
参数化:DSPy 独特地参数化了这些提示技术。要理解这种参数化,请注意任何试图实现特定签名的 LM 调用需要指定的参数包括:(1)要调用的特定 LM,(2)提示指令和每个签名字段的字符串前缀,(3)作为少样本提示(对于冻结的 LM)或作为训练数据(用于微调)的示例。本文主要关注的是自动生成和选择有用的示例。在我们的案例研究中发现,引导好的示例为系统地教导 LM 复杂的流水线新行为提供了强大的方法。
工具:DSPy 程序可以使用工具,这些模块执行计算。我们通过dspy.Retrieve
模块支持检索模型。
程序:DSPy 模块可以在按运行定义的接口中任意组合成流水线。直接受到 PyTorch 和 Chainer 的启发,首先在初始化时声明所需的模块,允许 DSPy 跟踪它们以进行优化,然后用任意代码在 forward 方法中调用这些模块来表达流水线。作为一个简单的例子,我们提供了以下简单但完整的检索增强生成(RAG)系统。
为了突出模块化,我们使用 ChainOfThought 作为基本 Predict 的直接替换。现在只需编写RAG()("Where is Guaran´ı spoken?")
即可使用它。注意,如果我们使用签名"context, question -> search query",我们会得到一个生成搜索查询而不是答案的系统。
▶提词器:自动化 Prompting
在编译 DSPy 程序时,通常会调用一个 teleprompter,这是一个优化器,它接收程序、训练集和度量标准,并返回一个新的优化程序。不同的 teleprompters 会采用不同的优化策略。
在 DSPy 中,训练集可能很小,可能只有少量示例,尽管更大规模的数据可以实现更强大的优化。训练示例可能是不完整的,即只需要输入值。管道步骤的标签不是必需的,除非它们需要在度量标准中使用。实际上,本文通常假设只有(最多)程序的最终输出有标签,而不是中间步骤。这种标签效率对于模块化至关重要:在 DSPy 中构建一个新的管道只需要重新编译新管道的代码,而不需要为新管道注释特定的数据。
度量标准可以是简单的概念,如精确匹配(EM)或 F1,但它们也可以是平衡多个关注点的完整 DSPy 程序。例如,本文可以针对一个问题–答案对的训练集 qa trainset 和度量标准 EM 编译上述 RAG 模块。这里优化的目标是有效地引导少样本示范。以下代码实现了这一点:
在这个例子中,BootstrapFewShot teleprompter
模拟了训练示例中的 RAG。它将收集每个模块的示范(即其输入–输出行为的示例),这些示范共同导致有效输出(即符合签名和度量标准)。如果想要在检索到的上下文中推动编译程序进行抽取,可以定义一个自定义度量标准来代替dspy.evaluate.answer_exact_match
DSPY COMPILER
DSPy 的一个重要表达能力来源于其编译或自动优化任何编程模型中的程序的能力。编译依赖于一个称为 teleprompter 的优化器,该优化器通过提示或微调来提高 DSPy 程序模块的质量(或成本)。典型的 teleprompter 会经历三个阶段。
阶段 1:候选生成 编译器首先(递归地)找到程序中所有唯一的预测模块(预测器),包括那些嵌套在其他模块下的预测器。对于每个唯一的预测器 ,teleprompter 可以为 的参数生成候选值:指令、字段描述或示例(即输入-输出对示例)。尽管语言模型(LMs)可能非常不可靠,但本文发现它们在多阶段设计的解决方案空间搜索方面相当高效。一个良好分解的程序通常可以找到至少几个训练示例,其中 LM 可以通过签名和度量强制执行的约束,如果需要,可以迭代地引导。
阶段 2:参数优化 现在,每个参数都有一组离散的候选值:示例、指令等。许多超参数调优算法(例如,随机搜索或 HyperOpt 和 Optuna 中的树结构 Parzen 估计器)可以应用于候选值的选择。另一种优化类型是使用 BootstrapFinetune 进行微调,其中示例用于更新每个预测器的 LM 权重。当应用此方法时,每个模块的 LM 参数都会更新为新的 LM 权重。通常,本文使用交叉验证在训练集或验证集上优化平均质量。这适用于任何阶段没有标签的情况,具体取决于度量的性质。
阶段 3:高阶程序优化 DSPy 编译器支持的另一种优化类型是修改程序的控制流。最简单的形式之一是集合,本文在案例研究中使用了这种形式。一个集合将引导同一程序的多个副本,然后将程序替换为一个新的程序,该程序并行运行所有副本并通过自定义函数(例如,多数投票)将其预测结果合并为一个。在未来的工作中,这个阶段可以轻松容纳更多动态(即测试时)引导以及自动回溯逻辑的技术。
6 案例研究: 数学问题回答
我们在流行的 GSM8K 数据集上进行评估,该数据集包含小学数学问题。我们从官方训练集中分别采样了 200 个和 300 个问答对用于训练和开发。最终评估使用了官方的 1.3k 测试集示例。为了避免在测试上过拟合,本文在开发集上进行了广泛的比较。按照之前在 GSM8K 上的工作,本文评估了出现在 LM 输出中的最终数值的准确性。
使用的程序:对于此任务,本文考虑了三个简单的 DSPy 程序:一个单步的 Predict 模块(vanilla),一个两步的ChainOfThought
模块(CoT),以及一个多阶段的ComparerOfThoughts
模块(ThoughtReflection
)。这些程序如下:
在反思中,从 LM 中采样了五个推理链(以及它们的答案),并通过内置的MultiChainComparison
模块并行比较,这个模块推广了先前的工作。这会生成一个新的答案,考虑到五次尝试的模式。重要的是,所使用的模块都是通用的,没有一个是特定的数学问题或特定的 LM。
编译:如本文前面讨论的,DSPy 程序可以编译成新的优化程序。在实验中,本文评估了零样本(不编译)以及一些编译策略。最简单的编译器是LabeledFewShot
:
此处,program 可以是任何 DSPy 模块。这只是从训练集中随机采样 k=8 个示例,用于训练示例和签名中共有的字段,在这种情况下是问题和答案,而不是推理。例如,应用这种随机采样时,本文报告了 3-5 次运行的平均值(取决于设置)。
接下来,本文还考虑了使用随机搜索来引导少样本示例:
这将为训练集中的示例生成示范链,并优化选择这些示范的过程以自我改进程序的模块。顾名思义,这是通过随机搜索完成的,将示范选择视为一个需要优化的参数。
接下来,如果需要,这个引导过程可以嵌套在 DSPy 中。特别是,可以使用优化后的引导程序本身来进一步引导另一个程序。例如,当原始零样本程序执行较差时,这种方法是相关的。
GSM8K 包含人类推理链。上述训练集不包含这些推理链。本文还使用人类 CoT 进行了评估,这扩展了训练集中的示例,包含了人类的推理字符串。这两个数据集可以互换使用,作为上述训练集参数的值。需要注意的是,编译通常在几分钟(或几十分钟)内完成,因为即使是成本较高的设置也只需要运行程序几千次(例如,对 150-300 个验证示例进行 10-20 次试验),并且可以并行进行。
结果:本文的实验结果如上表所示,其中包括开发结果以及对每种方法在测试集上的有前途的代表进行的评估。首先,vanilla 程序的结果显示,当需要直接预测答案时,即在不使用推理链的情况下,GPT-3.5 和 llama2-13b-chat 在数学文字题上表现不佳。这在缺乏良好示范时最为明显,可以在无编译设置(即零样本指令)和少样本设置(即随机采样问答对)中看到。然而,有趣的是,通过引导编译和将这个过程迭代到 bootstrap×2,vanilla 得到了显著的帮助。在检查引导的提示时,发现提示允许 LM 首先利用答案字段进行推理,这是允许的,因为度量标准提取了最终的数值进行评估。
接下来,本文考虑了 CoT 程序。虽然专家人类推理链(+human CoT)在可用时提供了很大的提升,但是使用引导可以匹配或超越这一点,证明了本文的假设,即 DSPy 可以减少手工制作提示的需要。除此之外,反思程序虽然仅比其他程序长几行,但显然是赢家,尽管 CoT 在集成方面也相当有效。总体而言,引导编译过程为每个程序带来了很大的提升,跨越了两种 LM。实际上,表中的所有程序都是通过组合两到四个 DSPy 模块和提示器表达的,总体上揭示了在 DSPy 规定的新范式中,是组合合适的通用模块,而不是操作字符串提示,提高了不同 LM 的准确率,从 4-20%提高到 49-88%。
总结
本文介绍了 DSPy,这是一种利用大语言模型(LMs)和其他工具的流水线设计 AI 系统的新编程模型。本文提出了这一抽象中引入的三个新概念(DSPy 签名、模块和提示器),并通过案例研究展示了它支持快速开发高效系统的能力,这些系统使用的 LMs 相对较小。尽管上下文学习在过去 2-3 年的 LM 研究中已被证明具有变革性,本文认为这一新兴范式的真正表现力在于构建复杂的文本转换图,其中可组合模块和优化器(提示器)结合起来,以更系统和可靠的方式利用 LMs。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。