详解LLM 核心技能-大文本分块技术

在构建 LLM 相关应用程序的背景下,分块 是将大段文本分解成较小片段的过程。这是一项必不可少的技术,有助于优化我们使用 LLM 嵌入内容后从矢量数据库获取的内容的相关性。在这篇博文中,我们将探讨它是否以及如何有助于提高 LLM 相关应用程序的效率和准确性。

分块的主要原因是为了确保我们嵌入的内容尽可能少地包含噪音,同时仍然具有语义相关性。

例如,在语义搜索中,我们会对文档语料库进行索引,每个文档都包含有关特定主题的宝贵信息。通过应用有效的分块策略,我们可以确保搜索结果准确捕捉用户查询的本质。如果我们的分块太小或太大,可能会导致搜索结果不准确或错失显示相关内容的机会。根据经验,如果文本块在没有周围上下文的情况下对人类有意义,那么对语言模型来说也是如此。因此,找到语料库中文档的最佳分块大小对于确保搜索结果准确且相关至关重要。

另一个例子是对话代理(我们在使用 Python 和 Javascript 之前介绍过)。我们使用嵌入的块基于知识库为对话代理构建上下文,该知识库为代理提供可信信息。在这种情况下,正确选择分块策略很重要,原因有二:首先,它将确定上下文是否与我们的提示真正相关。其次,考虑到我们可以为每个请求发送的令牌数量有限,它将确定我们是否能够在将检索到的文本发送给外部模型提供商(例如 OpenAI)之前将其放入上下文中。在某些情况下,例如当使用带有 32k 上下文窗口的 GPT-4 时,拟合块可能不是问题。不过,我们需要注意何时使用非常大的块,因为这可能会对我们从知识库中获得的结果的相关性产生不利影响。

在本文中,我们将探讨几种分块方法,并讨论在选择分块大小和方法时应考虑的权衡因素。最后,我们将提供一些建议,以确定最适合您的应用程序的分块大小和方法。


嵌入短内容和长内容

当我们嵌入内容时,我们可以根据内容是短(如句子)还是长(如段落或整个文档)预测不同的行为。

句子被嵌入时,生成的向量会关注句子的具体含义。与其他句子嵌入相比,比较自然会在这个层面上进行。这也意味着嵌入可能会错过段落或文档中更广泛的上下文信息。

嵌入完整段落或文档时,嵌入过程会考虑整体上下文以及文本中句子和短语之间的关系。这可以产生更全面的矢量表示,以捕捉文本的更广泛含义和主题。另一方面,较大的输入文本大小可能会引入噪音或削弱单个句子或短语的重要性,使得在查询索引时找到精确匹配变得更加困难。

查询的长度也会影响嵌入之间的关系。较短的查询(例如单个句子或短语)将集中于具体内容,可能更适合与句子级嵌入进行匹配。较长的查询(跨越多个句子或段落)可能更适合段落或文档级的嵌入,因为它可能会寻找更广泛的上下文或主题。

索引也可能是非同质的,包含_不同_大小块的嵌入。这可能会对查询结果相关性造成挑战,但也可能带来一些积极的影响。一方面,由于长内容和短内容的语义表示存在差异,查询结果的相关性可能会波动。另一方面,非同质索引可能会捕获更广泛的上下文和信息,因为不同的块大小代表文本中不同级别的粒度。这可以更灵活地适应不同类型的查询。

分块注意事项

有几个变量在确定最佳分块策略时发挥作用,这些变量根据用例而变化。以下是需要记住的一些关键方面:

  1. 被索引的内容的性质是什么? 您处理的是长文档(例如文章或书籍),还是短内容(例如推文或即时消息)?答案将决定哪种模型更适合您的目标,以及应应用哪种分块策略。
  2. 您使用的是哪种嵌入模型?它在哪些块大小上表现最佳? 例如,句子转换器模型在单个句子上效果很好,但像text-embedding-ada-002这样的模型在包含 256 或 512 个标记的块上表现更好。
  3. 您对用户查询的长度和复杂度有何期望?查询 内容是简短而具体,还是冗长而复杂?这也可能影响您选择对内容进行分块的方式,以便嵌入查询和嵌入块之间有更紧密的关联。
  4. 检索到的结果将如何在您的特定应用程序中使用? 例如,它们将用于语义搜索、问答、摘要还是其他目的?例如,如果您的结果需要输入到另一个具有令牌限制的 LLM,您必须考虑到这一点,并根据您希望在对 LLM 的请求中容纳的块数来限制块的大小。

回答这些问题将使您能够制定一个平衡性能和准确性的分块策略,这反过来将确保查询结果更加相关。

分块方法

组块划分的方法有很多种,每种方法可能适用于不同的情况。通过研究每种方法的优缺点,我们的目标是确定适合应用它们的场景。

固定大小分块

这是最常见、最直接的分块方法:我们只需决定块中的标记数量,以及(可选)它们之间是否应该有重叠。一般来说,我们希望在块之间保留一些重叠,以确保语义上下文不会在块之间丢失。在大多数情况下,固定大小的分块将是最佳路径。与其他形式的分块相比,固定大小的分块在计算上便宜且易于使用,因为它不需要使用任何 NLP 库。

以下是使用LangChain执行固定大小分块的示例:

ini 复制代码
text = "..." # your text
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
    separator = "\n\n",
    chunk_size = 256,
    chunk_overlap  = 20
)
docs = text_splitter.create_documents([text])

"内容感知"分块

这些方法利用了我们正在分块的内容的性质,并对其应用了更复杂的分块。以下是一些示例:

句子拆分

正如我们之前提到的,许多模型都针对嵌入句子级内容进行了优化。自然,我们会使用句子分块,有几种方法和工具可以做到这一点,包括:

  • 简单拆分: 最简单的方法是用句号("。")和换行符拆分句子。虽然这可能既快速又简单,但这种方法不会考虑所有可能的边缘情况。这是一个非常简单的例子:
ini 复制代码
text = "..." # your text
docs = text.split(".")
  • NLTK:自然语言工具包 (NLTK) 是一个用于处理人类语言数据的流行 Python 库。它提供了一个句子标记器,可以将文本拆分成句子,帮助创建更有意义的块。例如,要将 NLTK 与 LangChain 一起使用,您可以执行以下操作:
ini 复制代码
text = "..." # your text
from langchain.text_splitter import NLTKTextSplitter
text_splitter = NLTKTextSplitter()
docs = text_splitter.split_text(text)
  • spaCy:spaCy 是另一个用于 NLP 任务的强大 Python 库。它提供了复杂的句子分割功能,可以有效地将文本分成单独的句子,从而在生成的块中更好地保留上下文。例如,要将 spaCy 与 LangChain 一起使用,您可以执行以下操作:
ini 复制代码
text = "..." # your text
from langchain.text_splitter import SpacyTextSplitter
text_splitter = SpaCyTextSplitter()
docs = text_splitter.split_text(text)

递归分块

递归分块使用一组分隔符以分层和迭代的方式将输入文本划分为较小的块。如果首次尝试拆分文本未产生所需大小或结构的块,则该方法将使用不同的分隔符或标准对生成的块进行递归调用,直到达到所需的块大小或结构。这意味着虽然块的大小不会完全相同,但它们仍会"渴望"具有相似的大小。

以下是如何使用LangChain进行递归分块的示例:

ini 复制代码
text = "..." # your text
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
    # Set a really small chunk size, just to show.
    chunk_size = 256,
    chunk_overlap  = 20
)

docs = text_splitter.create_documents([text])

专门的分块

Markdown 和 LaTeX 是您可能会遇到的结构化和格式化内容的两个示例。在这些情况下,您可以使用专门的分块方法在分块过程中保留内容的原始结构。

  • Markdown:Markdown 是一种轻量级标记语言,常用于格式化文本。通过识别 Markdown 语法(例如标题、列表和代码块),您可以根据内容的结构和层次结构智能地划分内容,从而生成语义上更连贯的块。例如:
ini 复制代码
from langchain.text_splitter import MarkdownTextSplitter
markdown_text = "..."

markdown_splitter = MarkdownTextSplitter(chunk_size=100, chunk_overlap=0)
docs = markdown_splitter.create_documents([markdown_text])
  • LaTex:LaTeX 是一种文档准备系统和标记语言,常用于学术论文和技术文档。通过解析 LaTeX 命令和环境,您可以创建符合内容逻辑组织的区块(例如章节、小节和方程式),从而获得更准确、更符合上下文的结果。例如:
ini 复制代码
from langchain.text_splitter import LatexTextSplitter
latex_text = "..."
latex_splitter = LatexTextSplitter(chunk_size=100, chunk_overlap=0)
docs = latex_splitter.create_documents([latex_text])

语义分块

全局分块大小可能是一种过于简单的机制,无法考虑文档中各个段的含义。如果我们使用这种机制,我们就无法知道我们是否在组合彼此相关的段。

幸运的是,如果您使用 LLM 构建应用程序,您很可能已经具备创建嵌入的能力并且嵌入可用于提取数据中存在的语义含义。这种语义分析可用于创建由讨论同一主题或话题的句子组成的块。

以下是语义分块工作的步骤:

  1. 将文档分成句子。
  2. 创建句子组:为每个句子创建一个包含给定句子前后一些句子的组。该组本质上是由创建它的句子"锚定"的。您可以决定每个组中包含的前后具体数字 - 但组中的所有句子都将与一个 "锚定"句子相关联。
  3. 为每个句组生成嵌入,并将它们与其"锚"句关联起来。
  4. 按顺序比较每个组之间的距离:当您按顺序查看文档中的句子时,只要主题或主题相同 - 给定句子的句子组嵌入与其之前的句子组之间的距离就会很低。另一方面,更高的语义距离表示主题或主题已发生变化。这可以有效地将一个块与下一个块区分开来。

下一篇将讲述如何根据语义进行更精准的分块

确定应用程序的最佳块大小

如果常见的分块方法(例如固定分块)不容易适用于您的用例,这里有一些指针可以帮助您提出最佳分块大小。

  • 预处理数据- 在确定应用程序的最佳数据块大小之前,您需要先预处理数据以确保质量。例如,如果您的数据是从 Web 检索的,则可能需要删除只会增加噪音的 HTML 标签或特定元素。
  • 选择一系列块大小- 预处理完数据后,下一步是选择一系列潜在的块大小进行测试。如前所述,选择应考虑内容的性质(例如,短消息或长文档)、您将使用的嵌入模型及其功能(例如,标记限制)。目标是在保留上下文和保持准确性之间找到平衡。首先探索各种块大小,包括用于捕获更细粒度语义信息的较小块(例如,128 或 256 个标记)和用于保留更多上下文的较大块(例如,512 或 1024 个标记)。
  • 评估每个块大小的性能 - 为了测试各种块大小,您可以使用多个索引或具有多个命名空间的单个索引。使用代表性数据集,为要测试的块大小创建嵌入并将其保存在您的索引(或索引)中。然后,您可以运行一系列可以评估质量的查询,并比较各种块大小的性能。这很可能是一个迭代过程,您可以针对不同的查询测试不同的块大小,直到您可以确定内容和预期查询的最佳块大小。

结论

在大多数情况下,对内容进行分块非常简单 - 但当您开始偏离常规时,它可能会带来一些挑战。分块没有一刀切的解决方案,因此对一种用例有效的方法可能对另一种用例无效。希望这篇文章能帮助您更好地了解如何为您的应用程序进行分块。下一篇将讲述如何根据语义进行更精准的分块

相关推荐
机智的叉烧9 分钟前
前沿重器[57] | sigir24:大模型推荐系统的文本ID对齐学习
人工智能·学习·机器学习
凳子花❀12 分钟前
强化学习与深度学习以及相关芯片之间的区别
人工智能·深度学习·神经网络·ai·强化学习
泰迪智能科技012 小时前
高校深度学习视觉应用平台产品介绍
人工智能·深度学习
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
Eric.Lee20213 小时前
Paddle OCR 中英文检测识别 - python 实现
人工智能·opencv·计算机视觉·ocr检测
cd_farsight3 小时前
nlp初学者怎么入门?需要学习哪些?
人工智能·自然语言处理
AI明说3 小时前
评估大语言模型在药物基因组学问答任务中的表现:PGxQA
人工智能·语言模型·自然语言处理·数智药师·数智药学
Focus_Liu3 小时前
NLP-UIE(Universal Information Extraction)
人工智能·自然语言处理
PowerBI学谦3 小时前
使用copilot轻松将电子邮件转为高效会议
人工智能·copilot