赞
踩
解决问题:
方案:
更新:这里重新解释一下数据布局的部分。他用的是HNSW算法,图有很多层,具体的图结构参考《HNSW算法》那篇博客。这里只解释一下他是怎么做数据重布局的。原始图结构和重布局后的结构如下图所示,左侧是原始的HNSW,包含2个表,一个表主要存储上层的邻居关系,一个表主要存储原始向量和底层的邻居。
至于为什么原来的图结构不行,原文并没有说清楚。原文说的是这种图结构“在上层中需要额外的计算来索引目标点,并且在0层和上层都会进行非对齐的内存访问。在图遍历的时候会增加外存访问次数”。我个人理解,是因为原始的HNSW图是存储在内存中的,所以没有考虑外存访问开销的问题,数据按照节点为中心进行组织,每个节点占用的内存空间比较大,因此想要访问一系列点的某项信息会导致很多无效数据的读取。例如,我要在顶层检索距离查询向量最近的点,那么我就要从入口点出发依次查询他的邻居。虽然入口点的邻居的index号可以很容易查询到,但是首先根据index读取原始向量需要查询maxM次0层表;其次是计算出最近点后,查询最近点的邻居又需要查询上层表并且偏移到其最后一个元素才能获取邻居的ID,访存次数十分可观。
因此,重布局的核心思想就是解耦,每个表仅存储一种信息,因此一次IO可以读取很多点的相关信息。我认为解耦后。解耦后包含3种表:①首先是把原始数据按照数组的方式顺序存放的一张Raw data table表 ②然后是list table,list table存储的是每个层次中的邻居关系,有多少层就有多少张。因此除0层外,每张表的行数是不一样的,某个点在非0层list table中的位置是未知的,那么如何索引某个点非0层的邻居呢?这就是第③张表的作用,Index table存储了每个点在每个层的邻居信息的指针。如果某个点存在于某一层,那么可以直接通过Index table存储的指针找到list table中的那一行。
不过布局优化后的检索流程,文中并没有描述。根据我的理解,还是刚刚那个例子,当在顶层从入口点出发查找邻居时,仍然需要先在Index table查询到邻居表项的指针,然后访问顶层的list table后根据邻居的index号从Raw data table读取原始数据并计算,然后再从Index table读取最近的一个邻居的邻居表项,看起来并没有减少I/O次数。可能是我理解不到位吧。
但是解耦后是否会产生问题,例如更新的开销是否会急剧增加?(不过一般都没有考虑图更新的问题)其次这个是否还有可以优化的地方,例如每行之间是随机排布的吗,可以按照邻居关系对他进行排列提高读取数据的效率。
Knowledge:
解决问题:
传统的量化方案会导致精度损失,而非量化方案又会导致大量的内存占用,难以在单机上满足需求,CXL的出现使其成为了可能。然而CXL内存池的访问延迟比本地DRAM延迟高很多,如果单纯的将ANN部署在CXL内存池上会出现性能的严重下降(约3-5x,图8)。
方案:
为了提高在CXL扩展内存上执行ANN的性能,CXL-ANN做了以下工作:
Knowledge:
解决问题:
使用近存架构处理ANN时,近内存计算可能内存容量不足,近存储计算会由于不规则的I/O访问导致高I/O开销,读有效率和命中率分别只有39% 和16%。需要结合近内存和近存储的优势以提高性能。
方案:
问题:
解决问题:单个磁盘访问粒度在几十到几百 KB 不等,且不具备访问的局部性,因此不能有效地利用外部存储器件的预读机制和操作系统的缓存机制,同时产生读放大。
方案:针对大规模向量近似搜索场景,采用小内存和大硬盘混合存储的策略。SPANN 基于倒排文件设计,能够有效地将相似的向量以小规模聚类集合的方式连续地存储在磁盘上,通过加载有限个数的聚类集合来减少磁盘访问。
SPANN 采用查询感知的剪枝方法来调整不同查询向量需要加载的候选聚类的数目。通常,在倒排文件的检索过程中,首先会找到相同个数个最近的聚类中心点,然后对固定个聚类进行全量的搜索。然后,由于查询向量具有差异,有的“容易”查询向量只需要检索少数的聚类就能够获得高召回,而有的“难”查询向量需要检索更多的聚类。倒排文件使用相同的查找聚类个数会导致“容易”的查询向量需要检索很多召回收益不高的聚类。SPANN 通过查询向量与聚类中心点的距离远近程度来确定对该聚类的搜索是否是高召回收益的。SPANN 利用查询向量到与其最近的聚类中心点 的距离 作为尺度,使用搜索参数 来控制动态剪枝的程度。当查询向量和某聚类中心点的距离大于 ,则认为是查询向量和中心点距离较远,对这一聚类进行进一步搜索的收益不高,可以进行剪枝,不对其进行搜索。
待补充.....
主要思想就是把DiskANN中的I/O操作从阻塞读变成io_uring,并且在异步读时进行距离计算、更新候选列表等操作。
(本文解读来源于另一篇博客+一些个人补充)
一台只有 64G 内存的个人电脑连十亿原始数据都放不下,更别说建索引了。摆在我们面前的有两个问题:1. 如何用这么小的内存对这么大规模的数据集建索引?2. 如果原始数据内存放不下如何在搜索时计算距离?
本文提出的方法:
(3.1)对于第一个问题,先做全局 kmeans,将数据分成 k 个簇,然后将每个点分到距离最近的 I 个簇中,一般 I 取 2 就够了。对每个簇建基于内存的 Vamana 索引,最后将 k 个 Vamana 索引合并成一个索引,合并的方法就是两个图中相同的点就把边合并。
对于第二个问题,可以使用量化的方法,建索引时用原始向量,查询的时候用压缩向量。因为建索引使用原始向量保证图的质量,搜索的时候使用内存可以 hold 住的压缩向量距离计算。这时的压缩向量虽然有精度损失,但是建图的时候用的是全精度向量,能够把搜索方向带往正确的方向。
具体而言,DiskANN将包含全精度向量的图存在SSD上,PQ压缩后的向量(不包含图)存储在内存中。在检索图的时候,每次都需要去读SSD以获取邻居获取邻居id,然后读到邻居id后在内存中读取PQ向量进行距离计算。其实最开始看到这个方法的时候感觉有点奇怪,为什么不能把图存在内存中,避免每次获取邻居都要读SSD。我推测原因是首先图很大内存可能存不下(以bigann为例,10亿节点*4B(节点id)*50(假设平均50个邻居)=186.3GB);其次是获取邻居只需要一次IO因为一个点的邻居是集中存储的,但是原始向量无法集中存储,这样做的话反而读取向量会带来非常多的IO。
(3.2)关于索引的布局,论文中并没有介绍太多。3.2主要说的就是上面说的SSD存图+全精度向量,内存上存PQ向量。在磁盘上,对于每个点,先存储他的原始向量,然后存储他的后R个邻居的id。如果邻居小于R个那么用0填充。这样的话可以让每个点的占用空间都是一样的便于直接根据偏移量索引。
前面提到了,如果索引数据放在 SSD 上,为了保证搜索时延,尽可能减少磁盘访问次数和减少磁盘读写请求。因此 DiskANN 提出两种优化策略:
(3.3)Beam search: 简单的说就是批量读取。搜索 p 点时,如果 p 的邻居点不在缓存中,需要从磁盘加载 p 点的邻居点。由于一次少量的 SSD 随机访问操作和一次 SSD 单扇区访问操作耗时差不多,所以我们可以一次加载候选列表中W 个最相近的邻居信息,W 不能过大否则会浪费计算和 SSD 带宽让延迟变大。
(3.4)缓存热点:将起点开始 C 跳内的点常驻内存,C 取 3~4 就比较好。
(3.5)重排序:因为存储的时候全精度向量和邻居是存在一起的,所以可以一起读取上来而没有任何额外开销(因为闪存的读粒度通常为4KB,大于全精度向量+邻居的大小)。当读到原始向量后,DiskANN会根据原始向量重新计算该点的距离然后对候选列表进行重排序。
解决问题:精确匹配KNN检索中需要读取大量的向量数据,磁盘方案的I/O开销或者内存方案的内存开销都非常高。
核心思想:
因为是需要精确匹配,所以只能筛掉肯定不能匹配的向量。那么一个是可以把向量聚类,如果这个类别里面最近的向量都离查询点很远,这个聚类就可以不看了;其次是计算距离的时候,逐个特征计算,如果算到前几个特征的距离都已经超过当前的最近距离了,那么后面的特征也就没必要算了。
方案:
1. 聚类级别提前终止:如果一个聚类中,距离查询点最近的点,都超过了当前候选列表中最远的点,那么这整个聚类都没必要再计算了。具体做法是画两个圆,一个是该聚类中心为圆心,最远的点为半径的圆;另一个是查询点为圆心,聚类中心最远的点为半径,然后判断这两个圆是否相交即可。
这个工作还观察到,因为第二个以查询点为圆心的圆的半径是会随着查询的进行越来越短,那么尽早让半径缩短可以有助于筛选掉更多的聚类。因此他们在计算时先进行了一个排序,优先检索距离查询点q最近的聚类,进一步减少计算量。
2. 维度级别提前终止:由于L2距离是随特征数量单调递增的,那么前面几个特征的距离如果已经超过了当前候选列表的最长距离,后面的也不用算了距离只会越算越大。所以具体的做法就是,一个page只存储一批向量的前几个特征,然后读取一个page后可以提前筛选掉一些向量,然后继续读取下一个page。当被筛选光后,后面的page就可以不用读了。
但是这样做有个问题,因为闪存的读取粒度是Page,所以只要这一批向量里面还有一个没有被终止的,其他的向量还是被迫需要继续读取,浪费了读带宽。针对这一问题,本文提出了2个方法。第一个是二次聚类(sub-cluster),在一个聚类内部再聚类一个,离得比较近的当做一批向量一次存进一系列闪存里面,让他们尽量同时被筛选掉;第二个方法是维度重排序。因为维度的顺序其实没有什么意义,那么如果能把差距较大的维度放在前面先算,就能更早的筛选掉。那么怎么找到差距较大的维度,本文提出的一个简单方法是算一个所有向量的每个维度的平均值M,然后计算每个聚类中心中每个维度和M的距离d=ci-M,然后根据d的大小对特征进行排序存储,就可以以更大的概率优先计算到距离较大的特征维度提早筛选。
3. 当闪存通道数和一批特征需要的Page数相同时,每次检索的时候都是从同一个闪存通道开始,极大地降低了并发性,本文将一批特征的第一个Page均匀分布在不同闪存通道中避免这一问题。
Takeaway:这篇文章为了解决DiskANN内存消耗仍然很高的问题,在领接表中存储了每个邻居的PQ向量以避免在内存中存储PQ向量,极大地减少了内存开销并且不会引起过多的I/O次数,能够实现和DiskANN相近的性能。然而存储开销会放大很多倍,本文的方案很难在召回率和存储开销间实现tradeoff。
要解决的问题:
虽然DiskANN支持在内存不足的情况下进行检索,但是他仍然存在以下3个问题:
方法:
为了解决以上问题,AiSAQ的目标是实现极低内存开销的磁盘ANN检索。它将 PQ 向量数据从 DRAM 卸载到存储,并以几乎零内存使用量为目标,并且不引起显著的延迟增加。
至于AiSAQ所使用的方法其实很简单暴力,就是在邻居表中存储每个邻居的PQ向量。因为一个点的邻居列表通常不会很大(假设有30个邻居,邻居的id是uint32类型的,那么邻居表只需要30*4=120B),远小于一个磁盘Block的大小。因此AiSAQ将每个向量的PQ向量也存在邻居表里面(假设PQ向量为32B,那么30个邻居的PQ向量大小为32*30=960B)也不会引起额外的I/O开销。极大地降低了I/O的次数。
因为每次读取邻居表的时候都可以获取到邻居的PQ向量,因此内存中无需存储节点的PQ向量,计算距离后即可驱出内存。但是由于邻居表中只存储的邻居的id和PQ向量,并没有存储该节点的向量,因此需要保存入口点的向量,不过内存开销很低。(这部分存疑,没太看懂)
个人总结:
这篇文章要解决的问题和方案都与AiSAQ很相似(按照AiSAQ的说法,LM-DiskANN是在AiSAQ提交时刚刚发布的),就不细讲了。
Takeaway:DiskANN没有考虑入口点的选择问题,所有查询都从同一个顶点进入,导致路径过长。并且节点布局分散导致了大量的额外IO。针对这个问题这篇文章进行了:(1)入口点选择,对向量进行聚类后选择最近的聚类中心作为入口点开始搜索;(2)图索引的同构映射,传统的DiskANN图是分散布局的没有考虑节点之间的相关性,这篇文章将相邻的节点存在同一个闪存页上减少IO次数;(3)页面搜索,在I/O请求期间利用空闲的 CPU 资源来进一步在已经读取的闪存Page上继续搜索有没有更近的节点。
方案:
个人总结:
如上面所示的疑惑,图的盘上布局是否还有改进空间?不过这篇文章动机明确,方案合理,写作和图表通俗易懂,相信很快就会被顶会录用。
Takeaway:针对DRAM+NVM的混合内存场景下的ANN检索,由于NVM延迟较高因此应该减少访问次数。针对混合内存,HM-ANN认为HNSW的分层结构天然适用于该场景,具体的改进还包括:(1)构建高质量的上层:HM-ANN将HNSW中的底层放在NVM中,其余上层都放在DRAM中。为了尽量减少NVM的访问,因此要在DRAM中就定位到更精确的向量区域。HM-ANN通过选择度数更大的节点到上层和使上层更密集的方式,提高上层节点的定位能力;(2)在搜索阶段的优化包括:①HM-ANN先在L1层进行搜索(L1层的Candidate List长度由efSearchL1参数控制),以进一步精确定位;②HNSW只有一个入口点,HM-ANN使用多个入口点同时搜索以充分利用NVM的带宽;③在搜索L1层时,将 efSearchL1中的那些 candidates 的邻居元素及其在第 1 层的连接从 PMem 复制到 buffer。当第 0 层的搜索发生时,一部分数据已经在 DRAM 中被 prefetched 了,隐藏了访问 NVM的延迟(这部分没太读懂,原文也没写清楚)。
具体细节的话,可以参考这篇解析,写得足够清楚:论文赏析:极致性价比,非易失性内存在向量检索的应用
部分参考自:面向大规模向量搜索引擎的内存-硬盘优化的近似最近邻搜索算法(GRIP)_hnsw 内存消耗-CSDN博客
Takeaway:Grip的目的是构建一个使用DRAM+SSD的ANN检索系统(这篇文章和DiskANN是同时发表的)。在建图阶段,Grip采用了HNSW+IVFPQ,先对向量进行聚类然后在内存中用HNSW在聚类中心上进行建图,以避免线性扫描聚类中心;在检索阶段,Grip采用两阶段方式,现在内存中通过图定位到聚类中心,获取粗略的候选列表,然后分批异步地读SSD以精确地重新排序候选列表。
要解决的问题:
方法:
1. 本文先对IVFPQ的簇大小设置进行了分析。首先是可以通过增加候选列表的长度提高召回率(图1里的hit ratio我认为就是召回率),如图1a所示;其次是探究了簇大小对延迟的影响,如图1b所示。当选择进行搜索的簇越多时簇内检查时间Tc将会变长(这里其实不是很理解这个图为什么是这个趋势,可能是对selected cluster的理解有误);然后分析了聚类数量对召回率的影响,如图2a所示。例如在召回率都会0.6时,NC=1K(设置1000个聚类)相比NC=4K时可以少检查一半的聚类。然而NC=4K时每个聚类里面向量的数量只有=1K时的1/4,=16K时也展现出了类似的结果。因此聚类数量越多可以减少需要检查的向量数。
2. Grip和DiskANN类似,都将PQ向量存在内存中全精度向量存在SSD上。索引阶段分为2步,preview和validation。我理解的就是现在内存中用PQ向量确定候选列表,然后通过读SSD的全精度向量来进行二次验证。架构如图3所示,分为DRAM(被称为预览层)和SSD两个部分。DRAM先将PQ向量(图中成为short code)聚类,然后用中心节点建图,用图路由到最近的聚类中心。如果聚类中心接近查询节点那么这组向量会被一起选择。
3. 为了克服SSD延迟高的问题,Grip引入了3个技术:
详细解析可参考,写得很详细:我的七周七数据库 -- Manu: A Cloud Native Vector Database Management System - 知乎
Takeaway:Manu是一个云原生数据库,主要考虑的是实际环境中的可伸缩、高可用、一致性问题。文章描述的主要是和数据库架构如下图所示,关注的是可伸缩、一致性和高可用相关的内容,没有太多与存储性能相关的内容。
详细解析参考,主要就是照着原文翻译的:Milvus:A Purpose-Built Vector Data Management System - 知乎
Takeaway:Milvus和Manu都是Zilliz的产品,Milvus是初代产品。Milvus是一个比较完善的向量数据库产品,关注的点主要包括数据更新、AVX指令集加速、GPU加速和CPU协同、属性过滤检索和多向量检索、快照隔离等方面。存储优化方面,首先是向量存储优化,Milvus采用了单个向量行存储、多向量间列存储的模式,以加快多向量检索;其次是CPU缓存优化,主要是针对多线程检索的情况优化的,因为他们观察到每个线程都是独立检索整张图,那么每个线程在检索每个向量的时候都要把图读一遍,导致读内存频繁。Milvus的思路大概就是把向量分批,读上来一批向量后提供给所有线程进行检索,然后再集中读下一批向量(这个思路感觉可以扩展到基于SSD的ANN检索上)。
Takeaway:这篇文章其实是针对Graph learning的,虽然和ANN一样都是图,但是差别还是挺大的。这里只描述一下他在图重构方面的思路,可能对ANN图的重构有帮助。GLIST进行图重构的动机是提高节点存储的局部性,降低读的次数。做法是重新编排节点的ID,相邻的节点有相邻的ID,这样的话读到一个点时他的邻居也大概率存在同一个闪存页中。
具体的重编排算法是,他设置了一个阈值T,将度数低于T的节点进行排序(文中说因为度数过高的点一个page也存不下。不过我对这种做法持怀疑态度,因为度数高的点反而应该有更高的局部性,并且取到该点的概率也更高,集中存放的收益并不低),取前C个顶点作为重要顶点,对重要顶点进行采样(也就是取他们的h跳邻居。为了降低开销,采样时随机取一小部分就可以了)。然后根据与其他顶点重叠的大小对重要顶点进行排序,以便在顶点序列中保留与顶点相关的潜在空间局部性。最后,为选定的重要顶点及其相应的 h 跳邻居按顺序分配新的 ID。
Takeaway:面向的是内存、存储都受限的单机下的分段ANN检索。方法1是数据重布局提高局部性,以缩短搜索路径提高一次I/O中有效数据的比例;方法2是在内存中对一小批向量建图,以快速接近目标点;方法3是在读上来的块中找到最近的向量加入搜索中。这篇文章的方法其实没有很多特别之处,基本在之前被人提过了,例如和DiskANN++的点有挺多类似的(不过他们的作者列表有重合,不知道是否可以算作是DiskANN++的正式版?)。不过这篇文章进行了更细致的研究,例如提出了多种图重构算法、多种计算优化策略,并且对很多参数和问题进行了形式化的定义。
背景:
矢量数据库将大规模数据划分为多个段并将它们分布在服务器上。每个服务器可以管理多个段,通过查询协调器并行或串行处理向量查询。通常,每个段在严格的内存和磁盘空间限制内运行,通常拥有小于 2GB 的内存和小于 10GB 的磁盘容量。该文的目标是在这些空间限制内实现高搜索精度和效率。这就需要在 HVSS 的搜索性能和空间成本之间取得微妙的平衡。
至于为什么现有的基于磁盘的ANN不行,这方面的工作主要包括SPANN和DiskANN。SPANN 实现了较高的搜索精度和效率,但代价是大量的存储空间。它可能会将每个向量复制多达八次,从而导致大量的磁盘开销,在管理数千万个向量时远远超过数据段的容量。另一方面,DiskANN遵循图索引算法,以更少的磁盘容量实现了优越的搜索性能。它已作为磁盘上 HVSS 的基线。然而,它的数据布局和搜索策略仍然与基于内存的解决方案的范式保持一致,每个顶点访问都需要磁盘 I/O。基于磁盘的图搜索当驻留在内存中时准确性和效率方面表现出色。然而,一旦放置在磁盘上,由于搜索路径长和数据局部性差,它们会产生大量 I/O。
问题:
本文通过一个实验例子展示了IO开销的问题,上图为BIGANN 上 DiskANN 的搜索时间成本,很明显 TI/O 占 Ttotal 的 92.5%,而 Tcomp 和 Tother 总共占不到 7.5%。这凸显了效率瓶颈,即磁盘驻留图索引上 HVSS 的磁盘 I/O 成本。因此,本文的目标是通过最小化 TI/O 来提高搜索效率。
本文认为基于磁盘的ANN的性能瓶颈在于两方面:顶点利用率和搜索路径的长度。前者表示在给定块中加载有用顶点的程度,而后者表示从入口点到结果点所需的跳数。这两个因素都会影响磁盘 I/O 的数量,从而决定 I/O 时间。顶点利用率越高(i.e. 局部性越强),意味着每次磁盘 I/O 中加载的相关信息越多,从而可以更有效地利用磁盘带宽,并且每次查询所需的磁盘 I/O 更少。根据对 BIGANN 的评估,从磁盘读取的数据高达94%被浪费(如图2a);较长的搜索路径意味着搜索过程中磁盘 I/O 的数量增加,如果入口点固定那么可能离目标点很远需要很多次IO(如图2b)。
方法:
Starling 架构如图2cd所示,Starling①保留了图的拓扑结构,同时优化了数据布局,以提高顶点利用率并减少搜索路径长度;②此外,它采用了旨在减少磁盘 I/O 的块搜索策略。
图布局:1)磁盘布局。Starling 通过根据图拓扑打乱数据块来提高数据局部性(图 2(c) 左)。Starling 努力将顶点及其邻居存储在同一块中,从而使单个磁盘读取不仅可以获取目标顶点,还可以获取其他可能的候选顶点。2)内存布局。 Starling 使用内存中导航图识别基于磁盘的图的查询感知入口点(图 2(c) 右)。这种方法结合了多层图的概念,并涉及从基于磁盘的图中采样一小部分向量来构建导航图。给定一个查询,Starling 首先探索内存中的导航图以获取查询关闭顶点作为入口点。随后从这些识别的点开始基于磁盘的图形搜索。这种方法有效地减少了基于磁盘的图上的搜索路径长度。
为了衡量局部性,本文提出了一个参数OR(G)。OR(G)定义的是整张图的局部性,是每个顶点的局部性OR(u)的平均,OR(u)定义如下。简单来说OR(u)就是顶点u所在的块中,u的邻居点的数量占这个块的总数B(u)的比例。B(u)是点u所在块的顶点,N(u)是u的邻居点。
搜索策略:Starling 使用块搜索策略来利用数据局部性(图 2(d))。与基于顶点探索数据的方法不同,该策略按块处理数据。这样,它就可以受益于优化的数据布局。对于每个加载到内存的块,Starling 计算所有顶点到查询的距离。然后,它选择接近查询的顶点并检查它们的邻居是否有新的搜索候选。该策略通过探索每个块更多相关数据来降低磁盘操作成本,但增加了计算成本。本文通过三个计算优化方法进一步优化性能。
搜索策略中有2个优化点,一个是pipeline,为了将距离计算和块读取并行化,Starling首先对当前加载块中的目标顶点 u 执行距离计算(算法2第 6-7 行)。然后,它立即进行下一个块读取,同时还对包含 u 的块中的其他顶点执行计算(感觉没有写得很清楚?个人感觉是和CXLANN类似,在更新候选列表之前就继续读下一个);另一个是向量压缩。为了减少IO数量,Starling也使用了PQ压缩的方式在内存中驻留PQ向量,用于进行下一跳决策,和DiskANN类似。
总结:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。