跳转到内容

3.1 Flat 索引:暴力搜索的基准

Flat 不是"差"——它是 100% 召回率的保证,也是所有索引的基准线


这一节在讲什么?

Flat 索引是 FAISS 中最简单的索引类型——它不做任何优化,搜索时逐条计算查询向量与所有向量的距离。听起来很"笨",但 Flat 有两个不可替代的用途:一是数据量小时暴力搜索已经够快,二是作为基准测试的"标准答案"来评估其他索引的召回率。这一节我们快速过一遍 Flat 索引的用法和性能特征。


IndexFlatL2 / IndexFlatIP

python
import faiss
import numpy as np

d = 768
n = 100000

vectors = np.random.rand(n, d).astype('float32')

# L2 距离的 Flat 索引
index_l2 = faiss.IndexFlatL2(d)
index_l2.add(vectors)

# 内积的 Flat 索引
faiss.normalize_L2(vectors)
index_ip = faiss.IndexFlatIP(d)
index_ip.add(vectors)

# 搜索
query = np.random.rand(1, d).astype('float32')
faiss.normalize_L2(query)

distances_l2, indices_l2 = index_l2.search(query, k=5)
distances_ip, indices_ip = index_ip.search(query, k=5)

Flat 的性能

Flat 索引的搜索复杂度是 O(N×d)——N 是向量数量,d 是维度。每次搜索需要计算查询向量与所有 N 条向量的距离。

向量数量维度搜索延迟(CPU)
1 万768~1 ms
10 万768~10 ms
100 万768~100 ms
1000 万768~1000 ms

10 万条以下,Flat 的搜索延迟在 10ms 以内,完全够用。100 万条时延迟约 100ms,对大多数 RAG 场景也可以接受。超过 100 万条就需要考虑 IVF 或 HNSW 索引了。


Flat 作为基准测试

Flat 索引的召回率是 100%——因为它检查了每一条向量。这个特性让 Flat 成为评估其他索引召回率的"标准答案":

python
def measure_recall(index_ann, index_flat, queries, k=10):
    """测量 ANN 索引的召回率"""
    _, gt_indices = index_flat.search(queries, k)  # Ground Truth
    _, ann_indices = index_ann.search(queries, k)  # ANN 结果

    total_recall = 0
    for i in range(len(queries)):
        gt_set = set(gt_indices[i])
        ann_set = set(ann_indices[i])
        recall = len(gt_set & ann_set) / k
        total_recall += recall

    return total_recall / len(queries)

# 用法
recall = measure_recall(ivf_index, flat_index, test_queries, k=10)
print(f"Recall@10: {recall:.4f}")

常见误区:Flat 索引没有意义

有些同学觉得"Flat 就是暴力搜索,没有技术含量"。但 Flat 有两个不可替代的用途:第一,数据量小时 Flat 的性能已经足够好,不需要更复杂的索引;第二,Flat 是唯一能保证 100% 召回率的索引——当你需要验证其他索引的质量时,Flat 是唯一的基准。在生产环境中,很多团队会用"Flat 初筛 + 业务逻辑过滤"的方案来处理小规模数据。


小结

这一节我们快速过了 Flat 索引:IndexFlatL2/IndexFlatIP 是暴力搜索,召回率 100%,10 万条以下性能足够。Flat 的核心价值是作为基准测试的"标准答案"。下一节我们聊 IVF 索引——FAISS 中最重要的索引类型之一。

基于 MIT 许可发布