现在比较流行的几个向量检索库的应当是这么几个:Faiss, Milvus, Proxima:
Faiss有两种索引构建模式,一种是全量构建,二是增量的索引构建,也就是在原来的基础上添加向量。
第一次构建索引时需要经过Train和Add两个操作,后续添加新embedding就直接执行Add就是增量构建了。
构建索引时,faiss提供了两种基础索引类型,indexFlatL2(欧式距离) 、 indexFlatIP(内积), 也可以通过这两种类型,简单转换一下,弄一个余弦(Cosine)的索引方式出来。
# 以精确搜索时的IndexFlatL2索引类型为例
import faiss
import numpy as np
dimension = 128
nums_user = 64
user_embedding = np.random.random((nums_user, dimension)).astype('float32')
print(user_embedding.shape)
# 索引构建,以L2相似度计算方式为例
if index_type == "L2":
cpu_index = faiss.IndexFlatL2(dimension) # 构建索引index
gpu_index = faiss.index_cpu_to_all_gpus(cpu_index)
if index_type == "dot":
cpu_index = faiss.IndexFlatIP(dimension) # 构建索引index
gpu_index = faiss.index_cpu_to_all_gpus(cpu_index)
if index_type == "cosine":
# cosine = normalize & dot
faiss.normalize_L2(user_embedding)
cpu_index = faiss.IndexFlatIP(dimension) # 构建索引index
gpu_index = faiss.index_cpu_to_all_gpus(cpu_index)
print(gpu_index.is_trained) # False时需要train
gpu_index.add(user_embedding) # 添加数据
# 索引search
k = 10 # 返回结果个数
query = user_embedding[:5] # 查询本身
dis, ind = gpu_index.search(query, k)
print(dis) # 升序返回每个查询向量的距离
print(ind) # 升序返回每个查询向量的k个相似结果
上面简单说了下精确搜索时的IndexFlatL2索引类型为例的Faiss索引的构建及search,Faiss下是有支持多种索引类型的,不同类型的索引也是原理不同,同时速度不同,精度也会有一些差距。这里也是现在市面上如Faiss, Milvus, Proxima不同索引工具库的主要核心优化与差距点了。
一般日常应用情况下,这以上的工具库基本都能满足基本的需求了,不必太纠结于此。
三种索引方式:
- faiss.IndexFlatL2、indexFlatIP: L2欧氏距离以及点积的精确索引,也是说的暴力方式。
- faiss.IndexIVFFlat: 先进行聚类的倒排索引,支持欧式距离和向量内积两种距离算法
- IndexIVFPQ: 加聚类、加量化的倒排索引
L2距离以及点积模式下的索引,是精确索引,也就是上面例子中展示的,会发现它是不需要train的,因为它是一种暴力搜索,query会依次与向量库里的每一个向量做L2计算,然后返回结果,所以叫做精确模式,不会有精度上的损失,但这种缺陷也比较明显,向量库数量较大时,这种肯定是满足不了实时性的,所以就出现先聚类再索引的IndexIVFFlat、IndexIVFPQ,牺牲少许精度换速度。
IndexIVFFlat是一种先进行向量聚类分簇再进行相似性计算进行索引查询的方式。会对向量库先聚类分簇, 然后通过查询与query最近的聚类中心,然后返回这个类中所有与query相似的向量。
不同于精确索引-IndexFlatL2, 创建索引时就指定了量化器。创建IndexIVFFlat的时候需要指定一个量化器(quantizer),作为来判断落入哪个堆的计算方式。
在衡量向量相似度上,faiss提供了两种方法:①faiss.METRIC_L2(欧式距离)、②faiss.METRIC_INNER_PRODUCT(向量内积)。
dimension = 128
nums_user = 512
user_embedding = np.random.random((nums_user, dimension)).astype('float32')
nlist = 50 # 将库向量分割为50个聚类中心
top_k = 10
quantizer = faiss.IndexFlatL2(dimension) # 量化器
index = faiss.IndexIVFFlat(quantizer, dimension, nlist, faiss.METRIC_L2)
gpu_index = faiss.index_cpu_to_all_gpus(index)
#METRIC_L2计算L2距离, 或faiss.METRIC_INNER_PRODUCT计算内积
assert not gpu_index.is_trained # 倒排表索引类型需要训练
gpu_index.train(user_embedding)
assert gpu_index.is_trained
gpu_index.add(user_embedding)
gpu_index.nprobe = 50 # 选择n个空间进行索引,
query = user_embedding[:5] # 查询本身
dis, ind = gpu_index.search(query, top_k)
print(dis)
print(ind)
倒排快速索引可以通过改变nprobe的值,调整精度。nprobe的值越大,精度也增大,但时间会增加,当nprobe=nlist时,等效于IndexFlatL2精确的索引类型。 简单点说,倒排表索引首先通过聚类方法将向量库分割成若干子类,当查询向量来临,选择距离最近的类中心,然后在子类中应用精确查询方法,通过增加相邻的子类个数提高索引的精确度。
IndexIVFPQ是一种乘积量化索引,在上述两种索引方式中,在index中都保存了完整的数据库向量,在数据量非常大的时候,就会存在内存限制问题。所以在faiss中,乘积量化索引-IndexIVFPQ也是大数据量在线模式下最推荐采用的,牺牲部分精度换速度。IndexIVFFlat是一种先进行向量分割聚类,编码压缩向量,再进行相似性计算进行索引查询的方式,它采用的是有损压缩向量方式,是一种乘积量化索引。
1、faiss有两种索引构建模式,一种是全量构建,二是增量的索引构建,也就是在原来的基础上添加向量。第一次构建索引时需要经过Train和Add两个操作,后续添加新embedding就直接执行Add就是增量构建了。
2、Faiss提供了三种索引类型精确索引-IndexFlatL2、indexFlatIP、倒排快速索引-IndexIVFFlat、乘积量化索引-IndexIVFPQ, 精确索引是一种暴力无误差模式,检索库数据量不大时,优先追求精度时可以用这个。检索库数据量较大时,推荐使用乘积量化索引。
3、关于非精确索引下倒排快速索引-IndexIVFFlat、乘积量化索引-IndexIVFPQ,需指定量化器(quantizer),quantizer是作为来判断落入哪个堆的计算衡量方式。在衡量向量相似度上,faiss依然是提供了两种方法:①faiss.METRIC_L2(欧式距离)、②faiss.METRIC_INNER_PRODUCT(向量内积)
文章关联: link