CLIP ViT-H-14实战教程:与LangChain集成构建多模态RAG知识库

发布时间:2026/7/3 19:54:43

CLIP ViT-H-14实战教程:与LangChain集成构建多模态RAG知识库 CLIP ViT-H-14实战教程与LangChain集成构建多模态RAG知识库1. 引言当图片也能“说话”时想象一下你有一个庞大的图片库里面有产品图、设计稿、历史照片甚至是你随手拍的风景。现在你想找一张“夕阳下的海边小屋”或者“穿着红色裙子的女孩在跳舞”。传统的搜索方式要么靠你手动打标签要么靠文件名效率低不说还常常找不到。这就是我们今天要解决的问题。CLIP ViT-H-14这个由OpenAI开源的强大模型能让计算机真正“看懂”图片并用文字去理解它。而LangChain则是当前最火的AI应用开发框架能把各种AI能力像乐高积木一样组合起来。把它们俩结合起来我们能做什么我们可以构建一个多模态检索增强生成RAG知识库。简单说就是让AI不仅能根据文字搜文字还能根据文字搜图片甚至根据图片搜相关的文字资料。这对于内容管理、电商、设计、教育等领域简直是革命性的工具。这篇教程我将手把手带你从零开始部署CLIP服务到最终与LangChain集成打造一个属于你自己的、能“图文互搜”的智能知识库。整个过程清晰明了哪怕你之前没接触过这些技术也能跟着一步步做出来。2. 环境准备与CLIP服务部署万事开头难但部署CLIP服务这一步我们已经有人帮我们铺好了路。我们将使用一个预打包好的服务镜像它省去了我们安装依赖、下载模型等繁琐步骤。2.1 理解我们的工具CLIP ViT-H-14服务在动手之前我们先快速了解一下我们将要启动的这个服务是什么。核心模型CLIP ViT-H-14 (laion2B-s32B-b79K)。这是一个在超大规模图文对LAION-2B上训练过的视觉-语言模型。“ViT-H-14”代表它使用Vision Transformer架构并且是“Huge”版本有14x14的patch大小能力非常强。它能做什么这个服务启动后会做两件核心事提取图片特征你给它一张图片它能输出一个1280维的数学向量可以理解为这张图片的“数字指纹”。计算图文相似度你给它一张图和一段文字它能告诉你它们有多匹配。提供服务的方式Web界面一个简单的网页你可以上传图片查看提取的特征或者测试图文匹配。RESTful API这才是我们需要的。我们可以通过发送HTTP请求让其他程序比如我们的LangChain应用来调用图片特征提取功能。2.2 一键启动服务假设你已经拥有了这个服务的运行环境例如一个预装了镜像的云服务器或本地容器启动它非常简单。打开你的终端执行以下命令python /root/CLIP-ViT-H-14-laion2B-s32B-b79K_repackaged/app.py你会看到类似下面的输出说明服务正在启动并加载模型Loading model CLIP-ViT-H-14-laion2B-s32B-b79K... Model loaded successfully. Running on local URL: http://0.0.0.0:7860关键信息服务运行在7860端口。记住这个端口号。2.3 验证服务是否正常服务启动后我们最好先验证一下它是否工作正常。访问Web界面 打开你的浏览器输入http://你的服务器IP地址:7860。你应该能看到一个上传图片的界面。上传一张图片试试看看它能否成功显示提取的特征向量。这能最直观地确认服务是“活”的。测试API接口更重要的验证 Web界面是给人用的API是给程序用的。我们通过命令行工具curl来测试一下核心的图片编码API。 打开另一个终端窗口执行以下命令请将[YOUR_IMAGE_URL]替换成一张网络上可访问的图片地址例如https://example.com/test.jpg将[YOUR_HOST]替换为你的服务器IP或域名curl -X POST “http://[YOUR_HOST]:7860/encode_image” \ -H “Content-Type: application/json” \ -d ‘{“image_url”: “[YOUR_IMAGE_URL]”}’如果一切正常你会收到一个JSON格式的响应里面包含一个很长的数字列表1280维的向量这就是图片的特征。如果测试失败检查服务是否真的在运行、端口是否正确、网络是否通畅、图片URL是否有效。至此我们的CLIP图像编码服务就已经在后台稳定运行了它正等待着被我们的LangChain应用调用。接下来我们进入核心的集成环节。3. 核心集成让LangChain调用CLIP现在我们有了一个提供图片“指纹”生成服务的“工厂”CLIP服务。接下来我们要在LangChain这个“自动化流水线”上新建一个“工位”专门负责把图片送进这个工厂并把产出的“指纹”拿回来。在LangChain的体系中这个过程需要通过一个自定义的Embeddings类来实现。Embeddings类是LangChain用于将文本或其它数据转换为向量即嵌入的抽象。我们要为图片创建一个。3.1 创建自定义的图片嵌入类我们将创建一个名为CLIPImageEmbeddings的类。把它保存为一个独立的Python文件比如clip_embeddings.py。import requests from typing import List from langchain.embeddings.base import Embeddings from PIL import Image import io class CLIPImageEmbeddings(Embeddings): 一个自定义的Embeddings类用于通过CLIP服务获取图片的向量表示。 def __init__(self, base_url: str “http://localhost:7860”): # 初始化设置CLIP服务的API地址 self.base_url base_url self.encode_url f“{base_url}/encode_image” def _encode_image(self, image_path: str) - List[float]: 内部方法调用CLIP服务API编码单张图片。 # 准备请求数据这里我们假设服务支持本地文件路径或URL # 根据你的CLIP服务API设计调整。这里示例是发送图片URL。 # 如果你的服务接收base64或文件上传需要修改此部分。 data {“image_url”: image_path} # 另一种常见方式是直接读取图片文件并传输这里假设服务端支持从URL读取 # 对于本地文件可能需要先上传到可访问的地址或修改服务端API。 try: response requests.post(self.encode_url, jsondata, timeout30) response.raise_for_status() # 如果状态码不是200抛出异常 result response.json() # 假设API返回格式为 {“embedding”: [ ... ] } return result.get(“embedding”, []) except requests.exceptions.RequestException as e: print(f“调用CLIP API失败: {e}”) # 在实际生产中这里应该记录日志并抛出更明确的异常 return [] def embed_documents(self, texts: List[str]) - List[List[float]]: LangChain标准接口嵌入文档文本。本项目不用于文本可抛出异常或返回空。 raise NotImplementedError(“CLIPImageEmbeddings 仅支持图片嵌入不支持文本。”) def embed_query(self, text: str) - List[float]: LangChain标准接口嵌入查询文本。本项目不用于文本可抛出异常或返回空。 raise NotImplementedError(“CLIPImageEmbeddings 仅支持图片嵌入不支持文本。”) def embed_images(self, image_paths: List[str]) - List[List[float]]: 自定义方法嵌入多张图片。这是我们的核心功能。 embeddings [] for path in image_paths: emb self._encode_image(path) if emb: # 只添加成功的嵌入 embeddings.append(emb) return embeddings代码解读我们继承了LangChain的Embeddings基类。__init__方法初始化了CLIP服务的API地址。_encode_image是私有方法负责与我们的CLIP服务通信发送图片路径URL并接收返回的1280维向量。embed_documents和embed_query是LangChain要求的标准接口但我们这个类只处理图片所以直接抛出异常。这明确了它的职责。embed_images是我们自定义的核心方法它接收一个图片路径列表并返回对应的向量列表。注意上面的代码假设你的CLIP服务API接收一个image_url参数。你需要根据你实际部署的服务的API文档来调整_encode_image方法中的请求格式例如可能是文件上传multipart/form-data或接收base64编码。3.2 测试图片嵌入功能在继续构建知识库之前我们先写个小脚本测试一下这个类是否工作正常。创建一个test_embedding.py文件。from clip_embeddings import CLIPImageEmbeddings # 初始化嵌入器确保base_url指向你正在运行的CLIP服务 embedder CLIPImageEmbeddings(base_url“http://localhost:7860”) # 修改为你的实际地址 # 准备一张测试图片的路径这里用网络图片URL示例 test_image_url “https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/800px-Cat_November_2010-1a.jpg” try: # 嵌入单张图片 embeddings embedder.embed_images([test_image_url]) if embeddings: print(f“嵌入成功向量维度: {len(embeddings[0])}”) print(f“向量前10个值: {embeddings[0][:10]}”) # 打印前10个值看看 else: print(“嵌入失败未获取到向量。”) except Exception as e: print(f“测试过程中发生错误: {e}”)运行这个测试脚本。如果看到输出了1280维的向量恭喜你LangChain已经成功通过你写的“工位”从CLIP“工厂”拿到了图片的“数字指纹”。最关键的一步已经完成。4. 构建多模态RAG知识库有了图片嵌入器我们就可以开始搭建整个RAG系统了。RAG的核心流程是“检索-增强-生成”。对于多模态我们的知识库需要存储两种数据文本片段及其向量以及图片路径及其向量。4.1 设计知识库数据结构我们需要一个向量数据库来存储和检索这些向量。这里以Chroma为例因为它简单易用且与LangChain集成良好。当然你也可以选择Weaviate,Pinecone等。我们的知识库将包含两个独立的“集合”或“表”文本集合存储文本文档及其文本嵌入可以用OpenAI的text-embedding-ada-002或开源的BGE等模型。图片集合存储图片路径或唯一标识符及其通过CLIPImageEmbeddings得到的图片嵌入。4.2 创建并填充知识库我们编写一个构建脚本build_knowledge_base.py。import os from langchain.vectorstores import Chroma from langchain.embeddings import OpenAIEmbeddings # 用于文本 from clip_embeddings import CLIPImageEmbeddings # 用于图片 from langchain.schema import Document from langchain.document_loaders import TextLoader, DirectoryLoader # 示例用文本加载器 # 1. 初始化嵌入模型 text_embedder OpenAIEmbeddings() # 需要设置OPENAI_API_KEY环境变量 image_embedder CLIPImageEmbeddings(base_url“http://localhost:7860”) # 2. 准备数据 # 假设你的文本数据在 ./data/texts 目录下图片在 ./data/images 目录下 text_data_dir “./data/texts” image_data_dir “./data/images” # 加载文本文档 text_documents [] if os.path.exists(text_data_dir): loader DirectoryLoader(text_data_dir, glob“**/*.txt”, loader_clsTextLoader) text_documents loader.load() # 为每个文档添加来源类型元数据 for doc in text_documents: doc.metadata[“type”] “text” print(f“已加载 {len(text_documents)} 个文本文档。”) # 准备图片“文档”这里我们将图片路径视为文档内容 image_documents [] if os.path.exists(image_data_dir): # 获取所有图片文件示例支持jpg, png image_extensions [‘*.jpg’, ‘*.jpeg’, ‘*.png’, ‘*.bmp’, ‘*.gif’] image_paths [] for ext in image_extensions: image_paths.extend([os.path.join(root, name) for root, dirs, files in os.walk(image_data_dir) for name in files if name.lower().endswith(ext.replace(‘*’, ‘’))]) for img_path in image_paths: # 创建一个Document对象page_content可以是图片路径或描述metadata记录类型和路径 doc Document( page_contentimg_path, # 或者你可以用一些工具自动生成图片描述 metadata{“type”: “image”, “source”: img_path} ) image_documents.append(doc) print(f“已加载 {len(image_documents)} 张图片。”) # 3. 创建并持久化向量库 persist_directory “./chroma_db” # 创建文本向量库 if text_documents: text_vectordb Chroma.from_documents( documentstext_documents, embeddingtext_embedder, persist_directoryos.path.join(persist_directory, “text_collection”), collection_name“text_collection” ) text_vectordb.persist() print(“文本向量库构建完成。”) # 创建图片向量库关键步骤 if image_documents: # 首先获取所有图片的嵌入向量 print(“开始提取图片特征向量这可能需要一些时间...“) image_sources [doc.metadata[“source”] for doc in image_documents] image_embeddings image_embedder.embed_images(image_sources) # 确保嵌入向量数量与图片数量一致 valid_docs [] valid_embeddings [] for doc, emb in zip(image_documents, image_embeddings): if emb: # 只保留成功获取到嵌入的图片 valid_docs.append(doc) valid_embeddings.append(emb) if valid_docs: # Chroma 的 from_documents 需要传入embedding函数但我们已经有向量了。 # 我们可以使用 add_embeddings 方法或者用 from_embeddings。 # 这里使用 from_embeddings 更直接。 image_vectordb Chroma.from_embeddings( text_embeddingslist(zip([doc.page_content for doc in valid_docs], valid_embeddings)), embeddingimage_embedder, # 这里仍需传入embedding实例但实际不会用它计算 persist_directoryos.path.join(persist_directory, “image_collection”), collection_name“image_collection”, metadatas[doc.metadata for doc in valid_docs] ) image_vectordb.persist() print(f”图片向量库构建完成成功处理 {len(valid_docs)} 张图片。”) else: print(“未能成功提取任何图片的特征向量。”) else: print(“未找到图片数据。”) print(“多模态知识库构建完成”)这个脚本完成了以下工作分别初始化了文本和图片的嵌入模型。从指定目录加载文本文件和图片文件。使用文本嵌入模型为文本生成向量并存入Chroma的一个集合text_collection。使用我们自定义的CLIPImageEmbeddings为每张图片生成向量并存入Chroma的另一个集合image_collection。运行这个脚本你的多模态知识库就构建好了。所有向量都保存在本地的./chroma_db目录下。5. 实现多模态检索与问答知识库建好了最后一步就是让它“活”起来能够响应用户的查询。用户可能用文字提问也可能上传一张图片来提问。5.1 创建检索与生成链我们创建一个multimodal_rag.py文件来实现完整的问答流程。from langchain.vectorstores import Chroma from langchain.embeddings import OpenAIEmbeddings from clip_embeddings import CLIPImageEmbeddings from langchain.chat_models import ChatOpenAI # 用于生成最终答案 from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate import requests from PIL import Image import io class MultimodalRAGSystem: def __init__(self, persist_dir“./chroma_db”): # 加载已存在的向量数据库 self.text_vectordb Chroma( persist_directoryf“{persist_dir}/text_collection”, embedding_functionOpenAIEmbeddings(), collection_name“text_collection” ) self.image_vectordb Chroma( persist_directoryf“{persist_dir}/image_collection”, embedding_functionCLIPImageEmbeddings(base_url“http://localhost:7860”), # 注意这里 collection_name“image_collection” ) # 初始化大语言模型用于生成答案 self.llm ChatOpenAI(model_name“gpt-4”, temperature0) # 为文本检索链设置提示模板 text_qa_prompt PromptTemplate( input_variables[“context”, “question”], template“”“你是一个专业的助手请根据以下上下文信息回答问题。如果上下文信息不足以回答问题请如实说明。 上下文{context} 问题{question} 答案”“” ) self.text_qa_chain RetrievalQA.from_chain_type( llmself.llm, chain_type“stuff”, retrieverself.text_vectordb.as_retriever(search_kwargs{“k”: 3}), # 检索3个相关文本片段 chain_type_kwargs{“prompt”: text_qa_prompt} ) def query_with_text(self, question: str): ”“”用文本问题进行查询返回文本答案和相关图片。”“” print(f“用户提问: {question}”) # 1. 从文本库检索相关文本信息 text_answer self.text_qa_chain.run(question) print(f“基于文本的答案: {text_answer}”) # 2. 将问题本身作为“查询”从图片库检索相关图片 # 注意这里用文本问题去搜图片依赖于CLIP的跨模态能力。 # 我们需要将问题文本转换为图片向量空间中的“查询向量”。 # 但我们的CLIP服务目前只提供了图片编码API。一个变通方法是 # 假设我们有一些代表常见概念的图片或者我们无法直接进行“文搜图”。 # 更高级的实现需要服务端也暴露文本编码API。 # 这里我们简化处理如果问题中包含明显的物体名词我们可以尝试用这些名词去匹配图片元数据如果有的话。 # 这是一个待完善的环节。 print(“提示高级‘文搜图’功能需要CLIP文本编码API支持。”) # 3. 返回结果 return { “text_answer”: text_answer, “related_images”: [] # 暂时返回空或实现基于元数据的简单过滤 } def query_with_image(self, image_path: str, question: str “描述这张图片”): ”“”用图片进行查询返回相关文本和相似图片。”“” print(f“用户上传图片: {image_path} 附带问题: {question}”) # 1. 从图片库检索相似图片以图搜图 # 首先获取查询图片的向量 image_embedder CLIPImageEmbeddings(base_url“http://localhost:7860”) query_embedding image_embedder.embed_images([image_path]) if not query_embedding: return {“error”: “无法处理上传的图片。”} # 在图片库中进行相似度搜索 similar_images self.image_vectordb.similarity_search_by_vector( embeddingquery_embedding[0], k3 # 返回3张最相似的图片 ) print(f“找到 {len(similar_images)} 张相似图片:”) for img_doc in similar_images: print(f” - {img_doc.metadata.get(‘source’, ‘N/A’)}”) # 2. 根据检索到的图片关联查找相关文本信息 # 我们可以用相似图片的元数据如文件名、路径中的关键词去文本库中检索。 # 这里是一个简化示例提取第一张相似图片的文件名作为关键词 related_texts [] if similar_images: sample_image_source similar_images[0].metadata.get(“source”, “”) # 假设文件名包含描述性关键词例如 cat_sunny_day.jpg import re # 简单地从文件名中提取单词去掉扩展名和常见分隔符 filename_keywords re.sub(r‘[._-]’, ‘ ‘, os.path.basename(sample_image_source).split(‘.’)[0]).split() if filename_keywords: # 用这些关键词构造一个查询去文本库搜索 keyword_query ‘ ‘.join(filename_keywords[:3]) # 取前三个关键词 print(f“使用关键词 ‘{keyword_query}’ 检索相关文本...”) related_text_docs self.text_vectordb.similarity_search(keyword_query, k2) related_texts [doc.page_content for doc in related_text_docs] # 3. 将检索到的文本和图片信息组合让LLM生成一个综合回答 context_for_llm f“”” 用户上传了一张图片并询问“{question}”。 系统找到了以下与图片相关的信息 - 相似图片{ [img.metadata.get(‘source’, ‘’) for img in similar_images] } - 相关文本片段{‘ ‘.join(related_texts) if related_texts else ‘暂无相关文本信息。’} “”” final_answer self.llm.predict(context_for_llm “\n请根据以上信息生成一个友好、全面的回答。”) print(f“综合答案: {final_answer}”) return { “similar_images”: [img.metadata.get(“source”) for img in similar_images], “related_texts”: related_texts, “final_answer”: final_answer } # 使用示例 if __name__ “__main__”: rag_system MultimodalRAGSystem() # 示例1文本查询 print(“ 示例1文本查询 ”) result1 rag_system.query_with_text(“我们公司的主要产品是什么”) print(result1[“text_answer”]) print(“\n 示例2图片查询 ”) # 假设有一张测试图片路径 test_img_path “./data/images/example_product.jpg” if os.path.exists(test_img_path): result2 rag_system.query_with_image(test_img_path, “这张图片里的产品有什么特点”) print(result2[“final_answer”]) else: print(f“测试图片不存在: {test_img_path}”)5.2 系统工作流程解析这个MultimodalRAGSystem类封装了整个流程初始化加载之前构建好的文本和图片向量库并初始化一个大语言模型如GPT-4用于生成最终答案。文本查询 (query_with_text)用户输入一个问题。系统从文本向量库中检索出最相关的几段文本。将这些文本作为“上下文”连同问题一起发送给LLM生成一个文本答案。(高级功能)理论上可以用同样的问题去图片向量库中检索相关图片文搜图但这需要CLIP服务提供将文本编码为向量的API。我们的服务目前只提供了图片编码。这是一个可以扩展的方向。图片查询 (query_with_image)用户上传一张图片并可能附带一个问题如“描述这张图”或“这个产品用什么材料”。系统使用我们的CLIPImageEmbeddings将这张图片转换为向量。用这个向量在图片向量库中进行相似度搜索找到最相似的几张图片以图搜图。然后系统尝试从找到的相似图片中提取信息例如文件名中的关键词并用这些关键词去文本向量库中检索相关的文本描述。最后将检索到的所有信息相似图片列表、相关文本组合成一段上下文发送给LLM让它生成一个结合了图文信息的综合答案。6. 总结与展望至此我们已经完成了一个完整的、可运行的多模态RAG知识库原型。让我们回顾一下核心步骤和学到的关键点服务化我们将CLIP ViT-H-14这个复杂的模型封装成了一个提供标准API的独立服务这是工程化的第一步实现了能力解耦和复用。自定义集成通过创建CLIPImageEmbeddings类我们成功地将自定义的图片编码能力接入到LangChain的生态中。这是连接专有模型与通用框架的关键桥梁。多模态数据管理我们设计了一个包含文本和图片两个独立向量集合的知识库架构。这种分离存储、联合检索的思路是处理异构数据的一种有效方式。混合检索逻辑我们实现了初步的“文-文检索”和“图-图检索”并探索了“图-文关联检索”。真正的“文-图跨模态检索”需要模型提供双向编码能力这是我们未来可以优化的方向。6.1 下一步可以做什么这个原型已经具备了强大的潜力你可以从以下几个方向深化它完善CLIP服务为你的CLIP服务增加文本编码API从而实现真正的、高效的“用文字搜图片”功能。优化检索策略实现更智能的混合检索。例如同时用文字问题检索文本和图片如果支持文搜图然后将所有结果进行重排序Rerank选出最相关的信息送给LLM。丰富数据源接入更多类型的文档加载器PDF、PPT、Word、网页并探索为图片自动生成文字描述使用BLIP、LLaVA等模型然后将描述文本也存入数据库作为图文关联的桥梁。构建前端界面使用Gradio、Streamlit或前端框架打造一个用户友好的Web界面让用户可以拖拽上传图片、输入问题并直观地看到检索到的图片和生成的答案。部署与优化考虑将整个系统容器化用Docker Compose管理CLIP服务、向量数据库和Web应用并部署到云服务器上。通过本教程你不仅学会了如何集成CLIP与LangChain更重要的是掌握了一种思路将前沿的AI模型能力通过标准化接口暴露出来再通过灵活的框架将其组合成解决实际问题的应用。这种能力正是构建下一代智能系统的关键。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

相关新闻