草庐IT

2015TPAMI(IMI多维倒排索引)-The Inverted Multi-Index

Caucher 2023-03-28 原文

2012CVPR是本论文的会议版本。
本文是乘积量化技术(PQ) 最典型的索引方式。

1 INTRODUCTION

  • 乘积量化技术在查询时,需要找到query对应Voroni cell或者和周边cell的点,如果数据量比较大,Cell也比较大的话,那么返回的点就会很多,需要花在Refine上的时间也会更多。因此一个迫切的要求是设计更为细粒度的分区,即vorooni cell面积更小。

  • 一个最直接的方式是把codewords的个数提升一些,但是这同时意味着索引构建时间(学习时间)也更长。

  • 一些索引方法也可以引入进来,比如kd-tree,tree codebooks等,但是经常会降低查询准确性。

  • 本文提出的方法:多维倒排索引,在很多方面和倒排索引很相似,其优势是在不增加查询和构建时间的基础上,相比于倒排索引,能产生更细粒度的子分割(在中等大小的codebooks上)。内存消耗的增长也非常微小。

  • 多维倒排索引尤其适用于大数据集上。

3 THE INVERTED MULTI-INDEX

3.1 The structure of the inverted multi-index.

  • 乘积量化的核心思想就是先分段,每段分别聚类量化。
  • 问题出在查询上。简单点说,现在假设就分两段,每段K个codewords,那么在PQ空间上,一共有个聚类中心。
  • 标准的倒排索引就是把这个空间划分成个Voroni cell,查询时query在哪个cell就定位即可。如下左图(两维数据),但是会产生一些边界问题,也容易产生倾斜问题。
image.png
  • 本文的思想就是不用voroni cell,用grid,来切割PQ空间。每个轴上各格子中心点,其实就是那一组codewords聚类的中心。
  • 形式化上的来讲,空间上的每一个点,都找每一段(每个轴/维度)上找和它最近的那个聚类中心(codeword),各个维拼起来,就找到了所在的格子。


    image.png

3.2 Querying the multi-index.

  • 查询的时候,第一阶段分段query,分别找每一段落在轴的哪个位置上,和轴其他格子刻度的距离是多少;第二阶段是将各个轴的距离组合起来,找到最合适的若干格子用于查询。
    • 注意,这里需要各段的距离可以线性叠加,或至少有公式可以计算,比如欧氏距离的平方就可以直接相加表示拼接起来的距离。


      image.png

3.3 Inverted index vs. inverted multi-index.

  • 这里的标准倒排索引,指的是不分段,直接K-means,找出K个聚类中心,用Voroni cell划分空间,共K个cell,做倒排索引的情况。
    • 划分较粗;
    • 列表长度基本均衡;
    • 查询时,K次M维距离计算;
  • 多维倒排索引,就是分成2段,每段K-means找K个聚类中心,用gird划分空间,共个cell,做成倒排索引。
    • 划分较细;
    • 列表长度不均衡,甚至有很多空列表;
    • 查询时,每个维度K次计算,每次计算M/2向量距离,总共还是KM次计算。额外的计算量是给cell排序选TopK的计算量,但作者说这个量不大,应该确实如此。
    • 内存负载额外会承担一些列表元信息,因为列表更多了,但是这个信息是很小的。数据或者数据指针二者的内存消耗都是一样的,因为数据总数是一致的。codebooks大小也是一致的。

【编者注:考虑用Voroni cell做空间划分,然后用倒排索引做索引结构的情况:此时codeboos的大小也是一样的,但是在查询时,需要额外增加次计算算出和各个聚类中心的距离,计算到每个聚类中心的距离只需要查表就可以了。内存负载和多维倒排索引是一样的。列表长度相对也会更为均衡。】

  • 【论文中后面补充了一段为什么选择分两段是最优选择,不过编者没有理解,希望有理解的朋友给予指教。】

4 APPROXIMATE NEAREST NEIGHBOR SEARCH WITH INVERTED MULTI-INDEX

通过上述搜索方法得到的candidate vectors,被赋予量化后的向量和量化后的残存向量,进一步进行rerank。
本文将rerank过程进一步加速。
【编者注:有空更新】

5 EXPERIMENTS

5.1 Indexing Performance

Recall定义为1-NN准确找到的概率。

5.1.1 Candidate List Quality

T是检索的对象数。个人觉得和非PQ系列的方法对比意义有限。


image.png

5.1.2 Retrieval Speed

  • 首先是红色实线和蓝色实线,之间的差距就是multi-sequence算法,可见算法复杂度比较稳定,但仍有一定影响。
  • 红色线在列表长度较长时,查询时间开始线性增长。
  • 作者说,标准倒排索引的速度快,很可能来自向量化实现。


    image.png

5.1.3 Multi-Index + PCA

引入PCA降维之后,查询速度肯定会变快,文中没有实验。问题在于精确率会不会下降。
作者做了两种pca,一种native是直接对原始数据PCA,128维降成32维;另一种是分PQ-aware是分两段分别PCA再拼起来。
可以看到PQ-aware-PCA对准确率影响很小。


image.png

5.2 Approximate Nearest Neighbor Search

有关2015TPAMI(IMI多维倒排索引)-The Inverted Multi-Index的更多相关文章

  1. ruby-on-rails - 协会的 Rails 索引 - 2

    我发现自己需要这个。假设cart是一个包含用户列表的模型。defindex_of_itemcart.users.each_with_indexdo|u,i|ifu==current_userreturniendend获取此类关联索引的更简单方法是什么? 最佳答案 indexArray上的方法与您的index_of_item方法相同,例如cart.users.index(current_user)返回数组中第一个对象的索引==给obj。如果未找到匹配项,则返回nil。 关于ruby-on-

  2. ruby - Rails -- :id attribute? 所需的数据库索引 - 2

    因此,当我遵循MichaelHartl的RubyonRails教程时,我注意到在用户表中,我们为:email属性添加了一个唯一索引,以提高find的效率方法,因此它不会逐行搜索。到目前为止,我们一直在根据情况使用find_by_email和find_by_id进行搜索。然而,我们从未为:id属性设置索引。:id是否自动索引,因为它在默认情况下是唯一的并且本质上是顺序的?或者情况并非如此,我应该为:id搜索添加索引吗? 最佳答案 大多数数据库(包括sqlite,这是RoR中的默认数据库)会自动索引主键,对于RailsMigration

  3. ruby - 如何在 Ruby 中获取多维哈希中的键? - 2

    因此,对于普通哈希,您可以使用它来获取key:hash.keys如何获取如下所示的多维哈希的第二维键:{""=>{"first_name"=>"test","last_name"=>"test_l","username"=>"test_user","title"=>"SalesManager","office"=>"test","email"=>"test@test.com"}}每个项目都是唯一的。所以我想从上面得到的键是:first_name,last_name,username,title,officeandemail 最佳答案

  4. Ruby#index 方法 VS 二进制搜索 - 2

    给定一个元素和一个数组,Ruby#index方法返回元素在数组中的位置。我使用二进制搜索实现了我自己的索引方法,期望我的方法会优于内置方法。令我惊讶的是,内置的在实验中的运行速度大约是我的三倍。有Rubyist知道原因吗? 最佳答案 内置#indexisnotabinarysearch,这只是一个简单的迭代搜索。但是,它是用C而不是Ruby实现的,因此自然可以快几个数量级。 关于Ruby#index方法VS二进制搜索,我们在StackOverflow上找到一个类似的问题:

  5. ruby - 引用具有指定索引的枚举器值 - 2

    假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum

  6. ruby - Java 8 相当于 ruby​​ each_with_index - 2

    我想知道,是否有一些流操作可以像ruby​​中的each_with_index那样做。其中each_with_index遍历值以及值的索引。 最佳答案 没有专门用于该目的的流操作。但您可以通过多种方式模仿该功能。索引变量:以下方法适用于顺序流。int[]index={0};stream.forEach(item->System.out.printf("%s%d\n",item,index[0]++));外部迭代:以下方法适用于并行流,只要原始集合支持随机访问。Listtokens=...;IntStream.range(0,toke

  7. ruby - 在 Ruby 中动态生成多维数组 - 2

    我正在尝试动态构建一个多维数组。我想要的基本上是这样的(为简单起见写出来):b=0test=[[]]test[b]这给了我错误:NoMethodError:undefinedmethod`test=[[],[],[]]而且它工作正常,但在我的实际使用中,我不会事先知道需要多少个数组。有一个更好的方法吗?谢谢 最佳答案 不需要像您正在使用的索引变量。只需将每个数组附加到您的test数组:irb>test=[]=>[]irb>test[["a","b","c"]]irb>test[["a","b","c"],["d","e","f"]]

  8. ruby - 将 Logstash 中的时间戳时区转换为输出索引名称 - 2

    在我的场景中,Logstash收到的系统日志行的“时间戳”是UTC,我们在Elasticsearch输出中使用事件“时间戳”:output{elasticsearch{embedded=>falsehost=>localhostport=>9200protocol=>httpcluster=>'elasticsearch'index=>"syslog-%{+YYYY.MM.dd}"}}我的问题是,在UTC午夜,Logstash在外时区(GMT-4=>America/Montreal)结束前将日志发送到不同的索引,并且索引在20小时(晚上8点)之后没有日志,因为“时间戳”是UTC。我们已

  9. ruby - 从特定索引开始迭代数组 - 2

    我想从特定索引开始遍历数组。我该怎么做?myj.eachdo|temp|...end 最佳答案 执行以下操作:your_array[your_index..-1].eachdo|temp|###end 关于ruby-从特定索引开始迭代数组,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/44151758/

  10. ruby - Array of Arrays,根据索引处的数组内容删除一个索引? - 2

    我一直在努力学习如何处理由数组组成的数组。假设我有这个数组:my_array=[['ORANGE',1],['APPLE',2],['PEACH',3]我将如何找到包含'apple'的my_array索引并删除该索引(删除子数组['APPLE',2]因为'apple'包含在该索引的数组中)?谢谢-我非常感谢这里的帮助。 最佳答案 您可以使用Array.select过滤掉项目:>>a=[['ORANGE',1],['APPLE',2],['PEACH',3]]=>[["ORANGE",1],["APPLE",2],["PEACH",3

随机推荐