faiss的基本使用方法

在官方的使用demo中,我跑了下demo结果一样,但是这个表示的结果是什么意思呢?

faiss.IndexFlatL2(d)

官方解释是暴力搜索L2距离方法,add后即建立索引了,无需index.train(xb),我对比了,有没有这一步的结果是一样的

得到的D是距离,I是索引,

>>> k=4
>>> D,I=index.search(xb[:5],k)
>>> I
array([[  0, 393, 363,  78],
       [  1, 555, 277, 364],
       [  2, 304, 101,  13],
       [  3, 173,  18, 182],
       [  4, 288, 370, 531]])
>>> D
array([[0.       , 7.1751733, 7.207629 , 7.2511625],
       [0.       , 6.3235645, 6.684581 , 6.7999454],
       [0.       , 5.7964087, 6.391736 , 7.2815123],
       [0.       , 7.2779055, 7.5279865, 7.6628466],
       [0.       , 6.7638035, 7.2951202, 7.3688145]], dtype=float32)

这是合理性检测,因为

xb[:, 0] += np.arange(nb) / 1000.

这一步将每条数据的第一个元素全都加上了该元素的index,由于产生的数据是np.random.random即[0.0, 1.0)

那么在top1必然是该条数据本身,而距离本身就是倒排,从小到大的距离,也就是相似度从大到小。

下面看另外一个函数faiss.IndexFlatIP,余弦相似度还是余弦距离啊??这个sklearn可以作为对比。

【余弦距离=1-余弦相似度,余弦距离越小越好,此时余弦相似度越大】

1-创建index和加载数据集时间用的短,query时用的时间长,在没有用快速方法时,在1千万数据集中查1万个top4需要100s,这个时间太长了。d=64

2-验证中发现faiss.IndexFlatL2计算的是欧式距离的平方,将官方例子中得到的I挨个验证如下:k=6

L2 distances
 [[0.        7.1751733 7.207629  7.2511625 7.321895  7.351989 ]
 [0.        6.3235645 6.684581  6.7999454 6.8844795 6.919898 ]
 [0.        5.7964087 6.391736  7.2815123 7.640502  7.7231803]
 [0.        7.2779055 7.5279865 7.6628466 7.7859573 7.790914 ]
 [0.        6.7638035 7.2951202 7.3688145 7.3900466 7.46482  ]]
index
 [[  0 393 363  78 924 364]
 [  1 555 277 364 617 175]
 [  2 304 101  13 801 134]
 [  3 173  18 182 484  64]
 [  4 288 370 531 178 381]]
0.0,7.175172963007071,7.207629517529313,7.251163094237256,7.32189456020302,7.351988795840555,
0.0,6.323564681684957,6.684580949409394,6.7999456742750795,6.884479284324755,6.919898182110273,
0.0,5.796407676635283,6.391735719267899,7.281511928833652,7.640502286348237,7.723179155793559,
0.0,7.277905725671246,7.527987963394992,7.662846322185942,7.785958432863538,7.790914131558168,
0.0,6.763803673639131,7.295120098456721,7.368814161903231,7.3900459631258855,7.464819925508493,

3-faiss.IndexFlatIP求出来的D根本不是余弦方面的东西,既不是余弦距离也不是余弦相似度,你看看数值就知道了,完全超出了这俩数的最大范围2。(只是将上面的函数替换【L2——>IP】)

cosin distances ????
 [[1937.2388  1936.8904  1936.407   1936.4034  1936.3837  1936.2039 ]
 [3854.9084  3853.9912  3853.5012  3853.4224  3853.3848  3853.3577 ]
 [ 775.8727   775.72815  775.4991   775.3009   775.2481   775.1989 ]
 [1764.4421  1764.3489  1764.1312  1764.116   1764.0082  1763.9166 ]
 [1894.5415  1894.508   1893.7448  1893.7166  1893.6948  1893.63   ]]
index
 [[9996145 9999103 9998705 9999520 9996659 9999163]
 [9999930 9999529 9999160 9999743 9996855 9999219]
 [9986256 9990562 9997819 9999103 9990206 9993650]
 [9994808 9998483 9999930 9999520 9999895 9998792]
 [9999930 9994808 9995033 9999520 9996145 9996287]]

这是什么鬼??完全与L2得到的结果不同,难道说IP需要分nlist或者nprobe??官方加速例子中有train

所以我觉得这并不是余弦方面的东西,等待官方回复吧。

4-不同的结果,使用加速后得到的结果不同?这是怎么回事啊?

我猜想官方肯定会说需要设置一些参数,有些确实不同,

官方回复说,只有第一种方法得到的是准确的方法,其他方法均是近似方法。但第一种方法慢,想快又准,你在开玩笑,肯定是折中问题,trade-off,速度越快,Recall越小,这是显然的。

5-计算时间的比较,仍旧从1千万查询1万,但用top100,我估计需要设置的参数可能有很大影响

速度快慢可能和计算Recall有关

一是创建index时间,二是查询时间,仍旧用4中的可能不适合的脚本对比,后面再用1G的脚本

训练数据后创建index,再查找,所用的查找时间短,第一种方法是暴力搜索,没有训练数据,直接查找肯定慢。

但如果训练时间长也不符合线上实时应用。第2/3种方法均是训练时间长(10分钟内),这也是不行的

目前发现第4/6种速度较快[最后一个创建index2.56s,查询不到1s-0.8s,多GPU就是厉害],

第5种结果有点奇怪。index出现-1的情况,如下所示:查询时间虽然短,0.01s,但是结果有点可怕

 [ 767 1228  230 1196  706 1365  256 1488  562  940  718  346  658   43
  1431 1232  761  837 1108  593 1674  633 1363  152  477 1139  171 1198
   938  672  190 1328  472  525  885  855  977  704  694   86 1150  321
   486 1128  659 1019  881  354  850  742  412 1203  957 1179  496  905
   229 1119  464 1798 1118  402 1186  360 1200  629  985 1519 1483   -1
    -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1
    -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1   -1
    -1   -1]]

官方说Consider 增加 nprobe (or 减少 nlist).但是这样真的好吗?我还要尝试??是不是其他几种方法也可能出现这个情况啊

这次只是偶然没有出现,这就蛋疼了啊,有没有自适应或者一劳永逸的方法??按照这个数据量的话我推荐第6种,速度最快,且又相对正确。

【这个方法如果没有这种情况的话,速度是最快的,0.05s左右,创建index时间8s】

6-1G数据量的脚本,待后续吧

 

7-如果创建和查询时间都是10ms左右则无需保存index,然而大多数情况下不是的,而且保存的话不需要引入数据重新训练了

因而这里介绍怎么保存index,如何加载index,可能会与nmslib比较像

但是我大意了,并不是这样的啊,见我的issue,待续

 

补充:20200529-531

1.关于GPU中index保存的问题,官方任性直接把我的问题关了,仅仅提示了一点,将GPU中的index先推进CPU中才能保存。下面尝试下,仍旧以issue中代码为例

先给出一些参考,

# serialization of indexes to byte arrays

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

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

保存的index目前还不能用np读,是不可见的类,faiss能读。

目前单个GPU query 10000个top100在千万个item中只需32ms,满足需要,这个前提是内存中已经加载好了index,似乎index就没有保存的必要了。只要模型训练完成了,得到user和item的vector,那么只要传过来user id就立即检索并返回item index或者item ids,这个是没有问题的。

2-经过努力,将GPU中的index推进CPU后可以保存为np可读取的index,这真是太厉害了,小明哥,加油!

shape是(2640259323,),这个有点懵逼啊,怎么是一维的啊?而且保存的特别慢,所以我觉得还是别保存为txt的array了吧,其他如npy或者npz速度是相当快的,而且无需指定数据类型,可能里面也是类,至少有数据类型啥的信息,然后解码比较快。

经过证实,保存后的npy读取后是一样的结果。如下:

D:3
 [[6.9091606 7.3802013 7.4417973 7.5518293 7.681326  7.741092  7.7527466
  7.821533  7.8607397 7.8877263 7.899832  7.9249573 8.060022  8.081946
  8.087736  8.098019  8.1126    8.121007  8.171378  8.23694   8.24404
  8.269351  8.279968  8.345175  8.383005  8.401207  8.40219   8.406652
  8.415815  8.449169  8.460677  8.485904  8.500637  8.507256  8.56562
  8.5725765 8.602573  8.604515  8.625033  8.664806  8.684376  8.688564
  8.688978  8.704088  8.71067   8.71347   8.716373  8.726747  8.739986
  8.790137  8.797859  8.814909  8.816978  8.83519   8.870979  8.877043
  8.883351  8.884037  8.886318  8.921809  8.921879  8.927031  8.957334
  8.96539   8.967865  8.982478  8.990862  8.996934  9.006266  9.016508
  9.056393  9.087873  9.095709  9.108036  9.146466  9.165421  9.169607
  9.170092  9.184815  9.186552  9.198939  9.215143  9.2173195 9.222693
  9.230975  9.242826  9.261002  9.261921  9.26444   9.278326  9.280504
  9.282589  9.285308  9.289976  9.298021  9.300825  9.302398  9.308246
  9.310003  9.315947 ]
I:3
 [[ 846   13  263  425  556  421  711  133  479  703  477  801  122  334
   541   18  617 1001   81  414  108   58   79   51  223  827    2  199
   175 1071   26   42   57   89    4  611   78  873   63  218  391  411
   225  173  208  111   17  162  396  119  350  363  794  393  268  459
   412  160  100 1214  243  945  287  483  923 1108    6  331  295  215
   637  179  196  520 1292  784  630  282  705  332 1129  678  406  532
   211  398  112   49  439   48  145 1008   32  392 1224 1306  656  543
   134 1308]


D2:3
 [[6.9091606 7.3802013 7.4417973 7.5518293 7.681326  7.741092  7.7527466
  7.821533  7.8607397 7.8877263 7.899832  7.9249573 8.060022  8.081946
  8.087736  8.098019  8.1126    8.121007  8.171378  8.23694   8.24404
  8.269351  8.279968  8.345175  8.383005  8.401207  8.40219   8.406652
  8.415815  8.449169  8.460677  8.485904  8.500637  8.507256  8.56562
  8.5725765 8.602573  8.604515  8.625033  8.664806  8.684376  8.688564
  8.688978  8.704088  8.71067   8.71347   8.716373  8.726747  8.739986
  8.790137  8.797859  8.814909  8.816978  8.83519   8.870979  8.877043
  8.883351  8.884037  8.886318  8.921809  8.921879  8.927031  8.957334
  8.96539   8.967865  8.982478  8.990862  8.996934  9.006266  9.016508
  9.056393  9.087873  9.095709  9.108036  9.146466  9.165421  9.169607
  9.170092  9.184815  9.186552  9.198939  9.215143  9.2173195 9.222693
  9.230975  9.242826  9.261002  9.261921  9.26444   9.278326  9.280504
  9.282589  9.285308  9.289976  9.298021  9.300825  9.302398  9.308246
  9.310003  9.315947 ]
I2:3
 [[ 846   13  263  425  556  421  711  133  479  703  477  801  122  334
   541   18  617 1001   81  414  108   58   79   51  223  827    2  199
   175 1071   26   42   57   89    4  611   78  873   63  218  391  411
   225  173  208  111   17  162  396  119  350  363  794  393  268  459
   412  160  100 1214  243  945  287  483  923 1108    6  331  295  215
   637  179  196  520 1292  784  630  282  705  332 1129  678  406  532
   211  398  112   49  439   48  145 1008   32  392 1224 1306  656  543
   134 1308]

D is equal? True
I is equal? True

保存的npy大小与直接保存的index相同,没有啥区别。主要是uint8格式的数据,也不知道啥意思,如下示例:

index.shape: (2640259323,)
index_array:12
 [ 73 119  70 108  64   0   0   0 128 150 152   0]

3-尝试多GPU数据的index保存与读取

同样尝试了保存为npy,但是不可行,难道仅仅是因为单个GPU和多个GPU不同??出错如下:借鉴上面的代码

Wrong number or type of arguments for overloaded function 'write_index'.
  Possible C/C++ prototypes are:
    faiss::write_index(faiss::Index const *,char const *)
    faiss::write_index(faiss::Index const *,FILE *)
    faiss::write_index(faiss::Index const *,faiss::IOWriter *)

可以保存为index没有问题,但是同样的检索量,需要0.8s????what?

search Time: 0.808903

这种情况不知道为啥子。

4-查找了FlatIP相关东西,发现是内积的意思,相关issue在此

但是出错了。不纠结了,回去做面条吧。拜拜。

 

For Video Recommendation in Deep learning QQ Group 277356808

For Speech, Image, Video in deep learning QQ Group 868373192

I'm here waiting for you.
 

 

你可能感兴趣的:(Recommendation)