#LLM入门 | langchain | RAG # 4.3_文档分割

上一章讨论了文档标准化加载,现在转向文档的细分,这虽简单却对后续工作有重大影响。

一、为什么要进行文档分割

  1. 模型大小和内存限制:大型GPT模型参数众多,需大量计算和内存,而硬件设备如GPU或TPU有内存限制,文档分割有助于在这些限制内工作。
  2. 计算效率:长文本序列需更多资源,分块可提高计算效率。
  3. 序列长度限制:GPT模型有最大序列长度限制(如2048个token),超长文档需分割。
  4. 更好的泛化:多块训练增强模型对不同文本样式和结构的泛化。
  5. 数据增强:分割可增加训练样本,如将长文档分成多个独立样本。

注意事项:分割可能导致上下文信息丢失,特别是在分割点附近,需权衡分割方法。

image.png
图 4.3.1 文档分割的意义
单一字符分割易失语义,应分至完整语义段落或单元以保准确性。

二、文档分割方式

Langchain 中文本分割器都根据 chunk_size (块大小)和 chunk_overlap (块与块之间的重叠大小)进行分割。
image.png
图 4.3.2 文档分割示例

  • chunk_size 指每个块包含的字符或 Token (如单词、句子等)的数量
  • chunk_overlap 指两个块之间共享的字符数量,用于保持上下文的连贯性,避免分割丢失上下文信息

image.png
图 4.3.3 文档分割工具
Langchain提供多种文档分割方式,区别在怎么确定块与块之间的边界、块由哪些字符/token组成、以及如何测量块大小

三、基于字符分割

文本分割方法与任务类型紧密相关,尤其在拆分代码时。我们引入了语言文本分割器,含多种编程语言分隔符,需考虑不同语言差异
我们将从基于字符的分割开始探索,借助 LangChain 提供的 RecursiveCharacterTextSplitter 和 CharacterTextSplitter 工具来实现此目标。
CharacterTextSplitter 是字符文本分割,分隔符的参数是单个的字符串;RecursiveCharacterTextSplitter 是递归字符文本分割,将按不同的字符递归地分割(按照这个优先级[“\n\n”, “\n”, " ", “”]),这样就能尽量把所有和语义相关的内容尽可能长时间地保留在同一位置。因此,RecursiveCharacterTextSplitter 比 CharacterTextSplitter 对文档切割得更加碎片化
RecursiveCharacterTextSplitter 需要关注的是如下4个参数:

  • separators - 分隔符字符串数组
  • chunk_size - 每个文档的字符数量限制
  • chunk_overlap - 两份文档重叠区域的长度
  • length_function - 长度计算函数

3.1 短句分割

# 导入文本分割器
from langchain.text_splitter import RecursiveCharacterTextSplitter, CharacterTextSplitterchunk_size = 20 #设置块大小
chunk_overlap = 10 #设置块重叠大小# 初始化递归字符文本分割器
r_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size,chunk_overlap=chunk_overlap
)
# 初始化字符文本分割器
c_splitter = CharacterTextSplitter(chunk_size=chunk_size,chunk_overlap=chunk_overlap
)

接下来我们对比展示两个字符文本分割器的效果。

text = "在AI的研究中,由于大模型规模非常大,模型参数很多,在大模型上跑完来验证参数好不好训练时间成本很高,所以一般会在小模型上做消融实验来验证哪些改进是有效的再去大模型上做实验。"  #测试文本
r_splitter.split_text(text)
['在AI的研究中,由于大模型规模非常大,模','大模型规模非常大,模型参数很多,在大模型','型参数很多,在大模型上跑完来验证参数好不','上跑完来验证参数好不好训练时间成本很高,','好训练时间成本很高,所以一般会在小模型上','所以一般会在小模型上做消融实验来验证哪些','做消融实验来验证哪些改进是有效的再去大模','改进是有效的再去大模型上做实验。'] 

可以看到,分割结果中,第二块是从“大模型规模非常大,模”开始的,刚好是我们设定的块重叠大小

#字符文本分割器
c_splitter.split_text(text)

[‘在AI的研究中,由于大模型规模非常大,模型参数很多,在大模型上跑完来验证参数好不好训练时间成本很高,所以一般会在小模型上做消融实验来验证哪些改进是有效的再去大模型上做实验。’]
可以看到字符分割器没有分割这个文本,因为字符文本分割器默认以换行符为分隔符,因此需要设置“,”为分隔符。

# 设置空格分隔符
c_splitter = CharacterTextSplitter(chunk_size=chunk_size,chunk_overlap=chunk_overlap,separator=','
)
c_splitter.split_text(text)
Created a chunk of size 23, which is longer than the specified 20['在AI的研究中,由于大模型规模非常大','由于大模型规模非常大,模型参数很多','在大模型上跑完来验证参数好不好训练时间成本很高','所以一般会在小模型上做消融实验来验证哪些改进是有效的再去大模型上做实验。'] 

设置“,”为分隔符后,分割效果与递归字符文本分割器类似。
可以看到出现了提示"Created a chunk of size 23, which is longer than the specified 20",意思是“创建了一个长度为23的块,这比指定的20要长。”。这是因为CharacterTextSplitter优先使用我们自定义的分隔符进行分割,所以在长度上会有较小的差距

3.2 长文本分割

接尝试对长文本进行分割。

# 中文版
some_text = """在编写文档时,作者将使用文档结构对内容进行分组。 \这可以向读者传达哪些想法是相关的。 例如,密切相关的想法\是在句子中。 类似的想法在段落中。 段落构成文档。 \n\n\段落通常用一个或两个回车符分隔。 \回车符是您在该字符串中看到的嵌入的“反斜杠 n”。 \句子末尾有一个句号,但也有一个空格。\并且单词之间用空格分隔"""print(len(some_text))

177
我们使用以上长文本作为示例。

c_splitter = CharacterTextSplitter(chunk_size=80,chunk_overlap=0,separator=' '
)''' 
对于递归字符分割器,依次传入分隔符列表,分别是双换行符、单换行符、空格、空字符,
因此在分割文本时,首先会采用双分换行符进行分割,同时依次使用其他分隔符进行分割
'''r_splitter = RecursiveCharacterTextSplitter(chunk_size=80,chunk_overlap=0,separators=["\n\n", "\n", " ", ""]
)

字符分割器结果:
c_splitter.split_text(some_text)

['在编写文档时,作者将使用文档结构对内容进行分组。 这可以向读者传达哪些想法是相关的。 例如,密切相关的想法 是在句子中。 类似的想法在段落中。 段落构成文档。','段落通常用一个或两个回车符分隔。 回车符是您在该字符串中看到的嵌入的“反斜杠 n”。 句子末尾有一个句号,但也有一个空格。 并且单词之间用空格分隔'] 

递归字符分割器效果:

r_splitter.split_text(some_text)
['在编写文档时,作者将使用文档结构对内容进行分组。     这可以向读者传达哪些想法是相关的。 例如,密切相关的想法    是在句子中。 类似的想法在段落中。','段落构成文档。','段落通常用一个或两个回车符分隔。     回车符是您在该字符串中看到的嵌入的“反斜杠 n”。     句子末尾有一个句号,但也有一个空格。','并且单词之间用空格分隔'] 

如果需要按照句子进行分隔,则还要用正则表达式添加一个句号分隔符

r_splitter = RecursiveCharacterTextSplitter(chunk_size=30,chunk_overlap=0,separators=["\n\n", "\n", "(?<=\。 )", " ", ""]
)
r_splitter.split_text(some_text)
['在编写文档时,作者将使用文档结构对内容进行分组。','这可以向读者传达哪些想法是相关的。','例如,密切相关的想法    是在句子中。','类似的想法在段落中。 段落构成文档。','段落通常用一个或两个回车符分隔。','回车符是您在该字符串中看到的嵌入的“反斜杠 n”。','句子末尾有一个句号,但也有一个空格。','并且单词之间用空格分隔'] 

这就是递归字符文本分割器名字中“递归”的含义,总的来说,我们更建议在通用文本中使用递归字符文本分割器

四、基于 Token 分割

很多 LLM 的上下文窗口长度限制是按照 Token 来计数的。因此,以 LLM 的视角,按照 Token 对文本进行分隔,通常可以得到更好的结果。 通过一个实例理解基于字符分割和基于 Token 分割的区别

# 使用token分割器进行分割,
# 将块大小设为1,块重叠大小设为0,相当于将任意字符串分割成了单个Token组成的列
from langchain.text_splitter import TokenTextSplitter
text_splitter = TokenTextSplitter(chunk_size=1, chunk_overlap=0)
text = "foo bar bazzyfoo"
text_splitter.split_text(text)
# 注:目前 LangChain 基于 Token 的分割器还不支持中文

[‘foo’, ’ bar’, ’ b’, ‘az’, ‘zy’, ‘foo’]
可以看出token长度和字符长度不一样,token通常为4个字符

五、分割Markdown文档

5.1 分割一个自定义 Markdown 文档

分块旨在聚相关文本,可使用分隔符或利用文档结构(如Markdown的标题)。Markdown标题分割器按标题分块,并将标题作元数据。

# 定义一个Markdown文档from langchain.document_loaders import NotionDirectoryLoader#Notion加载器
from langchain.text_splitter import MarkdownHeaderTextSplitter#markdown分割器markdown_document = """# Title\n\n \
## 第一章\n\n \
李白乘舟将欲行\n\n 忽然岸上踏歌声\n\n \
### Section \n\n \
桃花潭水深千尺 \n\n 
## 第二章\n\n \
不及汪伦送我情""" 

我们以上述文本作为 Markdown 文档的示例,上述文本格式遵循了 Markdown 语法,如读者对该语法不了解,可以简单查阅该教程 :Markdown 教程

# 定义想要分割的标题列表和名称
headers_to_split_on = [("#", "Header 1"),("##", "Header 2"),("###", "Header 3"),
]markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on
)#message_typemessage_type
md_header_splits = markdown_splitter.split_text(markdown_document)print("第一个块")
print(md_header_splits[0])
print("第二个块")
print(md_header_splits[1])
第一个块
page_content='李白乘舟将欲行  \n忽然岸上踏歌声' metadata={'Header 1': 'Title', 'Header 2': '第一章'}
第二个块
page_content='桃花潭水深千尺' metadata={'Header 1': 'Title', 'Header 2': '第一章', 'Header 3': 'Section'} 

可以看到,每个块都包含了页面内容和元数据,元数据中记录了该块所属的标题和子标题。

5.2 分割数据库中的 Markdown 文档

在上一章中,我们尝试了 Notion 数据库的加载,Notion 文档就是一个 Markdown 文档。我们在此处加载 Notion 数据库中的文档并进行分割。

#加载数据库的内容
loader = NotionDirectoryLoader("docs/Notion_DB")
docs = loader.load()
txt = ' '.join([d.page_content for d in docs])#拼接文档headers_to_split_on = [("#", "Header 1"),("##", "Header 2"),
]
#加载文档分割器
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on
)md_header_splits = markdown_splitter.split_text(txt)#分割文本内容print(md_header_splits[0])#分割结果
page_content='Let’s talk about stress. Too much stress.  \nW

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

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

相关文章

538.把二叉搜索树转换成累加树

给出二叉 搜索 树的根节点&#xff0c;该树的节点值各不相同&#xff0c;请你将其转换为累加树&#xff08;Greater Sum Tree&#xff09;&#xff0c;使每个节点 node 的新值 原树中大于或等于 node.val 的值之和。 方法一&#xff1a;递归 class Solution{int sum 0;publ…

Leetcode—706. 设计哈希映射【简单】(constexpr)

2024每日刷题&#xff08;127&#xff09; Leetcode—706. 设计哈希映射 数组实现代码 class MyHashMap { public:MyHashMap() {memset(arr, -1, sizeof(arr));}void put(int key, int value) {arr[key] value;}int get(int key) {if(arr[key] -1) {return -1;} return arr…

虚拟 DOM

当在虚拟 DOM 中嵌套子元素时&#xff0c;可以在 JSX 中使用嵌套的方式来描述。以下是在 h1 元素内部包含 span 元素的示例&#xff1a; const element ( <h1> Hello, <span>React</span> </h1> ); 在这个示例中&#xff0c;h1 元素内部包含了一个 s…

【JS】web浏览器访问本地系统字体列表

1、核心API&#xff1a; queryLocalFonts&#xff1b;以下为主要注意事项&#xff1a; 注意浏览器最低版本 需用户授权 网站是否安全&#xff0c;若不安全默认禁止获取本地字体列表 # 代码演示 export async function getLocalFonst() {if ("queryLocalFonts" in…

Deep Learning Part Eight Attention--24.5.3

注意力是全部。 --&#xff08;你若安好&#xff0c;就是夏天&#xff09;安夏的座右铭 00.引子 上一章我们使用 RNN 生成了文本&#xff0c;又通过连接两个 RNN&#xff0c;将一个时序数据转换为了另一个时序数据。我们将这个网络称为 seq2seq&#xff0c;并用它成功求解了简…

从哪些方面可以看出现货黄金价格走势?

现货黄金价格的走势受到多种因素的影响&#xff0c;我们可以从宏观经济环境、货币政策、供需关系、市场情绪和技术分析几个主要方面来观察和分析这一贵金属的价格动态。现货黄金作为全球投资市场中的避险资产&#xff0c;其价格波动往往能体现出复杂的经济和政治变化。 宏观经济…

前端开发中的组件是什么

在前端开发中&#xff0c;组件指的是一种可重用的、独立的模块&#xff0c;用于构建用户界面的一部分。 组件可以是按钮、输入框、导航栏、列表等各种UI元素&#xff0c;也可以是更复杂的功能模块&#xff0c;如轮播图、模态框、表单验证等。组件化是现代前端开发的重要概念&a…

一文读懂RPC的框架前后逻辑

目录 前言1. 没RPC2. 有RPC 前言 对于八股或者知识点&#xff0c;常会被说到RPC框架的知识点 本文主要讲解RPC的基本逻辑 那先讲讲没有RPC的时候是怎样的 1. 没RPC 分布式系统中的服务之间通常采用其他方式进行通信&#xff0c;其中一种常见的方式是使用RESTful API或者SO…

Java EE/Jakarta EE范畴一览

Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;现在已经改名为Jakarta EE&#xff0c;是一套用于开发企业级应用的标准Java平台。它扩展了Java SE&#xff08;Standard Edition&#xff09;&#xff0c;添加了支持大规模、多层次、可靠、安全、可伸缩和可管…

脸爱云一脸通智慧管理平台 SystemMng 管理用户信息泄露漏洞(XVE-2024-9382)

0x01 产品简介 脸爱云一脸通智慧管理平台是一套功能强大,运行稳定,操作简单方便,用户界面美观,轻松统计数据的一脸通系统。无需安装,只需在后台配置即可在浏览器登录。 功能包括:系统管理中心、人员信息管理中心、设备管理中心、消费管理子系统、订餐管理子系统、水控管…

树莓派的几种登录方式、及登录失败解决方式

使用TF卡安装树莓派的系统后&#xff0c;可以通过编辑TF卡里的文件来设置和启用 “ VNC ” 、“ SSH ” 和 “ 串口 ” 功能。不过&#xff0c;在使用中打开VNC和SSH可能并不直观或方便&#xff0c;因为这些服务通常在树莓派的系统内部配置和启动。但你可以通过以下步骤来设置和…

OpenCV 入门(三)—— 车牌筛选

OpenCV 入门系列&#xff1a; OpenCV 入门&#xff08;一&#xff09;—— OpenCV 基础 OpenCV 入门&#xff08;二&#xff09;—— 车牌定位 OpenCV 入门&#xff08;三&#xff09;—— 车牌筛选 OpenCV 入门&#xff08;四&#xff09;—— 车牌号识别 OpenCV 入门&#xf…

C++11 设计模式7 策略模式 ,Strategy

策略模式的概念&#xff1a; 策略模式&#xff08;Strategy Pattern&#xff09;是 C 中常用的一种行为设计模式&#xff0c;它能在运行时改变对象的行为。在策略模式中&#xff0c;一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为模式。 在策略模式中&…

每日一题 礼物的最大价值

题目描述 礼物的最大价值_牛客题霸_牛客网 解题思路 这是一个典型的动态规划问题。我们可以使用一个二维数组 dp[][] 来存储到达每个格子时可以获得的最大价值。状态转移方程为 dp[i][j] max(dp[i-1][j], dp[i][j-1]) grid[i][j]&#xff0c;表示到达当前格子的最大价值是从…

Windows PC上从零开始部署ChatGML-6B-int4量化模型

引言 ChatGLM-6B是清华大学知识工程和数据挖掘小组&#xff08;Knowledge Engineering Group (KEG) & Data Mining at Tsinghua University&#xff09;发布的一个开源的对话机器人。6B表示这是ChatGLM模型的60亿参数的小规模版本&#xff0c;约60亿参数。 ChatGML-6B-in…

面试 Java 基础八股文十问十答第三十一期

面试 Java 基础八股文十问十答第三十一期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;TreeMap 有了解过吗…

knife4j-openapi3 使用笔记

knife4j官方文档&#xff1a;https://doc.xiaominfo.com/docs/quick-start/start-knife4j-version 增强模式文档&#xff1a;https://doc.xiaominfo.com/docs/features/enhance 1.用法 &#xff08;1&#xff09;控制层 Tag(name “用户信息管理”) Tag(name "用户信…

代码随想录第四十六天|单词拆分

题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09;

STM32F4xx开发学习_SysTick

SysTick系统定时器 SysTick属于CM4内核外设&#xff0c;有关寄存器的定义和部分库函数都在core_cm4.h这个头文件中实现&#xff0c;可用于操作系统&#xff0c;提供必要的时钟节拍 SysTick简介 SysTick是一个 24 位向下定时器&#xff0c;属于CM4内核中的一个外设&#xff0c;…

无卤素产品是什么?有什么作用?

无卤素产品&#xff0c;即在生产过程中完全不使用卤素元素——氟、氯、溴、碘等——的产品。 卤素元素&#xff0c;虽然在电子设备、材料等领域应用广泛&#xff0c;却也可能潜藏危害。其阻燃剂&#xff0c;一旦在产品生命周期结束后释放&#xff0c;将对土壤和水体造成污染&a…