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 std::vector<faiss::idx_t> &ids);
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 std::vector<faiss::idx_t>& ids)
{
	faiss::IDSelectorBatch selector(ids.size(), ids.data());
	return _indexIDMap_ptr->remove_ids(selector);
}

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

5.3 运行

在这里插入图片描述

Logo

一站式 AI 云服务平台

更多推荐