4. 使用不同距离度量进行搜索

任务说明

本案例在同一个Milvus集合中,分别使用L2(欧氏距离)、IP(内积)和余弦(COSINE)三种距离度量创建索引,并对相同查询向量执行搜索,比较三种度量下的搜索结果差异。通过本案例,你将掌握Milvus中距离度量的选择方法及其对相似度检索结果的影响。

实现方案

首先创建集合并插入一组二维向量数据。随后依次为集合创建支持不同距离度量的索引(每次创建新索引前先删除旧索引),分别执行搜索并输出结果。由于Milvus的搜索参数中metric_type必须与索引类型一致,因此每次切换度量时需重建索引。为便于比较,三种度量下均使用相同的查询向量和TopK参数。

代码实现

from pymilvus import (
    connections, CollectionSchema, FieldSchema, DataType,
    Collection, utility
)

# 1. 连接Milvus
connections.connect(host="localhost", port="19530")

# 2. 定义集合结构(二维向量便于直观比较)
collection_name = "metric_compare"
if utility.has_collection(collection_name):
    utility.drop_collection(collection_name)

fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
    FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=2)
]
schema = CollectionSchema(fields, description="distance metric comparison")
collection = Collection(collection_name, schema)

# 3. 插入数据(5个二维向量)
import random
random.seed(42)
data = [
    [i for i in range(5)],
    [
        [1.0, 0.0],   # 单位向量,指向x轴
        [0.0, 1.0],   # 单位向量,指向y轴
        [0.7071, 0.7071],  # 45度单位向量
        [2.0, 0.0],   # 长度2,x轴方向
        [0.0, 2.0]    # 长度2,y轴方向
    ]
]
collection.insert(data)
collection.flush()

# 4. 查询向量(与数据中第3条相同方向,但长度不同)
query_vector = [0.7071, 0.7071]

# 5. 定义索引参数与搜索参数
index_params = {"index_type": "FLAT", "params": {}}
search_params = {"topk": 3}

# 6. 依次使用三种距离度量
for metric in ["L2", "IP", "COSINE"]:
    # 删除旧索引,创建新度量索引
    if collection.has_index():
        collection.drop_index()
    index_params["metric_type"] = metric
    collection.create_index("vector", index_params)
    collection.load()

    # 执行搜索
    results = collection.search(
        data=[query_vector],
        anns_field="vector",
        param=search_params,
        limit=3,
        output_fields=["id"]
    )

    # 输出结果
    print(f"\n=== 距离度量: {metric} ===")
    for hits in results:
        for hit in hits:
            print(f"id: {hit.id}, distance: {hit.distance:.4f}")

    collection.release()

# 7. 清理
utility.drop_collection(collection_name)
connections.disconnect("default")

关键说明

上述代码中,数据向量特意选择了单位向量和不同长度的向量,以便观察三种度量的本质差异:L2度量返回欧氏距离(值越小越相似),IP返回内积(值越大越相似),COSINE返回余弦相似度(值越大越相似,且不受向量长度影响)。查询向量[0.7071, 0.7071]与数据中id=2的向量方向完全一致,但长度不同(查询向量长度为1,数据向量长度也为1,此处特意让查询向量与id=2完全一致以便观察)。

验证方法

运行代码后,观察控制台输出。预期结果应满足以下判定标准:

  • L2度量:id=2的distance应为0(完全重合),id=0和id=1的distance约为1.0(单位向量与坐标轴夹角45度),id=3和id=4的distance约为2.0(长度差异导致)。
  • IP度量:id=2的distance应为1.0(单位向量内积),id=0和id=1的distance约为0.7071,id=3和id=4的distance约为1.414(长度放大内积)。
  • COSINE度量:id=2的distance应为1.0(方向相同),id=0和id=1的distance约为0.7071,id=3和id=4的distance也约为0.7071(余弦不受长度影响,只与方向有关)。

若输出与上述逻辑一致,则说明三种距离度量均正确生效。注意IP和COSINE的排序可能因数值精度略有差异,但核心规律(L2最小值为0对应相同点,IP和COSINE最大值为1对应相同方向)必须成立。

三种距离度量对比(查询向量 [0.7071, 0.7071]) x y 0 1 2 1 2 id=0 (1,0) id=1 (0,1) id=2 (0.707,0.707) id=3 (2,0) id=4 (0,2) 查询向量 预期搜索结果 L2(欧氏距离,越小越相似) 1st: id=2, dist=0.0000 2nd: id=0, dist=1.0000 3rd: id=1, dist=1.0000 IP(内积,越大越相似) 1st: id=2, dist=1.0000 2nd: id=0, dist=0.7071 3rd: id=1, dist=0.7071 COSINE(余弦,越大越相似) 1st: id=2, dist=1.0000 2nd: id=0, dist=0.7071 3rd: id=1, dist=0.7071

运行与验证

确保Milvus服务已启动(默认端口19530),安装pymilvus库后直接运行上述代码。代码末尾会清理集合,如需保留数据可注释最后两行。观察输出结果与SVG中预期是否一致,重点检查:L2度量下id=2距离为0,IP和COSINE度量下id=2距离为1且排名第一。三种度量的排序差异主要体现在id=3和id=4上——L2中它们距离较远(约2.0),而IP中因长度放大内积值(约1.414),COSINE中因方向与坐标轴一致而距离为0.7071,这体现了长度对IP的影响和余弦对长度的不敏感性。