annoy
annoy (大约最近的邻居哦,是的)是一个具有Python绑定的C ++库,可以搜索接近给定查询点的空间中的点。它还创建了大型的基于文件的数据结构,这些数据结构被误入内存,以便许多过程可以共享相同的数据。
安装
要安装,只需完成PIP安装 – 用户annoy即可从PYPI中拉下最新版本。
对于C ++版本,只需克隆回购和#include“ annoy lib.h”即可。
背景
还有其他一些库可以进行最近的邻居搜索。 annoy几乎与最快的库一样快(见下文),但是实际上还有另一个真正使annoy分开的功能:它具有将静态文件用作索引的能力。特别是,这意味着您可以跨流程共享索引。 annoy也将创建索引加载的删除,因此您可以将索引传递给文件并迅速将其映射到内存中。另一个好annoy的是,它试图最大程度地减少内存足迹,因此索引很小。
为什么这有用?如果您想找到最近的邻居并且有很多CPU,则只需构建一次索引即可。您还可以将静态文件传递并分发用于生产环境,Hadoop作业等。任何过程都可以将索引加载到内存中,并能够立即进行查找。
我们在Spotify使用它进行音乐推荐。运行矩阵分解算法后,每个用户/项目都可以表示为f维空间中的向量。该库帮助我们搜索类似的用户/项目。我们在高维空间中有数百万个曲目,因此记忆使用是一个主要问题。
annoy是由erik Bernhardsson在黑客一周的几个下午建造的。
特征摘要
- 欧几里得距离,曼哈顿距离,余弦距离,锤子距离或点(内部)产品距离
- 余弦的距离等于归一化矢量的欧几里得距离= sqrt(2-2*cos(u,v))
- 如果您没有太多尺寸(例如<100),则效果更好
- 小记忆使用
- 让您在多个过程之间共享内存
- 索引创建与查找是分开的(特别是一旦创建树了就无法添加更多项目)
- 本机Python支持,用2.7、3.6和3.7测试。
- 在磁盘上构建索引以启用索引不适合内存的大数据集(由Rene Hollander贡献)
Python代码示例
annoy Index
import random
f = 40 # Length of item vector that will be indexed
t = annoy Index(f, \’angular\’)
for i in range(1000):
v = [random.gauss(0, 1) for z in range(f)]
t.add_item(i, v)
t.build(10) # 10 trees
t.save(\’test.ann\’)
# …
u = annoy Index(f, \’angular\’)
u.load(\’test.ann\’) # super fast, will just mmap the file
print(u.get_nns_by_item(0, 1000)) # will find the 1000 nearest neighbors\”>
from annoy import annoy Index import random f = 40 # Length of item vector that will be indexed t = annoy Index ( f , \'angular\' ) for i in range ( 1000 ): v = [ random . gauss ( 0 , 1 ) for z in range ( f )] t . add_item ( i , v ) t . build ( 10 ) # 10 trees t . save ( \'test.ann\' ) # ... u = annoy Index ( f , \'angular\' ) u . load ( \'test.ann\' ) # super fast, will just mmap the file print ( u . get_nns_by_item ( 0 , 1000 )) # will find the 1000 nearest neighbors
现在,它仅接受整数作为项目的标识符。请注意,它将为Max(ID)+1项目分配内存,因为它假设您的项目编号为0…N-1。如果您需要其他ID,则必须自己跟踪地图。
完整的Python API
- annoy索引(F,公制)返回一个新的索引,该索引是读写的,并存储了f维度的向量。公制可以是“角”,“欧几里得”,“曼哈顿”,“锤子”或“ dot”。
- A.ADD_ITEM(i,v)添加了带有向量v。的项目I(任何非负整数),请注意,它将为Max(i)+1项目分配内存。
- A.建造(N_Trees,n_jobs = -1)建造了n_trees树的森林。查询时,更多的树可提供更高的精度。调用构建后,不再添加更多项目。 N_jobs指定用于建造树木的线程数。 N_JOBS = -1使用所有可用的CPU内核。
- A.Save(FN,Prefault = false)将索引保存到磁盘并加载(请参阅下一个功能)。保存后,不再添加更多项目。
- A.负载(fn,prefault = false)加载(mmaps)磁盘的索引。如果将预配置设置为true,它将将整个文件预先读取到内存中(使用mmap with map_populate)。默认值为false。
- A.unload()卸载。
- a.get_nns_by_item(i,n,search_k = -1,include_distances = false)返回n最接近的项目。在查询期间,它将检查到search_k节点,如果未提供,则默认为n_trees * n。 search_k为您提供了更好的准确性和速度之间的运行时间权衡。如果将Inlude_distances设置为true,它将返回一个带有两个列表的2个元素元组:第二个包含所有相应距离的列表。
- a.get_nns_by_vector(v,n,search_k = -1,include_distances = false)相同,但通过向量v查询。
- a.get_item_vector(i)返回以前添加的项目I的向量。
- a.get_distance(i,j)返回项目i和j之间的距离。注意:这用于返回平方距离,但截至2016年8月已更改。
- a.get_n_items()返回索引中的项目数。
- a.get_n_trees()返回索引中的树木数。
- A.On_disk_build(FN)准备annoy以在指定文件中构建索引而不是RAM(在添加项目之前执行,无需保存后,构建后就保存)
- A.set_seed(种子)将用给定种子初始化随机数发生器。仅用于建造树,即在添加项目之前仅需通过它。致电A.Build(N_TREES)或A.LOAD(FN)后将没有效果。
笔记:
- 对值进行没有界限检查,因此要小心。
- annoy使用归一化向量的欧几里得距离在其角度距离中,对于两个向量u,V等于SQRT(2(1-COS(u,v)))
C ++ API非常相似:只需#include“ annoy lib.h”即可访问它。
权衡
调整annoy只需要两个主要参数:树木的数量N_Trees和在搜索搜索过程中检查的节点数量。
- 在构建时间期间提供N_TREES,并影响构建时间和索引大小。更大的值将带来更准确的结果,但索引较大。
- search_k在运行时提供,并影响搜索性能。更大的价值将带来更准确的结果,但需要更长的时间才能返回。
如果未提供search_k,则将默认为n * n_trees,其中n是近似最近的邻居的数量。否则,search_k和n_trees是大致独立的,即,如果保持search_k的稳定,则N_TREES的值不会影响搜索时间,反之亦然。基本上,考虑到您可以负担得起的内存量,建议将N_TREES设置尽可能大,并且建议将搜索_K设置尽可能大,考虑到查询的时间限制。
您也可以接受较慢的搜索时间,而支持减少加载时间,内存使用情况和磁盘IO。在受支持的平台上,该索引在加载过程中被预先出现并保存,从而使文件从磁盘中预先读取到内存中。如果将prefault设置为false,则根据搜索完成搜索的必要条件,而是从磁盘中读取MMAPPED索引页面并将其缓存。与索引尺寸相比,这可以大大增加早期搜索时间,但可能更适合具有较低内存的系统,当对加载索引执行少量查询时和/或当索引的大面积不太可能与搜索查询有关时。
它如何工作
使用随机投影并建立树。在树上的每个中间节点上,选择一个随机的超平面,将空间分为两个子空间。通过从子集中抽样两个点并将超平面等距从中取出两个点来选择这种超平面。
我们这样做是k时的,以便我们得到一片树林。必须通过查看精确度和性能之间的权衡来调整您的需求。
锤击距离(由马丁·阿穆勒(MartinAumüller)贡献)将数据包装到引擎盖下的64位整数中,并使用内置的位计数原始图,因此它可能很快。所有拆分都是轴对准的。
DOT产品距离(由Peter Sobot和Pavel Korobov贡献)使用Bachrach等人在Microsoft Research上使用Bachrach等人的方法将提供的向量从DOT(或“内部产品”)空间降低到更查询的余弦空间。
更多信息
- Dirk Eddelbuettel提供了R annoy的R版本。
- 安迪·斯隆(Andy Sloane)提供了Java版本的annoy ,尽管目前仅限于余弦和只读。
- Pishen Tsai提供了annoy的Scala包装器,该包装使用JNA来称呼C ++ annoy的库库。
- atsushi tatsuma提供了红宝石绑定,以使annoy 。
- TaneliLeppä提供了对GO的实验支持。
- Boris Nagaev写了Lua Bindings。
- 在Spotify Hack Week 2016年的一部分(此后),Jim Kang写了节点绑定为annoy 。
- Min-Seok Kim构建了Scala版本的annoy 。
- Hanabi1224与dotnet,jvm和Dart仅阅读绑定一起构建了仅易annoy版本的Rust版本。
- annoy机器学习聚会的演讲
- Linux,OS X和Windows上的Conda软件包annoy 。
- Ann-Benchmarks是几个近大约最近的邻居库的基准。 annoy似乎相当具竞争力,尤其是在更高的精确度上:
源代码
所有这些都用C ++编写,并具有一些丑陋的优化,以进行性能和记忆使用。您已被警告:)
该代码应得益于Qiang Kou和Timothy Riley。
要运行测试,请执行python setup.py nosetests。测试套件包括一个从Internet下载的大型现实世界数据集,因此执行需要几分钟。
讨论
随时向annoy – 用户组发表任何问题或评论。我在Twitter上@fulhack。
