深入探讨LLM和RAG系统中的分块技术

2024年09月06日 由 alex 发表 77 0

随着 GPT-3、GPT-4 和检索增强生成(RAG)系统等大型语言模型(LLM)的使用日益增多,有效处理文章、文档或代码等长篇内容成为一大挑战。LLM 通常有标记限制(如 GPT-3 中的 4096 个标记),从而限制了一次可处理的文本数量。为了解决这个问题,我们需要使用一种叫做分块的技术,将这些大型输入分解成较小的、可管理的片段。


本文将全面介绍分块技术在 LLM 和 RAG 中的应用,内容包括:

  1. 在 LLM 和 RAG 中什么是分块。
  2. 为什么分块在 LLM 和 RAG 系统中至关重要。
  3. LangChain 提供的分块方法。
  4. 实施分块的最佳实践。
  5. 详细示例和代码片段。


1. 什么是LLM和RAG背景下的分块?

LLM 中的分块是指将大型文档或文本分割成较小的片段,称为块。这一点很有必要,因为 LLM 有标记限制,这限制了它们一次能处理多少文本。例如,GPT-3 的标记限制为 4096,包括输入和输出标记。


在结合了检索模型(获取相关文本)和生成模型(生成响应)的 RAG 系统中,分块被用来确保大文档被分成可以被有效检索和处理的较小块。


2. 分块为何至关重要?

分块技术之所以重要,有以下几个原因:

  • 标记管理: 它确保输入文本不会超过 LLM 的标记限制。
  • RAG 中的检索准确性:较小的分块允许检索模型更准确地获取相关片段,从而获得更好的结果。
  • 语境保持: 有重叠的分块有助于保持分块之间的上下文,使 LLM 更容易生成连贯的响应。
  • 效率: 较小的结构化分块可加快处理和检索速度。


3. LangChain 中的分块方法

LangChain 提供多种文本分块工具,适用于从简单文本到复杂代码等各种类型的内容。以下是你可以在 LangChain 中使用的分块方法:


3.1. 字符文本分割器

字符文本分块器(CharacterTextSplitter)是最简单的分块器,它根据固定的字符数对文本进行分块。这种方法非常适合结构化内容,在这种情况下,保留特定的语义可能并不重要。


代码示例


from langchain.text_splitter import CharacterTextSplitter
# Define the splitter
text_splitter = CharacterTextSplitter(
    chunk_size=1000,  # Each chunk will be 1000 characters
    chunk_overlap=100  # There will be an overlap of 100 characters between chunks
)
# Sample text to split
text = "This is a very long document that needs to be split into smaller chunks."
# Perform the split
chunks = text_splitter.split_text(text)
# Print the chunks
for i, chunk in enumerate(chunks):
    print(f"Chunk {i+1}:\n{chunk}\n")


用例:最适用于要根据字符数进行分割的结构化文档,如文章或不太强调保留段落或句子的纯文本。


3.2. RecursiveCharacterTextSplitter (语义分块法)

RecursiveCharacterTextSplitter 更为先进,它能根据句子和段落等语义单位分割内容。它非常适合文章、报告和博客等对语义的保留至关重要的文档。


代码示例


from langchain.text_splitter import RecursiveCharacterTextSplitter
# Define the splitter
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,   # Each chunk will be 1000 characters
    chunk_overlap=100  # Overlap of 100 characters between chunks
)
# Sample text
text = "This is a long document divided into paragraphs and sentences."
# Perform the split
chunks = text_splitter.split_text(text)
# Print the chunks
for i, chunk in enumerate(chunks):
    print(f"Chunk {i+1}:\n{chunk}\n")


用例:适用于需要保持句子和段落结构的文章、博客和其他自然语言内容。


3.3. PythonCodeTextSplitter(代码分块)

为了处理代码,LangChain 提供了一个名为 PythonCodeTextSplitter 的专用分割器。该分割器可确保函数或类不会被分割成块,从而保持代码的逻辑流。


代码示例


from langchain.text_splitter import PythonCodeTextSplitter
# Define the Python code splitter
splitter = PythonCodeTextSplitter(
    chunk_size=500,    # Each chunk will be 500 characters
    chunk_overlap=50   # Overlap of 50 characters between chunks
)
# Sample Python code to split
code = """
def function_1():
    print("Hello from function 1")
def function_2():
    print("Hello from function 2")
"""
# Perform the split
chunks = splitter.split_text(code)
# Print the chunks
for i, chunk in enumerate(chunks):
    print(f"Chunk {i+1}:\n{chunk}\n")


用例:适用于处理对保持函数和类的逻辑流至关重要的代码。


3.4. 用于 JavaScript 和其他语言的 RecursiveCharacterTextSplitter

LangChain 还允许对 JavaScript、HTML 和 Markdown 等其他编程语言进行分块。这是通过使用 RecursiveCharacterTextSplitter.from_language() 方法来实现的,在该方法中,你可以指定语言语法。


JavaScript 代码示例:


from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.text_splitter import Language
# Define the JavaScript splitter
splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.JS,  # Choose JavaScript language
    chunk_size=60,         # Each chunk will be 60 characters
    chunk_overlap=10       # Overlap of 10 characters
)
# Sample JavaScript code
js_code = """
function greet() {
  console.log("Hello, World!");
}
greet();
"""
chunks = splitter.split_text(js_code)
# Print the chunks
for i, chunk in enumerate(chunks):
    print(f"Chunk {i+1}:\n{chunk}\n")


用例:该方法在处理 JavaScript 或 HTML 等非 Python 代码时非常有用。


3.5. Mardown 文本分割器

Markdown 内容通常遵循层次结构(标题、段落、列表等),因此有必要根据这些语义单位进行分块。


代码示例


from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.text_splitter import Language
# Define the Markdown splitter
splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.MARKDOWN, 
    chunk_size=500,
    chunk_overlap=100
)
# Sample Markdown document
markdown_text = """
# Heading 1
This is a paragraph under heading 1.
## Subheading
This is some more text under the subheading.
"""
# Perform the split
chunks = splitter.split_text(markdown_text)
for i, chunk in enumerate(chunks):
    print(f"Chunk {i+1}:\n{chunk}\n")


4. 有效分块的最佳做法

以下是为 LLM 和 RAG 系统分块文本时需要考虑的一些最佳实践:

  1. 分块大小和重叠: 尝试使用不同的分块大小和重叠度,在上下文保护和处理效率之间找到合适的平衡点。通常情况下,500-1000 个字符的分块大小和 100-200 个字符的重叠部分效果较好。
  2. 语义分块: 对自然语言或非结构化文本使用递归分割器,以保持句子和段落的完整性。
  3. 代码处理: 处理代码时,始终使用特定语言的分块器(如 PythonCodeTextSplitter),以避免破坏函数或逻辑块。
  4. 文档结构意识: 对于具有不同章节的文档(例如 Markdown 或 HTML),可使用 MarkdownTextSplitter 等专门的分割器来确保标题和章节边界得到保留。
  5. 标记考虑因素: 如果使用像 GPT 这样的 LLM,应确保块不超过模型的标记限制,其中包括输入和输出标记。


结论

分块是 LLM 和 RAG 系统中的一项关键技术,它能让模型高效处理长文本,同时保留上下文和含义。无论你处理的是自然语言内容还是复杂代码,LangChain 都能提供多种分割器,以满足不同的使用情况

文章来源:https://medium.com/@kshitijkutumbe/comprehensive-guide-to-chunking-in-llm-and-rag-systems-c579a11ce6e2
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
写评论取消
回复取消