Faiss使用教程详解

详情将官方Github地址:

https://github.com/facebookresearch/faiss/wiki/Faiss-indexes

Faiss是一个速度很快的向量Top k的召回算法库,适用于不同的应用场景,由于Faiss有各种索引构建的方式,本文主要简单介绍倒排索引的增、删、改、查的功能,以及Faiss算法的准确性和检索速度进行了介绍。

增加数据(注:插入相同id的数据,不是把之前的数据进行更新,而是又增加了一天相同id的数据)

# -*- coding: utf-8 -*-
import math
import time
import faiss
import numpy as np

# 增
## 验证新增一个新的id:vector是否能够同步(注:此时的id是之前没有出现过的)





d = 768  # 向量维数

data = [[i] * d for i in range(2000)]
data = np.array(data).astype('float32')  # 注意,只能插入float32类型的向量 
ids = np.arange(0, 2000)
data_length=len(ids)   # 自定义向量的Id


nlist = int(4 * math.sqrt(data_length))  # 聚类中心的个数
time1 = time.time()

quantizer = faiss.IndexFlatL2(d)  # 内部的索引方式依然不变
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)  # 倒排索引
index.train(data)  # 注意,倒排索引一定要进行train
index.add_with_ids(data,ids)
print(index.is_trained)
time2 = time.time()
print(f'构建索引插入数据的时间为{time2 - time1}')


query_vector = np.array([[1] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)  # 1代表返回的结果数
print(f'全1向量的最近的向量id为{ind}')
print(dis)



add_data = np.array([[1000] * 768]).astype('float32')
add_id = np.array([3000])
index.add_with_ids(add_data, add_id)
print(f'\n注意插入数据后的样本总数为{index.ntotal}')

query_vector = np.array([[1000] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'新插入的全1000向量的最近的向量id为{ind}')
print(dis)

query_vector = np.array([[1] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n全1向量的最近的向量id为{ind}')
print(dis)



根据自定Id对数据进行删除(注:如果一个Id对应多条数据,则该Id下的所有数据都删除了)



d = 768  # 维数

data = [[i] * d for i in range(500)]
data = np.array(data).astype('float32')
ids = np.arange(2000, 2500)


time1 = time.time()

index = faiss.index_factory(d, "IDMap,Flat", faiss.METRIC_L2)  # 不使用倒排索引,暴力搜索,此时不需要进行train
index.add_with_ids(data,ids)
print(index.is_trained)
time2 = time.time()
print(f'构建索引插入数据的时间为{time2 - time1}')

query_vector = np.array([[2] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n初始状态下,全2向量的最近的向量id为{ind}')
print(dis)

query_vector = np.array([[1] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n初始状态下,全1向量的最近的向量id为{ind}')
print(dis)

query_vector = np.array([[0] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n初始状态下,全0向量的最近的向量id为{ind}')
print(dis)

print('\n下面删除id为2000的数据,也就是全0的那条vector')
index.remove_ids(np.array([2000,2001]))

query_vector = np.array([[0] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n删除操作后,全0向量的最近的向量id为{ind}')
print(dis)


query_vector = np.array([[1] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n进行删除操作后,全1向量的最近的向量id为{ind}')
print(dis)

query_vector = np.array([[2] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n初始状态下,全2向量的最近的向量id为{ind}')
print(dis)

对数据进行更新(先删除,再添加)



d = 768  # 维数

data = [[i] * d for i in range(500)]
data = np.array(data).astype('float32')
# centers = int(8 * math.sqrt(len(data)))
ids = np.arange(2000, 2500)



nlist = 100
m = 8

time1 = time.time()

quantizer = faiss.IndexFlatL2(d)  # 内部的索引方式依然不变
# index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)  # PQ方式,每个向量都被编码为8个字节大小
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)  # 这个索引支持add_with_ids
index.train(data)
index.add_with_ids(data,ids)
print(index.is_trained)
time2 = time.time()
print(f'构建索引插入数据的时间为{time2 - time1}')

# index.nprobe = 10  # 选择n个维诺空间进行索引,

query_vector = np.array([[1] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'初始状态全1向量的最近的向量id为{ind}')
print(dis)

query_vector = np.array([[1000] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n初始状态全1000向量的最近的向量id为{ind}')
print(dis)


query_vector = np.array([[0] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n删除全0向量之前全0向量最近的index为{ind}')
print(dis)

index.remove_ids(np.array([2000]))
print('\n注意删除的向量id为2000,将全0向量进行删除')
print(f'样本的总数为{index.ntotal}')


query_vector = np.array([[0] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n删除全0向量之后全0向量最近的index为{ind}')
print(dis)

add_data = np.array([[1000] * 768]).astype('float32')
add_id = np.array([2000])
index.add_with_ids(add_data, add_id)
print(f'\n注意,此时将index为2000的数据进行了更新,更新的数据为全1000,插入数据后的样本总数为{index.ntotal}')


query_vector = np.array([[1000] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n更新后的全1000向量的最近的向量id为{ind}')
print(dis)

query_vector = np.array([[1] * 768]).astype('float32')
dis, ind = index.search(query_vector, 1)
print(f'\n全1向量的最近的向量id为{ind}')
print(dis)


时间消耗以及准确性测试

import sys
import time
import numpy as np
import faiss
import math


d = 768          #维数
n_data = 100000   
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')  # 注意,只能是float32
print(f'向量矩阵的所占内存为{sys.getsizeof(data)/1024/1024}')
ids = np.arange(n_data)



# nlist = 100
nlist = int(4*math.sqrt(n_data))


time1 = time.time()

quantizer = faiss.IndexFlatL2(d)  # 内部的索引方式依然不变
# m = 8
# index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)  # PQ方式,每个向量都被编码为8个字节大小
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)  # 这个索引支持add_with_ids
index.train(data)
index.add_with_ids(data,ids)
index.nprobe=6
print(index.is_trained)
time2 = time.time()
print(f'构建索引插入数据的时间为{time2 - time1}')
print(f'数据总数为{index.ntotal}') 


start_time=time.time()
for i in range(10000):
    query_vector=np.random.rand(1, 768).astype('float32')
    dis,ind=index.search(query_vector,1)
# # print(dis)
# # print(ind)
end_time=time.time()
print(f'10000条随机生成向量查询花费时间为{end_time-start_time}')

# 测试准确性
start_time = time.time()
distances,labels = index.search(data,1)
print("Recall for the data:", np.mean(labels.reshape(-1) == np.arange(len(data))), "\n")
end_time = time.time()
print(f'花费时间为{end_time - start_time}')

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值