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

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 方法对分块进行预处理。
    • 返回最终的分块列表。

完整代码如下:

import re
from trustrag.modules.document import rag_tokenizer
from trustrag.modules.chunks.base import BaseChunkerclass 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 exceedthe specified `chunk_size`. The class also provides methods to preprocess the text chunks by normalizing excessivenewlines 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_tokenizerself.chunk_size = chunk_sizedef 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 markssentence_endings = re.compile(r'([。!?.!?])')sentences = sentence_endings.split(text)# Merge punctuation marks with their preceding sentencesresult = []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 punctuationif sentences[-1]:result.append(sentences[-1])# Remove whitespace and filter out empty sentencesresult = [sentence.strip() for sentence in result if sentence.strip()]return resultdef 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 newlineswhile '\n\n\n\n' in chunk:chunk = chunk.replace('\n\n\n\n', '\n\n')# Normalize four or more consecutive spaceswhile '    ' in chunk:chunk = chunk.replace('    ', '  ')processed_chunks.append(chunk)return processed_chunksdef 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 texttext = ''.join(paragraphs)# Split the text into sentencessentences = self.split_sentences(text)# If no sentences are found, treat paragraphs as sentencesif len(sentences) == 0:sentences = paragraphschunks = []current_chunk = []current_chunk_tokens = 0# Iterate through sentences and build chunks based on token countfor 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 fitscurrent_chunk.append(sentence)current_chunk_tokens += len(tokens)else:# Finalize the current chunk and start a new onechunks.append(''.join(current_chunk))current_chunk = [sentence]current_chunk_tokens = len(tokens)# Add the last chunk if it contains any sentencesif current_chunk:chunks.append(''.join(current_chunk))# Preprocess the chunks to normalize formattingchunks = self.process_text_chunks(chunks)return chunksif __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}")

输出如下:

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

参考资料

  • 派神 -:让Langchain与你的数据对话(一):数据加载与分割
  • LangChain 按Token拆分文本内容
  • Chunking Strategies for LLM Applications | Pinecone
  • RAG行业交流中发现的一些问题和改进方法
  • 最详细的文本分块(Chunking)方法,直接影响LLM应用效果
  • RAG 分块Chunk技术优劣、技巧、方法汇总(五)

添加微信1185918903,关注公众号ChallengeHub获取更所咨询

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/3226.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】数据库约束和多表查询

目录 1.前言 2.数据库约束 2.1约束类型 2.2?NULL约束 2.3 NUIQUE&#xff1a;唯一约束 2.4?DEFAULT&#xff1a;默认值约束 2.5?PRIMARY KEY&#xff1a;主键约束 2.6 FOREIGN KEY&#xff1a;外键约束 1.7?CHECK约束 3.表的设计? 3.1一对一 3.2一对多 3.3多…

解锁C#语法的无限可能:从基础到进阶的编程之旅

目录 一、C# 基础语法 1.1 数据类型 1.2 变量与常量 1.3 运算符 1.4 控制流语句 二、C# 面向对象编程语法 2.1 类与对象 2.2 封装 2.3 继承 2.4 多态 虚方法 抽象类 接口 三、C# 高级语法 3.1 特性&#xff08;Attribute&#xff09; 预定义特性 自定义特性 3…

【2024年华为OD机试】 (C卷,100分)- 小明找位置(Java JS PythonC/C++)

一、问题描述 题目描述 小朋友出操&#xff0c;按学号从小到大排成一列&#xff1b; 小明来迟了&#xff0c;请你给小明出个主意&#xff0c;让他尽快找到他应该排的位置。 算法复杂度要求不高于nLog(n)&#xff1b;学号为整数类型&#xff0c;队列规模 ≤ 10000&#xff1…

vue2配置跨域后请求的是本机

这个我来说明一下&#xff0c;因为我们公司的后端设置解决了跨域问题&#xff0c;所以我有很久没有看相关的内容了&#xff0c;然后昨天请求了需要跨域的接口&#xff0c;请求半天一直不对&#xff0c;浏览器显示的是本机地址&#xff0c;我以为是自己配置错了&#xff0c;后面…

【Uniapp-Vue3】@import导入css样式及scss变量用法与static目录

一、import导入css样式 在项目文件中创建一个common文件夹&#xff0c;下面创建一个css文件夹&#xff0c;里面放上style.css文件&#xff0c;编写的是公共样式&#xff0c;我们现在要在App.vue中引入该样式。 在App.vue中引入该样式&#xff0c;这样就会使样式全局生效&#…

大模型WebUI:Gradio全解11——Chatbot:融合大模型的多模态聊天机器人(6)

大模型WebUI&#xff1a;Gradio全解11——Chatbot&#xff1a;融合大模型的多模态聊天机器人&#xff08;6&#xff09; 前言本篇摘要11. Chatbot&#xff1a;融合大模型的多模态聊天机器人11.6 为LLM Agent构建UI11.5.1 使用代理构建1. 使用transformers.agents的实际示例2. 使…

【React】插槽渲染机制

目录 通过 children 属性结合条件渲染通过 children 和 slot 属性实现具名插槽通过 props 实现具名插槽 在 React 中&#xff0c;并没有直接类似于 Vue 中的“插槽”机制&#xff08;slot&#xff09;。但是&#xff0c;React 可以通过 props和 children 来实现类似插槽的功能…

LoadBalancer负载均衡服务调用

LoadBalancer LoadBalancer&#xff08;负载均衡器&#xff09;是Spring Cloud中的一个关键组件&#xff0c;用于在微服务架构中实现服务请求的负载均衡。它的主要作用是将客户端的请求分发到多个服务实例上&#xff0c;以提高系统的可用性、性能和容错能力。通过LoadBalancer&…

Linux操作命令之云计算基础命令

一、图形化界面/文本模式 ctrlaltF2-6 图形切换到文本 ctrlalt 鼠标跳出虚拟机 ctrlaltF1 文本切换到图形 shift ctrl "" 扩大 ctrl "-" 缩小 shift ctrl "n" 新终端 shift ctrl "t" 新标签 alt 1,…

简历_使用优化的Redis自增ID策略生成分布式环境下全局唯一ID,用于用户上传数据的命名以及多种ID的生成

系列博客目录 文章目录 系列博客目录WhyRedis自增ID策略 Why 我们需要设置全局唯一ID。原因&#xff1a;当用户抢购时&#xff0c;就会生成订单并保存到tb_voucher_order这张表中&#xff0c;而订单表如果使用数据库自增ID就存在一些问题。 问题&#xff1a;id的规律性太明显、…

企业邮箱iRedMail搭建

用自己的域名作为邮箱的后缀&#xff0c;好看、有面子&#xff01;只要域名不过期&#xff0c;那么&#xff0c;你的邮箱就永远存在&#xff01; 邮件系统很多&#xff0c;宝塔自带的邮局更是简单&#xff0c;但是若想邮箱可靠&#xff08;丢邮件、发送邮件进入对方垃圾箱等&a…

在 Fluent 网格划分中使用薄网格特征

薄体模型的网格划分策略 薄体网格划分对于有效模拟薄壁结构或厚度明显小于其他尺寸的几何形状非常有利。当使用此类几何结构时&#xff0c;传统的体积网格划分技术可能会导致单元数量增加&#xff0c;因为它们试图捕获具有许多不必要单元的薄尺寸。薄体网格划分通过专门沿薄方…

Windows重装后NI板卡LabVIEW恢复正常

在重新安装Windows系统后&#xff0c;NI&#xff08;National Instruments&#xff09;板卡能够恢复正常工作&#xff0c;通常是由于操作系统的重新配置解决了之前存在的硬件驱动、兼容性或配置问题。操作系统重装后&#xff0c;系统重新加载驱动程序、清理了潜在的冲突或损坏的…

Html5 video标签学习

<video> 标签的属性 autoplay布尔属性声明该属性后&#xff0c;视频会尽快自动开始播放&#xff0c;不会停下来等待数据全部加载完成。controls布尔属性如果存在该属性&#xff0c;浏览器会在视频底部提供一个控制面板&#xff0c;允许用户控制视频的播放。controlslist…

OpenAI推出首个AI Agent!日常事项自动化处理!

2025 年1月15日&#xff0c;OpenAI 正式宣布推出一项名为Tasks的测试版功能 。 该功能可以根据你的需求内容和时间实现自动化处理。比方说&#xff0c;你可以设置每天早晨 7 点获取天气预报&#xff0c;或定时提醒遛狗等日常事项。 看到这里&#xff0c;有没有一种熟悉的感觉&a…

【Linux】Socket编程-TCP构建自己的C++服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; Socket 编程 TCP &#x1f98b; TCP socket API 详解&#x1f98b; 多线程远程命令执行&#x1f98b; 网络版计算器&#xff08;应用层自定义协议与序列化…

web开发工具之:三、JWT的理论知识,java的支持,封装的工具类可以直接使用

文章目录 前言一、JWT的理论知识1. 什么是 JWT&#xff08;JSON Web Token&#xff09;&#xff1f;2. **JWT 的组成**3. **JWT 的特点**4. **JWT 的使用场景**5. **JWT 的生命周期**6. **JWT 的优点**7. **JWT 的注意事项**5. **JWT 示例**总结 二、java的springboot支持1. po…

Jira中bug的流转流程

Jira中bug的状态 1. 处理Bug的流程2. bug状态流转详述bug的状态通常包括 1. 处理Bug的流程 2. bug状态流转详述 bug的状态通常包括 未解决 1. 测试人员创建一个bug&#xff0c;填写bug的详细信息&#xff0c;如概要、bug级别、复现步骤、现状、预期结果等 2. 定位bug&#x…

ChatGPT结合Excel辅助学术数据分析详细步骤分享!

目录 一.Excel在学术论文中的作用✔ 二.Excel的提示词✔ 三. 编写 Excel 命令 四. 编写宏 五. 执行复杂的任务 六. 将 ChatGPT 变成有用的 Excel 助手 一.Excel在学术论文中的作用✔ Excel作为一种广泛使用的电子表格软件&#xff0c;在学术论文中可以发挥多种重要作用&a…

Vue篇-07

Vue UI组件库 一、移动端常用的UI组件库 1.1、Vant 1.2、Cube UI 1.3、Mint UI 二、PC端常用的UI组件库 2.1、Element UI Element - The worlds most popular Vue UI framework 安装&#xff1a; 按需引入&#xff1a; 135_尚硅谷Vue技术_element-ui按需引入_哔哩哔哩_b…