用 Marqo 实现 5 行代码的文本搜图(Text-to-Image Search):完整实战指南

📅 发布时间:2026/10/13 2:04:03
用 Marqo 实现 5 行代码的文本搜图(Text-to-Image Search):完整实战指南
搜索引擎AI 应用后端电商NLP【免费下载链接】marqoEcommerce Search and Discovery - marqo.ai项目地址https://gitcode.com/gh_mirrors/ma/marqo点击查看免费下载导读本文基于 Marqo 开源张量搜索引擎完整演示如何用极简代码搭建一套「以文搜图」Text-to-Image Search系统从 Docker 启动服务、创建多模态索引、让容器内引擎访问本地图片到一句search()返回最相关的图像。读完本文你将掌握 Marqo 多模态检索的核心 APIcreate_index/add_documents/search理解treatUrlsAndPointersAsImages、CLIP 家族模型选择等关键配置在源码中的实际作用并具备把示例从 5 张图扩展到大规模数据集的实战能力。Marqo 是一个开源的张量向量搜索引擎原生支持多模态检索文本搜图、图搜文本、图搜图都可以在同一个索引上完成。本指南以 5 张来自 COCO 数据集的图片为示例走通安装 → 建索引 → 灌数据 → 搜索 → 可视化全流程。完整可运行代码位于仓库的 imagesearchguide.ipynb另有纯 Python 版 app.py 可直接对照执行。一、环境搭建启动 Marqo 服务与安装客户端1.1 用 Docker 启动 MarqoMarqo 以 Docker 镜像形式分发官方推荐直接拉取运行以下命令在 x64 Linux 上验证Apple M 系列芯片用户需参考项目 README 中的 M 系列说明。执行docker rm -f marqo docker pull marqoai/marqo:2.0.0 docker run --name marqo -it -p 8882:8882 --add-host host.docker.internal:host-gateway marqoai/marqo:2.0.0这里有几个值得留意的参数参数作用-p 8882:8882将容器内 8882 端口映射到宿主机Marqo 的 HTTP API 与客户端默认端口均为 8882--add-host host.docker.internal:host-gateway关键让容器内可以通过host.docker.internal访问宿主机网络本文后续本地图片直连方案依赖它--name marqo便于后续docker rm -f marqo清理重建本文写作时对应 Marqo 2.0.0 版本当前仓库源码已持续演进例如模型注册表已引入 Triton 推理编码器配置但create_index/add_documents/search这套核心 API 保持一致下述代码在后续版本中同样适用。1.2 安装 Marqo 客户端新建 Python 环境并安装官方客户端与可视化依赖conda create -n marqo-client python3.8 conda activate marqo-client pip install marqo matplotlib安装完成后验证连通性import marqo mq marqo.Client(http://localhost:8882)marqo.Client是全部操作的入口后续的索引创建、文档写入与搜索都由它发起。二、准备示例数据5 张 COCO 图片本指南从 COCO 数据集 目录中image0.jpg~image4.jpg无需自行下载。如果从零搭建你可以从该目录获得图片后放入本地./data/形成如下目录结构数据就绪后进入核心环节——搜索。三、创建多模态索引create_indexMarqo 的索引是所有操作索引、检索的载体。创建索引时需要指定模型与索引能力参数本示例只做最简配置index_name image-search-guide settings { model: open_clip/ViT-B-32/laion2b_s34b_b79k, treatUrlsAndPointersAsImages: True, } mq.create_index(index_name, settings_dictsettings)两个参数缺一不可treatUrlsAndPointersAsImages: True—— 多模态检索的开关。必须开启否则 URL 与指针一律按纯文本处理图片永远不会被当作图像嵌入。model: open_clip/ViT-B-32/laion2b_s34b_b79k—— 必须从CLIP 家族模型中选择open_clip/前缀即代表 OpenCLIP 系列因为 CLIP 将图像与文本映射到同一向量空间这正是图文互搜的数学基础。3.1 从源码理解这个模型在仓库的 model_registry.py 中可以查到该模型的注册条目open_clip/ViT-B-32/laion2b_s34b_b79k: { name: hf-hub:laion/CLIP-ViT-B-32-laion2B-s34B-b79K, dimensions: 512, type: open_clip, tritonImageEncoderProperties: { ... dims: [3, 224, 224] ... dims: [512] ... }, tritonTextEncoderProperties: { ... dims: [77] ... dims: [512] ... }, }要点模型实际对应laion/CLIP-ViT-B-32-laion2B-s34B-b79K输出向量维度 512图像编码器输入尺寸为3×224×224标准 CLIP 预处理后的 RGB 图像输出 512 维向量文本编码器以 token id 序列长度 77CLIP 的上下文窗口为输入同样输出 512 维向量——文本与图像最终落在同一 512 维向量空间检索时直接做向量相似度比较。3.2 从源码理解treatUrlsAndPointersAsImages该配置是非结构化索引Unstructured Index专属字段。在 marqo_index.py 中class UnstructuredMarqoIndex(MarqoIndex): type IndexType.Unstructured treat_urls_and_pointers_as_images: bool treat_urls_and_pointers_as_media: Optional[bool] None而在 unstructured_add_document_handler.py 的_infer_modality中该开关直接决定文档字段的类型推断路径if (not self.marqo_index.treat_urls_and_pointers_as_images and not self.marqo_index.treat_urls_and_pointers_as_media): return Modality.TEXT也就是说两个开关都关闭时所有内容一律按 TEXT 处理这正是必须开启图片开关的源码级原因开启后Marqo 才会调用infer_modality见 modality_utils.py按URL 文件扩展名 → MIME 类型嗅探 → base64 前缀的顺序判断内容到底是图片、视频、音频还是文本最终决定走哪条嵌入流水线。Modality枚举定义在 modality.pyTEXT、IMAGE、VIDEO、AUDIO。四、让容器内的 Marqo 访问本地图片Marqo 运行在 Docker 容器中默认无法直接读取宿主机磁盘上的图片。最朴素的思路是把图片上传到公网用 URL 访问——5 张图没问题但百万级数据集显然不可行。这里给出更优雅的解法在宿主机起一个极简 HTTP 文件服务器再通过host.docker.internal映射地址访问。4.1 启动本地静态文件服务import subprocess local_dir ./data/ pid subprocess.Popen([python3, -m, http.server, 8222, --directory, local_dir], stdoutsubprocess.DEVNULL, stderrsubprocess.STDOUT)这条命令在宿主机 8222 端口起了一个静态文件服务服务根目录指向./data/。4.2 生成 Marqo 可访问的 Docker 地址import glob import os # Find all the local images locators glob.glob(local_dir *.jpg) # Generate docker path for local images docker_path http://host.docker.internal:8222/ image_docker [docker_path os.path.basename(f) for f in locators] print(image_docker)输出[http://host.docker.internal:8222/image4.jpg, http://host.docker.internal:8222/image1.jpg, http://host.docker.internal:8222/image3.jpg, http://host.docker.internal:8222/image0.jpg, http://host.docker.internal:8222/image2.jpg]原理说明启动 Docker 时的--add-host host.docker.internal:host-gateway把host.docker.internal指向宿主机网关容器内 Marqo 便能通过该域名访问宿主机 8222 端口上的图片服务。这套本地 HTTP 服务 容器域名映射方案可无缝扩展到任意规模的本地数据集——无论 5 张还是 100 万张只需把文件放入目录即可。五、写入文档一行add_documents完成图片索引Marqo 要求输入为list[dict]形式的文档列表。把图片地址映射为文档documents [{image_docker : image, _id : str(idx)} for idx, image in enumerate(image_docker)] print(documents)输出[{image_docker: http://host.docker.internal:8222/image4.jpg, _id: 0}, {image_docker: http://host.docker.internal:8222/image1.jpg, _id: 1}, {image_docker: http://host.docker.internal:8222/image3.jpg, _id: 2}, {image_docker: http://host.docker.internal:8222/image0.jpg, _id: 3}, {image_docker: http://host.docker.internal:8222/image2.jpg, _id: 4}]image_docker自定义字段名值为图片 URL_id文档主键此处用序号唯一标识。随后调用add_documents()完成索引mq.index(index_name).add_documents(documents, tensor_fields[image_docker], devicecpu, client_batch_size 1)参数解读参数含义tensor_fields[image_docker]指定需要做张量向量化的字段。只有出现在tensor_fields中的字段才会被嵌入、参与相似度检索devicecpu推理设备。无 GPU 时用 CPU 即可完成若按官方 GPU 指南启用 CUDA改为devicecuda可显著提速client_batch_size1客户端分批大小。设为 1 便于观察逐条处理日志批量越大吞吐越高5.1 索引过程日志解读执行后会输出逐批处理日志2022-12-30 04:27:45,846 logger:marqo INFO add_documents pre-processing: took 0.000s for 5 docs, for an average of 0.000s per doc. 2022-12-30 04:27:45,848 logger:marqo INFO starting batch ingestion with batch size 1 2022-12-30 04:27:47,130 logger:marqo INFO add_documents batch 0 roundtrip: took 1.281s to add 1 docs, for an average of 1.281s per doc. 2022-12-30 04:27:47,131 logger:marqo INFO add_documents batch 0 Marqo processing: took 1.275s for Marqo to process index 1 docs (server unbatched), for an average of 1.275s per doc. ... 2022-12-30 04:27:51,573 logger:marqo INFO completed batch ingestion. 2022-12-30 04:27:51,575 logger:marqo INFO add_documents completed. total time taken: 5.729s.可见每张图约 1.0~1.3 秒CPU 推理 写入索引5 张图总计约 5.7 秒完成。日志中的roundtrip客户端往返耗时与Marqo processing服务端处理耗时两类指标可用于评估数据导入性能。5.2 底层调用链从源码看add_documents服务端入口在 document.py它先根据索引类型分发到StructuredAddDocumentsHandler/SemiStructuredAddDocumentsHandler/UnstructuredAddDocumentsHandler再交由对应的 handler 完成字段校验、模态推断、媒体下载与嵌入最后写入底层 Vespa 向量存储。本示例创建的是非结构化索引因此走UnstructuredAddDocumentsHandler分支其中_infer_modality判定image_docker字段为 IMAGE 模态后才会调用 CLIP 图像编码器生成 512 维向量对应 model_registry.py 中的tritonImageEncoderProperties。六、搜索一句search()返回结果图片全部入库后即可搜索。假设我们想找A rider on a horse jumping over the barrier骑马跨越障碍的骑手search_results mq.index(index_name).search(A rider on a horse jumping over the barrier, limit1, devicecpu)输出2022-12-30 04:28:56,472 logger:marqo INFO search (tensor): took 0.364s to send query and received 1 results from Marqo (roundtrip). Marqo itself took 0.358s to execute the search.仅用约 0.36 秒且无 GPU 加速就完成了检索。结果结构如下print(search_results)输出{hits: [{image_docker: http://host.docker.internal:8222/image1.jpg, _id: 1, _highlights: [{image_docker: http://host.docker.internal:8222/image1.jpg}], _score: 0.6133688}], processingTimeMs: 358, query: A rider on a horse jumping over the barrier, limit: 1}字段含义字段含义hits[]._id命中文档主键本例为1hits[]._score相似度分数本例 0.613越大越相关hits[]._highlights命中高亮字段展示命中的原始内容此处为图片 URLprocessingTimeMsMarqo 服务端搜索耗时358msquery原始查询文本limit请求返回条数七、可视化验证结果返回的是一串 URL肉眼难以直接判断相关性——把它渲染成图片验证import requests from PIL import Image fig_path search_results[hits][0][image_docker].replace(docker_path, local_dir) display(Image.open(fig_path))技巧说明docker_path替换回local_dir即把http://host.docker.internal:8222/image1.jpg映射成本地路径./data/image1.jpg直接打开。结果正是我们要找的骑马跨越障碍的骑手。至此从 Docker 启动到完成一次文本搜图核心代码不足 10 行。八、进阶扩展从 5 张图走向大规模数据集本示例只用了 5 张图要支撑更大规模的数据集可以从以下几个方向扩展扩充数据直接把更多图片支持 jpg/png/webp/tiff 等常见格式放进./data/目录glob.glob(local_dir *.jpg)会自动收拢全部文件无需改任何其他代码换模型在 model_registry.py 中可查看全部预置模型除open_clip/ViT-B-32/laion2b_s34b_b79k外还有如open_clip/ViT-B-16-SigLIP/webli768 维等其他 CLIP 系模型可按精度/速度需求替换settings[model]GPU 加速按官方 CUDA 启用指南配置后将add_documents与search的device参数改为cuda可大幅缩短嵌入与检索耗时调整批处理client_batch_size从 1 调大如 32、64批量嵌入能显著提升数据导入吞吐多模态互搜因为文本与图像共享同一向量空间把查询换成图片 URL 即可实现图搜图把文档字段换成文本即可实现文搜文——同一个索引天然支持 text-to-image / image-to-text / image-to-image 全部方向。九、要点回顾用 Marqo 实现多模态检索只需四步环境准备conda建环境 pip install marqo创建索引mq.create_index(...)CLIP 家族模型 treatUrlsAndPointersAsImagesTrue写入文档mq.index(index_name).add_documents(..., tensor_fields[...])搜索mq.index(index_name).search(查询文本, limit...)。核心诀窍再强调一次多模态功能必须显式开启treatUrlsAndPointersAsImages: True——在源码层面只有该开关打开Marqo 才会对 URL 内容做模态推断unstructured_add_document_handler.py否则一律按纯文本处理图文互搜便无从谈起。动手试试吧修改查询文本、换一批图片、调整limit与模型感受张量搜索在多模态场景下的威力。仓库中的 imagesearchguide.ipynb 与 app.py 提供了可直接运行的完整代码是继续深入 Marqo 的最佳起点。赞分享搜索引擎AI 应用后端电商NLP【免费下载链接】marqoEcommerce Search and Discovery - marqo.ai项目地址https://gitcode.com/gh_mirrors/ma/marqo点击查看免费下载相关推荐Infisical开源密钥管理平台的三大核心场景与技术实现Infisical开源密钥管理平台的三大核心场景与技术实现 想象一下这样的场景你的团队正在开发一个微服务架构的应用数据库密码、API密钥、第三方服务凭证等后端前端密钥管理应用安全认证鉴权Xinference 图像模型能力完全指南Text-to-Image、Image-to-Image、OCR 与整档解析实战Xinference 图像模型能力完全指南Text to Image、Image to Image、OCR 与整档解析实战 导读 Xinference 的图像模型推理服务人工智能大模型本地部署多模态语音Diffusers 文本生成图像Text-to-Image模型微调实战指南Diffusers 文本生成图像Text to Image模型微调实战指南 本指南基于 Diffusers 仓库的 韩文版 Text to image 训练人工智能媒体生成深度学习音频上一篇Apache MXNet 在 NVIDIA Jetson 上的安装与部署从源码构建、Docker 交叉编译到 GPU 推理验证下一篇FBShimmeringView生命周期管理避免内存泄漏最佳实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考