[RAG] RAG 검색기 (1) Semantic Search

Hunie_07·2025년 4월 14일
0

Langchain

목록 보기
26/35

📌 RAG 검색기

  1. 의미론적 검색 (Semantic Search)

    • Vector Store를 기반으로 한 검색 방식으로, 텍스트의 의미적 유사성을 고려하여 검색을 수행함
    • 임베딩 벡터 간의 유사도를 계산하여 의미적으로 관련성이 높은 문서를 찾아내는 특징이 있음
    • 동의어나 문맥적 의미를 파악할 수 있어 자연어 질의에 효과적임
  2. 키워드 검색 (Keyword Search)

    • BM25와 같은 전통적인 검색 알고리즘을 사용하여 키워드 매칭을 기반으로 검색을 수행함
    • 정확한 단어나 구문 매칭에 강점이 있으며, 계산 효율성이 높은 특징을 가짐
    • 직접적인 키워드 일치를 찾는 데 유용하나, 의미적 유사성을 파악하는 데는 한계가 있음
  3. 하이브리드 검색 (Hybrid Search)

    • 키워드 기반 검색과 의미론적 검색을 결합한 방식으로, EnsembleRetriever를 통해 구현됨
    • 두 검색 방식의 장점을 활용하여 더 정확하고 포괄적인 검색 결과를 제공함
    • 정확한 키워드 매칭과 의미적 연관성을 모두 고려하여 검색 성능을 향상시키는 특징이 있음

1️⃣ Semantic Search (의미론적 검색)

  • 의미론적 검색은 텍스트의 벡터 표현을 활용해 의미적 유사성 기반 검색 수행

  • Vector Store에 저장된 임베딩 벡터 간 유사도 계산으로 관련 문서 검색

  • 검색어와 문서 간의 문맥적 의미동의어 관계를 효과적으로 파악

  • 자연어 질의에 강점을 보이며 기존 키워드 검색의 한계를 보완

  • 전통적인 검색 방식과 달리 의미 기반 매칭으로 더 정확하고 포괄적인 검색 결과 제공

1. 벡터 저장소 초기화

  • cosine distance 기준으로 인덱싱

  • 파일은 기존 테슬라, 리비안 사용

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 데이터 로드
def load_text_files(txt_files):
    data = []

    for text_file in txt_files:
        loader = TextLoader(text_file, encoding='utf-8')
        data += loader.load()

    return data

korean_txt_files = glob(os.path.join('data', '*_KR.md')) 
korean_data = load_text_files(korean_txt_files)

# 문장을 구분하여 분할 - 정규표현식 사용 (문장 구분자: 마침표, 느낌표, 물음표 다음에 공백이 오는 경우)
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    encoding_name="cl100k_base",    # TikToken 인코더 이름
    separators=['\n\n', '\n', r'(?<=[.!?])\s+'],   # 구분자
    chunk_size=300,            # 문서 분할 크기
    chunk_overlap=50,          # 문서 분할 중첩  
    is_separator_regex=True,      # 구분자가 정규식인지 여부
    keep_separator=True,          # 구분자 유지 여부
)

korean_chunks = text_splitter.split_documents(korean_data)

print("한국어 청크 수:", len(korean_chunks))

- 출력

한국어 청크 수: 39

분할 문서 확인

for i, doc in enumerate(korean_chunks):
    print(f"[{i}]", doc.page_content)
    print("="*200)

- 출력

[0] Rivian Automotive, Inc.2009년에 설립된 미국의 전기 자동차 제조업체, 자동차 기술 및 야외 레크리에이션 회사입니다.

**주요 정보:**
========================================================================================================================================================================================================
[1] - **회사 유형:** 상장
- **거래소:** NASDAQ: RIVN
...

Document 객체에 메타데이터 추가

from langchain_core.documents import Document
# Document 객체에 메타데이터 추가
korean_docs = []

for chunk in korean_chunks:
    doc = Document(page_content=chunk.page_content, metadata=chunk.metadata)
    doc.metadata['company'] = '테슬라' if '테슬라' in doc.metadata['source'] else '리비안'
    doc.metadata['language'] = 'ko'
    doc.page_content = f"[출처] 이 문서는 {doc.metadata['company']}에 대한 문서입니다.\n----------------------------------\n{doc.page_content}"
    korean_docs.append(doc)

print("한국어 문서 수:", len(korean_docs))
print(korean_docs[0].metadata)
print(korean_docs[-1].metadata)
print("="*200)
print(korean_docs[0].page_content)

- 출력

한국어 문서 수: 39
{'source': 'data\\리비안_KR.md', 'company': '리비안', 'language': 'ko'}
{'source': 'data\\테슬라_KR.md', 'company': '테슬라', 'language': 'ko'}
========================================================================================================================================================================================================
[출처] 이 문서는 리비안에 대한 문서입니다.
----------------------------------
Rivian Automotive, Inc.2009년에 설립된 미국의 전기 자동차 제조업체, 자동차 기술 및 야외 레크리에이션 회사입니다.

**주요 정보:**

저장소 생성

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

# OpenAI Embeddings 모델을 로드
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# Chroma 벡터 저장소 생성하기
chroma_db = Chroma.from_documents(
    documents=korean_docs,
    embedding=embeddings,    
    collection_name="db_korean_cosine_metadata", 
    persist_directory="./chroma_db",
    collection_metadata = {'hnsw:space': 'cosine'}, # l2, ip, cosine 중에서 선택 
)

2. 벡터 저장소 로드

  • 미리 인덱싱해서 저장해 둔 저장소를 가져와서 사용
  • 이때 기존에 사용한 임베딩 모델을 초기화 필요
# 벡터 저장소 로드 
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

chroma_db = Chroma(
    collection_name="db_korean_cosine_metadata",
    embedding_function=embeddings,
    persist_directory="./chroma_db",
)

3. Semantic Search 실행

  • 벡터 저장소 검색기 객체 활용
  • 임베딩 벡터 간의 유사도를 기반으로 문서 검색
# 검색기 지정하여 테스트 
chroma_k_retriever = chroma_db.as_retriever(
    search_kwargs={"k": 2},
)

query = "리비안은 언제 사업을 시작했나요?"
retrieved_docs = chroma_k_retriever.invoke(query)

for doc in retrieved_docs:
    print(f"{doc.page_content} [출처: {doc.metadata['source']}]")
    print("="*200)

- 출력

[출처] 이 문서는 리비안에 대한 문서입니다.
----------------------------------
- **회사 유형:** 상장
- **거래소:** NASDAQ: RIVN
- **설립:** 20096, 플로리다 주 록ledge
- **설립자:** R. J. 스캐린지
- **본사:** 미국 캘리포니아 주 어바인
- **서비스 지역:** 북미
- **주요 인물:** R. J. 스캐린지 (CEO)
- **제품:** 전기 자동차, 배터리
- **생산량 (2023):** 57,232- **서비스:** 전기 자동차 충전, 자동차 보험
- **수익 (2023):** 443천만 미국 달러
- **순이익 (2023):** -54억 미국 달러
- **총 자산 (2023):** 168억 미국 달러 [출처: data\리비안_KR.md]
========================================================================================================================================================================================================
[출처] 이 문서는 리비안에 대한 문서입니다.
----------------------------------
Rivian Automotive, Inc.2009년에 설립된 미국의 전기 자동차 제조업체, 자동차 기술 및 야외 레크리에이션 회사입니다.

**주요 정보:** [출처: data\리비안_KR.md]
========================================================================================================================================================================================================

0개의 댓글