본문 바로가기

SK네트웍스 Family AI캠프 10기/Daily 회고

61일차. RAG - Native RAG(Vector DB)

더보기

 

61일 차 회고.

 

 단위 프로젝트 주제는 재료 기반 레시피 검색 서비스로 정해졌다.

 

 

 

 

1. Native RAG

 

 

1-1. Vector DB

 

Vector DB

  • 벡터를 고차원 공간의 포인트로 저장하고, 입력 벡터와 유사한 벡터를 효율적으로 검색한다.

 

Vector Embedding

  • 텍스트, 이미지, 오디오 등 다양한 데이터를 컴퓨터가 이해하고 처리할 수 있도록 고차원 수치 벡터 형태로 변환한다.

 

Vector DB 동작 원리

  • Indexing
    • 임베딩된 벡터를 검색 효율을 높이기 위한 데이터 구조에 매핑한다.
  • Querying
    • 유사도 함수(ex. 코사인 유사도, 유클리디안 거리등)를 적용하여 입력 벡터와 가장 유사한 벡터들을 찾는다.
  • Post Processing
    • 검색된 후보 중 실제로 가장 유사한 벡터를 최종적으로 선별하고 결과를 반환한다.

 

Chroma

  • Loader
from langchain_community.document_loaders import TextLoader

loader = TextLoader(DATA_PATH+"")
docs = loader.load()
  • Splitter
from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,
    chunk_overlap=50
)
splitted_docs = text_splitter.split_documents(docs)
  • Embedding Model
from langchain_openai import OpenAIEmbeddings

embedding = OpenAIEmbeddings(
    model="text-embedding-3-small"
)
  • Vector DB
from langchain_chroma import Chroma

chroma_path = "./chroma_db"
chroma_db = Chroma.from_documents(
    documents=splitted_docs,
    embedding=embedding,
    persist_directory=chroma_path,
    collection_name="my_db"
)

# 데이터 확인
chroma_db.get().keys()
# dict_keys(['ids', 'embeddings', 'documents', 'uris', 'data', 'metadatas', 'included'])

# 데이터 추가
from langchain_core.documents import Document

add_doc = Document(
    page_content="Hello",
    metadata={
        "source": "mydata.txt"
    },
    id="1"
)
chroma_db.add_documents([add_doc])

chroma_db.add_texts(
    texts=["Hello World", "Bye World"],
    metadatas=[
        {"source": "mydata.txt"},
        {"source": "mydata.txt"}
    ],
    ids=["2", "3"]
)

# 데이터 조건부 확인
chroma_db.get("1")

chroma_db.get(
    where={
        "source": "mydata.txt"
    }
)

# 데이터 삭제
chroma_db.delete(
    ids=["1", "2", "3"]
)

chroma_db.reset_collection()

# 유사도 검색
result = chroma_db.similarity_search(
    query="",
    k=3,
    filter={
        "source": ""
    }
)

# 유사도 검색 - MMR
result = chroma_db.max_marginal_relevance_search(
    query="",
    k=3,
    fetch_k=10
)

 

FAISS(Facebook AI Similarity Search)

  • Loader
from langchain_community.document_loaders import TextLoader

loader = TextLoader(DATA_PATH+"")
docs = loader.load()
  • Splitter
from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,
    chunk_overlap=50
)
splitted_docs = text_splitter.split_documents(docs)
  • Embedding Model
from langchain_openai import OpenAIEmbeddings

embedding = OpenAIEmbeddings(
    model="text-embedding-3-small"
)
  • Vector DB
import faiss
from langchain_community.vectorstores import FAISS

faiss_db = FAISS.from_documents(
    documents=splitted_docs,
    embedding=embedding
)

# 데이터 확인
faiss_db.docstore._dict

# 데이터 추가
with open(DATA+PATH+"") as f:
    file = f.read()

add_doc = FAISS.from_texts(
    texts=[file],
    embedding=embedding,
    metadatas=[
        {"source": DATA_PATH+""}
    ],
    ids=[""]
)

# In-Memory
from langchain_community.docstore.in_memory import InMemoryDocstore

embed_size = embedding.embed_query("")
db = FAISS(
    embedding_function=embedding,
    index=faiss.IndexFlatL2(len(embed_size)),
    docstore=InMemoryDocstore(),
    index_to_docstore_id={}
)
db.add_documents(
    splitted_docs
)

db.add_texts(
    texts=["Hello World", "Bye World"],
    metadatas=[
        {"source": "mydata.txt"},
        {"source": "mydata.txt"}
    ],
    ids=["1", "2"]
)

# 데이터 삭제
db.delete(ids=["1", "2"])

# 데이터 저장
faiss_path = "./faiss_db"
db.save_local(
    folder_path=faiss_path,
    index_name="faiss_index"
)

# 데이터 로드
load_db = FAISS.load_local(
    folder_path=faiss_path,
    index_name="faiss_index",
    embeddings=embedding,
    allow_dangerous_deserialization=True
)

# 유사도 검색
result = db.similarity_search(
    query="",
    k=3,
    filter={
        "source": DATA_PATH+""
    }
)

# 유사도 검색 - MMR
result = db.max_marginal_relevance_search(
    query="",
    k=3
)
  • Retriever
retriever_mmr = db.as_retriever(
    search_type="mmr",
    search_kwargs={
        "k": 3,
        "lambda_mult": 0.25
    }
)

result = retriever_mmr.invoke(
    input=""
)

 

 

1-2. RAG

 

Loader

from langchain_community.document_loaders  import PyMuPDFLoader

pdf_path = DATA_PATH + ".pdf"

loader = PyMuPDFLoader(pdf_path)
docs = loader.load()

docs = docs[2:]

 

Splitter

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=100
)
split_docs = text_splitter.split_documents(docs)

 

Embedding Model

from langchain_openai.embeddings import OpenAIEmbeddings

embedding = OpenAIEmbeddings()

 

Vector DB

from langchain_community.vectorstores import FAISS

vectorstore = FAISS.from_documents(
    documents=split_docs,
    embedding=embedding
)

 

Retriever

retriever = vectorstore.as_retriever()

 

Prompt

from langchain_core.prompts import PromptTemplate

prompt = PromptTemplate.from_template(
    """당신은 질문-답변(Question-Answering)을 수행하는 친절한 AI 어시스턴트입니다.
    당신의 임무는 주어진 문맥(context) 에서 주어진 질문(question) 에 답하는 것입니다.
    검색된 다음 문맥(context) 을 사용하여 질문(question) 에 답하세요.
    만약, 주어진 문맥(context) 에서 답을 찾을 수 없다면, 답을 모른다면 `주어진 정보에서 질문에 대한 정보를 찾을 수 없습니다` 라고 답하세요.
    한글로 답변해 주세요.
    단, 기술적인 용어나 이름은 번역하지 않고 그대로 사용해 주세요.
    
    #Context:
    {context}
    
    #Question:
    {question}
    
    #Answer:"""
)

 

LLM Model

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="gpt-4o-mini"
)

 

Chain

from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

rag_chain = (
    {
        "context": retriever,
        "question": RunnablePassthrough()
    },
    | prompt
    | llm
    | StrOutputParser()
)
rag_chain.invoke("")