微信扫码
与创始人交个朋友
我要投稿
引言
在最近一些 B 端、G 端的大模型项目中,大家都把 RAG 作为了应用大模型的起点。本篇文章旨在从 RAG 实施流程的中的数据预处理出发,介绍 RAG 数据预处理过程中,尤其是文档分割这一环节的一些细节。
数据采集是 RAG 系统的基础工作。在具体实施过程中,可初步划分为“数据源识别”、“数据治理”、“数据清洗”三个步骤。
1. 数据源识别
包括内部、外部的数据源识别,并与 RAG 应用建立持续更新的机制。
举个栗子,比如某地产公司想设计一个 RAG 系统,实现市场洞察、智能客服、辅助决策三类的场景应用,在识别数据源的过程可能会涉及以下内容:
内部数据:包括公司自有的资产数据库,销售数据库,在建项目管理,CRM 系统,物业管理系统等。
外部数据:政府的房产交易数据库,城市与土地规划信息库,城市交通规划数据库,经济指标,社交媒体数据,竞争对手数据等。
内部系统集成,实现内部系统的数据同步。
外部系统采集:比如建立与政府开发数据平台的 API 连接;订阅专业的地产数据服务;开发数据爬虫程序等。
建立实时的数据推送系统。
另一个重要的步骤是数据治理。这包括数据质量管理,安全与隐私保护,合规性管理,数据周期管理,元数据管理等内容。数据治理也是一个很大的主题,这里不再赘述。
此处的“数据清洗”指开发一套自动化数据清洗的管道,能按照数据治理过程中制定好的规则,实时、自动地处理重复数据、错误数据,能与 RAG 系统及其他业务系统建立良好的数据更新机制。
在 Langchain 中,PDF 等文档数据每一页都会转化为一个 Document 对象,这个对象包含两个属性:page_content 和 metadata.
page_content:从文档页面中提取到的文本内容;
metadata:页面的元数据,包含文档来源,页码,文件类型及其他的信息。LLM 在生成有洞察力的答案时,会依赖元数据追溯其所依赖的具体来源。
大语言模型在进行 Embedding 操作时,有上下文窗口长度的限制(虽然现在一些大模型长下文的长度窗口越来越大)。同时,为了提高检索的准确性和效率,需要对文档进行分块。分块时需要仔细考虑以下问题:
首先需要考虑的是 LLM 对输入上下文窗口大小的限制。
如我们在《高级 RAG 技术》中说过的,Chunk 较小时,生成时上下文的信息较少,但搜索的更精准;Chunk 较大时,比如整页,或者多个段落,甚至一整个文档作为一个 Chunk,块可能会涵盖更多信息,从而通过提供更丰富的上下文来提高生成效果。
Top-K 检索大小:举个例子,假设 LLM 对输入的上下文长度的限制为 10000 Tokens,考虑为用户的 Query 预留 1000 Tokens,为指令提示和聊天记录再预留 2000 Tokens,那么只剩下 7000 Tokens 可以给到检索到的文档信息。如果采用 TOP K=10,即从检索结果中选择前 10 个最相关的结果,那么每个块的大小为 7000/10=700,即每个块的大小约 700 个 token 左右。
尝试在分块大小的可选范围进行探索:选择时应考虑内容的性质(如短消息或长文档)、所用嵌入模型的特性及其能力,目标是找到保留上下文与保持准确性之间的平衡。可以尝试探索多种块大小,包括用于捕捉更细致语义信息的小块(例如,128 或 256 个 tokens)和用于保留更多上下文的大块(例如,512 或 1024 个 tokens)。
性能限制:由于 Transformer 的自注意力机制,太大的上下文长度会带来计算时间和内存的指数级增长。
在 LlamaIndex 发布的示例中,可以看到,随着分块大小的增加,平均响应时间略有上升。有趣的是,平均忠实度似乎在分块大小为 1024 时达到顶峰,而平均相关性则随着分块大小的增大持续提升,同样在 1024 时达到峰值。这表明,分块大小为 1024 可能在响应时间和响应质量(以忠实度和相关性衡量)之间达到一个最佳平衡。
固定大小的分块方法是在每个块中固定 token 数量进行分块,并设置一定数量的重叠部分,以确保上下文的丰富语义在各块间得以完整保留。
固定大小的分块方法逻辑简单,易于实现,无需复杂的算法或条件判断,可以直接按字数、词数或字符数等进行分块;而且块的大小固定,可以提高处理速度和效率,特别是在并行处理或批量操作时,每个块的处理时间大致相同,便于系统的优化和资源的合理分配。
除此之外,固定块的分块方法有助于在后续处理(如检索或编码)时保持一致的输入格式,减少处理时的复杂性。
但固定块的分块方法也存在很明显的问题:如语义完整性不足,若在不恰当的位置(如句子中间、段落中间)进行切割,导致语义不完整。这样会使得某些块的信息在语义上不完整或不连贯,影响检索的准确性和生成结果的质量;对于内容密度高或语义复杂的文本,固定分块可能不适用,容易造成信息的丢失或分块数量过多。
“Context-aware Chunking-上下文感知分块”是一种智能分块方法,在传统 NLP 任务和 RAG 中应用广泛。与固定大小分块不同,Context-aware Chunking 在分块时考虑了文本的语义和上下文信息,以确保每个块保持语义完整性和连贯性。常见的上下文感知分块的方法有以下几种:
常见的比如 Naive splitting,通过标点符号(如句号、问号、感叹号等)来进行分割。这种方法快速且简单, 但可能会在错误的位置进行分割,如在缩写或数字中的句号(如 “Dr. Smith” 或 “3.14”)也会被视为句子终止符,导致分割错误。
NLTK:NLTK 是一个广泛使用的 Python 自然语言处理库,提供了基于规则的句子分割器,如 PunktSentenceTokenizer
。NLTK 使用训练好的模型(如 Punkt 模型)结合上下文来识别句子边界,比 Naive splitting 更智能,能够处理常见的标点符号误分割问题,例如识别缩写中的句号不作为句子终止符,但在复杂的语境中可能仍然会出错。
spaCY:spaCy 是另一个强大的自然语言处理库,其句子分割器利用了依存句法分析和预训练的统计模型来准确识别句子边界。spaCy 可以在句法分析过程中自动确定句子边界,也可以通过标点符号来分割。
由于结合了依存句法分析,spaCy 能够更准确地处理复杂句子结构,尤其在处理带有嵌套从句或其他复杂句法结构时效果较好。特别适用于需要高准确度的句子分割任务,尤其是在处理复杂或非标准文本时表现出色。由于依赖更复杂的模型,spaCy 的计算开销较大;此外,某些语言或特定领域的文本可能需要额外的微调。
总结来说,
Naive splitting 是最简单的基于标点符号的分割方法,但容易出错。
NLTK 提供了更智能的句子分割工具,能处理一些常见的分割问题。
spaCy 使用更复杂的语言模型和句法分析来进行句子分割,通常能提供最准确的分割结果。
递归分块也是一种较高级的分块方法,通过递归的方式将文本逐步分割为较小的块。与普通的固定分块方法不同,递归分块能够更好地捕捉文本的层次结构和语义关联。而且能更灵活地处理各种类型的文本,尤其是那些语义层次复杂的长文档。
由于递归分块可以捕捉到文本的多层次结构,这对于需要细粒度理解和处理的任务非常有用。
其工作原理为:
初始分块:文本首先按照某个策略(如段落、句子)进行初步分块。此步的目标是创建第一层次的分块。
递归分块:对每个初始块,若块长度或复杂性超过了某个预设的阈值,则进一步将其分割为更小的子块。这一步可以基于语义、主题或其他上下文信息来进行。这个分割过程是递归的,可以多次进行,直到每个块都满足预设的条件(如长度合适、语义完整等)。
终止条件:递归分块的过程会根据设定的条件(如最大块长度、最小语义单位)终止。当达到这些条件时,分块过程停止,得到的块将用于后续的处理或模型输入。
LangChain 中的 RecursiveCharacterTextSplitter 类可以执行递归分块。
对于 Markdown 和 LaTeX 这两种常见的结构化文本,可以使用专门化分块(specialized chunking)的方法,可以在拆分过程中保留内容的原始结构。
B 端或 G 端客户的数据通常都以多种形态存在,比如常见的包含了图像、表格、文本内容的 pdf ,以及包含了常见文件格式的文件夹。
每种模态都有其独特的挑战和细微差别。构建多模态 RAG 流程时,必须捕捉并处理这些细微差别。举个例子:
对于以上三幅图,最左侧的丹霞地貌,很难用文字捕捉所有信息, 虽然可以用文字进行描述,但整体的“视觉效果”很难完全用语言表达。在多模态 RAG 中,如果用户查询与此相关的内容,系统可能需要更多依赖视觉特征的向量来进行检索和匹配,而不仅仅是依赖文本描述。
在中间智能驾舱的图示中,一部分细节可以用文字描述,如驾舱中每个设备的信息和位置。这种图像的检索可能需要结合文本和图像信息,利用文本描述来增强对关键点的理解,同时利用图像特征来捕捉整体的视觉信息。
最右侧的图是小米 SU7 话题趋势,这种图像的细节可以完全用文字描述, 这类信息的检索和处理可以较多地依赖文本向量,而图像向量的作用可能相对较小。
构建多模态 RAG 主要方法包括:
将所有模态嵌入同一向量空间;
将所有模态归结为一种主要模态
为不同模态设立独立存储。
仅以图像和文本输入进行探讨:
将不同类型的数据(文本、图像) 转换成具有相同维度和相似语义表示的向量:
所有模态的数据在处理后被转换成相同维度的向量。例如,图像和文本可能通过不同的模型处理,但最终都被转换为相同长度的向量(例如,512维)。
相同或相似的语义信息(如图像中的“猫”和文本中的“猫”)在同一向量空间中应该相互接近。换句话说,具有相同含义或相关含义的向量在向量空间中会彼此接近。
对于这种方法,如果已有仅含针对文本的 RAG 模型,需要将嵌入模型改为支持多模态嵌入的模型(如 CLIP, Contrastive Language-Image Pre-training );在生成阶段则将只能负责文本生成的 LLM 替换为多模态的 LLM (如 GPT-4V、LLaVA、FUYU-8b)。
根据应用的需求,选择一种主要模态,并将其他模态基于该主要模态进行构建。
比如,应用的最终目标是根据 PDF 文件进行基于文本的问答。这种情况下,按照常规方法处理文本数据,对于图像,则在数据预处理阶段,创建文本描述和元数据,并存储数据在后续使用。
在推理过程中,检索主要依据图像的文本描述和元数据进行,答案生成则可以结合 LLMs 与 MLLMs,具体取决于所检索图像的类型。
此种方法的优势在于,从图像中生成的元数据在回答客观问题时非常有用;而且绕开了“需要为图像嵌入更换嵌入模型”的需求;同时也不需要设计专门的排序器来对跨模态结果进行重新排序。
这种方法的劣势在于其数据预处理的成本以及原始图像中所包含的细节的缺失。
为不同的模态设置独立的数据存储,然后使用 Rank-reank 方法。
具体来说,在初次排序(Ranking)中, 系统首先将查询提交给多个模态的独立存储库,每个存储库根据其模态特性返回与查询最相关的前N个结果(chunks):文本存储库返回与查询匹配的文本片段,图像存储库返回与查询语义相关的图像。
一旦从各个模态的存储库中检索到这些初步结果,一个多模态重排序器(multimodal re-ranker)会接管这些结果。重排序器将这些来自不同模态的top-N结果整合,并根据它们与查询的整体相关性重新排序,最终提供给用户最相关的结果。
总结
本文初步介绍了 RAG系统中的数据处理方法,特别是文档分割的方法及多模态数据的处理。
数据采集(Data Ingestion)
包括数据源识别、数据治理和数据清洗
强调建立持续更新机制和自动化数据清洗管道
文档数据转换
将文档(如PDF)转换为包含页面内容和元数据的对象
文档分块
考虑因素:LLM的上下文窗口限制、块大小对检索精确度的影响、Top-K检索大小
建议探索不同的块大小,平衡上下文保留和准确性
分块方法
固定大小分块:简单但可能导致语义不完整
上下文感知分块(Context-aware Chunking):考虑语义和上下文信息
递归分块:适用于复杂结构的长文档
专业化分块:针对特定格式(如Markdown、LaTeX)的分块方法
多模态RAG数据处理
处理包含文本、图像、表格等多种形态的数据
主要方法:a. 将所有模态嵌入同一向量空间 b. 将所有模态归结为一种主要模态 c. 为不同模态设立独立存储,使用Rank-rerank方法
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-07-18
2024-05-05
2024-07-09
2024-05-19
2024-07-09
2024-06-20
2024-07-07
2024-07-07
2024-07-08
2024-07-09
2024-11-25
2024-11-06
2024-11-06
2024-11-05
2024-11-04
2024-10-27
2024-10-25
2024-10-21