You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FAISS索引与数据集索引不匹配,召回率为0问题求助

问题描述

我在FAISS中使用HNSW索引执行搜索后,尝试计算召回率,召回率定义为:
Recall = TP / (TP + FN)
其中,若查询图像出现在前10个搜索结果中则记为True Positive(TP),否则记为False Negative(FN)。

我处理的是规模约1M的数据集(以CIFAR-10为例),已经按照建议设置了HNSW参数,并且将dataloader的shuffle设为False,按顺序加载数据集构建FAISS索引,理论上FAISS的第n个索引应该对应数据集的第n个样本。但计算得到的召回率为0,TP=0。

我已经检查过数据加载的文件名正确,FAISS返回的文件名也无误,并且固定了随机种子,问题仍然存在。请问为何FAISS索引序号与数据集的序号不匹配?

索引构建代码

import faiss

index = faiss.IndexHNSWFlat(1280, 100, faiss.METRIC_L2)
index.hnsw.efSearch = 2000 
index.hnsw.efConstruction = 800

for data in tqdm(dataloader, desc="SYSTEM : Indexing Embeddings FAISS", unit="batch", leave=False): 
    index.add(data.embedding)

召回率计算代码

TP, FN = 0, 0

for data in tqdm(dataloader, unit="batch", leave=False):
    D, I = index.search(data.embedding, 10)
    filenames = [dataset[I(idx)].filename for idx in range(10)]
        
    if data.filename in filenames:
        TP += 1
    else:
        FN += 1

recall = TP / (TP + FN)
print(f"Recall= {round(recall, 3)}, TP = {TP}, FN = {FN}")

数据集与加载器设置

dataset = torchvision.datasets.CIFAR10
dataloader = DataLoader(dataset, batch_size=1, shuffle=False)

随机种子设置

seed = 111111
torch.manual_seed(seed)
np.random.seed(seed)
random.seed(seed)

问题分析与解决

1. 搜索结果索引解析错误(核心问题)

你的召回率计算代码中,对FAISS返回的索引数组I的访问方式完全错误。FAISS的index.search方法返回的I是二维数组,形状为(batch_size, k)——因为你设置了batch_size=1,所以I的实际形状是(1, 10),存储的是当前batch中每个样本对应的前10个近邻索引。

你现在写的I(idx)是调用数组的错误语法,正确的访问方式应该是I[0][idx],取第一个batch(唯一的batch)里的第idx个索引。这直接导致你取到的索引完全不是FAISS返回的正确值,自然匹配不上,TP为0。

修改后的代码片段:

# 替换原filenames行
filenames = [dataset[I[0][idx]].filename for idx in range(10)]

2. CIFAR-10数据集的filename字段问题

官方的torchvision.datasets.CIFAR10本身并不返回filename字段,你应该是自定义了数据集类来添加这个属性。需要确认:

  • 自定义数据集的__getitem__方法中,filename是否与样本的索引严格对应?
  • 构建索引和查询时使用的是同一个数据集实例吗?如果是分别创建的数据集,即使shuffle=False,也可能因为某些预处理逻辑导致顺序不一致。

3. 索引构建时的embedding顺序验证

可以先做一个小测试:取数据集的第0个样本的embedding,用FAISS搜索它自己,看返回的第一个索引是否为0。如果不是,说明索引构建时的embedding顺序就和数据集顺序不匹配:

test_emb = dataset[0].embedding.unsqueeze(0)  # 转为batch维度
D, I = index.search(test_emb, 1)
print(f"搜索第0个样本返回的索引: {I[0][0]}")

如果输出不是0,需要检查:

  • 构建索引时的dataloader是否真的shuffle=False?有没有在代码其他地方修改过dataloader的参数?
  • 构建索引时,data.embedding是否是按batch内的顺序正确添加到FAISS中的?比如如果你的dataloader返回的batch是乱序的(哪怕shuffle=False),或者embedding的维度处理有误,都会导致顺序错位。

4. 数据类型兼容问题

FAISS返回的索引是np.int64类型,如果你的自定义数据集的__getitem__方法不支持这种整数类型的索引(比如只接受Python原生int),可能会导致取到错误的样本。可以尝试将索引转为int后再访问:

filenames = [dataset[int(I[0][idx])].filename for idx in range(10)]

内容的提问来源于stack exchange,提问作者No Yeah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 10:25:23