跳到主要内容

Python-C++代码片段

有时,Faiss 的 C++ 层与 Python 层的交互方式并不直观。为帮助用户,我们整理了常用的 Python 代码片段,可以直接在 Jupyter Notebook 中复制使用,实现一些实用操作。

这些用法主要依赖 vector_to_array 及其他 Python/C++ 交互技巧。详细说明可参考 Faiss 代码结构文档

faiss.contrib.inspect_tools 模块包含多个实用函数,可用于检查 Faiss 对象。特别是 inspect_tools.print_object_fields 可以列出任意对象的所有属性及当前值。


如何从 PCA(主成分分析)对象中获取 numpy 格式的 PCA 矩阵?

可以使用 faiss.contrib.inspect_tools.get_LinearTransform_matrix 获取 PCA 矩阵。或者参考此代码示例:get_matrix_from_PCA.ipynb

这个方法可用于所有 LinearTransform(线性变换)对象。


如何获取或修改 ProductQuantizer 或 ResidualQuantizer 的质心(centroids)?


如何查看倒排列表(inverted lists)的内容?

可以使用 faiss.contrib.inspect_tools.get_invlist 获取倒排列表内容,或者参考此代码:get_invlists.ipynb


如何查找某个已存储向量对应的倒排列表?

这个功能不需要 C++ 层技巧,可参考 #3555


如何获取 HNSW 索引的链接结构?

请参考代码示例:demo_hnsw_struct.ipynb 可选在线渲染


如何获取 IndexNNDescent 的 knn 图?

请查看以下代码: demo_access_nndescent.ipynb


如何合并普通的 ArrayInvertedLists(数组倒排列表)?

参见代码示例:demo_merge_array_invertedlists.ipynb


Faiss 与 PyTorch 互操作:如何在不离开 GPU 的情况下使用 PQ 编码器?

相关代码示例:PQ_codec_pytorch.ipynb


如何探索未知内容的索引文件?

有时你只有一个索引文件,但不了解其中内容。在访问包装索引(wrapper index)的 Index 字段时,它只表现为一个普通 Index 对象。可以使用 downcast_index 将其转换为实际类型的“叶子”索引,以便提取所有信息。

代码演示如何用 downcast_index 提取具体内容: demo_explore_indedex.ipynb


如何从 IDMap 或 IDMap2 索引中获取全部 id?

IDMap2 继承自 IDMap,所以 这个代码 适用于两者。


如何在 IDMap2 和 IDMap 之间进行转换?

这段代码可以进行双向转换: convert_idmap2_idmap.ipynb


如何仅用 GPU 训练 k-means,并用于训练 CPU 索引?

详细用法见:train_ivf_with_gpu.ipynb



如何在添加向量(add)时使用 GPU?

代码示例:assign_on_gpu.ipynb


如何强制指定 k-means 初始化质心?

以及如何用于 IVF 索引训练。

更多细节请见:initial_centroids_demo.ipynb


如何将训练好的 OPQ 和/或 IVF 质心迁移到另一个索引?

参见官方 issue:Faiss #2455


如何替换倒排列表内容?

详细代码演示:demo_replace_invlists.ipynb


如何访问 PQ/IVFPQ/AQ(非8位编码方式)的原始编码内容?

需要使用 BitStringReader,详情请见 #2285


如何在 GPU 上用只有1个质心的 IVFPQ 模拟 IndexPQ?

IndexPQ(纯 PQ 索引)本身不支持 GPU,但可以使用一个只有一个质心的 IVFPQ 实现类似功能。

具体代码请见:demo_1_centroid_PQ.ipynb


如何访问基于图的索引(NSG 或 HNSW)中的向量数据?

图索引的数据存储在一个名为 storageIndexFlatCodes 索引中。参见 demo_access_NSG_data.ipynb

如果要获取重建后的向量,可以使用 index2.reconstruct(vector_id)index2.reconstruct_n()


如何将小型 C++ 对象包装后在 Python 中使用?

有时需要在 Faiss 中实现一个小的回调(callback),如某些特定用途的 C++ 类,但它过于具体或依赖外部代码,不适合直接包含进 Faiss。同时,Faiss 编译过程较为复杂。

此时可以利用 SWIG 工具,将一小段 C++ 代码打包为 Python 可用模块。

比如,用于 IDSelector(Faiss 的 id 选择器)对象,并包含 is_member 方法的实现:

示例见:bow_id_selector.swig

在 Linux 系统,已经用 Conda 安装 Faiss 且有 SWIG 4.x 的情况下,可以用如下命令行编译:

# 生成包装代码
swig -c++ -python -I$CONDA_PREFIX/include bow_id_selector.swig

# 编译生成的包装代码
g++ -shared -O3 -g -fPIC bow_id_selector_wrap.cxx -o _bow_id_selector.so \
-I $( python -c "import distutils.sysconfig ; print(distutils.sysconfig.get_python_inc())" ) \
-I $CONDA_PREFIX/include $CONDA_PREFIX/lib/libfaiss_avx2.so

这样会生成 bow_id_selector.py_bow_id_selector.so,可以在 Python 中直接加载使用:

import numpy as np
import faiss
import bow_id_selector

# 构造一个非常小的稀疏 CSR 矩阵
n = 3
indptr = np.array([0, 2, 3, 6], dtype='int32')
indices = np.array([7, 8, 3, 1, 2, 3], dtype='int32')

# 不要忘记用 swig_ptr,将 numpy 数组转为 C++ 指针
selector = bow_id_selector.IDSelectorBOW(n, faiss.swig_ptr(indptr), faiss.swig_ptr(indices))

selector.set_query_words(1, 2)
selector.is_member(0) # 返回 False
selector.is_member(1) # 返回 False
selector.is_member(2) # 返回 True
selector.is_member(3) # 会报错!

# 当然,也可以将其与 Faiss 对象结合使用
params = faiss.SearchParameters(sel=selector)
提示

使用自定义 SWIG 封装对象时,建议仔细检查指针类型及数据一致性,防止潜在的内存错误。