[LangChain] FAISS VectorStore를 사용해서 RAG (Retrieval-Augmented Generation) 구현하기
지난번 포스팅에서 RAG (Retrieval-Augmented Generation) 이란 무엇이고, LangChain으로 어떻게 구현하는지에 대해서 소개하였습니다.
이번 포스팅에서는
1 FAISS (Facebook AI Similarity Search) 는 무엇인가?
2. FAISS 알고리즘의 주요 단계와 구성 요소는 무엇인가?
3. LangChain의 FAISS VectorStore를 사용해서 RAG (Retrieval-Augmented Generation) 구현
하는 방법에 대해서 소개하겠습니다.
1. FAISS (Facebook AI Similarity Search) 는 무엇인가?
FAISS (Facebook AI Similarity Search)는 효율적인 알고리즘 및 밀집 벡터의 유사성 검색 및 클러스터링을 위한 라이브러리입니다. 페이스북 AI 연구팀(Facebook AI Research Tema)에 의해 개발된 FAISS는 대규모 벡터 검색 작업에 특히 유용하며, 이미지 검색, 추천 시스템, 자연어 처리와 같은 분야에 응용됩니다.
FAISS가 중요한 이유는 대규모, 고차원 데이터셋을 효율적으로 처리할 수 있는 능력(ability to efficiently handle large-scale, high-imensional datasets, ensuring speed and resource optimization) 때문입니다. 이는 빅데이터와 AI 시대에 필수적인 속도 및 자원 최적화를 보장합니다. 그 사용은 현대 데이터 기반 애플리케이션 및 연구의 필요성에 깊이 뿌리를 두고 있으며, 상업적 및 학술적 영역에서 중요한 도구가 되었습니다.
FAISS 알고리즘의 주요 특징은 다음과 같습니다.
(1) 벡터 검색 (Vector Search): FAISS는 대규모 데이터베이스에서 벡터를 효율적으로 검색하는 것을 목표로 합니다. 특히 딥러닝 모델에서 나오는 임베딩과 같은 고차원 데이터를 다룰 때 효과적입니다.
(2) 인덱싱 (Indexing): FAISS의 핵심 기능 중 하나는 벡터에 대한 다양한 유형의 인덱스를 생성하는 것입니다. 이러한 인덱스는 속도, 정확도, 메모리 사용 사이의 다른 균형점에 최적화되어 있습니다. FAISS에서 흔히 사용되는 인덱스 유형에는 Flat (Brute Force), Inverted File (IVF), 제품 양자화(Product Quantization) 인덱스 등이 있습니다.
(3) 대규모 효율성 (Efficiency at Scale): FAISS는 수백만에서 수십억에 이르는 벡터 데이터베이스를 효율적으로 처리하도록 최적화되어 있습니다. 그 효율성은 알고리즘 혁신(eg. 양자화)과 실제 최적화(eg. GPU의 효율적 사용)에서 비롯됩니다.
(4) 양자화 (Quantization): FAISS는 메모리 사용을 줄이고 검색 속도를 향상시키기 위해 벡터를 압축(compressing vectors)하는 양자화를 사용합니다. 제품 양자화는 인기 있는 기술 중 하나로, 여기서 고차원 벡터들이 더 작은 하위 벡터로 나뉘고 각 하위 벡터가 독립적으로 양자화됩니다.
(5) 검색 방법 (Search Methods): FAISS는 정확한 검색 (Exact Search)과 근사 검색 (Approximate Search) 방법을 모두 지원합니다. 정확한 검색은 가장 가까운 이웃을 찾는 것을 보장하지만 계산적으로 비쌀 수 있습니다. 근사 검색은 속도와 정확도 사이의 타협을 제공하며, 실제 응용 프로그램에서는 종종 사용됩니다.
(6) 배치 및 실시간 검색 (Batch and Real-time Searching): FAISS는 배치 및 실시간 쿼리 모두를 처리할 수 있어 다양한 용도에 적합합니다.
(7) 클러스터링 (Clustering): 유사성 검색 외에도 FAISS는 k-평균(k-Means)과 같은 알고리즘을 사용하여 대규모 벡터 세트를 클러스터링하는 데에도 사용할 수 있습니다.
(8) 언어 및 통합 (Language and Integration): FAISS는 효율성을 위해 C++로 구현되었지만 사용 편의성을 위해 파이썬 바인딩을 제공합니다. 인기 있는 딥러닝 프레임워크와 쉽게 통합할 수 있습니다.
FAISS는 매우 큰 데이터셋과 고차원 벡터를 효과적으로 처리할 수 있는 능력으로 인해, 빠르고 확장 가능한 유사성 검색이 필요한 작업에 AI 커뮤니티에서 인기 있는 선택입니다.
2. FAISS 알고리즘의 주요 단계와 구성 요소는 무엇인가?
(1) 벡터 표현 (Vector Representation)
(2) 클러스터링 (Clustering)
(3) 인덱스 구축 (Index Construction)
(4) 검색 (Searching)
(5) 후처리 (Post-Processing)
FAISS (Facebook AI Similarity Search) 알고리즘은 특히 대규모 데이터베이스에서 밀집 벡터의 효율적인 유사성 검색 및 클러스터링을 위해 설계되었습니다. FAISS의 과정과 방법은 여러 주요 단계와 구성 요소로 나눌 수 있습니다:
(1) 벡터 표현 (Vector Representation)
- 입력 (Input): FAISS는 이미지, 텍스트 또는 딥러닝과 같은 기술을 사용하여 생성된 고차원 데이터와 같은 데이터에서 생성된 밀집 벡터(dense vectors)로 작동합니다.
- 전처리 (Preprocessing): 특정 응용 프로그램에 필요한 경우 벡터는 정규화(normalization)되거나 전처리될 수 있습니다.
(2) 클러스터링 (Clustering)
- k-평균 클러스터링 (k-Means clustering): FAISS는 유사한 벡터를 그룹화하기 위해 k-평균과 같은 클러스터링 알고리즘을 지원합니다.
- 인덱싱에서의 사용: 클러스터링은 인덱싱 과정의 효율성을 향상시키는 데 사용될 수 있습니다.
(3) 인덱스 구축 (Index Construction)
- 인덱스 선택: 데이터셋의 크기와 속도, 정확도, 메모리 사용 사이의 원하는 균형에 따라 적절한 인덱스 유형이 선택됩니다. FAISS는 여러 인덱스 유형을 제공합니다
- (a) Flat Index: 완전 탐색 (Brute-force search), 가장 높은 정확도를 제공하지만 계산이 많이 필요합니다.
- (b) IVF (Inverted File Index): 속도와 정확도 사이의 균형을 맞추며, 중간에서 큰 데이터셋에 적합합니다. 역 파일 인덱스(IVF)는 문서 검색 시스템에서 단어나 용어가 어느 문서에 나타나는지를 빠르게 찾기 위한 데이터 구조입니다. 역 파일 인덱스(IVF)는 각 단어나 용어를 문서 ID와 연결하여 어떤 문서에서 해당 단어가 나타나는지를 기록합니다. 이를 통해 검색 시스템은 사용자 질의에 일치하는 문서를 신속하게 식별할 수 있습니다.
- (c) 제품 양자화 (Product Quantization): 메모리 사용을 줄이기 위해 벡터를 압축하며 매우 큰 데이터셋에 좋습니다.
- (d) HNSW (Hierarchical Navigable Small World): 그래프 기반 인덱스 (Graph-based index), 고차원 데이터에 효율적입니다.
- 인덱스 훈련: 양자화(Product Quantization)와 같은 일부 인덱스는 데이터의 분포를 학습하는 훈련 단계가 필요합니다. (float 의 min, max 값 등)
- 벡터 추가: 데이터셋 벡터가 인덱스에 추가됩니다.
(4) 검색 (Searching)
- 쿼리 벡터(Query Vectors): 데이터셋 벡터와 마찬가지로 쿼리 벡터는 사용자의 쿼리 데이터에서 생성됩니다.
- 인덱스 검색: 인덱스는 쿼리 벡터의 가장 가까운 이웃을 빠르게 검색하는 데 사용됩니다. 검색은 다음과 같을 수 있습니다.
- (a) 정확한 검색 (Exact Search): Brute-force (Exhaustive) Search 를 사용하여 정확한 가장 가까운 이웃을 찾지만, 검색해야 할 Passage가 많아질 수록 이에 비례해서 시간이 오래 걸립니다.
- (b) 근사 검색 (Approximate Search): 더 빠르지만 결과는 근사치입니다.
Inverted File (IVF)은 Pruning 을 하여 search space를 줄여서 search 속도를 증가시킵니다. 전체 vector store를 k-means Clustering을 통해 k 개의 Cluster로 군집화하고, Vector Index를 저장하고 있는 Inverted File (IVF)에는 각 Cluster의 중심 ID(Centroid ID)와 해당 Cluster 내의 Vector 들이 연결되어 있는 Inverted List Structure 형태로 저장되어 있습니다. 사용자의 쿼리가 들어오면 이를 임베딩을 변환하고, 먼저 k 개의 Cluster 의 centroids 와 유사도를 계산해서 가장 유사한 (즉, 거리가 가장 짧은) Cluster를 찾습니다. 그리고 그 해당 Cluster 내 passage 들의 임베딩에 대해서만 유저 쿼리 임베딩과 유사도 검색을 함으로써 serch space를 pruning하고 search 속도를 증가시키는 효과를 볼 수 있습니다.
- 매개변수: 이웃의 수(number of neighbors)나 검색 반경(search radus)과 같은 검색 매개변수를 조정할 수 있습니다.
[ IVF (Inverted File) 알고리즘으로 사용자 쿼리와 가장 가까운 Cluster 내 k 개의 유사한 Passage 찾기 ]
* 이미지 출처: https://towardsdatascience.com/similarity-search-knn-inverted-file-index-7cab80cc0e79
(5) 후처리 (Post-Processing)
- 랭킹 (Ranking): 결과는 쿼리 벡터와의 거리 또는 유사성에 따라 순위가 매겨질 수 있습니다.
- 필터링 (Filtering): 응용 프로그램별 기준에 따라 추가 필터링이 적용될 수 있습니다.
6. 추가 고려 사항
- GPU 가속: FAISS는 GPU를 활용하여 검색 과정을 가속화할 수 있어 큰 데이터셋에 대한 성능을 크게 향상시킵니다.
- 확장성: FAISS는 데이터셋 크기와 함께 확장되도록 설계되어 매우 큰 데이터셋에서도 효율성을 유지합니다.
- 통합: FAISS는 다른 도구 및 프레임워크와의 통합을 쉽게 하기 위해 파이썬 바인딩을 제공합니다.
요약하면, FAISS는 정확성, 속도, 메모리 사용 사이의 균형을 맞출 수 있는 다양한 옵션을 제공하는 대규모 유사성 검색 및 클러스터링 작업을 위한 포괄적인 도구 및 방법을 제공하며, 다양한 응용 프로그램에 적응할 수 있습니다.
3. LangChain의 FAISS VectorStore를 사용해서 RAG (Retrieval-Augmented Generation) 구현
터미널에서 먼저 pip install 로 openai, langchian, tiktoken, faiss-cup 모듈을 설치해줍니다.
! pip install -q openai langchain tiktoken faiss-cpu
(1) Text Embedding 변환과 FAISS VectorStore 저장
- embedding=OpenAIEmbeddings() 으로 passage 를 텍스트 임베딩(text embedding) 으로 변환합니다. 디폴트 모델은 deployment= "text-embedding-ada-002" 입니다.
- FAISS.from_texts() 로 dense vector 텍스트 임베딩을 FAISS VectorStore에 저장합니다.
: IVF(Inverted File) 형태로 Indexing 되어 저장되어, 사용자 쿼리와 유사한 문서를 찾을 때 빠르게 검색이 됩니다.
(2) Retriever, Prompt, Chat Model, Output Parser 정의하기
- Retriever 정의: vectorstore.as_retriever()
- Prompt 정의: ChatPromptTemplate.from_template(template)
- Model 정의: ChatOpenAI(). 디폴트는 model_name='gpt-3.5-turbo' 입니다.
- Output Parser 정의: StrOurputParser()
(3) 앞에서 정의한 파이프라인을 Chaining 연결하고, 실행하기
- Chaining: '|'
- Chain 실행: retrieval_chain.invoke("where did harrison work?")
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import ChatPromptTemplate
from langchain.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# Create a FAISS VectorStore
vectorstore = FAISS.from_texts(
["harrison worked at kensho"], embedding=OpenAIEmbeddings() # deployment="text-embedding-ada-002"
)
# Create a Retriever
retriever = vectorstore.as_retriever()
# Create a prompt, model
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI() # model_name='gpt-3.5-turbo'
# Chaining a pipeline
retrieval_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
# Run retrieva_chain
retrieval_chain.invoke("where did harrison work?")
# 'Harrison worked at Kensho.'
* LangChain tutorial: https://python.langchain.com/docs/expression_language/how_to/map
[Reference]
1. FAISS document: https://faiss.ai/index.html
2. LangChain FAISS VectorStore: https://js.langchain.com/docs/integrations/vectorstores/faiss
이번 포스팅이 많은 도움이 되었기를 바랍니다.
행복한 데이터 과학자 되세요! :-)