Faiss (非同步)
Facebook AI 相似性搜尋 (Faiss) 是一個用於高效相似性搜尋和密集向量叢集的函式庫。它包含可在任何大小的向量集中搜尋的演算法,甚至可搜尋可能不適合放入 RAM 的向量集。它還包括用於評估和參數調整的支援程式碼。
請參閱 FAISS 函式庫 論文。
您需要安裝 langchain-community
,使用 pip install -qU langchain-community
才能使用此整合
此筆記本示範如何使用與 FAISS
向量資料庫相關的功能,使用 asyncio
。LangChain 實作了同步和非同步向量儲存庫功能。
請參閱 同步
版本此處。
%pip install --upgrade --quiet faiss-gpu # For CUDA 7.5+ Supported GPU's.
# OR
%pip install --upgrade --quiet faiss-cpu # For CPU Installation
我們想要使用 OpenAIEmbeddings,因此我們必須取得 OpenAI API 金鑰。
import getpass
import os
if "OPENAI_API_KEY" not in os.environ:
os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")
# Uncomment the following line if you need to initialize FAISS with no AVX2 optimization
# os.environ['FAISS_NO_AVX2'] = '1'
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
loader = TextLoader("../../../extras/modules/state_of_the_union.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)
embeddings = OpenAIEmbeddings()
db = await FAISS.afrom_documents(docs, embeddings)
query = "What did the president say about Ketanji Brown Jackson"
docs = await db.asimilarity_search(query)
print(docs[0].page_content)
相似性搜尋與分數
有一些 FAISS 特定的方法。其中一種是 similarity_search_with_score
,它不僅允許您傳回文件,還可以傳回查詢與文件之間的距離分數。傳回的距離分數是 L2 距離。因此,分數越低越好。
docs_and_scores = await db.asimilarity_search_with_score(query)
docs_and_scores[0]
也可以使用 similarity_search_by_vector
搜尋與給定的嵌入向量相似的文件,它接受嵌入向量作為參數,而不是字串。
embedding_vector = await embeddings.aembed_query(query)
docs_and_scores = await db.asimilarity_search_by_vector(embedding_vector)
儲存與載入
您也可以儲存和載入 FAISS 索引。這很有用,因此您不必每次使用時都重新建立它。
db.save_local("faiss_index")
new_db = FAISS.load_local("faiss_index", embeddings, asynchronous=True)
docs = await new_db.asimilarity_search(query)
docs[0]
序列化與反序列化為位元組
您可以使用這些函數來 pickle FAISS 索引。如果您使用 90 mb 的嵌入模型 (sentence-transformers/all-MiniLM-L6-v2 或任何其他模型),則產生的 pickle 檔案大小將超過 90 mb。模型的大小也包含在總大小中。若要克服此問題,請使用以下函數。這些函數僅序列化 FAISS 索引,且大小會小得多。如果您希望將索引儲存在 SQL 等資料庫中,這會很有幫助。
from langchain_huggingface import HuggingFaceEmbeddings
pkl = db.serialize_to_bytes() # serializes the faiss index
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
db = FAISS.deserialize_from_bytes(
embeddings=embeddings, serialized=pkl, asynchronous=True
) # Load the index
合併
您也可以合併兩個 FAISS 向量儲存庫
db1 = await FAISS.afrom_texts(["foo"], embeddings)
db2 = await FAISS.afrom_texts(["bar"], embeddings)
db1.docstore._dict
{'8164a453-9643-4959-87f7-9ba79f9e8fb0': Document(page_content='foo')}
db2.docstore._dict
{'4fbcf8a2-e80f-4f65-9308-2f4cb27cb6e7': Document(page_content='bar')}
db1.merge_from(db2)
db1.docstore._dict
{'8164a453-9643-4959-87f7-9ba79f9e8fb0': Document(page_content='foo'),
'4fbcf8a2-e80f-4f65-9308-2f4cb27cb6e7': Document(page_content='bar')}
相似性搜尋與篩選
FAISS 向量儲存庫也支援篩選,由於 FAISS 本身不支援篩選,我們必須手動執行。這是透過先擷取比 k
更多的結果,然後篩選它們來完成的。您可以根據中繼資料篩選文件。您也可以在呼叫任何搜尋方法時設定 fetch_k
參數,以設定您想要在篩選之前擷取的文件數量。以下是一個小範例
from langchain_core.documents import Document
list_of_documents = [
Document(page_content="foo", metadata=dict(page=1)),
Document(page_content="bar", metadata=dict(page=1)),
Document(page_content="foo", metadata=dict(page=2)),
Document(page_content="barbar", metadata=dict(page=2)),
Document(page_content="foo", metadata=dict(page=3)),
Document(page_content="bar burr", metadata=dict(page=3)),
Document(page_content="foo", metadata=dict(page=4)),
Document(page_content="bar bruh", metadata=dict(page=4)),
]
db = FAISS.from_documents(list_of_documents, embeddings)
results_with_scores = db.similarity_search_with_score("foo")
for doc, score in results_with_scores:
print(f"Content: {doc.page_content}, Metadata: {doc.metadata}, Score: {score}")
Content: foo, Metadata: {'page': 1}, Score: 5.159960813797904e-15
Content: foo, Metadata: {'page': 2}, Score: 5.159960813797904e-15
Content: foo, Metadata: {'page': 3}, Score: 5.159960813797904e-15
Content: foo, Metadata: {'page': 4}, Score: 5.159960813797904e-15
現在我們進行相同的查詢呼叫,但我們僅篩選 page = 1
results_with_scores = await db.asimilarity_search_with_score("foo", filter=dict(page=1))
for doc, score in results_with_scores:
print(f"Content: {doc.page_content}, Metadata: {doc.metadata}, Score: {score}")
Content: foo, Metadata: {'page': 1}, Score: 5.159960813797904e-15
Content: bar, Metadata: {'page': 1}, Score: 0.3131446838378906
使用 max_marginal_relevance_search
也可以完成相同的事情。
results = await db.amax_marginal_relevance_search("foo", filter=dict(page=1))
for doc in results:
print(f"Content: {doc.page_content}, Metadata: {doc.metadata}")
Content: foo, Metadata: {'page': 1}
Content: bar, Metadata: {'page': 1}
以下是如何在呼叫 similarity_search
時設定 fetch_k
參數的範例。通常您會希望 fetch_k
參數 >> k
參數。這是因為 fetch_k
參數是在篩選之前將擷取的文件數量。如果您將 fetch_k
設定為較小的數字,您可能無法獲得足夠的文件來篩選。
results = await db.asimilarity_search("foo", filter=dict(page=1), k=1, fetch_k=4)
for doc in results:
print(f"Content: {doc.page_content}, Metadata: {doc.metadata}")
Content: foo, Metadata: {'page': 1}
支援某些 MongoDB 查詢和投影運算子,以進行更進階的中繼資料篩選。目前支援的運算子清單如下
$eq
(等於)$neq
(不等於)$gt
(大於)$lt
(小於)$gte
(大於或等於)$lte
(小於或等於)$in
(列表中的成員)$nin
(不在列表中)$and
(所有條件都必須符合)$or
(任何條件都必須符合)$not
(條件的否定)
可以使用以下方式執行與進階中繼資料篩選相同的上述相似性搜尋
results = await db.asimilarity_search(
"foo", filter={"page": {"$eq": 1}}, k=1, fetch_k=4
)
for doc in results:
print(f"Content: {doc.page_content}, Metadata: {doc.metadata}")
Content: foo, Metadata: {'page': 1}
刪除
您也可以刪除 ID。請注意,要刪除的 ID 應該是文件儲存庫中的 ID。
db.delete([db.index_to_docstore_id[0]])
True
# Is now missing
0 in db.index_to_docstore_id
False