You need to enable JavaScript to run this app.
文档中心
向量数据库 Milvus 版

向量数据库 Milvus 版

复制全文
下载 pdf
最佳实践
向量数据库 Milvus 版与方舟大模型的图文多模态混合搜索实践
复制全文
下载 pdf
向量数据库 Milvus 版与方舟大模型的图文多模态混合搜索实践
随着 AI 技术的发展,多模态混合检索(如图文互搜)成为主流需求。用户希望既能通过上传图片找到相似图片,也能通过文字描述搜索目标图像。实现该需求的核心在于两点:
  • 向量生成:将图片、文本等不同模态数据转化为统一维度的向量。
  • 向量检索:高效存储和查询海量向量,快速匹配相似结果。
在本文实践中,我们可以使用方舟中提供的图文向量化模型、图像理解模型生成 Milvus 实体,使用向量数据库 Milvus 版进行向量存储与近邻检索,二者结合起来可以实现图文多模态混合搜索的完整功能。
整体架构
架构组成
  • 离线数据写入:将图片数据集通过图生文模型,文本向量模型,图像向量模型生成 Milvus 数据实体写入向量数据库 Milvus 版。
  • 用户在线查询:用户可以通过输入文本、上传图像等方式进行搜索,将用户输入转换为向量后在Milvus中进行搜索得到结果。
核心流程:
  • 数据写入:图片 > 方舟图像向量化 > 生成向量 > 写入向量数据库 Milvus。
  • 检索:
  • 图片搜图:用户上传图片 > 图片生成向量 > 向量数据库 Milvus 版检索相似向量 > 返回关联图片。
  • 文本搜图:用户输入文本 > 文本生成向量 > 向量数据库 Milvus 版检索相似向量 > 返回关联图片。
前提条件
  • 已创建实例且实例处于运行中状态。具体操作,请参见创建实例
  • 已获取实例的访问地址。具体操作,请参见连接实例
说明
本实践中使用多模态向量化模型(Doubao-embedding-vision)和多模态理解模型(Doubao-1.5-thinking-vision-pro)。您可根据需要选择其他模型。关于各模型的介绍,请参见模型列表
  • 已安装 pymilvus。安装命令如下。
  • # Install specific PyMilvus version
    pip install pymilvus==2.5.8
    # Update PyMilvus to the newest version
    pip install --upgrade pymilvus
    # Verify installation success
    python -m pip list | grep pymilvus
  • 已下载 Milvus 官方数据集并解压。命令如下。
  • wget https://github.com/milvus-io/pymilvus-assets/releases/download/imagedata/reverse_image_search.zip
    unzip -q -o reverse_image_search.zip
数据处理
核心代码逻辑
  1. 读取单张图片,转为 base64 编码(方舟模型要求图片输入为 base64)。
  1. 调用方舟图生文接入点,生成描述文本。
  1. 调用方舟 embedding 模型接入点,生成图片和文本向量。
图片数据处理
本地图像数据需要转换成 base64 编码,或者需要提供可以访问的图片 URL。
import base64
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
  1. 生成文本。
  • 使用 doubao-1.5-thinking-vision-pro 模型为图片生成描述和关键词。
  • from volcenginesdkarkruntime import Ark
    def image_gen_text(image_path):
    image_base64 = encode_image(image_path)
    ark_client = Ark(
    api_key="<your_api_key>",
    base_url="https://ark-cn-beijing.bytedance.net/api/v3",
    timeout=120,
    max_retries=2,
    )
    response = ark_client.chat.completions.create(
    # 替换为您的推理接入点 ID
    model="<your_model_id>",
    messages=[
    {
    "role": "user",
    "content": [
    {
    "type": "image_url",
    "image_url": {
    # 需要注意:传入 Base64 编码前需要增加前缀 data:image/{图片格式};base64,{Base64 编码}:
    "url": f"data:image/jpeg;base64,{image_base64}"
    },
    },
    {
    "type": "text",
    "text": "先用 50 字内的文字描述这张图片,然后再给出 5 个关键词",
    },
    ],
    }
    ],
    )
    return response.choices[0].message.content
  1. 生成图片向量。
  • 使用 Doubao-embedding-vision 模型根据图片生成向量。
  • from volcenginesdkarkruntime import Ark
    def image_embedding(image_path):
    image_base64 = encode_image(image_path)
    ark_client = Ark(
    api_key="<your_api_key>",
    base_url="https://ark-cn-beijing.bytedance.net/api/v3",
    timeout=120,
    max_retries=2,
    )
    resp = ark_client.multimodal_embeddings.create(
    model="<your_model_id>",
    input=[
    {
    "type": "image_url",
    "image_url": {
    "url": f"data:image/jpeg;base64,{image_base64}"
    }
    }
    ]
    )
    embedding = resp.data.embedding
    # 确保向量是numpy数组并展平为一维
    embedding = np.array(embedding).flatten()
    print(f"向量维度:{embedding.shape}")
    return embedding
  1. 生成文本向量。
  • 使用 Doubao-embedding-vision 模型根据图片描述生成文本向量。
  • from volcenginesdkarkruntime import Ark
    def text_embedding(text):
    ark_client = Ark(
    api_key="<your_api_key>",
    base_url="https://ark-cn-beijing.bytedance.net/api/v3",
    timeout=120,
    max_retries=2,
    )
    resp = ark_client.multimodal_embeddings.create(
    model="<your_model_id>",
    input=[
    {
    "type": "text",
    "text": text
    }
    ]
    )
    embedding = resp.data.embedding
    # 确保向量是numpy数组并展平为一维
    embedding = np.array(embedding).flatten()
    print(f"向量维度:{embedding.shape}")
    return embedding
数据写入
  1. 设置 Schema 和 Index。
  • 向量数据库 Milvus版 中 Collection 类似传统数据库中的“表”,需先定义集合结构(含向量字段、元数据字段)。本示例将向量的 index_type 设置为 auto_indexmetric_type 为距离类型,在搜索时如果要指定距离类型需要与 index_params 保持一致,支持的距离类型可参考度量类型
  • 字段名称
    数据类型
    索引类型
    说明
    度量类型
    id
    int64
    auto_index
    自动 ID。
    -
    image_path
    varchar(512)
    auto_index
    图片地址。
    -
    image_embedding
    float_vector(2048)
    auto_index
    图片向量。
    L2
    image_description
    varchar(1024)
    auto_index
    图片描述。
    -
    image_desc_embedding
    float_vector(2048)
    auto_index
    描述向量。
    L2
  • from pymilvus import MilvusClient
    from pymilvus import DataType
    milvus_client = MilvusClient(
    uri="http://milvus-xxx.milvus.ivolces.com:19530", # Milvus 实例的访问地址
    token="<user>:<password>", # Milvus 实例的用户名和密码,替换为创建实例的用户名密码
    )
    def create_schema():
    schema = MilvusClient.create_schema(
    auto_id=False,
    enable_dynamic_field=True,
    )
    schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
    schema.add_field(field_name="image_path", datatype=DataType.VARCHAR, max_length=512)
    schema.add_field(field_name="image_embedding", datatype=DataType.FLOAT_VECTOR, dim=2048)
    schema.add_field(field_name="image_description", datatype=DataType.VARCHAR, max_length=1024)
    schema.add_field(field_name="image_desc_embedding", datatype=DataType.FLOAT_VECTOR, dim=2048)
    return schema
    def create_index_params():
    index_params = milvus_client.prepare_index_params()
    index_params.add_index(
    field_name="id",
    index_type="AUTOINDEX"
    )
    index_params.add_index(
    field_name="image_embedding",
    index_type="AUTOINDEX",
    metric_type="L2"
    )
    index_params.add_index(
    field_name="image_description",
    index_type="AUTOINDEX"
    )
    index_params.add_index(
    field_name="image_desc_embedding",
    index_type="AUTOINDEX",
    metric_type="L2"
    )
    return index_params
  • 您可根据实际需要将 index_type 设置为其他类型,例如 IVF_FLAT 或 HNSW。详情请参见 IVF_FLATHNSW。代码如下。
  • # IVF_FLAT类型
    index_params.add_index(
    field_name="your_vector_field_name", # Name of the vector field to be indexed
    index_type="IVF_FLAT", # Type of the index to create
    index_name="vector_index", # Name of the index to create
    metric_type="L2", # Metric type used to measure similarity
    params={
    "nlist": 64, # Number of clusters for the index
    } # Index building params
    )
    # HNSW类型
    index_params.add_index(
    field_name="your_vector_field_name", # Name of the vector field to be indexed
    index_type="HNSW", # Type of the index to create
    index_name="vector_index", # Name of the index to create
    metric_type="L2", # Metric type used to measure similarity
    params={
    "M": 64, # Maximum number of neighbors each node can connect to in the graph
    "efConstruction": 100 # Number of candidate neighbors considered for connection during index construction
    } # Index building params
    )
  1. 创建 Collection。
  • 在已创建的向量数据库 Milvus 版实例中创建 Collection,完整代码如下。具体操作,请参见创建 Collection
  • from pymilvus import MilvusClient
    from pymilvus import DataType
    milvus_client = MilvusClient(
    uri="http://milvus-xxx.milvus.ivolces.com:19530", # Milvus 实例的访问地址
    token="<user>:<password>", # Milvus 实例的用户名和密码,替换为创建实例的用户名密码
    )
    def create_collection_if_not_exists(collection_name):
    if collection_name in milvus_client.list_collections():
    return
    schema = MilvusClient.create_schema(
    auto_id=False,
    enable_dynamic_field=True,
    )
    schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
    schema.add_field(field_name="image_path", datatype=DataType.VARCHAR, max_length=512)
    schema.add_field(field_name="image_embedding", datatype=DataType.FLOAT_VECTOR, dim=2048)
    schema.add_field(field_name="image_description", datatype=DataType.VARCHAR, max_length=1024)
    schema.add_field(field_name="image_desc_embedding", datatype=DataType.FLOAT_VECTOR, dim=2048)
    index_params = milvus_client.prepare_index_params()
    index_params.add_index(
    field_name="id",
    index_type="AUTOINDEX"
    )
    index_params.add_index(
    field_name="image_embedding",
    index_type="AUTOINDEX",
    metric_type="L2"
    )
    index_params.add_index(
    field_name="image_description",
    index_type="AUTOINDEX"
    )
    index_params.add_index(
    field_name="image_desc_embedding",
    index_type="AUTOINDEX",
    metric_type="L2"
    )
    milvus_client.create_collection(
    collection_name=collection_name,
    schema=schema,
    index_params=index_params,
    consistency_level="Bounded",
    num_shards=1
    )
    print(f"Created collection {collection_name}")
  1. 批量写入图片数据。
  • 从下载的数据集中的 reverse_image_search.csv 文件逐条读取图片数据写入向量数据库 Milvus 版。代码如下。
  • import pandas as pd
    from tqdm import tqdm
    from pymilvus import MilvusClient
    from pymilvus import DataType
    milvus_client = MilvusClient(
    uri="http://milvus-xxx.milvus.ivolces.com:19530", # Milvus 实例的访问地址
    token="<user>:<password>", # Milvus 实例的用户名和密码,替换为创建实例的用户名密码
    )
    def batch_insert():
    df = pd.read_csv("reverse_image_search.csv")
    success_count = 0
    failure_count = 0
    collection_name = "image_search_collection_demo"
    create_collection_if_not_exists(collection_name)
    for image_path in tqdm(df["path"].tolist(), desc="生成图像embedding"):
    print(f"开始处理{image_path}")
    try:
    image_vector = image_embedding(image_path)
    image_desc = image_gen_text(image_path)
    image_desc_vector = text_embedding(image_desc)
    milvus_client.insert(
    collection_name=collection_name,
    data=[{"image_path": image_path, "image_embedding":image_vector, "image_description": image_desc, "image_desc_embedding": image_desc_vector}]
    )
    print(f"成功插入{image_path}")
    success_count += 1
    time.sleep(1) # 控制API调用频率
    except Exception as e:
    print(f"处理{image_path}失败,已跳过: {str(e)}")
    failure_count += 1
    print(f"成功插入 {success_count} 张图片,失败 {failure_count} 张图片")
  • 插入数据之后,可以在可视化管理工具 Attu 中查看写入的数据。具体操作,请参见 Attu 桌面快速入门
  1. 查看写入数据。
  • ./train/Rhodesian_ridgeback/n02087394_6382.JPEG 为例,生成的描述如下。
图片:一只棕色狗狗戴黑项圈,闭眼趴在花纹地毯上,氛围闲适。
关键词:狗狗、棕色、项圈、地毯、休憩。
搜索图片
以图搜图
用户上传一张图片,生成向量后在向量数据库 Milvus 版中检索相似图片,返回 top 10 结果。
milvus_client = MilvusClient(
uri="http://milvus-xxx.milvus.ivolces.com:19530", # Milvus 实例的访问地址
token="<user>:<password>", # Milvus 实例的用户名和密码,替换为创建实例的用户名密码
)
def query_by_image(image_path):
query_image_embedding = image_embedding(image_path)
res = milvus_client.search(
collection_name="image_search_collection_demo",
anns_field="image_embedding",
data=[query_image_embedding],
limit=10,
output_fields=["id", "image_path", "image_description"],
)
print(f"\n=== 图片搜索图片结果(查询图片:{image_path})===")
for i in range(len(res[0])):
print(f"第 {i} 个结果:")
print(f"id: {res[0][i].id}")
print(f"distance: {res[0][i].distance}")
print(f"image_path: {res[0][i].entity.get('image_path')}")
print(f"image_description: {res[0][i].entity.get('image_description')}")
query_by_image("test/basketball/n02802426_12693.JPEG")
输入图片如下。
输出结果如下。
=== 图片搜索图片结果(查询图片:test/basketball/n02802426_12693.JPEG)===
第 0 个结果:
id: 459761203689868587
image_path: ./train/basketball/n02802426_10137.JPEG
image_description: 图片展示篮球比赛场景,蓝队24号球员起跳投篮,白队23号球员尝试封盖,赛场为木质地板。
关键词:篮球比赛、投篮、防守、运动、球员
distance: 0.7994794249534607
第 1 个结果:
id: 459761203689868597
image_path: ./train/basketball/n02802426_7656.JPEG
image_description: 图片:室内篮球场,穿紫色“CURRY 35”球衣的球员投篮,白色球衣球员防守,旁有裁判观众。
关键词:篮球比赛、投篮、球员、球衣号码、室内球场
distance: 0.8123147487640381
第 2 个结果:
id: 459761203689868579
image_path: ./train/basketball/n02802426_8222.JPEG
image_description: 图片:室内球场上,三名身着篮球服的孩子参与比赛,穿橙色23号球衣的孩子正运球,两侧有队友与对手。
关键词:儿童篮球、篮球服、运球、室内球场、青少年运动
distance: 1.0420031547546387
... 其他结果省略
以文搜图
用户输入文本描述,生成向量后在向量数据库 Milvus 版中检索相似图片,返回 top 10 结果。
milvus_client = MilvusClient(
uri="http://milvus-xxx.milvus.ivolces.com:19530", # Milvus 实例的访问地址
token="<user>:<password>", # Milvus 实例的用户名和密码,替换为创建实例的用户名密码
)
def query_by_text(text):
query_text_embedding = text_embedding(text)
res = milvus_client.search(
collection_name="image_search_collection_demo",
anns_field="image_desc_embedding",
data=[query_text_embedding],
limit=10,
output_fields=["id", "image_path", "image_description"],
)
print(f"\n=== 文本搜索图片结果(查询文本:{text})===")
for i in range(len(res[0])):
print(f"第 {i} 个结果:")
print(f"id: {res[0][i].id}")
print(f"image_path: {res[0][i].entity.get('image_path')}")
print(f"image_description: {res[0][i].entity.get('image_description')}")
print(f"distance: {res[0][i].distance}")
query_by_text("极限运动跳伞")
输出结果如下。
=== 文本搜索图片结果(查询文本:极限运动跳伞)===
第 0 个结果:
id: 459761203689868077
image_path: ./train/parachute/n03888257_2007.JPEG
image_description: 图片:蓝天背景下,一名跳伞者借助绿白相间的降落伞空中滑翔,装备专业,画面动感。
关键词:跳伞、降落伞、蓝天、极限运动、滑翔
distance: 0.9275163412094116
第 1 个结果:
id: 459761203689868071
image_path: ./train/parachute/n03888257_63966.JPEG
image_description: 图片:蓝天白云下,跳伞者借金色降落伞悬空,下方有饰白十字的深色翼状装置,画面充满动感。
关键词:跳伞、降落伞、蓝天、翼装、极限运动
distance: 0.9574025273323059
第 2 个结果:
id: 459761203689868065
image_path: ./train/parachute/n03888257_4738.JPEG
image_description: 图片中一人正进行跳伞,蓝色降落伞张开,其在淡色天空下悬浮。
关键词:跳伞、降落伞、高空、天空、极限运动
distance: 0.9625174403190613
...其他结果省略
最近更新时间:2025.12.12 18:56:30
这个页面对您有帮助吗?
有用
有用
无用
无用