CLIP 图文检索系统构建跨模态语义搜索引擎1. 引言CLIP (Contrastive Language-Image Pre-training) 是 OpenAI 在 2021 年提出的跨模态模型它将图像和文本映射到同一个语义空间使得用文字搜图片和用图片搜文字成为可能。应用场景电商商品图搜“红色连衣裙” → 搜出所有红色连衣裙图片素材管理输入描述 → 找到匹配的设计素材内容审核图片 → 检索相似违规内容医学影像检索症状描述 → 找到相似病例影像2. CLIP 原理2.1 双塔架构图像 → Image Encoder (ViT/ResNet) → 图像嵌入向量 ─┐ ├→ 对比学习 → 余弦相似度 文本 → Text Encoder (Transformer) → 文本嵌入向量 ─┘2.2 对比学习目标给定一个 batch 的 N 个 (图像, 文本) 对 - 正样本对匹配的 (图像_i, 文本_i) - 负样本对不匹配的 (图像_i, 文本_j), j ≠ i 损失函数对称交叉熵损失 L 0.5 * (L_image_to_text L_text_to_image) 目标正样本对的余弦相似度最大化负样本对最小化3. 环境搭建pipinstalltorch torchvision pipinstalltransformers pipinstallfaiss-gpu# GPU 版 FAISSpipinstallpillow numpy4. 图像特征提取importtorchfromtransformersimportCLIPModel,CLIPProcessorfromPILimportImageimportosimportnumpyasnpclassCLIPFeatureExtractor:CLIP 特征提取器def__init__(self,model_nameopenai/clip-vit-large-patch14):self.devicecudaiftorch.cuda.is_available()elsecpuself.modelCLIPModel.from_pretrained(model_name).to(self.device)self.processorCLIPProcessor.from_pretrained(model_name)self.model.eval()torch.no_grad()defencode_image(self,image_path:str)-np.ndarray:提取单张图像特征imageImage.open(image_path).convert(RGB)inputsself.processor(imagesimage,return_tensorspt)inputs{k:v.to(self.device)fork,vininputs.items()}featuresself.model.get_image_features(**inputs)featuresfeatures/features.norm(dim-1,keepdimTrue)# L2 归一化returnfeatures.cpu().numpy().flatten()torch.no_grad()defencode_text(self,text:str)-np.ndarray:提取文本特征inputsself.processor(text[text],return_tensorspt,paddingTrue)inputs{k:v.to(self.device)fork,vininputs.items()}featuresself.model.get_text_features(**inputs)featuresfeatures/features.norm(dim-1,keepdimTrue)returnfeatures.cpu().numpy().flatten()defencode_batch_images(self,image_paths:list,batch_size32)-np.ndarray:批量提取图像特征all_features[]foriinrange(0,len(image_paths),batch_size):batch_pathsimage_paths[i:ibatch_size]images[Image.open(p).convert(RGB)forpinbatch_paths]inputsself.processor(imagesimages,return_tensorspt,paddingTrue)inputs{k:v.to(self.device)fork,vininputs.items()}featuresself.model.get_image_features(**inputs)featuresfeatures/features.norm(dim-1,keepdimTrue)all_features.append(features.cpu().numpy())returnnp.vstack(all_features)5. 向量索引与检索5.1 使用 FAISS 构建索引importfaissclassCLIPSearchEngine:基于 CLIP FAISS 的图文检索引擎def__init__(self,model_nameopenai/clip-vit-large-patch14):self.extractorCLIPFeatureExtractor(model_name)self.indexNoneself.image_paths[]self.dimension768# ViT-L/14 输出维度defbuild_index(self,image_dir:str):构建图像索引# 收集所有图片extensions{.jpg,.jpeg,.png,.bmp,.webp}self.image_paths[os.path.join(image_dir,f)forfinos.listdir(image_dir)ifos.path.splitext(f)[1].lower()inextensions]print(f索引{len(self.image_paths)}张图片...)# 提取特征featuresself.extractor.encode_batch_images(self.image_paths)featuresfeatures.astype(float32)# 构建 FAISS 索引iflen(self.image_paths)10000:# 小规模精确搜索self.indexfaiss.IndexFlatIP(self.dimension)# 内积 余弦相似度已归一化else:# 大规模IVF 近似搜索nlistmin(int(len(self.image_paths)**0.5),1000)quantizerfaiss.IndexFlatIP(self.dimension)self.indexfaiss.IndexIVFFlat(quantizer,self.dimension,nlist)self.index.train(features)self.index.nprobe20# 搜索的聚类数self.index.add(features)print(f索引构建完成共{self.index.ntotal}向量)defsave_index(self,path:str):保存索引到磁盘faiss.write_index(self.index,f{path}.index)np.save(f{path}.paths.npy,np.array(self.image_paths))defload_index(self,path:str):加载索引self.indexfaiss.read_index(f{path}.index)self.image_pathsnp.load(f{path}.paths.npy).tolist()defsearch_by_text(self,query:str,top_k10)-list:用文字搜图片text_featuresself.extractor.encode_text(query).astype(float32)text_featurestext_features.reshape(1,-1)scores,indicesself.index.search(text_features,top_k)results[]forscore,idxinzip(scores[0],indices[0]):results.append({path:self.image_paths[idx],score:float(score),})returnresultsdefsearch_by_image(self,image_path:str,top_k10)-list:用图片搜图片img_featuresself.extractor.encode_image(image_path).astype(float32)img_featuresimg_features.reshape(1,-1)scores,indicesself.index.search(img_features,top_k)results[]forscore,idxinzip(scores[0],indices[0]):results.append({path:self.image_paths[idx],score:float(score),})returnresults5.2 使用示例# 构建索引engineCLIPSearchEngine()engine.build_index(/data/product_images)engine.save_index(product_index)# 文字搜图resultsengine.search_by_text(红色连衣裙时尚风格,top_k5)forrinresults:print(f{r[score]:.3f}|{r[path]})# 以图搜图resultsengine.search_by_image(query.jpg,top_k5)forrinresults:print(f{r[score]:.3f}|{r[path]})6. Web API 服务fromfastapiimportFastAPI,UploadFile,Filefromfastapi.responsesimportJSONResponseimportshutil,tempfile appFastAPI()engineCLIPSearchEngine()engine.load_index(product_index)app.get(/search/text)asyncdefsearch_text(query:str,top_k:int10):resultsengine.search_by_text(query,top_k)returnJSONResponse(content{results:results})app.post(/search/image)asyncdefsearch_image(file:UploadFileFile(...),top_k:int10):withtempfile.NamedTemporaryFile(deleteFalse,suffix.jpg)astmp:shutil.copyfileobj(file.file,tmp)tmp_pathtmp.name resultsengine.search_by_image(tmp_path,top_k)os.unlink(tmp_path)returnJSONResponse(content{results:results})# 启动服务uvicorn app:app--host0.0.0.0--port8000# 测试curlhttp://localhost:8000/search/text?query蓝色运动鞋top_k57. 性能优化优化方法适用场景效果FAISS IVF10K 图片搜索速度提升 10xFAISS PQ1M 图片内存减少 8-16xGPU 索引实时搜索毫秒级响应特征缓存重复查询避免重复编码批量编码建索引时吞吐提升 5x8. 总结CLIP 图文检索的核心优势在于语义理解——不需要精确的关键词匹配只要语义相关就能检索到。结合 FAISS 向量数据库可以实现百万级图片的毫秒级检索。
企业数字化 ERP 产品动态
相关推荐
从逆向工程到爆破登录:Web安全入门实战与防御思路 1. 项目概述:从“点登录”到“懂安全”的思维跃迁在网络安全这个庞大而迷人的领域里,“逆向”和“爆破”这两个词总是带着一丝神秘色彩,尤其对于刚入门的新手而言。很多人一听到“爆破登录”,脑海里可能立刻浮现出电影里黑客在键盘… · 2026/9/25 10:30:59
MC34VR500电源管理芯片:为网络处理器提供集成化电源解决方案 1. 项目概述:为什么网络处理器需要一个“全能”电源管家?在物联网网关、工业路由器或者网络存储设备这类嵌入式系统的核心板上,最核心的芯片往往不是CPU,而是为整个系统提供“血液”的电源管理单元。我经手过不少项目,… · 2026/9/24 21:28:43
【Maven系列3】坐标与依赖 主要讲解Maven的依赖管理相关知识。前言Maven确实比较简单,估计大家在工作期间,每天抽出一点时间,基础部分几天就可以学完,所以这块感觉真没有啥好讲的。这篇文章先讲解依赖管理的基础知识,然后再结合一个简单的示例消… · 2026/9/25 18:27:59
【MyBatis系列1】基础知识(上) 主要讲解 MyBatis 的基础知识前言上个月完成了“Java 并发编程系列”和“Spring 基础系列”知识的学习,这个月我们主要学习 MyBatis。MyBatis 网上的资料真的非常多,整个知识体系也非常系统,如果要整体学完,需要花很多时间&#x… · 2026/9/25 18:27:59
【Maven系列2】生命周期与插件 讲解Maven的生命周期和插件,以及常用的命令。Maven生命周期三套生命周期Maven 的生命周期并不是一个整体,Maven 拥有三套相互独立的生命周期:clean:项目清理的处理default(或 build):项目部署的处理site:项… · 2026/9/25 18:27:53
Apereo CAS 作为 OAuth 2.0 服务端:协议端点、配置属性与授权流实战指南 后端认证鉴权单点登录 【免费下载链接】cas Apereo CAS - Identity & Single Sign On for all earthlings and beyond. 项目地址: https://gitcode.com/gh_mirrors/ca/cas 点击查看 免费下载 本文以 Apereo CAS 官方文档为骨架,系统讲解如何将 CAS … · 2026/9/25 18:27:47
色弱色盲色彩校正:Daltonization 算法在前端看板中的实战 色弱色盲色彩校正:Daltonization 算法在前端看板中的实战在全人类人口结构中,有超过 $8%$ 的男性与 $0.5%$ 的女性(全球近 3 亿人) 患有不同程度的先天性色觉障碍(Color Vision Deficiency, CVD / 俗称色弱与色盲&… · 2026/9/25 18:27:29
大模型辅助的 ARIA 角色与状态图谱自动标注流水线 大模型辅助的 ARIA 角色与状态图谱自动标注流水线在复杂的企业级前端富交互组件(如多层嵌套树形控件 TreeView、复合分段选项卡 Tabs、无级滑动滑块 Slider、以及网格数据表格 DataGrid)开发中,无障碍 ARIA 角色(Roles)… · 2026/9/25 18:27:23
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37