大型语言模型(LLMs)存在数据新鲜度问题。即使是像 GPT-4 这样最强大的模型,也不了解最近的事件。
根据 LLMs 的视角,世界仿佛停滞在某个时间点。它们只知道世界是如何在它们的训练数据中呈现的。
这对于依赖最新信息或特定数据集的任何用例都会带来问题。例如,您可能有一些内部公司文件,您希望通过 LLM 与之互动。
第一个挑战是将这些文件添加到 LLM 中,我们可以尝试训练 LLM 使用这些文件,但这是耗时且昂贵的。而且当添加新文件时会发生什么呢?为每个新文件进行训练是非常低效的,简直是不可能的。
那么,我们如何处理这个问题呢?我们可以使用检索增强技术。这种技术允许我们从外部知识库中检索相关信息,并将这些信息提供给我们的 LLM。
外部知识库就是我们了解 LLM 训练数据之外世界的窗口。在本章中,我们将学习如何使用 LangChain 为 LLMs 实施检索增强。
创建知识库
我们有两种主要类型的知识适用于 LLMs。参数化知识指的是 LLM 在训练过程中学到的一切,它充当了 LLM 的世界的冻结快照。
第二种类型的知识是源知识。这种知识包括通过输入提示输入到 LLM 中的任何信息。当我们谈论检索增强时,我们指的是向 LLM 提供有价值的源知识。
获取我们知识库的数据
为了帮助我们的 LLM,我们需要为其提供访问相关源知识的能力。为了实现这一点,我们需要创建我们自己的知识库。
我们从一个数据集开始。所使用的数据集自然取决于用例。它可以是用于协助编写代码的代码文档,用于内部聊天机器人的公司文件,或者其他任何内容。
在我们的示例中,我们将使用维基百科的一个子集。为了获取这些数据,我们将使用 Hugging Face 数据集:
from datasets import load_dataset
data = load_dataset("wikipedia", "20220301.simple", split='train[:10000]')
print(data)
大多数数据集将包含包含大量文本的记录。因此,我们通常的第一个任务是构建一个预处理管道,将那些长文本分割成更简洁的块。
创建文本块
将我们的文本分割成较小的块对于多个原因至关重要。主要目的是:
- 提高嵌入准确性 - 这将提高后续结果的相关性。
- 减少输入到 LLM 作为源知识的文本数量。限制输入可以提高 LLM 遵循指令的能力,降低生成成本,并帮助我们获得更快的响应。
- 为用户提供更精确的信息源,因为我们可以将信息源缩小到更小的文本块。
对于非常长的文本块,我们将超出嵌入或完成模型的最大上下文窗口。分割这些文本块使得将这些较长的文档添加到我们的知识库成为可能。
要创建这些块,首先需要一种测量文本长度的方法。LLMs 不是按单词或字符测量文本的 - 它们是按标记来测量的。
标记通常是单词或子词的大小,不同的 LLMs 有不同的标记大小。标记本身是由标记器构建的。我们将使用 gpt-3.5-turbo 作为我们的完成模型,并且我们可以像这样初始化该模型的标记器:
import tiktoken
tokenizer = tiktoken.get_encoding('p50k_base')
使用标记器,我们可以从纯文本创建标记并计算标记的数量。我们将把这个过程封装到一个名为 tiktoken_len 的函数中:
def tiktoken_len(text):
tokens = tokenizer.encode(
text,
disallowed_special=()
)
return len(tokens)
有了我们的标记计数函数准备好后,我们可以初始化一个 LangChain RecursiveCharacterTextSplitter 对象。这个对象将允许我们将文本分割成不超过我们通过 chunk_size 参数指定的长度的块。


