Created
Aug 14, 2024 11:27 AM
Favorite
Favorite
Priority
备注
推荐
类型
DSPy
这份笔记介绍了DSPy框架,用于基础模型编程,即语言模型(LMs)和检索模型(RMs)。
DSPy强调编程而非提示。它统一了用于提示和微调LMs的技术,以及通过推理和工具/检索增强来改进它们,所有这些都通过一组最小化的Python操作来表达和学习。
DSPy提供了用于指导LMs的可组合和声明性模块,采用熟悉的Python语法。此外,DSPy引入了一个自动编译器,教导LMs如何执行程序中的声明步骤。DSPy编译器将在内部_跟踪_您的程序,然后为大型LMs制作高质量提示(或为小型LMs训练自动微调),以教导它们执行您的任务的步骤。
0] 设置
正如我们将在下面看到的,DSPy 可以经常地教授强大的模型,比如
GPT-3.5
和本地模型,比如 T5-base
或 Llama2-13b
,使其在复杂任务上更加可靠。DSPy 将相同的程序编译成不同的 few-shot 提示和/或为每个 LM 进行微调。让我们开始设置吧。如果尚未安装 DSPy,下面的代码片段也将安装 DSPy。
In [1]:
1] 入门指南
我们将首先设置语言模型(LM)和检索模型(RM)。DSPy支持多种API和本地模型。在这个笔记本中,我们将使用GPT-3.5(
gpt-3.5-turbo
)和检索器ColBERTv2
。为了简化操作,我们已经设置了一个ColBERTv2服务器,托管了一个维基百科2017年“摘要”搜索索引(即包含来自2017转储的每篇文章的第一段),因此您无需担心设置!而且是免费的。
注意: 如果您想在不更改示例的情况下运行此笔记本,则不需要API密钥。所有示例已经在内部缓存,因此您可以检查它们!
In [2]:
在上面的最后一行中,我们将DSPy配置为默认使用 turbo LM 和 ColBERTv2 检索器(在维基百科2017摘要上)。如果需要的话,这将很容易被本地程序的部分覆盖。
工作流程说明
您可以为各种任务构建自己的DSPy程序,例如问答、信息提取或文本到SQL。
无论任务是什么,一般的工作流程如下:
- 收集一些数据。 定义程序输入和输出的示例(例如问题及其答案)。这可能只是您记下的一些快速示例。如果存在大型数据集,那就越多越好!
- 编写您的程序。 定义程序的模块(即子任务)以及它们应该如何相互交互以解决您的任务。
- 定义一些验证逻辑。 什么样的运行结果对您的程序来说是好的?也许答案需要具有特定的长度或遵循特定的格式?指定检查这一点的逻辑。
- 编译! 请求DSPy使用您的数据“编译”您的程序。编译器将使用您的数据和验证逻辑来优化您的程序(例如提示和模块),使其高效且有效!
- 迭代。 通过改进您的数据、程序、验证,或者使用DSPy编译器的更高级功能,重复这个过程。
现在让我们看看这个过程是如何实现的。
2] 任务示例
DSPy 可以适用于各种应用和任务。在这个介绍性笔记本中,我们将处理多跳问答(QA)的示例任务。
其他笔记本和教程将呈现不同的任务。现在,让我们从HotPotQA多跳数据集中加载一个小样本。
In [3]:
Out[3]:
我们刚刚加载了
trainset
(20个示例)和devset
(50个示例)。我们的训练集中的每个示例只包含一个问题和其(人工标注的)答案。DSPy通常需要非常少的标记。而您的流水线可能涉及六七个复杂步骤,您只需要标记初始问题和最终答案。DSPy将引导任何需要支持您的流水线的中间标签。如果您以任何方式更改流水线,则引导的数据将相应更改!
现在,让我们看一些数据示例。
In [4]:
在开发集中的示例包含第三个字段,即相关维基百科文章的标题。这并非必要,但为了本介绍的目的,它将帮助我们了解我们的程序表现如何。
In [5]:
加载原始数据后,我们对每个示例应用了
x.with_inputs('question')
,以告诉 DSPy 我们每个示例中的输入字段将仅为 question
。任何其他字段都是标签或元数据,不会提供给系统。In [6]:
请注意,HotPotQA数据集并没有什么特别之处:它只是一个示例列表。
您可以按照以下方式定义自己的示例。未来的笔记本将指导您在不寻常或数据稀缺的环境中创建自己的数据,这是DSPy擅长的领域。
3] 构建模块
在DSPy中,我们将保持以声明方式定义模块和在管道中调用它们来解决任务之间的清晰分离。
这使您可以专注于管道的信息流。然后,DSPy将获取您的程序并自动优化如何提示(或微调)适用于您特定管道的LM,以使其正常工作。
如果您有PyTorch的经验,您可以将DSPy视为基础模型空间的PyTorch。在我们看到它实际运行之前,让我们先了解一些关键部分。
使用语言模型:签名 和 预测器
在 DSPy 程序中每次调用 LM 都需要有一个 签名。
签名由三个简单元素组成:
- LM 应该解决的子任务的最小描述。
- 一个或多个输入字段的描述(例如,输入问题),我们将提供给 LM。
- 一个或多个输出字段的描述(例如,问题的答案),我们期望从 LM 中得到。
让我们为基本问题回答定义一个简单的签名。
In [7]:
在
BasicQA
中,文档字符串描述了这里的子任务(即回答问题)。每个InputField
或OutputField
也可以选择包含一个描述desc
。如果没有提供,它将从字段的名称中推断(例如,question
)。请注意,在DSPy中,这个签名并没有什么特别之处。我们可以很容易地定义一个签名,它接受来自PDF的长段落,并输出结构化信息,例如。
无论如何,既然我们有了一个签名,让我们定义并使用一个Predictor。预测器是一个模块,它知道如何使用LM来实现一个签名。重要的是,预测器可以学习来适应任务的行为!
In [8]:
在上面的示例中,我们询问了有关“餐厅:不可能”的厨师的预测器。该模型输出了一个答案(“美国人”)。
为了更清晰地查看,我们可以检查这个极其基本的预测器是如何实现我们的签名的。让我们检查我们的LM(turbo)的历史。
In [9]:
这位厨师既是英国人又是美国人,但我们无法知道模型是否只是猜测“美国人”,因为这是一个常见的答案。一般来说,添加检索和学习将有助于语言模型更加客观,我们将在接下来的一分钟内探讨这一点!
但在我们这样做之前,我们怎么样_只是_改变预测器呢?允许模型引发一系列思考与预测会是不错的。
In [10]:
这确实是一个更好的答案:模型找出了问题中的厨师是Robert Irvine,并正确地确定他是英国人。
这些预测器(
dspy.Predict
和dspy.ChainOfThought
)可以应用于_任何_签名。正如我们将在下面看到的,它们还可以被优化以从您的数据和验证逻辑中学习。使用检索模型
使用检索器非常简单。一个模块
dspy.Retrieve(k)
将搜索与给定查询匹配的前 k
个段落。默认情况下,这将使用我们在笔记本顶部配置的检索器,即在维基百科索引上的 ColBERTv2。
In [11]:
随意提出任何其他问题。
In [12]:
Out[12]:
4] 程序 1: 基本的检索增强生成(“RAG”)
让我们为这个任务定义我们的第一个完整程序。我们将构建一个用于答案生成的检索增强管道。
给定一个问题,我们将在维基百科中搜索前三个段落,然后将它们作为答案生成的上下文。
让我们从定义这个签名开始:
上下文,问题 --> 答案
。In [13]:
好的。现在让我们定义实际的程序。这是一个从
dspy.Module
继承的类。它需要两个方法:
__init__
方法将简单地声明它需要的子模块:dspy.Retrieve
和dspy.ChainOfThought
。后者被定义为实现我们的GenerateAnswer
签名。
forward
方法将描述使用我们拥有的模块来回答问题的控制流程。
In [14]:
编译 RAG 程序
定义了这个程序之后,现在让我们来编译它。编译程序将会更新每个模块中存储的参数。在我们的设置中,这主要是收集和选择好的示范,以便包含在您的提示中。
编译取决于三件事情:
- 一个训练集。 我们将使用上面的
trainset
中的 20 个问题-答案示例。
- 一个验证度量。 我们将定义一个快速的
validate_context_and_answer
函数,用于检查预测的答案是否正确。它还会检查检索到的上下文是否确实包含该答案。
- 一个特定的提示器。 DSPy 编译器包含了许多可以优化您的程序的提示器。
电子提示器: 电子提示器是强大的优化器,可以接受任何程序并学习引导和选择其模块的有效提示。因此得名,意为“远程提示”。
不同的电子提示器在优化成本与质量等方面提供各种权衡。在这个笔记本中,我们将使用一个简单的默认
BootstrapFewShot
。如果你喜欢类比,你可以将这看作是标准DNN监督学习设置中的训练数据、损失函数和优化器。而SGD是一种基本的优化器,还有更复杂(更昂贵!)的优化器,比如Adam或RMSProp。
In [15]:
现在我们已经编译了我们的RAG程序,让我们试一试。
In [16]:
很好。我们来检查语言模型的最后一个提示吧?
In [17]:
尽管我们还没有编写任何这些详细的演示,但我们看到DSPy能够从我们极其简单的程序中引导出这个包含3,000个标记的提示,用于3-shot检索增强生成,包括硬负面段落和思维链。
这展示了组合和学习的力量。当然,这只是由特定的电子提示器生成的,可能在每种设置下都不完美。正如您将在DSPy中看到的,您有一个庞大但系统化的选项空间,可以优化和验证程序的质量和成本。
如果您有兴趣,您可以轻松地检查学习到的对象本身。
In [18]:
评估答案
现在我们可以在开发集上评估我们的
compiled_rag
程序。当然,这个小数据集并不意味着是一个可靠的基准,但用它来进行说明是很有益的。首先,让我们评估预测答案的准确性(完全匹配)。
In [19]:
question | example_answer | gold_titles | context | pred_answer | answer_exact_match | |
0 | Are both Cangzhou and Qionghai in the Hebei province of China? | no | {'Cangzhou', 'Qionghai'} | ['Cangzhou | Cangzhou () is a prefecture-level city in eastern Hebei province, People\'s Republic of China. At the 2010 census, Cangzhou\'s built-up ("or metro") area... | No | ✔️ [True] |
1 | Who conducts the draft in which Marc-Andre Fleury was drafted to the Vegas Golden Knights for the 2017-18 season? | National Hockey League | {'2017 NHL Expansion Draft', '2017–18 Pittsburgh Penguins season'} | ['2017–18 Pittsburgh Penguins season | The 2017–18 Pittsburgh Penguins season will be the 51st season for the National Hockey League ice hockey team that was... | National Hockey League | ✔️ [True] |
2 | The Wings entered a new era, following the retirement of which Canadian retired professional ice hockey player and current general manager of the Tampa Bay... | Steve Yzerman | {'2006–07 Detroit Red Wings season', 'Steve Yzerman'} | ['Steve Yzerman | Stephen Gregory "Steve" Yzerman ( ; born May 9, 1965) is a Canadian retired professional ice hockey player and current general manager... | Steve Yzerman | ✔️ [True] |
3 | What river is near the Crichton Collegiate Church? | the River Tyne | {'Crichton Castle', 'Crichton Collegiate Church'} | ["Crichton Collegiate Church | Crichton Collegiate Church is situated about 0.6 mi south west of the hamlet of Crichton in Midlothian, Scotland. Crichton itself is... | River Tyne | ✔️ [True] |
4 | In the 10th Century A.D. Ealhswith had a son called Æthelweard by which English king? | King Alfred the Great | {'Æthelweard (son of Alfred)', 'Ealhswith'} | ["Æthelweard of East Anglia | Æthelweard (died 854) was a 9th-century king of East Anglia, the long-lived Anglo-Saxon kingdom which today includes the English counties... | King Alfred the Great | ✔️ [True] |
... 45 more rows not displayed ...
Out[19]:
评估检索结果
查看检索准确性也是有益的。有多种方法可以做到这一点。通常,我们可以检查检索到的段落是否包含答案。
话虽如此,由于我们的开发集包含应该被检索的黄金标题,我们可以在这里直接使用这些标题。
In [20]:
question | example_answer | gold_titles | context | pred_answer | gold_passages_retrieved | |
0 | Are both Cangzhou and Qionghai in the Hebei province of China? | no | {'Cangzhou', 'Qionghai'} | ['Cangzhou | Cangzhou () is a prefecture-level city in eastern Hebei province, People\'s Republic of China. At the 2010 census, Cangzhou\'s built-up ("or metro") area... | No | ❌ [False] |
1 | Who conducts the draft in which Marc-Andre Fleury was drafted to the Vegas Golden Knights for the 2017-18 season? | National Hockey League | {'2017 NHL Expansion Draft', '2017–18 Pittsburgh Penguins season'} | ['2017–18 Pittsburgh Penguins season | The 2017–18 Pittsburgh Penguins season will be the 51st season for the National Hockey League ice hockey team that was... | National Hockey League | ✔️ [True] |
2 | The Wings entered a new era, following the retirement of which Canadian retired professional ice hockey player and current general manager of the Tampa Bay... | Steve Yzerman | {'2006–07 Detroit Red Wings season', 'Steve Yzerman'} | ['Steve Yzerman | Stephen Gregory "Steve" Yzerman ( ; born May 9, 1965) is a Canadian retired professional ice hockey player and current general manager... | Steve Yzerman | ✔️ [True] |
3 | What river is near the Crichton Collegiate Church? | the River Tyne | {'Crichton Castle', 'Crichton Collegiate Church'} | ["Crichton Collegiate Church | Crichton Collegiate Church is situated about 0.6 mi south west of the hamlet of Crichton in Midlothian, Scotland. Crichton itself is... | River Tyne | ✔️ [True] |
4 | In the 10th Century A.D. Ealhswith had a son called Æthelweard by which English king? | King Alfred the Great | {'Æthelweard (son of Alfred)', 'Ealhswith'} | ["Æthelweard of East Anglia | Æthelweard (died 854) was a 9th-century king of East Anglia, the long-lived Anglo-Saxon kingdom which today includes the English counties... | King Alfred the Great | ❌ [False] |
... 45 more rows not displayed ...
尽管这个简单的
compiled_rag
程序能够正确回答相当一部分问题(在这个小数据集上超过40%),但检索质量要低得多。这可能表明语言模型经常依赖于训练期间记忆的知识来回答问题。为了解决这种检索能力较弱的问题,让我们探索一个涉及更高级搜索行为的第二个程序。
5] 程序 2: 多跳搜索(“Baleen”)
通过探索训练/开发集中更难的问题,很明显单个搜索查询通常不足以完成这项任务。例如,当一个问题询问《Right Back At It Again》的作者的出生城市时,可以看到这一点。一个搜索查询正确地识别了作者为“Jeremy McKinnon”,但它无法找出他的出生日期。
在检索增强的自然语言处理文献中,针对这一挑战的标准方法是构建多跳搜索系统,如GoldEn(Qi等,2019)和Baleen(Khattab等,2021)。这些系统读取检索结果,然后根据需要生成额外的查询来收集额外的信息。使用DSPy,我们可以轻松地在几行代码中模拟这样的系统。
我们仍然会使用上面RAG实现中的
GenerateAnswer
签名。现在我们需要一个签名来描述“跳跃”行为:根据一些部分上下文和一个问题,生成一个搜索查询以找到缺失的信息。In [21]:
注意:我们本可以编写
context = GenerateAnswer.signature.context
来避免重复描述 context
字段。现在,让我们定义程序本身
SimplifiedBaleen
。有许多可能的实现方式,但为简单起见,我们将此版本保留在关键要素上。In [22]:
正如我们所看到的,
__init__
方法定义了几个关键的子模块:- generate_query: 对于每一跳,我们将使用一个带有
GenerateSearchQuery
签名的dspy.ChainOfThought
预测器。
- retrieve: 这个模块将执行实际的搜索,使用生成的查询。
- generate_answer: 这个
dspy.Predict
模块将在所有搜索步骤之后使用。它具有一个GenerateAnswer
,用于实际生成答案。
forward
方法使用这些子模块进行简单的控制流程。- 首先,我们会循环最多
self.max_hops
次。
- 在每次迭代中,我们将使用位于
self.generate_query[hop]
处的预测器生成搜索查询。
- 我们将使用该查询检索前 k 个段落。
- 我们将将(去重的)段落添加到我们的
context
累加器中。
- 循环结束后,我们将使用
self.generate_answer
生成一个答案。
- 我们将返回一个带有检索到的
context
和预测的answer
的预测结果。
检查Baleen程序的零射版本
我们很快将编译这个程序。但在此之前,我们可以在“零射”设置中尝试它(即,没有任何编译)。
在零射(未编译)设置中使用程序并不意味着质量会很差。这只是意味着我们直接受到底层LM理解我们的子任务的可靠性的限制。
当使用最昂贵/强大的模型(例如GPT-4)处理最简单和最标准的任务(例如回答关于流行实体的简单问题)时,这通常是可以接受的。
然而,对于更专业的任务、新领域/设置以及更高效(或开放)的模型,零射方法很快就会显得力不从心。DSPy可以帮助您处理所有这些设置。
In [23]:
让我们检查最后三次对LM的调用(即生成第一跳的查询、生成第二跳的查询和生成答案)。
In [24]:
编译 Baleen 程序
现在是时候编译我们的多跳(
SimplifiedBaleen
)程序了。我们首先定义我们的验证逻辑,这将简单要求:
- 预测的答案与标准答案匹配。
- 检索到的上下文包含标准答案。
- 生成的查询中没有冗长的内容(即,没有超过 100 个字符的查询)。
- 生成的查询中没有粗略重复的内容(即,没有与之前的查询在 0.8 或更高的 F1 分数范围内)。
In [25]:
就像我们为RAG所做的那样,我们将使用DSPy中最基本的电传视器之一,即
BootstrapFewShot
。In [26]:
评估检索
早些时候,我们的简单RAG程序似乎并不很有效地找到回答每个问题所需的所有证据。通过在
SimplifiedBaleen
的forward
函数中添加一些额外步骤来解决这个问题了吗?编译是否有助于解决这个问题?这些问题的答案并不总是显而易见。然而,DSPy使得尝试许多不同的方法变得非常容易,而且付出的努力很少。
让我们评估我们编译和未编译的Baleen管道的检索质量!
In [27]:
In [28]:
question | example_answer | gold_titles | context | pred_answer | gold_passages_retrieved | |
0 | Are both Cangzhou and Qionghai in the Hebei province of China? | no | {'Cangzhou', 'Qionghai'} | ['Cangzhou | Cangzhou () is a prefecture-level city in eastern Hebei province, People\'s Republic of China. At the 2010 census, Cangzhou\'s built-up ("or metro") area... | No | ✔️ [True] |
1 | Who conducts the draft in which Marc-Andre Fleury was drafted to the Vegas Golden Knights for the 2017-18 season? | National Hockey League | {'2017 NHL Expansion Draft', '2017–18 Pittsburgh Penguins season'} | ["2017 NHL Expansion Draft | The 2017 NHL Expansion Draft was an expansion draft conducted by the National Hockey League on June 18–20, 2017 to... | National Hockey League (NHL) | ❌ [False] |
2 | The Wings entered a new era, following the retirement of which Canadian retired professional ice hockey player and current general manager of the Tampa Bay... | Steve Yzerman | {'2006–07 Detroit Red Wings season', 'Steve Yzerman'} | ['List of Tampa Bay Lightning general managers | The Tampa Bay Lightning are an American professional ice hockey team based in Tampa, Florida. They play... | Steve Yzerman | ❌ [False] |
3 | What river is near the Crichton Collegiate Church? | the River Tyne | {'Crichton Castle', 'Crichton Collegiate Church'} | ["Crichton Collegiate Church | Crichton Collegiate Church is situated about 0.6 mi south west of the hamlet of Crichton in Midlothian, Scotland. Crichton itself is... | River Tyne | ✔️ [True] |
4 | In the 10th Century A.D. Ealhswith had a son called Æthelweard by which English king? | King Alfred the Great | {'Æthelweard (son of Alfred)', 'Ealhswith'} | ['Æthelweard (son of Alfred) | Æthelweard (d. 920 or 922) was the younger son of King Alfred the Great and Ealhswith.', 'Æthelred the Unready |... | King Alfred the Great | ❌ [False] |
... 45 more rows not displayed ...
In [29]:
太棒了!这个编译的、多跳程序可能有一些东西。但这远非你所能做的全部:DSPy 为你提供了一个干净的可组合操作符空间,以处理你所见到的任何缺陷。
我们可以检查一些具体的例子。如果我们发现失败的原因,我们可以:
- 通过使用额外的子模块来扩展我们的流水线(例如,在检索后进行总结?)
- 通过使用更复杂的逻辑来修改我们的流水线(例如,也许我们需要在找到所有所需信息后跳出多跳循环?)
- 优化我们的验证逻辑(例如,也许使用一个度量标准,该度量标准使用第二个 DSPy 程序来进行答案评估,而不是依赖严格的字符串匹配)
- 使用不同的提示器来更积极地优化你的流水线。
- 添加更多或更好的训练示例!
或者,如果你真的想要的话,我们可以调整我们在程序中使用的签名描述,使它们更精确地适用于它们的子任务。这类似于提示工程,应该是最后的手段,考虑到 DSPy 给我们提供的其他强大选项!
In [30]: