向量搜索的性能和准确性高度依赖于输入向量的质量。在将高维向量投入索引之前,通常需要进行两个关键预处理步骤:归一化(Normalization) 和 主成分分析(PCA)降维。归一化确保向量处于统一的尺度,而 PCA 则能有效去除冗余信息、提高搜索效率并减少内存占用。
Faiss 提供了强大的预处理器组件,允许我们将这些转换步骤与索引结构无缝结合,从而实现端到端的自动化处理。这意味着我们在添加或查询向量时,可以直接使用原始的高维向量,而无需手动执行归一化和降维。
准备工作
确保你已安装 Faiss 和 NumPy:
pip install faiss-cpu numpy
核心技术点:Faiss 索引工厂 (index_factory)
虽然我们可以手动使用 faiss.IndexPreTransform 来组合转换器(如 faiss.PCAMatrix 和 faiss.Normalization),但 Faiss 推荐使用其强大的 索引工厂 (index_factory) 语法。该语法允许我们用一个简单的字符串定义复杂的索引结构和预处理流程。
步骤一:定义预处理和索引结构
我们将创建一个包含以下流程的索引:
- PCA64: 将 128 维数据降维到 64 维。
- L2: 对降维后的向量进行 L2 归一化(欧氏距离的L2范数)。
- Flat: 使用最基础的精确搜索索引。
import faiss
import numpy as np
import time
# 定义原始维度 D 和目标降维维度 D_out
D = 128 # 原始维度
D_out = 64 # 目标维度
NB = 10000 # 训练集大小
NQ = 5 # 查询向量数量
print(f"--- 初始化 Faiss 自动化预处理示例 ---")
# 1. 准备数据:生成训练集和查询集
# 注意:Faiss要求输入数据类型为 float32
np.random.seed(42)
xb = np.random.random((NB, D)).astype('float32')
# 训练集通常与数据集相同,或者是一个大型代表性子集
xt = xb[::2] # 使用一半数据作为训练集
# 原始查询向量
xq = np.random.random((NQ, D)).astype('float32')
# 2. 使用 index_factory 定义包含 PCA 和 L2 归一化的索引
# 结构字符串:"PCA{D_out},L2,Flat"
index_string = f"PCA{D_out},L2,Flat"
index = faiss.index_factory(D, index_string)
print(f"定义的索引结构:{index_string}")
print(f"索引是否需要训练:{index.is_trained}")
# 3. 训练索引(Fit the PCA Matrix)
# 这一步会自动计算主成分,并记录归一化所需的参数(虽然L2归一化不需要参数,但结构在此确定)
start_train = time.time()
index.train(xt)
end_train = time.time()
print(f"索引训练完成,耗时: {end_train - start_train:.4f}s")
print(f"训练后的维度: {index.d}")
print(f"索引是否已训练: {index.is_trained}")
# 4. 添加原始数据
# 注意:我们添加的是 D=128 维的原始数据,Faiss 会自动执行 PCA 和 L2 归一化再存储
index.add(xb)
print(f"已添加 {index.ntotal} 个向量")
# 5. 查询(Querying)
# 我们使用 D=128 维的原始查询向量进行查询
K = 4 # 搜索最近的4个邻居
start_search = time.time()
# D 是距离(Distance),I 是索引(Index)
D_results, I_results = index.search(xq, K)
end_search = time.time()
print(f"搜索完成,耗时: {end_search - start_search:.4f}s")
print("\n--- 查询结果 (原始索引 ID) ---")
for i in range(NQ):
print(f"查询向量 {i}: 最近邻索引 {I_results[i]}")
print(f"查询向量 {i}: 对应距离 {D_results[i]}")
# 6. 验证内部转换 (可选)
# 我们可以检查 Faiss 内部是否正确应用了 PCAMatrix
# PCAMatrix 是 index 的第一个 PreTransform 组件
# print(index.chain.at(0).is_trained) # 检查 PCA 是否训练
结果分析与总结
通过使用 index_factory 字符串 “PCA64,L2,Flat”,我们成功地定义了一个复杂的索引管道。
- 当我们调用 index.train(xt) 时,Faiss 自动训练了 PCAMatrix 组件,学习了数据的主成分方向。
- 当我们调用 index.add(xb) 时,每个 128 维的向量都会先被 PCA 降维到 64 维,然后进行 L2 归一化,最后才被存储到 Flat 索引中。
- 当我们调用 index.search(xq, K) 时,查询向量也会经过完全相同的预处理流程(PCA -> L2 归一化),确保查询和存储的向量处于相同的特征空间,从而保证搜索的准确性。
这种自动化处理极大地简化了代码结构,并确保了生产环境中向量处理的一致性。
汤不热吧