赞
踩
最近公司项目使用了Faiss来计算向量的相关性,比如有1亿个用户,计算每个用户和其最相似的50或者100个用户,此时如果用最暴力的方法,每次算一个用户的时候都遍历1亿个用户然后找出最相似的50/100个,那真的是要等到天长地久了,所以这时候一个快速计算的框架就显得尤为重要,于是今天的主人公Faiss闪亮登场!
Faiss的主要功能就是相似度搜索!尤其是大数据的场景下!
Faiss提供了针对不同场景下应用对Index的封装类。具体可参考:Faiss的index
而我们项目用到的是第二种:IndexFlatIP(Exact Search for Inner Product),also for cosine (normalize vectors beforehand) 因为本身就是要算向量的相似性cosine,而这个索引刚好适合!
以失去保证以找到最近邻居为代价来加速该过程的典型方法是采用诸如k均值的分区技术。 相应的算法有时被称为 cell-probe 方法:
我们使用基于多探测的基于分区的方法(可以联想到best-bin KD-tree的一种变体)。
特征空间被划分为 ncells 个单元格。
由于散列函数(在k均值的情况下,对最靠近查询的质心的分配),数据库向量被分配给这些单元中的一个,并且存储在由ncells反向列表形成的反向文件结构中。
在查询时,会选择一组 nprobe 个的反向列表,将查询与分配给这些列表的每个数据库向量进行比较,这样做,只有一小部分数据库与查询进行比较:作为第一个近似值,这个比例是 nprobe / ncells,但请注意,这个近似值通常被低估,因为反向列表的长度不相等。 当未选择给定查询的最近邻居的单元格时,将显示失败案例。
在C++中,相应的索引是索引IndexIVFFlat。
构造函数将索引作为参数,用于对反转列表进行赋值。 在该索引中搜索查询,并且返回的向量id(s)是应该被访问的反向列表。
import faiss
import numpy as np
d = 64 # dimension
nb = 100000 # database size
nq = 10000 # nb of queries
np.random.seed(1234) # make reproducible
xb = np.random.random((nb, d)).astype('float32') # 训练数据
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32') # 查询数据
xq[:, 0] += np.arange(nq) / 1000.
print(xb.shape)
xb
(100000, 64) array([[1.91519454e-01, 6.22108757e-01, 4.37727749e-01, ..., 6.24916732e-01, 4.78093803e-01, 1.95675179e-01], [3.83317441e-01, 5.38736843e-02, 4.51648414e-01, ..., 1.51395261e-01, 3.35174650e-01, 6.57551765e-01], [7.53425434e-02, 5.50063960e-02, 3.23194802e-01, ..., 3.44416976e-01, 6.40880406e-01, 1.26205325e-01], ..., [1.00811470e+02, 5.90245306e-01, 7.98893511e-01, ..., 3.39859009e-01, 3.01949501e-01, 8.53854537e-01], [1.00669464e+02, 9.16068792e-01, 9.55078781e-01, ..., 5.95364332e-01, 3.84918079e-02, 1.05637990e-01], [1.00855637e+02, 5.91134131e-01, 6.78907931e-01, ..., 2.18976989e-01, 6.53015897e-02, 2.17538327e-01]], dtype=float32)
print(xq.shape)
xq
(10000, 64) array([[ 0.81432974, 0.7409969 , 0.8915324 , ..., 0.72459674, 0.893881 , 0.6574571 ], [ 0.5844774 , 0.797842 , 0.74140453, ..., 0.6768835 , 0.05907924, 0.6396156 ], [ 0.75040764, 0.02659794, 0.5495097 , ..., 0.69562465, 0.16268532, 0.76653737], ..., [10.96773 , 0.05037309, 0.7342035 , ..., 0.89510185, 0.6490696 , 0.86151606], [10.831193 , 0.70606154, 0.1922274 , ..., 0.8026039 , 0.6854174 , 0.60209423], [10.078484 , 0.39106598, 0.01359335, ..., 0.63193923, 0.12561724, 0.78384215]], dtype=float32)
index = faiss.IndexFlatL2(d) # build the index
print(index.is_trained)
index.add(xb) # add vectors to the index 训练数据
print(index.ntotal) # 看索引的总数量 按行来
True
100000
k = 4 # we want to see 4 nearest neighbors
D, I = index.search(xq, k) # actual search
print(I[:5]) # neighbors of the 5 first queries-对应ID
print(D[-5:]) # neighbors of the 5 last queries-对应距离
[[ 381 207 210 477]
[ 526 911 142 72]
[ 838 527 1290 425]
[ 196 184 164 359]
[ 526 377 120 425]]
[[6.5315704 6.97876 7.0039215 7.013794 ]
[4.335266 5.2369385 5.3194275 5.7032776]
[6.072693 6.5767517 6.6139526 6.7323 ]
[6.637512 6.6487427 6.8578796 7.0096436]
[6.2183685 6.4525146 6.548767 6.581299 ]]
import time nlist = 100 #聚类中心的个数 k = 4 quantizer = faiss.IndexFlatL2(d) # the other index index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2) # here we specify METRIC_L2, by default it performs inner-product search assert not index.is_trained t0 = time.time() index.train(xb) # 训练数据 t1 = time.time() print('训练数据时间为 %.2f ' % (t1-t0)) assert index.is_trained t0 = time.time() index.add(xb) # add may be a bit slower as well t1 = time.time() print('加索引时间为 %.2f ' % (t1-t0)) t0 = time.time() D, I = index.search(xq, k) # actual search D, I = index.search(xb[:5], k) # actual search print('自己搜索自己的结果为: ', D) print('查看训练集中前五个最接近的的ID为: ',I) print('查看训练集中和前五个最接近的距离为: ',D) t1 = time.time() print('默认1个聚类中心搜索时间为 %.2f ' % (t1-t0)) print(I[-5:]) # neighbors of the 5 last queries index.nprobe = 10 # default nprobe is 1, try a few more t0 = time.time() D, I = index.search(xq, k) t1 = time.time() print('10个聚类中心搜索时间为 %.2f ' % (t1-t0)) print(I[-5:]) # neighbors of the 5 last queries
训练数据时间为 0.13 加索引时间为 0.10 自己搜索自己的结果为: [[0. 7.1751738 7.20763 7.2511625] [0. 6.3235645 6.684581 6.799946 ] [0. 5.7964087 6.391736 7.2815123] [0. 7.2779055 7.527987 7.6628466] [0. 6.7638035 7.2951202 7.3688145]] 查看训练集中前五个最接近的的ID为: [[ 0 393 363 78] [ 1 555 277 364] [ 2 304 101 13] [ 3 173 18 182] [ 4 288 370 531]] 查看训练集中和前五个最接近的距离为: [[0. 7.1751738 7.20763 7.2511625] [0. 6.3235645 6.684581 6.799946 ] [0. 5.7964087 6.391736 7.2815123] [0. 7.2779055 7.527987 7.6628466] [0. 6.7638035 7.2951202 7.3688145]] 默认1个聚类中心搜索时间为 0.10 [[ 0 393 363 78] [ 1 555 277 364] [ 2 304 101 13] [ 3 173 18 182] [ 4 288 370 531]] 10个聚类中心搜索时间为 1.07 [[ 9900 10500 9309 9831] [11055 10895 10812 11321] [11353 11103 10164 9787] [10571 10664 10632 9638] [ 9628 9554 10036 9582]]
nlist = 100
m = 8 # number of bytes per vector
k = 4
quantizer = faiss.IndexFlatL2(d) # this remains the same
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)
# 8 specifies that each sub-vector is encoded as 8 bits
index.train(xb)
index.add(xb)
D, I = index.search(xb[:5], k) # sanity check
print('查看训练集中前五个最接近的的ID为: ',I)
print('查看训练集中和前五个最接近的距离为: ',D)
index.nprobe = 10 # make comparable with experiment above
D, I = index.search(xq, k) # search
print(I[-5:])
查看训练集中前五个最接近的的ID为: [[ 0 78 608 159]
[ 1 1063 555 380]
[ 2 304 134 46]
[ 3 64 773 265]
[ 4 288 827 531]]
查看训练集中和前五个最接近的距离为: [[1.6157446 6.1152263 6.4348035 6.564185 ]
[1.389575 5.6771317 5.9956017 6.486294 ]
[1.7025063 6.121688 6.189084 6.489888 ]
[1.8057697 6.544031 6.6684756 6.8593984]
[1.4920276 5.79976 6.190908 6.3791513]]
[[ 9900 8746 9853 10437]
[10494 10507 11373 9014]
[10719 11291 10424 10138]
[10122 9638 11113 10630]
[ 9229 10304 9644 10370]]
ngpus = faiss.get_num_gpus()
print("number of GPUs:", ngpus)
number of GPUs: 0
# build a flat (CPU) index
index_flat = faiss.IndexFlatL2(d)
# make it into a gpu index
gpu_index_flat = faiss.index_cpu_to_gpu(res, 0, index_flat)
cpu_index = faiss.IndexFlatL2(d)
gpu_index = faiss.index_cpu_to_all_gpus(cpu_index) # build the index
gpu_index.add(xb) # add vectors to the index
print(gpu_index.ntotal)
k = 4 # we want to see 4 nearest neighbors
D, I = gpu_index.search(xq, k) # actual search
print(I[:5]) # neighbors of the 5 first queries
print(I[-5:]) # neighbors of the 5 last queries
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。