#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, CharacterTextSplitter

chunk_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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vscode 实现代码跳转功能

随笔 目录 1. 安装Python 2. 安装Pylance 3. 选择解释器 1. 安装Python 2. 安装Pylance 3. 选择解释器 到此即可实现跳转功能

Pytorch学习笔记——神经网络基本框架

一、神经网络是什么 神经网络在人工智能和深度学习的领域&#xff0c;一般称为人工神经网络&#xff0c;即ANN(Artificial Neural Network)&#xff0c;是一种模仿人脑神经系统工作方式的计算模型。被广泛应用于人工智能、自动控制、机器人、统计学等领域的信息处理中。 二、…

图像处理之PCA(C++)

图像处理之PCA&#xff08;C&#xff09; 文章目录 图像处理之PCA&#xff08;C&#xff09;前言一、PCA原理1.原理思想2.实现步骤 二、代码实现总结 前言 在科研、工程应用中&#xff0c;我们往往所获取的数据都包含着很多冗余的信息&#xff0c;这些冗余的信息会对我们分析数…

CP AUTOSAR之CANXLDriver详细说明(正在更新中)

本文遵循autosar标准&#xff1a;R22-11 1 简介及功能概述 本规范描述了AUTOSAR 基础软件模块CAN XL 驱动程序的功能、API和配置。   本文档的基础是[1,CiA610-1]和[2,CiA611-1]。假设读者熟悉这些规范。本文档不会再次描述CAN XL 功能。   CAN XL 驱动程序是最低层的一部…

【七十六】【算法分析与设计】2435. 矩阵中和能被 K 整除的路径,87. 扰乱字符串,三维动态规划

2435. 矩阵中和能被 K 整除的路径 给你一个下标从 0 开始的 m x n 整数矩阵 grid 和一个整数 k 。你从起点 (0, 0) 出发&#xff0c;每一步只能往 下 或者往 右 &#xff0c;你想要到达终点 (m - 1, n - 1) 。 请你返回路径和能被 k 整除的路径数目&#xff0c;由于答案可能很大…

【Python爬虫实战入门】:全球天气信息爬取

文章目录 一、爬取需求二、所需第三方库2.1 简介 三、实战案例四、完整代码 一、爬取需求 目标网站&#xff1a;http://www.weather.com.cn/textFC/hb.shtml 需求&#xff1a;爬取全国的天气&#xff08;获取城市以及最低气温&#xff09; 目标url&#xff1a;http://www.weath…

数字孪生技术在垃圾焚烧处理中的可视化应用

在迈向智慧城市的进程中&#xff0c;数字孪生技术在垃圾处理领域展现出了巨大潜力。特别是在垃圾焚烧过程的管理和优化上&#xff0c;数字孪生垃圾焚烧可视化技术已成为一项革命性的进步。 通过 HT 构建虚拟的垃圾焚烧模型&#xff0c;实时映射和模拟实际焚烧过程中的各项关键…

QT+网络调试助手+TCP服务器

一、UI界面设计 二、单线程 代码设计 1、 查找合法的本地地址&#xff0c;用于当作服务器的IP地址 #include <QThread> #include <QTcpSocket> #include <QNetworkInterface> #include <QMessageBox>QList<QHostAddress> ipAddressesList QNe…

抖音短视频矩阵系统技术源头/源代码开发部署/SaaS贴牌/源码api代开发

抖音短视频矩阵系统技术源头/源代码开发部署/SaaS贴牌/源码官方平台api授权代开发 一、短视频矩阵系统源码开发步骤 短视频矩阵系统的源头开发步骤通常包括以下几个关键阶段&#xff1a; 1.需求分析&#xff1a;明确系统的目标用户、功能需求、性能要求等。 2.系统设计&…

bite阶段性测试_数据结构

解决问题之前我们要了解什么是度&#xff0c;特别是二叉树中的度&#xff0c;和图论中的度的定义是不同的 什么是度&#xff1a; 在图论中&#xff0c;一个节点&#xff08;或称为顶点&#xff09;的“度”是指与该节点直接相连的边的数量。度是用来衡量一个节点与其他节点连接…

Python:实现b站登录并保存登录信息(baidu Comate插件帮助我逐行分析代码)

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

O2OA(翱途)支持高斯_openGauss,瀚高_HighGo,磐维_panweidb等各种国产postgres分支数据库接入

O2OA&#xff08;翱途&#xff09;作为一款企业级应用平台&#xff0c;其支持多种数据库系统是其灵活性和可扩展性的重要体现。从MySQL、Oracle到国产的达梦、神州等数据库&#xff0c;再到对PostgreSQL的原生支持&#xff0c;O2OA展现了其对不同数据库环境的良好适应性。特别地…

LeetCode 难题解析 —— 正则表达式匹配 (动态规划)

10. 正则表达式匹配 思路解析 这道题虽然看起来不难理解&#xff0c;但却存在多种可能&#xff0c;当然这种可能的数量是有限的&#xff0c;且其规律对于每一次判别都使用&#xff0c;所以自然而然就想到用 动态规划 的方法啦 接下来逐步分析可能的情况&#xff1a; &#x…

stm32f103zet6_DAC_2_输出电压

实现效果 DAC输出的电压 同过电压表测量电压 1.DAC配置的步骤 初始化DAC时钟。配置DAC的GPIO端口。设置DAC的工作模式&#xff08;例如&#xff0c;是否使用触发功能&#xff0c;是否启用DAC中断等&#xff09;。启动DAC。 2常用的函数 函数 HAL_DAC_Start() - 开启指定…

企业终端安全管理软件有哪些?终端安全管理软件哪个好?

终端安全的重要性大家众所周知&#xff0c;关系到生死存亡的东西。 各类终端安全管理软件应运而生&#xff0c;为企业提供全方位、多层次的终端防护。 有哪些企业终端安全管理软件&#xff1f; 一、主流企业终端安全管理软件 1. 域智盾 域智盾是一款专为企业打造的全面终端…

淘宝商品搜索API:关键字搜索返回值详解与利用

在当今电子商务蓬勃发展的时代&#xff0c;淘宝作为中国最大的在线购物平台之一&#xff0c;拥有海量的商品信息和用户数据。为了更好地满足商家和开发者的需求&#xff0c;淘宝提供了商品搜索API&#xff0c;允许通过关键字搜索来获取商品信息。本文将详细解析淘宝商品搜索API…

LeetCode 每日一题 Day 144-157

2385. 感染二叉树需要的总时间 给你一棵二叉树的根节点 root &#xff0c;二叉树中节点的值 互不相同 。另给你一个整数 start 。在第 0 分钟&#xff0c;感染 将会从值为 start 的节点开始爆发。 每分钟&#xff0c;如果节点满足以下全部条件&#xff0c;就会被感染&#xf…

抖音小店怎么快速出体验分?分享三种不花一分钱,就能出分的技巧

哈喽~我是电商月月 才做抖音小店&#xff0c;新开的店铺是没有体验分的 没有体验分就没法用猜你喜欢和搜索流量&#xff0c;也没法持续做精选联盟&#xff0c;没体验分店铺就不好出单 于是很多朋友就去网上选择找S分机构&#xff0c;想快速出体验分&#xff0c;但这种方式我…

学习软考----数据库系统工程师24

关系数据库设计基础知识 函数依赖 码 多值依赖 性质

Semi-decentralized Federated Ego Graph Learning for Recommendation

论文概况 本文是2023年WWW的一篇联邦推荐论文&#xff0c;提出了一个半去中心化的联合自我图学习框架。 Introduction 作者提出问题 现有的推荐方法收集所有用户的自我图来组成一个全局图&#xff0c;导致隐私风险。联合推荐系统已被提出来缓解隐私问题&#xff0c;但在客户…