Faiss教程:入门
Faiss处理固定维度d的数据,矩阵每一行表示一个向量,每列表示向量的一项。Faiss采用32-bit浮点型存储。
假设xb为数据集,维度为\(nb\times{d}\);xq是查询数据,维度为\(nq\times{d}\)
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.
为数据构建索引,Faiss包含非常多的索引类型,这里我们采用最简单的版本IndexFlatL2,基于L2距离进行brute-force搜索。
所有的索引的构建都需要知道它们操作数据的维度(d),其中大多索引需要一个训练过程,基于训练集来分析向量的分布。对IndexFlatL2,我们可以跳过训练。
索引创建后,add 和 search操作便可以基于索引来执行了。add 添加数据到索引(添加到xb)。
我们可以查看索引的属性状态,is_trained是否训练完成(有些不需要训练),ntotal被索引数据的数目。
有一些索引,需要提供向量的整数ID,如果ID没有提供,add可以采用数据的序号数,第一个数据为0,第二个是1,以此类推。
import faiss # make faiss available
index = faiss.IndexFlatL2(d) # build the index
print(index.is_trained)
index.add(xb) # add vectors to the index
print(index.ntotal)
# output
True
100000
基于索引便可以进行k近邻查询了,结果矩阵为\(nq\times{k}\),第i行表示第i个查询向量,每行包含k个最近邻的ID,距离依次递增。同时返回相同维度的距离矩阵。
k = 4 # we want to see 4 nearest neighbors
D, I = index.search(xb[:5], k) # sanity check
print(I)
print(D)
D, I = index.search(xq, k) # actual search
print(I[:5]) # neighbors of the 5 first queries
print(I[-5:]) # neighbors of the 5 last queries
# output
[[ 0 393 363 78]
[ 1 555 277 364]
[ 2 304 101 13]
[ 3 173 18 182]
[ 4 288 370 531]]
[[ 0. 7.17517328 7.2076292 7.25116253]
[ 0. 6.32356453 6.6845808 6.79994535]
[ 0. 5.79640865 6.39173603 7.28151226]
[ 0. 7.27790546 7.52798653 7.66284657]
[ 0. 6.76380348 7.29512024 7.36881447]]
[[ 381 207 210 477]
[ 526 911 142 72]
[ 838 527 1290 425]
[ 196 184 164 359]
[ 526 377 120 425]]
[[ 9900 10500 9309 9831]
[11055 10895 10812 11321]
[11353 11103 10164 9787]
[10571 10664 10632 9638]
[ 9628 9554 10036 9582]]
受向量第一项的影响,查询数据中头部数据的最近邻也在数据集的头部。
加速查询,首先可以把数据集切分成多个,我们定义Voronoi Cells,每个数据向量只能落在一个cell中。查询时只需要查询query向量落在cell中的数据了,降低了距离计算次数。
通过IndexIVFFlat索引,可以实现上面的思想,它需要一个训练的阶段。IndexIVFFlat需要另一个索引,称为quantizer,来判断向量属于哪个cell。
search方法也相应引入了nlist(cell的数目)和nprobe(执行搜索的cell数)
nlist = 100
k = 4
quantizer = faiss.IndexFlatL2(d) # the other index
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
# here we specify METRIC_L2, by default it performs inner-product search
assert not index.is_trained
index.train(xb)
assert index.is_trained
index.add(xb) # add may be a bit slower as well
D, I = index.search(xq, k) # actual search
print(I[-5:]) # neighbors of the 5 last queries
index.nprobe = 10 # default nprobe is 1, try a few more
D, I = index.search(xq, k)
print(I[-5:]) # neighbors of the 5 last queries
# output
[[ 9900 10500 9831 10808]
[11055 10812 11321 10260]
[11353 10164 10719 11013]
[10571 10203 10793 10952]
[ 9582 10304 9622 9229]]
[[ 9900 10500 9309 9831]
[11055 10895 10812 11321]
[11353 11103 10164 9787]
[10571 10664 10632 9638]
[ 9628 9554 10036 9582]]
结果并不完全一致,因为落在Voronoi cell外的数据也可能离查询数据更近。适当增加nprobe可以得到和brute-force相同的结果,nprobe控制了速度和精度的平衡。
IndexFlatL2 和 IndexIVFFlat都要存储所有的向量数据,这对于大型数据集是不现实的。Faiss基于PQ提供了变体IndexIVFPQ来压缩数据向量(一定的精度损耗)。
向量仍是存储在Voronoi cells中,但是它们的大小可以通过m来设置(m是d的因子)。
由于向量值不在准确存储,所以search计算的距离也是近似的了。
nlist = 100
m = 8 # number of bytes per vector
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:])
# output
[[ 0 424 363 278]
[ 1 555 1063 24]
[ 2 304 46 346]
[ 3 773 182 1529]
[ 4 288 754 531]]
[[ 1.45568264 6.03136778 6.18729019 6.38852692]
[ 1.4934082 5.74254704 6.19941282 6.21501732]
[ 1.60279942 6.20174742 6.32792568 6.78541422]
[ 1.69804895 6.2623148 6.26956797 6.56042767]
[ 1.30235791 6.13624859 6.33899879 6.51442146]]
[[10664 10914 9922 9380]
[10260 9014 9458 10310]
[11291 9380 11103 10392]
[10856 10284 9638 11276]
[10304 9327 10152 9229]]
最近距离(到自身)不再是0了,因为数据被压缩了。整理64位 32-bits向量,被分割为8份,每份用8bits表示,所以压缩因子为32。
查询数据集的结果和IVFFlat对比,大多是错误的,但是它们都在10000左右。这种策略在实际数据中是更好的:
- 均匀分布的数据是很难索引的,它们很难聚类和降维
- 自然数据,相似数据比不相干数据的距离要显著的更小。
使用工厂方法简化索引构建
index = faiss.index_factory(d, "IVF100,PQ8")
PQ8替换为Flat便得到了IndexFlat索引,工厂方法是非常有效的,尤其是对数据采用预处理的时候,如参数"PCA32,IVF100,Flat",表示通过PCA把向量减低到32维。
Faiss可以基本无缝地在GPU上运行,首先申请GPU资源,并包括足够的显存空间。
res = faiss.StandardGpuResources() # use a single GPU
使用GPU创建索引
# build a flat (CPU) index
index_flat = faiss.IndexFlatL2(d)
# make it into a gpu index
gpu_index_flat = faiss.index_cpu_to_gpu(res, 0, index_flat)
索引的使用和CPU上类似
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
使用多张GPU卡
ngpus = faiss.get_num_gpus()
print("number of GPUs:", ngpus)
cpu_index = faiss.IndexFlatL2(d)
gpu_index = faiss.index_cpu_to_all_gpus( # build the index
cpu_index
)
gpu_index.add(xb) # add vectors to the index
print(gpu_index.ntotal)
k = 4 # we want to see 4 nearest neighbors
D, I = gpu_index.search(xq, k) # actual search
print(I[:5]) # neighbors of the 5 first queries
print(I[-5:]) # neighbors of the 5 last queries
Faiss教程:入门的更多相关文章
- wxPython中文教程入门实例
这篇文章主要为大家分享下python编程中有关wxPython的中文教程,分享一些wxPython入门实例,有需要的朋友参考下 wxPython中文教程入门实例 wx.Window 是一个基类 ...
- Asp.Net MVC4.0 官方教程 入门指南之五--控制器访问模型数据
Asp.Net MVC4.0 官方教程 入门指南之五--控制器访问模型数据 在这一节中,你将新创建一个新的 MoviesController类,并编写代码,实现获取影片数据和使用视图模板在浏览器中展现 ...
- Asp.Net MVC4.0 官方教程 入门指南之四--添加一个模型
Asp.Net MVC4.0 官方教程 入门指南之四--添加一个模型 在这一节中,你将添加用于管理数据库中电影的类.这些类是ASP.NET MVC应用程序的模型部分. 你将使用.NET Framewo ...
- Asp.Net MVC4.0 官方教程 入门指南之三--添加一个视图
Asp.Net MVC4.0 官方教程 入门指南之三--添加一个视图 在本节中,您需要修改HelloWorldController类,从而使用视图模板文件,干净优雅的封装生成返回到客户端浏览器HTML ...
- Asp.Net MVC4.0 官方教程 入门指南之二--添加一个控制器
Asp.Net MVC4.0 官方教程 入门指南之二--添加一个控制器 MVC概念 MVC的含义是 “模型-视图-控制器”.MVC是一个架构良好并且易于测试和易于维护的开发模式.基于MVC模式的应用程 ...
- BAT脚本编写教程入门提高篇
BAT脚本编写教程入门提高篇 批处理文件的参数 批处理文件还可以像C语言的函数一样使用参数(相当于DOS命令的命令行参数),这需要用到一个参数表示符“%”. %[1-9]表示参数,参数是指在运行批处理 ...
- Nhibernate 4.0 教程入门
Nhibernate 4.0 教程 目录 1. 下载Nhibernate 4.04. 1 2. 入门教程... 2 3. 测试项目详解... 3 4. 总结.. ...
- Elasticsearch 教程--入门
1.1 初识 Elasticsearch 是一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎,可以说 Lucene 是当今最先进,最高效的全功能开源搜索引擎框架. 但是 L ...
- laravel教程入门笔记
安装laravel框架 1.安装命令 composer create-project --prefer-dist laravel/laravel ytkah ytkah表示文件夹名,如果不写的话自动会 ...
随机推荐
- 定制jQuery File Upload为微博式单文件上传
日志未经声明,均为AlloVince原创.版权采用『 知识共享署名-非商业性使用 2.5 许可协议』进行许可. jQuery File Upload是一个非常优秀的上传组件,主要使用了XHR作为上传方 ...
- 【MySQL】MySQL存储过程介绍
目录结构: contents structure [-] 存储过程简介 关于MySQL的存储过程 MySQL存储过程的创建 格式 声明分割符 参数 变量 注释 MySQL存储过程的调用 MySQL存储 ...
- SQL临时表
临时表就是那些名称以井号 (#) 开头的表.如果当用户断开连接时没有除去临时表,SQL Server 将自动除去临时表.临时表不存储在当前数据库内,而是存储在系统数据库 tempdb 内. 临时表有 ...
- preg_match用法
preg_match 利用 preg_match(),我们可以完成字符串的规则匹配.如果找到一个匹配,preg_match() 函数返回 1,否则返回 0.还有一个可选的第三参数可以让你把匹配的部分存 ...
- android studio 如何让包名展开
通常我们新建一个包名的时候,会发现他们连在一起,根本无法在创建一个同级的包 工具/原料 电脑,android studio 方法/步骤 1,我们先在包名下建一个包,变成了这样,根本无法在同 ...
- 【转】Tesla Autopilot
Tesla Autopilot 以下内容是<Tesla Model S的设计失误>一文中新加入的小节.由于写作时间相距太远,而且由于它的时效性,现在也把它单独提出来,独立成文. 两个月前, ...
- Java数据结构和算法(六):前缀、中缀、后缀表达式
前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...
- 如何使Android应用支持多种屏幕分辨率
原文:http://android.eoe.cn/topic/android_sdk 描述: 让您指定您的应用支持的屏幕的大小并且可以通过屏幕兼容模式来支持比您应用所支持更大的屏幕.所以这对于您需要在 ...
- 关于的 recorder robotium 的Eclipse插件(URL:http://recorder.robotium.com/updates/或者说不可用)
最近在学robotium.看到别人说robotium的Eclipse的插件非常好用. 打算安装时.发现死活都无法连接http://recorder.robotium.com/updates/ 过程是 ...
- Atitit html5.1 新特性attilax总结
Atitit html5.1 新特性attilax总结 9. 嵌入 header 和 footer1 7. 校验表单1 6. 浏览器的上下文菜单2 1. 响应式图像2 Attilax觉得还不错的心特性 ...