Faiss那点事~

最近看了下Faiss的原因,今天让我们来Faiss那点事~
全名叫Facebook AI Similarity Search。顾名思义,Facebook的Ai相似检索。主要用来对海量的,高维的向量进行快速靠谱的检索。
在一些深度模型应用场景下,在线推理时间太长,性能要求高的系统很难接受,为此就需要走延迟召回这条路线,离线学习深度模型的表示型向量,构建Faiss索引库,在线快速召回。

但是Faiss之所以落地,它解决了哪些问题?又是如何解决的呢?

解决问题

试想,有一个向量q,要在另外一个规模在1000个规模大小的向量集合中,选择一个最近的向量,距离可以用欧氏距离或者余弦相似度。怎么选?

暴力选,那就需要q和1000个向量,分别算距离,总共需要1000次, O(n), 海量数据下,根本不可行。

那怎么办呢?
这种情况下,Faiss就出现了,来拯救银河系,具体说是来拯救这个问题。锁定了这个问题,一直没有放松,给出了一系列的解决方案。敲黑板就两点:

  1. 数据压缩,减少空间占用
  2. Product-Quantization,提高检索效率。

如何解决

背景介绍 - 预热阶段

把大象装冰箱总共分三步,总共分三步。
同理,把检索搞定,也是分三步:

  1. 压缩训练 (打开冰箱门)
  2. 索引数据 (把数据推进去)
  3. 检索 (关上冰箱门)

其中最关键的就是1,2两部分,索引,也是Faiss的核心。围着而Faiss则为这种场景提供了一套解决方案。
Faiss从两个方面改善了暴力搜索算法存在的问题:降低空间占用加快检索速度首先,
Faiss中提供了若干种方法实现数据压缩,包括PCA、Product-Quantization等。

三种不同的索引检索方式

接下来,我们会收看三种不同的索引检索方式:

精确搜索

要做到精确,就不能有损失,那就要用最笨的方式,最慢的方式,多快好省在这里是行不通的。
没错,就是暴力检索,即遍历所有的向量,算出最近的向量。 不要训练啊!!!
距离分为两种:
faiss.indexFlatL2: 欧式距离
faiss.indexFlatIP: 内积
代码如下:
index = faiss.IndexFlatL2(d) # buid the index, d是向量维度
// index = faiss.indexFlatIP(d)
index.add(xb)
D,I = index.search(xb[:5],k) # 返回xb[:5]五个向量最近的K个向量。

倒排文件: indexIVFFlat

向量太多了,怎么办? 暴力破解,岂不要累的血喷!!!
Faiss的第二大招来了,indexIVFFlat(倒排文件)
名字高大上,说起来也很简单。过程如下:
1) 我要打1w个,...., 说错了,我要建100个类,于是,首先使用k-means建立100个聚类中心。
针对query查询向量q, 则:
2)查询最近的聚类中心,也就是锁定最近的类
3)在该类内部,比较所有向量, 得到与查询向量q最相似的向量。

与上面,精确查找不同的是,建索引前,需要实现制订一个量化器(quantizer), faiss提供了两种衡量相似度的方法, 用于K-means聚类:
1)faiss.METRIC_L2 # 欧式距离,
2)faiss.METRIC_INNER_PRODUCT。 # 向量内积。

nlist =100 # 聚类中心个数
k = 20 # 查找最相似的k个向量
quantizer = faiss.IndexFlatL2(d) # 量化器 d是向量维度。
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)

index.train(data) # 训练数据data
index.add(data) #添加data 在第一次build索引文件的时候,需要经过Train和Add两个过程 。 后面的话只需要Add就行了。
index.nprobe = 50 # 选择n个维诺空间进行索引, .index.nprobe应该是选择最近的50个类,进行查找最最近的向量。
dis, ind = index.search(query, k)

乘积量化倒排文件: IndexIVFPQ

这个可是Faiss的最大招了。设计的很牛掰。最关键的有下面两个部分:
Product Quantizer, 简称PQ,将矢量编码或解码为代码。
Inverted File System,简称IVF。

  • Product Quantizer, PQ
    PQ有一个Pre-train的过程,其实就是对应Faiss的Train阶段,共有两部操作:
    1.Clustering
    2.Assign
    以一个128维的向量库为例:

Cluster具体做法
(a) 切分为4段,这样每一段的就是32维
(b) 针对每一段,都执行下面操作:
聚类,聚256个类,用8bit就可以表示每个类的类ID。
(c) 经过上面2后,每一个向量,分成四段,每一段都属于1个类。
如果用类 ID表示这段向量的话,那就是4个类ID。如下面图,每个向量最后都成了4个类ID。

也就是N * 32 -> N * 4

Assign具体做法

同样是以N=128,M=4为例,对于每一个查询向量:
(a) 把128维分成4段32维向量,
(b) 计算每一段向量与之前预训练好的簇心的距离,
由于每一段有256个类心,所以需要每一段需要算256次,4段的话算4256次,于是就得到了一个4256的表。
(c) 接下来,就可以按下面方法算,查询向量与库里任意向量的距离了,计算方法如下:
假定,查询向量为q, 库里向量为d:
分别依次查询,库向量每一段所属的类中心向量 与 查询向量对应段的向量距离。 不好意思,前面那个4256的表,都帮我们计算出来了,即查询向量跟所有类(4256)的距离。查表即可~~~

这样的话,我们的计算就大大简便了。至于简便了多少呢??让我们来认真算下:

查询向量q和所有的类生成表: 4 * 256
假定库里有n个doc, 则需要查表: 4 N次。
所以啊,最后总共需要 4
256 + 4 *N次操作。

image.png

代码
nlist = 50 # 聚类中心个数
k = 4 # 查询相似的k个向量
m = 8 # number of bytes per vector 每个向量都被编码为8个字节大小
quantizer = faiss.IndexFlatL2(d)
index = faiss.IndexIVFPQ(quantizer,d,nlist,m,8) ##注意这个时候 没有相似度 度量参数

index.train(xb)
index.add(xb)
index.nprobe = 3 # 搜索的聚类 个数
D,I = index.search(xq[:5],k)

注意:

  1. IndexIVFPQ不支持构建增量索引
  2. 第一次构建,需要Train + Add两个操作。
    增量操作,则只需要Add
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容