尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

多模态RAG技术解析:从图文理解到混合检索的实战指南

多模态RAG技术解析:从图文理解到混合检索的实战指南 1. 从文本到万物为什么我们需要多模态RAG如果你最近在折腾RAG检索增强生成大概率已经玩转了纯文本的问答。用向量数据库存文档用大模型生成答案效果确实不错。但不知道你有没有遇到过这样的场景用户甩给你一张产品结构图问“这个零件叫什么”或者扔过来一份财务报表的截图问“第三季度的净利润是多少”。这时候传统的文本RAG就傻眼了——它只能处理文字对于图片、表格、图表里的信息它要么视而不见要么只能依赖图片文件名或周边那点可怜的文本描述来“盲猜”召回率和准确性都惨不忍睹。这就是多模态RAG要解决的核心痛点。RAG的核心思想是“先检索相关知识再生成答案”。当知识本身不再局限于纯文本而是散落在图片、表格、PDF、幻灯片甚至视频中时我们的检索系统也必须升级具备“看懂”这些非文本信息的能力。这不仅仅是技术上的“炫技”更是解决实际业务问题的刚需。想想看企业内部的培训材料、产品手册、设计图纸、实验报告有多少是以图文并茂的形式存在的一个只能读文字的AI助手其能力天花板是非常明显的。多模态RAG的目标就是打破这个天花板。它要让检索系统像人一样既能读懂报告里的文字也能看懂旁边的柱状图趋势还能提取表格里的关键数据最终将这些分散在不同模态中的信息融合起来给出一个准确、完整的答案。这背后的技术栈也从单一的文本嵌入模型扩展到了视觉编码器、多模态大模型、跨模态对齐等一系列新组件。接下来我们就深入这个“万物皆可检索”的新世界看看它是如何工作的以及在实际搭建时会遇到哪些“坑”。2. 多模态RAG的核心技术栈拆解不只是“文本向量”“图片向量”很多人初看多模态RAG会简单地理解为“把图片也变成向量存起来”。这个理解只对了一半而且是最简单的那一半。一个真正可用的多模态RAG系统其技术栈要复杂和精细得多。我们可以把它拆解为几个关键环节每个环节都有不同的技术选型和设计考量。2.1 多模态文档的解析与表示从“像素”和“单元格”到“语义”这是整个流程的第一步也是最基础的一步。不同类型的非文本数据需要不同的“解码器”。对于图片核心任务是将视觉信息转化为机器可理解的、富含语义的表示。通常有两种主流路径使用专用视觉编码器如CLIP、BLIP提取特征向量这是目前最主流、效果最好的方式。以OpenAI的CLIP模型为例它通过在海量“图片-文本”对上对比学习学会了将图片和文本映射到同一个语义空间。这意味着用CLIP提取的图片向量和用文本嵌入模型如text-embedding-ada-002提取的文本向量在空间上是可比的。你可以用“一只猫在沙发上”这段文字去直接检索相关的猫图片。在实际处理时我们通常取CLIP模型视觉编码器ViT输出的[CLS] token的向量或池化后的特征作为图片的表示向量。使用多模态大模型如GPT-4V、Qwen-VL生成详细的文本描述这种方法被称为“视觉字幕”或“视觉理解”。我们将图片输入给多模态大模型让它用自然语言详细描述图片的内容、物体、关系、文字等。例如对于一张财务报表截图模型可能输出“这是一张2023年Q3的利润表截图。标题为‘XX公司合并利润表’。表格包含以下列项目、本期金额、上期金额。行数据包括营业收入 1.2亿元营业成本 8000万元……” 随后我们可以将这个生成的文本描述用传统的文本嵌入模型转化为向量。这种方法的优点是生成的描述富含语义便于后续的文本检索和模型理解缺点是完全依赖大模型的描述能力可能丢失细节且推理成本较高。对于表格表格的处理更为特殊它兼具结构化和语义信息。直接文本化Markdown/HTML将表格结构转换为Markdown或HTML格式的文本字符串。例如一个简单的表格会变成| 产品 | 销量 | 销售额 | |---|---|---| | A | 100 | 10万 | | B | 150 | 15万 |然后对这个文本字符串进行嵌入。这种方法简单直接保留了表格的结构信息嵌入模型能较好地理解行列关系。使用专用表格理解模型对于复杂表格可以考虑使用像TAPAS、Table Transformer这类专门为表格设计的模型。它们能更好地理解表格的语义、表头结构、单元格之间的关系甚至能执行一些简单的推理如“销量最高的产品是什么”。这些模型通常也能输出表格的结构化表示或特征向量。关键信息抽取有时我们只关心表格中的核心数据点。可以先用OCR如果表格是图片或解析库如果表格是PDF/Word提取出表格内容和结构然后通过规则或小模型抽取出“键值对”。例如从利润表中抽取出{“净利润”: “5000万元”, “同比增长”: “20%”}。将这些键值对作为文本进行处理和嵌入。注意在实际项目中混合使用多种策略是常态。例如对于一份图文混排的PDF我们会用PyMuPDF或pdfplumber解析出文本块和图片块的位置信息文本块直接嵌入图片块则用CLIP提取向量或GPT-4V生成描述。关键在于构建一个统一的“文档单元”列表每个单元都带有其内容文本或向量和元数据来源、页码、类型等。2.2 混合检索策略当文本、图片、向量同台竞技当我们的知识库中同时存在文本向量、图片向量和经过处理的表格文本向量时如何执行一次高效的检索这里不再是简单的向量相似度计算而需要设计混合检索策略。1. 跨模态统一向量检索这是最理想的状况前提是你的文本和图片表示在同一个向量空间。例如都使用CLIP模型文本通过CLIP的文本编码器得到向量图片通过CLIP的视觉编码器得到向量。这样无论用户输入的是文本问题“找一张有向日葵的图片”还是上传一张图片以图搜图都可以直接计算余弦相似度在统一的向量索引如Milvus, Pinecone, Weaviate中进行检索。这种方式的优点是简洁、高效底层逻辑一致。2. 多路召回与融合排序在更多情况下不同模态的信息可能使用不同的嵌入模型或者我们需要结合关键词匹配等传统方法。这时就需要“多路召回融合排序”的策略。文本检索路使用BM25、TF-IDF或文本向量模型在纯文本内容包括图片的描述文本、表格的文本化内容中进行检索。图像检索路使用图片向量在图片向量库中进行检索。混合查询路用户查询本身也可能是多模态的。例如用户上传一张零件图片并问“这个零件在手册哪一页” 我们需要同时用CLIP处理图片部分得到向量用文本嵌入模型处理问题文本部分得到向量然后设计策略融合两个检索结果。召回得到多个候选列表后就需要进行融合重排。常见策略有加权分数融合为每一路召回结果设定一个权重计算加权后的综合分数。例如文本检索得分权重0.7图像检索得分权重0.3。交叉编码器重排这是一个更精细但计算代价更高的方法。将用户查询和每一个召回的候选片段无论是文本还是图片描述一起输入一个更强大的、但不适合大规模检索的模型如Cross-Encoder让该模型直接输出一个相关度分数。用这个分数对初步召回的结果进行重新排序。这对于提升最终答案的准确性非常有效。** Reciprocal Rank Fusion (RRF)**一种无需分数标准化的融合方法对每路召回结果根据其排名来计算分数最后加总。它对不同检索系统返回的分数尺度差异不敏感。2.3 多模态上下文构建与答案生成检索到相关的文本片段和图片后如何将它们组织成有效的上下文喂给大模型生成答案这里比纯文本RAG更复杂。上下文构建的挑战长度限制多模态大模型如GPT-4V的上下文窗口同样有限而一张图片的高清描述可能就占去几百个token。我们需要精心挑选最相关的信息避免上下文爆炸。模态融合不能简单地把图片向量扔给模型。对于支持视觉的模型我们需要以特定的格式如Markdown内嵌图片链接或Base64编码的图片数据将图片信息放入上下文。对于文本化的表格需要保持其可读性。指令设计给模型的指令Prompt需要明确告知它如何处理多模态上下文。例如“你是一个助手将看到一些文本片段和图片。请根据这些信息回答问题。图片将以[Image: 描述]的形式提供其中描述是对图片内容的文字总结。”一个典型的上下文组装示例你是一个专业的产品支持助手。请根据以下提供的产品手册片段和产品图片信息回答用户的问题。 [文本片段1] 来自《安装指南》第5页拧紧A部件时需使用配套的六角扳手扭矩不得超过5N·m否则可能导致螺纹滑丝。 [图片1: 描述] 一张示意图展示了A部件一个银色金属块与B部件黑色塑料件的组装对接方式红色箭头指示了拧紧方向。 [文本片段2] 来自《故障排查》第12页如果A部件安装后异响请检查B部件的卡扣是否完全入位参考图示。 [图片2: 描述] 一张特写图片显示了B部件卡扣一个黄色塑料凸起完全入位绿色对勾标识和未完全入位红色叉号标识的对比状态。 用户问题安装A部件后发现有吱吱声可能是什么原因该怎么检查在这个例子中我们混合了文本和图片描述共同构成了一个丰富的上下文。大模型需要综合理解文本中的扭矩警告、故障描述以及图片中的组装方式和卡扣状态才能给出准确的答案“异响可能由于B部件卡扣未完全入位导致。请先停止操作参照图片2中‘未完全入位’的状态检查B部件黄色卡扣。如果确认未入位请先拆卸A部件注意反向拧松扭矩同样需控制将B部件卡扣按压到位后重新安装A部件并确保扭矩不超过5N·m。”3. 实战搭建一个简易多模态RAG系统的实现路径理论说了这么多我们来点实际的。下面我将以处理一个包含图片和表格的PDF产品手册为例勾勒一个简易的多模态RAG实现路径。这里我们选择一种兼顾效果和复杂度的方案用CLIP处理图片用文本嵌入处理文字和表格使用多路召回。3.1 环境准备与文档解析首先我们需要一个强大的文档解析库。对于PDFunstructured库是一个很好的选择它能较好地识别文档中的文本、表格和图片区域。# 安装核心库 pip install unstructured[pdf] pillow torch transformers pip install sentence-transformers # 用于文本嵌入 pip install openai # 如果需要用OpenAI的嵌入模型 pip install chromadb # 轻量级向量数据库支持多集合# 示例使用unstructured解析PDF from unstructured.partition.pdf import partition_pdf import os # 解析PDF提取元素 raw_pdf_elements partition_pdf( filenameproduct_manual.pdf, extract_images_in_pdfTrue, # 关键提取图片 infer_table_structureTrue, # 关键推断表格结构 strategyhi_res, # 高精度策略对图文混排效果好 extract_image_block_types[Image, Table], # 提取图片和表格块 ) # 遍历元素分类存储 text_chunks [] table_chunks [] image_chunks [] # 这里存储的是图片的PIL.Image对象或文件路径 for element in raw_pdf_elements: if hasattr(element, category): if element.category Title or element.category NarrativeText: # 处理文本可以进行分块 text_chunks.append(str(element)) elif element.category Table: # 处理表格转换为Markdown table_html element.metadata.text_as_html # 可以将HTML表格转换为简化的Markdown这里简化处理 table_chunks.append(f[Table]: {str(element)}) elif element.category Image: # 保存图片 image_path element.metadata.image_path # unstructured会保存图片到临时目录 image_chunks.append({ path: image_path, reference: element.metadata.page_number # 记录页码等信息 })3.2 多模态嵌入与向量化存储接下来我们分别处理文本含表格文本和图片。from sentence_transformers import SentenceTransformer from PIL import Image import torch from transformers import CLIPProcessor, CLIPModel import chromadb from chromadb.config import Settings # 初始化模型 text_embedder SentenceTransformer(all-MiniLM-L6-v2) # 文本嵌入模型 clip_model CLIPModel.from_pretrained(openai/clip-vit-base-patch32) clip_processor CLIPProcessor.from_pretrained(openai/clip-vit-base-patch32) # 初始化向量数据库客户端 client chromadb.Client(Settings(persist_directory./chroma_db)) # 创建两个集合一个存文本一个存图片 text_collection client.create_collection(namemanual_texts) image_collection client.create_collection(namemanual_images) # 处理文本和表格块 text_ids [] text_embeddings [] text_metadatas [] for i, chunk in enumerate(text_chunks table_chunks): # 合并文本和表格文本 embedding text_embedder.encode(chunk).tolist() text_ids.append(ftext_{i}) text_embeddings.append(embedding) text_metadatas.append({content: chunk[:200], type: text if i len(text_chunks) else table}) # 批量添加到集合 text_collection.add( embeddingstext_embeddings, documents[chunk for chunk in (text_chunks table_chunks)], # 存储原始内容 metadatastext_metadatas, idstext_ids ) # 处理图片块 image_ids [] image_embeddings [] image_metadatas [] for i, img_info in enumerate(image_chunks): image Image.open(img_info[path]) inputs clip_processor(imagesimage, return_tensorspt) with torch.no_grad(): image_features clip_model.get_image_features(**inputs) image_embedding image_features.squeeze().tolist() # 得到图片向量 image_ids.append(fimage_{i}) image_embeddings.append(image_embedding) image_metadatas.append({path: img_info[path], page: img_info[reference], type: image}) # 批量添加到图片集合 image_collection.add( embeddingsimage_embeddings, # 图片集合的documents可以存图片路径或简单描述这里存路径 documents[info[path] for info in image_chunks], metadatasimage_metadatas, idsimage_ids ) client.persist() # 持久化到磁盘3.3 混合检索与答案生成当用户提问时我们需要执行混合检索。def hybrid_retrieval(query_text, query_image_pathNone, top_k5): 混合检索函数 query_text: 用户文本问题 query_image_path: 可选用户上传的图片路径 top_k: 每路召回数量 results {} # 1. 文本检索路 query_text_embedding text_embedder.encode(query_text).tolist() text_results text_collection.query( query_embeddings[query_text_embedding], n_resultstop_k ) results[text] [] for doc, meta in zip(text_results[documents][0], text_results[metadatas][0]): results[text].append({content: doc, metadata: meta, score: 1.0}) # chromadb返回无分数简化处理 # 2. 图片检索路 (如果用户上传了图片) if query_image_path: query_image Image.open(query_image_path) inputs clip_processor(imagesquery_image, return_tensorspt) with torch.no_grad(): query_image_embedding clip_model.get_image_features(**inputs).squeeze().tolist() image_results image_collection.query( query_embeddings[query_image_embedding], n_resultstop_k ) results[image] [] for doc, meta in zip(image_results[documents][0], image_results[metadatas][0]): # 对于图片我们可以用CLIP的文本编码器生成一个简短描述用于后续提示词 # 这里简化处理直接使用路径 results[image].append({path: doc, metadata: meta, score: 1.0}) # 3. 融合策略 (简单版优先文本图片作为补充) all_candidates [] # 给文本结果较高优先级 for item in results.get(text, []): all_candidates.append((item, text, 1.0)) for item in results.get(image, []): all_candidates.append((item, image, 0.7)) # 图片权重0.7 # 按权重排序这里简化实际可用RRF等 all_candidates.sort(keylambda x: x[2], reverseTrue) final_context [] seen set() for candidate, modality, _ in all_candidates[:top_k*2]: # 取前N个 if modality text: context_str f[Text]: {candidate[content][:500]}... # 截断 if context_str not in seen: final_context.append(context_str) seen.add(context_str) else: # image # 这里需要将图片转换为模型可接受的格式。假设我们使用GPT-4V需要准备图片的Base64或URL。 # 我们用一个占位描述代替 context_str f[Image from page {candidate[metadata][page]}]: A product diagram or photo related to the query. if context_str not in seen: final_context.append(context_str) seen.add(context_str) return final_context # 使用示例 user_question 这个设备的电源接口在哪里长什么样 # 假设用户没有上传图片 contexts hybrid_retrieval(user_question, top_k3) # 构建Prompt prompt f你是一个产品手册助手。请根据以下提供的上下文信息回答用户的问题。上下文可能包含文本描述和图片描述。 上下文信息 {chr(10).join(contexts)} 用户问题{user_question} 请给出准确、简洁的回答如果信息不足请说明。 print(prompt) # 将prompt发送给LLM如GPT-4, Claude, 或本地Qwen等获取答案 # answer llm.generate(prompt)4. 避坑指南与进阶思考多模态RAG的挑战与优化搭建一个能跑通的多模态RAG原型并不难但要让它在实际生产环境中稳定、准确、高效地运行你会遇到一系列挑战。下面是我在实践过程中总结的几个关键“坑点”和优化思路。4.1 模态对齐与语义鸿沟CLIP不是万能的最大的坑莫过于“语义鸿沟”。我们假设CLIP模型将图片和文本映射到了同一个空间但这个空间的对齐质量直接决定了检索效果。问题CLIP是在互联网规模的通用数据上训练的。对于高度专业领域的图片如工程图纸、医学影像、特定行业的图表它的理解能力会下降。用“机械传动原理图”去检索可能找出一堆不相关的机械产品外观图而不是真正的原理图。解决方案领域微调如果数据量和算力允许在你自己领域的“图片-文本”对上对CLIP进行微调。这能显著提升模型对专业术语和视觉概念的对齐能力。混合描述策略不要完全依赖CLIP的向量。对于关键图片可以结合使用多模态大模型生成详细、专业的文本描述然后将该描述文本进行嵌入。这样相当于用更强大的模型为图片“翻译”了一次虽然牺牲了纯粹的向量检索效率但提升了语义准确性。后过滤在向量检索后增加一个基于规则的或轻量级模型的过滤层。例如检索出图片后用一个小型分类器判断它是否是“原理图”、“实物图”还是“流程图”根据用户问题意图进行过滤。4.2 上下文管理与令牌消耗多模态信息的“体重”管理多模态信息尤其是高分辨率的图片描述或Base64编码是消耗LLM上下文令牌的“大户”。问题一张图片的详细描述可能占用500-1000个token。如果一次检索返回5张相关图片的描述加上文本片段上下文很容易超过模型限制如GPT-4的8K、32K。这不仅增加成本还可能因为信息过载导致模型性能下降。解决方案智能摘要与压缩对检索到的长文本和图片描述进行二次摘要。可以使用一个较小的、专门用于摘要的LLM将冗长的描述压缩成核心事实的要点。分页或层次化检索不要试图一次性把所有相关信息塞进去。可以采用“两阶段”策略第一阶段用较粗的检索如只检索文本确定大概范围第二阶段针对这个范围再进行一次精细的多模态检索获取最关键的几张图片或几段文本。利用模型的视觉能力如果使用GPT-4V这类原生支持视觉的模型并且图片数量不多可以考虑直接将图片以Base64格式嵌入Prompt让模型自己“看”。这比用文字描述更精准但需注意其支持的图片数量、分辨率和总token限制。4.3 评估体系的构建如何衡量“看得懂”的效果纯文本RAG的评估已经够难了评估检索相关性、答案忠实度、信息完整性等多模态RAG的评估更是难上加难。挑战如何评估系统是否“正确理解”了一张图片传统的基于文本匹配的评估指标如BLEU, ROUGE完全失效。思路人工评估黄金标准构建一个测试集包含多模态查询和对应的标准答案或标准支持文档列表。由领域专家进行人工评分这是最可靠但成本最高的方法。基于LLM的自动评估用另一个强大的LLM如GPT-4作为“裁判”。将系统检索到的上下文和生成的答案连同原始问题和标准答案如果有一起给裁判LLM让它从相关性、准确性、完整性等维度打分。这正在成为一种主流方法。任务导向的评估根据下游任务设计评估指标。例如如果系统用于QA就评估答案的正确率如果用于以图搜图就评估检索结果的mAP平均精度均值。4.4 架构选型与成本考量在理想与现实间权衡多模态RAG的架构选择直接影响复杂度和成本。轻量级方案如上文示例CLIP 文本嵌入 向量数据库 大语言模型API。适合快速验证和中小规模应用。成本主要来自大模型API调用用于生成最终答案可能也用于生成图片描述。重型一体化方案使用开源的多模态大模型如LLaVA、Qwen-VL-Chat端到端地处理。用户输入和知识库文档图片都直接喂给模型依靠模型自身的注意力机制完成“检索”和“生成”。这种方法简化了架构但要求模型上下文窗口极大且推理成本高对复杂知识库的检索精度可能不如专门的检索系统。混合方案推荐将专用检索器向量数据库与多模态大模型结合。检索器负责从海量知识中快速筛选出候选集多模态大模型负责对候选集进行深度理解、重排和最终答案生成。这是目前平衡效果、速度和成本的主流选择。最后一个容易被忽略但至关重要的点是数据治理。多模态知识库的构建比纯文本更麻烦。你需要确保图片清晰、表格解析准确、不同版本的文件同步更新。建立一套从原始文档PDF/PPT/Word到解析、清洗、嵌入、入库的自动化流水线是项目能持续运营的基础。多模态RAG不是一劳永逸的工具而是一个需要持续喂养高质量、多格式“食粮”的系统它的智能程度最终取决于你喂给它的数据的质量和数量。
返回列表