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

