벡터 저장소와 임베딩을 활용한 LangChain 데이터 대화 구현

이전에 문서 로드 및 분할 방법에 대해 다루었으며, 이번에는 벡터 저장소와 임베딩 기술을 중심으로 설명합니다.

LangChain을 사용하여 외부 데이터와 상호작용하는 과정은 다음과 같은 단계로 이루어집니다:

  • 문서 로딩 (Document Loading)
  • 분할 (Splitting)
  • 저장 (Storage)
  • 검색 (Retrieval)
  • 출력 (Output)

지난 포스트에서 문서 로드 및 분할에 대해 설명했으므로, 이제 벡터화 및 저장 단계를 살펴보겠습니다.

문서 로드 및 분할

먼저 CS229 머신러닝 강의 자료 PDF 파일을 로드합니다:

from langchain.document_loaders import PyPDFLoader

pdf_loaders = [
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture01.pdf"),
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture01.pdf"),  # 중복 확인용
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture02.pdf"),
    PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture03.pdf")
]

document_list = []
for loader in pdf_loaders:
    document_list.extend(loader.load())

다음으로 문서를 분할합니다:

from langchain.text_splitter import RecursiveCharacterTextSplitter

split_config = RecursiveCharacterTextSplitter(
    chunk_size=1500,
    chunk_overlap=150
)

chunked_docs = split_config.split_documents(document_list)
print(f"총 {len(chunked_docs)} 개의 청크 생성됨")

임베딩 처리

텍스트 임베딩은 자연어를 고차원 벡터 공간으로 변환하는 작업입니다. OpenAI 임베딩 모델을 사용해보겠습니다:

from langchain.embeddings.openai import OpenAIEmbeddings
import numpy as np

embedder = OpenAIEmbeddings()

sentences = [
    "나는 강아지를 좋아한다.",
    "나는 작은 동물을 좋아한다.",
    "오늘 기분이 좋지 않다."
]

vectors = [embedder.embed_query(s) for s in sentences]

# 유사도 계산
similarity_1_2 = np.dot(vectors[0], vectors[1])
similarity_1_3 = np.dot(vectors[0], vectors[2])
similarity_2_3 = np.dot(vectors[1], vectors[2])

print(f"문장 1과 2의 유사도: {similarity_1_2:.2f}")
print(f"문장 1과 3의 유사도: {similarity_1_3:.2f}")
print(f"문장 2와 3의 유사도: {similarity_2_3:.2f}")

벡터 데이터베이스 구성

Chroma 벡터 데이터베이스를 생성하고 청크된 문서들을 저장합니다:

from langchain.vectorstores import Chroma

db_path = 'docs/chroma/'
vector_db = Chroma.from_documents(
    documents=chunked_docs,
    embedding=embedder,
    persist_directory=db_path
)

print(f"데이터베이스 내 문서 수: {vector_db._collection.count()}")

유사도 검색 수행

질문에 대한 관련 문서를 검색합니다:

query = "도움을 요청할 수 있는 이메일 주소가 있나요?"
relevant_docs = vector_db.similarity_search(query, k=3)

print(f"검색 결과 문서 수: {len(relevant_docs)}")
print("첫 번째 문서 내용:")
print(relevant_docs[0].page_content)

데이터베이스 영속화

생성된 벡터 데이터베이스를 로컬에 저장합니다:

vector_db.persist()

검색 실패 사례

중복 문서로 인한 문제:

duplicate_query = "MATLAB에 대해 어떤 언급이 있었나요?"
duplicate_results = vector_db.similarity_search(duplicate_query, k=5)

# 중복 문서 확인
for idx, doc in enumerate(duplicate_results[:2]):
    print(f"문서 {idx+1} 메타데이터: {doc.metadata}")

특정 문서 제한 검색 실패:

specific_query = "세 번째 강의에서 회귀 분석에 대해 어떤 내용이 있었나요?"
filtered_results = vector_db.similarity_search(specific_query, k=5)

for doc in filtered_results:
    print(f"문서 출처: {doc.metadata['source']}")

태그: LangChain vector-database embeddings chroma OpenAI

6월 20일 01:54에 게시됨