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越大查询越快,但是内存消耗更大,构建更久,同时召回率降低。一般有如下设定的经验值:

  1. 小于100w行数据,lists设置为行数/1000
  2. 大于100w行数据,lists设置为行数开平方根
# embedding列指定向量类型和距离函数
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);

除了vector外,还支持halfvecbit类型。
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模型的单精度浮点数外,还支持半精度浮点数,二元向量或者稀疏向量,更多类型还在陆续适配中。

posted @ 2024-11-24 10:11  深度学习机器  阅读(115)  评论(0编辑  收藏  举报