目录
- 1. Faiss简介
- 2. FAISS 的主要特点
- 2.1 高效性
- 2.2 支持多种索引类型
- 2.3 灵活性
- 2.4 GPU 加速
- 2.5 易于集成
- 3. 应用场景
- 4. 安装
- 4.1 安装依赖
- 4.2 编译源码
- 4.2.1 下载Faiss源码
- 4.2.2 编译
- 5. Demo
- 5.1 代码
- 5.2 编译
- 5.3 运行
1. Faiss简介
FAISS(Facebook AI Similarity Search)是由 Facebook AI Research 开发的一个高效的相似性搜索库,主要用于大规模向量数据的相似性搜索和聚类。它特别适合处理高维数据,如图像特征、文本嵌入等。
2. FAISS 的主要特点
2.1 高效性
FAISS 采用了多种高效的算法和数据结构,能够快速进行最近邻搜索(Nearest Neighbor Search),即使在大规模数据集上也能保持较好的性能。
2.2 支持多种索引类型
FAISS 提供了多种索引结构,包括:
- 平面索引(Flat Index):简单直接,适合小规模数据。
- 倒排索引(IVF):适合大规模数据,使用聚类来减少搜索空间。
- HNSW(Hierarchical Navigable Small World):一种基于图的索引,适合高维数据。
- PQ(Product Quantization):通过量化减少内存占用,适合处理大规模数据。
2.3 灵活性
FAISS 支持多种数据类型,包括浮点数和二进制数据。用户可以根据需求选择合适的索引类型和参数。
2.4 GPU 加速
FAISS 支持 GPU 加速,使得在处理非常大的数据集时能够显著提高性能。
2.5 易于集成
FAISS 提供了 C++ 和 Python 接口,方便用户在不同的应用场景中集成和使用。
3. 应用场景
FAISS 广泛应用于以下领域:
- 图像检索:通过特征向量快速查找相似图像。
- 自然语言处理:处理文本嵌入,进行相似句子或文档的检索。
- 推荐系统:根据用户历史行为推荐相似项目。
- 聚类分析:对高维数据进行聚类。
4. 安装
4.1 安装依赖
该项目依赖于BLAS 组件 OpenBLAS 和 IntelMKL BLAS 【官方支持】
4.2 编译源码
4.2.1 下载Faiss源码
Faiss源码地址
4.2.2 编译
cmake .. -DFAISS_ENABLE_GPU=OFF -DFAISS_ENABLE_PYTHON=OFF -DBUILD_SHARED_LIBS=ON -DFAISS_ENABLE_C_API=ON -DCMAKE_BUILD_TYPE=Release -DFAISS_ENABLE_CUVS=OFF -DBUILD_TESTING=OFF
基本参数介绍:
-DFAISS_ENABLE_GPU=ON *是否构建GPU支持
-DFAISS_ENABLE_PYTHON=OFF 是否构建Python 支持
-DBUILD_TESTING=ON 是否编译Testing 【依赖于googletest】
-DFAISS_ENABLE_C_API=ON 是否需要提高CAPI支持
-DCMAKE_BUILD_TYPE=Release 编译类型
-DBUILD_SHARED_LIBS=ON 是否生成动态库
GPU版本编译请参考:编译Faiss-gpu【InterMKL】C++
5. Demo
5.1 代码
FaissDB.h
#ifndef FACERECOGNITION_CPP_FAISSDB_H
#define FACERECOGNITION_CPP_FAISSDB_H
//faiss
#include <faiss/IndexHNSW.h>
#include <faiss/IndexIDMap.h>
#include <faiss/index_io.h>
#include <vector>
#include <string>
#include <thread>
#include <mutex>
#include <filesystem>
class FaissDB {
public:
FaissDB(int dim, const std::string &db_name);
~FaissDB();
int insert(int n, const std::vector<float> &vec, const std::vector<faiss::idx_t> &ids);
int query(int n, const std::vector<float> &vec, int topk, std::vector<float> &distances, std::vector<faiss::idx_t> &indices);
int remove(const faiss::IDSelector& sel);
private:
std::unique_ptr<faiss::IndexHNSWFlat> _indexHNSW_ptr;
std::unique_ptr<faiss::Index> _indexIDMap_ptr;
std::mutex _rw_lock;
std::string _db_name;
};
#endif //FAISSDB_H
FaissDB.cpp
#include "FaissDB.h"
#include <iostream>
using namespace std;
FaissDB::FaissDB(int dim, const std::string &db_name)
{
this->_db_name = db_name;
if (std::filesystem::exists(db_name))
{
_indexIDMap_ptr = unique_ptr<faiss::Index>(faiss::read_index(db_name.c_str()));
std::cout<<"load faiss data success from file"<<std::endl;
}
else
{
_indexHNSW_ptr = unique_ptr<faiss::IndexHNSWFlat>(new faiss::IndexHNSWFlat(dim, 200));
_indexIDMap_ptr= unique_ptr<faiss::IndexIDMap>(new faiss::IndexIDMap(_indexHNSW_ptr.get()));
}
}
FaissDB::~FaissDB()
{
}
int FaissDB::insert(int n, const std::vector<float>& vec, const std::vector<faiss::idx_t>& ids)
{
std::lock_guard<std::mutex> lk(_rw_lock);
_indexIDMap_ptr->add_with_ids(n, vec.data(), ids.data());
faiss::write_index(_indexIDMap_ptr.get(), _db_name.c_str());
return 0;
}
int FaissDB::query(int n, const std::vector<float>& vec, int topk, std::vector<float>& distances,
std::vector<faiss::idx_t>& indices)
{
std::lock_guard<std::mutex> lk(_rw_lock);
if (_indexIDMap_ptr->ntotal == 0)
{
std::cerr << "Index is empty. Please add data before searching." << std::endl;
return -1;
}
_indexIDMap_ptr->search(n, vec.data(), topk, distances.data(), indices.data());
return 0;
}
int FaissDB::remove(const faiss::IDSelector& sel)
{
return _indexIDMap_ptr->remove_ids(sel);
}
faiss-demo.cpp
#include "FaissDB.h"
using namespace std;
int main(){
const int d = 128;
const int n = 5;
std::vector<float> data(d * n);
for (int i = 0; i < d * n; ++i) {
data[i] = static_cast<float>(rand()) / RAND_MAX;
}
std::vector<faiss::idx_t> ids = {101, 102, 103, 104, 105};
int M = 16;
FaissDB faiss_db(d, "faiss.index");
faiss_db.insert(n, data.data(), ids.data());
const int k = 3;
std::vector<float> query(d);
for (int i = 0; i < d; ++i) {
query[i] = static_cast<float>(rand()) / RAND_MAX;
}
std::vector<faiss::idx_t> result_ids(k);
std::vector<float> distances(k);
faiss_db.query(1, query.data(), k, distances.data(), result_ids.data());
std::cout << "search result:" << std::endl;
for (int i = 0; i < k; ++i) {
std::cout << "vector ID: " << result_ids[i] << ", dis: " << distances[i] << std::endl;
}
return 0;
}
这里我封装了3个接口,索引类型为HNSW,insert的时候指定id。
注意:如果要使用add_with_ids方法插入index,必须使用faiss::IndexIDMap封装一下,否则会报错
5.2 编译
g++ faiss-demo.cpp FaissDB.cpp -o faiss-demo -I/usr/local/include -L/usr/local/lib -lfaiss -fopenmp -lopenblas