article / aiznoyer
从零构建向量数据库
从零构建向量数据库
第 1 章
向量
在数学中,向量通常用黑斜体字母表示,如 等。向量也可以用二维坐标表示,例如二维平面上的向量 可以表示为 ,其中 和 分别表示 在 轴和 轴上的分量。扩展到更高维度也是适用的。
在几何意义上,两个向量的和向量是一个向量的首与另一个向量的尾组成的向量;与当前向量大小相等、方向相反的向量,我们称之为当前向量的负向量,两个向量的差向量可以转化为一个向量与另一个负向量的和。
向量和标量的差异
- 向量具有方向和大小,而标量只有大小;
- 向量可以用坐标表示,而标量通常用普通的数值表示;
- 向量可以进行加、减和数乘等运算,而标量可以进行加、减、乘、除等运算。
向量间的相似度
余弦相似度
余弦相似度(Cosine Similarity)是一种常用的向量相似度度量方法,用于衡量两个向量之间的相似度。它通过计算两个向量夹角的余弦值来评估它们的相似程度。 余弦相似度的公式如下:
其中, 和 是两个向量,它们的夹角 表示向量 和 之间的夹角,cosine_similarity (A, B) 也可以写作 。 表示向量 和 的点积, 和 分别表示向量 和 的模(或范数)。
余弦相似度的取值范围在 之间,其中 表示两个向量完全相同, 表示两个向量完全相反, 表示两个向量正交(无相似度)。余弦相似度的有点事它不受向量大小的影响,只关注向量之间的方向。 因此,当我们需要比较两个向量在方向上的相似程度时,可以选择使用余弦相似度。
内积
内积(Inner Product, IP)是指两个向量在相同方向上的投影长度的乘积。它可以用于衡量两个向量在方向上的相似程度。 当我们需要比较两个特征在某些特征上的绝对数值时可以选择使用内积。 内积的公式如下:
假设 和 是两个向量,它们的内积可以表示为:
在几何上,向量的内积等于它们的模的乘积乘以它们之间夹角的余弦值。即
欧式距离
欧式距离(Euclidean Distance)是指在欧氏空间中,两个点之间的直线距离。它可以直观的表示两点之间的远近程度。两个向量之间的欧氏距离通过计算对应分量差值的平方和,再开方得到。欧氏距离关注的是向量之间的绝对距离,适用于比较两个向量在空间中的相对位置。当我们关注两个向量之间的差异程度时,可以选择使用欧氏距离。 欧式距离的公式如下:
其中, 和 是两个 n 维向量, 和 分别表示向量 和 的第 个分量。欧式距离通常被简称为 L2,其中 L 表示长度,2 就是上述计算公式中的指数 2。
(在我们的论文中,用一个样本的向量召回相似样本的向量时,我们通常会选择使用欧式距离作为相似度度量,因为欧式距离关注的是向量之间的差异程度,而余弦相似度关注的是向量之间的方向相似度。)
为什么需要向量数据库?
向量数据与传统数据的差异
- 维数差异:向量数据通常是高维的,而传统数据(如关系型数据库)通常是低维的。我们无法简单的将传统数据中的每个特征映射到一个维度上,而向量数据可以直接将每个特征表示为一个维度上的数值。我们在设计向量数据库的底层存储结构时可以更好地按快去规划存储空间,从而优化存储效率。
- 字段内相关性差异:向量数据多个维度之间的相关性通常较高,这些维度是神经网络经过学习之后提取出来的,因此往往要对多个字段组合计算才能比较准确地衡量两个向量之间的相似度。向量数据很少单独在某一个维度上作比较,多个维度更多地是一个集合,它们同时行动,彼此相关。而传统数据格式的多个键-值对是完全独立的个体,它们独立代表了数据的一个方面。
- 优化手段差异:向量数据的格式单一,每个维度的数据往往都是固定的数据格式,这可以帮我们找到一些数学上的优化手段,例如使用索引结构来加速向量的检索。
- 使用方法差异:传统的数据格式基于关键词进行精确匹配,不会去理解词语背后的语义。而向量数据的匹配是基于语义理解的,也许两个词看起来差异很大,但其实语义很接近。(“玻璃”和“玻璃心”;“天气不错”和”晴空万里“)
向量数据库的设计
- 结合向量数据格式设计相应的存储、索引和查询组件,面向向量数据库中独特的“向量”部分,把向量数据的管理能力做到极致;
- 重视数据库技术多年积累的通用能力,将这些能力应用到向量数据库中,向量数据库的本质依然是数据库系统;
- 轻装上阵,不急于补充传统数据库的都有能力,在演进过程中逐步为向量数据库增加个功能,小步快跑地应对AI技术的快速发展。
向量数据本质上是一种结构化的数据形式,它由非结构化数据通过向量化技术转化而来,从而成为计算机可以理解的数据。
第 3 章
基础能力
逻辑层次
向量数据库通常包含五个逻辑层次,分别是实例、库、集合、文档和字段。其中实例位于最顶层,字段位于最底层,五者是依次向下包含的关系,共同构建了向量数据库的逻辑结构。
向量数据库
├─ 实例(instance):向量数据库资源管理的载体,实现向量数据库逻辑概念与物理概念之间的映射
│ ├─ 物理资源集合
│ ├─ 连接地址
│ └─ 授权访问信息
├─ 库(database):逻辑上相关的集合组合的容器,一个实例可以容纳多个库,一个库汇集了一系列集合
│ ├─ 逻辑上相关的集合组合
│ ├─ 数据隔离
│ └─ 统一管理
├─ 集合(collection):存储向量数据的逻辑载体,类似于关系型数据库中的表(shardNum 表示数据分片数/replicaNum 表示副本数/indexes 表示索引配置)
│ ├─ 多个字段的组合
│ ├─ 可靠性和可用性参数配置
│ └─ 索引类型定义
├─ 文档(document):相当于关系型数据库中的一行数据
│ ├─ 多个字段的组合
│ ├─ 数据存储的最底层完整单元
│ └─ 数据操作的基本单位
└─ 字段(field):向量数据库中的数据最小单位,文档中的单个数据项,代表了文档的一个属性或特征
├─ 标量字段
│ ├─ 文本
│ ├─ 数值
│ └─ 日期
└─ 向量字段
├─ 向量数据
└─ 索引类型(如扁平、HNSW、IVF)
索引
- 主键索引(Primary Key Index):每个文档都有一个唯一的标识符,称为主键。主键索引用于快速定位和访问特定文档,类似于关系型数据库中的主键索引。
- 向量索引(Vector Index):为了支持向量数据的快速检索,向量数据库通常会为向量字段创建索引。这些索引可以基于不同的算法,如扁平索引、HNSW 索引、IVF 索引等。向量索引的目的是将向量数据映射到一个低维空间,使得在该空间中进行相似度计算变得高效。 主流向量索引方法的重要信息
| 向量索引 | 简洁描述 | 优点 | 缺点 | 适用场景 | 参数配置 |
|---|---|---|---|---|---|
| 扁平索引 | 全量比较所有向量的暴力搜索方法 | 1. 实现简单 2. 无索引构建开销 3. 搜索结果精确 | 1. 搜索速度慢 2. 内存占用大 3. 不适合大规模数据 | 1. 小规模数据集 2. 对搜索精度要求极高的场景 3. 向量维度较低的情况 | 无特殊参数 |
| HNSW | 分层图索引结构,通过构建多层导航图加速搜索 | 1. 搜索速度快 2. 检索精度高 3. 支持动态插入 | 1. 索引构建时间长 2. 内存占用较大 3. 索引构建参数调优复杂 | 1. 大规模数据集 2. 对搜索速度和精度要求都较高的场景 3. 在线服务场景 | 1. M:每层的最大连接数 2. ef_construction:构建时的候选列表大小 3. ef_search:搜索时的候选列表大小 |
| IVF | 倒排文件索引,将向量空间划分为多个聚类中心 | 1. 索引构建速度快 2. 内存占用适中 3. 支持大规模数据 | 1. 搜索精度略低于HNSW 2. 聚类中心数量影响搜索效果 3. 不适合高维向量 | 1. 大规模数据集 2. 对搜索速度要求较高,精度要求适中的场景 3. 批量查询场景 | 1. nlist:聚类中心数量 2. nprobe:查询时访问的聚类中心数量 3. 距离计算方法 |
- 过滤器索引(Filter Index):为了支持基于非向量字段的条件查询,向量数据库通常会为这些字段创建过滤器索引。过滤器索引可以基于哈希索引、B+树索引等数据结构,用于快速定位满足条件的文档。
- 索引重建(Index Rebuild):为了保持索引的效率和准确性,向量数据库通常会定期对索引进行重建。索引重建的过程包括删除旧索引、重新插入文档数据、重新构建索引结构等操作。重建索引的频率可以根据数据集的大小、写入频率和查询需求来调整。
关键指标:
- 访问延迟(latency):指从发送查询请求到返回查询结果的时间延迟。较低的访问延迟可以提高系统的响应速度和用户满意度。
- 实例吞吐量(throughput):指系统在单位时间内能够处理的查询请求数量。较高的实例吞吐量可以支持更大的并发查询需求。
- 召回率(recall):指系统返回的相关文档数量与查询条件下所有相关文档数量的比例。较高的召回率可以确保系统返回的文档与查询需求相符。
高阶能力
动态schema
在MySQL数据库中,所有字段必须事先定义。后续的数据写入过程会对字段的存在和类型进行严格的校验。 向量数据库具备动态schema的能力,即可以在运行时动态添加、删除或修改字段。使用 update/upsert 接口进行数据写入和更新时,我们可以根据业务变化动态的增加或减少字段,从而提高应用使用数据库的灵活性。
别名机制
向量数据是通过预训练的量化模型生成的。同时,所有的数据都需要由同一个向量化模型生成。如果新旧模型生成的向量数据混合在一起使用,将对召回率产生负面影响。 为了解决这个问题,向量数据库引入了别名机制。别名机制允许用户为每个字段指定一个别名,而不是直接使用字段名。通过别名机制,用户可以在不改变字段名的情况下,切换使用不同的向量化模型生成的向量数据。 举个例子:假设客户端当前访问的集合名称为 A,我们可以在后台新建一个集合 B,并使用新的向量化模型生成的向量数据写入 B。写入成功后,可以将集合 B 的别名设置为 A,客户端仍使用 A 来访问数据,但是实际访问的是集合 B。
向量化
为了使用向量数据库,需要开发者自己选择合适的向量化模型对数据进行向量化。为了简化开发者的使用流程,向量数据库通常会提供一些机制,从而使开发者能够直接通过原始数据与向量数据库进行交互。
混合查询
混合查询结合了向量字段和标量字段,配合自定义的标量字段和过滤器条件表达式作为查询条件,实现对向量数据库的综合操作。