AI知识库

53AI知识库

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


RAG优化攻略:最详细的RAG构建分块策略与实战代码案例
发布日期:2024-08-03 12:52:59 浏览次数: 2290 来源:AI技术研习社


在构建RAG相关的应用程序时,分块是一项关键技术。这一过程将大块文本分解为较小的段落,从而更容易管理和处理文本数据。分块技术对于优化内容嵌入和提高检索效率至关重要。当我们将内容嵌入到LLM中时,分块可以显著提升从向量数据库中返回内容的相关性。

在这篇博文中,我们将深入探讨分块技术如何在提高LLM效率和准确性方面发挥作用,以及其在实际应用程序中的具体应用和优势。通过分析和实验证明,我们将展示分块如何帮助开发者构建更智能和高效的LLM应用程序。

什么是分块?

在构建基于大语言模型(LLM)的应用程序时,如检索增强生成(RAG)系统,分块(chunking)是一项至关重要的技术。分块是将大块文本分解成较小段落的过程,这使得文本数据更易于管理和处理。通过分块,我们能够更高效地进行内容嵌入(embedding),并显著提升从向量数据库中召回内容的相关性和准确性。

在实际操作中,分块的好处是多方面的。首先,它能够提高模型处理的效率,因为较小的文本段落更容易进行嵌入和检索。

其次,分块后的文本能够更精确地匹配用户查询,从而提供更相关的搜索结果。这对于需要高精度信息检索和内容生成的应用程序尤为重要。

通过优化内容的分块和嵌入策略,我们可以最大化LLM在各种应用场景中的性能。分块技术不仅提高了内容召回的准确性,还提升了整体系统的响应速度和用户体验。

因此,在构建和优化基于LLM的应用程序时,理解和应用分块技术是不可或缺的步骤。

例如,在语义搜索中,我们对文档语料库进行索引,每个文档都包含有关特定主题的有价值的信息。通过应用有效的分块策略,我们可以确保我们的搜索结果准确捕捉用户查询的本质。

如果我们的块太小或太大,可能会导致搜索结果不准确或错过显示相关内容的机会。根据经验,如果文本块在没有周围上下文的情况下对人类有意义,那么它对语言模型也有意义。因此,为语料库中的文档找到最佳块大小对于确保搜索结果的准确性和相关性至关重要。

另一个例子是对话式代理(我们在使用 Python 和 Javascript 之前介绍过)。我们使用嵌入的块,根据知识库为对话代理构建上下文,该知识库使代理基于受信任的信息。

在这种情况下,对我们的分块策略做出正确的选择非常重要,原因有两个:首先,它将确定上下文是否真的与我们的提示相关。

其次,考虑到我们可以为每个请求发送的令牌数量有限制,它将确定我们是否能够在将检索到的文本发送到外部模型提供商(例如 OpenAI)之前将其放入上下文中。

在某些情况下,例如将 GPT-4 与 32k 上下文窗口一起使用时,拟合块可能不是问题。尽管如此,我们还是需要注意何时使用非常大的块,因为这可能会对我们从 Pinecone 获得的结果的相关性产生不利影响。

为什么分块很重要?

Pinecone公司的Roie Schwaber-Cohen指出:“开始思考如何将我的内容分成更小的块的原因是,这样当我检索时,它实际上能够命中正确的内容。你将用户的查询嵌入,然后将其与内容的嵌入进行比较。

如果你嵌入的内容大小与用户查询的大小差异很大,你就更可能得到较低的相似度得分。”这句话强调了分块的关键作用:通过合理的分块,可以确保用户查询与内容之间的相似度得分更高,从而提高搜索结果的相关性和准确性。

分块不仅优化了向量嵌入的效果,还提高了系统的整体性能。合理的分块策略能够在保留上下文信息的同时,提供足够细粒度的内容描述,从而实现高效、精准的检索。这对于任何依赖于大规模文本数据处理和信息检索的应用程序,尤其是那些需要高精度内容匹配和生成的场景,都是至关重要的。

因此,在开发基于LLM的应用程序时,理解并应用分块技术,能够显著提升模型的表现和用户体验。这不仅仅是一个技术优化的问题,更是提升系统智能和可靠性的关键步骤。

Embedding的时候需要注意什么?

在嵌入内容(即embedding)时,我们需要根据内容的长度来预测和调整不同的行为。内容的长度可以是短如句子,或者长如段落甚至整个文档。不同长度的内容在嵌入过程中会有不同的表现和效果。

当嵌入一个句子时,生成的向量会集中在句子的特定含义上。因此,当与其他句子的嵌入进行比较时,这些向量会在句子的层次上进行比较。这种方法有助于精确捕捉单个句子的意义,但可能会忽略在更大段落或整个文档中存在的更广泛的上下文信息。

当嵌入一个完整的段落或文档时,嵌入过程不仅要考虑整体上下文,还要考虑文本中各个句子和短语之间的关系。这种方法可以产生更全面的向量表示,捕捉文本的广泛含义和主题。然而,较大的输入文本也可能引入噪声或淡化单个句子或短语的重要性,从而在查询索引时更难找到精确匹配。

查询的长度也会影响嵌入之间的关系。较短的查询,如单个句子或短语,将专注于细节,可能更适合与句子级别的嵌入进行匹配。而跨越多个句子或段落的较长查询则可能更适合段落或文档级别的嵌入,因为它们会寻找更广泛的上下文或主题。

索引的同质性也是一个需要注意的问题。索引可能包含不同大小的块的嵌入,这种非同质性可能在查询结果相关性方面带来挑战,但也有潜在的好处。一方面,由于长内容和短内容的语义表示不一致,查询结果的相关性可能会波动。另一方面,非同构索引可以捕获更大范围的上下文和信息,因为不同块大小表示文本的不同粒度级别。这种多样性可以更灵活地容纳不同类型的查询。

总之,在进行内容嵌入时,需要综合考虑内容长度、上下文关系和查询匹配的层次,以确保嵌入的准确性和查询结果的相关性。合理的嵌入策略不仅能够提高模型的表现,还能够显著改善用户的搜索和交互体验。

分块需要考虑的因素有哪些?

在确定最佳分块策略时,有多个因素需要仔细考虑,这些因素将对最终的选择起到至关重要的影响。以下是一些需要记住的关键点:

首先,要考虑被索引内容的性质。这可能包括处理较长的文档,如文章或书籍,或较短的内容,如微博或即时消息。不同类型的内容需要不同的分块策略和模型。例如,长文档可能需要将内容分成较大的块以捕捉广泛的上下文,而短内容则可以分成较小的块以保持精确度和细节。

其次,嵌入模型的选择也至关重要。不同的嵌入模型在不同大小的块上表现最佳。例如,sentence-transformer模型在单个句子上效果很好,而像text-embed-ada-002这样的模型在包含256或512个tokens的块上表现更佳。因此,了解和选择适合的嵌入模型对分块策略的成功至关重要。

另外,还需要考虑用户查询的长度和复杂性。用户输入的问题是简短而具体,还是冗长而复杂?这将直接影响我们选择的分块方式,以确保查询和文本块之间的相关性。例如,短查询可能需要与句子级别的嵌入进行匹配,而长查询则更适合与段落或文档级别的嵌入进行比较。

此外,检索结果的使用方式也是一个关键因素。例如,检索结果是用于语义搜索、问答、摘要还是其他目的?这与底层连接的大语言模型(LLM)有直接关系。LLM的tokens限制可能会影响分块大小的选择。例如,如果LLM有严格的tokens限制,则需要确保分块后的内容不会超出这些限制。

没有一成不变的最佳分块策略,只有最适合当前应用和需求的策略。为了确保查询结果的准确性和相关性,有时需要灵活使用多种不同的分块策略。这种多样化的方法可以帮助捕捉不同类型的查询需求和内容特性,从而提供更精确和有效的检索结果。

分块的方法汇总

1. 固定大小分块

固定大小分块是一种最常见且最直接的分块方法。此方法中,我们需要决定每个块中包含的tokens数量,并确定这些块之间是否应该有重叠。通常,我们会在块之间保持一些重叠,以确保语义上下文在块之间不会丢失。这种方法的一个主要优势是,它在计算上更加经济且易于实现,因为在分块过程中无需依赖任何复杂的自然语言处理(NLP)库。

固定大小的分块方式在大多数情况下是最佳选择。其简单性和高效性使得这种方法在许多应用场景中得到了广泛应用。通过预先设定的块大小和适度的重叠,我们可以确保文本内容在被切分后仍然保持语义连贯性,从而提高后续处理和检索的准确性。

此外,固定大小分块的另一个优势在于其一致性。每个块具有相同的大小,使得后续处理步骤,如嵌入和检索,更加标准化和可预测。这种一致性不仅简化了实现过程,还提高了系统的整体稳定性和性能。

总之,固定大小分块是一种高效且实用的分块策略,特别适用于需要处理大量文本数据的应用场景。它不仅在计算资源方面具有优势,还能够有效地保持文本的语义完整性,为后续的文本嵌入和检索提供了坚实的基础。

text = "..." # your textfrom langchain.text_splitter import CharacterTextSplittertext_splitter = CharacterTextSplitter(separator = "\n\n",chunk_size = 256,chunk_overlap= 20)docs = text_splitter.create_documents([text])

2. 句子分割

句子分块是一种基于自然语言结构的分块方法,特别适用于需要保留文本精确语义和上下文连贯性的应用场景。在句子分块中,文本被分割成一个个独立的句子,每个句子作为一个分块单元。这种方法确保了每个分块都能完整地表达其含义,不会因为被截断而丧失语义信息。

句子分块的主要优势在于其高精度和上下文保持能力。由于每个分块都是一个完整的句子,因此在嵌入过程中,生成的向量能够准确地捕捉句子的具体含义。这使得句子分块特别适合用于需要高精度语义搜索、问答系统和精细语义分析的任务。

此外,句子分块在处理用户查询时也表现出色。短查询和具体问题可以直接与句子级别的嵌入进行匹配,从而提高检索结果的相关性和准确性。通过这种方法,系统能够更精准地响应用户的需求,提供更相关的结果。

然而,句子分块也有其局限性。在处理长文本或复杂文档时,仅依赖句子分块可能不足以捕捉文本中的更广泛上下文和主题关系。在这种情况下,句子分块可以与其他分块策略结合使用,以提供更全面和多层次的文本表示。

import spacy
# 加载spaCy的英文模型nlp = spacy.load("en_core_web_sm")
def sentence_chunking(text):"""将文本分割成句子:param text: 输入文本:return: 包含句子的列表"""doc = nlp(text)sentences = [sent.text for sent in doc.sents]return sentences
# 示例文本text = "Sentence chunking is a method used in natural language processing. It involves breaking down a text into individual sentences. This approach ensures that each chunk is a complete sentence, preserving the semantic integrity."
# 进行句子分块chunks = sentence_chunking(text)for i, chunk in enumerate(chunks):print(f"Chunk {i+1}: {chunk}")

在上述代码中,我们首先加载了spaCy的英文模型。sentence_chunking函数接收一个文本字符串,并使用spaCy将其解析为句子。每个句子被提取出来并存储在一个列表中,最后返回该列表。这种方法简单高效,能够确保每个分块都是完整的句子,从而为后续的文本处理和分析提供坚实的基础。

3. 递归分割

递归分块是一种高级文本分块方法,通过使用一组分隔符以分层和迭代的方式将输入文本分解成更小的块。该方法的核心思想是,如果在初始分割时未能生成所需大小或结构的块,则会使用不同的分隔符或标准对生成的块递归调用,直到获得所需的块大小或结构。虽然最终生成的块大小可能不完全相同,但它们会逼近所需的大小,从而保持一定的均匀性。

递归分块方法的主要优势在于其灵活性和适应性。它能够根据文本的实际内容和结构进行调整,逐步细化块的大小,直至达到最佳的分割效果。这种方法特别适合处理结构复杂或长度不一的文本,使得分块后的文本既能保持语义完整性,又能满足后续处理的需求。

递归分块方法的具体实现通常涉及以下几个步骤:

a. 初始分割:使用主要分隔符(如段落或章节)对文本进行初步分割。

b. 递归调用:如果初始分割未能生成满足条件的块,则使用次级分隔符(如句子或短语)对每个初始块进行进一步分割。

c. 迭代处理:重复上述过程,直到所有块均达到所需大小或结构。

import re
def recursive_chunking(text, max_chunk_size, delimiters):"""递归分块函数,根据一组分隔符将文本分割成接近所需大小的块:param text: 输入文本:param max_chunk_size: 每个块的最大字符数:param delimiters: 分隔符列表,从高到低层次排序:return: 分割后的文本块列表"""if len(text) <= max_chunk_size:return [text]
delimiter = delimiters[0]chunks = re.split(delimiter, text)if len(delimiters) == 1:# 如果没有更多分隔符,直接返回分割块return chunks
result_chunks = []for chunk in chunks:if len(chunk) > max_chunk_size:# 递归调用,使用下一个分隔符result_chunks.extend(recursive_chunking(chunk, max_chunk_size, delimiters[1:]))else:result_chunks.append(chunk)
return result_chunks
# 示例文本text = """Recursive chunking is an advanced method of text chunking. It involves using a set of delimiters to break down the input text into smaller chunks in a hierarchical and iterative manner. If the initial split does not produce chunks of the desired size or structure, this method will recursively apply different delimiters or criteria to the resulting chunks until the desired chunk size or structure is achieved. This means that while the chunk sizes are not exactly uniform, they approximate the desired size."""
# 分隔符列表,从高到低层次排序delimiters = [r'\.\s*', r'\n', r'\s']
# 最大块大小max_chunk_size = 100
# 进行递归分块chunks = recursive_chunking(text, max_chunk_size, delimiters)for i, chunk in enumerate(chunks):print(f"Chunk {i+1}: {chunk}\n")

这里有一个例子,如何配合LangChain使用递归分块:

text = "..." # your textfrom langchain.text_splitter import RecursiveCharacterTextSplittertext_splitter = RecursiveCharacterTextSplitter(# Set a really small chunk size, just to show.chunk_size = 256,chunk_overlap= 20)
docs = text_splitter.create_documents([text])

4. 专门分块

专门的分块是针对特定应用场景和文本结构的定制化分块方法。这种方法依据文本的内容和结构特征,采用特定的分块策略,以实现更高效、更精确的文本处理。专门的分块可以结合领域知识和具体需求,灵活调整分块方式,以优化文本的后续处理和分析。

这种分块方法的核心在于根据文本的特性选择合适的分隔符和分块策略。例如,在处理法律文档时,可能需要按条款或章节分块,以确保每个分块都能独立地表达特定的法律条款。而在处理技术文档时,则可能根据函数或代码段进行分块,以便于代码的理解和分析。

专门的分块方法通常涉及以下几个步骤:首先,识别文本中的关键结构或语义单元;然后,定义适合的分隔符或规则;最后,将文本按照这些规则进行分块。这样的定制化分块不仅可以提高文本的处理效率,还能确保在分析和检索时的高准确性。

import re
def specialized_chunking(text, delimiter):"""专门的分块函数,根据特定的分隔符将文本分割成块:param text: 输入文本:param delimiter: 分隔符,用于定义分块的边界:return: 分割后的文本块列表"""# 使用正则表达式根据特定分隔符分割文本chunks = re.split(delimiter, text)# 去除空块chunks = [chunk.strip() for chunk in chunks if chunk.strip()]return chunks
# 示例文本:技术文档中的代码块text = """# This is a code blockdef example_function():print("Hello, world!")
# Another code blockdef another_function():return True"""
# 定义特定的分隔符,例如注释标记delimiter = r'#\s*\w+'
# 进行专门分块chunks = specialized_chunking(text, delimiter)for i, chunk in enumerate(chunks):print(f"Chunk {i+1}:\n{chunk}\n")

在上述代码中,specialized_chunking函数根据特定的分隔符(例如代码注释标记)将文本分割成块。通过正则表达式匹配特定的分隔符,文本被分割为若干个代码块,每个块代表一个独立的代码段。这样可以确保文本按照预定义的结构进行分块,从而提高处理和分析的效率。

专门的分块方法能够根据具体需求和应用场景进行调整,提供高效的文本处理解决方案。通过这种定制化的方式,可以更好地适应文本的结构特征,优化后续的分析和检索过程。

5. 语义分块

语义分块是一种通过理解文本语义来进行分块的方法。这种方法不仅考虑文本的表面结构,还深入到文本的实际含义和上下文关系中,以生成具有语义连贯性的块。语义分块的核心在于确保每个生成的块都在语义上完整并能独立表达清晰的信息,从而优化信息检索和处理的效果。

在进行语义分块时,文本首先被分析以识别语义单元,如主题、概念或逻辑段落。然后,基于这些语义单元,文本被分割成若干个块,每个块在语义上是自洽的。这种方法特别适用于需要高精度语义理解的应用场景,如知识图谱构建、智能问答系统和内容推荐等。

以下是使用LangChain进行语义分块的Python示例代码。LangChain是一个用于构建语言模型应用程序的框架,可以方便地处理和分析文本数据。在这个示例中,我们将使用LangChain的TextSplitter类进行语义分块,以确保每个块在语义上都是连贯的。

from langchain.text_splitter import TextSplitterfrom langchain.llms import OpenAI
# 初始化语言模型llm = OpenAI(api_key="your-api-key")
# 初始化TextSplittertext_splitter = TextSplitter(llm=llm, chunk_size=1000, chunk_overlap=200)
# 示例文本text = """Semantic chunking involves breaking text into chunks based on its semantic content rather than just its structural elements. By understanding the meaning and context of the text, each chunk is created to be semantically coherent. This approach ensures that each chunk represents a complete idea or concept, which is crucial for applications requiring precise semantic analysis.
For example, in building knowledge graphs, semantic chunking helps to isolate distinct concepts and relationships. In question-answering systems, it allows for better matching between user queries and relevant content."""
# 执行语义分块chunks = text_splitter.split(text)for i, chunk in enumerate(chunks):    print(f"Chunk {i+1}:\n{chunk}\n")

在上述代码中,我们首先初始化了一个OpenAI语言模型实例,然后使用LangChain的TextSplitter类进行文本的语义分块。TextSplitter类允许我们设置每个块的最大大小和块之间的重叠部分,以确保生成的块在语义上是连贯的。split方法将文本分割成多个语义块,每个块都能够清晰地表达一个完整的概念或主题。

通过语义分块,文本的处理可以更加精确地符合实际的语义需求,特别是在复杂的应用场景中,能够显著提升信息检索的效果和内容的相关性。这种方法能够有效地将大块文本分解成具有语义连贯性的较小块,从而优化文本分析和处理的过程。

总结

分块需要综合考虑多种因素,以确保生成的块既能满足应用的具体需求,又能在计算资源和性能之间取得平衡。

首先,块大小应与应用程序的功能和目标密切相关。例如,对于需要细粒度语义分析的应用,如细节丰富的问答系统或深入的文本理解,较小的块大小可能更为合适。这种块大小能够提供更精确的上下文信息,避免丢失关键细节。然而,块过小可能会导致上下文断裂,使得语义理解受到影响。

其次,考虑到处理效率和存储要求,块大小也应与计算资源和性能需求相匹配。在资源有限的情况下,较小的块可以减少内存和处理时间的消耗,但也可能增加处理的复杂性。相对较大的块虽然在处理时可能更具效率,但需要更多的存储和计算资源,并且在文本分析时可能会引入更多的噪声。

应用场景的特性也会影响块大小的选择。例如,在处理法律文档或技术手册时,可能需要根据章节或条款进行分块,这样的块大小能够更好地保持文档结构的完整性。而在处理社交媒体内容或实时消息时,较小的块可能更适合,能够更灵活地适应内容的快速变化。

最终,确定最佳块大小的过程通常是一个迭代过程,需要根据实际使用情况进行调整。通过对不同块大小的实验和评估,可以找到最适合特定应用程序的块大小,从而优化文本处理的效果和效率。


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

产品:大模型应用平台+智能体定制开发+落地咨询服务

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

联系我们

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

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询