【Faiss】快速入门(二)

2024-08-27 17:58
文章标签 入门 快速 faiss

本文主要是介绍【Faiss】快速入门(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Tutorial 快速入门

数据准备

faiss可以处理固定维度d的向量集合,这样的集合这里用二维数组表示。 一般来说,我们需要两个数组:
1.data。包含被索引的所有向量元素;
2.query。索引向量,我们需要根据索引向量的值返回xb中的最近邻元素。
为了对比不同索引方式的差别,在下面的例子中我们统一使用完全相同的数据,即维数d为512,data包含2000个向量,每个向量符合正态分布。
需要注意的是,faiss需要数组中的元素都是32位浮点数格式。 datatype = 'float32'。

import numpy as np 
d = 512          #维数
n_data = 2000   
np.random.seed(0) 
data = []
mu = 3
sigma = 0.1
for i in range(n_data):data.append(np.random.normal(mu, sigma, d))
data = np.array(data).astype('float32')# print(data[0])# 查看第六个向量是不是符合正态分布
import matplotlib.pyplot as plt 
plt.hist(data[5])
plt.show()

query = []
n_query = 10
mu = 3
sigma = 0.1
np.random.seed(12) 
query = []
for i in range(n_query):query.append(np.random.normal(mu, sigma, d))
query = np.array(query).astype('float32')

精确索引

在使用faiss时,我们是围绕index对象进行的。index中包含被索引的数据库向量,在索引时可以选择不同方式的预处理来提高索引的效率,表现维不同的索引类型。在精确搜索时选择最简单的IndexFlatL2索引类型。
IndexFlatL2类型遍历计算查询向量与被查询向量的L2精确距离,不需要训练操作(大部分index类型都需要train操作)。
在构建index时要提供相关参数,这里是向量维数d,构建完成index之后可以通过add()和search()进行查询。

import sys
sys.path.append('/home/maliqi/faiss/python/')
import faiss
index = faiss.IndexFlatL2(d)  # 构建index
print(index.is_trained)  # False时需要train
index.add(data)  #添加数据
print(index.ntotal)  #index中向量的个数结果:
True
2000

 

k = 10  # 返回结果个数
query_self = data[:5]  # 查询本身
dis, ind = index.search(query_self, k)
print(dis)  # 升序返回每个查询向量的距离
print(ind)  # 升序返回每个查询向量的k个相似结果结果为:
[[0.       8.007045 8.313328 8.53525  8.560175 8.561642 8.624167 8.6282348.709978 8.77004 ][0.       8.27809  8.355579 8.42606  8.462017 8.468868 8.487028 8.5499638.562824 8.599199][0.       8.152368 8.156569 8.223303 8.276016 8.376871 8.379269 8.4061228.418619 8.443283][0.       8.260519 8.336826 8.339298 8.40288  8.46439  8.474661 8.4790438.485248 8.526599][0.       8.346273 8.407202 8.462828 8.49723  8.520801 8.597084 8.6003868.605133 8.630594]]
[[   0  798  879  223  981 1401 1458 1174  919   26][   1  981 1524 1639 1949 1472 1162  923  840  300][   2 1886  375 1351  518 1735 1551 1958  390 1695][   3 1459  331  389  655 1943 1483 1723 1672 1859][   4   13  715 1470  608  459  888  850 1080 1654]]

因为查询向量是数据库向量的子集,所以每个查询向量返回的结果中排序第一的是其本身,L2距离是0.

k = 10
dis, ind = index.search(query, k)
print(dis)
print(ind)结果为:
[[8.61838   8.782156  8.782816  8.832029  8.837633  8.848496  8.8979788.916636  8.919006  8.9374   ][9.033303  9.038907  9.091705  9.15584   9.164591  9.200112  9.2018849.220335  9.279477  9.312859 ][8.063818  8.211029  8.306456  8.373352  8.459253  8.459892  8.4985578.546464  8.555408  8.621426 ][8.193894  8.211956  8.34701   8.446963  8.45299   8.45486   8.4735728.50477   8.513636  8.530684 ][8.369624  8.549444  8.704066  8.736764  8.760082  8.777319  8.8313458.835486  8.858271  8.860058 ][8.299072  8.432398  8.434382  8.457374  8.539217  8.562359  8.5790338.618736  8.630861  8.643393 ][8.615004  8.615164  8.72604   8.730943  8.762621  8.796932  8.7970688.797365  8.813985  8.834726 ][8.377227  8.522776  8.711159  8.724562  8.745737  8.763846  8.7686028.7727995 8.786856  8.828224 ][8.342917  8.488056  8.655106  8.662771  8.701336  8.741287  8.7436088.770507  8.786264  8.849051 ][8.522164  8.575703  8.68462   8.767247  8.782909  8.850494  8.8837338.90369   8.909393  8.91768  ]]
[[1269 1525 1723 1160 1694   48 1075 1028  544  916][1035  259 1279 1116 1398  879  289  882 1420 1927][ 327  345 1401  389 1904 1992 1612  106  981 1179][1259  112  351  804 1412 1987 1377  250 1624  133][1666  854 1135  616   94  280   30   99 1212    3][ 574 1523  366  766 1046   91  456  649   46  896][1945  944  244  655 1686  981  256 1555 1280 1969][ 879 1025  390  269 1115 1662 1831  610   11  191][ 156  154   99   31 1237  289  769 1524   56  661][ 427  182  375 1826  610 1384 1299  750    2 1430]]

倒排表快速索引

在数据量非常大的时候,需要对数据做预处理来提高索引效率。一种方式是对数据库向量进行分割,划分为多个d维维诺空间,查询阶段,只需要将查询向量落入的维诺空间中的数据库向量与之比较,返回计算所得的k个最近邻结果即可,大大缩减了索引时间。
nlist参数控制将数据集向量分为多少个维诺空间;
nprobe参数控制在多少个维诺空间的范围内进行索引。

nlist = 50  # 将数据库向量分割为多少了维诺空间
k = 10
quantizer = faiss.IndexFlatL2(d)  # 量化器
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)# METRIC_L2计算L2距离, 或faiss.METRIC_INNER_PRODUCT计算内积
assert not index.is_trained   #倒排表索引类型需要训练
index.train(data)  # 训练数据集应该与数据库数据集同分布
assert index.is_trainedindex.add(data)
index.nprobe = 50  # 选择n个维诺空间进行索引,
dis, ind = index.search(query, k)
print(dis)
print(ind)结果为:
[[8.61838   8.782156  8.782816  8.832029  8.837633  8.848496  8.8979788.916636  8.919006  8.9374   ][9.033303  9.038907  9.091705  9.15584   9.164591  9.200112  9.2018849.220335  9.279477  9.312859 ][8.063818  8.211029  8.306456  8.373352  8.459253  8.459892  8.4985578.546464  8.555408  8.621426 ][8.193894  8.211956  8.34701   8.446963  8.45299   8.45486   8.4735728.50477   8.513636  8.530684 ][8.369624  8.549444  8.704066  8.736764  8.760082  8.777319  8.8313458.835486  8.858271  8.860058 ][8.299072  8.432398  8.434382  8.457374  8.539217  8.562359  8.5790338.618736  8.630861  8.643393 ][8.615004  8.615164  8.72604   8.730943  8.762621  8.796932  8.7970688.797365  8.813985  8.834726 ][8.377227  8.522776  8.711159  8.724562  8.745737  8.763846  8.7686028.7727995 8.786856  8.828224 ][8.342917  8.488056  8.655106  8.662771  8.701336  8.741287  8.7436088.770507  8.786264  8.849051 ][8.522164  8.575703  8.68462   8.767247  8.782909  8.850494  8.8837338.90369   8.909393  8.91768  ]]
[[1269 1525 1723 1160 1694   48 1075 1028  544  916][1035  259 1279 1116 1398  879  289  882 1420 1927][ 327  345 1401  389 1904 1992 1612  106  981 1179][1259  112  351  804 1412 1987 1377  250 1624  133][1666  854 1135  616   94  280   30   99 1212    3][ 574 1523  366  766 1046   91  456  649   46  896][1945  944  244  655 1686  981  256 1555 1280 1969][ 879 1025  390  269 1115 1662 1831  610   11  191][ 156  154   99   31 1237  289  769 1524   56  661][ 427  182  375 1826  610 1384 1299  750    2 1430]]

通过改变nprobe的值,发现在nprobe值较小的时候,查询可能会出错,但时间开销很小,随着nprobe的值增加,精度逐渐增大,但时间开销也逐渐增加,当nprobe=nlist时,等效于IndexFlatL2索引类型。
简而言之,倒排表索引首先将数据库向量通过聚类方法分割成若干子类,每个子类用类中心表示,当查询向量来临,选择距离最近的类中心,然后在子类中应用精确查询方法,通过增加相邻的子类个数提高索引的精确度。

乘积量化索引

在上述两种索引方式中,在index中都保存了完整的数据库向量,在数据量非常大的时候会占用太多内存,甚至超出内存限制。
在faiss中,当数据量非常大的时候,一般采用乘积量化方法保存原始向量的有损压缩形式,故而查询阶段返回的结果也是近似的。

nlist = 50
m = 8                             # 列方向划分个数,必须能被d整除
k = 10
quantizer = faiss.IndexFlatL2(d)  
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 4)# 4 表示每个子向量被编码为 4 bits
index.train(data)
index.add(data)
index.nprobe = 50
dis, ind = index.search(query_self, k)  # 查询自身
print(dis)
print(ind)
dis, ind = index.search(query, k)  # 真实查询
print(dis)
print(ind)结果为:
[[4.8332453 4.916275  5.0142426 5.0211687 5.0282335 5.039744  5.0633745.0652556 5.065288  5.0683947][4.456933  4.6813188 4.698038  4.709836  4.72171   4.7280436 4.7285644.728917  4.7406554 4.752378 ][4.3990726 4.554667  4.622962  4.6567664 4.665245  4.700697  4.70566464.715714  4.7222314 4.7242   ][4.4063187 4.659938  4.719548  4.7234855 4.727058  4.7630377 4.7671384.770565  4.7718883 4.7720337][4.5876865 4.702366  4.7323933 4.7387223 4.7550535 4.7652235 4.78202724.788397  4.792813  4.7930083]]
[[   0 1036 1552  517 1686 1666    9 1798  451 1550][   1  725  270 1964  430  511  598   20  583  728][   2  761 1254  928 1913 1886  400  360 1850 1840][   3 1035 1259 1884  584 1802 1337 1244 1472  468][   4 1557  350  233 1545 1084 1979 1537  665 1432]]
[[5.184828  5.1985765 5.2006407 5.202751  5.209732  5.2114754 5.22038275.22132   5.2252693 5.2286644][5.478416  5.5195136 5.532296  5.563965  5.564443  5.5696826 5.5865555.5897493 5.59312   5.5942397][4.7446747 4.8150816 4.824335  4.834736  4.83847   4.844829  4.8506634.853364  4.856619  4.865398 ][4.733185  4.7483554 4.7688575 4.783175  4.785554  4.7890463 4.79395774.797909  4.8015175 4.802591 ][5.1260395 5.1264906 5.134188  5.1386065 5.141901  5.148476  5.17560865.1886897 5.192538  5.1938267][4.882325  4.900981  4.9040375 4.911916  4.916094  4.923492  4.9284334.928472  4.937878  4.9518585][4.9729834 4.976016  4.984484  5.0074816 5.015956  5.0174923 5.02008875.0217285 5.028976  5.029479 ][5.064405  5.0903125 5.0971365 5.098599  5.108646  5.113497  5.11559155.1244674 5.1263866 5.129635 ][5.060173  5.0623484 5.075763  5.087064  5.100909  5.1075807 5.1093095.110051  5.1323767 5.1330123][5.12455   5.149974  5.151128  5.163775  5.1637926 5.1726117 5.17325455.1762547 5.1780767 5.185327 ]]
[[1264  666   99 1525 1962 1228  366  268  358 1509][ 520  797 1973  365 1545 1032 1077   71  763  753][1632  689 1315  321  459 1486  818 1094  378 1479][ 721 1837  537 1741 1627  154 1557  880  539 1784][1772  750 1166 1799  572  997  340  127  756  375][1738 1978  724  749  816 1046 1402  444 1955  246][1457 1488 1902 1187 1485  986   32  531   56  913][1488 1244  121 1144 1280 1078 1012 1215 1639 1175][ 426   45  122 1239  300 1290  546  505 1687  434][ 263  343 1025  583 1489  356 1570 1282  627 1432]]

实验发现,乘积量化后查询返回的距离值与真实值相比偏小,返回的结果只是近似值。
查询自身时能够返回自身,但真实查询时效果较差,这里只是使用了正态分布的数据集,在真实使用时效果会更好,原因有:
1.正态分布的数据相对更难查询,难以聚类/降维;
2.自然数据相似的向量与不相似的向量差别更大,更容易查找;

这篇关于【Faiss】快速入门(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1112374

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

MySQL-CRUD入门1

文章目录 认识配置文件client节点mysql节点mysqld节点 数据的添加(Create)添加一行数据添加多行数据两种添加数据的效率对比 数据的查询(Retrieve)全列查询指定列查询查询中带有表达式关于字面量关于as重命名 临时表引入distinct去重order by 排序关于NULL 认识配置文件 在我们的MySQL服务安装好了之后, 会有一个配置文件, 也就

v0.dev快速开发

探索v0.dev:次世代开发者之利器 今之技艺日新月异,开发者之工具亦随之进步不辍。v0.dev者,新兴之开发者利器也,迅速引起众多开发者之瞩目。本文将引汝探究v0.dev之基本功能与优势,助汝速速上手,提升开发之效率。 何谓v0.dev? v0.dev者,现代化之开发者工具也,旨在简化并加速软件开发之过程。其集多种功能于一体,助开发者高效编写、测试及部署代码。无论汝为前端开发者、后端开发者

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显