暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

向量数据库|第2期|pgvectorscale

yanzongshuaiDBA 2024-11-04
222

向量数据库|2|pgvectorscale

大家都听说过pgvector,一个PostgreSQL存储和查询向量的扩展,是PG AI生态当之无愧的最受推崇的工具之一。pgvectorPG中添加了vector类型,以及各种搜索操作符和索引,使其拥有vectorsmetadata的完整数据库能力。但他的HNSW索引有两个问题:

1)需要将整个索引都放到内存,否则会变慢。索引成为整个应用的唯一瓶颈

2)基于HNSW索引的search不能以一种合理的方式充分利用预过滤。利用HNSW查询向量首先需要进行索引查询、获取结果,然后才能进行过滤。更为糟糕的是,HNSW仅能从search中返回最大结果集(大多数情况下小于 1,000,否则会变得很慢)。假设你有一个多租户应用,维护数千支teams,每一个有数千个文本(或向量)。针对你的查询请求,HNSW会查询所有vectors,返回一个有限数量的结果集,然后在此结果集上执行过滤。如果最佳结果位于不相干team中时,无法保证为你的team找到任何向量。

pgvectorscaleRust语言开发的)添加了基于磁盘的流索引,可以解决这两个问题。准确的说,它添加了微软DiskANN论文中提到的StreamingDiskANN,一种图结构的专门用于过滤的近邻查询的索引。

pgvectorscale的三大特性:支持DiskANN索引(算法)、支持streaming post-filtering、支持SBQstatistical binary quantization)。

1、DiskANN算法

DiskANN算法和HNSW类似也是一种基于图的查询算法。基于图的算法有一个问题:查找一个距离start position非常远的点非常耗时,因为它需要大量hops

HNSW通过引入一个分层系统(顶层仅有“long-range”边,即高速公路边,帮助快速进入正确的区域,并且有指向低层节点的指针允许以更细粒度的遍历图)。该方法虽然解决了Long-range问题,但通过分层系统引入了更多的indirection,需要更多随机访问,从而需要将该图放到RAM中以获得更好的性能。

与此相反,DiskANN使用单层图,在图构建时通过引用远距离的邻居边来解决long-range问题。单层构建简化了算法,并且在查询时减少了不必要的随机访问,使得SSD更加高效。

相对于HNSW来说,它从一个随机图开始,他的核心是一个名为Vamana的图ANN算法,即构建图的算法为

1)随机初始化一个出度为R的图G:每个节点随机选择R数量的邻居节点并建立连接

2)计算图G的导航点,也就是近似质心

3)以距离全局质心最近的点作为搜索算法起始节点,为每个点重新选择近邻

4)基于步骤1初始化的随机近邻图和步骤3确定的起始点,对每个点做GreedySearch即贪婪搜索,将搜索路径上的所有点都放到近邻集。这里需要跳到1.11.2部分了解如何选择候选邻居集。

5)执行步骤3)和4)两遍,第一遍α=1,第二遍α>=1。

其实是,在随机图基础上,从质心开始遍历两遍重新生成新图。

如何体现Disk?

对于索引构建的问题,DiskANN 在构建索引时通过 k-means 聚类的方式将整个数据集划分成 k 个簇,然后将每个节点分配到最近的 l 个簇中,每个簇分别构建 Vamana 图,这样就可以在内存中进行索引的构建,最后通过简单的边并集将所有不同的图合并为一个图

1.1如何选择近邻?

现有的一些近邻图算法选边策略:如上图给绿色点选择邻居,假设要给它选择两个邻居,从构建一个精确的近邻图角度来看,一定是选择离绿色点最近的两个点(点1和点2)。这样得到的图精确了,但是可能造成邻居集中在一块的现象,不能在各个方向均匀分布。DiskANN论文中提出,一个精确的近邻不并不一定是最合适的ANNS
因此可以通过连接远距离的点来构建近邻图:先将距离绿色点最近的点1选中(加入绿色点的近邻集),然后对点2进行判断:if d(1,2) < d(2,绿点),则不将点2加入绿色点的近邻集。其中d表示两点的距离。依次对3及左下角的其他点进行判断(顺序是离旅店由近到远),最后判断点4d(4,1)>d(4,绿点),所以将4加入绿点的近邻集。
这种方法比较激进,会删除很多长边。

1.2如何调整,再增加一些长边?

 
一种极端情况下,数据集中的点都在一条线上,如上图。如此按照1.1进行选边时,每个点只能和离它最近的两个点相连。这样执行ANNS时,会导致平均跳数很高,如果配合外村执行搜索,可能会导致多次磁盘IOa距离b最近,将a加入b的近邻;接着判断cd(a,c)>d(b,c)c加入b的近邻;直线上的其他点就如同被ac屏蔽掉一样,不和b相连了,因为d(a,其他)<d(b,其他)或者d(c,其他)<d(其他)
Vamana对此进行了调整,增加一个参数α:α*d(a或者c,其他)>d(b,其他),通过调整α值,从而增加一些长边。

2、streaming post-filtering

很多时候,搜索语义相似的条目时,希望额外加上过滤条件进行限制。比如文档通常和一组标签相关联,希望通过请求匹配的标签以及向量相似性来限制搜索。
tow-stage post-filtering的问题:做近似近邻搜索时,选了比如top 5个,但是这几个都没有落在匹配的集合中,那么过滤后,就会返回错误的结果。
很多基于HNSW的索引包括pgvector中的实现,若检索集中没有足够多的条目与条件匹配,那么就可能结果严重失真。
从图中就可以清晰看出,它加了个get_next()函数,会持续进行调用,直到得到正确个数的记录。

3、新的量化算法:statistical binary quantization

许多向量索引使用压缩来减少向量存储所需的空间,并使索引遍历更快,但代价是精度有所损失。常见的算法有乘积量化(PQ)和二进制量化(BQ)。Pgvector0.7.0版本中对HNSW索引添加了BQ
BQ压缩算法以一种非常简单的方式将浮点向量转换成二进制向量:对于向量中的每个元素,如果该值大于0.0,则将二进制值设置成1,否则设置成0.然后距离函数就变成了XOR函数。为什么是异或?我们可以这么理解,二进制向量将空间分成下图的象限,异或函数计算从一个象限到另一个象限需要穿过多少个平面:
每个浮点维度转换为两位(我们后来将其推广)。这个想法是使用平均值和标准差来导出 z 分数(一个值与通过标准偏差归一化的平均值的距离),然后将 z 分数分为三个区域。然后,我们将这三个区域编码为两位,使得相邻区域的 XOR 距离为 1,并且距离随着 z 分数距离的增加而增加。在具有三个区域的两位情况下,编码为 000111
通过实验,我们发现两位编码确实有助于提高 768 维情况下的准确性。因此,默认情况下,我们对任何小于 900 维的数据使用两位编码,否则使用一位编码。在 768 维数据集的一个代表性示例中,当从一位编码切换到两位编码时,召回率从 96.5% 提高到 98.6%,在如此高的召回率水平下这是一个显着的改进。

4、参考

https://www.timescale.com/blog/how-we-made-postgresql-as-fast-as-pinecone-for-vector-data/

https://www.timescale.com/blog/how-we-built-a-content-recommendation-system-with-pgai-and-pgvectorscale/

https://github.com/timescale/pgvectorscale/?ref=timescale.com

https://proceedings.neurips.cc/paper_files/paper/2019/file/09853c7fb1d3f8ee67a61b6bf4a7f8e6-Paper.pdf?ref=timescale.com

https://mp.weixin.qq.com/s?__biz=MzkxOTQwNzU0Ng==&mid=2247486484&idx=1&sn=f254f2adbe4d3d3186fd04fdf3e4de5b&chksm=c1a3d2c1f6d45bd758dac5380ffbff2ad97a8ae7f435b4265feadd3fe0fe09c0a43cfc6ca136#rd

https://blog.csdn.net/whenever5225/article/details/106863674

https://dl.acm.org/doi/pdf/10.5555/3454287.3455520

https://mp.weixin.qq.com/s/ynXT945swVVJDFk8UJ-xIQ


文章转载自yanzongshuaiDBA,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论