Skip to main content
Open In Colab在 GitHub 上打开

Faiss (Async)

Facebook AI 相似性搜索 (Faiss) 是一个用于高效相似性搜索和密集向量聚类的库。它包含的算法可以在任何大小的向量集中进行搜索,直到 RAM 中可能不适合的向量。它还包括用于评估和参数调整的支持代码。

请参阅 FAISS Library 论文。

Faiss 文档

您需要安装langchain-communitypip install -qU langchain-community使用此集成

此笔记本展示了如何使用与FAISSvector 数据库使用asyncio. LangChain 实现了同步和异步 vector store 函数。

synchronous版本在这里

%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]

序列化和反序列化为 bytes

您可以通过这些函数来 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 vectorstore 也可以支持过滤,因为 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}")
API 参考:文档
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}

以下是如何设置fetch_k参数similarity_search.通常,您需要fetch_k参数 >>k参数。这是因为fetch_kparameter 是筛选之前将获取的文档数。如果您将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 应该是 docstore 中的 ID。

db.delete([db.index_to_docstore_id[0]])
True
# Is now missing
0 in db.index_to_docstore_id
False