Facebook AI Similarity Search (Faiss)는 밀집 벡터의 효율적인 유사도 검색 및 클러스터링을 위한 라이브러리입니다. RAM에 맞지 않을 수 있는 크기를 포함하여 모든 크기의 벡터 집합에서 검색하는 알고리즘을 포함합니다. 또한 평가 및 파라미터 튜닝을 위한 지원 코드도 포함되어 있습니다. The FAISS Library 논문을 참조하세요.
Faiss documentation. 이 integration을 사용하려면 pip install -qU langchain-communitylangchain-community를 설치해야 합니다. 이 노트북은 asyncio를 사용하여 FAISS vector database와 관련된 기능을 사용하는 방법을 보여줍니다. LangChain은 동기 및 비동기 vector store 함수를 구현했습니다. 동기 버전은 여기를 참조하세요.
pip install -qU  faiss-gpu # For CUDA 7.5+ Supported GPU's.
# OR
pip install -qU  faiss-cpu # For CPU Installation
OpenAIEmbeddings를 사용하려면 OpenAI API Key를 가져와야 합니다.
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)

score를 포함한 유사도 검색

FAISS 전용 메서드가 몇 가지 있습니다. 그 중 하나는 similarity_search_with_score로, 문서뿐만 아니라 쿼리와 문서 간의 거리 점수도 반환할 수 있습니다. 반환되는 거리 점수는 L2 거리입니다. 따라서 점수가 낮을수록 좋습니다.
docs_and_scores = await db.asimilarity_search_with_score(query)

docs_and_scores[0]
문자열 대신 embedding 벡터를 파라미터로 받는 similarity_search_by_vector를 사용하여 주어진 embedding 벡터와 유사한 문서를 검색하는 것도 가능합니다.
embedding_vector = await embeddings.aembed_query(query)
docs_and_scores = await db.asimilarity_search_by_vector(embedding_vector)

저장 및 로드

FAISS index를 저장하고 로드할 수도 있습니다. 이는 사용할 때마다 다시 생성할 필요가 없어 유용합니다.
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로 직렬화 및 역직렬화

이 함수들을 사용하여 FAISS Index를 pickle할 수 있습니다. 90mb 크기의 embeddings 모델(sentence-transformers/all-MiniLM-L6-v2 또는 다른 모델)을 사용하는 경우, 결과 pickle 크기는 90mb 이상이 됩니다. 모델의 크기도 전체 크기에 포함됩니다. 이를 극복하려면 아래 함수를 사용하세요. 이 함수들은 FAISS index만 직렬화하므로 크기가 훨씬 작아집니다. SQL과 같은 데이터베이스에 index를 저장하려는 경우 유용할 수 있습니다.
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 vectorstore를 병합할 수도 있습니다.
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보다 많은 결과를 가져온 다음 필터링하는 방식으로 수행됩니다. metadata를 기반으로 문서를 필터링할 수 있습니다. 또한 검색 메서드를 호출할 때 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 query and projection operators는 더 고급 metadata 필터링을 위해 지원됩니다. 현재 지원되는 연산자 목록은 다음과 같습니다:
  • $eq (같음)
  • $neq (같지 않음)
  • $gt (보다 큼)
  • $lt (보다 작음)
  • $gte (크거나 같음)
  • $lte (작거나 같음)
  • $in (리스트 내 포함)
  • $nin (리스트 내 미포함)
  • $and (모든 조건이 일치해야 함)
  • $or (어떤 조건이든 일치해야 함)
  • $not (조건의 부정)
고급 metadata 필터링을 사용한 동일한 유사도 검색은 다음과 같이 수행할 수 있습니다:
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

Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I