检索增强生成RAG系列3--RAG优化之文档处理

在上一章中罗列了对RAG准确度的几个重要关键点,主要包括2方面,这一章就针对其中一方面,来做详细的讲解以及其解决方案。

目录

  • 1 文档解析
    • 1.1 文档解析工具
    • 1.2 实战经验
    • 1.3 代码演示
  • 2 文档分块
    • 2.1 分块算法
    • 2.2 实战经验
    • 2.3 代码演示
  • 3 文档embedding
    • 3.1 实战经验
    • 3.2 代码演示
  • 4 向量数据库

在这里插入图片描述

1 文档解析

对于我们来说,查询的文档可能包括pdf、Excel、图片等等各种各样的内容,而这些内容中包括很多不同的格式,比如pdf中可能还包括表格和图片,因此选择哪些文档解析工具非常重要。

1.1 文档解析工具

在市面上有很多各种各样的解析工具,不同解析工具有着不同的解析方法,在langchain中已经集成了很多针对不同类型文档的解析,可供大家选择。(注意:其中有(非)标志代表没有集成在langchain中,另外还有其它的就没有一一罗列了)

文档类型解析工具
txtTextLoader、unstructured
pdfunstructured、pypdf、pdfplumer、pdfminer、Camelot(非)、pymupdf(非)、LlamaParse (非)
wordunstructured
PPTunstructured
htmlunstructured、Firecrawl(非)
jsonJSONLoader
图片unstructured
ExcelCSVLoader
markdownunstructured
emailunstructured
arxivArxivLoader

1.2 实战经验

其实不同文件的读取无非就是想看看能否很好的将文件内容解析出来,而解析的好与坏其实跟你要解析的文档息息相关。以下是常见几个考察点:

  • 文本不丢失
  • 语言支持情况(中文、英文等)
  • 段落信息是否能解析(段落其实是一个很重要却容易被忽视的考察点,因为模型是理解文本,因此段落有助于更好的理解文档的语义)
  • 标题是否能识别
  • 表格,图片信息能否读取
  • 针对不同文档类型的细节(比如pdf的页头、页尾、分栏等细节)

下面以pdf为例,简述一下pdf各类工具的优缺点:

工具语言支持段落信息表格图片细节
pypdf英文支持较好段落支持一般表格图片差分栏
pdfplumer中文支持好段落支持好表格读取好分栏效果不好
pdfminer中文支持好段落支持好表格图片一般分栏
pymupdf(非)中午支持较好段落支持好表格图片一般分栏
Camelot(非)中文支持好段落支持一般表格图片好分栏
LlamaParse (非)中午支持较好段落支持好表格图片好分栏

1.3 代码演示

以下以PyPDF和PDFMiner为例子给大家演示一下代码

# 1 PyPDF演示,安装pypdf:pip install pypdf
from langchain.document_loaders import PyPDFLoaderloader = PyPDFLoader("data/demo.pdf")
pages = loader.load()
# 读取后pages长度=6,说明按照每一页读取的
print(len(pages))
# 段落,我们可以看到其中文本:ABSTRACT 的前面的换行与其它换行并不没有不同,因此识别段落一般
print(pages[0].page_content[300:400])
# 表格,之所以选取第4页,是该页有表格,可以看到表格内容已经被解析出来
print(pages[3].page_content)
# 图片,之所以选取第3页,是该页有图片(可以看到图片不能解析,需要配合rapidocr-onnxruntime,后续有例子)
print(pages[2].page_content)
# 读取图片,需要安装rapidocr-onnxruntime:pip install rapidocr-onnxruntime
loader = PyPDFLoader("data/demo.pdf", extract_images=True)
pages = loader.load()
print(pages[2].page_content)# 2 演示PDFMiner,提前安装pdfminer.six:pip install pdfminer.six
from langchain.document_loaders import PDFMinerLoaderloader = PDFMinerLoader("data/demo.pdf")
pages = loader.load()
# 读取后pages长度=1,说明将文档按照一页读取
print(len(pages))
# 段落,我们可以看到其中文本:ABSTRACT 的前面的换行与其它换行不同,因此很好的识别段落
print(pages[0].page_content[400:600])
# 表格和图片,我们可以找一些大概第3页的图片能够被解析出来,第4页表格内容也能够被解析出来
print(pages[0].page_content)

从上面代码可以看出,我们分析了几个要素

  • 段落:很明显PDFMiner能够识别更好的段落,即大标题这些可以有2个换行符合,而PyPDF并不能
  • 图片和表格:PyPDF增加插件也能识别
  • 分页:为什么要列分页,因为这样省的你去讲每一页都手动连接在一起
  • 换行:这里还有一个细节,就是事实上pdf中的换行并不是内容真正换行,大家可以尝试一下unstructured,实际上会将不是真正换行的拼接在一起,这样可以让模型理解语义更加容易

因此对于不同文件类型,选型非常重要,特别是对你的业务文档类型以及文档中包含的内容,都是一种考察需求。

2 文档分块

前文已经讲了之所以要分块是因为2个原因:

  • 因为大模型有token长度限制
  • 过长的token其实对于大模型的理解和推理会变慢或者不准确。

那么文档分块如何分,怎么分才是最佳的,下面通过2个方面讲述一下

2.1 分块算法

文本分块的的分割流程如下:

  • 1)将文本拆分为小的、语义上有意义的块(通常是句子)。
  • 2)将这些小块组合成较大的块,直到达到某个大小(即你设定的大小,通常有一个函数测量)。
  • 3)一旦达到该大小,将该块作为自己的文本片段,然后开始创建一个具有一定重叠的新文本块(以保持块之间的上下文)。

分块流程如上,那么这意味着有3个方面将影响你的分块:

  • 文本如何拆分
  • 块大小如何测量
  • 重叠块大小

下表就是按照不同分块逻辑,罗列出目前的分块算法,其应用也在描述中体现出来,大家可以按照自身文档的内容选取不同的分块算法:

方法分块符号是否加入metadata描述
Character用户自定义字符基于用户定义的字符拆分文本。一种更简单的方法。
Recursive用户自定义字符的列表递归拆分文本。递归分割文本的目的是试图将相关的文本片段保持在一起。这是开始拆分文本的推荐方式。
MarkdownMarkdown 特定字符基于特定于Markdown的字符拆分文本。值得注意的是,这增加了关于该块来自哪里的相关信息(基于降价)
HTMLHTML特定字符基于特定于HTML的字符拆分文本。值得注意的是,这添加了关于该块来自何处的相关信息(基于HTML)
CodeCode (Python, JS) 特定字符基于特定于编码语言的字符拆分文本。有15种不同的语言可供选择。
TokenTokens拆分令牌上的文本。有几种不同的方法来衡量token。
Semantic Chunker句子或语义首先对句子进行拆分。然后,如果它们在语义上足够相似,就将它们组合在一起。

2.2 实战经验

如何选择符合自身业务的分块,这里有一些准则可供大家参考:

  • 分块算法:不同业务类型,对应不同的分块算法,在2.1中的表格可以供大家选择。
  • 块的大小:这一部分可能没有统一标准说到底多大合适,一般都需要通过实验验证。比如你使用什么模型,其token限制是多少,然后在此基础上,可以按照128、256、512、1024等等不同分块方式去测试比如:响应时间、正确率、关联度等指标。
  • 重叠部分:至于需要重叠多少,这个也是没有统一标准,按照块的大小的方式去测试。

2.3 代码演示

下面例子,选择Recursive分块算法,来说明一下分块时需要注意的问题:

from langchain.text_splitter import RecursiveCharacterTextSplittertext = '''ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型,基于 General Language Model (GLM) 架构,具有 62 亿参数。结合模型量化技术,用户可以在消费级的显卡上进行本地部署(INT4 量化级别下最低只需 6GB 显存)。 ChatGLM-6B 使用了和 ChatGPT 相似的技术,针对中文问答和对话进行了优化。经过约 1T 标识符的中英双语训练,辅以监督微调、反馈自助、人类反馈强化学习等技术的加持,62 亿参数的 ChatGLM-6B 已经能生成相当符合人类偏好的回答,更多信息请参考我们的博客。欢迎通过 chatglm.cn 体验更大规模的 ChatGLM 模型。为了方便下游开发者针对自己的应用场景定制模型,我们同时实现了基于 P-Tuning v2 的高效参数微调方法 (使用指南) ,INT4 量化级别下最低只需 7GB 显存即可启动微调。ChatGLM-6B 权重对学术研究完全开放,在填写问卷进行登记后亦允许免费商业使用。'''# 1.按照300进行分割
text_splitter = RecursiveCharacterTextSplitter(chunk_size=300,chunk_overlap=0,length_function=len
)
chunks = text_splitter.split_text(text)
print(len(chunks))
# 我们可以看到其分割为3段,长度分别是10、292、136.其实他默认的分隔符就是换行
for chunk in chunks:print(len(chunk))# 2.按照200进行分割
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200,chunk_overlap=0,length_function=len
)
chunks = text_splitter.split_text(text)
print(len(chunks))
# 我们可以看到其分割为4段,长度分别是10、169、122、136。其中第二段被分了2段。因为原来第二段不符合200.
for chunk in chunks:print(len(chunk))
# 但是这里有个问题,可以打印第二段,发现其分割有点随机,把我们有语义关联的一句话分开掉。
print(chunks[1])# 3.解决分割不符合语义,通过separators传入分割符合,会按照分割符合顺序进行分割
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200,chunk_overlap=0,length_function=len,separators=["\n", "。", ","],keep_separator=True
)
chunks = text_splitter.split_text(text)
print(len(chunks))
# 我们可以看到其分割为4段,长度分别是10、162、130、136。
for chunk in chunks:print(len(chunk))
# 这里就解决了分割
print(chunks[1])

从上面代码可以看到

  • 分块的逻辑:是先按照一个大的分隔符进行分割,默认是换行符,然后再将较小的合并到符合长度之下。也就是前面所说的分割流程
  • 分块分隔符:当默认的时,有可能没有次级的分块分隔符,那么会随机切断,所以使用separators参数传入,可以更好分割

3 文档embedding

文档最终是通过embedding向量化后存入向量数据库,而选择不同embedding对于最终查询来说非常重要(这里大家可以自行补一下embedding基础,这里就不讲embedding原理)。如果embedding做得不好,导致查询的结果与答案不一致,那么如何选对embedding也是对于RAG的准确度至关重要。
这里需要注意的是embedding也是一个训练好的模型,目前大部分开源的embedding模型都是使用Sentence Bert。这里不详细讲述,大家有兴趣可以去了解其论文。这个主要给大家讲述几个重要选择embedding模型的实战参考。

3.1 实战经验

对于我们如何选择一个embedding,我根据一些实战中的经验总结如下:

  • 排行榜:首先要知道有哪些embedding模型,一般我们去hugging face的排行版上找:https://huggingface.co/spaces/mteb/leaderboard
  • 语言:在排行榜中,你可以根据你的业务选择语言
  • embedding维度:这个需要根据你的业务,如果你业务语义丰富,那么选择维度更高更好,如果语义不丰富,其实选择维度更低会更好。
  • sequence length:不同embedding模型支持不同sequence length,因此需要根据你的业务选择不同的模型
  • 模型大小:这个会根据实际你有多少资源作为选择标准
  • 业务效果:以上几个标准可能只是基本维度,最终还是需要看看业务效果,因此你可能需要选择几个比较合适的模型,然后再逐一的去测试一下其效果相对于你的业务结果如何

3.2 代码演示

下面通过m3e-base模型加上TSNE画出相关性,可以直观感受到你的模型是否准确理解句子的语义。
注明:本例子来自大神的demo代码:https://github.com/blackinkkkxi/RAG_langchain/blob/main/learn/embedding_model/embedd.ipynb)

import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
from sentence_transformers import SentenceTransformermodel = SentenceTransformer('moka-ai/m3e-base')# 测试句子
sentences =['为什么良好的睡眠对健康至关重要?' ,'良好的睡眠有助于身体修复自身,增强免疫系统','在监督学习中,算法经常需要大量的标记数据来进行有效学习','睡眠不足可能导致长期健康问题,如心脏病和糖尿病','这种学习方法依赖于数据质量和数量','它帮助维持正常的新陈代谢和体重控制','睡眠对儿童和青少年的大脑发育和成长尤为重要','良好的睡眠有助于提高日间的工作效率和注意力','监督学习的成功取决于特征选择和算法的选择','量子计算机的发展仍处于早期阶段,面临技术和物理挑战','量子计算机与传统计算机不同,后者使用二进制位进行计算','机器学习使我睡不着觉',
]
# 通过调用model的encode进行embedding
embeddings = model.encode(sentences)
len(embeddings)
len(embeddings[0])
# 通过TSNE工具画出图表来直观看看相关性
tsne = TSNE(n_components=2 ,  perplexity=5)
embeddings_2d = tsne.fit_transform(embeddings)
plt.rcParams['font.sans-serif'] = ['Kaitt', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False
color_list = ['black'] * len(embeddings_2d[1:])
color_list.insert(0, 'red')
plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1] , color=color_list )
for i in range(len(embeddings_2d)):plt.text(embeddings_2d[:,0][i], embeddings_2d[:,1][i]+2,  sentences[i] ,color=color_list[i] )# 显示图表
plt.show()

4 向量数据库

向量数据库需要存储向量化后的数据,然后通过问题查询相似度,得到最终相关的几个答案,扔给大模型进行回答。那么向量数据库的存储和相似度查询就可能会影响RAG最终的结果,因此,我们选择哪一种向量数据库,对于我们来说还是比较重要的。(注意:这里可能只从影响RAG准确度来说,真正选择向量数据库可能要考虑的方面还是比较多的,这里就不列举了

向量数据库中,相似度是我们最看重的指标之一,因为它对于RAG最终的准确度有着很大的影响。以下表格列出几种不同相似度算法及其应用场景(注意也还有不同的算法,这里就不再累述,只是作为说明相似度)

算法原理应用场景对应数据库
欧氏距离欧几里得空间中,两个点之间的最短直线距离机器学习:欧氏距离常用于 k 最近邻算法 (KNN) 中,计算样本之间的距离。图像检索:欧氏距离常用于图像检索中,计算图像之间的相似度。推荐系统:欧氏距离常用于推荐系统中,计算用户之间的相似度Chroma、Milvus、Faiss
余弦相似度在向量空间中,两个向量夹角的余弦值文本检索:余弦相似度常用于文本检索中,计算文本之间的相似度。自然语言处理:余弦相似度常用于自然语言处理中,计算词语之间的相似度。信息检索:余弦相似度常用于信息检索中,计算文档之间的相似度Chroma、Milvus
点积相似度两个向量的点积除以它们的模长的乘积文本检索:点积相似度常用于文本检索中,计算文本之间的相似度。自然语言处理:点积相似度常用于自然语言处理中,计算词语之间的相似度。推荐系统:点积相似度常用于推荐系统中,计算用户之间的相似度Chroma、Milvus 、Faiss
曼哈顿距离两个向量对应分量差的绝对值的总和图像处理:曼哈顿距离常用于图像处理中,计算图像之间的差异。机器学习:曼哈顿距离常用于机器学习中,计算样本之间的距离。数据挖掘:曼哈顿距离常用于数据挖掘中,计算数据点之间的相似度Faiss

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

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

相关文章

python自动化办公之shutil

目录 1复制文件,此时存在2份相同文件 2移动文件,此时仅有1份文件 3删除文件,此时0份文件 用到的库:shutil,os 实现的效果:复制文件,移动文件,删除文件 代码: 1复制…

使用Colly库进行高效的网络爬虫开发

引言 随着互联网技术的飞速发展,网络数据已成为信息获取的重要来源。网络爬虫作为自动获取网页内容的工具,在数据分析、市场研究、信息聚合等领域发挥着重要作用。本文将介绍如何使用Go语言中的Colly库来开发高效的网络爬虫。 什么是Colly库&#xff1…

超声波清洗机怎么选?极力推荐四款口碑大牌超声波清洗机

相信大家都知道超声波清洗机,每次眼镜脏的时候,去眼镜店里让老板帮忙清洗,她们用的就是超声波清洗机,通过超声波的原理深入物品深处清洁,清洁效果非常好。相对手洗的方式,超声波清洗机能够保护镜片在清洗过…

【开放词汇分割】Side Adapter Network for Open-Vocabulary Semantic Segmentation

论文链接:Side Adapter Network for Open-Vocabulary Semantic Segmentation 代码链接:https://github.com/MendelXu/SAN 作者:Mengde Xu,Zheng Zhang,Fangyun Wei,Han Hu,Xiang Bai 发表单位:华中科技大学、微软亚洲研究院 会…

Vue 快速入门案例

步骤一&#xff1a;引入vue.js文件 添加<script>标签并标明路径 步骤二&#xff1a;定义Vue对象 el Vue接管区域 data 定义数据模型 步骤三&#xff1a;编写视图层的展示 v-model 绑定数据模型 {{要展示的数据模型}} 运行效果 总结 文本框里的值&a…

雪花算法的原理以及实现

文章目录 一、简介二、算法优缺点三、算法实现 一、简介 有这么一种说法&#xff0c;自然界中并不存在两片完全一样的雪花的。每一片雪花都拥有自己漂亮独特的形状、独一无二。雪花算法也表示生成的ID如雪花般独一无二。 雪花算法 &#xff08;SnowFlake &#xff09;算法&am…

几度互联网站群管理系统全媒体解决方案

随着高考的结束&#xff0c;各高校开启了紧张的招生宣传工作&#xff0c;几度互联网站群系统助力各高校招生宣传。 学校官方网站是互联网时代学校对外交流的重要途径和信息公开的主要载体&#xff0c;是展示学校形象、密切联系师生的重要窗口&#xff0c;是加强校园宣传思想工…

【MySQL备份】Percona XtraBackup篇

目录 1.关于Percona XtraBackup 2. Percona XtraBackup有哪些特点&#xff1f; 3.安装Percona XtraBackup 3.1.环境信息 3.2.安装步骤 4.实战演练 4.1.全量备份与恢复 4.2.总结 1.关于Percona XtraBackup Percona XtraBackup是世界上唯一的开源、免费的MySQL热备份 为…

品牌推广方案怎么写?策划书模板与实战技巧分享

品牌想要快速得到市场的认可&#xff0c;一个精心策划的品牌推广方案是脱颖而出的关键。 作为一名手工酸奶品牌创始人&#xff0c;目前全国也复制了100多家门店&#xff0c;这篇文章&#xff0c;我和大家分享下&#xff0c;如何做一个清晰的结构框架、策划书模板以及实战技巧&…

【论文阅读】-- TimeNotes:时间序列数据的有效图表可视化和交互技术研究

TimeNotes: A Study on Effective Chart Visualization and Interaction Techniques for Time-Series Data 摘要1 介绍和动机2 文献2.1 时间序列数据探索2.1.1 数据聚合2.1.2 基于透镜2.1.3 基于布局 3 任务和设计3.1 数据3.2 领域表征3.3 探索、分析和呈现 4 TimeNotes4.1 布局…

Kaggle竞赛——房价预测

目录 1. 特征分析1.1 数据集导入1.2 统计缺失值1.3 可视化缺失值1.4 缺失值相关性分析1.5 训练集和测试集缺失数据对比1.6 统计特征的数据类型1.7 数值型特征分布直方图1.8 数值型特征与房价的线性关系1.9 非数值型特征的分布直方图1.10 非数值型特征箱线图1.11 数值型特征填充…

JAVA:常用的算法指南

请关注微信公众号&#xff1a;拾荒的小海螺 博客地址&#xff1a;http://lsk-ww.cn/ 1、简述 在软件开发过程中&#xff0c;算法扮演着关键的角色。它们用于解决各种问题&#xff0c;从数据处理到搜索、排序等。本文将介绍几种常见的算法及其 Java 实现&#xff0c;包括排序算…

基于java+springboot+vue实现的农产品直卖平台(文末源码+Lw)266

摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#xff0c;让整个世界都可以即时通…

Python从0到100(三十三):xpath和lxml类库

1. 为什么要学习xpath和lxml lxml是一款高性能的 Python HTML/XML 解析器&#xff0c;我们可以利用XPath&#xff0c;来快速的定位特定元素以及获取节点信息 2. 什么是xpath XPath&#xff0c;全称为XML Path Language&#xff0c;是一种用于在XML文档中进行导航和数据提取的…

Python基础之多进程

文章目录 1 多进程1.1 简介1.2 Linux下多进程1.3 multiprocessing1.4 Pool1.5 进程间通信1.6 分布式进程 1 多进程 1.1 简介 要让Python程序实现多进程&#xff08;multiprocessing&#xff09;&#xff0c;我们先了解操作系统的相关知识。 Unix/Linux操作系统提供了一个fork…

豆包文科成绩超了一本线,为什么理科不行?

卡奥斯智能交互引擎是卡奥斯基于海尔近40年工业生产经验积累和卡奥斯7年工业互联网平台建设的最佳实践&#xff0c;基于大语言模型和RAG技术&#xff0c;集合海量工业领域生态资源方优质产品和知识服务&#xff0c;旨在通过智能搜索、连续交互&#xff0c;实时生成个性化的内容…

Java - 程序员面试笔记记录 实现 - Part2

2.1 输入输出流 流可以被看作一组有序的字节集合&#xff0c;即数据在两个设备间的传输。 字节流&#xff1a;以字节作为单位&#xff0c;读到一个字节就返回一个字节&#xff1b;InputStream & OutputStream。 字符流&#xff1a;使用字节流读到一个到多个字节先查询码…

基于RabbitMQ的异步消息传递:发送与消费

引言 RabbitMQ是一个流行的开源消息代理&#xff0c;用于在分布式系统中实现异步消息传递。它基于Erlang语言编写&#xff0c;具有高可用性和可伸缩性。在本文中&#xff0c;我们将探讨如何在Python中使用RabbitMQ进行消息发送和消费。 安装RabbitMQ 在 Ubuntu 上安装 Rabbi…

提升写作效率:探索AI在现代办公自动化中的应用

工欲善其事&#xff0c;必先利其器。 随着AI技术与各个行业或细分场景的深度融合&#xff0c;日常工作可使用的AI工具呈现出井喷式发展的趋势&#xff0c;AI工具的类别也从最初的AI文本生成、AI绘画工具&#xff0c;逐渐扩展到AI思维导图工具、AI流程图工具、AI生成PPT工具、AI…

ubuntu 系统中 使用docker 制作 Windows 系统,从此告别 vmware虚拟机

我的系统是 ubuntu 24 前期准备工作&#xff1a; 安装dockerdocker pull 或者 手动制作镜像 docker build 的话 必须要 科学上网&#xff0c; 好像阿里镜像都下不下来。需要 知道 docker 和docker compose 命令的使用方式 我是给docker 挂了 http代理 如果你能pull下来镜像 …