1. 前言

本篇笔记主要分析faiss code下的python接口文件——faiss.py的工作流程以及内容。

2. faiss.py分析

2.1 导入文件

在faiss code 编译完成后,在python目录下执行make/make install命令后,会在该文件下产生faiss.py, faiss/, faiss.egg-info/等文件和目录,其中faiss.py和faiss/目录下的__init__.py内容一样,但是在应用程序中import faiss时,导入的是__init__.py文件。

2.2 执行流程

faiss.py在导入时,会执行以下工作:

  1. 导入依赖模块
import numpy as np
import sys
import inspect
import pdb
import platform
import subprocess
import logging
  1. 创建一个log文件,这里__name__是"faiss"
logger = logging.getLogger(__name__)
  1. 根据指令集加载swigfaiss包
try:instr_set = instruction_set()if instr_set == "AVX2":logger.info("Loading faiss with AVX2 support.")from .swigfaiss_avx2 import *else:logger.info("Loading faiss.")from .swigfaiss import *except ImportError:# we import * so that the symbol X can be accessed as faiss.Xlogger.info("Loading faiss.")from .swigfaiss import *

在instruction_set方法中会调用platform.system()和subprocess.check_output来获取当前平台的系统信息和CPU的指令集信息。我使用的平台返回的是Linux和"AVX2"。

  1. 记录版本信息
__version__ = "%d.%d.%d" % (FAISS_VERSION_MAJOR,FAISS_VERSION_MINOR,FAISS_VERSION_PATCH)

上述信息定义在Index.h文件中,我使用的版本是1.6.1

  1. 替换clustering的train方法
handle_Clustering()def handle_Clustering():def replacement_train(self, x, index):assert x.flags.contiguousn, d = x.shapeassert d == self.dself.train_c(n, swig_ptr(x), index)replace_method(Clustering, 'train', replacement_train)def replace_method(the_class, name, replacement, ignore_missing=False):try:orig_method = getattr(the_class, name)except AttributeError:if ignore_missing:returnraiseif orig_method.__name__ == 'replacement_' + name:# replacement was done in parent classreturnsetattr(the_class, name + '_c', orig_method)setattr(the_class, name, replacement)

将clustering class中的train方法名称替换为replacement的名称。
replace_method方法中:

  • the_class:类名,文件中直接使用的类定义在swigfaiss.py中,即这里能调用的类都必须是在该文件中有定义的。
  • name: 函数名,即要重新替换的index的方法
  • replacement: 替换后的方法,这里传入方法的地址,在faiss.py中针对index的部分函数如train, add等定义了一套python下的方法,主要是添加了"replacement_"前缀。所以,在应用程序中调用上述方法时会运行到替换后的方法中来。如index.add --> replacement_add
  1. 替换 ProductQuantizer 和 ScalarQuantizer 的方法
handle_Quantizer(ProductQuantizer)
handle_Quantizer(ScalarQuantizer)def handle_Quantizer(the_class):def replacement_train(self, x):n, d = x.shapeassert d == self.dself.train_c(n, swig_ptr(x))def replacement_compute_codes(self, x):n, d = x.shapeassert d == self.dcodes = np.empty((n, self.code_size), dtype='uint8')self.compute_codes_c(swig_ptr(x), swig_ptr(codes), n)return codesdef replacement_decode(self, codes):n, cs = codes.shapeassert cs == self.code_sizex = np.empty((n, self.d), dtype='float32')self.decode_c(swig_ptr(codes), swig_ptr(x), n)return xreplace_method(the_class, 'train', replacement_train)replace_method(the_class, 'compute_codes', replacement_compute_codes)replace_method(the_class, 'decode', replacement_decode)

这两个类是量化器相关的,这里会替换三个函数: train, compute_codes, decode。

  1. 替换MatrixStats的初始化方法
def handle_MatrixStats(the_class):original_init = the_class.__init__def replacement_init(self, m):assert len(m.shape) == 2original_init(self, m.shape[0], m.shape[1], swig_ptr(m))the_class.__init__ = replacement_inithandle_MatrixStats(MatrixStats)

MatrixStats用于报告数据集相关的一些统计信息并对其进行注释。

  1. 替换已经导入的index的部分方法
this_module = sys.modules[__name__]for symbol in dir(this_module):obj = getattr(this_module, symbol)# print symbol, isinstance(obj, (type, types.ClassType))if inspect.isclass(obj):the_class = objif issubclass(the_class, Index):handle_Index(the_class)if issubclass(the_class, IndexBinary):handle_IndexBinary(the_class)if issubclass(the_class, VectorTransform):handle_VectorTransform(the_class)if issubclass(the_class, AutoTuneCriterion):handle_AutoTuneCriterion(the_class)if issubclass(the_class, ParameterSpace):handle_ParameterSpace(the_class)

首先sys.modules返回所有已经导入的faiss相关的module,然后根据symbol名称判断是否为class,如果是,则根据该class的父类类型依次替换部分方法,具体的替换过程在handle_xxx中实现。

  1. 对类或方法添加参数引用
add_ref_in_constructor(IndexIVFFlat, 0)
add_ref_in_constructor(IndexIVFFlatDedup, 0)
add_ref_in_constructor(IndexPreTransform, {2: [0, 1], 1: [0]})
add_ref_in_method(IndexPreTransform, 'prepend_transform', 0)
add_ref_in_constructor(IndexIVFPQ, 0)
add_ref_in_constructor(IndexIVFPQR, 0)
add_ref_in_constructor(Index2Layer, 0)
add_ref_in_constructor(Level1Quantizer, 0)
add_ref_in_constructor(IndexIVFScalarQuantizer, 0)
add_ref_in_constructor(IndexIDMap, 0)
add_ref_in_constructor(IndexIDMap2, 0)
add_ref_in_constructor(IndexHNSW, 0)
add_ref_in_method(IndexShards, 'add_shard', 0)
add_ref_in_method(IndexBinaryShards, 'add_shard', 0)
add_ref_in_constructor(IndexRefineFlat, 0)
add_ref_in_constructor(IndexBinaryIVF, 0)
add_ref_in_constructor(IndexBinaryFromFloat, 0)
add_ref_in_constructor(IndexBinaryIDMap, 0)
add_ref_in_constructor(IndexBinaryIDMap2, 0)add_ref_in_method(IndexReplicas, 'addIndex', 0)
add_ref_in_method(IndexBinaryReplicas, 'addIndex', 0)# seems really marginal...
# remove_ref_from_method(IndexReplicas, 'removeIndex', 0)if hasattr(this_module, 'GpuIndexFlat'):# handle all the GPUResources refsadd_ref_in_function('index_cpu_to_gpu', 0)add_ref_in_constructor(GpuIndexFlat, 0)add_ref_in_constructor(GpuIndexFlatIP, 0)add_ref_in_constructor(GpuIndexFlatL2, 0)add_ref_in_constructor(GpuIndexIVFFlat, 0)add_ref_in_constructor(GpuIndexIVFScalarQuantizer, 0)add_ref_in_constructor(GpuIndexIVFPQ, 0)add_ref_in_constructor(GpuIndexBinaryFlat, 0)
  • add_ref_in_constructor():对arg1的类添加arg2的参数引用
  • add_ref_in_method():对arg1类中arg2的方法添加arg3的参数引用
  • add_ref_in_function():对arg1的函数添加arg2的参数引用,其中arg1的函数是独立于类的函数。
  1. 替换MapLong2Long的方法
replace_method(MapLong2Long, 'add', replacement_map_add)
replace_method(MapLong2Long, 'search_multiple', replacement_map_search_multiple)

2.3 定义的方法接口

faiss.py除了在import 时会自动执行上述操作,在文件中还定义了一些独立的函数供用户应用程序调用。

1. GPU相关操作

index_cpu_to_gpu_multiple_py()
此方法的作用是将index从CPU中拷贝到所有的GPU中,为GPU索引和资源构建c++向量。

方法调用了swigfaiss.py的index_cpu_to_gpu_multiple(),该函数拷贝操作的具体实现,初始定义在gpu/GpuCloner.cpp中。

def index_cpu_to_gpu_multiple_py(resources, index, co=None):"""builds the C++ vectors for the GPU indices and theresources. Handles the common case where the resources are assigned tothe first len(resources) GPUs"""vres = GpuResourcesVector()vdev = IntVector()for i, res in enumerate(resources):vdev.push_back(i)vres.push_back(res)index = index_cpu_to_gpu_multiple(vres, vdev, index, co)index.referenced_objects = resourcesreturn index
  • resources:资源列表
  • index: CPU中索引实例
  • return index: GPU中的索引实例

index_cpu_to_all_gpus()
此方法的作用与同样是拷贝index,但是接口更简单,更易于应用程序调用,其内部通过调用index_cpu_to_gpu_multiple_py实现。

def index_cpu_to_all_gpus(index, co=None, ngpu=-1):if ngpu == -1:ngpu = get_num_gpus()res = [StandardGpuResources() for i in range(ngpu)]index2 = index_cpu_to_gpu_multiple_py(res, index, co)return index2
  • index: 要拷贝的CPU 索引实例
  • co: 拷贝的参数,默认为0
  • ngpu: gpu个数,默认为-1,此时由系统判断
  • index2: 拷贝后GPU中的索引实例

2. numpy数组和std::vector转换操作

vector_to_array()
将c++的std::vector容器转换成np的数组

def vector_to_array(v):""" convert a C++ vector to a numpy array """classname = v.__class__.__name__assert classname.endswith('Vector')dtype = np.dtype(vector_name_map[classname[:-6]])a = np.empty(v.size(), dtype=dtype)if v.size() > 0:memcpy(swig_ptr(a), v.data(), a.nbytes)return a# 其中vector_name_map表示了std::vector和python中数据类型的映射:
vector_name_map = {'Float': 'float32','Byte': 'uint8','Char': 'int8','Uint64': 'uint64','Long': 'int64','Int': 'int32','Double': 'float64'}
  • v: 要转换的c++类型的数组
  • a: 转换后的numpy风格的列表

vector_float_to_array()
同vector_to_array()

def vector_float_to_array(v):return vector_to_array(v)

copy_array_to_vector()
将numpy的列表拷贝到c++的容器中

def copy_array_to_vector(a, v):""" copy a numpy array to a vector """n, = a.shapeclassname = v.__class__.__name__assert classname.endswith('Vector')dtype = np.dtype(vector_name_map[classname[:-6]])assert dtype == a.dtype, ('cannot copy a %s array to a %s (should be %s)' % (a.dtype, classname, dtype))v.resize(n)if n > 0:memcpy(v.data(), swig_ptr(a), a.nbytes)

3. 数据操作相关的接口

kmin(array, k)
返回数组array中每一行的k个最小值

  • array: 要搜索的二维数组
  • k: 要查找的最小值的个数

返回值:

  • D : 返回<m, k>的二维数组,类型为int
  • I : 返回<m, k>的二维数组,类型为float32

kmax(array, k)
返回数组array中每一行的k个最大值,其他同kmin()

pairwise_distances(xq, xb, mt=METRIC_L2, metric_arg=0)
计算两组向量之间的整个成对距离矩阵

  • xq: 第一个二维向量
  • xb: 第二个二维向量
  • mt: 计算的距离类型,默认为L2
  • metric_arg: 距离类型参数

返回值,dis: 一个<m1, m2>的二维向量,其中m1为xq的行数,m2为xb的行数

rand/randint/randn

  • rand: 产生浮点型的随机数
  • randint: 产生int64型随机数
  • randn: 从正态分布中产生folat32型随机数

eval_intersection(I1, I2)
两个结果表的每行之间相交的大小

normalize_L2(x)

def normalize_L2(x):fvec_renorm_L2(x.shape[1], x.shape[0], swig_ptr(x))

4. kmeans类

class Kmeans:"""shallow wrapper around the Clustering object. The important method is train()."""def __init__(self, d, k, **kwargs):"""d: input dimension, k: nb of centroids. Additionalparameters are passed on the ClusteringParameters object,including niter=25, verbose=False, spherical = False"""self.d = dself.k = kself.gpu = Falseself.cp = ClusteringParameters()for k, v in kwargs.items():if k == 'gpu':self.gpu = velse:# if this raises an exception, it means that it is a non-existent fieldgetattr(self.cp, k)setattr(self.cp, k, v)self.centroids = Nonedef train(self, x):n, d = x.shapeassert d == self.dclus = Clustering(d, self.k, self.cp)if self.cp.spherical:self.index = IndexFlatIP(d)else:self.index = IndexFlatL2(d)if self.gpu:if self.gpu == True:ngpu = -1else:ngpu = self.gpuself.index = index_cpu_to_all_gpus(self.index, ngpu=ngpu)clus.train(x, self.index)centroids = vector_float_to_array(clus.centroids)self.centroids = centroids.reshape(self.k, d)self.obj = vector_float_to_array(clus.obj)return self.obj[-1] if self.obj.size > 0 else 0.0def assign(self, x):assert self.centroids is not None, "should train before assigning"self.index.reset()self.index.add(self.centroids)D, I = self.index.search(x, 1)return D.ravel(), I.ravel()

5. 索引与数组的转换

serialize_index()
将索引转换成uint8型数组

def serialize_index(index):""" convert an index to a numpy uint8 array  """writer = VectorIOWriter()write_index(index, writer)return vector_to_array(writer.data)

deserialize_index()
使用数组生成索引

def deserialize_index(data):reader = VectorIOReader()copy_array_to_vector(data, reader.data)return read_index(reader)

3. 总结

  1. 导入faiss.py后可以直接生成各类Index的实例,其中参数与c++中初始定义的相同,但是并不是所有的函数都可以直接调用,取决于该文件中是否替换该类型的index。
  2. faiss.py本身与c++ core不关联,它通过swigfaiss而形成完整的index的操作;
  3. swigfaiss.py是由SWIG将C++编写的软件嵌入联接成的python语言的接口模块。要继续分析修改c++代码(添加函数和功能)如何反应在python应用程序中,还需要研究SWIG软件的编译规则,并修改之。

Faiss(12):python接口faiss.py文件分析相关推荐

  1. faiss的python接口使用

    faiss的python接口使用 1. 简介 2. 安装 3. 示例 1. 简介 faiss是一种ann(Approximate Nearest Neighbor)库,可以用于特征的入库,检索. 不仅 ...

  2. python的setup.py文件及其常用命令

    python的setup.py文件及其常用命令 上传者:tingting1718      我也要"分享赚钱" 2014/7/7 关注(286) 评论(0) 声明:此内容仅代表网友 ...

  3. Python: 如何将py文件转成exe文件?

    Python: 如何将py文件转成exe文件? 1.安装PyInstaller模块 pip install PyInstaller 2.将py文件打包成exe执行文件 找到需要打包的py文件所在路径, ...

  4. python中执行py文件出错(提示File “<stdin>”,line 1,SyntaxError:invalid syntax)

    python中执行py文件出错(提示File "<stdin>",line 1,SyntaxError:invalid syntax) 解决办法: 上图中已通过输入py ...

  5. Python+Pandas读取Excel文件分析关系最好的两个演员

    董老师又双叒叕送书啦,6本<Python程序设计基础与应用(第2版)> 推荐图书: <Python程序设计(第3版)>,(ISBN:978-7-302-55083-9),董付国 ...

  6. python导入其他py文件-Python如何import其它.py文件及其函数

    ​ 如上图所示,我想在test_1.py文件中import我在lstm_1.py中定义的LstmParam和 LstmNetwork.我直接采用的是最简单的引用方法:from lstm_1 impor ...

  7. python导入其他py文件-Python中py文件引用另一个py文件变量的方法

    最近自己初学Python,在编程是遇到一个问题就是,怎样在一个py文件中使用另一个py文件中变量,问题如下: demo1代码 import requests r = requests.get(&quo ...

  8. python怎样导出py文件_导出python模块(到字符串或py文件)

    摘要: 我想要一个'module'类型的变量并导出它.在 我使用import从.py文件导入python模块并对其进行更改.我需要将模块导出回一个文件,或者获取完整模块的字符串表示形式,然后将其写入磁 ...

  9. python shell 运行py文件,python怎么运行py文件

    python运行py文件的方法:首先在资源管理器里复制一下py文件存放的路径,并打开命令行:然后切换到py文件的路径下面:接着输入"python 文件名.py":**后按下回车键, ...

最新文章

  1. linux 简单dns搭建,搭建一个简易的DNS服务
  2. 本科毕业的互联网女主管,却被迫要嫁给开挖掘机的高中毕业生!这是咋回事?...
  3. mysql分析表命令_MySql分析整理命令
  4. [bzoj4823][洛谷P3756][Cqoi2017]老C的方块
  5. linux基本操作命令(centos)
  6. [03] Android系统亮度调节
  7. C# 6.0 的新语法特性
  8. ubuntu java环境变量_hadoop:伪分布模式环境变量的配置
  9. 解决ScrollViewer嵌套的DataGrid、ListBox等控件的鼠标滚动事件无效
  10. log4j 配置和使用
  11. python-excel读取代码1
  12. 拼多多总显示服务器冻僵,为什么拼多多商家后台会打不开?什么原因导致的?
  13. latex审阅时添加行号
  14. mac tab command没法切换窗口
  15. 手绘类短视频怎么制作?从剪辑到配音,后期制作也很重要
  16. RTMP转HTTP-FLV视频流web端应用流程记录
  17. linux查询文件大小
  18. 引路蜂技术博客论坛开放
  19. 基于asp.net网上选课系统设计
  20. kubernetes中infra容器的理解

热门文章

  1. 你否有遇到Spring事务失效,花费太多时间找bug
  2. CANoe.DiVa 操作指南 - DTC自动化测试
  3. 102_Hadoop常用命令
  4. 【题】【贪心】NKOJ3827 火车运输
  5. 14nm 8代酷睿?Intel Coffee Lake曝2018上市
  6. shp文件转3dtitle
  7. 带你详细了解 Android Lifecycle
  8. 健身管理系统 -健身管理软件模板
  9. 微信小程序-如何实现input等输入框禁止输入空格【亲测有效】
  10. 终于知道mac下剪切的快捷键是什么了