基于milvus的多模态检索

基于milvus的多模态检索

  • 1.Milvus简介(2019)
    • [1.1 什么是向量检索](#1.1 什么是向量检索)
  • [2 Milvus安装](#2 Milvus安装)
    • [2.1 服务器配置](#2.1 服务器配置)
    • [2.2 前提条件](#2.2 前提条件)
    • [2.3 启动attu](#2.3 启动attu)
    • [2.4 下载示例代码进行测试](#2.4 下载示例代码进行测试)
  • [3 多模态搜索场景](#3 多模态搜索场景)
    • [3.1 文搜图](#3.1 文搜图)
    • [3.2 图搜图(相似图片搜索)](#3.2 图搜图(相似图片搜索))
    • [3.3 使用 Milvus Python SDK 实现多模态搜索](#3.3 使用 Milvus Python SDK 实现多模态搜索)

本文包含milvus安装使用、attu 可视化,完整指南启动 Milvus 进行了向量相似度搜索,利用CLIP模型进行多模态检索,附完整代码。

1.Milvus简介(2019)

1.1 什么是向量检索

向量是具有一定大小和方向的量,可以简单理解为一串数字的集合,就像一行多列的矩阵,比如:2,0,1,9,0,6,3,0。每一行代表一个数据项,每一列代表一个该数据项的各个属性。特征向量是包含事物重要特征的向量。大家比较熟知的一个特征向量是RGB (红-绿-蓝)色彩。每种颜色都可以通过对红®、绿(G)、蓝(B)三种颜色的比例来得到。这样一个特征向量可以描述为:颜色 = 红,绿,蓝。向量检索是指从向量库中检索出距离目标向量最近的 K 个向量。一般我们用两个向量间的欧式距离,余弦距离等来衡量两个向量间的距离,一次来评估两个向量的相似度。

2 Milvus安装

2.1 服务器配置

复制代码
(milvus) [root@ecs-86676-0005 suanfa_jingxiang]# docker --version
Docker version 19.03.9, build 9d988398e7
(milvus) [root@ecs-86676-0005 suanfa_jingxiang]# docker-compose --version
Docker Compose version v2.21.0
(milvus) [root@ecs-86676-0005 suanfa_jingxiang]# uname -a
Linux ecs-86676-0005 4.18.0-348.7.1.el8_5.x86_64 #1 SMP Wed Dec 22 13:25:12 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

使用Docker Compose安装 Milvus standalone(即单机版),进行一个快速milvus的体验。

2.2 前提条件

  • 系统可以使用centos或者ubuntu
  • 系统已经安装docker和docker-compose
  • milvus版本这里选择2.3.1。由于milvus依赖etcd和minio,因此需要先启动这2个组件。同样也使用docker进行启动。

etcd:用来存储milvus的元数据。

minio:用来存储milvus的向量数据和索引数据。

下载milvus-standalone-docker-compose.yml 文件,保存为docker-compose.yml:

复制代码
wget https://github.com/milvus-io/milvus/releases/download/v2.3.1/milvus-standalone-docker-compose.yml -O docker-compose.yml

这里经过了一定修改,让其更加方便使用。

这个yml文件里面定义了etcd、minio、milvus的启动参数。

修改后的docker-compose.yml文件内容如下:

复制代码
version: '3.5'

services:
  etcd:
    container_name: milvus-etcd
    image: quay.io/coreos/etcd:v3.5.5
    environment:
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1000
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_SNAPSHOT_COUNT=50000
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
    ports:
      - "2379:2379"
    command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
    healthcheck:
      test: ["CMD", "etcdctl", "endpoint", "health"]
      interval: 5s
      timeout: 3s
      retries: 10

  minio:
    container_name: milvus-minio
    image: minio/minio:RELEASE.2023-03-20T20-16-18Z
    environment:
      MINIO_ACCESS_KEY: minioadmin
      MINIO_SECRET_KEY: minioadmin
    ports:
      - "9001:9001"
      - "9000:9000"
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data
    command: minio server /minio_data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 5s
      timeout: 3s
      retries: 10

  standalone:
    container_name: milvus-standalone
    image: milvusdb/milvus:v2.3.1
    command: ["milvus", "run", "standalone"]
    environment:
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
      interval: 30s
      start_period: 90s
      timeout: 20s
      retries: 3
    ports:
      - "19530:19530"
      - "9091:9091"
    depends_on:
      - "etcd"
      - "minio"

networks:
  default:
    name: milvus

然后后台启动这些容器:

复制代码
docker-compose up -d
-d 代表后台启动
使用ps命令查看容器:

如果看到healthy状态,说明容器内的服务可以正常使用了。

2.3 启动attu

attu为milvus的一款图形化管理工具,非常方便对milvus的一些管理。

启动attu:

复制代码
docker pull zilliz/attu:v2.3.0l
docker run -d --name=attu -p 2099:3000 -e MILVUS_URL=your ip zilliz/attu:v2.3.0

2.4 下载示例代码进行测试

下载 hello_milvus.py 直接或使用以下命令

复制代码
wget https://raw.githubusercontent.com/milvus-io/pymilvus/v2.2.8/examples/hello_milvus.py
or
wget https://raw.githubusercontent.com/milvus-io/pymilvus/v2.2.x/examples/hello_milvus.py

3 多模态搜索场景

3.1 文搜图

文搜图是指用自然语言描述来检索相关图片。这种搜索模式结合了文本理解和图像特征匹配。

  • 实现步骤:
  1. 使用 Chinese CLIP 模型将查询文本转换为向量。
  2. 在 Milvus 中搜索与查询向量最相似的图片向量。

3.2 图搜图(相似图片搜索)

图搜图允许用户上传一张图片,然后找到数据库中与之相似的图片。

  • 实现步骤:
  1. 使用 Chinese CLIP 模型提取查询图片的特征向量。
  2. 在 Milvus 中执行向量相似度搜索,找出最相似的图片。

3.3 使用 Milvus Python SDK 实现多模态搜索

下面我们将使用 Milvus Python SDK 来演示如何实现上述搜索场景的核心功能。Milvus 提供了多种 API 和 SDK,包括 RESTful API、Python SDK (PyMilvus)、Go SDK、Java SDK、Node.js SDK,以及由 Microsoft 贡献的 C# SDK。在本例中,我们将使用 PyMilvus 。

  1. 连接Milvus并创建集合
    这里,我们使用了 IVF_FLAT 索引。Milvus 支持多种索引类型,包括 FLAT、IVF_FLAT、IVF_SQ8、IVF_PQ、HNSW、ANNOY 等,您可以根据具体需求选择合适的索引类型。
    首先,安装必要的库:

    pip install pymilvus clip torch pillow

完整代码如下:

复制代码
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType, utility
import os
import torch
from PIL import Image
import clip


class MilvusImageSearch:
    def __init__(self, host="0.0.0.0", port="19530", top_k=2, save_folder="output", image_folder_path="data"):
        self.top_k = top_k
        self.save_folder = save_folder
        self.image_folder_path = image_folder_path

        # 连接到 Milvus 服务器
        connections.connect("default", host=host, port=port)
        self.collection_name = "multimodal_image_collection"

        if utility.has_collection(self.collection_name):
            utility.drop_collection(self.collection_name)

        # 定义 Collection Schema
        fields = [
            FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=False),
            FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=512),
            FieldSchema(name="image_path", dtype=DataType.VARCHAR, max_length=512)
        ]
        schema = CollectionSchema(fields, "多模态图片集合")
        self.collection = Collection(self.collection_name, schema)

        # 创建索引以加快搜索速度
        index_params = {"metric_type": "L2", "index_type": "IVF_FLAT", "params": {"nlist": 1024}}
        self.collection.create_index("embedding", index_params)

        # 加载 CLIP 模型
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        self.model, self.preprocess = clip.load("ViT-B/32", device=self.device)

    def image_to_vector(self, image_path):
        image = Image.open(image_path)
        image = self.preprocess(image).unsqueeze(0).to(self.device)
        with torch.no_grad():
            image_features = self.model.encode_image(image)
        return image_features.cpu().numpy()[0]

    def batch_insert_images(self, batch_size=10):
        image_files = [os.path.join(self.image_folder_path, img) for img in os.listdir(self.image_folder_path)
                       if img.lower().endswith(('.png', '.jpg', '.jpeg'))]

        ids, embeddings, image_paths = [], [], []
        for i, image_file in enumerate(image_files):
            try:
                vector = self.image_to_vector(image_file)
                ids.append(i + 1)
                embeddings.append(vector)
                image_paths.append(image_file)
            except Exception as e:
                print(f"处理图片 {image_file} 出错: {e}")
                continue

            if len(embeddings) == batch_size:
                try:
                    self.collection.insert([ids, embeddings, image_paths])
                    print(f"成功插入 {len(ids)} 条记录")
                    ids, embeddings, image_paths = [], [], []
                except Exception as e:
                    print(f"插入错误: {e}")

        if embeddings:
            try:
                self.collection.insert([ids, embeddings, image_paths])
                print(f"成功插入剩余的 {len(ids)} 条记录")
            except Exception as e:
                print(f"插入错误: {e}")

        self.collection.flush()
        self.collection.load()

    def search_images_by_text(self, query_text):
        try:
            text_inputs = clip.tokenize([query_text]).to(self.device)
            with torch.no_grad():
                text_features = self.model.encode_text(text_inputs)
            query_vector = text_features.cpu().numpy()[0]
            search_params = {"metric_type": "L2", "params": {"nprobe": 10}}
            results = self.collection.search([query_vector], "embedding", search_params, limit=self.top_k,
                                             output_fields=["id", "image_path"])
            return results
        except Exception as e:
            print(f"搜索错误: {e}")
            return []

    def search_similar_images(self, query_image_path):
        try:
            query_vector = self.image_to_vector(query_image_path)
            search_params = {"metric_type": "L2", "params": {"nprobe": 10}}
            results = self.collection.search([query_vector], "embedding", search_params, limit=self.top_k,
                                             output_fields=["id", "image_path"])
            return results
        except Exception as e:
            print(f"搜索错误: {e}")
            return []

    def display_results(self, results):
        os.makedirs(self.save_folder, exist_ok=True)  # 创建保存图片的目录
        for i, result in enumerate(results[0]):
            print(f"Top {i + 1} 匹配图片ID: {result.id}, 距离: {result.distance}")
            image_path = result.entity.get("image_path")
            print(f"图片路径: {image_path}")
            try:
                image = Image.open(image_path)
                image.show()
                # 保存图片到指定目录
                save_path = os.path.join(self.save_folder, f"result_{i + 1}.jpg")
                image.save(save_path)
                print(f"图片已保存到: {save_path}")
            except Exception as e:
                print(f"显示图片出错: {e}")


# 使用示例
search_system = MilvusImageSearch(top_k=3, save_folder="output", image_folder_path="data")

# 插入图片
search_system.batch_insert_images()

# 根据文本搜索图片
query = "dog"
results = search_system.search_images_by_text(query)
print(f"查询文本: '{query}'")
search_system.display_results(results)

# 根据图片搜索相似图片
query_image = "./data/4.jpeg"
results = search_system.search_similar_images(query_image)
print(f"查询图片: '{query_image}'")
search_system.display_results(results)

结果展示

参考:https://blog.csdn.net/weixin_44839084/article/details/142828988?ops_request_misc=\&request_id=\&biz_id=102\&utm_term=基于milvus的多模态检索\&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb\~default-0-142828988.142^v100^pc_search_result_base4\&spm=1018.2226.3001.4187

相关推荐
Navicat中国29 分钟前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI1 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区1 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris3 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy3 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V3 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai
AI_AGENT_DEV_AI3 小时前
AI英语教培智能体的开发
人工智能
物联网软硬件开发-轨物科技3 小时前
【技术白皮书】光伏清检一体化机器人系统解决方案技术白皮书
大数据·运维·网络·人工智能·机器人
海宇数据4 小时前
零信任架构实战:基于海宇网上踪迹欺诈行为识别构建自动化信贷资信评估网关
运维·人工智能·架构·自动化
冬奇Lab4 小时前
开源项目第242期:HyperFrames —— 写 HTML 就能渲染视频,专为 AI Agent 设计的确定性视频框架
人工智能·开源·音视频开发