当前位置: 首页 > news >正文

RAG 多模态检索方案:图片 + 文本的联合 Embedding 工程实践

RAG 多模态检索方案:图片 + 文本的联合 Embedding 工程实践

一、"用户上传了截图问'这个报错怎么解决',系统检索不到"

用户上传了一张报错截图并问"这个怎么解决"。RAG 系统试图对截图中的文字做 OCR 提取文本,但 OCR 准确率只有 80%,把NullPointerException识别成了NullPointerExceplion,向量检索自然找不到匹配的文档。

传统 RAG 只能处理文本。多模态 RAG 让系统能直接理解图片内容,而不依赖不完美的 OCR 中间层。核心思路:用多模态 Embedding 模型(如 CLIP)将图片和文本映射到同一个向量空间,实现图文混合检索。

二、多模态 RAG 的联合检索架构

flowchart TD Q[用户查询: 文字+图片] --> SPLIT{内容类型} SPLIT -->|文字| TE[文本 Embedding 模型] SPLIT -->|图片| IE[图像 Embedding 模型: CLIP] TE --> VEC_T[文本向量] IE --> VEC_I[图像向量] VEC_T --> FUSE[向量融合: 加权平均或拼接] VEC_I --> FUSE FUSE --> SEARCH[向量库检索] subgraph Index[多模态知识库索引] T_DOC[文本文档: 文本向量] I_DOC[截图/图片: 图像向量] T_DOC --> IDX[统一向量索引] I_DOC --> IDX end SEARCH --> IDX IDX --> RESULTS[Top-K 多模态结果] RESULTS --> CONTEXT[拼接: 文本+图片描述] CONTEXT --> LLM[多模态 LLM 推理] LLM --> ANSWER[回答]

关键技术点:CLIP 模型确保图文向量在同一个语义空间、向量融合策略(平均/加权/拼接)影响检索精度、知识库需要同时为图片和文本建立向量索引。

三、Python 实现:多模态检索

import io import base64 import numpy as np from dataclasses import dataclass from typing import Optional, Union from PIL import Image import openai # ========== 多模态文档 ========== @dataclass class MultimodalDocument: """多模态文档:文本 + 可选图片""" doc_id: str text_content: str image_base64: Optional[str] = None # 截图 text_embedding: Optional[np.ndarray] = None image_embedding: Optional[np.ndarray] = None modality: str = "text" # text, image, mixed # ========== 多模态 Embedding 编码器 ========== class MultimodalEncoder: """多模态编码器:文本 + 图片统一向量化""" def __init__(self, clip_model: str = "ViT-B/32"): self.text_dim = 1536 # text-embedding-ada-002 self.image_dim = 512 # CLIP ViT-B/32 self.fused_dim = self.text_dim + self.image_dim def encode_text(self, text: str) -> np.ndarray: """文本向量化""" response = openai.Embedding.create( model="text-embedding-ada-002", input=text, ) vec = np.array(response["data"][0]["embedding"]) return vec / np.linalg.norm(vec) # L2 归一化 def encode_image(self, image_base64: str) -> np.ndarray: """图像向量化(使用 CLIP)""" # 实际项目中使用 open_clip 或 transformers # import clip # model, preprocess = clip.load("ViT-B/32") # image = preprocess(Image.open(io.BytesIO(base64.b64decode(image_base64)))) # with torch.no_grad(): # vec = model.encode_image(image.unsqueeze(0)) # return vec.numpy().flatten() # 简化模拟 vec = np.random.randn(self.image_dim) return vec / np.linalg.norm(vec) def fuse_embeddings( self, text_vec: np.ndarray, image_vec: Optional[np.ndarray] = None, strategy: str = "concat", text_weight: float = 0.6, image_weight: float = 0.4, ) -> np.ndarray: """融合文本和图像向量 strategy: concat: 拼接 [text_vec, image_vec](保留全部信息) avg: 加权平均(维度对齐) """ if image_vec is None: return text_vec if strategy == "concat": # 拼接后归一化 fused = np.concatenate([text_vec * text_weight, image_vec * image_weight]) return fused / np.linalg.norm(fused) elif strategy == "avg": # 加权平均 fused = text_vec * text_weight + image_vec * image_weight return fused / np.linalg.norm(fused) return text_vec # ========== 多模态索引 ========== class MultimodalIndex: """多模态向量索引""" def __init__(self): self.vectors: list[np.ndarray] = [] self.documents: dict[int, MultimodalDocument] = {} def add_document(self, doc: MultimodalDocument, fused_vec: np.ndarray): """添加文档到索引""" idx = len(self.vectors) self.vectors.append(fused_vec) self.documents[idx] = doc return idx def search( self, query_vec: np.ndarray, top_k: int = 5, ) -> list[tuple[MultimodalDocument, float]]: """检索最相似的文档""" if not self.vectors: return [] # 批量计算余弦相似度 vectors_matrix = np.stack(self.vectors) similarities = np.dot(vectors_matrix, query_vec) # Top-K top_indices = np.argsort(similarities)[::-1][:top_k] results = [] for idx in top_indices: doc = self.documents[int(idx)] score = float(similarities[int(idx)]) results.append((doc, score)) return results # ========== 多模态 RAG 引擎 ========== class MultimodalRAGEngine: """多模态 RAG 引擎""" def __init__(self): self.encoder = MultimodalEncoder() self.index = MultimodalIndex() def index_document(self, doc: MultimodalDocument): """索引文档""" # 计算文本向量 text_vec = None if doc.text_content: text_vec = self.encoder.encode_text(doc.text_content) doc.text_embedding = text_vec # 计算图像向量 image_vec = None if doc.image_base64: image_vec = self.encoder.encode_image(doc.image_base64) doc.image_embedding = image_vec # 融合向量 if image_vec is not None: fused = self.encoder.fuse_embeddings(text_vec, image_vec, "concat") else: fused = text_vec if fused is not None: self.index.add_document(doc, fused) def query( self, text_query: str, image_query: Optional[str] = None, top_k: int = 5, ) -> list[dict]: """多模态查询""" # 编码查询 text_vec = self.encoder.encode_text(text_query) image_vec = None if image_query: image_vec = self.encoder.encode_image(image_query) # 融合查询向量 query_vec = self.encoder.fuse_embeddings(text_vec, image_vec, "concat") # 检索 results = self.index.search(query_vec, top_k) return [ { "doc_id": doc.doc_id, "text": doc.text_content[:200], "has_image": doc.image_base64 is not None, "score": score, "modality": doc.modality, } for doc, score in results ] # ========== 图片预处理 ========== class ImagePreprocessor: """图片预处理工具""" @staticmethod def preprocess_for_embedding(image_path: str) -> str: """预处理图片,返回 base64""" img = Image.open(image_path) # 1. 调整尺寸(CLIP 要求 224x224) img = img.resize((224, 224), Image.Resampling.LANCZOS) # 2. 转 RGB(处理 PNG 透明度等) if img.mode != "RGB": img = img.convert("RGB") # 3. 转 base64 buffer = io.BytesIO() img.save(buffer, format="JPEG", quality=85) return base64.b64encode(buffer.getvalue()).decode() @staticmethod def preprocess_for_display(image_path: str, max_size: int = 1024) -> str: """预处理用于模型输入的图片(大图压缩)""" img = Image.open(image_path) # 保持宽高比缩放到 max_size w, h = img.size if max(w, h) > max_size: ratio = max_size / max(w, h) img = img.resize((int(w*ratio), int(h*ratio)), Image.Resampling.LANCZOS) buffer = io.BytesIO() img.save(buffer, format="JPEG", quality=80) return base64.b64encode(buffer.getvalue()).decode()

四、多模态 RAG 的边界与挑战

CLIP 的 Vector Space 不完美。CLIP 的图文对齐是基于互联网图文对训练的,对于专业领域图片(如架构图、流程图、代码截图)理解能力有限。微调 CLIP 或使用领域专用的多模态模型是必要的。

向量融合的策略影响大。Concat 保留全部信息但维度翻倍,检索速度变慢;Average 维度不变但信息有损。建议从 Average 开始,如果检索精度不够再换 Concat。

图片存储成本。知识库中存储原始图片(如截图)会显著增加存储开销。在送入 Embedding 模型之前压缩图片(224x224),原始大图只在需要展示时才读取。

多模态 LLM 的推理成本。图文混合输入比纯文本消耗更多 Token。在检索阶段用轻量级的 CLIP Embedding,只在最后推理阶段才把图片送给多模态 LLM,避免重复的图片编码开销。

五、总结

多模态 RAG 的核心是把图片和文本映射到同一个向量空间。关键步骤:用 CLIP 做图像 Embedding + 与文本向量做融合 + 在统一索引中检索。实现路径:先验证 CLIP 在你的领域图片上的检索精度,确认可用后再建立完整的图文索引。图片预处理是性价比最高的优化——合适的尺寸和压缩比可以降低 50% 的存储和传输开销。

http://www.jsqmd.com/news/1196458/

相关文章:

  • 江诗丹顿香港官方售后攻略|2026年7月最新网点地址及客户服务电话权威发布 - 江诗丹顿官方服务中心
  • ChunkDot:百万级稀疏向量的高效余弦相似度计算方案
  • 阜阳本地企业GEO营销工具全维度横向测评推荐:2026年选型7大维度深度解析 - 企业新闻快传
  • 卡美德生物科普Phosphatidylserine(磷脂酰丝氨酸)
  • 恭喜!AMD AI 技术征文大赛获奖名单公布
  • ChatGPT简历优化实战手册(HR筛选算法逆向拆解版)
  • 分布式软总线:设备发现与连接的低功耗优化(173)
  • 模板驱动型文档自动化:从Word手工到可审计流水线
  • Python数据清洗实战:缺失值、重复行、异常值与类型校准
  • 2026年河北调压撬品牌排行整理 鸿霖气体技术亮点及行业选购参考 - 每天一杯纯牛奶
  • 微信接口开发:高效实现多媒体消息自动推送
  • 分布式系统的最终一致性方案——从本地消息表到事务消息的渐进式演进
  • Java与大模型工程化落地实践思考
  • 卡地亚中国官方售后服务中心|官方热线及全部网点地址权威信息公示(2026年7月更新) - 卡地亚服务中心
  • 2026年云南电大中专每周报名注册学籍!名额限制~ - 云南成教资讯
  • Agentic AI工程落地:云平台能力对比与选型指南
  • 迪尼玛吊带公司 - 米諾
  • 培养肉产业为什么需要食品级生长因子?Qkine FG体系与细胞农业合规思路
  • 模板驱动型文档自动化:结构化模板重构文档生产流
  • 遗传算法工程化实战:选择压力、交叉适配与自适应变异
  • 长沙开福区适老化改造|维小达|适老厨房、卫生间、卧室改造、全屋无障碍改造、适老定制、老旧小区居家商用一站式适老化设计施工养护服务 - 一点传媒
  • Agent + 无头网站:未来所有网站,可能都只有一个界面
  • 【学习笔记】端侧大模型:Phi、Gemma 与小模型逆袭(33/35)
  • Sqribble文档自动化原理:规则引擎驱动的专业出版流水线
  • 卡地亚中国官方售后服务中心|服务热线及门店官方地址权威信息公示(2026年7月更新) - 卡地亚官方售后中心
  • 渗透测试第一步:信息收集做得好,漏洞挖到老(第五弹·实战侦察篇)
  • 2026年盘点:5大英语小升初宁波机构综合评测
  • 宿州本地企业GEO营销工具全维度横向测评推荐:2026年AI搜索时代工具选型七维深度解析 - 子柔传媒
  • Go 并发模型的性能边界:Goroutine 数量、Channel 吞吐与锁粒度的实验分析
  • 遗传算法实战调优指南:从编码选择到参数协同的系统化方法