FAISS

Faiss简介

 
!pip install faiss-cpu
    
FAISS(Facebook AI Similarity Search)
是一个由Facebook AI Research开发的库,用于高效地处理和搜索大规模的向量数据。

在许多AI和机器学习应用中,我们经常需要处理高维度的向量数据,
并需要快速地 找到与给定向量最相似的向量 。

例如,
在推荐系统中,我们可能需要找到与用户的兴趣向量最相似的商品向量;
在自然语言处理中,我们可能需要找到与给定词向量最相似的词向量。
这种操作被称为“最近邻搜索”。

全部代码

 
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv(filename="env.txt"))

from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS

from langchain_openai import OpenAI
from langchain_openai import OpenAIEmbeddings
    

 
!echo "肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸功能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。">/tmp/a.txt
!echo "from langchain_community.document_loaders import TextLoader">>/tmp/a.txt
!echo "from langchain_community.vectorstores import FAISS">>/tmp/a.txt

loader = TextLoader('/tmp/a.txt')
documents = loader.load()

# 准备文本分割器
text_splitter = RecursiveCharacterTextSplitter(chunk_size=50, chunk_overlap=0)

# 将大文档分成小块
texts = text_splitter.split_documents(documents)
print(texts[0].page_content)  # str
    
    

 
#选择嵌入模型
#embeddings = OpenAIEmbeddings(deployment='text-embedding-ada-002')
#model = OpenAIEmbeddings(model="text-embedding-3-large")
embeddings = OpenAIEmbeddings()

#将文本保存到FAISS 向量数据库中
db = FAISS.from_documents(texts, embeddings)
    

 
#大小写影响不大
docs = retriever.invoke("FAISS")
print("\n\n".join([x.page_content[:200] for x in docs[:2]]))
    
FAISS

TextLoader

能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。

 
#大小写影响不大
docs = retriever.invoke("FAISS")
print("\n\n".join([x.page_content[:200] for x in docs[:2]]))

FAISS

TextLoader
    

 
docs = retriever.invoke("呼吸")
print("\n\n".join([x.page_content[:200] for x in docs[:2]]))

肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸功

能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。
    

FAISS.from_documents

 
FAISS.from_documents(
    documents: 'List[Document]',
    embedding: 'Embeddings',
    **kwargs: 'Any',
)

Docstring:
Return VectorStore initialized from documents and embeddings.
    

 
#将文本保存到FAISS 向量数据库中
db = FAISS.from_documents(texts, embeddings)

# 准备文件搜索器
retriever = db.as_retriever()

#返回一个文档数组,其元素按向量相似度从高到低排列
docs = retriever.invoke("faiss")
print("\n\n".join([x.page_content[:200] for x in docs[:3]]))
    

FAISS.from_texts

 
FAISS.from_texts(
    texts: 'List[str]',
    embedding: 'Embeddings',
    metadatas: 'Optional[List[dict]]' = None,
    ids: 'Optional[List[str]]' = None,
    **kwargs: 'Any',
) -> 'FAISS'
Docstring:
Construct FAISS wrapper from raw documents.

This is a user friendly interface that:
    1. Embeds documents.
    2. Creates an in memory docstore
    3. Initializes the FAISS database

 
db_text =FAISS.from_texts(texts, embeddings)
retriever_text = db_text.as_retriever()
docs = retriever_text.invoke("FAISS")
print("\n\n".join([x.page_content[:200] for x in docs[:2]]))

 


 
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv(filename="env.txt"))

from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS

from langchain_openai import OpenAI
from langchain_openai import OpenAIEmbeddings

 
# 准备文本分割器
text_splitter = RecursiveCharacterTextSplitter(chunk_size=50, chunk_overlap=10,length_function = len)


text="""
肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸功能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
"""
texts = text_splitter.split_text(text)
texts
['肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸',
 '病邪犯肺,影响其呼吸功能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。',
 'from langchain_community.document_loaders import',
 'import TextLoader',
 'from langchain_community.vectorstores import',
 'import FAISS']

 
# 准备文本分割器
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=10,length_function = len)


text="""
肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸功能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
"""
texts = text_splitter.split_text(text)
texts
['肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸功能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。',
 'from langchain_community.document_loaders import TextLoader',
 'from langchain_community.vectorstores import FAISS']


 
db_text =FAISS.from_texts(texts, embeddings)
retriever_text = db_text.as_retriever()
docs = retriever_text.invoke("FAISS")
print("\n\n".join([x.page_content[:200] for x in docs[:2]]))
FAISS

TextLoader

 


 


from_embeddings:覆盖,新加载的向量会覆盖内存数据库中的向量

 
# 定义一个长文本。
text = """
肺与美容的关系
肺主气主呼吸
肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸功能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。
肺主宣发与肃降
肺主宣发是指肺气向上升宣和向外布散的功能。
若肺气不能宣发而凝滞,清阳不升,则见胸满、鼻塞、咳喘、面色晦暗;
浊气不能肃降,也能引起呼吸短促、喘促、咳痰等症状;
肺与大肠相表里,肺失肃降,则大肠传导失常,肠道实热,表里失和,面部就易出黄褐斑或生痤疮、酒渣鼻。
肺主皮毛通调水道
肺主皮毛,是指肺脏通过它的宣发作用把水谷精微输布于皮毛,以滋养周身皮肤、毛发、肌肉,也能温分肉、充皮肤、肥腠理、司开阖,防御外邪入侵。肺气足则皮肤滋润光滑有弹性,毫毛浓密光泽等;肺气虚,不能行气与津液以温养毫毛,毫毛之营养不足,就会憔悴枯槁,皮肤干燥,毛发暗淡枯稿,面色淡白;卫外不固则易发风疹过敏等症;肺热上熏则发痤疮、酒渣鼻、皮炎等症。
肺开窍于鼻
鼻是肺呼吸的通道,所以称“鼻为肺窍”。肺与大肠相表里,肺失肃降,则大肠传导失常,粪便排出不畅。临床上,肺失清肃,则大便困难;大肠实热,又引起肺气不利而咳喘胸满;表里失和,患者面部就易出黄褐斑或生痤疮。

"""

from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
    separator = "\n",
    chunk_size = 200,
    chunk_overlap  = 50
)
texts = text_splitter.split_text(text)

 
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

embeddings = OpenAIEmbeddings()

#1. Embeds documents.
#2. Creates an in memory docstore
#3. Initializes the FAISS database
faiss = FAISS.from_texts(texts, embeddings)
    

 
embeddings = OpenAIEmbeddings()
text_embeddings = embeddings.embed_documents(texts)
text_embedding_pairs = zip(texts, text_embeddings)

 
type(text_embeddings)
list

len(text_embeddings[0])
1536

 
#1. Embeds documents.
#2. Creates an in memory docstore
#3. Initializes the FAISS database
faiss = FAISS.from_embeddings(text_embedding_pairs, embeddings)

 


 


FAISS检索

Retriever(检索器)

 
在使用FAISS(Facebook AI Similarity Search)库进行大规模向量搜索时,
通常会遇到直接使用FAISS的API进行搜索(如faiss.search()或faiss.knn_query()等)
和将FAISS封装为Retriever(检索器)并通过invoke()方法调用的两种方式。
虽然两者都能实现向量搜索的功能,但将FAISS转化为Retriever后使用,
在某些场景下具有一些明显的优势,主要体现在以下几个方面:
    

 
更高的抽象级别和灵活性:
将FAISS封装为Retriever后,可以在更高层次的抽象上操作,
这意味着你可以更容易地与其他组件(如文本处理、数据处理流水线等)集成。
这种封装使得FAISS的使用更加灵活,尤其是在复杂的机器学习或数据科学项目中。

统一的接口:
如果你的项目中使用了多个检索器(如基于Elasticsearch、向量数据库等),
将FAISS也封装为Retriever可以提供一个统一的接口来调用它们。
这有助于简化代码结构,使得在不同检索器之间切换或组合使用变得更加容易。

易于扩展和维护:
随着项目的发展,可能需要添加新的检索器或修改现有的检索逻辑。
将FAISS封装为Retriever后,你可以通过继承或组合的方式轻松地扩展或修改检索逻辑,
而无需修改大量底层代码。

更好的错误处理和日志记录:
在Retriever的实现中,你可以添加自定义的错误处理和日志记录逻辑,
以便更好地监控和调试检索过程。
这对于大型项目或生产环境尤为重要,因为它们需要更高的稳定性和可维护性。

支持异步和并行处理:
某些Retriever实现可能支持异步或并行处理,这可以显著提高检索性能,
尤其是在处理大量查询或大型数据集时。
虽然FAISS本身也支持多线程搜索,但将FAISS封装为Retriever后,
你可以更容易地将其集成到支持异步或并行处理的框架中。

与框架和库的兼容性:
在某些情况下,将FAISS封装为Retriever可能是为了满足特定框架或库的要求。
例如,在构建基于Jina AI的神经网络搜索系统时,
将FAISS作为后端搜索引擎并封装为Retriever是推荐的做法,因为这样可以更容易地与其他Jina组件集成。

 
综上所述,将FAISS转化为Retriever后使用的主要优势在于提高了
抽象级别、灵活性、可扩展性、可维护性以及与其他组件的兼容性。
然而,这也取决于你的具体需求和项目上下文。
如果项目规模较小且不需要复杂的集成和扩展,直接使用FAISS的API可能更为简单直接。
    
    

 

    

 

    

 


 

  

 


FAISS持久化

index存储与加载

 
import faiss  
import numpy as np  
    
# 创建一些随机数据  
np.random.seed(42)  
data = np.random.rand(1000, 64).astype('float32')  # 假设有1000个64维的向量  
    
# 创建一个平面索引(IndexFlatL2),使用L2距离  
index = faiss.IndexFlatL2(64)  
    
# 将数据添加到索引中  
index.add(data)  
    
# 保存索引到文件  
faiss.write_index(index, "my_index.index")  
    
# 从文件中加载索引  
loaded_index = faiss.read_index("my_index.index")  
  

 
# 验证加载的索引是否包含相同的数据(这里仅作为示例,通常不会直接比较索引对象)  
# 实际上,你应该通过查询来验证索引的有效性  
query = np.random.rand(1, 64).astype('float32')  
dis, ind = loaded_index.search(query, 5)  # 查询最相似的5个向量  
    
  

 
dis
array([[6.099057 , 6.650052 , 6.803897 , 6.9097624, 6.952726 ]],
      dtype=float32)

ind
array([[502, 606, 234,  85, 162]])


query
array([[0.15736598, 0.02078138, 0.32708594, 0.8243158 , 0.01537996,
        0.10943393, 0.80685484, 0.3058517 , 0.9382135 , 0.38024414,
        0.79670393, 0.25878984, 0.06183124, 0.8815545 , 0.92729646,
        0.70809966, 0.7700678 , 0.00712762, 0.7538235 , 0.4721964 ,
        0.45279047, 0.54006994, 0.27121022, 0.6962885 , 0.29558888,
        0.5586066 , 0.13692723, 0.67507565, 0.18961276, 0.17041399,
        0.02428316, 0.12178577, 0.23257025, 0.22230937, 0.32767144,
        0.05441642, 0.935198  , 0.11917588, 0.7019639 , 0.53507316,
        0.5527888 , 0.10551683, 0.24321494, 0.7798813 , 0.46368647,
        0.45706782, 0.5459118 , 0.43380588, 0.80613595, 0.7487052 ,
        0.3170042 , 0.8120811 , 0.5186064 , 0.6277368 , 0.86617583,
        0.47599325, 0.75626034, 0.62808734, 0.42674494, 0.7890484 ,
        0.10515912, 0.5812202 , 0.04641076, 0.15616913]], dtype=float32)

data[ind]
  

 
确保在调用write_index之前,你的索引已经包含了所有需要的数据,包括向量和可能的ID映射等。
read_index加载的索引将是一个与原来内存中相同的索引对象的副本,所以你可以直接使用它来进行搜索等操作。
FAISS索引持久化功能依赖于磁盘I/O,因此可能会比内存操作慢一些。但在需要节省内存或持久化索引以供后续使用时,这是一个非常有用的特性。
综上所述,FAISS通过write_index和read_index函数提供了索引的持久化功能,允许你将内存中的索引结构及其向量数据保存到磁盘上,并在需要时重新加载它们。

    

 

    

 

    

index注意事项

 
索引类型:
示例中使用了IndexFlatL2,这是一种简单的索引类型,
适用于小规模数据集或作为其他更复杂索引(如IndexIVFFlat、IndexIVFPQ)的基准。
对于大规模数据集,建议使用更高效的索引类型。
    
数据规模:
当处理大规模数据集时,索引的保存和加载可能会消耗较多的磁盘空间和时间。
因此,在实际应用中,需要根据具体情况选择合适的索引类型和持久化策略。
    
异常处理:
在保存和加载索引时,应添加适当的异常处理逻辑以处理可能出现的错误(如文件权限问题、磁盘空间不足等)。

FAISS当数据量大时,应该使用哪种索引

 
IVF(Inverted File)索引 
inverted 英/ɪnˈvɜːtɪd/ 美/ɪnˈvɜːrtɪd/ adj. 反向的,倒转的,颠倒的,(尤指)倒置的 v.(使)倒转,颠倒,倒置

特点:
IVF索引是FAISS中应用最广泛的索引类型之一,
它将向量空间划分为多个子空间(聚类),并为每个子空间构建一个倒排索引(哈希表)。
搜索时,首先根据查询向量找到对应的子空间,然后在该子空间中进行相似度计算和搜索。
这种方法通过减少搜索的空间范围,显著提高了搜索效率。
    
适用场景:
适用于大规模数据集,能够显著加快搜索速度,同时保持较高的召回率。
    
PQ(Product Quantization)索引
特点:
PQ索引是一种用于大规模向量量化的索引类型。
它将向量划分为若干个子向量,并对每个子向量进行量化,
然后将量化后的子向量拼接起来形成新的向量表示。
在搜索时,通过比较量化后的向量来计算相似度。
PQ索引通过减小向量的维度,降低了计算和存储的复杂度。

适用场景:
适用于需要量化向量以节省存储空间和计算资源的情况,尤其适用于大规模数据集。

 

    

index只是index:存储的是向量,搜索的输入也是向量,而不是文本

 
# 定义一个长文本。
text = """
肺与美容的关系
肺主气主呼吸
肺主呼吸之气、一身之气。肺司呼吸的功能正常,则气道通畅,呼吸调匀,面有色泽。若病邪犯肺,影响其呼吸功能,则出现胸满咳嗽、喘促、呼吸不利、面色紫绀等症状。
肺主宣发与肃降
肺主宣发是指肺气向上升宣和向外布散的功能。
若肺气不能宣发而凝滞,清阳不升,则见胸满、鼻塞、咳喘、面色晦暗;
浊气不能肃降,也能引起呼吸短促、喘促、咳痰等症状;
肺与大肠相表里,肺失肃降,则大肠传导失常,肠道实热,表里失和,面部就易出黄褐斑或生痤疮、酒渣鼻。
肺主皮毛通调水道
肺主皮毛,是指肺脏通过它的宣发作用把水谷精微输布于皮毛,以滋养周身皮肤、毛发、肌肉,也能温分肉、充皮肤、肥腠理、司开阖,防御外邪入侵。肺气足则皮肤滋润光滑有弹性,毫毛浓密光泽等;肺气虚,不能行气与津液以温养毫毛,毫毛之营养不足,就会憔悴枯槁,皮肤干燥,毛发暗淡枯稿,面色淡白;卫外不固则易发风疹过敏等症;肺热上熏则发痤疮、酒渣鼻、皮炎等症。
肺开窍于鼻
鼻是肺呼吸的通道,所以称“鼻为肺窍”。肺与大肠相表里,肺失肃降,则大肠传导失常,粪便排出不畅。临床上,肺失清肃,则大便困难;大肠实热,又引起肺气不利而咳喘胸满;表里失和,患者面部就易出黄褐斑或生痤疮。

"""

from langchain.text_splitter import CharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

 
def get_embeddings(text):
    text_splitter = CharacterTextSplitter(
            separator = "\n",
            chunk_size = 1000,
            chunk_overlap  = 50
        )
    texts = text_splitter.split_text(text)
    embeddings = OpenAIEmbeddings()
    text_embeddings = embeddings.embed_documents(texts)
    return texts,text_embeddings

# get_embeddings(text)
  

 
texts,embeddings = get_embeddings(text)
  

 
import os
import faiss  
import numpy as np  
  

 
index_path = "my_index.index"

# if os.path.exists(index_path):
# 创建一个平面索引(IndexFlatL2),使用L2距离  
dimensions = len(embeddings[0])

index = faiss.IndexFlatL2(dimensions)  
    
# 将数据添加到索引中  
index.add(np.array(embeddings))  
    
# 保存索引到文件  
faiss.write_index(index, "my_index.index")  
    
# 从文件中加载索引  
loaded_index = faiss.read_index("my_index.index")  
  

 
query = "肺与美容的关系"
_,embeddings_query = get_embeddings(query)
  

 
dis, ind = loaded_index.search(np.array(embeddings_query),k=1)
dis
array([[0.23161677]], dtype=float32)

ind
array([[0]])
  

 

  

 

  

 

  

 


参考