Faiss为啥这么快?原来是量化器在做怪!1

简介: Faiss为啥这么快?原来是量化器在做怪!

一、介绍

       Faiss(Facebook AI Similarity Search)是一个面向相似性搜索和聚类的开源库,专注于高维向量的快速相似性搜索。该库提供了一系列高效的算法和数据结构,可用于处理大规模高维向量数据,广泛应用于信息检索、机器学习和深度学习等领域。本文主要介绍Faiss中包含的量化器,量化器可以将高维向量映射到低维码本(codebook)以便进行快速近似最近邻搜索。当然在介绍量化器之前还有说一些前置的概念。


Faiss的API:

Search — Faiss documentation


二、常用的距离

       衡量两个向量的距离常用的有L2距离(空间距离)和余弦相似度(角度距离),其中余弦相似度在Faiss中并未直接提供,而是使用内积来变相实现,接下来介绍一下L2距离、内积和余弦相似度。

1.L2距离(欧氏距离)

       L2距离是最常见的距离度量方式,用于衡量两个向量之间的空间距离。在二维空间中,L2距离可以通过勾股定理计算得出,即两点之间的直线距离。在多维空间中,L2距离表示为两个向量之间每个对应维度差值的平方和的平方根。

       空间中两点的L2距离公示如下:

       其中n是AB两点的坐标向量长度。

2.内积(Inner Product)

       内积(点积)和余弦相似度是线性代数中常用的两种度量方式,它们在机器学习和数据科学中经常用于衡量向量之间的相似性。余弦相似度是基于向量的方向来度量的,而不是它们的长度。内积有他的几何意义,但是比较抽象,我理解他的主要作用还是用来表示余弦相似度,以下是内积公式,很简单就是向量的每一维相乘后相加:

3.内积和余弦相似度

       要在 Faiss 中使用余弦相似度,通常需要将向量进行归一化处理,然后使用内积计算余弦相似度。那么为什么将向量进行归一化处理后,内积就等价于余弦距离了呢,其实很简单,先来看一下余弦相似度公式:


       取值范围[-1, 1],值越接近1表示向量越相似。通过上式可以看出,当向量进行归一化处理后,它们的长度(范数)变为1,即 。所以在向量归一化处理之后,两个向量的内积实际上就等于它们之间的余弦相似度。

       当然也可以将余弦相似度的的结果控制在0到1之间,像这样 ,其实在很多时候这样做更有用。

4.余弦距离

       多说一句余弦距离,余弦距离定义为余弦相似度的一个变形,通常用于衡量向量之间的不相似度。它可以通过以下公式计算:

       取值范围[0, 2],越接近0距离越近,当然也可以做一下归一化。

三、Flat索引

       Flat索引是一种基本的索引结构,使用简单的线性扫描方式进行相似性搜索。简单来说,就是暴力搜索,入库的向量不会经过任何形式的预处理(例如归一化)或量化,它们以原始的、完整的形式存储。在进行相似度检索时,Flat索引会完整地扫描整个库,因此它的计算结果一定是全局最优的。


       Flat有如下相关算法:

image.png        IndexFlat、IndexFlatL2、IndexFlatIP的代码很像,就写在一起,代码中的1024表示向量长度,下面的例子我们都使用1024维的向量:

import numpy as np
import faiss
 
# 创建一些示例向量数据
np.random.seed(0)
data = np.random.rand(10, 1024).astype('float32')
 
# 创建 Flat 索引
# 第二个参数使用faiss.METRIC_L2时该方法等价于IndexFlatL2
# 第二个参数使用faiss.METRIC_INNER_PRODUCT时该方法等价于IndexFlatIP
index = faiss.IndexFlat(64, faiss.METRIC_L2)
# 创建 FlatL2 索引
# index = faiss.IndexFlatL2(1024)
# 创建 FlatIP 索引
# index = faiss.IndexFlatIP(1024)
 
# 向索引中添加向量数据
index.add(data)
 
# 创建一个查询向量
query = np.random.rand(1, 1024).astype('float32')
 
# 执行相似性搜索
k = 5  # 指定返回最相似的 k 个向量
D, I = index.search(query, k)  # D 为距禃,I 为对应的向量索引
 
print("最相似的向量索引:", I)
print("对应的距禃:", D)
 
'''
最相似的向量索引: [[1 3 9 2 8]]
对应的距禃: [[ 7.4712954  8.061846   8.335889   8.582125  10.274038 ]]
'''

       下面是IndexFlat1D的代码示例:

import numpy as np
import faiss
 
# 创建一些示例的一维向量数据
data = np.array([[1.2], [3.4], [5.6], [7.8], [9.0]], dtype='float32')
 
# 创建 IndexFlat1D 索引
index = faiss.IndexFlat1D()
 
# 向索引中添加向量数据
index.add(data)
 
# 创建一个查询向量
query = np.array([[4.5]], dtype='float32')
 
# 执行相似性搜索
k = 1  # 指定返回最相似的 1 个向量
D, I = index.search(query, k)  # D 为距禃,I 为对应的向量索引
 
print("最相似的向量索引:", I)
print("对应的距禃:", D)
 
'''
最相似的向量索引: [[2]]
对应的距禃: [[1.0999999]]
'''

四、IVF索引

      IVF(Inverted File)倒排索引,相较于暴力匹配(Flat)多了一个预筛的步骤。目的是减少需要计算距离的次数。

       索引过程并不复杂,就是直接对所有向量做k-means,然后所有的向量都会归到某一个簇中;检索的时候,每来一个查询向量,首先计算其与每个簇心的距离,然后选择距离最近的若干个簇,只计算查询向量与这几个簇底下的向量的距离,然后排序取top-k就可以了。

       索引过程如下:

       检索过程如下:


       Faiss具体实现有一个小细节就是在计算查询向量和一个簇底下的向量的距离的时候,所有向量都会被转化成与簇心的残差,这应该就是类似于归一化的操作。使用了IVF过后,需要计算距离的向量个数就少了几个数量级,最终向量检索就变成一个很快的操作。

       IVF相关的代码会随着量化算法介绍。

五、量化算法

       量化算法大体分3类,标准量化、乘积量化和加法量化。简单地说,标准量化直接将向量离散化,不变维度;乘积量化是将向量分段,然后对每段进项单独量化;加法量化是将完整的向量拆分成多级向量(维度与原始向量相同)的和,对每级进行量化。

       Faiss中量化算法一般是成组出现的,由量化类、量化索引和IVF索引,其中量化类可以对数据进行量化,返回量化结果,一般并不直接使用;量化索引在量化类的基础上将数据进行索引存储,可以执行检索;IVF量化索引是使用了倒排索引的量化索引。

1.标准量化(Scalar Quantizer)

       标量量化是最基础的量化手段,将向量分成若干个区间,然后将每个区间映射到一个离散的数值用对数据直接量化。比如数据是1000x1024按照8bit量化,假设每个维度的取值范围是0到1000,我们先根据维度分成1024组,每组划分为个级别,每个级别代表4(1000/256)。最终向量中的值将被分配到所在组的256个级别中的一个,得到量化结果。


       值得注意的是,在检索的时候faiss.IndexScalarQuantizer还会遍历所有数据,然后对筛选出的数据计算精确的距离,时间复杂度很高,所以实际中不要使用faiss.IndexScalarQuantizer,可以使用faiss.IndexIVFScalarQuantizer


Faiss中SQ相关的有如下相关算法:

类名 描述
faiss.ScalarQuantizer(1024,faiss.ScalarQuantizer.QT_8bit)

标准量化

d:每个向量的维度

qtype:QT_8bit的意思是每个维度聚类簇的数量为

image.png

示例代码如下:

"""
标准量化示例
"""
 
import numpy as np
import faiss
import time
 
 
def test_sq():
    # 设定量化器参数
    quantizer = faiss.ScalarQuantizer(d, faiss.ScalarQuantizer.QT_8bit)
 
    # 对示例数据进行训练
    quantizer.train(data)
 
    # 进行量化
    codes = quantizer.compute_codes(data)
    print("data0:", data[0])
    print("codes0:", codes[0])
 
 
def test_index():
    # 向索引中添加数据
    t = time.time()
    index.train(data)
    index.add(data)
    cost1 = time.time() - t
 
    # 进行近似最近邻搜索
    k = 50  # 返回最近邻的数量
    t = time.time()
    D, I = index.search(query, k)
    cost2 = time.time() - t
    return D, I, cost1, cost2
 
 
if __name__ == '__main__':
    d = 1024
    nlist = 32
    n = 1000000
    np.random.seed(0)
    # 生成一些随机向量作为示例数据
    data = np.random.rand(n, d).astype(np.float32)
    # 定义查询向量
    query = np.random.rand(1, d).astype(np.float32)
 
    # 量化器的示例
    test_sq()
 
    # 暴力检索的示例
    index = faiss.IndexFlat(d, faiss.METRIC_L2)
    D, I, cost1, cost2 = test_index()
    print("Flat索引 最近邻的距离:", D)
    print("Flat索引 最近邻的索引:", I)
    print("Flat索引 耗时:", cost1, cost2)
 
    # SQ索引的示例
    index = faiss.IndexScalarQuantizer(d, faiss.ScalarQuantizer.QT_8bit, faiss.METRIC_L2)
    D, I, cost1, cost2 = test_index()
    print("SQ索引 最近邻的距离:", D)
    print("SQ索引 最近邻的索引:", I)
    print("SQ索引 耗时:", cost1, cost2)
 
    # IVF+SQ的示例
    # quantizer = faiss.IndexScalarQuantizer(d, faiss.ScalarQuantizer.QT_8bit, faiss.METRIC_L2)
    quantizer = faiss.IndexFlat(d, faiss.METRIC_L2)
    index = faiss.IndexIVFScalarQuantizer(quantizer, d, nlist, faiss.ScalarQuantizer.QT_8bit, faiss.METRIC_L2)
    D, I, cost1, cost2 = test_index()
    print("IVF+SQ索引 最近邻的距离:", D)
    print("IVF+SQ索引 最近邻的索引:", I)
    print("IVF+SQ索引 耗时:", cost1, cost2)

Faiss为啥这么快?原来是量化器在做怪!2:https://developer.aliyun.com/article/1507797


相关文章
|
存储 人工智能 自然语言处理
|
4月前
|
自然语言处理 数据挖掘 测试技术
Qwen3-VL-Embedding系列上新:探索统一多模态表征与排序
2025年6月,Qwen3-VL-Embedding与Qwen3-VL-Reranker开源,基于Qwen3-VL打造,支持文本、图像、视频等多模态检索与跨模态理解,具备统一表示学习、高精度重排序能力,广泛适用于全球化多语言场景,助力高效多模态信息检索。
1849 5
|
10月前
|
存储 机器学习/深度学习 算法
|
5月前
|
存储 缓存 人工智能
图索引性能提升 400%:详解 VSAG 向量检索框架
VSAG 是蚂蚁集团开源的图索引向量检索框架。 本文源自 VSAG 团队在 VLDB'25 发表的《VSAG: An Optimized Search Framework for Graph-based Approximate Nearest Neighbor Search》,介绍 VSAG 框架如何通过缓存优化、自动调参和距离计算加速,在保证高召回率前提下将检索性能提升最高 400%。
|
算法 数据挖掘
Faiss为啥这么快?原来是量化器在做怪!2
Faiss为啥这么快?原来是量化器在做怪!
1356 1
|
12月前
|
存储 算法 NoSQL
千亿级向量索引的秘密武器:一文详解蚂蚁集团的工程实践和开源突破
本文整理自2025QCon全球软件大会贾玮(蚂蚁集团NoSQL数据库和向量数据库的技术负责人)的演讲实录。 本文围绕向量检索技术的研究与实践展开系统性阐述,包含以下四个维度: 1.向量检索的基础原理以及相关的核心技术挑战; 2.蚂蚁集团在向量检索领域的工程实践和具体案例; 3.向量检索领域的最新学术研究和应用成果; 4.蚂蚁开源向量索引库VSAG的最新进展。
|
搜索推荐 数据挖掘 数据处理
《探索 Faiss:原理与应用解析》
在数据驱动的时代,高效处理和搜索海量数据至关重要。Faiss 是一个专为大规模相似性搜索和聚类设计的库,擅长处理高维向量数据,广泛应用于文本处理、图像识别等领域。本文深入解析 Faiss 的原理、使用方法及其在图像检索、文本相似性比较和推荐系统中的实际应用,帮助读者掌握这一强大工具,提升数据处理能力。
894 2
|
算法 数据挖掘 数据库
表格存储低成本向量检索服务助力 AI 检索
本文阐述了阿里云表格存储(Tablestore)如何通过其向量检索服务应对大规模数据检索的需求,尤其是在成本、规模和召回率这三个关键挑战方面。
647 13
|
SQL 机器学习/深度学习 自然语言处理
Text-to-SQL技术演进 - 阿里云OpenSearch-SQL在BIRD榜单夺冠方法剖析
本文介绍了Text-to-SQL的技术演进,并对OpenSearch-SQL方法进行剖析。
2467 8
|
存储 数据库 索引
faiss 三种基础索引方式
faiss 三种基础索引方式
1282 1