PostgreSQL 数据库向量化的核心:pgvector
pgvector介绍
pgvector是一款开源的向量搜索引擎,除了具备所有Postgres数据库的特性外,最主要的特点是能在Postgres数据库存储和检索向量数据,支持向量的精确检索和模糊检索。向量格式除了传统embedding模型的单精度浮点数外,还支持半精度浮点数,二元向量或者稀疏向量。
安装
Docker
docker pull pgvector/pgvector:pg17
手动编译
cd /tmp
git clone --branch v0。8。0 https://github。com/pgvector/pgvector。git
cd pgvector
make
make install # may need sudo
手动编译过程可能会出错,可以参考官方给出的解决方法
激活
# 每次新建一个数据库都需要执行如下操作激活插件
CREATE EXTENSION vector;
使用方法
# 数据库已经支持vector类型的数据了
CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));
# 添加2条向量数据
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
# 向量检索(基于L2距离度量)
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
距离函数
符号 | 类型 |
---|---|
<-> | L2 |
<#> | 内积 |
<=> | 余弦距离 |
<+> | L1 |
<~> | 汉明距离 |
<%> | Jaccard相似度 |
截止到目前的0.8.0版本,距离度量能够支持上述几种函数
索引
pgvector支持创建两种索引,用于加速向量维度的检索过程。
IVFFlat
IVFFlat算法将向量划分为多个子集,通过在指定数量的子集中进行相似度匹配,从而降低大规模检索的耗时。
需要注意的是,该算法需要在数据集中存在数据才可以进行索引构建,并且后续如果数据集数量出现比较大的变化,或者数据分布改变,则需要重新构建索引。IVFFlat在构建索引阶段会比较耗时,但在检索阶段相比无索引情况下会有比较明显的速度提升。
索引阶段
索引创建阶段只需要指定lists
参数,用于把向量集划分成子集的数量。lists
越大查询越快,但是内存消耗更大,构建更久,同时召回率降低。一般有如下设定的经验值:
- 小于100w行数据,
lists
设置为行数/1000 - 大于100w行数据,
lists
设置为行数开平方根
# embedding列指定向量类型和距离函数
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);
除了vector
外,还支持halfvec
和bit
类型。
● vector
- 最高支持2,000维
● halfvec
- 最高支持4,000维
● bit
- 最高支持64,000维
查询阶段
索引查询阶段需要指定probes
参数,用于指定在多少个子集中进行查询,默认是1,即最相似的子集。设置越大则召回率越高,但耗时更久,一般可以设置为lists
数量的开平方根。
# 查看probes,默认是1
SHOW ivfflat.probes;
# 设置检索子集数量为10
SET ivfflat.probes = 10;
效果实测
在MacbookPro M2 Pro 32g单机上进行测试,取多次结果的范围进行记录
生成10w条1536维向量,使用随机生成的同维度向量进行相似度检索,probes
为1,耗时如下:
构建参数 | 构建耗时 | 检索耗时 |
---|---|---|
无索引 | - | 450ms |
List=10 | 8s | 40~80ms |
List=100 | 23s | 10~60ms |
List=1000 | 5分38s | 8~20ms |
根据结果不难发现,List
参数从10到100检索耗时提升比较明显,但是从10增加到100,检索耗时提升几乎可以忽略,但构建耗时却大大增加,符合前面提到的经验值的范围。
HNSW
HNSW是一个多层的图结构,由一个概率模型来确定每个节点的最高层,越高层节点数量越少,越稀疏,最底层包含所有的数据节点,是一个完整的数据集。与IVFFlat不同,该算法在数据集为空的适合也能创建索引,后续数据集中添加数据则会自动更新图的结构和信息。
索引阶段
索引阶段有2个参数可以设置:
● m
- 每一层中最大相邻节点的数量,默认是16
● ef_construction
- 最大候选子集的数量,默认是64
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);
● vector
- 最高支持2,000维
● halfvec
- 最高支持4,000维
● bit
- 最高支持64,000维
● sparsevec
- 最高支持1,000维
查询阶段
索引查询阶段需要指定ef_search
参数,用于指定在多少个子集中进行查询,默认是40.设置越大则召回率越高,但耗时更久。
# 参考查询参数,默认是40
SHOW hnsw.ef_search;
# 修改参数命令
SET hnsw.ef_search = 100;
效果实测
默认16,64参数的情况下,其他条件与前面一致
构建参数 | 检索耗时 |
---|---|
无索引 | 400~500ms |
m=16, ef_construction=64 | <50ms(默认ef_search=40的情况) |
m=16, ef_construction=64 | 50~100ms(SET hnsw.ef_search = 100之后) |
默认参数情况下,HNSW的检索耗时在50ms以内,而IVFFlat的耗时在10~60ms之间,两者几乎差不多,IVFFlat稍快一些,如果数据集的数量更大,则两者差距可能更加明显。
其他特性
除了前面提到的向量和检索外,pgvector还具备如下特性。
1 向量二次召回的另一种解决方案
向量二次召回,除了使用rerank模型外,还可以使用pgvector提供的子维度查询。即子向量阶段召回较多候选对象,在二次阶段用完整向量进行计算,步骤如下:
创建一个子向量维度的索引
CREATE INDEX ON items USING hnsw ((subvector(embedding, 1, 3)::vector(3)) vector_cosine_ops);
使用该索引进行粗略筛选
CREATE INDEX ON items USING hnsw ((subvector(embedding, 1, 3)::vector(3)) vector_cosine_ops);
使用完整的向量维度进行二次召回,提高召回率
SELECT * FROM (
SELECT * FROM items ORDER BY subvector(embedding, 1, 3)::vector(3) <=> subvector('[1,2,3,4,5]'::vector, 1, 3) LIMIT 20
) ORDER BY embedding <=> '[1,2,3,4,5]' LIMIT 5;
2 迭代索引
这种方法是为了解决近似查询结果不满足数量要求而提出的,可以在不满足数量的情况下再进行迭代,直到满足数量为止,只需要分别设置参数即可。
SET hnsw。max_scan_tuples = 20000;
SET ivfflat。max_probes = 100;
3 其他向量类型
向量格式除了传统embedding模型的单精度浮点数外,还支持半精度浮点数,二元向量或者稀疏向量,更多类型还在陆续适配中。