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)?
- ProductQuantizer(PQ,产品量化器):详见 access_PQ_centroids.ipynb
- ResidualQuantizer(RQ,残差量化器):详见 demo_replace_RQ_codebooks.ipynb
如何查看倒排列表(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)中的向量数据?
图索引的数据存储在一个名为 storage 的 IndexFlatCodes 索引中。参见 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 方法的实现:
在 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 封装对象时,建议仔细检查指针类型及数据一致性,防止潜在的内存错误。