OpenAI 使用基于嵌入的搜索回答问题

在许多常见情况下,模型未在数据上进行训练,这些数据包含要在生成对用户查询的响应时可访问的关键事实和信息。如下所示,解决此问题的一种方法是将附加信息放入模型的上下文窗口中。这在许多用例中是有效的,但会导致更高的代币成本。在本笔记本中,我们探讨了这种方法与嵌入库搜索之间的权衡。

GPT 擅长回答问题,但仅限于它从训练数据中记住的主题。

如果你想让 GPT 回答关于不熟悉话题的问题,你应该怎么做?例如,

  • 2021年9月之后的最新活动
  • 您的非公开文件
  • 过去对话中的信息
  • 等。

此笔记本演示了一种两步 Search-Ask 方法,使 GPT 能够使用参考文本库回答问题。

  1. 搜索:在文本库中搜索相关文本部分
  2. 问:将检索到的文本部分插入到 GPT 的消息中,并向其提问

1. 为什么搜索比微调更好

GPT 可以通过两种方式学习知识:

  • 通过模型权重(即在训练集上微调模型)
  • 通过模型输入(即将知识插入到输入消息中)

尽管微调感觉像是更自然的选择——毕竟,数据训练是 GPT 学习所有其他知识的方式——但我们通常不建议将其作为教授模型知识的一种方式。微调更适合教授专业任务或风格,而对于事实回忆则不太可靠。

打个比方,模型权重就像长期记忆。当你微调一个模型时,就像一周后要为考试而学习一样。当考试到来时,模型可能会忘记细节,或者记错它从未读过的事实。

相比之下,消息输入就像短期记忆。当您在消息中插入知识时,就像在打开笔记的情况下参加考试一样。有了笔记,模型更有可能得出正确答案。

与微调相比,文本搜索的一个缺点是每个模型都受到一次可以读取的最大文本量的限制:

模型最大文本长度
gpt-3.5-turbo4,096 tokens(~5 页)
gpt-48,192 tokens(~10 页)
gpt-4-32k32,768 tokens(~40 页)

(新模型具有更长的上下文,gpt-4-1106-preview 具有 128K 上下文窗口)

继续这个类比,你可以把这个模型想象成一个学生,他一次只能看几页笔记,尽管可能有书架上的教科书可以借鉴。

因此,要构建一个能够利用大量文本来回答问题的系统,我们建议使用搜索-询问方法。

可以通过多种方式搜索文本。例如,

  • 基于词汇的搜索
  • 基于图形的搜索
  • 基于嵌入的搜索

此示例笔记本使用基于嵌入的搜索。嵌入很容易实现,并且特别适用于问题,因为问题通常不会在词汇上与其答案重叠。

将仅嵌入搜索视为您自己系统的起点。更好的搜索系统可能会结合多种搜索方法,以及受欢迎程度、新近度、用户历史记录、与先前搜索结果的冗余、点击率数据等功能。同样,GPT 还可以通过自动将问题转换为关键字集或搜索词来潜在地改善搜索结果。

3. 完整程序

具体而言,此笔记本演示了以下过程:

  1. 准备搜索数据(每个文档一次)
    1. 收集(Collect):我们将下载几百篇关于 2022 年奥运会的维基百科文章
    2. 块(Chunk):文档被拆分为要嵌入的简短、大部分是独立的部分
    3. 嵌入(Embed):每个部分都嵌入了 OpenAI API
    4. 存储(Store):保存嵌入(对于大型数据集,请使用矢量数据库)
  2. 搜索(每个查询一次)
    1. 给定用户问题,从 OpenAI API 生成查询的嵌入
    2. 使用嵌入,按与查询的相关性对文本部分进行排名
  3. 询问(每次查询一次)
    1. 将问题和最相关的部分插入到 GPT 的消息中
    2. 返回 GPT 的答案

3.1 成本

由于 GPT 比嵌入搜索更昂贵,因此具有大量查询的系统的成本将由步骤 3 主导。

  • 对于 gpt-3.5-turbo 每个查询使用 ~1,000 个令牌,每个查询的成本为 ~0.002 美元,或每美元 ~500 个查询(截至 2023 年 4 月)
  • 对于gpt-4 ,再次假设每个查询 ~1,000 个代币,则每个查询的成本为 ~0.03 USD,或每美元 ~30 个查询(截至 2023 年 4 月)

当然,确切的成本将取决于系统的具体情况和使用模式。

4. 序言

我们将从以下方面开始:

  • 导入必要的库
  • 选择用于嵌入、搜索和问答的模型

疑难解答:安装库

如果需要安装上述任何库,请在终端中运行。pip install {library_name}

例如,若要安装库,请运行:openai

4.1 激励示例:GPT 无法回答有关时事的问题

由于 gpt-3.5-turbo 和 gpt-4 的训练数据大多在 2024 年 3 月结束,因此模型无法回答有关最近事件(例如 2026 年冬季奥运会)的问题。

例如,让我们试着问“哪些运动员在 2026 年获得了冰壶金牌?

运行结果

在这种情况下,模型对 2026 年一无所知,无法回答问题。

为了帮助模型了解 2026 年冬奥会冰壶(现在是2024年4月,所以下面的数据是2022年的数据,修改了年份而已),

我们可以将相关维基百科文章的上半部分复制并粘贴到我们的消息中:

运行下面的代码

运行结果:

多亏了输入消息中包含的维基百科文章,GPT 回答正确。

在这种特殊情况下,GPT 足够聪明,意识到最初的问题被低估了,因为有三个冰壶金牌项目,而不仅仅是一个。

当然,这个例子在一定程度上依赖于人类的智慧。我们知道这个问题是关于冰壶的,所以我们插入了一篇关于冰壶的维基百科文章。

本笔记本的其余部分演示如何通过基于嵌入的搜索自动执行此知识插入。

5. 准备搜索数据

为了节省您的时间和费用,我们准备了一个预嵌入数据集,其中包含数百篇关于 2022 年冬季奥运会的维基百科文章。

要了解我们如何构建此数据集,或自行修改它,请参阅嵌入维基百科文章以进行搜索。

数据 chunked 在 https://cdn.openai.com/API/examples/data/winter_olympics_2022.csv

由于2022.csv 需要修改为2026年,我们保存下来,然后修改里面有关2022的为2026年

现在,我们将定义一个搜索函数,该函数:

  • 接受用户查询和带有文本和嵌入列的数据帧
  • 使用 OpenAI API 嵌入用户查询
  • 使用查询嵌入和文本嵌入之间的距离对文本进行排名
  • 返回两个列表:
    • 排名前 N 的文本,按相关性排名
    • 它们相应的相关性分数

运行结果:

7. 询问

通过上面的搜索功能,我们现在可以自动检索相关知识并将其插入到 GPT 的消息中。

下面,我们定义一个函数:ask

  • 接受用户查询
  • 搜索与查询相关的文本
  • 将该文本填充到 GPT 的消息中
  • 将消息发送给 GPT
  • 返回 GPT 的答案

7.1 示例问题

最后,让我们问我们的系统关于金牌冰壶运动员的原始问题:

翻译为中文:

gpt-3.5-turbo 尽管对 2026 年冬奥会一无所知,但我们的搜索系统能够检索到参考文本供模型阅读,使其能够正确列出男子和女子锦标赛的金牌获得者。

然而,它仍然不是很完美——该模型未能列出混合双打项目的金牌得主。

7.2 错误答案疑难解答

要查看错误是由于缺少相关源文本(即搜索步骤失败)还是缺乏推理可靠性(即询问步骤失败),您可以查看文本 GPT 是通过设置给出的。print_message=True

在这种特殊情况下,查看下面的文字,看起来给模型的 #1 文章确实包含所有三个项目的奖牌获得者,但后来的结果强调了男子和女子锦标赛,这可能会分散模型的注意力,无法给出更完整的答案。

知道这个错误是由于询问步骤中的推理不完善,而不是搜索步骤中的检索不完美造成的,让我们专注于改进询问步骤。

改善结果的最简单方法是使用功能更强大的模型,例如 .让我们试试 GPT-4 吧。

翻译为中文:

7.3 更多示例

以下是该系统运行中的更多示例。随意尝试自己的问题,看看效果如何。一般来说,基于搜索的系统在具有简单查找的问题上表现最好,而在需要组合和推理多个部分来源的问题上表现最差。

原文链接:Question answering using embeddings-based search | OpenAI Cookbook

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部