精华内容
下载资源
问答
  • Annoy
    2021-03-24 12:00:09

    annoy 安装

    QS1: gcc 版本太低, 需要进行升级
    参考: http://www.vpser.net/manage/centos-6-upgrade-gcc.html
    在这里插入图片描述

    QS2:在安装的时候回遇到以下的情况,提示 “fatal error: Python.h: No such file or directory”

    ERROR: Command errored out with exit status 1:
         command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-8nik2grm/annoy_8d4a0092f225406a9a9d27f03a9146b5/setup.py'"'"'; __file__='"'"'/tmp/pip-install-8nik2grm/annoy_8d4a0092f225406a9a9d27f03a9146b5/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-__bt1mwd/install-record.txt --single-version-externally-managed --compile --install-headers /usr/local/include/python3.6m/annoy
             cwd: /tmp/pip-install-8nik2grm/annoy_8d4a0092f225406a9a9d27f03a9146b5/
        Complete output (17 lines):
        running install
        running build
        running build_py
        creating build
        creating build/lib.linux-x86_64-3.6
        creating build/lib.linux-x86_64-3.6/annoy
        copying annoy/__init__.py -> build/lib.linux-x86_64-3.6/annoy
        running build_ext
        building 'annoy.annoylib' extension
        creating build/temp.linux-x86_64-3.6
        creating build/temp.linux-x86_64-3.6/src
        gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python3.6m -c src/annoymodule.cc -o build/temp.linux-x86_64-3.6/src/annoymodule.o -D_CRT_SECURE_NO_WARNINGS -march=native -O3 -ffast-math -fno-associative-math -DANNOYLIB_MULTITHREADED_BUILD -std=c++14
        src/annoymodule.cc:17:10: fatal error: Python.h: No such file or directory
           17 | #include "Python.h"
              |          ^~~~~~~~~~
        compilation terminated.
        error: command 'gcc' failed with exit status 1
        ----------------------------------------
    ERROR: Command errored out with exit status 1: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-8nik2grm/annoy_8d4a0092f225406a9a9d27f03a9146b5/setup.py'"'"'; __file__='"'"'/tmp/pip-install-8nik2grm/annoy_8d4a0092f225406a9a9d27f03a9146b5/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-__bt1mwd/install-record.txt --single-version-externally-managed --compile --install-headers /usr/local/include/python3.6m/annoy Check the logs for full command output.
    

    sol, 进行安装
    yum install python-devel 但是需要注意 python的安装版本,跟你需要的是否一致,这个默认安装的是 python2

    Loaded plugins: fastestmirror, langpacks, product-id, search-disabled-repos, subscription-manager
    
    This system is not registered with an entitlement server. You can use subscription-manager to register.
    
    Loading mirror speeds from cached hostfile
     * centos-sclo-rh: mirrors.aliyun.com
     * centos-sclo-sclo: mirrors.aliyun.com
    Package python-devel-2.7.5-90.el7.x86_64 already installed and latest version
    Nothing to do
    

    如果版本不一致,其实并不起作用。

    yum install python3-devel 这个是安装的python3的

    更多相关内容
  • Spark-Annoy(WIP) 在Apache Spark上构建 Index。 然后使用Annoy查询邻居。 注意 我在5分钟内使用100个节点构建了117M个64维向量的索引。 设置为; // version: 0.1.4 // spark.executor.instances = 100 // ...
  • Annoy(Approximate Nearest Neighbors Oh Yeah)是一个 C++ 库,带有 Python 绑定,用于搜索空间中靠近给定查询点的点。 它还创建了大型只读基于文件的数据结构,这些数据结构被映射到内存中,以便许多进程可以共享...
  • annoy-1.17.0-cp37-cp37m-win_amd64
  • 资源分类:Python库 所属语言:Python 资源全名:annoy-1.16.0.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
  • annoy-1.17.0-cp38-cp38-win_amd64
  • annoy-1.17.0-pp37-pypy37_pp73-win_amd64
  • annoy简单介绍 annoy是高维空间求近似最近邻的一个开源库。其官网介绍如下: Annoy (Approximate Nearest Neighbors Oh Yeah) is a C++ library with Python bindings to search for points in space that are close...
  • 资源来自pypi官网。 资源全名:annoy-1.16.3.tar.gz
  • annoy-1.17.0-cp310-cp310-win32
  • annoy-1.17.0-cp37-cp37m-win32
  • annoy-1.17.0-cp36-cp36m-win_amd64
  • annoy-1.17.0-cp39-cp39-win32

    2021-08-19 10:18:42
    annoy-1.17.0-cp39-cp39-win32
  • annoy python3.6 离线安装包 annoy-1.15.2-cp36-cp36m-win_amd64
  • 预编译好的python3.7+window10下的annoy包;可以避开安装VC++14.0。 应该直接解压到site-packages下就能用了~
  • Annoy算法简单介绍

    2022-08-05 17:46:00
    Annoy算法介绍

    Annoy算法

    与Faiss相比,Annoy搜索,速度更快一点,主要目的是建立一个数据结构快速找到任何查询点的最近点。通过牺牲查询准确率来换取查询速度,这个速度比faiss速度还要快。

    是什么

    Annoy:最近邻向量搜索,

    原理/过程

    算法原理:先构建索引,对于每个二叉树都建立索引,在这里二叉树是随机构造的

    第一步:先随机找两个点,根据这两个点进行连线,找到垂直平分线,称为超平面。

     

    第二步:在切分后的子空间,继续按照上面的方法,找一个点,然后,进行连线,找垂直平分超平面。

     

    不断继续分割,直到子空间中数据量不超过k,在这里k是可以提前定义的,如k=10.

     

    其实,从另一个角度出发,在不断分割的过程中,类似于二叉树,从根节点到子节点,然后不断的切割

     

    通过二叉树来表示空间节点的分布,节点表示子空间,在点的分布空间中,接近的子空间在二叉树中表现为位置靠近的节点。

    对应着一个假设条件,对于空间内距离非常近的两个点,任何切分方法都不能使之分开。

    要想查找某个点,只需要从根节点出发,一路往下走,最终到达子节点,自然就找到匹配的数据了。

    但是有时候可能找到的数据量比较少,

    1、优先队列

    把多棵树放到优先队列,挨个进行处理,然后设定阈值,如果查询的点,与二叉树中某个节点距离比较近,我们可以在根节点出发的时候,同样找旁边的相关节点,走两个分支。

     

     

    或者还可以建立多个二叉树,

    多个二叉树进行交叉,构成森林,基本上可以覆盖到查找匹配点所在的区域,然后通过计算所有点的距离,并进行排序,取TopK

     

     

    如何查询:

    把每个二叉树的根节点都放进优先队列

    对每一个二叉树都进行搜索,每一个二叉树都可以得到TopK个候选集

    删除重复候选集

    计算候选集与查询点的距离,并进行排序

    返回TopK

    优缺点

    优点:查询速度快,时间复杂度O(log(n))

    缺点:对于存在边界附近的点,可能还是会无法查找出来,导致损失一部分查询精确率。

    如何优化

    应用场景

    如:在搜索业务中,数据候选集dataset,需要对新来的一个或多个数据进行查询,返回数据集中与该查询最相似的TopK数据。

    最笨的方法,每条数据都计算一次,当然,如果候选集比较小,也无所谓,对于海量数据呢,总不能依次计算吧。通过annoy算法可以大大降低查询时间,在牺牲少量查询精确率的情况下。

      

    具体应用

    安装包:pip install annoy

    参数介绍:

    重要设置:n_trees:树的个数,直接影响构建索引的时间,值越大表示最终的精度越高,但是会有更多的索引,主要影响索引时间

    Search_k:衡量查询精度和速度,值越大表示搜索耗时越长,搜索的精度越高;如果不进行设定的情况下,默认为n_trees*n

    from annoy import AnnoyIndex
    
    import random
    
     
    
    # f 表示向量的维度
    
    f = 40
    
    # 'angular' 是 annoy 支持的一种度量;
    
    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)]
    
        # i 是一个非负数,v 表示向量
    
        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
    
     
    
    # 返回与第 0 个向量最相似的 Top 100 向量;
    
    print(u.get_nns_by_item(0, 1000)) # will find the 1000 nearest neighbors
    
     
    
    # 返回与该向量最相似的 Top 100 向量;
    
    print(u.get_nns_by_vector([random.gauss(0, 1) for z in range(f)], 1000))
    
     
    
    # 返回第 i 个向量与第 j 个向量的距离;
    
    # 第 0 个向量与第 0 个向量的距离
    
    print(u.get_distance(0, 0))
    
    # 第 0 个向量与第 1 个向量的距离
    
    print(u.get_distance(0, 1))
    
     
    
    # 返回索引中的向量个数;
    
    print(u.get_n_items())
    
    # 返回索引中的树的棵数;
    
    print(u.get_n_trees())
    
     
    
    # 不再加载索引
    
    print(u.unload())
    
    
    
    不同距离应用:基于hamming距离
    
    from annoy import AnnoyIndex
    
     
    
    # Mentioned on the annoy-user list
    
    bitstrings = [
    
     '0000000000011000001110000011111000101110111110000100000100000000',
    
        '0000000000011000001110000011111000101110111110000100000100000001',
    
        '0000000000011000001110000011111000101110111110000100000100000010',
    
        '0010010100011001001000010001100101011110000000110000011110001100',
    
        '1001011010000110100101101001111010001110100001101000111000001110',
    
        '0111100101111001011110010010001100010111000111100001101100011111',
    
        '0011000010011101000011010010111000101110100101111000011101001011',
    
        '0011000010011100000011010010111000101110100101111000011101001011',
    
        '1001100000111010001010000010110000111100100101001001010000000111',
    
        '0000000000111101010100010001000101101001000000011000001101000000',
    
        '1000101001010001011100010111001100110011001100110011001111001100',
    
        '1110011001001111100110010001100100001011000011010010111100100111',
    
    ]
    
     
    
    # 将其转换成二维数组
    
    vectors = [[int(bit) for bit in bitstring] for bitstring in bitstrings]
    
     
    
    # 64 维度
    
    f = 64
    
    idx = AnnoyIndex(f, 'hamming')
    
    for i, v in enumerate(vectors):
    
        idx.add_item(i, v)
    
     
    
    # 构建索引
    
    idx.build(10)
    
    idx.save('idx.ann')
    
    idx = AnnoyIndex(f, 'hamming')
    
    idx.load('idx.ann')
    
    js, ds = idx.get_nns_by_item(0, 5, include_distances=True)
    
     
    
    # 输出索引和 hamming 距离
    
    print(js, ds)

    基于欧几里得距离的 annoy 使用案例:

    from annoy import AnnoyIndex
    
    import random
    
     
    
    # f 表示向量的维度
    
    f = 2
    
    # 'euclidean' 是 annoy 支持的一种度量;
    
    t = AnnoyIndex(f, 'euclidean')  # Length of item vector that will be indexed
    
    # 插入数据
    
    t.add_item(0, [0, 0])
    
    t.add_item(1, [1, 0])
    
    t.add_item(2, [1, 1])
    
    t.add_item(3, [0, 1])
    
     
    
    # 树的数量
    
    t.build(n_trees=10) # 10 trees
    
    t.save('test2.ann')
    
    u = AnnoyIndex(f, 'euclidean')
    
    u.load('test2.ann') # super fast, will just mmap the file
    
    print(u.get_nns_by_item(1, 3)) # will find the 1000 nearest neighbors
    
    print(u.get_nns_by_vector([0.1, 0], 3))

    展开全文
  • ANNOY索引算法

    2021-09-03 14:02:39
    Annoy向量检索算法 官方包:https://github.com/spotify/annoy (gensim自带AnnoyIndexer) https://medium.com/@kevin_yang/ python 接口 pip install --user annoy AnnoyIndex(f, metric) #返回一个只读索引,...

    Annoy向量检索算法

    官方包:https://github.com/spotify/annoy
    (gensim自带AnnoyIndexer)
    https://medium.com/@kevin_yang/

    python 接口

    pip install --user annoy

    AnnoyIndex(f, metric) #返回一个只读索引,存储f维向量. Metric可以是 "angular", "euclidean", "manhattan", "hamming", or "dot".
    a.add_item(i, v) # 添加item i (非负整数) 和对应的向量. 会给分配max(i)+1的空间
    a.build(n_trees, n_jobs=-1) #生成一个由n_trees组成的森林。更多的树会有更高的精确度.生成之后,不能再添加别的item. n_jobs代表生成森林时的线程个数. n_jobs=-1代表使用所有的CPU核.
    a.save(fn, prefault=False) #保存索引
    a.load(fn, prefault=False) #加载索引文件。将会把所有数据加载到内存中 (using mmap with MAP_POPULATE). Default is False.
    a.unload() #unloads.
    a.get_nns_by_item(i, n, search_k=-1, include_distances=False) #returns the n closest items. During the query it will inspect up to search_k nodes which defaults to n_trees * n if not provided. search_k代表时间和准确率和速度的折中. include_distances为True将会返回两个item和distance
    a.get_nns_by_vector(v, n, search_k=-1, include_distances=False) #same but query by vector v.
    a.get_item_vector(i) #返回第i item的vector
    a.get_distance(i, j) #返回i和j的距离. NOTE: this used to return the squared distance, but has been changed as of Aug 2016.
    a.get_n_items() #返回索引的数据的个数
    a.get_n_trees() #返回索引的树的个数
    a.on_disk_build(fn) #prepares annoy to build the index in the specified file instead of RAM (execute before adding items, no need to save after build)
    a.set_seed(seed) #will initialize the random number generator with the given seed. Only used for building up the tree, i. e. only necessary to pass this before adding the items. Will have no effect after calling a.build(n_trees) or a.load(fn).
    
    注意:
    没有对值执行边界检查,因此要小心。
    使用归一化向量的欧几里德距离作为其角度距离,对于两个向量u,v等于sqrt(21-cos(u,v)))
    
    n_tree在构建期间提供,并影响构建时间和索引大小。值越大,结果越准确,但索引越大。
    search_k是在运行时提供的,会影响搜索性能。值越大,结果越准确,但返回时间越长。
    如果未提供search_k,它将默认为n*n_tree,其中n是近似最近邻的数目。
    否则,search_k和n_tree基本上是独立的,即如果search_k保持不变,n_tree的值不会影响搜索时间,反之亦然。
    基本上,考虑到您可以负担的内存量,建议将n_tree设置得尽可能大,并且考虑到查询的时间限制,建议将search_k设置得尽可能大。
    
    

    示例:

    def __init__(self):
    	self.index = AnnoyIndex(self.dim, 'angular')   # Length of item vector that will be indexed
    	for i in range(len(self.vectors)):
    	    self.index.add_item(i, self.vectors[i])
    	    
    	self.index.build(5) # 5 trees
    	self.index.save(annoy_path)
    	self.index.load(annoy_path) # super fast, will just mmap the file
    
    
    def search(self, input_v):
          input_shape = input_v.shape
          candidates = []
          for i in range(input_shape[0]):
              input_i = np.array(input_v[i,:]).reshape(-1,1)
              candi_ids_i = self.index.get_nns_by_vector(input_i, self.top_k, search_k=-1, include_distances=False)
    
              candi_i = []
              ids = set()
              for i in range(self.top_k):
                  id_i = self.id_lists[candi_ids_i[i]]
                  if candi_ids_i[i] in ids:
                      continue
                  vector_i = np.array(self.index.get_item_vector(candi_ids_i[i])).reshape(-1,1)
                  dis_i = cos_sim(input_i, vector_i)
                  candi_i.append((id_i, dis_i[0]))
                  ids.add(id_i)
              candidates.append(candi_i)
          return candidates
    

    C++接口

    #include “annoylib.h”.

    参考

    https://blog.csdn.net/lsp1991/article/details/78127754
    https://blog.csdn.net/lsp1991/article/details/78127754

    算法目标

    annoy 算法的目标是建立一个数据结构能够在较短的时间内找到任何查询点的最近点,在精度允许的条件下通过牺牲准确率来换取比暴力搜索要快的多的搜索速度。

    算法流程

    1: 建立索引

    Annoy的目标是建立一个数据结构,使得查询一个点的最近邻点的时间复杂度是次线性
    Annoy 通过建立一个二叉树来使得每个点查找时间复杂度是O(log n)

    看下面这个图,随机选择两个点,以这两个节点为初始中心节点,执行聚类数为2的kmeans过程,最终产生收敛后两个聚类中心点。这两个聚类中心点之间连一条线段(灰色短线),建立一条垂直于这条灰线,并且通过灰线中心点的线(黑色粗线)。这条黑色粗线把数据空间分成两部分。在多维空间的话,这条黑色粗线可以看成等距垂直超平面。
    在这里插入图片描述

    接下里在超平面分割后的字空间内按照同样的方法继续确定超平面分割字空间,通过这样的方法我们可以将子空间的从属关系用二叉树来表示:
    在这里插入图片描述

    然后再继续分割,继续重复上述步骤,直到子节点包含的数据点数不超过 K 个,这里我们取 K = 10。
    在这里插入图片描述

    通过多次递归迭代划分的话,最终原始数据会形成类似下面这样一个二叉树结构。二叉树底层是叶子节点记录原始数据节点,其他中间节点记录的是分割超平面的信息。Annoy建立这样的二叉树结构是希望满足这样的一个假设: 相似的数据节点应该在二叉树上位置更接近,一个分割超平面不应该把相似的数据节点分割二叉树的不同分支上。对应的二叉树结构如下所示:
    在这里插入图片描述

    2 查询过程

    通过上述步骤,我们建立了二叉树的结构用于表示上述点分布空间,每个节点都表示一个子空间,在点分布空间中接近的子空间在二叉树结构中表现为位置靠近的节点。

    这里有一个假设,如果两个点在空间中彼此靠近,任何超平面都不可能将他们分开。
    如果要搜索空间中的任意一个点,我们都可以从根结点遍历二叉树。
    假设我们要找下图中红色 X 表示的点的临近点:查找的过程就是不断看他在分割超平面的哪一边。
    从二叉树索引结构来看,就是从根节点不停的往叶子节点遍历的过程。通过对二叉树每个中间节点(分割超平面相关信息)和查询数据节点进行相关计算来确定二叉树遍历过程是往这个中间节点左孩子节点走还是右孩子节点走。
    通过以上方式完成查询过程。
    在这里插入图片描述

    3 存在问题

    但是上述描述存在两个问题:
    (1)查询过程最终落到叶子节点的数据节点数小于 我们需要的Top N相似邻居节点数目怎么办?
    (2)两个相近的数据节点划分到二叉树不同分支上怎么办?

    4 解决方法

    (1)如果分割超平面的两边都很相似,那可以两边都遍历;下面是是个示意图:
    在这里插入图片描述
    2) 建立多棵二叉树树,构成一个森林,每个树建立机制都如上面所述那样。多棵树示意图如下所示:
    在这里插入图片描述
    (3) 采用优先队列机制:采用一个优先队列来遍历二叉树,从根节点往下的路径,根据查询节点与当前分割超平面距离(margin)进行排序。

    5:合并节点

    每棵树都返回一堆近邻点后,如何得到最终的Top N相似集合呢?首先所有树返回近邻点都插入到优先队列中,求并集去重, 然后计算和查询点距离, 最终根据距离值从近距离到远距离排序, 返回Top N近邻节点集合。

    展开全文
  • 1. 写在前面 在写fun-rec新闻推荐系统的YouTubeDNN召回的时候, 得到用户向量和新闻向量,基于用户向量,需要从海量新闻里面得到最相似的TopK个新闻, 此时需要... 这篇文章主要是记录下如何用annoy工具包做向量检索

    1. 写在前面

    在写fun-rec新闻推荐系统的YouTubeDNN召回的时候, 得到用户向量和新闻向量,基于用户向量,需要从海量新闻里面得到最相似的TopK个新闻, 此时需要用到快速向量检索技术,之前用过的一个工具是faiss, 具体使用方法我也记录了一篇博客Faiss(Facebook开源的高效相似搜索库)学习小记, 但是faiss在windows系统中并不是很好安装,并且看着也有些复杂, 这次又接触了另一个向量检索的好用工具包, 就是annoy了。 这篇文章主要是记录下如何用annoy工具包做向量检索。

    简单的总结: annoy包用于向量最近邻检索,从海量item中快速查找相似的TopK个item

    关于annoy包的详细介绍,可以见https://github.com/spotify/annoy

    2. 安装annoy

    首先,我们需要先安装annoy, 我们可以直接pip install annoy 或者指定源进行安装pip install -i https://pypi.tuna.tsinghua.edu.cn/simple annoy

    但是我用这个命令的时候, 会报Microsoft visual c++ 14.0 is required …, 因为我这边的系统目前是Windows,Linux或者Mac上应该是好使。
    在这里插入图片描述

    这个错误, 之前装faiss或者需要C++编译环境的那种包的时候,貌似也遇到过, 一劳永逸的方式,就是装一个C/C++编译环境,但是贼麻烦,并且占的内存也非常大。

    我这里目前不想用这种方式, 采用另一种方式, 这里给定一个python万能包库, 在里面搜索annoy, 找到指定的python版本, 然后下载即可。

    然后本地pip install 文件的绝对路径进行安装, 这个方法我这边好使。既然说到安装包了,就再多整理一点知识。

    我们安装python包的时候, 最常用的就是用pip安装了,这里也借着这个机会, 学习了下pip常用的命令, 也一并记录到这里了, 详细内容看pip必备速查表

    # 安装python包
    pip install 包名
    
    # 指定版本号
    pip install 包名==版本 
    pip install 包名>=2.22, <3
    pip install 包名!=2.22
    
    # 指定镜像源安装
    pip install -i url 包名  # 其中国内镜像源( url ) 可以是清华、中科大、豆瓣等
    #清华:https://pypi.tuna.tsinghua.edu.cn/simple
    #豆瓣:http://pypi.douban.com/simple/
    
    # 本地wheel文件安装 whl文件可以去https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyhook离线下载到本地
    pip install 包名.whl
    
    # github仓库中安装
    pip install git+包的github地址
    
    # 更新python库
    pip install 包名 -U
    
    # 查看可提供安装的版本
    pip install 包名==lemon
    
    # 查看已经安装的python库
    pip list
    # 查询当前环境可升级的包
    pip list -o
    
    # 查看python库的信息
    pip show 包名
    pip show 包名 -f
    
    # 卸载包
    pip uninstall 包名
    
    # 导出依赖包列表 freeze命令, 复用包很方便
    pip freeze > requirements.txt  # 获取当前环境安装python库的版本信息,导入到txt文件中
    # 从依赖包中安装
    pip install -r requirements.txt
    
    # 将python库制作成wheel文件,可以提供给他人用.whl文件,先安装wheel库
    pip install wheel
    # 特定python库制作成wheel文件
    pip wheel --wheel-dir DIR some-package[==version] # 其中,DIR是保存文件的路径,比如users/lemon/Downloads
    
    # 根据依赖包信息,制作wheel文件
    pip wheel --wheel-dir DIR -r requirements.txt
    

    另外一种方式,直接下载包,然后离线复制到对应环境的包目录下面。

    • windows环境: Anaconda -> Lib -> site-packages
    • Linux环境: anaconda -> lib -> python版本 -> site-packages
    • Mac环境: anaconda -> pkgs

    这样,也可以去相应文件夹下看具体包实现的底层源码了。

    3. annoy的基础使用

    这里主要参考了annoy的GitHub的例子,写下来

    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)
    #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
    

    这个例子其实非常容易懂, annoy的作用就是海量向量中快速的搜索近邻向量,那么首先我们应该先为海量向量构建成高效搜索的索引, 这里采用的就是树的方式。所以前面的7行代码,主要就是在构建索引。 而真正检索的其实是最后一句, 这句话的作用是检索1000个和0位置的向量最相似的1000个向量,这里返回的结果中,会有它本身。

    下面整理关于annoy常用的函数了:

    • 构建索引相关的函数

      • AnnoyIndex(f, metric): 返回一个可读可写的存储f维向量的新索引, 这里的metric可以是"angular", “euclidean”, “manhattan”, “hamming”, or “dot”. 这里的角距离余弦相似度的归一化公式sqrt(2(1-cos(u,v)))
      • a.add_item(i, v): 在i位置(非负整数)添加向量. 这个词典最大是max(i)+1个items, 比如我有10000个item, 词典大小是0-9999位置,每个位置i存储对应item的向量, 通过这个函数,就能把词典给建立起来
      • a.build(n_trees, n_jobs=-1): 建立一棵n_trees的森林,树越多, 精度越高, 在创建之后,就不能添加额外的的项了,n_jobs用于指定建立树的线程数, -1表示用所有额外的cpu核
      • a.save(fn, prefault=False): 将索引保存到磁盘并加载(参见下一个函数)。保存后,不能添加更多的项目。
      • a.load(fn, prefault=False): 从磁盘加载(mmaps)一个索引。如果prefault设置为True,它将预读取整个文件到内存中(使用mmap和MAP POPULATE)。默认是假的。


      上面这几个,是如何用annoy包构建好向量词典,以及如何把向量组织起来(树的方式), 然后保存等。下面这几个,就是如何得到TopK的函数使用。

    • 向量检索时用到的函数

      • a.get_nns_by_item(i, n, search_k=-1, include_distances=False): 返回最接近的n个item。查询过程中,将检查search_k个节点,默认为n_trees* n。serarch_k实现了准确性和速度之间的运行时间权衡。include_distances为True时将返回一个包含两个列表的2元素元组:第二个包含所有相应的距离。
      • a.get_nns_by_vector(v, n, search_k=-1, include_distances=False): 和上面的根据item查询一样,只不过这里时给定一个查询向量v,比如给定一个用户embedding, 返回n个最近邻的item, 一般这样用的时候, 后面的距离会带着,可能作为精排那面的强特
      • a.get_item_vector(i): 返回索引i对应的向量
      • a.get_distance(i, j): 返回item_i和item_j的平方距离
    • 索引属性函数

      • a.get_n_items(): 返回索引中的items个数,即词典大小
      • a.get_n_trees(): 索引树的个数

    两个超参数需要考虑: 树的数量n_trees和搜索过程中检查的节点数量search_k

    • n_trees: 在构建期间提供,影响构建时间和索引大小。值越大,结果越准确,但索引越大。
    • search_k: 在运行时提供,并影响搜索性能。值越大,结果越准确,但返回的时间越长。如果不提供,就是n_trees * n, n是最近邻的个数

    看看我这里的几个例子:

    在这里插入图片描述

    4. YoutubeDNN中的应用

    YoutubeDNN做召回的时候,我们能够根据模型得到用户的embedding和item的embedding, 我们接下来,是拿着用户的embedding, 然后去海量item中,检索最相似的TopK返回回来,作为用户的候选item。 那么假设我们已经有了user_embs和item_embs, 我们如何通过annoy进行快速近邻检索呢?

    我这里写了一个函数:

    def get_youtube_recall_res(user_embs, doc_embs, user_idx_2_rawid, doc_idx_2_rawid, topk):
        """近邻检索,这里用annoy tree"""
        # 把doc_embs构建成索引树
        f = user_embs.shape[1]
        t = AnnoyIndex(f, 'angular')
        for i, v in enumerate(doc_embs):
            t.add_item(i, v)
        t.build(10)
        # 可以保存该索引树 t.save('annoy.ann')
        
        # 每个用户向量, 返回最近的TopK个item
        user_recall_items_dict = collections.defaultdict(dict)
        for i, u in enumerate(user_embs):
            recall_doc_scores = t.get_nns_by_vector(u, topk, include_distances=True)
            # recall_doc_scores是(([doc_idx], [scores])), 这里需要转成原始doc的id
            raw_doc_scores = list(recall_doc_scores)
            raw_doc_scores[0] = [doc_idx_2_rawid[i] for i in raw_doc_scores[0]]
            # 转换成实际用户id
            user_recall_items_dict[user_idx_2_rawid[i]] = dict(zip(*raw_doc_scores))
        
        # 默认是分数从小到大排的序, 这里要从大到小
        user_recall_items_dict = {k: sorted(v.items(), key=lambda x: x[1], reverse=True) for k, v in user_recall_items_dict.items()}
        
        # 保存一份
        pickle.dump(user_recall_doc_dict, open('youtube_u2i_dict.pkl', 'wb'))
        
        return user_recall_items_dict
    

    这里还有额外的两个参数是user_idx_2_rawid, doc_idx_2_rawid, 这两个是字典, 保存的是用户向量所在的位置索引与用户原始id之间的映射,以及item向量所在索引与原始item_id之间的映射, 我们最终的字典里面,应该是要保存用户原始id和item的原始id的,这个函数运行之后,得到的结果就是这个样子:

    在这里插入图片描述
    Ok, 关于annoy的探索先到这里,后面如果再学习到新知识,再进行补充。

    展开全文
  • 快速索引词向量-annoy

    2022-03-16 17:37:36
    annoy 是 高维空间求近似最近邻的框架,速率快,轻便实用。 GitHub地址:https://github.com/spotify/annoy 配合腾讯词向量,可以快速查找语义接近的向量: tencent_annoy_index = AnnoyIndex(self.size,metric='...
  • 通过以随机的时间间隔做一些烦人的事情来慢慢地让 MATLAB 用户发疯。 自定义以部署您自己的个人邪恶品牌。 一些想法: >>发出哔声,哔声%发出声音>> disp('Why?'), 为什么 % 显示废话>> disp... 灵感来自 Annoy-a-tro
  • 安装python版本的annoy

    2021-09-09 19:38:54
    安装python版本的annoy 1.pip安装,失败 使用pip install annoy来安装python版本的annoy,但是报错如下 WARNING: Discarding ...
  • 首先介绍annoy : Annoy是高维空间求近似最近邻的一个开源库。 Annoy构建一棵二叉树,查询时间为O(logn)。 Annoy通过随机挑选两个点,并使用垂直于这个点的等距离超平面将集合划分为两部分。 如图所示,图中灰色...
  • 这篇文章将介绍两个常用的向量最邻近检索工具:Annoy和Faiss。 Annoy Annoy Github 安装 pip install annoy 支持的距离度量 Annoy仅支持树结构的索引类型。 欧式距离euclidean 内积dot 汉明距离hamming 两...
  • annoy向量检索算法

    千次阅读 2019-12-11 11:37:06
    annoy 算法的目标是建立一个数据结构能够在较短的时间内找到任何查询点的最近点,在精度允许的条件下通过牺牲准确率来换取比暴力搜索要快的多的搜索速度。 二:算法流程 1:建立索引 Annoy的目标是建立一个数据...
  • annoy-1.17.0-cp39-cp39-linux_aarch64
  • python安装annoy

    2021-02-21 10:53:25
    python安装annoy debug解决 ERROR: Command errored out with exit status 1: ERROR: annoy-1.17.0-cp39-cp39-win_amd64.whl is not a supported wheel on this platform.
  • 自然语言处理(NLP)-第三方库(工具包):Annoy 【向量最邻近检索工具】 参考资料: 推荐系统的向量检索工具: Annoy & Faiss
  • 快速计算距离Annoy算法 基本原理 高维稀疏数据进行快速相似查找,可以采用learning to hash参考:Minhashing & LSH & Simhash 技术汇总,但高维稠密数据查找则采用annoy 如何从海量文本中快速查找出相似的Top...
  • Annoy 同样通过建立一个二叉树来使得每个点查找时间复杂度是O(log n),和kd树不同的是,annoy没有对k维特征进行切分。 annoy的每一次空间划分,可以看作聚类数为2的KMeans过程。收敛后在产生的两个聚类中心连线之间...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,914
精华内容 765
关键字:

Annoy