目录
1 什么是向量数据库
2向量数据库工作原理
2.1 数据向量化
2.2相似度计算
2.3向量检索
3向量检索算法
3.1 基于树的方法
3.1.1 KDTree
3.1.2 Annoy
3.2基于图的方法
3.2.1 NSW
3.2.2 HNSW
3.3基于量化的方法
3.4基于哈希的方法
3.5基于倒排索引的方法
3.6 向量检索发展趋势
4主要开源向量数据库
4.1 Faiss
4.2 Elasticsearch
4.3 Milvus
4.4 PGVector
5AI与向量数据库
6参考资料
1 什么是向量数据库
向量数据库(Vector Database),也叫矢量数据库,主要用来存储和处理向量数据。
在数学中,向量是有大小和方向的量,可以使用带箭头的线段表示,箭头指向即为向量的方向,线段的长度表示向量的大小。两个向量的距离或者相似性可以通过汉明距离、欧式距离或者余弦距离得到。
图像、文本和音视频这种非结构化数据都可以通过某种变换或者嵌入学习转化为向量数据存储到向量数据库中,从而实现对图像、文本和音视频的相似性搜索和检索。这意味着您可以使用向量数据库根据语义或上下文含义查找最相似或相关的数据,而不是使用基于精确匹配或预定义标准查询数据库的传统方法。
向量数据库的主要特点是高效存储与检索。利用索引技术和向量检索算法能实现高维大数据下的快速响应。向量数据库也是一种数据库,除了要管理向量数据外,还是支持对传统结构化数据的管理。实际使用时,有很多场景会同时对向量字段和结构化字段进行过滤检索,这对向量数据库来说也是一种挑战。
2向量数据库工作原理
严格来说数据向量化本不属于向量数据库,但是数据向量化又是一项很重要的工作,为了流程的完整性暂且放进去。区别与传统数据库主要有以下几个地方不相同:数据向量化,向量检索和相似度计算。
2.1 数据向量化
目前的主流的数据有图像、文本,音频和视频。
图像数据,其实际上是一个二维矩阵,并且矩阵的每一个元素是由R、G、B三通道的值组成的,因此可以直接转化为向量数据。
视频数据可以看成是在图像数据的基础之上增加了一个时间维度,看成一个三维矩阵。
文本数据,文本数据向量化的方法有很多种,有离散的方法也有连续的方法,都有详细的方法论可以参考,可以参考组里其他同事的总结:NLP 中语言表示 (向量化) 的基本原理和历史演变综述(https://blog.csdn.net/u010280923/article/details/130555437)。
音频数据,音频数据可以先转成文本,按照文本的方法进行处理。
2.2相似度计算
向量的距离可以用来衡量两个向量的相似度,常见的有余弦距离,欧式距离和向量内积几种方式。
余弦距离。通过计算两个向量的夹角余弦值来计算相似性。夹角为0时相似度为1,夹角90度时,相似度为0,夹角180时相似度为-1,因此余弦相似度的取值范围为[-1,1]。
欧式距离。欧式距离全称是欧几里得距离,度量的是空间上两个点之间的连线距离,空间上的点都可以看着是从原点出发的向量。
向量内积。又称数量积,是指接受在实数R上的两个向量并返回一个实数值标量的二元运算。两个向量a = [a1, a2,…, an]和b = [b1, b2,…, bn]的点积定义为:a·b=a1b1+a2b2+……+anbn。
2.3向量检索
输入一个向量,从数据库中查找与输入向量最相似的topN个向量返回。实际应用中,还需要保存数据与向量的映射关系,即返回topN个向量及其数据id,根据数据id获取具体的数据。
若不采用任何算法进行索引,需要遍历数据库中所有的数据,计算查询数据与库中数据的相似性,之后按照相似度倒序返回topN条。这种方式一般也称着暴力检索,召回率和准确率都是最高的,但是在数据量大的情况下遍历计算相似度是非常耗时的,需要一些策略算法进行优化,在召回率,内存占用和响应时间之间权衡。
3向量检索算法
向量检索其实可以看着是近似最近邻搜索(Approximate nearest neighbor search),通过预先的索引构建,尽可能减小数据查询时的搜索空间,加快检索速度。目前主要的几种检索算法有:基于树的方法,基于图的方法,基于乘积量化的方法,基于哈希的方法以及基于倒排索引的方法。
3.1 基于树的方法
基于树的方法主要思想是将数据进行划分,类似二叉查找树,搜索时减小了搜索的空间,达到快速检索的目的。但是这种方法不适合高维空间。基于树的方法主要有KDTree(K-Dimensional Tree)和Annoy(Approximate Nearest Neighbors Oh Yeah)。
3.1.1 KDTree
KDTree的构建同样基于递归划分的思想,首先选取方差最大的维度,以该维度上所有数据的中位数作为切分点,将超矩形区域切割成两个子区域,小于中位数的划分到左边,小于中位数的划分到右边,递归的构建左右子树,直到所有的数据被划分完成为止。
下面是一棵构建完成的KDTree示例。
基于KDTree的搜索流程:
1、查询数据p,当前节点的划分维度d,判断数据p在维度d的坐标值与节点值的大小,如果大于走左子节点,小于则走右子节点;
2、直到遍历到叶节点,标记为已访问,计算与叶节点的距离为最近距离;
3、在叶子节点进行回溯操作,每次回溯都判断p与父节点中未被访问到的分支的距离。
a)假如该分支节点与p的距离大于当前最近距离,则继续向上回溯,直至根节点;
b)假如该分支节点与p距离小于当前最近距离,则使用该分支,走步骤1和步骤2直到叶子节点,更新叶子与Q的最近距离。
4、返回子区域的topK个节点。
3.1.2 Annoy
Annoy使用超平面来对空间进行划分,超平面的是借助KMeans(k=2)来寻找两个聚类中心,中心的连线的法平面即为划分的超平面,不对迭代,直到每个子空间的数据个数低于指定阈值。
要搜索这个空间中的任何一点,可以从根开始遍历二叉树。每个中间节点(上面树中的小方块)都定义了一个超平面,因此可以根据超平面来判断接下来继续的是左子节点还是右子节点。搜索一个点可以在对数时间内完成,即树的高度。
3.2基于图的方法
基于图的方法主要是使用图这种数据结构,利用邻居节点之间的连通性构建高速公路,将问题转化为图的遍历,能快速缩小搜索范围,加快检索速度。但是图这种数据结构比较复杂,为了达到极致的检索速度,一般会将整个图加载到内存,造成其内存占用非常高,但是检索快,召回率高。
目前常用的图搜索算法主要是HNSW(Hierarchical Navigating Small World),而NSW(Navigating Small World)则是其前置算法,也需要了解一下。
3.2.1 NSW
NSW在朴素构图的基础上,增加了如下约定:所有的节点必须有相邻的节点;相邻的节点必须有连线;邻近友元的个数作为超参,由用户指定。主要用来解决朴素构图法的一些局限。
NSW构图过程:
插入一个新节点时,先查找与新节点最近的m个节点,连接新节点与这m个节点。
NSW搜索过程:
查询数据q,从图中随机寻找一个entrypoint节点以及其邻居节点,计算entrypoint以及邻居节点与p的距离,如果有邻居与p的距离小于p与entrypoint的距离,设置最小距离点,直到最后找到一个point,其所有邻居和query的距离都大于这个point。
如上图所示,要从构建好的近邻图中搜索和绿色节点query最近的节点,从随机挑选的entry points开始,一开始通过红色的long-link迅速到了query附近的节点,再通过黑色的short-link找到满足的点。
NSW同样也是一种近似计算,不能保证是真实的最近邻。先插入的点冗余边多,更有可能形成高速公路,越往后插入的点,拥有高速公路的可能性越低。
3.2.2 HNSW
HNSW是一种基于NSW的改进方案,主要是利用跳表的思想,使用分层来实现,每层都是一个NSW,即也可以叫做分层的NSW方法。越上层,节点数稀疏,节点之间距离越远,越往下,节点变多,连接也增加,平均距离越近。
HNSW构图方法:
数据插入到最底层,然后随机取一个层数作为该新节点的层数,从底层一次向上每一层都增加该节点,在每一层找到距离最近的k个节点与插入节点建立边,每一层的搜索算法和NSW一样。
对每一个节点,有一个最大边数emax,若建立新边后,旧的节点边超过emax,则删掉距离最大的边。
HNSW搜索方法:
从最上层开始,每一层通过NSW算法找到最近邻节点,下一层的开始搜索节点为上一层的搜索结果。
3.3基于量化的方法
量化方法的出发点是减少计算量,上面的两种方法都是减少搜索空间,但是单个计算量还是很大的。通过量化的方法,损失一定的精度,减少计算量,达到加快检索速度的目的。
量化的方法主要包括SQ( ScalarQuantization )和PQ( Product Quantization )。
SQ是将每一个维度量化成指定位数的一个数,比如将32位的int量化成8位的int,通过损失一定的精度,缩减存储成本。而PQ是将整个向量划分为M段,每一段量化成一个指定位数的数,比如下图将128维(每个维度32位)的向量分成4段,每段量化成一个8位的数,则相当于每段含有256个聚类中心,那么一个128维的向量可以用4维(每个维度8位)的向量表示。
这里以PQ进行说明。
PQ建索引过程,先对每个向量进行切分量化,之后对每一段进行聚类。以一个128维的向量库为例进行说明,先将128维的向量分成4段,每段32维,每段通过聚类量化为8位(256个聚类中心,2的8次方),即一个128维的向量可以量化成4维(每维8位)表示。
检索时,以同样的方法把128维的查询向量分成4段的32维向量,计算每一段向量与当前段聚类中心的距离得到一个4*256的表,将查询向量量化为[m1,m2,m3,m4],然后从前面计算的表中查询计算查询向量与库里面向量的距离d=d1+d2+d3+d4,d1为查询向量第一段子向量与其ID为m1的簇心的距离,d2,d3,d4同理。
3.4基于哈希的方法
基于哈希的方法主要是利用哈希函数的特性,相似度高的数据其哈希值也相似。哈希函数可以将高维数据转换成低维数据,极大提高建设效率。但是哈希函数同样也有另一个局限性,哈希值相似的数据并不一定相似,导致这种方法的召回率并不是很高。基于哈希的方法主要是LSH(Locality-Sensitive Hashing)。
LSH也是一种基于空间划分的方式,假设两个向量相似,那么他们的哈希值也是相似的。利用哈希函数将相似的向量映射到相同的桶里面。查询时先找到与查询向量最相似的桶,再对桶内的数据进行查询计算最相似的topk。
3.5基于倒排索引的方法
倒排索引,也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。
对向量建倒排索引,还是要借助于聚类的方法,通过聚类,将数据划分为m个子空间,然后将数据与聚类中心的映射关系建立倒排索引。搜索时可以先找到与查询向量最近的聚类中心,然后从倒排索引中找到聚类中心包含的所有子空间数据,选择k个最相似的数据。
3.6 向量检索发展趋势
目前来看,向量检索的架构与传统的数据的检索架构大致相同,每种算法都有其优缺点,目前的需求场景也越来越多样性,都在向着超大规模数据量发展,并且对快速响应/内存占用/高召回率都有一定的要求,单一算法无法很好的满足。一般都是多种算法组合应用,发挥各自的有点,扬长避短。例如倒排索引加乘积量化(IVFPQ),能兼顾召回与查询速度。
因为是向量数据,其矩阵运算还可以用到GPU加速,在硬件条件足够的时候,GPU也是另一个加速查询的选项。
4主要开源向量数据库
4.1 Faiss
Faiss是一个用于高效相似性搜索和密集向量聚类的开源库。Faiss是用C++编写的,带有完整的Python/numpy包装器。一些常用算法都有GPU实现。
Faiss提供了很多的索引算法,能构建不同的索引类型,并提供了欧式距离或者点积的相似度计算功能,有些索引类型是简单的基线,例如精确搜索。大多数可用的索引结构需要考虑搜索时间,搜索质量,每个索引向量使用的内存等。
使用范例,建立索引。
import faiss # make faiss availableindex = faiss.IndexFlatL2(d) # build the indexprint(index.is_trained)index.add(xb) # add vectors to the indexprint(index.ntotal)
检索数据。
k = 4 # we want to see 4 nearest neighborsD, I = index.search(xb[:5], k) # sanity checkprint(I)print(D)D, I = index.search(xq, k) # actual searchprint(I[:5]) # neighbors of the 5 first queriesprint(I[-5:]) # neighbors of the 5 last queries
4.2 Elasticsearch
Elasticsearch,是一个支持各种类型数据的分布式搜索和分析引擎。Elasticsearch在7.3版本中,添加了对向量数据索引的支持,支持混合查询,但是向量检索采用的是暴力计算,性能损耗较大。Elasticsearch在8.0版本引入了knnsearch其实就是一种近似最近邻搜索算法,相似度支持欧式距离,点积和余弦相似性,knnsearch底层其实使用的是HNSW。但是这种方式无法进行混合检索。
PUT vector-index-test{"mappings": {"properties": {"my_vector": {"type": "dense_vector","dims": 128,"index": true,"similarity": "dot_product"}}}}
GET my-index-knn/_knn_search{"knn": {"field": "title_vector","query_vector": [0.3, 0.1, 1.2, ...],"k": 10,"num_candidates": 20},"_source": ["name", "date"]}
4.3 Milvus
Milvus(https://github.com/milvus-io/milvus)是一个开源的分布式向量数据库,它具备高可用、高性能、易拓展的特点,用于海量向量数据的实时召回。
Milvus 基于 Faiss、Annoy、HNSW 等向量搜索库构建,核心是解决稠密向量相似度检索的问题。在向量检索库的基础上,Milvus 支持数据分区分片、数据持久化、增量数据摄取、标量向量混合查询、time travel 等功能,同时大幅优化了向量检索的性能,可满足任何向量检索场景的应用需求。
4.4 PGVector
PGVector(https://github.com/pgvector/pgvector)是PG数据库的向量插件,让PG数据库支持向量索引检索的能力。pgvector 的索引算法使用的是基于faiss的ivfflat索引,提供优异的召回率。
# Enable the extension (do this once in each database where you want to use it)CREATE EXTENSION vector;# Create a vector column with 3 dimensionsCREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));# Insert vectorsINSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');# Get the nearest neighbors by L2 distanceSELECT * FROM items ORDER BY embedding '[3,1,2]' LIMIT 5;# Also supports inner product () and cosine distance ()
5AI与向量数据库
近年来,人工智能发展迅速,特别是大语言模型的发展将人工智能又推上了一个新台阶。语义表示(embedding)虽然还做不到像图片数据那样无损表示,但是经过大语言模型的端到端学习,特别是多模型语料学习,使语义表示又更近了一步。这对于向量数据库来说又是一次发展机遇。
基于语义的检索,可以改进传统搜索引擎,语义检索相比于传统的以term为单位的检索,除了能召回包含关键字的数据,同时可以召回与查询句子同义表述的数据。实际应用时可以将两种召回源结果进行排序返回。
更可以用来做语义缓存。目前大模型对话API的调用比较费算力,其实有很多问题都是重复的,可以对用户的对话结果进行语义缓存,不用每次都要输入到模型进行推理,在一些单轮的场景还是比较实用的。
希望在不久的将来能完全跨越语义鸿沟,那时真的就是万物皆可embedding,所有的知识都会存储在向量数据库中,真正的人工智能才会到来。
6参考资料
[1] What is a Vector Database?
[2] Vector Database | Microsoft Learn
[3] 专栏 – 数据库发展史 | TiDB 社区
[4] Faiss | 哈喽哈咯
[5]Nearest neighbors and vector models – part 2 – algorithms and data structures
[6] Find nearest neighbor using KD Tree | kanoki
[7] 十分钟带你入门向量检索技术 – 知乎
[8] Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs