支持私有云部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


RAG如何选择最佳分块大小?

发布日期:2025-04-03 12:52:10 浏览次数: 1565 作者:三黄工作室
推荐语

探索如何通过最佳分块策略优化大语言模型应用,提高语义搜索和Chatbot的效率和准确性。

核心内容:
1. 分块在大语言模型中的作用与重要性
2. 分块策略对语义搜索和Chatbot的影响
3. 选择合适的分块大小和方法的权衡因素

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

今天和大家讲一下Chunking(分块) 在构建LLM(大语言模型)相关应用中的重要性。

如果你经常接触LLM相关的项目,这里提到的“分块”就显得尤为重要了。简单来说,分块就是把一大段文本拆分成更小的部分。这个过程对于优化从向量数据库中检索到的内容的相关性非常关键,尤其是在我们用LLM对内容进行嵌入时。

我们都知道现在任何内容被索引之前,都需要先进行嵌入处理。而分块的主要目的就是为了确保我们在嵌入的时候,能够尽量减少噪声,同时让语义保持相关性。

举个例子,在语义搜索中,我们需要索引大量文档,每篇文档都包含某个特定主题的重要信息。通过有效的分块策略,我们可以确保搜索结果能精准地捕捉用户的查询意图。如果分块太小或太大,就会导致搜索结果不够精确,甚至会错过一些重要的相关内容。

一般的经验法则是:如果一段文本在没有上下文的情况下对人类是有意义的,那么它对语言模型也会有意义。因此,找到适合文档集合的最佳分块大小,是确保搜索结果准确性和相关性的关键。

当然,分块的策略不仅仅适用于语义搜索,还广泛应用于对话AI(Chatbot)。在Chatbot中,嵌入后的分块内容会被用来构建基于知识库的上下文,从而让ChatBot有可靠的依据去生成回答。在这种场景下,选择正确的分块策略非常重要,主要有两个原因:

第一,这决定了上下文是否真的与我们的提问相关;

第二,这也决定了我们能否在将文本发送给外部模型提供者(例如OpenAI)之前,将其适配到有限的token窗口内。

在某些情况下,比如使用GPT-4这种支持32k上下文窗口的模型时,适应大块文本可能不是问题。但我们仍然需要谨慎对待过大块的情况,因为这可能会影响检索到的结果的相关性。

在这篇文章里,我们会探讨几种不同的分块方法,并讨论在选择分块大小和方法时需要考虑的权衡因素。最后,会给出一些建议,帮助你为自己的应用选择最佳的分块大小和方法。

 嵌入短内容和长内容的不同表现 

当我们对内容进行嵌入时,可以预见根据内容长短的不同,嵌入的表现也会有所区别。短内容(如句子)和长内容(如段落或整篇文章)的处理方式会有显著差异。

当一个句子被嵌入时,生成的向量主要聚焦于该句子的具体含义。比较也自然会在这样的粒度上进行。不过,这种方式可能会忽略掉段落或文档中更广泛的上下文信息。

相比之下,当整个段落或文档被嵌入时,嵌入过程会同时考虑整体上下文以及句子和短语之间的关系。这样得到的向量表示往往更加全面,能够捕捉到文本的宏观意义和主题。但另一方面,较大的输入文本可能会引入噪音,稀释个别句子或短语的重要性,从而使得在查询索引时难以找到精确匹配。

此外,查询的长度也会影响嵌入之间的关联性。较短的查询(如单句或短语)会更关注细节,因此更适合与句子级别的嵌入进行匹配。而较长的查询(如多句或多段)则可能更适合与段落或文档级别的嵌入进行匹配,因为它通常寻找的是更广义的上下文或主题。

索引也可能呈现非同质化的特点,即其中包含不同大小的分块嵌入。这种情况既带来挑战,也可能有一些积极的效果。一方面,由于长内容和短内容的语义表征存在差异,查询结果的相关性可能会波动。另一方面,非同质化的索引有可能捕捉到更广泛的上下文和信息,因为不同大小的分块代表了文本的不同粒度,这可以让系统更灵活地应对各种类型的查询。

 分块策略要考虑的因素 

有几个变量会影响最佳分块策略的选择,这些变量因应用场景而异。以下是一些需要注意的关键点:

  1. 要索引的内容性质是什么? 是处理长文档(如文章或书籍)还是短内容(如术语解释或聊天消息)?答案不仅决定了哪种模型最适合你的目标,也决定了应该采用什么样的分块策略。
  2. 使用的是哪种嵌入模型,它在哪些分块大小上表现最佳? 比如,sentence-transformer模型在处理单独句子时表现良好,而像text-embedding-ada-002这样的模型则在包含256或512个token的分块上效果更好。
  3. 用户查询的长度和复杂性预期如何? 查询是简短具体的,还是冗长复杂的?这也会影响你如何选择分块策略,以确保嵌入查询和嵌入分块之间有更好的相关性。
  4. 检索结果将在具体应用中如何使用? 例如,它们是用来做语义搜索、问答、摘要生成还是其他用途?如果结果需要传递给另一个有token限制的LLM,那必须考虑这一点,并根据你想放入请求中的分块数量来限制分块的大小。

回答这些问题将帮助你制定出一种既能平衡性能又能保证准确性的分块策略,从而使查询结果更加相关。

 分块方法 

分块的方法有很多种,每种方法在不同情况下各有优劣。通过分析每种方法的利弊,我们要能够找出最适用自己场景的方法。

固定大小分块

这是最常见的也是最直接的分块方法:我们只需决定每个分块包含的token数量,并且可以选择是否让它们之间有一定的重叠。一般来说,为了让语义上下文不会在分块之间丢失,我们会保留一些重叠部分。固定大小分块在大多数常见情况下都是最优路径。与其他形式的分块相比,这种方法计算成本低且易于使用,因为它不需要依赖任何NLP库。

下面是一个使用LangChain进行固定大小分块的例子:

text = "..." # 文本
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
  separator = "\n",
  chunk_size = 256,
  chunk_overlap = 20
)
docs = text_splitter.create_documents([text])

“内容感知”分块

这是一系列利用待分块内容特性并施加更复杂分块方法的策略。以下是几个例子:

句子分割

正如前面提到的,许多模型在嵌入句子级别内容时表现优异。自然地,我们会使用句子分块,而且有许多方法和工具可以实现这一点,包括:

  • 朴素分割: 最简单的办法是按句号(“.”)和换行符分割句子。虽然这种方法快速简单,但无法考虑到所有可能的边界情况。以下是一个非常简单的例子:
text = "..." # 文本
docs = text.split(".")
  • NLTK:自然语言工具包(NLTK)是一个流行的用于处理人类语言数据的Python库。它提供了句子分词器,可以帮助将文本分割成句子,从而创建更有意义的分块。例如,要结合LangChain使用NLTK,可以这样做:
text = "..." # 文本
from langchain.text_splitter import NLTKTextSplitter
text_splitter = NLTKTextSplitter()
docs = text_splitter.split_text(text)
  • spaCy:spaCy是另一个强大的Python NLP任务库。它提供了复杂的句子分割功能,可以高效地将文本划分为独立的句子,从而更好地保留上下文。例如,结合LangChain使用spaCy,可以这样做:
text = "..." # 文本
from langchain.text_splitter import SpacyTextSplitter
text_splitter = SpaCyTextSplitter()
docs = text_splitter.split_text(text)

递归分块

递归分块以分层和迭代的方式将输入文本划分成更小的块,使用一组分隔符。如果最初的尝试未能产生所需大小或结构的块,该方法会递归调用自身,直到达到所需的块大小或结构为止。这意味着尽管块的大小不一定完全相同,但它们仍然会“趋向”于相似的大小。

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

text = "..." # 文本
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
  # 设置一个非常小的块大小,只是为了展示。
  chunk_size = 256,
  chunk_overlap = 20
)
docs = text_splitter.create_documents([text])

专门分块

Markdown和LaTeX是两种常见的结构化和格式化内容的例子。在这些情况下,可以使用专门的分块方法来在分块过程中保留原始结构。

  • Markdown:Markdown是一种常用的轻量级标记语言,用于格式化文本。通过识别Markdown语法(例如标题、列表和代码块),可以智能地根据其结构和层次划分内容,从而生成更具语义一致性的块。例如:
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命令和环境,可以创建尊重内容逻辑组织的块(例如章节、小节和方程),从而获得更准确和上下文相关的结果。例如:
from langchain.text_splitter import LatexTextSplitter
latex_text = "..."
latex_splitter = LatexTextSplitter(chunk_size=100, chunk_overlap=0)
docs = latex_splitter.create_documents([latex_text])

语义分块

这是一种新的分块方法,由Greg Kamradt首次提出。在他的代码示例中,Kamradt指出,全局分块大小可能过于简单,无法考虑到文档内各段落的含义。如果我们使用这种机制,就无法知道我们是否在组合那些彼此无关的段落。

这是代码示例地址:https://github.com/FullStackRetrieval-com/RetrievalTutorials/blob/main/tutorials/LevelsOfTextSplitting/5_Levels_Of_Text_Splitting.ipynb

这种语义分析可以用来创建围绕同一主题或话题的句子组成的块。

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

  1. 将文档分解为句子。
  2. 创建句子组:对于每个句子,创建一个包含该句子前后若干句子的组。这个组本质上是由创建它的句子“锚定”的。可以决定在每个组中包含多少前后的句子——但组内的所有句子都将与一个“锚定”句子相关联。
  3. 为每个句子组生成嵌入,并将其与“锚定”句子相关联。
  4. 顺序比较每组之间的距离:依次查看文档中的句子时,只要主题或话题保持不变,当前句子组与其前一句子组的语义距离就会较低。相反,较高的语义距离表明主题或话题发生了变化。这可以有效地将一个块与下一个块区分开来。

LangChain基于Kamradt的工作创建了一个语义分块分割器

地址为:https://python.langchain.com/docs/modules/data_connection/document_transformers/semantic-chunker/

 找到适合应用的最佳分块大小 

以下建议,可以帮助你在常见的分块方法(如固定分块)不适用时,确定最佳的分块大小。

  • 预处理数据 - 在确定最佳分块大小之前,需要先对数据进行预处理以确保质量。例如,如果数据是从网络上获取的,可能需要移除HTML标签或其他仅增加噪音的元素。
  • 选择一系列分块大小 - 数据预处理完成后,下一步是选择一系列潜在的分块大小进行测试。如前所述,选择应考虑到内容的性质(例如短消息或长文档)、将使用的嵌入模型及其能力(例如token限制)。目标是在保持上下文的同时维持准确性。开始时可以探索多种分块大小,包括较小的块(例如128或256个token)以捕捉更细粒度的语义信息,以及较大的块(例如512或1024个token)以保留更多上下文。
  • 评估每个分块大小的性能 - 为了测试各种分块大小,可以使用多个索引。使用代表性数据集,为要测试的分块大小创建嵌入并保存在索引中。然后运行一系列查询来评估质量,并比较不同分块大小的性能。这是一个迭代过程,需要针对不同的查询测试不同的分块大小,直到能确定最适合内容和预期查询的最佳分块大小。

 结论 

分块没有一刀切的解决方案,所以适合一个用例的方法可能不适合另一个。希望这篇文章能帮助你更好地理解如何为你的应用选择合适的分块策略。

- END -

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询