首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >RAG 切块Chunk技术总结与自定义分块实现思路

RAG 切块Chunk技术总结与自定义分块实现思路

作者头像
致Great
发布于 2025-01-18 06:03:49
发布于 2025-01-18 06:03:49
93100
代码可运行
举报
文章被收录于专栏:自然语言处理自然语言处理
运行总次数:0
代码可运行

TrustRAG项目地址🌟:https://github.com/gomate-community/TrustRAG

可配置的模块化RAG框架

切块简介

在RAG(Retrieval-Augmented Generation)任务中,Chunk切分是一个关键步骤,尤其是在处理结构复杂的PDF文档时。PDF文档可能包含图片、奇怪的排版等,增加了Chunk切分的难度。

Chunk粒度的影响
  • 句子/单词粒度:注重局部、关键信息的查询,但可能缺失上下文信息。
  • 长篇段落/文章粒度:Embedding结果反映整个文章的意思,但难以精准到个体单词。
不同场景的Chunk分块粒度
  • 微博:少字符,适合较小的Chunk粒度。
  • 知乎/小红书:中小量字符数,适合中等Chunk粒度。
  • 博客:超多字符,适合较大的Chunk粒度。
  • 专业性较强、专有名词较多的文章:需要较小的Chunk粒度以保留专业信息。
  • 综述类信息总结文章:适合较大的Chunk粒度以保留整体信息。
Chunk切分对信息的影响
  1. 上下文信息:例如,《统计学习的要素》这本书有18章,每章专注于一个主题,副标题和第二层副标题等。人们习惯于在语境中理解文章。
  2. 位置信息:文本的权重取决于它们在文档中的位置。文档开头和结尾的文字比中间的文字更重要。
  3. 连续的信息:一个故事可能以“in the beginning”开头,然后以“then”、“therefore”、“after that”继续,直到以“finally”结尾。使用分块策略,这种连接可能不再完整。
  4. 描述信息:使用分块,可能无法保证描述性信息集中在一起。
RAG任务不擅长回答的问题
  • 小范围的描述性问题:例如,哪个主体具有某些特征?
  • 关系推理:寻找从实体A到实体B的路径或识别实体集团。
  • 时间跨度很长的总结:例如,“列出所有哈利波特的战斗”或“哈利波特有多少次战斗?”
确定最佳分块策略的因素
  1. 被索引内容的性质:处理较长的文档(如文章或书籍)还是较短的内容(如微博或即时消息)?
  2. 使用的Embedding模型:例如,sentence-transformer模型在单个句子上工作得很好,但像text-embedding-ada-002这样的模型在包含256或512个tokens的块上表现得更好。
  3. 用户查询的长度和复杂性:用户输入的问题文本是简短而具体的还是冗长而复杂的?
  4. 检索结果的使用方式:用于语义搜索、问答、摘要或其他目的?底层连接的LLM的tokens限制也会影响分块的大小。

总之,没有最好的分块策略,只有适合的分块策略。为了确保查询结果更加准确,有时候需要选择性地使用几种不同的策略。

下面是Langchain/Langchain-chatchat,Langchain提供了很多文本切割的工具,其中langchain默认使用RecursiveCharacterTextSplitter,还有其他的切块方法比如:

  • RecursiveCharacterTextSplitter
  • CharacterTextSplitter
  • TokenTextSplitter
  • MarkdownHeaderTextSplitter
  • CodeTextSplitter
  • spaCy(TokenTextSplitter变形)
  • SentenceTransformersTokenTextSplitter(TokenTextSplitter变形)
  • NLTKTextSplitter(TokenTextSplitter变形)
  • GPT2TokenizerFast
  • AliTextSplitter
  • ChineseRecursiveTextSplitter
  • ChineseTextSplitter
  • zh_title_enhance

如何确定最佳块大小

确定最佳块大小通常需要通过A/B测试来进行。运行一系列查询来评估质量,并比较不同块大小的性能。这是一个反复测试的过程,针对不同的查询测试不同的块大小,直到找到最佳的块大小。

经验之谈
  • 较小的块大小:为了获得更好的结果,建议使用较小的块大小。微软的分析表明,较小的块大小有助于提高性能。
  • 分割策略:在分割文本时,可以选择不同的分割策略。最简单的方法是在单词的中间切断,也可以尝试在句子或段落的中间切断。为了得到更好的结果,可以重叠相邻的块。
Embedding模型的限制

Embedding模型在呈现多主题、多回合语料库时不如简单语料库有效。这就是为什么RAG(Retrieval-Augmented Generation)更喜欢较短的块。

  • 块大小范围:在微软的分析中,最小的块大小是512个tokens。一些企业级RAG应用程序中的块大小只有100个tokens。
  • 信息丢失:分块策略会将文本语料库分解成小块,导致信息丢失。数据块越小,丢失的信息就越多。因此,存在一个最优的块大小,过小的分块可能不太理想。
寻找最优块大小

寻找最优块大小就像超参数调优一样,必须用你的数据或者文档做实验。

文本重叠对准确率的提升

重叠的作用

重叠可以帮助将相邻的块链接在一起,并为块提供更好的上下文信息。然而,即使是非常激进的25%重叠也只能将准确率提高1.5%,从42.4%提高到43.9%。这意味着这不是优化RAG性能的最有效方法。

重叠的局限性
  • 处理代词:重叠在处理代词时很有效。例如,上一个chunk提到“徐悲鸿非常擅长画马”,下一句说“他画马的主要技法是xxxxx”,如果缺少重叠,代词“他”就会失去上下文。
  • 小块不适用:重叠分块甚至不能用于小块。
知识图谱的引入
  • 知识图谱的优势:在知识图谱的帮助下,RAG可以将这些关系存储在图数据库中,块之间的连接可以完全保留。如果关系推理对您的项目至关重要,这是一个非常可观的解决方案。
  • 挑战:从非结构化文本中建立知识图谱是非常重要的。自动提取的实体和关系可能包含大量的噪声,忽略了太多的真实信息。必须非常仔细地检查产品的质量。
支持向量搜索的关系数据库
  • pgvector:像pgvector这样的数据库允许您将复杂的信息存储为列,同时保留语义搜索功能。它比知识图谱更容易与其他企业系统集成,也更灵活。

自定义切块

下面是TrustRAG项目中,实现一个句子切块的逻辑:

1. 句子切块初始化
  • 作用: 初始化 SentenceChunker 类,设置 tokenizer 和分块的最大 token 数量。
  • 逻辑:
    • 调用父类的 __init__ 方法。
    • 设置 tokenizerrag_tokenizer,用于计算句子的 token 数量。
    • 设置 chunk_size,默认值为 512,表示每个分块的最大 token 数量。
2. 切分句子
  • 作用: 将输入的文本按照句子进行分割,支持中英文的句子分割。
  • 逻辑:
    • 使用正则表达式 re.compile(r'([。!?.!?])') 匹配句子结束的标点符号(中文:。!?;英文:.!?)。
    • 将文本按照这些标点符号进行分割,得到一个包含句子和标点符号的列表。
    • 将标点符号与前面的句子合并,形成完整的句子。
    • 处理最后一个句子(如果它没有标点符号)。
    • 去除句子前后的空白字符,并过滤掉空句子。
    • 返回一个包含所有句子的列表。
3. 处理切块
  • 作用: 对分块后的文本进行预处理,主要是规范化多余的换行符和空格。
  • 逻辑:
    • 遍历每个分块,处理其中的换行符和空格:
      • 将四个或更多连续的换行符替换为两个换行符。
      • 将四个或更多连续的空格替换为两个空格。
    • 返回处理后的分块列表。
4. 段落切块
  • 作用: 将输入的段落列表分块,确保每个分块的 token 数量不超过 chunk_size
  • 逻辑:
    • 将段落列表合并为一个完整的文本。
    • 使用 split_sentences 方法将文本分割成句子列表。
    • 如果没有分割出句子,则将段落列表作为句子列表。
    • 初始化 chunks 列表和 current_chunk 列表,用于存储当前分块的句子和 token 数量。
    • 遍历句子列表,计算每个句子的 token 数量:
      • 如果当前分块的 token 数量加上当前句子的 token 数量不超过 chunk_size,则将句子加入当前分块。
      • 否则,将当前分块加入 chunks 列表,并开始一个新的分块。
    • 处理最后一个分块(如果它包含句子)。
    • 使用 process_text_chunks 方法对分块进行预处理。
    • 返回最终的分块列表。

完整代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import re
from trustrag.modules.document import rag_tokenizer
from trustrag.modules.chunks.base import BaseChunker

class SentenceChunker(BaseChunker):
    """
    A class for splitting text into chunks based on sentences, ensuring each chunk does not exceed a specified token size.

    This class is designed to handle both Chinese and English text, splitting it into sentences using punctuation marks.
    It then groups these sentences into chunks, ensuring that the total number of tokens in each chunk does not exceed
    the specified `chunk_size`. The class also provides methods to preprocess the text chunks by normalizing excessive
    newlines and spaces.

    Attributes:
        tokenizer (callable): A tokenizer function used to count tokens in sentences.
        chunk_size (int): The maximum number of tokens allowed per chunk.

    Methods:
        split_sentences(text: str) -> list[str]:
            Splits the input text into sentences based on Chinese and English punctuation marks.

        process_text_chunks(chunks: list[str]) -> list[str]:
            Preprocesses text chunks by normalizing excessive newlines and spaces.

        get_chunks(paragraphs: list[str]) -> list[str]:
            Splits a list of paragraphs into chunks based on a specified token size.
    """

    def __init__(self, chunk_size=512):
        """
        Initializes the SentenceChunker with a tokenizer and a specified chunk size.

        Args:
            chunk_size (int, optional): The maximum number of tokens allowed per chunk. Defaults to 512.
        """
        super().__init__()
        self.tokenizer = rag_tokenizer
        self.chunk_size = chunk_size

    def split_sentences(self, text: str) -> list[str]:
        """
        Splits the input text into sentences based on Chinese and English punctuation marks.

        Args:
            text (str): The input text to be split into sentences.

        Returns:
            list[str]: A list of sentences extracted from the input text.
        """
        # Use regex to split text by sentence-ending punctuation marks
        sentence_endings = re.compile(r'([。!?.!?])')
        sentences = sentence_endings.split(text)

        # Merge punctuation marks with their preceding sentences
        result = []
        for i in range(0, len(sentences) - 1, 2):
            if sentences[i]:
                result.append(sentences[i] + sentences[i + 1])

        # Handle the last sentence if it lacks punctuation
        if sentences[-1]:
            result.append(sentences[-1])

        # Remove whitespace and filter out empty sentences
        result = [sentence.strip() for sentence in result if sentence.strip()]

        return result

    def process_text_chunks(self, chunks: list[str]) -> list[str]:
        """
        Preprocesses text chunks by normalizing excessive newlines and spaces.

        Args:
            chunks (list[str]): A list of text chunks to be processed.

        Returns:
            list[str]: A list of processed text chunks with normalized formatting.
        """
        processed_chunks = []
        for chunk in chunks:
            # Normalize four or more consecutive newlines
            while '\n\n\n\n' in chunk:
                chunk = chunk.replace('\n\n\n\n', '\n\n')

            # Normalize four or more consecutive spaces
            while '    ' in chunk:
                chunk = chunk.replace('    ', '  ')

            processed_chunks.append(chunk)

        return processed_chunks

    def get_chunks(self, paragraphs: list[str]) -> list[str]:
        """
        Splits a list of paragraphs into chunks based on a specified token size.

        Args:
            paragraphs (list[str]|str): A list of paragraphs to be chunked.

        Returns:
            list[str]: A list of text chunks, each containing sentences that fit within the token limit.
        """
        # Combine paragraphs into a single text
        text = ''.join(paragraphs)

        # Split the text into sentences
        sentences = self.split_sentences(text)

        # If no sentences are found, treat paragraphs as sentences
        if len(sentences) == 0:
            sentences = paragraphs

        chunks = []
        current_chunk = []
        current_chunk_tokens = 0

        # Iterate through sentences and build chunks based on token count
        for sentence in sentences:
            tokens = self.tokenizer.tokenize(sentence)
            if current_chunk_tokens + len(tokens) <= self.chunk_size:
                # Add sentence to the current chunk if it fits
                current_chunk.append(sentence)
                current_chunk_tokens += len(tokens)
            else:
                # Finalize the current chunk and start a new one
                chunks.append(''.join(current_chunk))
                current_chunk = [sentence]
                current_chunk_tokens = len(tokens)

        # Add the last chunk if it contains any sentences
        if current_chunk:
            chunks.append(''.join(current_chunk))

        # Preprocess the chunks to normalize formatting
        chunks = self.process_text_chunks(chunks)
        return chunks

if __name__ == '__main__':
    with open("../../../data/docs/news.txt","r",encoding="utf-8") as f:
        content=f.read()
    tc=SentenceChunker(chunk_size=128)
    chunks = tc.get_chunks([content])
    for chunk in chunks:
        print(f"Chunk Content:\n{chunk}")

输出如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Chunk Content:
韩国总统警卫处长辞职

#韩国总统警卫处长辞职#更新:韩国总统警卫处长朴钟俊今天(110日)到案接受调查前,向代总统崔相穆递交辞呈。#韩国总统警卫处长到案接受调查#今天上午,朴钟俊抵达韩国警察厅国家调查本部,接受警方调查。他在接受调查前向现场记者表示,针对被停职总统尹锡悦的逮捕令存在法理上的争议,对尹锡悦的调查程序应符合总统身份,而不是以逮捕令的形式进行。
Chunk Content:
他还说,政府机构间不能出现流血事件。韩国高级公职人员犯罪调查处(公调处)13日组织人员前往位于首尔市龙山区汉南洞的总统官邸进行抓捕,但遭总统警卫处抵抗,双方对峙5个多小时后,公调处宣布抓捕行动失败。韩国“共同调查本部”以涉嫌妨碍执行特殊公务为由对朴钟俊立案,要求其到案接受调查。朴钟俊曾两次拒绝到案接受警方调查。(总台记者 张昀

参考资料

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
LLM开发者必备:掌握21种分块策略让RAG应用性能翻倍
检索增强生成(Retrieval-Augmented Generation, RAG)是当前AI工程师在实际应用中面临的重要技术挑战之一。从理论角度来看,RAG的工作原理相对直观:从自定义数据源中检索相关上下文,然后基于这些上下文让大语言模型生成对应的回答。
deephub
2025/08/20
3490
LLM开发者必备:掌握21种分块策略让RAG应用性能翻倍
一文解决RAG核心痛点:当文本分块遇上动态重叠与结构预测
由于附加文档可能非常大,因此步骤 1 还涉及分块,其中将大文档分成较小/易于管理的部分。
聚客AI
2025/08/07
3820
一文解决RAG核心痛点:当文本分块遇上动态重叠与结构预测
RAG系统文本分块优化指南:9种实用策略让检索精度翻倍
检索增强生成(Retrieval-Augmented Generation, RAG)技术通过将外部知识检索与大语言模型生成能力相结合,实现了基于检索文本块(chunk)上下文的高质量内容生成。RAG系统的性能很大程度上依赖于文本分块策略的选择和实施。
deephub
2025/08/20
3610
RAG系统文本分块优化指南:9种实用策略让检索精度翻倍
如何高效提升大模型的RAG效果?多种实用策略一次掌握
持续提升RAG(检索增强生成,Retrieval-Augmented Generation)的效果是当前许多企业应用大模型时非常关注的一个关键问题。虽然RAG看起来简单,但真正要做到效果持续提升,还真不是一件容易的事。咱们今天就用更轻松的语言,结合实际案例,聊聊如何通过多种策略持续增强RAG能力,帮助你在实际落地项目中游刃有余。
fanstuck
2025/03/13
6432
如何高效提升大模型的RAG效果?多种实用策略一次掌握
分块的艺术:提升 RAG 效果的关键
在RAG系统中,这种高效的实现往往是通过“分块”来实现的。你可以把它想象成把一本厚书分成几章——这样一来,阅读和理解就轻松多了。同样地,分块技术把大段复杂的文本拆分成更小、更容易处理的片段,让AI能更快、更准确地理解和处理信息。
致Great
2025/01/10
6630
分块的艺术:提升 RAG 效果的关键
不要盲目再使用DeepSeek R1和QWQ这些推理模型做RAG了
DeepSeek R1 在首次发布时就展现出了强大的推理能力。在这篇文章中,我们将详细介绍使用 DeepSeek R1 构建针对法律文件的 RAG 系统的经验。
技术人生黄勇
2025/03/11
4580
不要盲目再使用DeepSeek R1和QWQ这些推理模型做RAG了
Chunking:基于大模型RAG系统中的文档分块
【引】“枯萎,无法回避,如人之生老病死;荒芜,无法接受,如碌碌无为一生。” 这是周六回乡下除草的感受。有所得,有所感,对工程技术也是如此。
半吊子全栈工匠
2024/11/07
1.4K0
Chunking:基于大模型RAG系统中的文档分块
高级RAG技术第1部分:数据处理
最近的论文《搜索增强生成中的最佳实践》通过实证研究评估了各种增强RAG技术的效果,旨在汇聚一套RAG的最佳实践。
点火三周
2024/08/19
6970
高级RAG技术第1部分:数据处理
使用 Jina Embeddings v2 在 Elasticsearch 中进行后期分块处理
在这篇文章中,我们将配置并使用 jina-embeddings-v2,这是第一个开源的8K上下文长度嵌入模型。我们会从使用 semantic_text 的开箱即用实现开始,然后介绍如何实现后期分块。
点火三周
2024/12/06
3920
使用 Jina Embeddings v2 在 Elasticsearch 中进行后期分块处理
Chonkie:面向大语言模型的轻量级文本分块处理库
Chonkie是一个专为大语言模型(LLM)应用场景设计的轻量级文本分块处理库,提供高效的文本分割和管理解决方案。该库采用最小依赖设计理念,特别适用于现实世界的自然语言处理管道。本文将详细介绍Chonkie的核心功能、设计理念以及五种主要的文本分块策略。
deephub
2025/08/20
2310
Chonkie:面向大语言模型的轻量级文本分块处理库
怎么短平快地把RAG做好:以厦门银行RAG比赛为例讲解
前段时间笔者参加了厦门银行的第五届数创金融杯大模型应用挑战赛,其中初赛是金融监管制度智能问答,属于经典RAG问答,具体比赛任务如下:
致Great
2025/07/12
2490
怎么短平快地把RAG做好:以厦门银行RAG比赛为例讲解
告别碎片化!两大先进分块技术如何提升RAG的语义连贯性?
可以看出, 当前RAG分块策略的研究聚焦于平衡上下文保留与计算效率。传统方法(固定分块、语义分块)在简单场景中仍具优势,而延迟分块和上下文检索在复杂语义任务中表现更优但代价高昂。未来需探索轻量化上下文增强、长文档优化技术,并建立统一评估标准以推动实际应用。
致Great
2025/04/30
4590
告别碎片化!两大先进分块技术如何提升RAG的语义连贯性?
【RAG实战 】 手把手教你从零手撸一个语义切块,解锁更多优化技巧!
在RAG(Retrieval-Augmented Generation)中,chunk是个关键步骤。它的核心目标,就是把语义相近的内容放在一起,语义不同的内容拆开,这样后续的检索(retrieve)和重排序(rerank)才能更有效。
致Great
2025/03/24
5890
【RAG实战 】 手把手教你从零手撸一个语义切块,解锁更多优化技巧!
【RAG最新研究】优化RAG系统的最佳实践与深度解析
这篇论文主要关注的是检索增强型生成(RAG)系统中的一个核心问题:不同的组件和配置如何影响系统的性能。
致Great
2025/01/16
7310
【RAG最新研究】优化RAG系统的最佳实践与深度解析
5 分钟内搭建一个免费问答机器人:Milvus + LangChain
答案是 5 分钟。只需借助开源的 RAG 技术栈、LangChain 以及好用的向量数据库 Milvus。必须要强调的是,该问答机器人的成本很低,因为我们在召回、评估和开发迭代的过程中不需要调用大语言模型 API,只有在最后一步——生成最终问答结果的时候会调用到 1 次 API。
Zilliz RDS
2023/12/26
1.9K0
5 分钟内搭建一个免费问答机器人:Milvus + LangChain
RAG 架构实战:Fixed-Size Chunking(固定切块) 解析
Hello folks,我是 Luga,今天我们来聊一下人工智能应用场景 - 构建高效、灵活的计算架构的 RAG 架构的切块策略—Fixed-Size Chunking(固定切块)。
Luga Lee
2025/05/25
2250
RAG 架构实战:Fixed-Size Chunking(固定切块) 解析
开发基于云的RAG应用,使用开源 LLM
按照以下步骤指南,使用 BentoML、LangChain 和 MyScaleDB 创建自定义 AI 应用程序。
云云众生s
2024/07/07
3860
开发基于云的RAG应用,使用开源 LLM
RAG 技术合集:检索增强生成的实践指南
大模型“幻觉”像幽灵:参数量越大,一本正经胡说八道的可信度越高。微调(Fine-tune)能缓解,却要 GPU 土豪 + 数据标注民工;提示工程(Prompt Engineering)轻量,却受 4 k/8 k context 天花板。RAG(Retrieval-Augmented Generation)把“闭卷考试”变成“开卷带小抄”——先检索后生成,既不改权重,也能把私域知识塞进 7 B 模型,效果常追平甚至反超 60 B。本文用一份代码仓库(文末链接)串联 BM25、Dense、ColBERT、Rerank、FLARE 五类范式,帮你画出一条“能跑、能测、能上线”的 RAG 成长曲线。
江南清风起
2025/09/28
1250
RAG实战-Markdown文件解析思路分析与实现
最近遇到几个伙伴关于markdown解析的问题,都是比较偏向于实际使用场景的,这里我们一开始我们去做markdown文件解析会自觉的会困在一个陷阱,就是:
致Great
2025/01/09
5040
RAG实战-Markdown文件解析思路分析与实现
【LangChain系列】第二节:文档拆分
在上一篇博客中,我们学习了如何使用LangChain的文档加载器将文档加载为标准格式。加载文档后,下一步是将它们拆分为更小的块。这个过程乍一看似乎很简单,但有一些微妙之处和重要的考虑因素会显着影响下游任务的性能和准确性。
Freedom123
2024/05/16
1.6K0
推荐阅读
相关推荐
LLM开发者必备:掌握21种分块策略让RAG应用性能翻倍
更多 >
领券
一站式MCP教程库,解锁AI应用新玩法
涵盖代码开发、场景应用、自动测试全流程,助你从零构建专属AI助手
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档