【annoy】高维空间求近似最近邻
在介绍腾讯词向量时,用到了annoy,这里对annoy的用法详细做一下介绍。
GitHub地址:https://github.com/spotify/annoy
Annoy是Erik Bernhardsson在Hack Week期间花了几个下午写的(github原话),全称Approximate Nearest Neighbors Oh Yeah(这个Oh Yeah真是亮瞎眼)。这个包的优点就是快,内存占用也小,还可以静态存储索引用于更多任务。目前已经在音乐推荐系统Spotify中应用。
这个工具可以用来计算空间中点的距离,输入某个点,返回最近的若干点。
这个包直接pip install annoy
就可以安装,C++版本的直接下载后#include "annoylib.h"
。
使用方法:
from annoy import AnnoyIndex
import random
f = 40
t = AnnoyIndex(f, 'angular') # Length of item vector that will be indexed
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 = AnnoyIndex(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
默认的索引是从0到n-1。
API:
AnnoyIndex(f, metric)
f 向量维度,metric 距离度量方式,取值 "angular", "euclidean", "manhattan", "hamming", or "dot". 计算angular是使用的sqrt(2(1-cos(u,v)))这个公式,用的欧几里得距离。euc = sqrt(2(1-cos))。
a.add_item(i, v)
添加元素 i 和向量 v
a.build(n_trees, n_jobs=-1)
n_trees是森林的树数目,值越大结果越精确,n_jobs 进程数,默认-1使用所有的CPU。build调用之后,就不能再添加元素了。
a.save(fn, prefault=False)
保存到本地
a.load(fn, prefault=False)
从本地读取,prefault是预先读入内存,默认为False.
a.unload()
清除加载内容
a.get_nns_by_item(i, n, search_k=-1, include_distances=False)
返回n个最近元素。在查询过程中,它将检查search_k个节点,如果没有提供,默认为n_trees * n。search_k给了一个速度和准确率的折中。include_distances设为True,会提供一个由两个列表组成的二元组,第二个列表包含所有相关的距离。
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的距离,squared distance
a.get_n_items()
返回元素数量
a.get_n_trees()
返回树数目
a.on_disk_build(fn)
指定在除了RAM之外的其他文件上构建索引(在添加元素之前执行)
a.set_seed(seed)
在构建树之前可以指定随机数
这里面主要有两个参数需要调,n_trees和search_k,一个是构建时的参数,一个是搜索时的参数。
下面这是qps和recall值之间的trade-off,使用时要综合考虑准确率和速度。