跳到主要内容LLM 大文本分块技术详解与最佳实践 | 极客日志PythonAI算法
LLM 大文本分块技术详解与最佳实践
大文本分块是构建 LLM 应用的关键步骤,旨在将长文档分解为语义连贯的片段以优化向量检索效果。本文详细阐述了分块的核心原理,对比了固定大小、递归、语义等多种分块方法的优缺点及适用场景。通过介绍基于 LangChain 的代码实现示例,指导开发者根据内容性质、嵌入模型特性及查询复杂度选择合适的分块策略。此外,文章还探讨了如何评估不同块大小的性能,并提供了预处理数据和迭代优化的具体建议,帮助开发者在上下文窗口限制与检索准确性之间找到平衡,提升 RAG 系统的整体表现。
追风少年0 浏览 在构建基于大语言模型(LLM)的应用程序背景下,**分块(Chunking)**是将大段文本分解成较小片段的过程。这是一项必不可少的技术,有助于优化我们使用 LLM 嵌入内容后从矢量数据库获取的内容的相关性。在这篇文章中,我们将探讨它是否以及如何有助于提高 LLM 相关应用程序的效率和准确性。
分块的主要原因是为了确保我们嵌入的内容尽可能少地包含噪音,同时仍然具有语义相关性。
例如,在语义搜索中,我们会对文档语料库进行索引,每个文档都包含有关特定主题的宝贵信息。通过应用有效的分块策略,我们可以确保搜索结果准确捕捉用户查询的本质。如果我们的分块太小或太大,可能会导致搜索结果不准确或错失显示相关内容的机会。根据经验,如果文本块在没有周围上下文的情况下对人类有意义,那么对语言模型来说也是如此。因此,找到语料库中文档的最佳分块大小对于确保搜索结果准确且相关至关重要。
另一个例子是对话代理(Agent)。我们使用嵌入的块基于知识库为对话代理构建上下文,该知识库为代理提供可信信息。在这种情况下,正确选择分块策略很重要,原因有二:首先,它将确定上下文是否与我们的提示真正相关。其次,考虑到我们可以为每个请求发送的令牌数量有限,它将确定我们是否能够在将检索到的文本发送给外部模型提供商(例如 OpenAI)之前将其放入上下文中。在某些情况下,例如当使用带有 32k 上下文窗口的 GPT-4 时,拟合块可能不是问题。不过,我们需要注意何时使用非常大的块,因为这可能会对我们从知识库中获得的结果的相关性产生不利影响。
在本文中,我们将探讨几种分块方法,并讨论在选择分块大小和方法时应考虑的权衡因素。最后,我们将提供一些建议,以确定最适合您的应用程序的分块大小和方法。
嵌入短内容和长内容
当我们嵌入内容时,我们可以根据内容是短(如句子)还是长(如段落或整个文档)预测不同的行为。
句子级嵌入
当句子被嵌入时,生成的向量会关注句子的具体含义。与其他句子嵌入相比,比较自然会在这个层面上进行。这也意味着嵌入可能会错过段落或文档中更广泛的上下文信息。这对于需要精确匹配关键词或短语的场景非常有效,但在处理复杂推理任务时可能显得不足。
段落或文档级嵌入
嵌入完整段落或文档时,嵌入过程会考虑整体上下文以及文本中句子和短语之间的关系。这可以产生更全面的矢量表示,以捕捉文本的更广泛含义和主题。另一方面,较大的输入文本大小可能会引入噪音或削弱单个句子或短语的重要性,使得在查询索引时找到精确匹配变得更加困难。
查询长度的影响
查询的长度也会影响嵌入之间的关系。较短的查询(例如单个句子或短语)将集中于具体内容,可能更适合与句子级嵌入进行匹配。较长的查询(跨越多个句子或段落)可能更适合段落或文档级的嵌入,因为它可能会寻找更广泛的上下文或主题。
非同质索引
索引也可能是非同质的,包含不同大小块的嵌入。这可能会对查询结果相关性造成挑战,但也可能带来一些积极的影响。一方面,由于长内容和短内容的语义表示存在差异,查询结果的相关性可能会波动。另一方面,非同质索引可能会捕获更广泛的上下文和信息,因为不同的块大小代表文本中不同级别的粒度。这可以更灵活地适应不同类型的查询。
分块注意事项
有几个变量在确定最佳分块策略时发挥作用,这些变量根据用例而变化。以下是需要记住的一些关键方面:
- 被索引的内容的性质是什么? 您处理的是长文档(例如文章或书籍),还是短内容(例如推文或即时消息)?答案将决定哪种模型更适合您的目标,以及应应用哪种分块策略。
- 您使用的是哪种嵌入模型?它在哪些块大小上表现最佳? 例如,Sentence Transformers 模型在单个句子上效果很好,但像 BERT 这样的模型在包含 256 或 512 个标记的块上表现更好。查阅所选模型的官方文档通常能提供最佳实践建议。
- 您对用户查询的长度和复杂度有何期望? 查询内容是简短而具体,还是冗长而复杂?这也可能影响您选择对内容进行分块的方式,以便嵌入查询和嵌入块之间有更紧密的关联。
- 检索到的结果将如何在您的特定应用程序中使用? 例如,它们将用于语义搜索、问答、摘要还是其他目的?例如,如果您的结果需要输入到另一个具有令牌限制的 LLM,您必须考虑到这一点,并根据您希望在对 LLM 的请求中容纳的块数来限制块的大小。
回答这些问题将使您能够制定一个平衡性能和准确性的分块策略,这反过来将确保查询结果更加相关。
分块方法
组块划分的方法有很多种,每种方法可能适用于不同的情况。通过研究每种方法的优缺点,我们的目标是确定适合应用它们的场景。
固定大小分块
这是最常见、最直接的分块方法:我们只需决定块中的标记数量,以及(可选)它们之间是否应该有重叠。一般来说,我们希望在块之间保留一些重叠,以确保语义上下文不会在块之间丢失。在大多数情况下,固定大小的分块将是最佳路径。与其他形式的分块相比,固定大小的分块在计算上便宜且易于使用,因为它不需要使用任何 NLP 库。
以下是使用 LangChain 执行固定大小分块的示例:
from langchain.text_splitter import CharacterTextSplitter
text = "这是一个示例文本,用于演示固定大小分块的效果。我们需要确保文本被正确地分割成合适的块。"
text_splitter = CharacterTextSplitter(
separator="\n\n",
chunk_size=256,
chunk_overlap=20
)
docs = text_splitter.create_documents([text])
'内容感知'分块
这些方法利用了我们正在分块的内容的性质,并对其应用了更复杂的分块逻辑。
句子拆分
正如我们之前提到的,许多模型都针对嵌入句子级内容进行了优化。自然,我们会使用句子分块,有几种方法和工具可以做到这一点:
- 简单拆分: 最简单的方法是用句号('。')和换行符拆分句子。虽然这可能既快速又简单,但这种方法不会考虑所有可能的边缘情况。这是一个非常简单的例子:
text = "这是一个句子。这是另一个句子。"
docs = text.split(".")
- NLTK: 自然语言工具包 (NLTK) 是一个用于处理人类语言数据的流行 Python 库。它提供了一个句子标记器,可以将文本拆分成句子,帮助创建更有意义的块。例如,要将 NLTK 与 LangChain 一起使用,您可以执行以下操作:
from langchain.text_splitter import NLTKTextSplitter
text_splitter = NLTKTextSplitter()
docs = text_splitter.split_text(text)
- spaCy: spaCy 是另一个用于 NLP 任务的强大 Python 库。它提供了复杂的句子分割功能,可以有效地将文本分成单独的句子,从而在生成的块中更好地保留上下文。例如,要将 spaCy 与 LangChain 一起使用,您可以执行以下操作:
from langchain.text_splitter import SpacyTextSplitter
text_splitter = SpacyTextSplitter()
docs = text_splitter.split_text(text)
递归分块
递归分块使用一组分隔符以分层和迭代的方式将输入文本划分为较小的块。如果首次尝试拆分文本未产生所需大小或结构的块,则该方法将使用不同的分隔符或标准对生成的块进行递归调用,直到达到所需的块大小或结构。这意味着虽然块的大小不会完全相同,但它们仍会尽量保持相似的大小。
以下是如何使用 LangChain 进行递归分块的示例:
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=256,
chunk_overlap=20,
separators=["\n\n", "\n", " ", ""]
)
docs = text_splitter.create_documents([text])
专门的分块
Markdown 和 LaTeX 是您可能会遇到的结构化和格式化内容的两个示例。在这些情况下,您可以使用专门的分块方法在分块过程中保留内容的原始结构。
- Markdown: Markdown 是一种轻量级标记语言,常用于格式化文本。通过识别 Markdown 语法(例如标题、列表和代码块),您可以根据内容的结构和层次结构智能地划分内容,从而生成语义上更连贯的块。
from langchain.text_splitter import MarkdownTextSplitter
markdown_text = "# 标题\n\n正文内容..."
markdown_splitter = MarkdownTextSplitter(chunk_size=100, chunk_overlap=0)
docs = markdown_splitter.create_documents([markdown_text])
- LaTeX: LaTeX 是一种文档准备系统和标记语言,常用于学术论文和技术文档。通过解析 LaTeX 命令和环境,您可以创建符合内容逻辑组织的区块(例如章节、小节和方程式),从而获得更准确、更符合上下文的结果。
from langchain.text_splitter import LatexTextSplitter
latex_text = "\\section{Introduction}\\n内容..."
latex_splitter = LatexTextSplitter(chunk_size=100, chunk_overlap=0)
docs = latex_splitter.create_documents([latex_text])
语义分块
全局分块大小可能是一种过于简单的机制,无法考虑文档中各个段的含义。如果我们使用这种机制,我们就无法知道我们是否在组合彼此相关的段。
幸运的是,如果您使用 LLM 构建应用程序,您很可能已经具备创建嵌入的能力并且嵌入可用于提取数据中存在的语义含义。这种语义分析可用于创建由讨论同一主题或话题的句子组成的块。
- 将文档分成句子。
- 创建句子组:为每个句子创建一个包含给定句子前后一些句子的组。该组本质上是由创建它的句子'锚定'的。您可以决定每个组中包含的前后具体数字 - 但组中的所有句子都将与一个'锚定'句子相关联。
- 为每个句组生成嵌入,并将它们与其'锚'句关联起来。
- 按顺序比较每个组之间的距离:当您按顺序查看文档中的句子时,只要主题或主题相同 - 给定句子的句子组嵌入与其之前的句子组之间的距离就会很低。另一方面,更高的语义距离表示主题或主题已发生变化。这可以有效地将一个块与下一个块区分开来。
确定应用程序的最佳块大小
如果常见的分块方法(例如固定分块)不容易适用于您的用例,这里有一些指针可以帮助您提出最佳分块大小。
预处理数据
在确定应用程序的最佳数据块大小之前,您需要先预处理数据以确保质量。例如,如果您的数据是从 Web 检索的,则可能需要删除只会增加噪音的 HTML 标签或特定元素。清理特殊字符和标准化空格也是必要的步骤。
选择一系列块大小
预处理完数据后,下一步是选择一系列潜在的块大小进行测试。如前所述,选择应考虑内容的性质(例如,短消息或长文档)、您将使用的嵌入模型及其功能(例如,标记限制)。目标是在保留上下文和保持准确性之间找到平衡。首先探索各种块大小,包括用于捕获更细粒度语义信息的较小块(例如,128 或 256 个标记)和用于保留更多上下文的较大块(例如,512 或 1024 个标记)。
评估每个块大小的性能
为了测试各种块大小,您可以使用多个索引或具有多个的单个索引。使用代表性数据集,为要测试的块大小创建嵌入并将其保存在您的索引中。然后,您可以运行一系列可以评估质量的查询,并比较各种块大小的性能。
- 召回率 (Recall@K): 衡量在检索到的 K 个结果中,有多少是真正相关的。
- 平均倒数排名 (MRR): 衡量第一个相关结果出现的位置。
- 准确率 (Precision@K): 衡量检索结果中相关结果的比例。
这很可能是一个迭代过程,您可以针对不同的查询测试不同的块大小,直到您可以确定内容和预期查询的最佳块大小。
常见陷阱与优化建议
- 过度重叠: 虽然重叠有助于保持上下文,但过多的重叠会导致存储成本增加和检索延迟上升。通常 10%-20% 的重叠率是合理的起点。
- 代码块截断: 在处理代码文件时,固定大小分块可能会切断函数或类定义。务必使用支持代码语法的分块器(如
CodeTextSplitter)。
- 元数据丢失: 分块后,请确保保留原始文档的元数据(如来源 URL、作者、日期),以便在检索后进行溯源。
结论
在大多数情况下,对内容进行分块非常简单 - 但当您开始偏离常规时,它可能会带来一些挑战。分块没有一刀切的解决方案,因此对一种用例有效的方法可能对另一种用例无效。选择合适的分块策略需要结合具体的业务场景、数据特征以及模型能力进行综合考量。
通过本文的介绍,希望您能更好地理解如何为您的应用程序进行分块。在实际应用中,建议采用 A/B 测试的方法,对比不同分块策略下的检索效果,持续优化系统性能。未来的研究方向还包括结合动态分块算法,根据查询意图实时调整分块粒度,以进一步提升 RAG(检索增强生成)系统的智能化水平。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online