HNSW 分布式构建实践

HNSW分布式构建实践

作者:魏子敬

一、背景

随着大模型时代的到来,向量检索领域面临着前所未有的挑战。embedding 的维度和数量空前增长,这在工程上带来了极大的挑战。智能引擎事业部负责阿里巴巴搜推广及 AI 相关工程系统的设计和建设,我们在实际业务迭代与发展中遭遇了 embedding 维度和数量扩张带来的诸多问题,其中索引构建时间问题尤为突出。

图 1 HNSW

以 HNSW[1]算法为代表的近似图算法因其高性价比与高召回率特性,在向量召回领域已逐渐成为主流技术选择。尤其在淘宝天猫、拍立淘、闲鱼等平台的搜索召回场景中,近似图算法扮演了至关重要的角色,其应用范围极为广泛。但是近似图算法被诟病的一个主要问题就是索引构建时间过长,特别是在高维海量数据场景下,这一缺点被进一步放大。

在分布式场景下,基于分而治之的思想,原始数据会被划分成多个正交子集,然后每个节点只负责其中的一个子集,这样就可以用多个独立的计算(存储)节点来解决单机无法解决的海量数据问题。在向量召回场景中也不例外,原始的 embedding 数据集会被按照 pk 哈希被均匀划分到多个分列中,这样通过一次分片就可以极大缩小单个实例内需要处理的向量数量,然后查询时只需要分别在各列的索引中先做召回,最后再把各列的结果汇总就可以了。

那是不是在不考虑成本的情况下,不管有多少数据只要无线分片下去就能解决一切规模膨胀带来的问题?

1.1 分布式的可扩展性困境(Scalability)

在我们的搜索推荐场景,业务上一般只要求最终一致性,这使我们无需支付高昂的成本来支持复杂的事务处理或维护严格一致性。而宽松的一致性要求使得水平扩展更加容易。然而即使如此,分布式环境的不稳定性以及网络开销等因素仍然限制了这种扩展能力,更不用说成本问题。

在向量召回场景中,仅因离线索引构建过慢而扩列是不划算的。以 HNSW 为例,如果撇开其他索引和检索时间,同样是在召回 topk 情境下,不进行分列并将所有数据构建成一个图,在检索计算量上来看是必然最优的。因此一个自然的问题是:如何加速近似图的构建呢?利用现代处理器的多线程并发处理能力是一个显而易见的方向。

1.2 多线程并发构建的瓶颈

实际上在 HNSW 原始的论文[1]中就提到了只要在几个关键节点加上锁就能实现构图的并行,而且图的质量几乎不受影响,这得感谢建图过程中绝大部分时间各个执行流之间都是完全隔离,同步点很少,在主流的向量库中也大都支持了 HNSW 的多线程构建,在十并发以内几乎可以实现接近线性的加速比,但是在我们的实践中也很快发现当并发数量加到三十之后加速比差不多就到了极限,即便增加更多并发,构建时间也难以进一步缩短。而且在线上环境中,由于混布环境资源稳定性的限制,很难确保所有子图构建都能始终处于高速状态。

二、分治构图

这就引出了最终的方案: 分布式构图,和水平分列分片不同,我们这里的分布式构图指的是使用多个实例来构建一张图,即分治构图,通过一些调研工作, 我们也发现业界和学术界其实早就有了一些较为成熟的方案[2][3][4], 这些方案中除了 Pyramid[4]的方案(详见附录)外其他都大同小异,都能抽象成下边三个分治步骤:

1. 按照某种方法拆分原数据集,将原始数据集合 X 拆分成多个 X', X'之间可能会有交集。

2. 分别对每个 X'进行构图 G'。

3. 将所有 G'合并回一个大图 G,这一步通常是简单的边合并,随后可能还会对大 G 进行进一步的 Fine-grained optimization 处理。

第一步拆分可以选择的方案有 k-means、随机拆分、pca 主从分析后的多重随机拆分,如果原始数据集合过大,可能会先有一个随机采样的过程。

第二步通常都是正常的子图构建。

第三步都会有边合并,合并后的图可能会需要进一步的处理,如简单邻居传播或者 nn-desent[5]。如果在第一步拆分的过程中各个集合没有交集,在第三部合图时还需要加入一些胶水节点来保证图的连通性。

图 2 两种合并图的方式

2.1 分布式 HNSW

由于近似图算法的理论基础不足,工程上通常很难提前预估方案的有效性, 经过大量的线下实验验证后,结合工程实现上的考量,我们最终采用了 DISKANN[2]的分布式构图方案:第一步首先从原始数据集中采样一些 doc 做 k-means 聚集出 k 个聚心,然后把每篇 doc 分配到距离它最近的α个聚心上,这样每个聚心都得到了一个集合,每个聚心对应的集合分别构建 HNSW,最后在 reduce 过程直接对相同点做边合并,对于合并后度数超限的点也会进行 prune,利用 k 个集合之间的相同点来合并原图。

图 3 分治构图

图 4 分治构图

这种方案的优势就是改动小,舍弃了合图后的 Fine-grained optimization 过程,工程实践上简单,缺点就是虽然大部分计算都被分散到了子节点中,但总计算量其实比较大,最终参与计算的向量数量会膨胀α - 1 倍。但是在实践中子图的构图 ef 参数可以适当调小,用来抵消膨胀的计算量。我们在多个公开数据集以及部分线上生产数据集中实验后论证了这种分治构图法对图的质量影响并不大,大部分情况下甚至有正收益。

剩下的问题就是如何在 BuildService[6]中支持上述的三部构图流。BuildService 是智能引擎团队推出的分布式索引构建系统,在阿里搜推场景有着广泛的应用。幸运的是,凭借 BuildService 强大的分布式 DAG 图执行能力,HNSW 的分布式构建流程图可以非常简单的使用 BuildService 的索引定制框架来表达。

目前分布式构图功能已经在拍立淘、闲鱼等多个场景上线,离线全量时间及稳定性都得到极大的改善,平均全量时间由原来的 7~10h 降低至平均 3h 左右。

三、其他相关工作

3.1 Filtered HNSW

过滤召回或者带类目(标签)召回也是向量召回的一个很重要的应用场景,因为 embedding 本身很难完全表征完整的类目信息,所以针对一些特殊场景需要将向量召回和普通的类目过滤相互结合。

针对这种问题平凡的解决方案(查询时通过 filter 过滤)只在查询阶段对过滤条件进行了处理,并没有关注索引构建环节,所以并没有很好的利用到问题的特性,而且最坏情况下可能需要把全图遍历完才能凑够需要召回的向量个数。

另一个解决方案是为每个离散类目单独构建向量索引,但是当类目数量过多或者每个向量关联到多个类目时这个方案的存储成本就会变得难以接受。

在这方面 DISKANN[7]的另一篇文章提出了针对离散类目(标签)场景的两种带类目元信息的向量索引构建方案: FilteredVamana 和 StitchedVamana , 一种是基于流式处理的算法,另一种是基于批量构建的算法。这两种算法的核心都是在索引构建时不仅要考虑向量距离度量,还要考虑到相关的类目标签。

我们参考 StitchedVamana 批量构建的方法利用上文提到的分布式 HNSW 实现了 Stitched HNSW 索引: 给每个类目都构建一个子图,这样因为每个点有多个标签,所以每个点可能会属于多个子图,最后利用子图间的点重叠做图归并。

合并的过程中,如果出现度数超过限制的情况就进行三角裁边,这里的三角裁边使用了论文中的带类目三角裁边 FilteredRobustPrune,在最终的合并图上会给每个类目都保留一个导航点,这样查询时每种类目都有自己的导航点,包含相同类目的点在一个连通块内,查询过程中也只会访问包含这些类目的点,因此可以解决后置方案过滤中计算放大的问题,同时因为最终还是合并到一个图上,可以解决为每个类目单独建索引带来的索引膨胀问题。我们在拍立淘业务上线此功能后,大幅提升了带类目查询的向量召回性能。

四、未来工作

以 HNSW 算法为代表的各种近似图算法虽然近年来在各个业务场景都大放光彩,但是其理论基础不足[8]、缺少 error bound[8][9]、构建效率过低、不利于并行查询[10]等问题也逐渐被暴露出来。未来我们也会进一步发力,为复杂数据应用场景中的向量检索问题提供更高效更精确的解决方案。

五、附录

Pyramid[4]的方案比较特殊,严格说它是一个多级索引的架构,最终产出的不是一张图,在我们进行简单的实验后发现综合查询效果并不理想,首先放弃了这个方案。

图 5 Pyramid 的架构

参考文献

[01] Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs

https://arxiv.org/pdf/1603.09320

[02] DiskANN: Fast Accurate Billion-point Nearest Neighbor Search on a Single Node

https://suhasjs.github.io/files/diskann_neurips19.pdf

[03] Scalable k-NN graph construction for visual descriptors

https://pages.ucsd.edu/~ztu/publication/cvpr12_knnG.pdf

[04] Pyramid: A General Framework for Distributed Similarity Search

https://arxiv.org/pdf/1906.10602

[05] Efficient k-nearest neighbor graph construction for generic similarity measures

https://dl.acm.org/doi/abs/10.1145/1963405.1963487

[06] 阿里巴巴内部广泛使用的大规模分布式检索系统 Havenask 的索引构建服务

https://mp.weixin.qq.com/s/uRdk5voz2mmSge1babLC3A

[07] Filtered − DiskANN: Graph Algorithms for Approximate Nearest Neighbor Search with Filters

https://harsha-simhadri.org/pubs/Filtered-DiskANN23.pdf

[08] Worst-case Performance of Popular Approximate Nearest Neighbor Search Implementations: Guarantees and Limitations

https://arxiv.org/pdf/2310.19126

[09] Graph based Nearest Neighbor Search: Promises and Failures

https://export.arxiv.org/pdf/1904.02077

[10] Speed-ANN: Low-Latency and High-Accuracy Nearest Neighbor Search via Intra-Query Parallelism

https://arxiv.org/pdf/2201.13007

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/934844.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows 11 12 月补丁星期二修复了 72 个漏洞和一个零日漏洞

微软于 2024 年 12 月为 Windows 11 发布的补丁星期二修复了其产品生态系统中的 72 个漏洞,包括 Windows 通用日志文件系统驱动程序中一个被积极利用的零日漏洞。 这个严重漏洞可以通过基于堆的缓冲区溢出授予攻击者系统权限,使其成为此版本中优先级最高…

从模型到视图:如何用 .NET Core MVC 构建完整 Web 应用

MVC模式自出现以来便成为了 Web 开发的基石,它通过将数据、业务逻辑与用户界面分离,使得应用更加清晰易于维护,然而随着前端技术的飞速发展和框架如 React、Vue、Angular 等的崛起,许多开发者开始倾向于前后端分离的方式&#xff…

深度学习详解

深度学习(Deep Learning,DL)是机器学习(Machine Learning,ML)中的一个子领域,利用多层次(深层)神经网络来自动从数据中提取特征和规律,模仿人脑的神经系统来进…

nmap详解

Nmap(Network Mapper)是一个开放源代码的网络探测和安全审核的工具。由于它的功能强大,被广泛应用于网络安全领域。以下是Nmap的一些主要功能及其在实战中的应用举例。 Nmap的主要功能: 端口扫描:检测目标主机上开放…

Tina Linux如何enable开机LOGO

想要在Tina Linux开机的时候显示开机LOGO,我们需要进行如下几步工作: 在Uboot设备树中添加对应的屏幕设备树节点.修改Uboot配置,让其在开机时自动加载LOGO到屏幕上.在boot-resource分区中添加对应的启动LOGO图片,并命名为bootlog…

SpringBoot常见的注解

Spring Boot 常见注解详解 在 Spring Boot 开发中,注解是简化开发过程和提高效率的核心工具。通过使用各种注解,我们可以实现依赖注入、配置管理、Web 开发、数据访问等功能。以下是一些常见的 Spring Boot 注解,并对每个注解的作用进行了详…

使用 Python 爬取某网站简历模板(bs4/lxml+协程)

使用 Python 爬取站长素材简历模板 简介 在本教程中,我们将学习如何使用 Python 来爬取站长素材网站上的简历模板。我们将使用requests和BeautifulSoup库来发送 HTTP 请求和解析 HTML 页面。本教程将分为两个部分:第一部分是使用BeautifulSoup的方法&am…

【QGIS入门实战精品教程】4.12:QGIS创建标识码(BSM)字段并生成标识码

文章目录 一、加载实验数据二、生成BSM三、说明一、加载实验数据 加载配套实验数据包(订阅专栏后,从私信查收)中的4.12.rar中的自然幢数据,如下图所示: 打开属性表,查看属性表中的BSM效果: BSM字段说明:字符串,10位长度,以1开头,从1开始的连续序号结尾,总长度为10…

【GL009】C/C++总结(一)

自查目录 1. typedef 和 #define 的区别 2. const 、volatile 和 static 的区别 3. const修饰指针 4. 数组指针和指针数组 5. 函数指针和指针函数 6. C/C内存管理 6.1 内存分布图解 6.2 C语言中的内存分配方式 6.3 堆(Heap)和栈(Sta…

Synchronizad优化原理(JUC)

目录 java对象头一:Monitor二:sychronized的优化轻量级锁(轻量级锁)锁膨胀(重量级锁)(重量级锁)锁自旋偏向锁(比轻量级锁更轻量)偏向锁状态如何撤销偏向锁批量…

Android显示系统(08)- OpenGL ES - 图片拉伸

Android显示系统(02)- OpenGL ES - 概述 Android显示系统(03)- OpenGL ES - GLSurfaceView的使用 Android显示系统(04)- OpenGL ES - Shader绘制三角形 Android显示系统(05)- OpenGL…

Vscode 构建 uniapp vue3 + ts 微信小程序项目

前言 为什么要使用 Vscode 来开发构建 uniapp 项目?从个人角度来讲,仅是想要 Vscode 丰富的插件生态,以及最重要的优秀的 TtypeScript 类型检查支持,因为本人是 TS 重度使用者。 如果你更习惯使用 js 进行开发,使用 …

[游戏开发] Unity中使用FlatBuffer

什么是FlatBuffer 为什么用FloatBuffer,优势在哪? 下图是常规使用的各种数据存储类型的性能对比。 对序列化数据的访问不需要打包和拆包——它将序列化数据存储在缓存中,这些数据既可以存储在文件中,又可以通过网络原样传输&…

软件工程 概述

软件 不仅仅是一个程序代码。程序是一个可执行的代码,它提供了一些计算的目的。 软件被认为是集合可执行的程序代码,相关库和文档的软件。当满足一个特定的要求,就被称为软件产品。 工程 是所有有关开发的产品,使用良好定义的&…

负载均衡策略:L(P)策略;L(Max) ;L(LDS)

负载均衡策略:L(P)策略;L(Max) ;L(LDS) 1. Proportion load distribution L(P)策略; 策略含义:服务器不配置为可变服务率,调度器按照服务器服务率的倒数比例分配负载。即每个服务器分配到的任务量与该服务器服务率的倒数成正比 2. (L(Max)) load distribution((L…

探店小程序:解锁商业新生态,定制未来

在数字化浪潮席卷全球的今天,商业的边界正在被重新定义。随着移动互联网技术的飞速发展,探店小程序作为一种新兴的商业模式,正以其独特的优势迅速成为连接商家与消费者的桥梁。我们刚刚为一家客户成功交付了一款集分销、分润、商业模式定制开…

从EXCEL表格到WEB TABLE的实践

前言 EXCEL管理数据 Bootstrap Bootstrap 是一个流行的开源前端框架,它由 Twitter 的员工开发,用于快速开发响应式和移动设备优先的网页和应用程序。 jQuery jQuery 是一个快速、小巧且功能丰富的 JavaScript 库。它简化了 HTML 文档的遍历、事件处理…

HarmonyOS(65) ArkUI FrameNode详解

Node 1、Node简介2、FrameNode2.1、创建和删除节点2.2、对FrameNode的增删改2.3、 FramNode的查询功能3、demo源码4、总结5、参考资料1、Node简介 在HarmonyOS(63) ArkUI 自定义占位组件NodeContainer介绍了自定义节点复用的原理(阅读本本篇博文之前,建议先读读这个),在No…

独家首发 | 基于 KAN、KAN卷积的轴承故障诊断模型

往期精彩内容: Python-凯斯西储大学(CWRU)轴承数据解读与分类处理 基于FFT CNN - BiGRU-Attention 时域、频域特征注意力融合的轴承故障识别模型-CSDN博客 基于FFT CNN - Transformer 时域、频域特征融合的轴承故障识别模型-CSDN博客 P…

【总结·反思·汇报·思考02】裸辞后,我的一些感想和感悟。

Hello,大家好! 首先,我需要向大家道个歉,对不起!因为最近发生了一些事情,博客文章一直没有更新。(90度鞠躬道歉) 那么,最近到底发生了什么呢?相信大家已经从…