尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

RAG 多模态检索方案:图片 + 文本的联合 Embedding 工程实践
📅 发布时间:2026/7/15 22:47:57

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% 的存储和传输开销。

相关新闻

  • 卡美德生物科普Phosphatidylserine(磷脂酰丝氨酸)
  • 微信接口开发:高效实现多媒体消息自动推送
  • Python数据清洗实战:缺失值、重复行、异常值与类型校准

最新新闻

  • 学工系统:数字化重塑校园生活,解锁高效学生管理新体验
  • C++多线程编程:从锁机制到高并发实战
  • 2026武穴黄金回收深度测评|本土30年老店无损检测,全城免费上门零扣费 - 华金汇黄金回收
  • 2026 浠水黄金回收完整实操指南|深耕 30 年本土合规老店,全县 24 小时免费上门零压价、无隐形扣费 - 华金汇黄金回收
  • Codect 从入门到实战:AI生成代码检测工具的完整踩坑指南
  • 2023年全国电赛H题信号分离装置:从加法器到DDS的硬件实现与频谱分析实战

日新闻

  • 告别启动盘残留:用Diskpart彻底清除U盘EFI分区与恢复完整空间
  • 2026 年宜春诚信的塑料缠绕膜厂家哪个好,缠绕膜背后的秘密:你不知道的成本陷阱 - 领域鉴赏官
  • Arch ECS 入门指南:10分钟掌握C#高性能数据驱动架构

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号