faiss的基本使用方法.docx
faiss的基本使用方法中 我跑了下demo结果一样 但是这个表示的结果是什么意思呢 faiss.IndexFlatL2(d) 官方解释是暴力搜索L2间隔 方法 add后即建立索引了 无需index.train(xb) 我比照了 有没有这一步的结果是一样的 得到的D是间隔 I是索引 k 4 D,I index.search(xb:5,k)array( 0, 393, 363, 78, 1, 555, 277, 364, 2, 304, 101, 13, 3, 173, 18, 182, 4, 288, 370, 531)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 3810.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.295120208456721,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 所以我觉得这并不是余弦方面的东西 。 4- 我猜测官方肯定会讲需要设置一些参数 有些确实不同 官方回复讲 只有第一种方法得到的是准确的方法 其他方法均是近似方法。但第一种方法慢 想快又准 你在开玩笑 肯定是折中问题 trade-off 速度越快 Recall越小 这是显然的。 5-计算时间的比拟 仍旧从1千万查询1万 但用top100 我估计需要设置的参数可能有很大影响 一是创立index时间 二是查询时间 仍旧用4中的可能不合适的脚本比照 后面再用1G的脚本 训练数据后创立index 再查找 所用的查找时间短 第一种方法是暴力搜索 没有训练数据 直接查找肯定慢。 但假如训练时间长也不符合线上实时应用。第2/3种方法均是训练时间长 10分钟内 这也是不行的 目前发现第4/6种速度较快最后一个创立index2.56s 查询不到1s-0.8s 多GPU就是厉害 第5种结果有点奇怪。 如下所示 查询时间固然短 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- 待后续吧 7-假如创立以及查询时间都是10ms左右那么无需保存index 然而大多数情况下不是的 而且保存的话不需要引入数据重新训练了 因此这里介绍怎么保存index 怎样加载index 可能会与nmslib比拟像 但是我大意了 并不是这样的啊 待续 补充 20200529-531 1.关于GPU中index保存的问题 官方任性直接把关了 仅仅提示了一点 将GPU中的index先推进CPU中才能保存。下面尝试下 仍旧以issue中代码为例 先给出一些 # serialization of indexes to byte arraysdef 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.3802021 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 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