接触到大模型的RAG功能后,我才逐渐了解向量数据库。其中,FAISS是最古老、基础的向量数据库,有必要分析一下。
RAG和向量数据库
RAG(Retrieval-Augmented Generation,检索增强生成),结合信息检索和大模型生成的一种技术架构,通过为大模型提供知识源,来提升大模型准确度、减少大模型幻觉、提升大模型推理速度,以及让大模型能够进行“本地化”地回答。
RAG的主要流程就是“检索 - 增强 - 生成”:
- 检索:将输入的查询转化为向量,利用向量数据库找出最接近的上下文数据
- 增强:将检索结果与原始查询拼接,增强了大模型对于原始查询中关键词的理解
- 生成:大模型基于原始查询和检索到的上下文数据,更快速地生成更准确、更本地化的回答。
举个例子:
| 类型 | 数据 |
|---|---|
| 输入 | 我的手机经常卡死,怎么解决? |
| 检索R | 搜索到结果: 我的手机很容易卡死,怎么办?回答:清理手机垃圾。 我的手机很卡,如何解决?回答:清理微信缓存的图片数据。 |
| 增强A | 我的手机经常卡死,怎么解决?参考资料: “我的手机很容易卡死,怎么办?回答:清理手机垃圾。” “我的手机很卡,如何解决?回答:清理微信缓存的图片数据。” |
| 生成G | 可以尝试一下清理手机垃圾,尤其是微信缓存的图片数据 |
这个例子中,向量数据库查询到了近似数据,大模型利用上下文得到了很多关联信息,然后用自然语言输出最终答案。而本地化体现在,如果这里提供的数据是“苹果手机知识库”,那么大模型就会回答苹果手机如何处理;如果是“华为手机数据库”,则是华为手机的解决方法。
RAG这套架构在2023年开始流行,解决了很多实际问题:
- 大模型幻觉:当时大模型的参数量不高,幻觉问题很严重,通过提示具体上下文,能够很好地限制幻觉
- 大模型可泛化:大模型可以使用泛化的模型,不需要费时费力自己训练一个专用的大模型
- 知识垂直、安全、可增量更新:知识库的知识非常垂直,而且本地保存非常安全,随时可以增量更新
其中,向量数据库充当了非常重要的角色,它的查询速度和信息关联程度,很大程度上决定了整个RAG架构的速度和回答质量。
向量数据库的挑战
我们知道向量是很容易从数学上比较近似程度的,因此人们把文字、图片、视频、音频、甚至整个二进制文件这种非结构化的多模态数据,将其转换为向量存入数据库中,就能够很好地比较两份数据的近似程度了。
这种转换有两部分挑战:
- 向量化:多模态非结构化数据,提取主要特征。这块以后有时间单独分析。
- 检索和存储向量:这就是向量数据库的主要能力
围绕检索和存储向量,向量数据库的主要挑战就是:
- 存储海量数据,传统索引查询高维向量数据太慢,需要更好的索引结构
- 查询最近邻时,需要平衡或取舍准确率、速度、吞吐量
利用大模型学习FAISS主要架构
AI发展这几年,所有组件都在卷向量数据库的能力,甚至ES、Redis、Postgre这些传统数据库也都开始支持向量存储。
FAISS(Facebook AI Similarity Search)则是2015年Meta开发的一个古老的C++编写的向量数据库,用于早年间机器学习的一些应用场景,比如:
- 推荐系统:淘宝“每日推荐”、“找相似”
- 数据挖掘:计算与标签的匹配度
- ……
最近FAISS能又火起来,主要是它提供了Python接口、搭建简易、支持CPU和GPU两种模式,非常利于RAG的入门和简单实现。
- 提供python接口:意味着和LangChain这种早期RAG的具体实现组件能够很容易地结合
- 搭建简易:完全不用单独部署一个服务,直接用磁盘和内存
- 支持CPU和GPU:GPU资源稀缺,如果不在乎性能(入门和简单需求通常不在乎),只需要CPU就能够轻易上手
网络的知识总是过时的,而读源代码不但耗时,还可能因为不熟悉语言特性(比如这里的C++)可能在一些细节上绕圈和错误解读。现在有Cursor、Trae这种AI IDE,很容易能够借助大模型来进行最新的源码阅读:
这是著名向量数据库FAISS,用C++编写。请你阅读源代码,作为FAISS专家的身份回答我的后续问题。

FAISS架构

FAISS的核心组件是:索引和量化。
- 索引:提供向量数据存储和搜索的整体架构和流程
- 量化:具体的压缩、编解码、计算优化
索引层
核心索引类型,提供不同的性能权衡:
| 索引类型 | 搜索速度 | 内存占用 | 精度 | 适用场景 |
|---|---|---|---|---|
| IndexFlat | 慢 | 高 | 100% | 小规模精确搜索 |
| IndexIVF | 中 | 中 | 高 | 大规模近似搜索 |
| IndexHNSW | 快 | 高 | 高 | 高性能近似搜索 |
| IndexPQ | 快 | 低 | 中 | 内存受限场景 |
| IndexBinary | 快 | 极低 | 中 | 二进制向量 |
IVF(Inverted File)倒排文件索引
IVF索引是一种基于聚类的索引结构的索引方法。核心思想是:先划分多个聚类桶,每个桶再放置一个向量列表。
- 插入时,先找到聚类桶,再放置进列表
- 查询时,只需要访问与当前向量最相关的几个聚类桶,检索效率很高。
倒排列表是在Lucene、ES时代就已经存在的产物,key是分词抓到的关键词,文档为value(实际可能是文档ID、词频、位置、偏移量等),可以通过关键词快速定位文档。类似地,对于向量,key就是聚类中心,value就是向量数据。
| key(聚类中心ID) | value(向量ID和数据) |
|---|---|
| 0 | [(V1, <0.1, 0.3, ...>), (V5, <0.1, 0.7, ...>)] |
| 1 | [(V2, <0.5, 0.7, ...>), (V4, <0.6, 0.9, ...>)] |
| 2 | [(V3, <0.8, 0.1, ...>)] |
HNSW(Hierarchical Navigable Small World)分层可导航小世界索引
HNSW 索引是一种基于分层图结构的索引方法。核心思想是:分层小世界图+贪心搜索。
NSW是HNSW的前身,主要是贪心搜索:
- 每个节点与多个邻居相连
- 检索时从随机节点出发,通过 “贪心搜索”(每次跳向距离查询向量更近的邻居)快速逼近目标 很明显的缺点就是,数据量很大时,图会巨幅膨胀,导致贪心搜索的跳数增加,延迟变高。

HNSW基于NSW的改进,有点像redis里面的跳表的图版本:
- 节点层数随机生成(比如90%的节点在底层,9%在中间层,1%在顶层),大部分节点在底层,小部分节点在顶层
- 每层都是一个独立的NSW
HNSW插入流程
插入时:根据概率公式(概率和维护的最近邻数量M有关)生成随机层高L,从顶层开始一直向下贪心遍历,从L层开始直至底层每一层都要建立连接:
- 从当前层的入口出发,贪心地找到局部最近邻
- 如果是顶层,则随机选择入口
- 如果是中间层,最近邻为入口
- 如果低于等于L层,会为该节点在该层创建一个分身节点,仅维护M个最近邻,然后与这M个最近邻建立双向连接,避免中间层图的膨胀
- 当遍历到底层时,进行更广泛的贪心搜索,维护2*M个最近邻,建立双向连接,保证底层的稠密
- 各层建立连接的过程中,如果被连接的节点已经达到M、2*M的上限,则会触发他们更新最近邻
HNSW查询流程
查询时:从顶层开始一直向下贪心遍历到1层,然后在最底层(0层)执行精确搜索
- 顶层到1层,贪心地维护最近邻候选集,然后以这些候选节点为种子继续下一层遍历,候选集数量会在贪心后保持不变
- 进入到最底层,精确地维护最近邻候选集,这一层需要精确计算向量之间的距离,然后返回TopK
量化层
量化层是FAISS的核心组件之一,主要负责向量压缩和距离计算优化。
向量压缩是指,将高维浮点向量压缩为低比特编码,大幅减少内存占用,支持十亿级向量存储。 距离计算优化是指,通过查表加速距离计算,避免直接计算高维向量距离,同时支持非对称距离计算(ADC)。
| 量化器 | 压缩率 | 精度 | 速度 | 适用场景 |
|---|---|---|---|---|
| 乘积量化器 ProductQuantizer 分段量化,平衡精度和压缩率 | 高 | 中 | 快 | 大规模相似搜索 |
| 标量量化器 ScalarQuantizer 每维度独立量化 | 中 | 中 | 最快 | 简单量化需求 |
| 加性量化器 AdditiveQuantizer 多层量化叠加 | 高 | 高 | 中 | 高精度需求 |
| 残差量化器 ResidualQuantizer 残差量化 | 高 | 高 | 中 | 残差编码场景 |
| RaBit量化器 RaBitQuantizer 快速二进制量化 | 极高 | 低 | 快 | 超大规模数据 |
乘积量化器PQ
PQ是最经典和常用的量化器,它将高维向量空间划分为多个子空间,在每个子空间内进行独立的标量量化,然后将各个子空间的量化结果组合起来表示原始向量。
PQ量化过程
假设向量库中的所有向量都是N=128维,我们指定参数M=4,意味着高维向量会被划分为4段,每一段是32维。然后对于每个分段进行聚类,聚合256(固定经验值)个簇心,每个向量对应M=4个簇心的ID,将浮点数的N维向量表示成了整型的簇心ID(0~255,uint8),实现了降维。
PQ计算距离过程
将查询输入的向量进行同样的量化处理,将128维的向量分成4段32维子向量,然后每个段与对应段的全部256个簇心计算距离,得到Mx256的距离表。对于库中的每个向量,利用簇心ID对这个查询向量和簇心的距离表进行反查,将得到的距离加起来,便是与查询向量之间的距离。
PQ将float向量降维还有一个重要的能力:实现了归一化。归一化在机器学习里面的重要性不言而喻,可以参考之前写过的一篇文章《特征归一化特性及其数学原理推导》。这里通过将浮点数任意的数据限制到0~255,实际上就是实现了归一化。
此外可以预见,对于PQ的性能,N的维度越高,这种降维查询的提效就越大;但库中存储的数据越多,因为是反查,遍历查询的效率就会越低。因此量化和索引是结合起来使用的,例如IndexIVFPQ = IndexIVF + ProductQuantize,通过倒排索引来减少存储库中向量的个数,再利用PQ算法提供快速的距离计算,既避免了倒排索引计算距离时间复杂度高的弊端、又避免了PQ在数据量大时遍历数据太多的弊端。
实际运用
这里只描述一下过程。我们有个场景需要对“用户反馈数据”进行分类,比如情感分类、功能点分类。一开始想到的是传统的机器学习方法,比如“随机森林”、“神经网络”,但实际使用下来并不好,因为这类特定领域的反馈数据的数据量非常少,因此分类的准确率极低。
后面考虑使用大模型+RAG的模式,大模型输入了非常多的“常识”,足够对分类进行判别。同时给予少量的人工标注的“用户反馈数据”,来提升对特定领域的认知。
- 大模型(LLM):大模型使用的是2张L20卡运行的“Qwen3-8B”,使用vllm搭建。8B的参数量已经能涌现足够的智能了。
- 向量化(embedding):采用常规的文本向量化组件SentenceTransformer:all-MiniLM-L6-V2
- 向量数据库(VectorDB):采用的是FAISS,因为容易上手。正在考虑迁移到Milvus,因为FAISS有个实际运用才会发现的弊端是——不方便在线增减数据。
- RAG:采用的是LangChain搭建,比较方便。后面在考虑使用SpringAI,毕竟主语言是Java。
整体效果非常好,相比于传统机器学习方案有质的提升。



粤公网安备44030002014216号