RAG文档分块(Chunk)的策略和检索召回优化阶段重排序方法

RAG可以总结成如下步骤,本次我们探讨文档分块(Chunk)的策略,以及检索召回优化阶段重排序方法。

  1. RAG分块Chuck的策略

  1. 固定大小分块:这是最常见的分块方法,通过设定块的大小和是否有重叠来决定分块。这种方法简单直接,不需要使用任何NLP库,因此计算成本低且易于使用。

  2. 基于结构的分块:常见的HTML、MARKDOWN格式,或者其他可以有明确结构格式的文档。这种可以借助“结构感知”对文档分块,充分利用文档文本意外的信息。

  3. 基于语义的分块:这种策略旨在确保每个分块包含尽可能多的语义独立信息。可以采用不同的方法,如标点符号、自然段落、或者NLTK、Spicy等工具包来实现语义分块,或者Embedding-based方法。

  4. 递归分块:递归分块使用一组分隔符,以分层和迭代的方式将输入文本划分为更小的块。如果最初分割文本没有产生所需大小或结构的块,则该方法会继续递归地分割直到满足条件。 这些策略各有优势和适用场景,选择合适的分块策略取决于具体的应用需求和数据特性。很遗憾,到目前为止还没有什么是最优的策略,但这也是很难有一个产品一统天下的原因。同时策略可以组合使用,并不是一类文档只能用一种策略。

1.1. 固定大小分块

固定大小分块将文本划分为包含固定数量token的块。通常,为了保持语义上下文的一致性和连贯性,会在不同的块之间设置一定程度的重叠。这种方法简单易用,且不需要消耗大量计算资源。

此外,固定大小分块还可以通过使用特定的工具或库来辅助实现,例如在LangChain中,可以使用CharacterTextSplitter工具来切分文档,将其分成小块,.from_tiktoken_encoder()方法将编码作为参数(例如cl100k_base)或模型名称(例如gpt-4)。所有其他参数,如chunk_size分块大小、chunk_overlap重叠大小和分隔符。

text_splitter = CharacterTextSplitter.from_tiktoken_encoder (encoding="cl100k_base", chunk_size=100, chunk_overlap=0 ) texts = text_splitter.split_text(state_of_the_union)

1.2 基于结构的分块

除了普通文本文件之外的其他文档类型。比如图片、PDF、代码片段等等。前两个方法对于这些情况并不适用,所以需要找到不同的策略。RAG模型需要对输入的Markdown或HTML文本进行解析和转换,以便于后续的处理。

例如,LangChain提供的MarkdownHeaderTextSplitter,HTMLHeaderTextSpliter是一个“结构感知”的分块器,在元素级别拆分文本,并为与任何给定块“相关”的每个标头添加元数据。它可以逐元素返回块,或者将元素与相同的元数据组合,目的是

(a)保持相关文本在语义上分组

(b)保留文档结构中编码的上下文丰富的信息。

1.3 基于递归的分块

递归的分块是文档根据段落分隔符、新行、空格和单个字符等分隔符的层级进行分割。

递归分块在处理长文本时的优势主要包括以下几点:

  1. 灵活性和适应性:递归分块通过使用一组分隔符以层级和迭代的方式将输入文本划分为更小的块,这种方法可以根据文本的内容和结构动态调整分块的大小和形状,从而更好地适应不同类型的文本数据。

  2. 信息流动性:由于采用了递归机制,递归分块能够使信息在不同的片段之间流动,这有助于模型捕捉到跨越多个片段的上下文关系,从而提高模型对文本的理解能力。

但递归分块可能会导致计算过程变得更加复杂,因为它需要不断地评估和调整分隔符,以达到所需的分块效果。这可能会增加处理时间,尤其是在处理大规模文本数据时。

LangChain的文本递归分块实现为递归拆分器RecursiveCharacterTextSplitter

 

1.4 基于语义的分块

基于语义的分块方法是用嵌入表示字符串的语义含义。当两个句子的嵌入表示差异超过某个阈值后,就会被分割。利用这个特性,使用嵌入来找到语义上相似的文本聚类。假设是语义上相似的块应该放在一起。

方法包括

  • NLTKTextSplitter:NLTK(The Natural Language Toolkit)是一套用Python编程语言编写的用于英语符号和统计自然语言处理(NLP)的库和程序。SpacyTextSplitter:spaCy是一个用于高级自然语言处理的开源软件库,使用Python和Cython编程语言编写。SentenceTransformersTokenTextSplitter:用于句子转换器模型的文本拆分器。默认行为是将文本拆分为适合您想要使用的句子转换模型的标记窗口的块。

  • Embedding-based方法,在这种方法中,首先需要将用户查询转换为嵌入向量,以保持语义信息。这通常通过使用预训练的词嵌入模型(如Word2Vec或BERT)来完成。这些模型能够捕捉到文本中的深层语义关系。接下来,将这些嵌入向量与数据库中的内容嵌入向量进行比较,计算它们之间的相似度,从而检索出最相关的内容。分块处理可以通过将文本分割成适当大小的小块来实现,这些小块既不丢失原有的含义,也便于处理和存储。

LangChain的SemanticChunker的工作原理是决定何时“分解”句子。这是通过寻找任何两个句子之间嵌入的差异来完成的。当这个差异超过某个阈值时,它们就会被分割。

from langchain_experimental.text_splitter import SemanticChunker from langchain_openai.embeddings import OpenAIEmbeddings text_splitter = SemanticChunker(OpenAIEmbeddings())docs = text_splitter.create_documents([state_of_the_union], breakpoint_threshold_type="percentile") print(docs[0].page_content)
  1. 排序

混合检索能够结合不同检索技术的优势,以获得更好的召回结果。然而,在不同检索模式下的查询结果需要进行合并和归一化(将数据转换为统一的标准范围或分布,以便更好地进行比较、分析和处理),然后再一并提供给大模型。在这个过程中,我们需要引入一个评分系统:重排序模型(Rerank Model)。

重排方法主要有以下两种类型

  • 基于重排模型:这些模型可以输出文档与查询之间的相关性

  • 基于 LLM:由于大模型可以更全面地捕捉语义信息,也可被用于重排序

2.1 基于重排模型

目前,最佳的重排模型是 Cohere,但它是一项付费服务。开源的 bge-reranker-large 模型具有与 Cohere 相似的能力。

2.2 利用大模型进行重排

Prompt 的方式引导 LLM 进行重排序,如图。

(a) Query generation 这种方法让 LLM 根据文档内容,使用其通过计算和学习得到的对数概率值来生成与该段落相关的 query 。

(b) Relevance generation 让 LLM 评估给定的文档段落与 query 之间的相关性,并输出相关性程度。

(c) Permutation generation 会对一组文档段落进行重排列,并生成一个按照相关性排名的文档段落列表,以便确定哪些文档段落最适合给定的 query。

当候选文档的数量非常大,而 LLM 可能无法一次性处理所有的文本数据。因此,通常无法一次性输入所有文本。则使用滑动窗口的方法。

使用滑动窗口对这 8 个段落进行重排序的示意图,滑动窗口大小为 4,步长为 2。 蓝色框代表前两个窗口,黄色框代表最后一个窗口。滑动窗口的应用顺序是从后向前的,这说明前一个窗口中的前两个段落将参与下一个窗口的重排序。source:https://arxiv.org/pdf/2304.09542.pdf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值