【faiss】用于高效相似性搜索和聚类的C++库 | 源码详解与编译安装

目录

●Faiss

■faiss::Index

■faiss::read_index

■faiss::write_index

■faiss::IndexFlatIP

■faiss::Index::idx_t

■完整代码示例

■总结

●Faiss库源码详解

●Faiss库编译安装



Faiss(Facebook AI Similarity Search) 是一个用于高效相似性搜索和聚类的 C++ 库,支持在大规模向量集合中快速查找最近邻(ANN, Approximate Nearest Neighbor)。

●Faiss

■faiss::Index

核心基类:Faiss中最核心的抽象类,表示一个向量索引结构。所有索引类型都继承自faiss::Index;提供统一接口:添加向量、搜索最近邻、保存/加载索引等。

常用方法

cpp 复制代码
/**
 * 向索引中添加 n 个维度为 d 的向量。
 *
 * 向量会被隐式地分配标签,标签范围为 ntotal 到 ntotal + n - 1。
 * 该函数将输入的向量切分为比 blocksize_add 小的块,并调用 add_core 函数进行添加。
 *
 * @param n      向量的数量
 * @param x      输入矩阵,大小为 n * d
 */
virtual void add(idx_t n, const float* x) = 0;
cpp 复制代码
/**
 * 对索引中的 n 个 d 维向量进行查询。
 *
 * 此函数用于在索引中搜索每个输入向量的 k 个最近邻向量。
 * 如果某次查询的结果不足,则结果数组将用 -1 填充。
 *
 * @param n           输入向量的数量
 * @param x           要搜索的输入向量数组,大小为 n * d
 * @param k           需要检索的最近邻向量数量
 * @param distances   输出的距离数组,存储每个查询向量与最近邻向量之间的距离,大小为 n*k
 * @param labels      输出的标签数组,存储最近邻向量的标签,大小为 n*k
 * @param params      可选的搜索参数,用于调整搜索行为(默认为 nullptr)
 */
virtual void search(
        idx_t n,
        const float* x,
        idx_t k,
        float* distances,
        idx_t* labels,
        const SearchParameters* params = nullptr) const = 0;

■faiss::read_index

用于从磁盘文件(二进制格式)中加载一个已保存的 Faiss 索引。支持跨平台使用(Linux/Windows/macOS)。

函数原型:

cpp 复制代码
faiss::Index* faiss::read_index(const char* fname, int32_t io_flags = 0);
  • fname: 文件路径;
  • io_flags: 可选标志,例如IO_FLAG_MMAP(内存映射加载);

Index* read_index(const char* fname, int io_flags = 0);

Index* read_index(FILE* f, int io_flags = 0);

Index* read_index(IOReader* reader, int io_flags = 0);

使用 示例

cpp 复制代码
faiss::Index* index = faiss::read_index("my_index.index");

■faiss::write_index

将当前索引对象保存到磁盘文件。支持跨平台使用(Linux/Windows/macOS)。

函数原型:

cpp 复制代码
void faiss::write_index(const Index* index, const char* fname);
  • index: 要保存的索引对象;
  • fname: 保存的文件路径;

使用示例:

cpp 复制代码
faiss::write_index(index, "my_index.index");

■faiss::IndexFlatIP

这是一个精确搜索 索引,使用****内积(Inner Product)****作为相似度度量。适用于向量已经归一化的情况,内积等价于余弦相似度。

暴力搜索 :无压缩/量化,完整计算所有向量距离。

精度最高,速度最慢(时间复杂度 O(n))。

使用示例:

cpp 复制代码
int d = 64; // 向量维度

faiss::IndexFlatIP index(d); // 创建一个基于内积的索引,64维向量

特点:不使用近似,精度高;搜索复杂度为 O(N),适合小数据集,需要 100% 准确率的场景。

■faiss::Index::idx_t

这是 Faiss 中用于表示索引编号的类型定义,通常为int64_t,64 位整数(支持超大索引)。

表示向量 ID(索引),用于add()、search()等函数的参数。

使用示例:

cpp 复制代码
faiss::Index::idx_t n = 1000; 

■完整代码示例

test.cpp内容如下:

cpp 复制代码
#include <faiss/IndexFlat.h>
#include <faiss/index_io.h>
#include <iostream>
#include <vector>

int main() {
    // 向量维度
    int d = 64;

    // 创建索引
    faiss::IndexFlatIP index(d);

    // 准备训练数据(1000 个向量)
    size_t nb = 1000;
    std::vector<float> xb(d * nb);
    for (size_t i = 0; i < xb.size(); i++) {
        xb[i] = drand48();  // 随机初始化
    }

    // 添加向量到索引中,添加完成后,索引结构内部会组织这些向量,以便在后续的 search 操作中快速找到最近邻。
    // nb:要添加的向量数量(本例中是 1000 个向量)。
    // xb.data():指向存储所有向量数据的内存起始地址。
    // xb 是一个 std::vector<float>,其中存储了 nb 个向量,每个向量有 d 个浮点数
    index.add(nb, xb.data());

    // 保存索引到文件
    faiss::write_index(&index, "index.faiss");

    // 重新加载索引
    faiss::Index* loaded_index = faiss::read_index("index.faiss");

    // 查询向量(1 个)
    std::vector<float> xq(d);
    // 这个循环为 xq 向量填充了 64 个随机浮点数值,构造出一个随机的查询向量。
    // 假设 d = 5,运行这段代码后,xq 可能是这样一个向量:xq = [0.123, 0.456, 0.789, 0.012, 0.345]
    for (int i = 0; i < d; i++) {
        xq[i] = drand48();  // 随机查询向量
    }

    // 搜索最近邻(top-10)
    int k = 10;
    std::vector<float> distances(k);
    std::vector<faiss::idx_t> labels(k);
    
    // 已经训练好的索引 loaded_index,现在有一个查询向量 xq
    // 在这个索引里找出与它最相似的前 k 个向量,并把它们的距离和 ID 分别保存在 distances 和 labels 里
    loaded_index->search(1, xq.data(), k, distances.data(), labels.data());

    // 输出结果
    std::cout << "Top-" << k << " 最近邻 ID 和距离:" << std::endl;
    for (int i = 0; i < k; i++) {
        std::cout << "ID: " << labels[i] << ", 距离: " << distances[i] << std::endl;
    }

    // 清理资源
    delete loaded_index;

    return 0;
}

编译:

cpp 复制代码
g++ test.cpp -o test -lfaiss -lopenblas -fopenmp
cpp 复制代码
./test

运行结果示例:

■总结

|---------------------|-----------------------|
| 类/函数名 | 作用 |
| faiss::Index | 所有索引的基类,提供统一接口 |
| faiss::read_index | 从文件加载索引 |
| faiss::write_index | 将索引保存到文件 |
| faiss::IndexFlatIP | 精确搜索索引,使用内积(适用于归一化向量) |
| faiss::Index::idx_t | 表示索引编号的类型,通常为int64_t |

●Faiss库源码详解

  • faiss/index_io.h
cpp 复制代码
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// I/O code for indexes

#ifndef FAISS_INDEX_IO_H
#define FAISS_INDEX_IO_H

#include <cstdio>
#include <string>
#include <typeinfo>
#include <vector>

/** I/O functions can read/write to a filename, a file handle or to an
 * object that abstracts the medium.
 *
 * The read functions return objects that should be deallocated with
 * delete. All references within these objectes are owned by the
 * object.
 */

namespace faiss {

// 前向声明常用的数据结构
struct Index;                // 索引基类
struct IndexBinary;          // 二值索引
struct VectorTransform;      // 向量变换
struct ProductQuantizer;     // 乘积量化器
struct IOReader;             // 输入流抽象
struct IOWriter;             // 输出流抽象
struct InvertedLists;        // 倒排列表

/// IO标志位:跳过图索引的存储数据
const int IO_FLAG_SKIP_STORAGE = 1;

// 将Index对象写入文件
void write_index(const Index* idx, const char* fname, int io_flags = 0);
void write_index(const Index* idx, FILE* f, int io_flags = 0);
void write_index(const Index* idx, IOWriter* writer, int io_flags = 0);

// 将二值索引写入文件
void write_index_binary(const IndexBinary* idx, const char* fname);
void write_index_binary(const IndexBinary* idx, FILE* f);
void write_index_binary(const IndexBinary* idx, IOWriter* writer);

// read_index 支持的标志位,仅部分索引类型支持
const int IO_FLAG_READ_ONLY = 2;                         // 只读模式
const int IO_FLAG_ONDISK_SAME_DIR = 4;                   // 文件路径相对于索引文件目录
const int IO_FLAG_SKIP_IVF_DATA = 8;                     // 不加载 IVF 数据到内存,仅保留列表大小
const int IO_FLAG_SKIP_PRECOMPUTE_TABLE = 16;            // 不预计算查找表
const int IO_FLAG_PQ_SKIP_SDC_TABLE = 32;                // 不计算 SDC 表,禁用 PQ 相关距离计算
const int IO_FLAG_MMAP = IO_FLAG_SKIP_IVF_DATA | 0x646f0000; // 使用内存映射读取数据

// 从文件/流中读取Index对象
Index* read_index(const char* fname, int io_flags = 0);
Index* read_index(FILE* f, int io_flags = 0);
Index* read_index(IOReader* reader, int io_flags = 0);

// 从文件/流中读取二值索引
IndexBinary* read_index_binary(const char* fname, int io_flags = 0);
IndexBinary* read_index_binary(FILE* f, int io_flags = 0);
IndexBinary* read_index_binary(IOReader* reader, int io_flags = 0);

// 写入VectorTransform对象
void write_VectorTransform(const VectorTransform* vt, const char* fname);
void write_VectorTransform(const VectorTransform* vt, IOWriter* f);

// 从文件/流中读取VectorTransform对象
VectorTransform* read_VectorTransform(const char* fname);
VectorTransform* read_VectorTransform(IOReader* f);

// 写入ProductQuantizer对象
void write_ProductQuantizer(const ProductQuantizer* pq, const char* fname);
void write_ProductQuantizer(const ProductQuantizer* pq, IOWriter* f);

// 从文件/流中读取ProductQuantizer对象
ProductQuantizer* read_ProductQuantizer(const char* fname);
ProductQuantizer* read_ProductQuantizer(IOReader* reader);

// 写入倒排列表
void write_InvertedLists(const InvertedLists* ils, IOWriter* f);

// 从输入流中读取倒排列表
InvertedLists* read_InvertedLists(IOReader* reader, int io_flags = 0);

} // namespace faiss

#endif
  • faiss/IndexFlat.h
cpp 复制代码
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// -*- c++ -*-

#ifndef INDEX_FLAT_H
#define INDEX_FLAT_H

#include <vector>

#include <faiss/IndexFlatCodes.h>

namespace faiss {

/**
 * IndexFlat 是一个存储完整向量并执行穷举搜索的索引结构。
 * 它继承自 IndexFlatCodes。
 */
struct IndexFlat : IndexFlatCodes {
    /**
     * 构造函数
     * @param d 输入向量的维度
     * @param metric 距离度量类型,默认为 L2
     */
    explicit IndexFlat(
        idx_t d, ///< 输入向量的维度
        MetricType metric = METRIC_L2);

    /**
     * 搜索与查询向量最接近的 k 个向量
     * @param n 查询向量的数量
     * @param x 查询向量,大小为 n * d
     * @param k 需要返回的最近邻数量
     * @param distances 输出的距离,大小为 n * k
     * @param labels 输出的标签,大小为 n * k
     * @param params 可选的搜索参数
     */
    void search(
        idx_t n,
        const float* x,
        idx_t k,
        float* distances,
        idx_t* labels,
        const SearchParameters* params = nullptr) const override;

    /**
     * 在指定半径范围内搜索向量
     * @param n 查询向量的数量
     * @param x 查询向量,大小为 n * d
     * @param radius 搜索半径
     * @param result 输出范围搜索结果
     * @param params 可选的搜索参数
     */
    void range_search(
        idx_t n,
        const float* x,
        float radius,
        RangeSearchResult* result,
        const SearchParameters* params = nullptr) const override;

    /**
     * 重构指定索引处的向量
     * @param key 要重构的向量的索引
     * @param recons 输出重构的向量
     */
    void reconstruct(idx_t key, float* recons) const override;

    /**
     * 计算与部分向量的子集之间的距离
     * @param x 查询向量,大小为 n * d
     * @param labels 每个查询向量需要比较的向量索引,大小为 n * k
     * @param distances 对应的输出距离,大小为 n * k
     */
    void compute_distance_subset(
        idx_t n,
        const float* x,
        idx_t k,
        float* distances,
        const idx_t* labels) const;

    // 获取指向浮点数据的指针
    float* get_xb() {
        return (float*)codes.data();
    }
    const float* get_xb() const {
        return (const float*)codes.data();
    }

    IndexFlat() {} // 默认构造函数

    /**
     * 获取距离计算器
     * @return 返回一个 FlatCodesDistanceComputer 实例
     */
    FlatCodesDistanceComputer* get_FlatCodesDistanceComputer() const override;

    /* 独立编解码接口(在这种情况下只是内存拷贝) */
    void sa_encode(idx_t n, const float* x, uint8_t* bytes) const override;

    void sa_decode(idx_t n, const uint8_t* bytes, float* x) const override;
};

/**
 * IndexFlatIP 是 IndexFlat 的特例,使用内积(Inner Product)作为距离度量
 */
struct IndexFlatIP : IndexFlat {
    explicit IndexFlatIP(idx_t d) : IndexFlat(d, METRIC_INNER_PRODUCT) {}
    IndexFlatIP() {}
};

/**
 * IndexFlatL2 是 IndexFlat 的特例,使用 L2 范数作为距离度量
 * 它还支持缓存 L2 范数以加速计算
 */
struct IndexFlatL2 : IndexFlat {
    std::vector<float> cached_l2norms; ///< L2 范数缓存

    /**
     * 构造函数
     * @param d 输入向量的维度
     */
    explicit IndexFlatL2(idx_t d) : IndexFlat(d, METRIC_L2) {}
    IndexFlatL2() {}

    /**
     * 获取距离计算器(针对 L2 范数缓存进行优化)
     * @return 返回一个 FlatCodesDistanceComputer 实例
     */
    FlatCodesDistanceComputer* get_FlatCodesDistanceComputer() const override;

    void sync_l2norms(); ///< 计算并缓存 L2 范数
    void clear_l2norms(); ///< 清除 L2 范数缓存
};

/**
 * IndexFlat1D 是 IndexFlatL2 的优化版本,专门用于 1D 向量
 */
struct IndexFlat1D : IndexFlatL2 {
    bool continuous_update = true; ///< 是否连续更新排列?

    std::vector<idx_t> perm; ///< 排序后的数据库索引

    explicit IndexFlat1D(bool continuous_update = true); ///< 构造函数

    /**
     * 如果未启用连续更新,则在最后一次添加和首次搜索之间调用此函数
     */
    void update_permutation();

    void add(idx_t n, const float* x) override; ///< 添加向量
    void reset() override; ///< 重置索引

    /**
     * 搜索最近邻(返回的是 L1 距离,而不是 L2)
     * @param n 查询向量的数量
     * @param x 查询向量,大小为 n * d
     * @param k 需要返回的最近邻数量
     * @param distances 输出的距离,大小为 n * k
     * @param labels 输出的标签,大小为 n * k
     * @param params 可选的搜索参数
     */
    void search(
        idx_t n,
        const float* x,
        idx_t k,
        float* distances,
        idx_t* labels,
        const SearchParameters* params = nullptr) const override;
};

} // namespace faiss
#endif

●Faiss库编译安装

编译安装

安装依赖

cpp 复制代码
#安装依赖

sudo apt-get update

sudo apt-get install libomp-dev
cpp 复制代码
# 安装 OpenBLAS

sudo apt-get install libopenblas-dev

下载faiss源码并编译安装

bash 复制代码
#下载源码

git clone https://github.com/facebookresearch/faiss.git

cd faiss

mkdir build && cd build
​
bash 复制代码
cmake .. -DFAISS_ENABLE_CXX=ON -DFAISS_ENABLE_PYTHON=OFF -DBUILD_SHARED_LIBS=ON  -DCMAKE_BUILD_TYPE=Release  -DFAISS_ENABLE_GPU=OFF  -DOpenMP_CXX_FLAGS="-fopenmp"  -DOpenMP_CXX_LIB_NAMES="gomp" -DOpenMP_gomp_LIBRARY=/usr/lib/x86_64-linux-gnu/libgomp.so.1 -DBUILD_TESTING=OFF 
bash 复制代码
make -j$(nproc)  && sudo make install

报错Could NOT find OpenMP_CXX (missing: OpenMP_gomp_LIBRARY) (found version"4.5")

解决方法 :-DOpenMP_gomp_LIBRARY=/usr/lib/x86_64-linux-gnu/libgomp.so.1

默认安装路径为/usr/local/,头文件位于/usr/local/include/faiss/,库文件位于/usr/local/lib/libfaiss.a


交叉编译到Android

【Android】交叉编译faiss库 | 问题解决-CSDN博客


至此,本文的内容就结束了。

相关推荐
java叶新东老师14 分钟前
git stash 命令详解
linux·运维·flink
朝朝又沐沐1 小时前
算法竞赛阶段二-数据结构(36)数据结构双向链表模拟实现
开发语言·数据结构·c++·算法·链表
写bug的羊羊1 小时前
CentOS 9 配置国内 YUM 源
linux·运维·centos
逝雪Yuki2 小时前
Leetcode——287. 寻找重复数
c++·leetcode·二分查找·双指针·环形链表
李永奉3 小时前
C语言-流程控制语句:for循环语句、while和do…while循环语句;
c语言·开发语言·c++·算法
打码农的篮球4 小时前
STL——list
开发语言·c++·list
Johny_Zhao4 小时前
CentOS Stream 9上部署FTP应用服务的两种方法(传统安装和docker-compose)
linux·网络安全·信息安全·kubernetes·云计算·containerd·ftp·yum源·系统运维
守望时空334 小时前
RustDesk搭建指南
linux
C++ 老炮儿的技术栈5 小时前
在 Scintilla 中为 Squirrel 语言设置语法解析器的方法
linux·运维·c++·git·ubuntu·github·visual studio
@蓝莓果粒茶5 小时前
LeetCode第350题_两个数组的交集II
c++·python·学习·算法·leetcode·职场和发展·c#