在官方的使用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.