RAG 系统中文本分割(Chunking)的原理与最佳实践
本文深入探讨了 RAG 系统中文本分割(Chunking)的原理与作用。文章分析了为何需要将大文本分解为小段,对比了句子级与段落级嵌入的差异,详细阐述了定长分块、内容感知分块、递归分块及格式分块等方法及其适用场景。此外,还讨论了预处理、块大小选择、重叠设置及性能评估等最佳实践,旨在帮助开发者优化检索效果,提升大模型应用的整体性能。

本文深入探讨了 RAG 系统中文本分割(Chunking)的原理与作用。文章分析了为何需要将大文本分解为小段,对比了句子级与段落级嵌入的差异,详细阐述了定长分块、内容感知分块、递归分块及格式分块等方法及其适用场景。此外,还讨论了预处理、块大小选择、重叠设置及性能评估等最佳实践,旨在帮助开发者优化检索效果,提升大模型应用的整体性能。

在构建基于大语言模型(LLM)的应用程序,尤其是检索增强生成(RAG)系统时,Chunking(文本分块)是将大量非结构化文本分解为较小、可管理片段的关键过程。这是优化从向量数据库中获得的 LLM 上下文嵌入内容相关性的核心环节。本文将深入探讨 Chunking 如何影响 RAG 应用的效率与准确性,并分析不同策略的权衡。
在 RAG 架构中,我们在向量数据库(如 Pinecone、Milvus 等)中索引的任何内容都需要先经过 Embedding(嵌入)处理。Chunking 的主要目的是在对上下文片段进行嵌入的过程中,尽可能减少噪声,同时保证内容仍然具有语义相关性。
在语义搜索场景中,我们为一个文档集建立索引,每个文档都包含有关特定主题的宝贵信息。通过采用有效的分块策略,可以确保搜索结果准确匹配用户查询的本意。
一般来说,如果一段文字脱离语境的上下文,对于人类来说仍然可以理解其含义,那么这段文字对于语言模型来说也是可以理解的。因此,确定语料库中文档的最佳块大小对提高搜索结果准确性和相关性是非常重要的。
在构建会话智能体(Conversational Agents)时,我们需要使用知识库中嵌入的文本段来构建让智能体使用的知识上下文。这种情况下,必须对这些文本段作出正确的选择,原因有二:
当嵌入内容时,可以根据内容是简短(例如句子)还是长(例如文本段或整个文档)来设计不同的 Embedding 行为。理解这种差异对于选择合适的 Chunking 策略至关重要。
当一个句子被嵌入时,得到的向量会聚焦于该句子的特定含义。当用其他句子的嵌入进行搜索时,通常在这一层面上进行。这也意味着该嵌入可能会遗漏在文本段或文件中会找到的更宽泛的上下文信息。这种方法适合需要精确匹配具体事实或定义的场景。
当一个完整的段落或文档被嵌入时,嵌入过程要关注整体的上下文以及文本内部句子和短语之间的关系。这可能会产生一个更全面的向量表示,向量会表示出文本更广泛的含义和主题。另一方面,较大的输入文本尺寸可能会引入噪声、淡化单个句子或短语的重要性,使得在查询索引时找到精确匹配更困难。
查询的长度也影响着嵌入向量之间的关系。像一个单独的句子或短语这类较短的查询,会专注于具体内容,可能更适合与句子级别的嵌入进行匹配。一个跨越多个句子或一个段落的较长查询可能与段落或文档级别的嵌入更匹配,因为它可能在寻找更广泛的上下文或主题。
索引也可能是非均质的,并包含不同大小块的嵌入。这可能对查询结果的相关性提出挑战,但也可能有一些积极的影响。一方面,由于长短内容的语义表示之间的差异,查询结果的相关性可能会波动。另一方面,非均质索引可能能够更广泛地捕获上下文和信息,因为不同的块大小代表文本中不同级别的粒度。这可能更灵活地适应不同类型的查询,但会增加评估和优化的复杂度。
确定最佳分块策略有几个关键变量,这些变量因使用场景而异。以下是一些需要注意的核心问题:
text-embedding-ada-002 这样的通用模型在包含 256 或 512 个标记的块上表现更优。了解模型的训练数据分布有助于设定初始参数。以下是几种常见的 Chunking 方法,每种方法都有不同的适用场景。了解每种方法的优势和劣势,目标是正确的场景下使用正确的 Chunking 方法。
这是最常见,也是最直接的分块方法:简单地决定我们的块中的字符数或标记数,并选择性地决定它们之间是否应该有任何重叠。一般来说,块之间应该保持一些重叠(Overlap),以确保语义上下文不会在块之间丢失。在大多数常见情况下,固定大小的分块将是最佳路径。与其他形式的分块相比,固定大小的分块的计算代价较低且使用成本不高,因为它不需要使用任何复杂的 NLP 库。
from langchain.text_splitter import CharacterTextSplitter
text = "这是一个示例文本,用于演示固定大小的分块方法。" * 100
text_splitter = CharacterTextSplitter(
separator="\n\n", # 分隔符,优先按此分割
chunk_size=256, # 块大小,单位通常是字符或 token
chunk_overlap=20 # 重叠部分,防止语义断裂
)
docs = text_splitter.create_documents([text])
这是一系列利用我们正在分块的内容性质并对其应用更复杂分块的方法。以下是一些例子:
之前提到过,许多模型都被优化过,用于嵌入句子级内容。当然,我们会使用句子分块,并且有几种方法和工具可以达成目的。
text = "..." # your text
docs = text.split(".")
from langchain.text_splitter import NLTKTextSplitter
text = "..." # your text
text_splitter = NLTKTextSplitter()
docs = text_splitter.split_text(text)
from langchain.text_splitter import SpacyTextSplitter
text = "..." # your text
text_splitter = SpacyTextSplitter()
docs = text_splitter.split_text(text)
Recursive Chunking 使用分层、迭代的概念形成一组分割器,用分割器将文本分割为较小的块。如果当前文本拆分出的段大小不满足需求大小,那么会递归地在带有不同的分离器或标准的结果块上继续调用,直到达到所需的块大小或结构为止。这意味着,尽管块的尺寸并不完全相同,但它们仍然与'预期'的尺寸相似。这种方法通常能更好地保留文档的结构层次。
下面是 LangChain 中的例子:
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = "..." # your text
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=256,
chunk_overlap=20,
separators=["\n\n", "\n", ".", " ", ""]
)
docs = text_splitter.create_documents([text])
Markdown 和 LaTeX 是经常遇到的两个结构化、格式化内容的示例。在这中情况下,可以使用专门的 Chunking 方法来获取、分割内容,以尊重文档的逻辑结构。
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])
from langchain.text_splitter import LatexTextSplitter
latex_text = "..." # your latex content
latex_splitter = LatexTextSplitter(chunk_size=100, chunk_overlap=0)
docs = latex_splitter.create_documents([latex_text])
有一些经验法则可以帮助我们明确最佳的块尺寸。如果固定的块(例如固定字符数)不容易应用于您的用例,请使用最佳的块大小调整策略。
在确定最佳的块大小之前,先预处理数据,保证数据质量。举个例子,如果数据是从网络上获取的,你可能需要删除 HTML 标签或其他特定元素标签,去掉所有噪音。清洗后的数据能显著提高嵌入向量的质量。
预处理数据后,下一步就是选择一些尺寸进行测试。如前所述,尺寸的选择需要考虑内容的性质(例如,短消息或冗长的文档)、LLM 的限制(例如令牌限制)。目的是在保留环境和保持准确性之间找到平衡。首先探索各种尺寸,包括较小的块(例如 128 或 256 Token),以捕获更多的粒状语义信息和较大的块(例如 512 或 1024 代币),以保留更多上下文。
为了测试各种块大小,你可以使用多个索引或单个索引配合不同的查询集合。使用代表性数据集,为你想要测试的块大小创建嵌入并保存到索引中。然后运行一系列查询,评估质量,并比较各种块大小的性能。这很可能是一个迭代过程,其中针对不同的查询测试不同的块大小,直到你能确定最适合你的内容和预期查询的最佳块大小。
重叠是 Chunking 中最容易被忽视但至关重要的参数。设置适当的 chunk_overlap 可以确保跨块边界的语义信息不被切断。例如,如果一个句子被截断在两个块之间,第一个块缺少后半句,第二个块缺少前半句,都会导致语义不完整。通常建议重叠大小为块大小的 10% 到 20%。
除了基于向量的语义检索,还可以结合关键词检索(BM25)。在某些情况下,Chunking 策略需要兼顾这两种方式。例如,对于包含专有名词或特定术语的文档,较细粒度的分块有助于关键词匹配,而较粗粒度的分块有助于语义理解。混合检索可以结合两者的优势。
对于长度差异巨大的文档集合,静态分块可能不是最优解。可以考虑动态分块策略,即根据文档的实际长度和内容密度自动调整块大小。例如,对于代码文件,可以按函数或类进行分块;对于小说,可以按章节分块。
文本分割(Chunking)是 RAG 系统中连接原始数据与向量数据库的桥梁。选择合适的 Chunking 策略直接影响检索的准确性和生成回答的质量。没有一种通用的最佳策略适用于所有场景,开发者需要根据具体的数据类型、查询模式以及模型限制进行实验和优化。通过理解不同分块方法的原理,并结合预处理、重叠设置及评估反馈,可以构建出高效、精准的 RAG 应用。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online