跳转到内容

FAISS 向量搜索库教程大纲

不是数据库,是搜索引擎——FAISS,用 C++ 的速度做十亿级向量检索


📖 教程概述

如果你已经学过本系列的 Chroma、pgvector 和 Milvus 教程,你可能会问:为什么还要学 FAISS?它们不都能做向量搜索吗?答案在于一个关键区别——FAISS 不是数据库,是库。Chroma、pgvector、Milvus 都是"数据库":它们管存储、管索引、管查询、管持久化,你把数据交给它们,它们帮你搞定一切。FAISS 只做一件事——在内存中快速搜索向量——不管存储、不管持久化、不管查询语言、不管分布式。但正因为它只做这一件事,它做到了极致:单机十亿级向量毫秒级检索、GPU 加速、最丰富的索引类型和量化算法。

FAISS 是向量搜索领域的"瑞士军刀"——Milvus 的底层索引引擎就是 FAISS,很多向量数据库的索引构建也依赖 FAISS。理解 FAISS,你就理解了向量搜索的底层原理。本教程不要求你把 FAISS 用于生产(生产用 Milvus 或 pgvector 更合适),而是通过 FAISS 深入理解向量索引的算法原理——IVFFlat 的聚类搜索、HNSW 的图导航、PQ 的乘积量化、OPQ 的优化量化——这些原理在 Milvus 和 pgvector 中同样适用,但 FAISS 让你更直接地接触底层细节。

本教程共 7 章:第 1~2 章建立 FAISS 的基本认知和核心概念,第 3~4 章深入索引类型和量化算法,第 5 章探索 GPU 加速和高级特性,第 6 章实战 RAG 系统,第 7 章讨论 FAISS 与向量数据库的协作。如果你已经学过 Milvus 教程,很多索引概念你已经了解了,本教程会侧重 FAISS 的独特之处——纯内存操作、GPU 加速、更细粒度的索引控制、以及它作为向量数据库底层引擎的角色。


🗺️ 章节规划

第1章:FAISS 概述与快速上手

1.1 FAISS 是什么?向量库 vs 向量数据库的根本区别

  • FAISS 的定位:Facebook AI Research 开发的高效向量相似度搜索库,纯 C++ 实现,Python 绑定
  • 库 vs 数据库的本质区别
    • 库(FAISS):只管内存中的向量搜索,不管存储、不管持久化、不管查询语言
    • 数据库(Milvus/pgvector/Chroma):管存储、管索引、管查询、管持久化、管并发
  • FAISS 的核心能力:丰富的索引类型(20+)、GPU 加速、批量搜索、向量量化、距离计算
  • FAISS 不做什么:不做数据持久化(进程退出数据就没了)、不做标量过滤(没有 WHERE)、不做分布式(单机或单 GPU)、不做查询语言(只有 Python/C++ API)
  • 谁在用 FAISS:Meta 内部搜索推荐、Milvus(底层索引引擎)、很多向量数据库的索引构建依赖 FAISS
  • 与已学教程的关系:FAISS 是 Milvus/pgvector 索引的"底层引擎"——理解 FAISS 就是理解向量索引的底层原理

1.2 安装与环境配置

  • CPU 版本pip install faiss-cpu——纯 CPU,适合学习和中小规模
  • GPU 版本pip install faiss-gpu——需要 CUDA,适合大规模和高性能场景
  • conda 安装conda install -c conda-forge faiss-cpu / faiss-gpu
  • 版本选择:faiss-cpu vs faiss-gpu 的功能差异、CUDA 版本兼容性
  • 验证安装import faiss; print(faiss.__version__)
  • 常见安装问题:CUDA 版本不匹配、conda vs pip 冲突、macOS 不支持 GPU 版本

1.3 Hello World:5 分钟跑通第一个向量搜索

  • 最简示例:创建 IndexFlatL2 → 添加向量 → 搜索 → 解读结果
  • 逐行解析:维度 d、向量矩阵、距离矩阵 D、索引矩阵 I 的含义
  • 距离值的含义:L2 距离的数值范围和直觉解释
  • 与 Milvus/Chroma 的对比:同样的搜索,FAISS 只需要 5 行代码,但需要自己管理数据
  • 常见误区:FAISS 的向量必须是 float32、必须是连续内存(numpy array)、维度必须一致

第2章:FAISS 核心概念

2.1 Index:FAISS 的核心抽象

  • Index 是什么:FAISS 中所有向量索引的基类,封装了"添加向量"和"搜索向量"的接口
  • Index 的核心方法
    • add(vectors):添加向量到索引
    • search(query, k):搜索最近邻,返回距离和索引
    • add_with_ids(vectors, ids):添加向量并指定自定义 ID
    • range_search(query, radius):范围搜索(距离小于阈值的向量)
    • reset():清空索引
    • ntotal:索引中的向量总数
  • Index 的属性d(维度)、ntotal(向量数)、is_trained(是否已训练)
  • Index 的分类:Flat(暴力)、IVF(倒排)、HNSW(图)、PQ(量化)等
  • 与 Milvus 的对比:FAISS 的 Index ≈ Milvus 的索引,但 FAISS 的 Index 同时包含数据和索引结构

2.2 距离度量:L2 vs IP vs Cosine

  • FAISS 支持的距离度量
    • L2(欧氏距离):IndexFlatL2IndexIVFFlat
    • IP(内积):IndexFlatIPIndexIVFFlat
    • Cosine(余弦距离):FAISS 没有原生 Cosine Index——需要先归一化向量再用 IP
  • L2 vs IP 的选择:跟 Milvus 教程中的选择指南一致
  • Cosine 的实现方式faiss.normalize_L2(vectors)IndexFlatIP
  • 常见误区:直接用 IndexFlatIP 搜索未归一化的向量——结果不是余弦相似度

2.3 向量 ID 与结果映射

  • 默认 ID 行为:FAISS 默认用向量的添加顺序作为 ID(0, 1, 2, ...)
  • 自定义 IDIndexIDMap 包装器——给向量指定任意 int64 ID
  • ID 的用途:用 ID 映射回原始数据(文档内容、商品信息等)
  • 常见误区:FAISS 的 ID 只支持 int64——如果你需要字符串 ID,需要自己维护映射表

第3章:基础索引类型

3.1 Flat 索引:暴力搜索的基准

  • IndexFlatL2 / IndexFlatIP:暴力搜索,逐条计算距离,召回率 100%
  • 什么时候用 Flat:数据量 < 10 万、基准测试、验证其他索引的召回率
  • Flat 的性能:10 万条 768 维向量的搜索延迟约 10~50ms
  • IndexFlat 的变体:IndexFlatL2、IndexFlatIP、IndexRefineFlat(用于重排序)

3.2 IVF 索引:倒排文件加速

  • IVF 原理回顾:K-Means 聚类 → 倒排列表 → 搜索时只扫描 nprobe 个聚类
  • FAISS 的 IVF 实现
    • IndexIVFFlat:IVF + 原始向量(无压缩)
    • IndexIVFPQ:IVF + PQ 量化(后续章节详解)
    • IndexIVFSQ:IVF + SQ 量化
  • 训练(train)的概念:IVF 索引需要先训练聚类中心——index.train(vectors)index.add(vectors)
  • 训练数据的要求:训练数据应该代表真实数据分布,数据量至少是 nlist × 39
  • nlist 和 nprobe 参数:nlist = √N(聚类数),nprobe = nlist × 5%(搜索时扫描的聚类数)
  • 与 pgvector/Milvus IVFFlat 的对比:原理相同,参数名不同(nlist vs lists,nprobe vs probes)
  • 常见误区:在空索引上直接 add 而不先 train——IVF 索引必须先 train 再 add

3.3 HNSW 索引:图搜索的极速体验

  • HNSW 原理回顾:多层图结构、O(log N) 搜索、全内存驻留
  • FAISS 的 HNSW 实现IndexHNSWFlat
  • 参数M(每层连接数,默认 32)、efConstruction(构建搜索宽度,默认 40)、efSearch(搜索宽度,默认 16)
  • FAISS HNSW vs pgvector HNSW vs Milvus HNSW:参数名和默认值对比
  • HNSW 的内存估算:跟 Milvus 教程中的公式一致
  • 常见误区:FAISS HNSW 的 efSearch 默认值 16 太低——生产环境建议 100+

第4章:量化与压缩索引

4.1 为什么需要量化:内存是向量搜索的最大瓶颈

  • 内存瓶颈的量化分析:1000 万条 768 维 float32 向量 ≈ 29 GB,HNSW 索引 ≈ 60 GB
  • 量化的核心思想:用精度换内存——把 float32 压缩成更小的表示
  • FAISS 的量化方案:PQ(乘积量化)、SQ(标量量化)、OPQ(优化乘积量化)
  • 与 Milvus 量化的关系:Milvus 的 IVF_PQ 底层就是调用 FAISS 的 PQ 实现

4.2 PQ(Product Quantization):乘积量化详解

  • PQ 原理深入:子空间划分 → K-Means 聚类 → 编码 → 查表距离计算
  • FAISS 中的 PQIndexPQIndexIVFPQ
  • PQ 参数m(子空间数量)、nbits(每个子空间的编码位数,默认 8)
  • PQ 的压缩比计算:768 维 × 4 字节 → m × 1 字节,压缩比 = 768 × 4 / m
  • PQ 的距离计算:SDC(对称距离计算)vs ADC(非对称距离计算)——ADC 更精确
  • PQ 的召回率:通常 85%~95%,取决于 m 和 nbits
  • 常见误区:m 设得太小(如 m=8)导致精度损失过大——768 维向量建议 m=48~96

4.3 OPQ(Optimized Product Quantization):优化量化

  • OPQ 解决了 PQ 的什么问题:PQ 的子空间划分是均匀切分,不保证每个子空间的方差均衡——OPQ 通过旋转矩阵优化子空间划分
  • OPQ 原理:在 PQ 之前学习一个旋转矩阵 R,使得 R×X 的子空间方差更均衡
  • FAISS 中的 OPQIndexPQ + OPQMatrixIndexIVFPQ + 旋转
  • OPQ vs PQ 的性能对比:OPQ 召回率通常比 PQ 高 3~5 个百分点,代价是额外的训练时间
  • 何时使用 OPQ:对召回率要求高、愿意多花训练时间

4.4 SQ(Scalar Quantization):标量量化

  • SQ 原理:把每个 float32 维度线性映射到 int8(1 字节),压缩比 4 倍
  • FAISS 中的 SQIndexScalarQuantizerIndexIVFScalarQuantizer
  • SQ 的 6 种量化模式:QT_8bit、QT_6bit、QT_8bit_uniform、QT_4bit、QT_fp16、QT_8bit_direct
  • SQ vs PQ:SQ 实现简单、精度损失小,但压缩比只有 4 倍;PQ 压缩比更高但精度损失更大
  • 常见误区:SQ 不需要训练(除了 QT_8bit_uniform)——因为量化参数可以从数据统计得到

第5章:GPU 加速与高级特性

5.1 GPU 加速:FAISS 的性能杀手锏

  • 为什么 GPU 能加速向量搜索:大规模并行计算——GPU 可以同时计算数千个向量的距离
  • FAISS GPU 的架构:数据在 CPU 和 GPU 之间传输、索引在 GPU 内存中构建和搜索
  • GPU Index 类型IndexFlatL2GpuIndexFlatL2IndexIVFFlatGpuIndexIVFFlatIndexIVFPQGpuIndexIVFPQ
  • CPU → GPU 迁移faiss.index_cpu_to_gpu() / faiss.index_gpu_to_cpu()
  • 多 GPU 支持IndexReplicas(副本并行)和 IndexShards(分片并行)
  • 性能对比:GPU vs CPU 的搜索延迟和吞吐量差异(通常 5~20 倍加速)
  • 常见误区:数据量小时 GPU 反而更慢——因为 CPU↔GPU 数据传输的开销超过了计算加速

5.2 Index 组合与复合索引

  • FAISS 的组合哲学:把多个 Index 组件像搭积木一样组合
  • 常用组合模式
    • IndexIDMap(IndexIVFFlat):IVF + 自定义 ID
    • IndexPreTransform(IndexIVFPQ):PCA 降维 + IVF + PQ
    • IndexRefineFlat(IndexIVFPQ):IVF+PQ 初筛 + Flat 重排序
    • IndexHNSW(IndexIVFPQ):HNSW 图 + PQ 压缩
  • IndexPreTransform:在索引前对向量做预处理(PCA 降维、OPQ 旋转、归一化)
  • IndexRefine:用粗索引快速召回候选,再用精细索引重排——两阶段搜索
  • 常见误区:组合越多越好——每个组件都有开销,过度组合反而降低性能

5.3 批量搜索与距离计算

  • 批量搜索:一次传入多个查询向量,FAISS 内部并行计算
  • 距离矩阵计算faiss.pairwise_distances()——计算两组向量之间的所有距离
  • knn 搜索faiss.knn()——不创建索引直接搜索(适合一次性搜索)
  • range_search:距离小于阈值的向量搜索
  • 性能优化:批量大小对吞吐量的影响、避免逐条搜索

5.4 索引的序列化与持久化

  • FAISS 不自动持久化:进程退出数据就没了——你需要自己保存和加载
  • 序列化方法
    • faiss.write_index(index, "index.faiss")faiss.read_index("index.faiss")
    • faiss.serialize_index(index)faiss.deserialize_index(bytes)(内存序列化)
  • 序列化的内容:索引结构 + 向量数据 + 训练参数
  • 增量更新:FAISS 的大部分索引支持 add() 增量添加,但 IVF 索引的聚类中心不会自动更新
  • 常见误区:序列化后修改了原始数据,期望索引自动更新——FAISS 的索引和数据是独立的

第6章:FAISS 在 RAG 系统中的实战

6.1 FAISS 在 RAG 架构中的角色

  • FAISS 不是 RAG 的完整方案——它是 RAG pipeline 中的"检索引擎"组件
  • FAISS 的角色定位
    • Chroma/Milvus:完整的向量数据库,管存储+索引+查询
    • FAISS:纯检索引擎,只管内存中的向量搜索
  • FAISS 适合的 RAG 场景
    • 数据量中等(< 1000 万)、不需要持久化、不需要标量过滤
    • 需要极致搜索性能(GPU 加速)
    • 需要自定义索引组合(如 PCA + IVF + PQ + Refine)
    • 嵌入式部署(不需要独立的数据库服务)
  • FAISS 不适合的 RAG 场景
    • 需要标量过滤(WHERE 条件)→ 用 pgvector 或 Milvus
    • 需要数据持久化 → 用 pgvector 或 Milvus
    • 需要多用户并发 → 用 Milvus
    • 需要分布式 → 用 Milvus

6.2 端到端 RAG Demo:用 FAISS 构建文档问答

  • 项目结构:数据加载 → 切分 → embedding → 构建 FAISS 索引 → 搜索 → 生成
  • 完整 Python 实现
    • 文档加载与切分
    • Embedding 编码(SentenceTransformers)
    • 构建 FAISS 索引(IndexIVFFlat 或 IndexHNSWFlat)
    • 搜索最近邻 → 用 ID 映射回原始文档
    • Prompt 组装与 LLM 调用
  • 与 pgvector/Milvus RAG Demo 的对比:同样的功能,FAISS 更轻量但需要自己管理数据
  • 标量过滤的实现:FAISS 不支持标量过滤——需要在应用层先过滤再搜索,或者搜索后过滤

6.3 FAISS + pgvector 混合方案

  • 为什么需要混合方案:FAISS 负责高性能向量搜索,pgvector 负责结构化数据和标量过滤
  • 混合架构设计
    • pgvector 存储结构化数据 + 向量数据(持久化 + 事务)
    • FAISS 在内存中构建索引(高性能搜索)
    • 搜索结果用 ID 映射回 pgvector 获取完整数据
  • 数据同步策略:pgvector 为主存储,FAISS 索引定期从 pgvector 重建
  • 适用场景:需要 SQL + 标量过滤 + 高性能向量搜索的组合

第7章:FAISS 与向量数据库的协作

7.1 FAISS 作为向量数据库的底层引擎

  • Milvus 与 FAISS 的关系:Milvus 的 IVFFlat、IVF_PQ、HNSW 等索引底层调用 FAISS
  • 为什么向量数据库选择 FAISS:C++ 性能、丰富的索引类型、GPU 支持、久经考验
  • FAISS 在 Milvus 中的位置:IndexNode 调用 FAISS 构建索引 → 索引文件存入对象存储 → QueryNode 加载索引到内存
  • 理解这层关系的价值:当你调优 Milvus 的索引参数时,实际上是在调 FAISS 的参数

7.2 FAISS vs 向量数据库:何时用哪个

  • 选 FAISS 的场景
    • 嵌入式应用、不需要持久化、不需要标量过滤
    • 需要极致性能(GPU 加速、自定义索引组合)
    • 研究/实验/原型验证
    • 作为其他系统的组件
  • 选向量数据库的场景
    • 需要数据持久化和事务
    • 需要标量过滤(WHERE 条件)
    • 需要多用户并发和权限控制
    • 需要分布式
  • 选 pgvector:需要 SQL + 事务 + 中等规模向量搜索
  • 选 Milvus:需要分布式 + 大规模 + 量化
  • 选 Chroma:需要快速原型 + Python 原生

7.3 FAISS 的局限性与未来

  • 已知局限
    • 不支持标量过滤——无法在搜索时同时做 WHERE 条件
    • 不支持分布式——单机或单 GPU
    • 不支持数据持久化——需要自己序列化
    • Python API 不够友好——很多参数需要手动配置
    • 文档相对匮乏——很多高级功能需要看源码
  • FAISS 的不可替代性:作为向量索引的"基础设施"——即使你用 Milvus,底层也在用 FAISS
  • 未来趋势:向量数据库的索引层越来越标准化,FAISS 作为底层引擎的地位不会动摇

🎯 学习路径建议

向量搜索新手(1-2天):
  第1-2章 → 理解 FAISS 的定位和核心概念
  → 能用 FAISS 创建索引、添加向量、执行搜索

索引研究者(2-3天):
  第3-4章 → 深入 IVF/HNSW/PQ/OPQ 的原理和参数
  → 能根据场景选择和调优索引类型

性能工程师(2-3天):
  第5章 → 掌握 GPU 加速、复合索引、批量操作
  → 能让 FAISS 在生产场景中发挥极致性能

RAG 开发者(1-2天):
  第6章 → 用 FAISS 构建 RAG 系统
  → 理解 FAISS 与向量数据库的协作方式

架构师:
  第7章 → 理解 FAISS 在向量数据库生态中的位置
  → 能做出正确的技术选型决策

📚 与 Chroma / pgvector / Milvus 教程的对照阅读

如果你已经学过本系列的前三个教程,以下是 FAISS 与它们的对照:

概念ChromapgvectorMilvusFAISS
本质嵌入式向量数据库PG 扩展(向量数据库)分布式向量数据库向量搜索库
数据持久化✅ 自动✅ PostgreSQL✅ 对象存储❌ 需手动序列化
标量过滤✅ where✅ SQL WHERE✅ expr❌ 不支持
事务✅ ACID
索引类型HNSW(自动)IVFFlat/HNSW8 种20+ 种
GPU 加速✅(GPU_IVF_PQ)✅(原生支持)
分布式
自定义索引组合✅(积木式组合)
部署pip installDocker/RDSDocker/K8spip install
适合规模< 10M< 100M10M~10B+< 1B(单机)
核心优势零配置SQL 原生分布式性能极致
学习价值快速上手SQL+向量分布式向量索引原理

本大纲遵循以下写作原则:每节以白板式通俗介绍开场 → 深入原理与面试级知识点 → 配合代码示例("比如下面的程序...")→ 覆盖常见用法与误区 → 结尾总结要点衔接下一节。

基于 MIT 许可发布