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

news2025/1/20 14:53:57

在上一章中罗列了对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 PyPDFLoader

loader = 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 PDFMinerLoader

loader = 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 RecursiveCharacterTextSplitter


text = '''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 SentenceTransformer

model = 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.coloradmin.cn/o/1880727.html

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

相关文章

Workerman在线客服系统源码,附搭建教程

源码介绍: Workerman在线客服系统源码。 workerman是一个高性能的PHP socket 服务器框架,workerman基于PHP多进程以及libevent事件轮询库,PHP开发者只要实现一两个接口,便可以开发出自己的网络应用,例如Rpc服务、聊天…

基于Java的会员制医疗预约服务管理信息系统

你好呀,我是计算机学姐码农小野!如果有相关需求,可以私信联系我。 开发语言:Java 数据库:MySQL 技术:Java技术ssm框架,结合JSPM工作流引擎 工具:IDEA/Eclipse、Navicat、Maven …

每日一题——Python实现PAT乙级1090 危险品装箱(举一反三+思想解读+逐步优化)4千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 题目链接:https://pintia.cn/problem-sets/994805260223102976/exam/problems/typ…

2024 vue3入门教程:windows系统下部署node环境

一、打开下载的node官网 Node.js — 下载 Node.js 二、根据个人喜好的下载方法,下载到自己的电脑盘符下 三、我用的是方法3下载的压缩包,解压到E盘nodejs目录下(看个人) 四、配置电脑的环境变量,新建环境变量的时候…

树莓派4B学习笔记17:RBG_LED全色域灯的驱动模块编写

今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi) 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: ​ Python 版本3.7.3: ​ 今日学习:RBG_LED全色域灯的驱动模块编写…

初学者轻松搞定19个经典的Python程序以及代码演示

Python的经典程序展示了Python语言基本特性和功能的简单示例,这些程序在学习和理解Python编程语言的过程中起着至关重要的作用. 一些常见的经典Python程序及其在学习Python时的功能: 1.Hello, World! print("Hello, World!")解释:这是Python的基本输出…

(单机架设教程)凯旋|当年的QQ游戏

前言 今天给大家带来一款单机游戏的架设:凯旋 , 当年的QQ游戏 如今市面上的资源参差不齐,大部分的都不能运行,本人亲自测试,运行视频如下: 凯旋单机 搭建教程 此游戏架设需要安装虚拟机, 没有…

【面试系列】Python 高频面试题

欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、…

基于源码详解ThreadPoolExecutor实现原理

个人博客地址 基于源码详解ThreadPoolExecutor实现原理 | iwts’s blog 内容拆分 这里算是一个总集,内容太多,拆分成几个比较重要的小的模块: ThreadPoolExecutor基于ctl变量的声明周期管理 | iwts’s blog ThreadPoolExecutor 工作线程…

【PL理论深化】(13) 变量与环境:文法结构 | 真假表达式:isZero E | let 表达式叠放 | 定义的规则 | 条件语句的使用

💬 写在前面:从现在开始,让我们正式设计和实现编程语言。首先,让我们扩展在之前定义的整数表达式语言,以便可以使用变量和条件表达式。 目录 0x00 文法结构 0x01 真假表达式:isZero E 0x02 let 表达式叠…

1-5题查询 - 高频 SQL 50 题基础版

目录 1. 相关知识点2. 例题2.1.可回收且低脂的产品2.2.寻找用户推荐人2.3.大的国家2.4. 文章浏览 I2.5. 无效的推文 1. 相关知识点 sql判断,不包含null,判断不出来distinct是通过查询的结果来去除重复记录ASC升序计算字符长度 CHAR_LENGTH() 或 LENGTH(…

LeetCode-Leetcode 1120:子树的最大平均值

LeetCode-Leetcode 1120:子树的最大平均值 题目描述:解题思路一:递归解题思路二:0解题思路三:0 题目描述: 给你一棵二叉树的根节点 root,找出这棵树的 每一棵 子树的 平均值 中的 最大 值。 子…

文章浮现之单细胞VDJ的柱状图

应各位老师的需求复现一篇文章的中的某个图 具体复现图5的整个思路图,这里没有原始数据,所以我使用虚拟生产的metadata进行画图 不废话直接上代码,先上python的代码的结果图 import matplotlib.pyplot as plt import numpy as np# 数据&#…

RocketMQ常用基本操作

文章中的rabbitmq使用的是rocketmq-all-5.1.3-bin-release版本,需要安装包的可自行下载 RockerMQ启动停止命令 启动命令 nohup sh bin/mqnamesrv & nohup sh bin/mqbroker -n localhost:9876 --enable-proxy & 查看日志 tail -f ~/logs/rocketmqlogs/…

sqlmap注入详解

免责声明:本文仅做分享... 目录 1.介绍 2.特点 3.下载 4.帮助文档 5.常见命令 指定目标 请求 HTTP cookie头 HTTP User-Agent头 HTTP协议的证书认证 HTTP(S)代理 HTTP请求延迟 设定超时时间 设定重试超时 设定随机改变的参数值 利用正则过滤目标网址 避免过多的…

江大白 | 何凯明入职 MIT,首次带队提出Diffusion Loss,扩散模型思想提升生成速度和效果 !

本文来源公众号“江大白”,仅用于学术分享,侵权删,干货满满。 原文链接:何凯明入职 MIT,首次带队提出Diffusion Loss,扩散模型思想提升生成速度和效果 ! 导读 在图像生成领域中,作…

在数字化转型中,中小企业如何打造数字化产品和服务?

引言:随着社会的发展和消费者行为的变化,市场对数字化产品和服务的需求日益增长。中小企业需要紧跟这一趋势,通过开发数字化产品和服务来满足消费者的新需求。云计算、大数据、人工智能等先进技术的出现,为中小企业提供了更多的机…

志愿填报指南:为什么我强烈建议你报考计算机专业

首先恭喜2024届高考的同学们,你们已经通过了高考的考验,即将进入人生的新阶段——大学。 现在正是高考完填报志愿的时刻,Left听到身边朋友提到报考志愿的诸多问题: 志愿填报怎么填?我要报考什么专业?这个…

基于线调频小波变换的非平稳信号分析方法(MATLAB)

信号处理领域学者为了改进小波变换在各时频区间能量聚集性不高的缺点,有学者在小波分析基础上引入调频算子构成了线性调频小波变换,线调频小波一方面继承了小波变换的理论完善性,另一方面用一个新的参数(线调频参数)刻…

ONLYOFFICE 桌面编辑器 8.1 现已发布:功能完善的 PDF 编辑器、幻灯片版式、改进从右至左显示、新的本地化选项等

继 ONLYOFFICE 文档 8.1 发布后,适用于 Linux、Windows 和 macOS 的 ONLYOFFICE 桌面应用程序最新版本也已推出。它具有在线套件的最主要功能,例如功能齐全的 PDF 编辑器、演示文稿中的幻灯片版式、改进的 RTL 支持、新的本地化选项等。 点击进入ONLYOF…