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) |
|
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可能更为简单直接。 |
|
|
|
|
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]]) |
|
|