Faiss原理介绍

Faiss原理介绍

github
Faiss 包含多种相似性搜索方法。它假设实例表示为向量并由整数标识,并且可以将向量与 L2(欧几里得)距离或点积进行比较。与查询向量相似的向量是那些与查询向量具有最低 L2 距离或最高点积的向量。它还支持余弦相似度,因为这是归一化向量的点积。

大多数方法,如基于二进制向量和紧凑量化代码的方法,只使用向量的压缩表示,不需要保留原始向量。这通常是以降低搜索精度为代价的,但这些方法可以扩展到单个服务器上主内存中的数十亿个向量。

Faiss 的工作原理

Faiss 是围绕存储一组向量的索引类型构建的,并提供了一个函数来使用 L2 和/或点积向量比较在它们中进行搜索。一些索引类型是简单的基线,例如精确搜索。大多数可用的索引结构对应于关于

  • 搜索时间
  • 搜索质量
  • 每个索引向量使用的内存
  • 训练时间
  • 无监督训练需要外部数据

faiss 三个最基础的 index. 分别是 IndexFlatL2, IndexIVFFlat, IndexIVFPQ

IndexFlat2 最基础的Index

# IndexFlat2
import numpy as np

d = 64 # 维度
nb = 100000  # 数据大小
nq = 10000  # nb的查询
np.random.seed(1314)
xb = np.random.random((nb, d)).astype('float32')
xb[:,0] += np.arange(nb)/1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:,0] += np.arange(nq)/1000.

import faiss
index = faiss.IndexFlatL2(d) # 建立索引
print(index.is_trained)
index.add(xb) # 添加向量到索引
print(index.ntotal) # 查询总数

k = 4 #top 值
D, I = index.search(xb[:5], k)
print(D)  # 相近得分
print(I)  # 相近索引
D, I = index.search(xq, k)
print(I)

IndexIVFFlat 更快的搜索

在d维空间中定义Voronoi单元格,并且每个数据库矢量都落入其中一个单元格中。在搜索时,只有查询x所在单元中包含的数据库向量y与少数几个相邻查询向量进行比较。(划分搜索空间)

import numpy as np

d = 64                           # dimension
nb = 100000                      # database size
nq = 10000                       # nb of queries
np.random.seed(1234)             # make reproducible
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.

import faiss
nlist = 100  # 聚类中心的个数  搜索空间
k = 4 # 查找最相似的k个向量
quantizer = faiss.IndexFlatL2(d)  # the other index
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
assert not index.is_trained
index.train(xb) ## 训练
assert index.is_trained
index.add(xb)
D, I = index.search(xq, k)
print(I[-5:])                  # neighbors of the 5 last queries
index.nprobe = 10              # 查找聚类中心的个数,默认为1个。
D, I = index.search(xq, k)
print(I[-5:])

IndexIVFPQ 更低的内存占用

有损存储
索引IndexFlatL2和IndexIVFFlat都存储完整的向量。 为了扩展到非常大的数据集,Faiss提供了基于产品量化器的有损压缩来压缩存储的向量的变体。压缩的方法基于乘积量化(Product Quantizer)
在这种情况下,由于矢量没有精确存储,搜索方法返回的距离也是近似值

import numpy as np

d = 64                           # dimension
nb = 100000                      # database size
nq = 10000                       # nb of queries
np.random.seed(1234)             # make reproducible
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.

import faiss

nlist = 100
m = 8
k = 4
quantizer = faiss.IndexFlatL2(d)  # this remains the same
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)
                                  # 8 specifies that each sub-vector is encoded as 8 bits
index.train(xb)
index.add(xb)
D, I = index.search(xb[:5], k) # sanity check
print(I)
print(D)
index.nprobe = 10              # make comparable with experiment above
D, I = index.search(xq, k)     # search
print(I[-5:])

Faiss核心算法实现

Faiss对一些基础的算法提供了非常高效的失效

  • 聚类Faiss提供了一个高效的k-means实现
  • PCA降维算法
  • PQ(ProductQuantizer)编码/解码

Faiss功能流程说明

   通过Faiss文档介绍可以了解faiss的主要功能就是相似度搜索。如下图所示,以图片搜索为例,所谓相似度搜索,便是在给定的一堆图片中,寻找出我指定的目标最像的K张图片,也简称为KNN(K近邻)问题。

GPU加速

GPU 实现可以接受来自 CPU 或 GPU 内存的输入。在具有 GPU 的服务器上,GPU 索引可用作 CPU 索引的直接替换(例如,替换IndexFlatL2GpuIndexFlatL2),并且自动处理到/从 GPU 内存的复制。但是,如果输入和输出都驻留在 GPU 上,结果会更快。支持单 GPU 和多 GPU 使用。

import numpy as np

d = 64                           # dimension
nb = 100000                      # database size
nq = 10000                       # nb of queries
np.random.seed(1234)             # make reproducible
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.

import faiss                     # make faiss available

res = faiss.StandardGpuResources()  # use a single GPU

## Using a flat index

index_flat = faiss.IndexFlatL2(d)  # build a flat (CPU) index

# make it a flat GPU index
gpu_index_flat = faiss.index_cpu_to_gpu(res, 0, index_flat)

gpu_index_flat.add(xb)         # add vectors to the index
print(gpu_index_flat.ntotal)

k = 4                          # we want to see 4 nearest neighbors
D, I = gpu_index_flat.search(xq, k)  # actual search
print(I[:5])                   # neighbors of the 5 first queries
print(I[-5:])                  # neighbors of the 5 last queries


## Using an IVF index

nlist = 100
quantizer = faiss.IndexFlatL2(d)  # the other index
index_ivf = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
# here we specify METRIC_L2, by default it performs inner-product search

# make it an IVF GPU index
gpu_index_ivf = faiss.index_cpu_to_gpu(res, 0, index_ivf)

assert not gpu_index_ivf.is_trained
gpu_index_ivf.train(xb)        # add vectors to the index
assert gpu_index_ivf.is_trained

gpu_index_ivf.add(xb)          # add vectors to the index
print(gpu_index_ivf.ntotal)

k = 4                          # we want to see 4 nearest neighbors
D, I = gpu_index_ivf.search(xq, k)  # actual search
print(I[:5])                   # neighbors of the 5 first queries
print(I[-5:])                  # neighbors of the 5 last queries

参考

https://blog.csdn.net/kanbuqinghuanyizhang/article/details/80774609
https://www.jianshu.com/p/43db601b8af1

你可能感兴趣的:(算法,python,机器学习,算法)