ChatGPT 带火了大模型,也带火了向量数据库。大模型训练成本高,学习新知识的周期太长,而向量数据库刚好可以充当大模型的 “记忆” 模块,找到与新问题相似的旧问题交给大模型处理,极大地扩展了大模型的应用范围,这才有了这一轮向量数据库的火爆。其实在传统的 AI 和机器学习场景中,向量数据库已经有所应用,比如人脸识别、图像搜索、语音识别等。

向量数据库的主要任务是从海量向量数据中寻找与目标向量相似的向量。这在逻辑上没啥难的,就是个 TopN 而已,用传统关系数据库的 SQL 也很容易写出这种查询。但是,高维向量无法定义次序,也就无法事先建立索引,传统数据库中只能硬遍历,计算量非常巨大,性能很差。向量数据库则对这件事做了特别优化,提供了一些高效查找相似向量的方法,大幅度提高性能。

看起来,向量数据库就是一种有特殊用途的数据库,只要把向量数据装进去,就可以实现向量查找了。毕竟我们就是这么使用关系数据库的,把结构化数据装进去,然后就可以用 SQL 查询,通常不用关心 SQL 是如何被数据库解析和优化的。

然而,向量数据库并没有这么简单,使用它实现向量查找的过程是复杂且个性化的。

1、向量数据库通常没有像关系数据库那个简单易用的 SQL 语法,只有基础算法的 API,需要用较基础的程序语言(C/C++,Java, 好一点会有 Python 接口)调用这些 API 自行完成向量查找任务,除 Python 外的这些语言都没有成熟通行的向量数据类型,用来开发向量相关的计算并不方便。即使 Python 有向量数据类型,受限于它有限的大数据处理能力和并行能力,做向量计算也有不少困难。关系数据库就没有这种问题,直接用 SQL 查询数据即可,通常不需要依赖基础程序语言的计算能力。

2、向量计算需要个性化的数据预处理,没有这个过程,后续的运算就会失去意义。比如浮点数向量需要归一化、降维、变换等;二值向量要数据转换、维度排序和选择等。即使是相同格式的数据,预处理方式可能也不同,比如文本向量,可能只需要正常的降维,而图像数据还要进行卷积。这些预处理不仅方法众多,还都有许多要调配的参数,而且经常和具体的数据相关,具有很强的个性化。一般的向量数据库并不提供这类预处理方法,或者即使提供也只有固定的几种方式,无法满足个性化的需求。结果这工作只能依赖于外围的程序语言或再引入第三方技术来完成。相比之下,在关系数据库中使用结构化数据就简单得多,通常不需要预处理,即使有也都很简单,无非就是编码和数据类型转换等,SQL 本身都能做。

3、需要选择创建索引的算法甚至是选择向量数据库。高效查找向量的关键是有适应数据分布特点的索引,但索引算法太多,比如 k-means 聚类、IVF-PQ 算法、Faiss 算法、HNSW 算法、LSH 算法等,仅仅是了解这些方法都需要相当的数学知识,况且通常还要反复调整算法参数(比如 k-means 的参数 k,对数据不了解或者经验不足的开发人员几乎不可能设置出合适的 k)才可能得到查找效率高且精度也较高的聚类算法。还有更坏的消息,向量数据库通常只提供了几种索引算法,而这些方法并不是 “放之四海而皆准” 的,还需要根据数据分布特点而定,如果随意选择了一款向量数据库,在调了很久的算法参数后,发现该向量数据库的索引根本不适合自己的数据(查找效率或者精度无法满足要求),这时再换数据库恐怕会是一场灾难。下图是目前排名靠前的几个向量数据库的索引创建方法:

我们用关系数据库做性能测试时,通常只要随机生成足够数量的数据就可以了,最多也只是考虑字段的数据类型和取值范围。但向量计算并不行,高维向量所处的空间太大,完全随机生成的数据几乎没有任何聚集性,如果用聚类的方法来创建索引,每个类别的数量都不会很多,很难起到减少遍历量的效果,也就无法提高查找效率了。而实际的向量数据(比如指纹数据,人脸数据)通常会在高维空间中的某些位置聚集,这就很容易创建出有效的索引,从而保证查询相似向量的效率和准确率,而这些只有拿到实际数据后才能完成测试。这个现象也可以从侧面说明向量计算的个性化。

1、向量计算和预处理

比如向量的乘积:

A=[[1,2],[2,3],[3,4]]

B=[[1],[2]]

向量的欧式距离:

A1=10.(rand())

A2=10.(rand())

向量归一化后的余弦相似度:

PCA 降维:

A=[[1,2,3,4],[2,3,1,2],[1,1,1,-1],[1,0,-2,-6]]

2、与向量数据类型匹配的程序逻辑

A
1=100.(10.(rand()))
2=10.(rand())
3=A1.(dis(~,A2))
4=A1.@m(dis(~,A2))

SPL 实践:高维二值向量查找》(以下简称《实践》)是我们最近刚用 SPL 做的一个关于相似向量查找的案例。

从本次实践中不难看出高维向量查找的个性化程度非常高,首先是聚类方法的选择上就是个难题,常用的 k-means 算法需要确定参数 k,在本例中,这个参数无法确定;在聚类过程中还有质心移动的过程,而二值向量的质心不容易定义。

为了创建更有效的索引,我们只能为本例数据定制个性化的聚类算法 —— 拆分式聚类和逐步聚类。其次是相似度评估方法的选择上,业界最常用的是余弦相似度,但对于本例中的二值向量,余弦相似度会违背人的直观感受,比如当 1 很多时,如果只有少量维度不同,算出的 cos 相似度会很大;当 0 很多时,还是少量维度不同时,算出的 cos 相似度会小很多。

所以,我们选择用异或距离来评估相似度,这样算出的相似度更符合人的直观感受,随之还带来另一个好处,那就是异或距离的计算量更小。

整个实践过程完美实践了 SPL 编程的便捷性,在基础算法方面,SPL 提供了 bits () 函数,将二值序列转换成按位存储的长整型 (long),正是这一步使得计算变成了按位运算,大幅提高了运算效率(包括创建索引和向量比对过程);用 bit1 () 函数计算两个向量的异或距离,进一步提升了计算效率。

除了这些基础算法外,SPL 还轻松的完成了的两种聚类方法 —— 拆分式聚类和逐步聚类,两种聚类算法的逻辑还比较复杂,SPL 中的核心代码都只用了十几个代码格,如果用 C++ 或者 Java 来完成,恐怕没有几百上千行代码搞不定吧

在高性能方面,本次实践中使用了 SPL 提供的高效计算方法,比如 group@n (),其中的 @n 选项是按序号分组,比普通的哈希分组快了很多。其实 SPL 还有很多其他的高效算法,比如二分法、有序计算等,而且使用起来也很方便,一般只需要增加 @b 或 @o 选项即可。

在并行方面,聚类过程中有不少地方用到了并行,SPL 只要在普通函数中增加 @m 选项就可以实现并行(比如 group@nm ()),充分利用了多核 CPU 的计算资源。而 Python 在这方面是有欠缺的,所以它也没有想象中的那么好用。

虽然实践中没有涉及大数据的内容,但其实即使是大数据运算,SPL 也不在话下,它拥有完善的游标机制,结合高效的存储方案,可以轻松写出高效的向量计算程序。

通过此次实践过程,最直观的感受就是相似向量的查找过程并没有那么简单,通常不是向量数据库提供的那些算法 API 排列组合后能完成的(其实即使是对 API 的排列组合也需要相当的统计学和数学知识,并且经过反复试验后才能实现),这种情况下,使用 SPL 来完成可能是更好的选择。

在部署时,一般的向量数据库通常很重,部署、调试和维护都很复杂,有些还必须在云上使用,但很多场景的数据因为安全原因是不能上云的。而 SPL 很轻,无论是部署、调试和维护都更简单,甚至可以嵌入应用程序中,任何地方都可以拥有向量计算能力。

SPL 目前在聚类算法的完备度上还不够丰富,但随着 SPL 不断补充这些基础算法,SPL 将会比现有的向量数据库更向量数据库。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐