RAG & LANCHAIN (3)- Langchain 개념 & 문법 정리 ch.09 벡터저장소(VectorStore)

이영락·2024년 8월 30일
0

인공지능 공부

목록 보기
15/33

CH09 벡터 저장소(VectorStore)

벡터 저장소(VectorStore)는 Retrieval-Augmented Generation (RAG) 시스템의 네 번째 단계로, 이전 단계에서 생성된 임베딩 벡터를 효율적으로 저장하고 관리하는 과정입니다. 이 단계는 향후 검색에서 벡터들을 빠르게 조회하고 관련 문서를 신속하게 찾아내는 데 필수적입니다.

벡터 저장소의 필요성

  1. 빠른 검색 속도: 임베딩 벡터를 효과적으로 저장하고 색인화하면 대량의 데이터에서도 관련 정보를 빠르게 검색할 수 있습니다.

  2. 스케일러빌리티: 데이터가 증가함에 따라 벡터 저장소는 이를 수용할 수 있어야 합니다. 효율적인 저장 구조는 데이터베이스의 확장성을 보장하고, 시스템의 성능 저하 없이 대규모 데이터를 관리할 수 있도록 합니다.

  3. 의미 검색(Semantic Search) 지원: 벡터 저장소는 키워드 기반 검색이 아닌 의미적으로 유사한 문장을 검색할 수 있도록 지원합니다. 이는 사용자 질문과 의미상으로 유사한 단락을 조회할 수 있게 해줍니다.

벡터 저장소의 중요성

벡터 저장소는 RAG 시스템의 검색 기능과 직접적으로 연결되어 있으며, 전체 시스템의 응답 시간과 정확성에 큰 영향을 미칩니다. 데이터를 잘 관리하고 필요할 때 즉시 접근할 수 있도록 함으로써, 사용자에게 신속하고 정확한 정보를 제공합니다.

코드 예시: FAISS를 사용한 벡터 저장소 생성

다음은 FAISS(VectorStore 중 하나)를 사용하여 벡터 저장소를 생성하고 데이터를 저장하는 코드 예시입니다.

from langchain_community.vectorstores import FAISS

# 단계 4: DB 생성(Create DB) 및 저장
# documents: 텍스트를 임베딩한 결과물들이 포함된 리스트
# embeddings: 임베딩 객체

# 벡터스토어를 생성합니다.
vectorstore = FAISS.from_documents(documents=documents, embedding=embeddings)

# 생성된 벡터스토어를 사용해 검색 작업을 수행할 수 있습니다.

이 코드를 통해 생성된 벡터 저장소는 다양한 검색 작업에 사용될 수 있으며, 특히 RAG 시스템에서 중요한 역할을 합니다.

참고자료

  • 벡터 저장소 사용 방법: 다양한 벡터 저장소의 사용 방법과 적용 예시를 제공하는 문서.
  • LangChain VectorStores: LangChain 프레임워크에서 제공하는 벡터 저장소 관련 기능과 활용 방법에 대한 자세한 설명을 제공합니다.

CH09 벡터 저장소: Chroma 사용법

Chroma는 벡터 데이터를 효율적으로 저장하고 검색하는 오픈 소스 벡터 데이터베이스로, 개발자의 생산성과 행복을 목표로 설계되었습니다. 아래는 Chroma 벡터 저장소의 주요 기능과 사용 예제를 다룹니다.

Chroma 설치 및 환경 설정

# 필요한 패키지 설치
%pip install chromadb langchain-openai langchain_community

# API 키를 환경 변수로 관리하기 위한 설정
from dotenv import load_dotenv
load_dotenv()

데이터 로드 및 텍스트 분할

from langchain_community.document_loaders import TextLoader
from langchain_openai.embeddings import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 텍스트 파일을 로드하고 문서로 변환
loader1 = TextLoader("data/nlp-keywords.txt")
loader2 = TextLoader("data/finance-keywords.txt")

# 텍스트 분할 설정
text_splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=0)
split_doc1 = loader1.load_and_split(text_splitter)
split_doc2 = loader2.load_and_split(text_splitter)

# 문서 개수 확인
len(split_doc1), len(split_doc2)

벡터 저장소 생성

기본 생성

from langchain_chroma import Chroma

# 기본 벡터 저장소 생성
db = Chroma.from_documents(
    documents=split_doc1, embedding=OpenAIEmbeddings(), collection_name="my_db"
)

디스크에 저장

# 저장할 경로 지정
DB_PATH = "./chroma_db"

# 벡터 저장소를 디스크에 저장
persist_db = Chroma.from_documents(
    split_doc1, OpenAIEmbeddings(), persist_directory=DB_PATH, collection_name="my_db"
)

디스크에서 로드

# 디스크에서 벡터 저장소 로드
persist_db = Chroma(
    persist_directory=DB_PATH,
    embedding_function=OpenAIEmbeddings(),
    collection_name="my_db",
)

벡터 저장소 조회

# 저장된 데이터 확인
persist_db.get()

텍스트로부터 벡터 저장소 생성

# 문자열 리스트로 벡터 저장소 생성
db2 = Chroma.from_texts(
    ["안녕하세요. 정말 반갑습니다.", "제 이름은 테디입니다."],
    embedding=OpenAIEmbeddings(),
)

유사도 검색

# 유사도 검색
db.similarity_search("TF IDF 에 대하여 알려줘")

# 결과 개수 지정
db.similarity_search("TF IDF 에 대하여 알려줘", k=2)

# 메타데이터 필터 적용
db.similarity_search(
    "TF IDF 에 대하여 알려줘", filter={"source": "data/nlp-keywords.txt"}, k=2
)

벡터 저장소에 문서 추가 및 업데이트

from langchain_core.documents import Document

# 문서 추가 및 업데이트
db.add_documents(
    [
        Document(
            page_content="안녕하세요! 이번엔 도큐먼트를 새로 추가해 볼께요",
            metadata={"source": "mydata.txt"},
            id="1",
        )
    ]
)

벡터 저장소에서 문서 삭제

# 문서 삭제
db.delete(ids=["1"])

컬렉션 초기화

# 컬렉션 초기화
db.reset_collection()

벡터 저장소를 검색기로 변환

# 기본 검색기 생성
retriever = db.as_retriever()

# MMR 알고리즘을 사용한 다양성 높은 검색
retriever = db.as_retriever(
    search_type="mmr", search_kwargs={"k": 6, "lambda_mult": 0.25, "fetch_k": 10}
)

# 특정 임계값 이상의 유사도를 가진 문서만 검색
retriever = db.as_retriever(
    search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.8}
)

멀티모달 검색

Chroma는 텍스트 외에도 이미지 등의 멀티모달 데이터를 저장하고 검색할 수 있습니다.

이미지 임베딩 및 저장

from langchain_experimental.open_clip import OpenCLIPEmbeddings

# OpenCLIP 임베딩 함수 객체 생성
image_embedding_function = OpenCLIPEmbeddings(
    model_name="ViT-H-14-378-quickgelu", checkpoint="dfn5b"
)

# 이미지 경로 설정
image_uris = ["path_to_image1.jpg", "path_to_image2.jpg"]

# 벡터 저장소 생성 및 이미지 추가
image_db = Chroma(
    collection_name="multimodal",
    embedding_function=image_embedding_function,
)
image_db.add_images(uris=image_uris)

이미지 검색

# 이미지 검색
retriever = image_db.as_retriever(search_kwargs={"k": 3})
image_retriever = ImageRetriever(retriever)
result = image_retriever.invoke("A Dog on the street")

FAISS를 활용한 벡터 검색 및 클러스터링 튜토리얼

Facebook AI Similarity Search (FAISS)는 밀집 벡터의 효율적인 유사도 검색 및 클러스터링을 위한 강력한 라이브러리입니다. 이 튜토리얼에서는 FAISS를 사용하여 벡터 저장소를 생성, 관리 및 검색하는 방법을 설명합니다.

환경 설정 및 데이터 로드

먼저 필요한 패키지를 설치하고 환경을 설정합니다.

# 필요한 패키지 설치
%pip install faiss-cpu langchain langchain_openai langchain_community

# API 키를 환경변수로 관리하기 위한 설정
from dotenv import load_dotenv
load_dotenv()

데이터 로드 및 텍스트 분할

텍스트 데이터를 로드하고 분할합니다.

from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 텍스트 파일을 로드하고 문서로 변환
loader1 = TextLoader("data/nlp-keywords.txt")
loader2 = TextLoader("data/finance-keywords.txt")

# 텍스트 분할 설정
text_splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=0)
split_doc1 = loader1.load_and_split(text_splitter)
split_doc2 = loader2.load_and_split(text_splitter)

# 문서 개수 확인
len(split_doc1), len(split_doc2)

FAISS 벡터 저장소 생성

기본 생성

FAISS 벡터 저장소를 생성하고 초기화합니다.

import faiss
from langchain_community.vectorstores import FAISS
from langchain_community.docstore.in_memory import InMemoryDocstore
from langchain_openai import OpenAIEmbeddings

# 임베딩 생성
embeddings = OpenAIEmbeddings()
dimension_size = len(embeddings.embed_query("hello world"))

# FAISS 벡터 저장소 생성
db = FAISS(
    embedding_function=embeddings,
    index=faiss.IndexFlatL2(dimension_size),
    docstore=InMemoryDocstore(),
    index_to_docstore_id={},
)

문서로부터 생성

FAISS 벡터 저장소를 문서로부터 생성합니다.

db = FAISS.from_documents(documents=split_doc1, embedding=embeddings)

유사도 검색

FAISS 벡터 저장소에서 유사한 문서를 검색합니다.

# 유사도 검색
results = db.similarity_search("TF IDF 에 대하여 알려줘", k=2)
for result in results:
    print(result.page_content)

문서 추가 및 삭제

벡터 저장소에 문서를 추가하고 삭제할 수 있습니다.

from langchain_core.documents import Document

# 문서 추가
db.add_documents([Document(page_content="새로운 문서", metadata={"source": "mydata.txt"})])

# 문서 삭제
db.delete(["new_doc1"])

로컬 저장 및 로드

FAISS 저장소를 로컬에 저장하고 다시 로드할 수 있습니다.

# 로컬에 저장
db.save_local(folder_path="faiss_db", index_name="faiss_index")

# 로컬에서 로드
loaded_db = FAISS.load_local(
    folder_path="faiss_db",
    index_name="faiss_index",
    embeddings=embeddings,
    allow_dangerous_deserialization=True,
)

FAISS 객체 병합

두 개의 FAISS 객체를 병합할 수 있습니다.

db2 = FAISS.from_documents(documents=split_doc2, embedding=embeddings)
db.merge_from(db2)

검색기로 변환

FAISS 벡터 저장소를 검색기로 변환하여 다양한 검색 옵션을 사용할 수 있습니다.

# 기본 검색기 생성
retriever = db.as_retriever()

# MMR 검색
retriever = db.as_retriever(search_type="mmr", search_kwargs={"k": 6, "lambda_mult": 0.25})
results = retriever.invoke("Word2Vec 에 대하여 알려줘")
for result in results:
    print(result.page_content)

Pinecone을 활용한 벡터 데이터베이스 구축 및 검색 튜토리얼

Pinecone은 대규모 데이터셋에 대해 고성능 벡터 저장 및 검색을 제공하는 벡터 데이터베이스입니다. 이 튜토리얼에서는 Pinecone을 사용하여 벡터 데이터를 저장, 관리, 검색하는 방법을 안내합니다.

Pinecone vs Chroma vs FAISS

Pinecone은 다음과 같은 장점이 있습니다:

  • 확장성: 대규모 데이터셋에서 뛰어난 성능을 발휘합니다.
  • 관리 용이성: 완전 관리형 서비스로, 인프라 관리를 최소화할 수 있습니다.
  • 실시간 업데이트: 실시간 데이터 삽입, 업데이트, 삭제가 가능합니다.
  • 고가용성: 클라우드 기반으로 높은 가용성과 내구성을 제공합니다.
  • API 친화적: RESTful/Python API를 통해 쉽게 통합할 수 있습니다.

그러나 비용이 높고, 커스터마이징에 제한이 있으며, 데이터 주권 문제도 있을 수 있습니다. 반면, Chroma와 FAISS는 오픈소스이며, 로컬에서 실행 가능하고 초기 비용이 낮습니다. 하지만 확장성에서는 Pinecone에 비해 제한적입니다.

Pinecone 설정 및 데이터 전처리

API 키 설정

from dotenv import load_dotenv
load_dotenv()

데이터 전처리

PDF 파일을 로드하고 텍스트로 분할합니다.

from langchain_community.document_loaders import PyMuPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import glob

text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
split_docs = []

files = sorted(glob.glob("data/*.pdf"))
for file in files:
    loader = PyMuPDFLoader(file)
    split_docs.extend(loader.load_and_split(text_splitter))

len(split_docs)

문서 전처리

전처리된 문서에서 필요한 메타데이터를 추출합니다.

from langchain_teddynote.community.pinecone import preprocess_documents

contents, metadatas = preprocess_documents(
    split_docs=split_docs,
    metadata_keys=["source", "page", "author"],
    min_length=5,
    use_basename=True,
)

Pinecone 인덱스 생성

Pinecone에서 새로운 인덱스를 생성합니다.

from langchain_teddynote.community.pinecone import create_index
import os

pc_index = create_index(
    api_key=os.environ["PINECONE_API_KEY"],
    index_name="teddynote-db-index",
    dimension=4096,
    metric="dotproduct",
)

Sparse Encoder 생성 및 문서 업로드

Sparse Encoder 생성

from langchain_teddynote.community.pinecone import create_sparse_encoder, fit_sparse_encoder

sparse_encoder = create_sparse_encoder(stopwords(), mode="kiwi")
saved_path = fit_sparse_encoder(sparse_encoder=sparse_encoder, contents=contents, save_path="./sparse_encoder.pkl")

문서 업로드

문서를 Pinecone DB에 업로드합니다.

from langchain_teddynote.community.pinecone import upsert_documents
from langchain_upstage import UpstageEmbeddings

upsert_documents(
    index=pc_index,
    namespace="teddynote-namespace-01",
    contents=contents,
    metadatas=metadatas,
    sparse_encoder=sparse_encoder,
    embedder=UpstageEmbeddings(),
    batch_size=32,
)

검색기 생성 및 검색

Pinecone을 사용한 하이브리드 검색기를 생성하고, 검색을 수행합니다.

from langchain_teddynote.community.pinecone import init_pinecone_index, PineconeKiwiHybridRetriever

pinecone_params = init_pinecone_index(
    index_name="teddynote-db-index",
    namespace="teddynote-namespace-01",
    api_key=os.environ["PINECONE_API_KEY"],
    sparse_encoder_path="./sparse_encoder.pkl",
    stopwords=stopwords(),
    tokenizer="kiwi",
    embeddings=UpstageEmbeddings(),
    top_k=5,
    alpha=0.5,
)

pinecone_retriever = PineconeKiwiHybridRetriever(**pinecone_params)

# 예시 검색
search_results = pinecone_retriever.invoke("gpt-4o 미니 출시 관련 정보에 대해서 알려줘")
for result in search_results:
    print(result.page_content)
    print(result.metadata)
    print("\n====================\n")

동적 검색 및 필터링

동적 파라미터를 사용하여 검색 결과를 조정할 수 있습니다.

# 필터링을 적용한 검색
search_results = pinecone_retriever.invoke(
    "앤스로픽의 claude 출시 관련 내용을 알려줘",
    search_kwargs={"filter": {"page": {"$lt": 5}}, "k": 2},
)
for result in search_results:
    print(result.page_content)
    print(result.metadata)
    print("\n====================\n")
profile
AI Engineer / 의료인공지능

0개의 댓글

관련 채용 정보