Stay Hungry,Stay Foolish!

Incremental addition of vectors to FAISS

Incremental addition of vectors to FAISS

https://github.com/facebookresearch/faiss/issues/1284

There will be some regimes in which PQ provides a speedup for search (where the overhead of PQ lookup is overridden by the win in reducing memory bandwidth requirements), but typically the technique is used for compression rather than speedup and in many applications it will only be slower. Adding vectors it is certainly slower.

In the regime in which you are in, IndexIVFFlat makes the most sense, but this is an approximate index. The train() data needs to be on a representative sample of your data distribution. In the very tiny database regime (say, < 1000 vectors or so), a flat index will likely remain superior. As you are periodically adding new vectors, you may have to decide for yourself whether or not you wish to retrain an index / build a new one from scratch.

An exact non-exhaustive search space partitioning scheme like a k-D tree will likely work best in your low-ish dimensional (d < 20) use case. This kind of index can be built incrementally. There is no implementation of this in Faiss, however (it is typically designed for high dimensions, usually 32 - 2048 or so), so you'd have to find another library for it.

https://stackoverflow.com/questions/69938317/how-to-add-index-to-python-faiss-incrementally

 

Enable memory-efficient vector indexing with IndexIVFPQ by maintaining a rolling window of training data. The train_partial method accumulates and trims embeddings, periodically retraining the index to keep clustering representative. When adding new embeddings, the method ensures the index is initialized and trained before insertion.

class IncrementalFaissIndex:
    def __init__(self, dimension=768, num_lists=1000):
        # Create quantizer once
        self.quantizer = faiss.IndexFlatL2(dimension)
        self.index = None
        self.trained_data = []

    def train_partial(self, new_embeddings, max_train_size=50000):
        # Accumulate data
        self.trained_data.extend(new_embeddings)
        
        # Trim to max size
        if len(self.trained_data) > max_train_size:
            self.trained_data = self.trained_data[-max_train_size:]
        
        # Retrain index with accumulated data
        if self.index is None or len(self.trained_data) >= max_train_size:
            train_array = np.array(self.trained_data)
            
            # Reinitialize index with new training
            self.index = faiss.IndexIVFPQ(
                self.quantizer, 
                train_array.shape[1], 
                num_lists=1000, 
                m=16, 
                bits=8
            )
            self.index.train(train_array)

    def add_embeddings(self, new_embeddings):
        # Ensure index is initialized and trained
        if self.index is None:
            self.train_partial(new_embeddings)
        
        # Add new embeddings
        self.index.add(new_embeddings)


FAISS 三种向量检索方式学习


https://www.cnblogs.com/zhangkele/p/18706932

How do I persist FAISS indexes?

https://stackoverflow.com/questions/78734751/how-do-i-persist-faiss-indexes

 
 

In fact, FAISS is considered as an in-memory database itself in order to vector search based on similarity that you can serialize and deserialize the indexes using functions like write_index and read_index within the FAISS interface directly or using save_local and load_local within the LangChain integration which typically uses the pickle for serialization.

If you need to store serialized files, you could manually save them in a NoSQL database like MongoDB as binary data, and then deserialize and retrieve them when needed, however, it is not the best practice!

If you are looking for a vector database that is not in-memory and capable in a scalable system, you might want to consider using Milvus which is designed for this purpose.

 

基于大模型框架langchain中的faiss向量数据库的应用与完整代码实现

https://zeeklog.com/ji-yu-da-mo-xing-kuang-jia-langchain-zhong-de-faiss-xiang-liang-shu-ju-ku-de-ying-yong-yu-wan-zheng-dai-ma-shi-xian/

基于大模型框架langchain中的faiss向量数据库的应用与完整代码实现

大家好,我是微学AI,今天给大家介绍一下基于大模型框架langchain中的faiss向量数据库的应用与完整代码实现。首先,我们提供了数据样例,并将其输入到向量数据库中。随后,通过相似度查找功能,实现了对数据的快速检索。文章还提供了实现这一过程的完整Python代码,为读者在实际应用中提供了参考和指导。通过本文,读者可以深入了解faiss向量数据库在数据检索领域的应用及其优势。

www.zeeklog.com  - 基于大模型框架langchain中的faiss向量数据库的应用与完整代码实现

文章目录

一、langchain 中 faiss 向量数据库概述

1.1 langchain 简介及其在自然语言处理中的角色

1.1.1 什么是 langchain?

Langchain 是一个专为自然语言处理(NLP)任务设计的开源框架,它允许开发者更高效地构建能够理解和生成人类语言的应用程序。该平台集成了多种强大的工具和库,如Hugging Face Transformers等,以支持各种复杂的NLP场景,从简单的文本分类到更加高级的任务,比如机器翻译、情感分析以及对话系统等。通过提供一套标准化接口和服务,langchain极大地简化了开发流程,使得即使是那些对底层技术细节不甚了解的人也能轻松创建出高质量的语言模型应用。

1.1.2 langchain 的核心功能

  • 多模态数据处理:除了纯文本之外,还支持图像、音频等多种形式的信息作为输入或输出。
  • 可扩展性:用户可以方便地添加新的组件或修改现有模块,从而满足特定需求。
  • 易用性:提供了简洁直观的API,降低了学习成本。
  • 社区活跃度高:拥有庞大且活跃的开发者社群,不断贡献新特性与最佳实践案例。

1.2 Faiss 向量数据库介绍及优势

1.2.1 Faiss 概览

FAISS (Facebook AI Similarity Search) 是由 Facebook 开发的一个用于大规模向量相似度搜索和聚类的库。它特别适用于需要快速查找最接近给定查询点的数据点的情况,在推荐系统、信息检索等领域有着广泛的应用前景。与其他解决方案相比,FAISS 在处理海量数据时表现出色,尤其是在高维空间中进行高效搜索的能力尤为突出。

1.2.2 相似性搜索的重要性

随着互联网上非结构化数据量的增长,如何有效地组织这些信息成为一个挑战。传统的关系型数据库难以应对这种复杂度较高的查询需求。而基于向量表示的方法,则可以通过将每个项目映射成固定长度的数值向量来简化这一过程。这样做的好处是不仅大大减少了存储空间占用,同时也便于利用数学运算来进行高效比较。因此,在诸如图像识别、语音转文字转换等领域内,采用向量化方法已成为主流趋势之一。

1.2.3 Faiss 在相似性搜索上的优势

  • 高性能:采用了先进的索引技术和算法优化策略,能够在极短时间内完成数百万乃至数十亿规模的数据集上的近邻搜索。
  • 灵活性:支持多种不同类型的向量(例如二进制、浮点数等),并提供了丰富多样的索引选项供用户选择。
  • 内存效率:通过压缩技术减少所需RAM大小,使得即使是在资源受限环境下也能良好运行。
  • 易于集成:虽然最初是针对C++编写的,但官方提供了Python绑定版本,使其可以直接被Python应用程序调用。

1.3 Langchain 与 Faiss 结合的意义

当我们将Langchain与Faiss结合起来使用时,不仅可以充分利用两者各自的优势,还能进一步拓宽应用场景范围。例如,在构建聊天机器人时,我们可能希望根据用户的提问找到与其最相关的知识条目;或者是在电商网站中实现个性化商品推荐。在这种情况下,首先利用Langchain将原始文本转化为具有一定语义含义的向量表示,然后再借助于Faiss的强大搜索能力快速定位到潜在匹配项,最终返回给用户最为相关的结果。这种方式既保证了结果的质量,又提升了整体响应速度,实现了良好的用户体验。

通过对Langchain的基本概念及其在NLP领域的作用进行了简要回顾,并深入探讨了Faiss向量数据库的特点及其在相似性搜索方面所展现出的独特魅力后,我们可以看出二者结合使用将会带来怎样的变革潜力。接下来的部分将继续围绕这一主题展开讨论,包括具体数据样例准备、如何将这些数据导入到Faiss数据库中,以及如何执行高效的相似度查找等内容。

二、数据样例展示

在准备将数据导入至 Faiss 向量数据库前,理解什么样的数据格式是适合的非常重要。Faiss 是 Facebook AI Research 开发的一个库,专为高效处理大规模向量相似性搜索而设计。它支持多种索引类型,允许根据应用需求灵活选择最佳方案。为了能够有效地利用 Faiss 的功能,我们需要先准备好正确的数据形式。本章节中,我们将探讨如何构建一个简单的文本语料库作为示例,并将其转换成适用于 Faiss 的向量表示。

2.1 数据预处理与向量化

2.1.1 文本清洗

首先,从原始文档开始,比如文章、新闻报道或者社交媒体帖子等非结构化文本信息。这些文本往往包含大量不需要的信息,例如 HTML 标签、特殊符号、URL 链接等。因此,在进行任何进一步处理之前,必须先对文本执行清洗步骤:

  • 移除 HTML/XML 标记
  • 替换或移除非字母数字字符
  • 将所有内容转换为小写(有助于减少词汇变体)
  • 删除停用词(如“the”、“is”等常见但无实际意义的单词)

2.1.2 分词

接下来,通过分词技术将每个文档分解成一系列单独的词语或短语。这一步骤对于后续生成高质量的词嵌入至关重要。常用的方法包括基于规则的方法和统计模型,后者可以更好地捕捉语言中的细微差别。

2.1.3 词嵌入生成

有了经过清洗并分词后的文本之后,下一步就是将它们转换为数值型向量。这可以通过多种方式完成,其中包括但不限于 Word2Vec、GloVe 或者 BERT 等深度学习模型。以 BERT 为例,它可以产生固定长度的上下文相关向量表示,这对于捕捉句子间的语义关系特别有效。

示例代码片段:
from transformers import BertTokenizer, BertModel
import torch

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

def get_bert_embedding(text):
    inputs = tokenizer(text, return_tensors='pt', truncation=True, padding=True)
    outputs = model(**inputs)
    # 使用最后一层的隐藏状态平均值作为句子嵌入
    sentence_embeddings = torch.mean(outputs.last_hidden_state, dim=1).squeeze()
    return sentence_embeddings.detach().numpy()

sample_text = "This is a sample text to demonstrate the process."
vector = get_bert_embedding(sample_text)
print(vector)

2.2 构建数据集

一旦我们有了每个文档对应的向量表示,就可以构建最终的数据集了。理想情况下,这个数据集应该是一组 N 维浮点数数组,其中 N 表示特征空间的维度,通常是几百到几千不等,具体取决于所选的词嵌入模型。

2.2.1 数据特点

  • 高维性:现代自然语言处理任务经常涉及非常高维的空间。
  • 稀疏性:虽然一些高级模型可能产生稠密向量,但在传统方法中,很多特征可能仅出现在少量样本上。
  • 规模大:随着互联网的发展,待处理的数据量日益增长。

2.2.2 数据结构

对于 Faiss 而言,最直接适用的数据结构是一个二维 numpy 数组或 PyTorch 张量,其形状为 (n_samples, n_features),这里 n_samples 指的是训练样本的数量,而 n_features 则代表每条记录的特征数量。

示例数据构造:

假设我们现在有 1000 条文本记录,每条记录都已经被转换成了 768 维的 BERT 嵌入,则可以这样创建我们的输入数据:

import numpy as np

# 假设 X 是已经准备好的 BERT 嵌入矩阵
X = np.random.randn(1000, 768).astype(np.float32)  # 注意类型转换,确保使用 float32

以上就是关于如何准备适合作为 Faiss 输入的数据样例的详细介绍。接下来的部分将深入讨论如何把这些向量存储到 Faiss 数据库中,并实现高效的查询服务。

三、输入数据到向量数据库

在本章节中,我们将详细介绍如何将准备好的数据样例成功导入到Faiss向量数据库里。这一过程不仅涵盖了数据预处理的基本步骤,还包括了创建索引、添加向量等关键技术点。通过本章的学习,读者能够掌握利用Python与Faiss库来管理大规模向量数据集的方法。

3.1 数据预处理

3.1.1 向量化工具选择

在开始之前,重要的是选择合适的文本嵌入生成模型或图像特征提取器等,以将原始内容转换为固定长度的向量表示。对于文本处理任务,常见的选择包括但不限于BERT, Sentence Transformers等;而针对图像识别场景,则可能采用ResNet, VGG等架构。这里假设我们已经拥有了经过上述模型处理后得到的一系列向量形式的数据样本。

3.1.2 确定维度

所有要被存储在同一个Faiss索引内的向量必须具有相同的维度。因此,在正式构建索引前,请确保您所有的数据都已经被转化为了相同维度的向量。

3.2 安装并引入Faiss库

首先,你需要安装最新版本的Faiss库。可以通过pip命令轻松完成这一步骤:

pip install faiss-cpu

接下来,在您的Python脚本文件顶部加入如下语句以正确地引用Faiss:

import faiss

3.3 创建索引结构

根据实际应用场景的需求(如对内存使用效率的要求、查询速度等),可以选择不同类型的索引结构。对于初学者而言,推荐从IndexFlatL2开始尝试,这是一种简单但高效的全量扫描方式,适用于小规模数据集。当面对更大规模的问题时,则可以考虑更复杂的索引类型,比如基于树结构的IndexIVFFlat或者哈希方法IndexLSH等。

下面是一个创建基本IndexFlatL2索引的例子:

dimension = 768  # 假设每个向量有768维
index = faiss.IndexFlatL2(dimension)

请注意调整这里的dimension变量值以匹配你实际使用的向量大小。

3.4 添加向量至索引

一旦有了初始化好的索引对象,就可以开始向其中添加数据了。这通常通过调用add()函数实现。需要注意的是,传入此函数的数据应当是numpy数组格式,并且其形状应为(样本数量, 向量维度)。

例如,如果我们有一批已经准备好待存入数据库的向量vectors,那么执行添加操作的方式如下所示:

# vectors 是一个形状为 (N, D) 的 numpy 数组
index.add(vectors)

此处,N代表总共有多少条记录,D则对应每条记录所对应的特征向量长度。

3.5 保存与加载索引

随着项目进展,可能需要频繁地读写索引文件。为此,Faiss提供了方便的接口来支持这些操作。具体来说,可以使用write_index()read_index()两个方法来实现索引对象的序列化与反序列化。

将当前状态下的索引保存至硬盘:

faiss.write_index(index, "my_index.index")

从磁盘上恢复已有的索引:

index = faiss.read_index("my_index.index")

以上即完成了从零开始构造并向Faiss数据库中插入新条目的全过程。接下来的部分将深入探讨如何基于现有索引来执行高效精准的信息检索任务。

四、相似度查找原理与方法

在现代的信息检索系统中,快速而准确地找到与给定查询最相似的数据点是一项关键技术。随着数据量的爆炸性增长,传统基于文本内容的搜索方法已经难以满足需求,特别是在处理高维向量空间时。FAISS(Facebook AI Similarity Search)是一个由Facebook开发的强大库,专为大规模向量相似度搜索设计。本部分将深入探讨FAISS进行高效相似度查找的核心原理,包括其使用的相似度计算方法以及支撑这些操作的索引结构。

4.1 相似度计算基础

4.1.1 向量表示法

在开始讨论具体的相似度测量之前,有必要先了解什么是向量及其在信息检索中的作用。向量可以看作是一组数值特征的集合,每个元素代表一个特定维度上的值。例如,在自然语言处理领域,文档或句子通常被转换成固定长度的向量,这种过程称为嵌入(embedding)。通过这种方式,非结构化的文本数据得以转化为数学对象,使得机器学习模型能够理解和处理它们。

4.1.2 常见的相似度度量

  • 欧氏距离:这是最直观的距离度量方式之一,定义为两个点之间直线距离的平方和开根号。当应用于相似度评估时,较小的距离意味着更高的相似性。
  • 余弦相似度:它衡量的是两向量之间的夹角大小,范围从-1到+1。完全相同的方向对应于+1的最大值,而完全相反的方向则得到-1。在许多应用场景下,尤其是对于词向量等高维稀疏数据,余弦相似度比欧氏距离更受欢迎。
  • 曼哈顿距离:也称城市街区距离,计算两个点沿坐标轴方向的距离总和。虽然不如欧氏距离普遍,但在某些特定情况下(如网格布局的城市规划问题)具有实际意义。
  • Jaccard指数:用于比较有限样本集之间的相似性和多样性,特别适用于二进制属性的数据集。公式为交集大小除以并集大小。

4.2 FAISS 中的索引类型

为了支持高效的相似度搜索,FAISS提供了一系列不同类型的索引结构,每种都有自己的优势和适用场景。选择合适的索引是优化性能的关键步骤之一。

4.2.1 FlatL2 索引

FlatL2 是最简单的索引形式之一,直接存储所有训练向量,并使用 L2 范数(即欧氏距离)来计算最近邻。尽管这种方法简单且准确,但由于没有对原始数据做任何压缩或简化,因此在处理大规模数据集时可能会遇到性能瓶颈。

4.2.2 IVFFlat 和 IVFPQ 索引

逆向文件文件格式(IVF, Inverted File Index)是一种分层次的索引策略,首先根据一定数量的中心点将整个向量空间划分为若干个桶(bucket),然后每个新来的向量都会被分配到距离最近的那个中心所对应的桶里。查询时仅需检查少数几个相关联的桶即可大大减少比较次数。PQ (Product Quantization)进一步通过对桶内向量进行量化编码来减小内存占用。

4.2.3 HNSW 索引

Hierarchical Navigable Small World (HNSW) 图是一种新颖的数据结构,允许非常快地执行近似最近邻居搜索。它的构建基于一种特殊的图构造算法,该算法确保了任意两点间存在一条“短路径”。这使得即使在极端庞大的数据库中也能保持良好的响应速度。

4.3 实践应用指南

正确选择适合任务需求的相似度度量标准及相应的索引类型对于实现高性能的相似性搜索至关重要。一般来说,在处理稠密型低维数据时可优先考虑采用欧氏距离搭配 FlatL2 或 IVFFlat;而对于高维稀疏数据,则倾向于选用余弦相似度配合 HNSW 这样的高级索引机制。此外,还需要根据具体的应用背景调整参数设置,比如聚类中心的数量、PQ 的子向量维度等,以达到最佳效果。

综上所述,FAISS 提供了一套强大而灵活的工具集,帮助开发者们克服了大规模向量相似度搜索中面临的挑战。通过对上述概念和技术的理解与实践,我们可以更加有效地挖掘海量数据背后隐藏的价值。

五、完整 Python 代码实现

在这一部分,我们将通过一个具体的例子来展示如何利用 LangChain 框架结合 Faiss 向量数据库实现文本数据的存储与检索。本示例将覆盖从环境设置到最终查询结果获取的全过程,并附有详细的步骤说明及代码解析,旨在帮助读者理解每个操作背后的意义。

5.1 环境准备

首先确保您的开发环境中已安装必要的库。对于本教程来说,您需要 langchain, faiss-cpufaiss-gpu (取决于是否使用GPU加速),以及 transformers 库以处理文本嵌入。

pip install langchain faiss-cpu transformers

注意:如果您计划使用 GPU 版本的 Faiss,则应安装 faiss-gpu 而不是 faiss-cpu

5.1.1 导入库文件

接下来,在Python脚本或Jupyter笔记本中导入所有必需的库。

from langchain.embeddings import HuggingFaceEmbeddings
import faiss
import numpy as np

这里我们选择了Hugging Face提供的模型来进行文本向量化,但您可以根据实际情况选择其他模型。

5.2 构建嵌入模型

为了能够将文本转换为数值型向量表示,我们需要定义一个嵌入模型。

embedder = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")

上述代码行创建了一个基于预训练MiniLM模型的嵌入器实例。此特定版本的MiniLM适合大多数应用场景且对计算资源要求不高。

5.3 准备样本数据并生成向量

假设我们现在有一批文档内容需要被存储进Faiss数据库中。下面给出了一些简单的英文句子作为示例数据:

documents = [
    "The cat sat on the mat.",
    "Dogs are known for their loyalty.",
    "Birds can fly and sing.",
    "Fish live in water.",
]

使用之前创建的嵌入器对象,我们可以很容易地为这些文档生成对应的向量表示:

vectors = embedder.embed_documents(documents)

此时vectors变量包含了所有输入文档对应的高维向量形式。

5.4 创建和填充Faiss索引

现在我们已经获得了文档向量,接下来就是构建Faiss索引来存储它们了。

dimension = len(vectors[0])  # 获取单个向量的维度
index = faiss.IndexFlatL2(dimension)  # 初始化一个Flat L2距离索引
index.add(np.array(vectors))  # 将文档向量添加到索引中

以上代码片段完成了以下任务:

  • 根据第一个文档向量确定整个向量空间的维度。
  • 基于欧氏距离(L2范数)创建了一个扁平索引结构。这适用于较小规模的数据集;对于大规模应用可能需要考虑更高效的索引类型如IVFFlat等。
  • 使用NumPy数组形式将所有文档向量一次性加入至索引内。

5.5 执行相似度搜索

最后一步是演示如何基于给定查询执行最近邻查找。

query = "Animals that can swim."
query_vector = embedder.embed_query(query)
_, indices = index.search(np.array([query_vector]), k=2)  # 查找最接近的两个邻居
print("Most similar documents:")
for i in indices[0]:
    print(documents[i])

这段代码实现了如下功能:

  • 对用户提供的查询字符串进行编码,获得其向量表示。
  • 在Faiss索引上执行k近邻搜索(这里设定k=2),得到与查询最相关的文档索引列表。
  • 打印出找到的相关文档内容。

通过这样一个流程,我们就成功地建立起了一个小型的语义搜索引擎!当然,实际部署时还需要考虑更多因素如索引持久化、查询性能优化等,但这超出了当前示例范围。

希望本节内容能为您提供足够的信息来开始探索LangChain与Faiss的强大组合。

Could not load content



posted @   lightsong  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
历史上的今天:
2022-02-13 Inversion of Control
2016-02-13 自签名证书的是与非
千山鸟飞绝,万径人踪灭
点击右上角即可分享
微信分享提示