FAISS로 빠르고 효율적인 Vector Store 구축하기

고라니·2024년 10월 15일
post-thumbnail

✅ Intro

RAG 시스템을 주제로 프로젝트를 진행 중에 있는데 vector storeFAISS를 도입하려고 한다. chroma를 깨작 만져본 적이 있는데 이번 기회에 FAISSvector store를 구축해보려고 한다.

✅ Basic Concepts

FAISS에 대해 간략하게 소개하자면 대규모 벡터 검색을 효율적으로 처리하기 위한 Facebook AI Research의 오픈소스 라이브러리이다. FAISS는 특히 대규모, 고차원의 데이터를 효율적으로 처리하는데 있어 강점이 있다.
지금 진행하는 프로젝트는 row 하나당 sequence가 길수록 성능이 좋아질 것이라고 판단했고, 이미지 데이터를 활용하는 방안도 생각중에 있기 때문에 chroma보다는 멀티모달에 강한 FAISS를 선택했다.

✅ Quick Start

📌 package install

# colab
!pip install langchain langchain_community faiss-cpu

추가적으로 임베딩 모델을 허깅페이스에서 다운로드 받기 위해서 sentence-transformers 패키지도 install 한다.

# colab
!pip install -U sentence-transformers

📌 데이터 전처리

임베딩 모델인 jhgan/ko-sroberta-multitask128토큰이 최대 입력 한도이다. 따라서 토큰 수에 따른 데이터 청킹이 필요하다.

데이터 파싱

from langchain_community.document_loaders.csv_loader import CSVLoader

loader_file = CSVLoader(
						file_path='YOUR_CSV_FILE_PATH', 
                        encoding='' # cp949 or utf-8
)
                        
file = loader_file.load()

데이터 청킹

TokenTextSplitter 는 토큰 수를 기반으로 청크를 생성할 때 유용하다. 토큰 수의 기반 모델은 OpenAI의 tiktoken을 사용한다.

!pip install tiktoken

tiktoken을 먼저 설치해준다.

from langchain_text_splitters import TokenTextSplitter

text_splitter = TokenTextSplitter(
    chunk_size=100,  # 청크 크기를 100으로 설정
    chunk_overlap=0,  # 청크 간 중복을 0으로 설정
    add_start_index=True # 청크의 시작 인덱스 표기
)

# csv파일을 청크로 분할
splits = text_splitter.split_documents(file) # document 형식으로 불러온 file을 청크로 분할
print("분할된 csv의 첫번재 청크 : ",splits[0])
print("분할된 청크의 개수 : ", len(splits))

📌 임베딩 모델

임베딩 모델은 주최 측에서 몇 가지 모델 안에서 선택하도록 제한을 두었는데 그 중에서 jhgan/ko-sroberta-multitask 모델로 진행했다.
Similarity searchMaximum marginal relevance search 사이에서 고민했는데 PoC 단계이므로 Similarity search로 실험했다.

from langchain.embeddings import HuggingFaceEmbeddings

modelPath = "jhgan/ko-sroberta-multitask"
model_kwargs = {'device':'cpu'}
encode_kwargs = {'normalize_embeddings': True} # 벡터간의 cosine similarity 계산에서 크기는 제외된 방향성만 고려

embeddings_model = HuggingFaceEmbeddings(
    model_name=modelPath,     
    model_kwargs=model_kwargs, 
    encode_kwargs=encode_kwargs 
)

📌 Vector Store 생성

from langchain_community.vectorstores import FAISS
from langchain_community.vectorstores.utils import DistanceStrategy

vectorstore = FAISS.from_documents(documents = splits,
                                   embedding = embeddings_model,
                                   distance_strategy = DistanceStrategy.COSINE
                                  )
vectorstore

✅ Test

similarity_search 함수를 활용해서 테스트 쿼리에 벡터 서치가 잘 적용되는지 확인했다.
similarity_search 메서드는 주어진 쿼리와 가장 유사한 문서들을 검색한다.
주요 매개변수는 다음과 같다.

query (str): 유사한 문서를 찾기 위한 검색 쿼리 텍스트
k (int): 반환할 문서 수. 기본값은 4
filter (Optional[Union[Callable, Dict[str, Any]]]): 메타데이터 필터링 함수 또는 딕셔너리. 기본값은 None
fetch_k (int): 필터링 전에 가져올 문서 수. 기본값은 20

query = '20대가 많이 찾는 맛집은 어디인가요?'
docs = vectorstore.similarity_search(query)
print(len(docs))
print(docs[0].page_content)

동작 방식

  1. 쿼리 인코딩
    • 주어진 쿼리 문자열을 벡터로 변환한다. vectorstore를 생성할 때 사용된 임베딩 모델을 사용하여 수행된다.
  2. 유사도 검색
    • 쿼리 벡터와 벡터 스토어에 저장된 벡터들 간의 가장 유사한 벡터들을 찾아낸다.
    • 이 때, 유사도 계산 방법은 vectorstore 생성 시 지정된 distance_strategy에 따라 결정된다.
  3. 결과 반환
    • 검색 결과로 얻어진 문서들을 유사도 순으로 정렬하여 반환한다.

최종적으로 생성된 vector db를 로컬에 저장해 놓는다. 지정한 폴더 위치에 index.faiss, index.pkl 두 가지 파일이 생성되면 성공이다.

vectorstore.save_local('YOUR_LOCAL_PATH')

✅ 마무리

FAISS가 다른 vector db와는 다르게 인덱스의 개념이 있다고해서 조금 어렵게 다가왔는데 막상 구현은 크게 차이가 없는 것 같다.
FAISS로 vector db를 구축하는데 일단 성공은 했지만 성능 개선이 필요하다.


reference

profile
고라니에요

0개의 댓글