一、媒介
FAISS(Facebook AI Similarity Search)是由Facebook AI Research开发的一个开源库,重要用于高效地举行大规模相似性搜索和聚类操作。重要功能如下:
- 向量索引与搜索:FAISS提供了多种索引和搜索向量的方法,包罗暴力搜索(Flat)、倒排索引(IVF)、分层可导航小世界图(HNSW)和乘积量化(PQ)等。这些方法可以根据应用场景在速度、精确性和内存使用之间举行权衡。
- 支持多种间隔度量:FAISS支持多种间隔度量方式,如L2间隔(欧几里得间隔)、余弦相似度和内积(点积),实用于不同的应用场景。
- CPU和GPU支持:FAISS能够使用CPU和GPU加快索引和搜索过程,在大规模数据集上体现出色,尤其得当需要实时搜索的场景。
具有以下的特点:
- 高效性:FAISS针对大规模数据集举行了优化,能够快速处理数十亿向量。
- 可扩展性:FAISS设计用于处理大规模数据集,能够有用管理数十亿向量。
- 灵活性:FAISS允许用户根据应用需求调整索引和搜索参数,并且可以动态添加、更新和删除向量。
- 开源性:作为开源库,FAISS提供了广泛的定制化和集成本领。
二、基本操作
1、安装版本
faiss分为cpu和gpu两个版本,一般环境下,安装cpu版本就够用了。
- # 安装CPU版本
- pip install faiss-cpu
- # 安装GPU版本(需要CUDA支持)
- pip install faiss-gpu
复制代码 2、导入库并设置基本参数
- import faiss
- import numpy as np
- # 〇,基本参数设置
- d = 64 # 向量维度
- nb = 100000 # index向量库的数据量
- nq = 1000 # 待检索query的数目
- index_type = 'Flat' # index 类型
- metric_type = faiss.METRIC_INNER_PRODUCT # 度量(相似度/距离)类型
复制代码 引入numpy库是为了后续构造多维数组数据。我们先界说向量索引的重要参数,其说明如下:
- d(dimension),待构造向量的维度
- nb,待构造的向量库中的数据量。
- nq,待构造的检索向量的数据量。
- index_type,索引的范例,索引范例有许多种,可以参考这篇文章(Faiss(4):索引(Index)_faiss index-CSDN博客),重要范例如下:
1、Flat(暴力检索),该方法是Faiss所有index中最精确的,召回率最高的方法,但速度慢,占内存大,一般用于小于50万数据,且内存不紧张的场景中。
2、IVFx Flat(倒排暴力检索),通过倒排的头脑,先聚类中央,通过减少搜索范围,提升搜索服从,相比Flat其速度大大提升,发起百万级向量可以使用。IVFx中的x是k-means聚类中央的个数,好比"IVF100,Flat"。
3、PQx(乘积量化),将一个向量的维度切成x段,举行检索在取交集,得出最后的Top-K,其速度很快,而且占用内存较小,召回率也相对较高。实用于内存及其稀缺,并且需要较快的检索速度,不那么在意召回率。Qx中的x为将向量切分的段数,如" Q16"。
4、LSH(局部敏感哈希),局部敏感哈希依靠碰撞来举行分桶和聚类,聚类较近的归属同一个桶的概率很大。其index占内存很小,检索也比较快,但是召回率非常拉垮,实用于候选向量库非常大,离线检索,内存资源比较稀缺的环境。
5、HNSWx(分层导航),这是一种基于图检索的改进方法,检索速度极快,10亿级别秒出检索结果,召回率也非常惊人,但是内存占用极大,实用于于不在乎内存,并且有充裕的时间来构建index。HNSWx中的x为构建图时每个点最多连接多少个节点。
这里我们为了演示,选用简朴的Flat模式。
- metric_type,相似度间隔,重要有METRIC_L2(欧几里得间隔,L2间隔),METRIC_INNER_PRODUCT(余弦相似度)。这里我们选用余弦相似度。
3、预备向量数据构建库索引
由于FAISS没有默认的向量化模型,我们暂时直接使用向量数据来构建。起首使用numpy库创建向量数据。
- # 一,准备向量库向量
- print('============================== 1,base vector ==============================')
- np.random.seed(1234) #设置种子
- xb = np.random.random((nb, d)).astype('float32') #生成nb行,d列的数组,并转为float32类型
- xb[:, 0] += np.arange(nb) / 1000. # 第一列添加唯一的偏移量
- faiss.normalize_L2(xb) #对向量进行L2归一化
- print('xb.shape = ',xb.shape,'\n')
- #============================== 1,base vector ==============================
- #xb.shape = (100000, 64)
复制代码 这里预备了100000行64维的向量数据。接下来就添加到库中,并构建索引。
- # 二,构建向量库索引
- print('============================== 2,create&train ==============================')
- index = faiss.index_factory(d,index_type,metric_type) #通过ndex_factory构建索引,等价于 faiss.IndexFlatIP(d)
- print('index.is_trained=',index.is_trained) # 输出为True,代表该类index不需要训练,只需要add向量进去即可
- index.train(xb)
- index.add(xb) # 将向量库中的向量加入到index中
- print('index.ntotal=',index.ntotal,'\n') # 输出index中包含的向量总数,为100000
- #============================== 2,create&train ==============================
- #index.is_trained= True
- #index.ntotal= 100000
复制代码 4、预备查询向量数据并向量检索
有了数据后,就可以检索。这里预备10000行待检索的向量数据。
- # 三,准备查询向量
- print('============================== 3,query vector ==============================')
- xq = np.random.random((nq, d)).astype('float32') #准备nq行,d维的查询向量数组
- xq[:, 0] += np.arange(nq) / 1000. # 待检索的query向量
- faiss.normalize_L2(xq)
- print('xq.shape = ',xq.shape,'\n')
复制代码 检索并返回前5个向量的最相似结果。
- # 四,相似向量查询
- print('============================== 4, search ==============================')
- k = 4 # topK的K值
- D, I = index.search(xq, k) # xq为待检索向量,返回的I为每个待检索query最相似TopK的索引list,D为其对应的距离
- print('nearest vector ids:\n',I[:5],'\n')
- print('metric(distances/scores) to query:\n',D[-5:],'\n')
- #============================== 4, search ==============================
- #nearest vector ids:
- # [[ 207 381 1394 1019]
- # [ 300 911 142 526]
- # [ 838 1541 527 148]
- # [ 196 359 184 466]
- # [ 526 120 917 765]]
- #metric(distances/scores) to query:
- # [[0.87687665 0.86128217 0.85667735 0.85451 ]
- # [0.870294 0.8666884 0.8593493 0.852314 ]
- # [0.86291504 0.8580746 0.8538497 0.84994483]
- # [0.86920005 0.8660047 0.8647547 0.8634623 ]
- # [0.85396254 0.8491496 0.84744585 0.8432566 ]]
复制代码 5、新增和删除索引向量
对于现有的向量数据,可以通过add和remove_ids指令举行新增和删除。
- # 五,增删索引向量
- print('============================== 5, add&remove ==============================')
- xa = np.random.random((10000, d)).astype('float32') #新增10000行
- xa[:, 0] += np.arange(len(xa)) / 1000.
- faiss.normalize_L2(xa)
- index.add(xa)
- print('after add, index.ntotal=',index.ntotal)
- index.remove_ids(np.arange(1000,1111)) #删除1000-1111的向量
- print('after remove, index.ntotal=',index.ntotal,'\n')
- #============================== 5, add&remove ==============================
- #after add, index.ntotal= 110000
- #after remove, index.ntotal= 109889
复制代码 6、生存并加载索引
以上创建好的索引可以持久化生存到本地,并重新读取继续操作。
- # 六,保存加载索引
- print('============================== 6, write&read ==============================')
- faiss.write_index(index, "large.index")
- index_loaded = faiss.read_index('large.index')
- print('index_loaded.ntotal=', index_loaded.ntotal)
复制代码 三、案例实践
接下来,我们将一些短语,通过嵌入式模型向量化后,再通过FAISS举行检索。其输入和输出短语与上一篇一样。
起首安装sentence_transformers库,使用它加载预训练嵌入式模型,
- pip install sentence_transformers
复制代码 嵌入式模型使用all-MiniLM-L6-v2,与上篇保持同等。如果本地没有该模型,会自动从hungface上下载,如果网络的缘故原由,要使用其国内的镜像。
- import os
- #使用hf的国内镜像,设置为环境变量
- os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
- from sentence_transformers import SentenceTransformer
- import faiss
- import numpy as np
- # 加载预训练模型,使用ll-MiniLM-L6-v2最为embedding模型
- model = SentenceTransformer("all-MiniLM-L6-v2")
复制代码 对待加载以及待检索的短语举行向量化
- # 待加载的短语
- corpus = [
- "海内存知己,天涯若比邻",
- "大漠孤烟直,长河落日圆",
- "春眠不觉晓,处处闻啼鸟",
- "会当凌绝顶,一览众山小",
- "海上生明月,天涯共此时",
- "举头望明月,低头思故乡",
- "山重水复疑无路,柳暗花明又一村",
- "不识庐山真面目,只缘身在此山中",
- "采菊东篱下,悠然见南山",
- "谁言寸草心,报得三春晖",
- "忽如一夜春风来,千树万树梨花开",
- "落霞与孤鹜齐飞,秋水共长天一色",
- "青山遮不住,毕竟东流去",
- "春江潮水连海平,海上明月共潮生",
- "两岸猿声啼不住,轻舟已过万重山",
- "问渠那得清如许?为有源头活水来",
- "竹外桃花三两枝,春江水暖鸭先知",
- "身无彩凤双飞翼,心有灵犀一点通",
- "众里寻他千百度,蓦然回首,那人却在,灯火阑珊处",
- "莫愁前路无知己,天下谁人不识君"
- ]
- # 通过embedding模型将短语向量化
- corpus_embeddings = model.encode(corpus)
- # 待查询短语
- query = "明月几时有,把酒问青天"
- # 通过embedding模型将查询短语向量化
- query_embedding = model.encode([query])
复制代码 初始化faiss索引,并将短句的向量数据添加到索引中
- # 初始化 Faiss 索引
- dimension = corpus_embeddings.shape[1] # 向量维度
- print("dimension:", dimension)
- index = faiss.IndexFlatL2(dimension) # 使用 L2 距离
- index.add(corpus_embeddings) # 添加语料库向量到索引
复制代码 检索待查询语句的相似语句,并返回前top5
- # 检索与查询向量最接近的前 k 个结果
- k = 5 # 返回前 5 个最相似的结果
- distances, indices = index.search(query_embedding, k)
- # 打印检索结果
- print("Query:", query)
- print("Top K Results:")
- for i, idx in enumerate(indices[0]):
- print(f"Rank {i+1}: {corpus[idx]} (Distance: {distances[0][i]:.4f})")
复制代码 打印的结果如下:
- Query: 明月几时有,把酒问青天
- Top K Results:
- Rank 1: 举头望明月,低头思故乡 (Distance: 0.4828)
- Rank 2: 海上生明月,天涯共此时 (Distance: 0.5092)
- Rank 3: 青山遮不住,毕竟东流去 (Distance: 0.5768)
- Rank 4: 海内存知己,天涯若比邻 (Distance: 0.5937)
- Rank 5: 莫愁前路无知己,天下谁人不识君 (Distance: 0.5976)
复制代码 可以看到与前一篇的结果同等。
四、总结
本文先容了FAISS的基本用法,并通过案例实践,演示了嵌入向量,创建索引以及检索的过程。
附件
向量数据库技能系列一-基本原理
向量数据库技能系列二-Milvus先容
向量数据库技能系列三-Chroma先容
向量数据库技能系列四-FAISS先容
向量数据库技能系列五-Weaviate先容
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |