[RAG] RAG 검색기 (3) Hybrid Search

Hunie_07·2025년 4월 14일
0

Langchain

목록 보기
28/35

(2) Keyword Search 에서 이어집니다.

📌 RAG 검색기

3️⃣ Hybrid Search (하이브리드 검색)

  • 하이브리드 검색키워드 검색의미론적 검색EnsembleRetriever로 통합

  • 정확한 키워드 매칭의미적 유사성을 동시에 고려하여 검색 수행

  • 두 검색 방식의 장점을 결합하여 더 포괄적이고 정확한 결과 도출

  • 검색 성능 향상을 위해 각 방식의 가중치 조정이 가능

  • 키워드와 의미 기반 검색의 시너지 효과로 더 향상된 검색 성능 실현 가능


앙상블 검색기 생성

from langchain.retrievers import EnsembleRetriever

# 앙상블 검색기 생성
ensemble_retrievers = [chroma_k_retriever, bm25_retriever]

ensemble_retriever = EnsembleRetriever(
    retrievers=ensemble_retrievers, 
    weights=[0.5, 0.5]  # 각 검색기의 가중치
)

# 검색기를 사용하여 검색
query = "리비안이 설립된 연도는?"

retrieved_docs = ensemble_retriever.invoke(query)

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

- 출력

[출처] 이 문서는 리비안에 대한 문서입니다.
----------------------------------
Rivian Automotive, Inc.2009년에 설립된 미국의 전기 자동차 제조업체, 자동차 기술 및 야외 레크리에이션 회사입니다.

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

4️⃣ 검색 성능 평가

1. 테스트 데이터

  • 합성된 데이터는 품질 검증수동 수정 과정을 거쳐 정제

  • 테스트용 데이터는 다양한 유형의 질문답변 패턴을 포함해야 함

  • 신뢰할 수 있는 검색 성능 평가를 위해 고품질 테스트 데이터 확보가 중요


테스트셋 로드

# 기존에 생성해 둔 테스트셋 로드
# 테스트셋 로드
import pandas as pd
df_qa_test = pd.read_excel("data/testset.xlsx")

print(f"테스트셋: {df_qa_test.shape[0]}개 문서")
df_qa_test.head(2)

2. Information Retrieval 평가지표


테스트셋의 컨텍스트를 문서 객체로 변환

# 테스트 데이터셋의 특정 행에 있는 컨텍스트 데이터를 Document 객체 리스트로 변환
from langchain_core.documents import Document

context_docs = []
for i, row in df_qa_test.iterrows():
    row_docs = []
    for doc in eval(row['reference_contexts']):
        row_docs.append(Document(page_content=doc))

    context_docs.append(row_docs)


print(f"컨텍스트 문서: {len(context_docs)}개 문서")
print("="*200)
print(context_docs[0])

- 출력

컨텍스트 문서: 49개 문서
========================================================================================================================================================================================================
[Document(metadata={}, page_content='Tesla, Inc.는 미국의 다국적 자동차 및 청정 에너지 회사입니다. 이 회사는 전기 자동차(BEV), 고정형 배터리 에너지 저장 장치, 태양 전지판, 태양광 지붕널 및 관련 제품/서비스를 설계, 제조 및 판매합니다. 2003년 7월 Martin Eberhard와 Marc Tarpenning이 Tesla Motors로 설립했으며, Nikola Tesla를 기리기 위해 명명되었습니다. Elon Musk는 2004년 Tesla의 초기 자금 조달을 주도하여 2008년에 회장 겸 CEO가 되었습니다.')]

(1) Keyword Search (Kiwi 토크나이저 + BM25검색기)

Kiwi + BM25 검색기 생성

from krag.tokenizers import KiwiTokenizer
from krag.retrievers import KiWiBM25RetrieverWithScore

# BM25 검색기 초기화 (k=3)
retriever_bm25_kiwi = KiWiBM25RetrieverWithScore(
    documents=korean_docs, 
    kiwi_tokenizer=KiwiTokenizer(model_type='knlm', typos='basic'), 
    k=3, 
)       

문서 검색

# BM25 검색기를 사용하여 문서 검색
question = df_qa_test['user_input'].iloc[0]
print("질문:", question)
print("="*200)
context = df_qa_test['reference_contexts'].iloc[0]
print("관련 문서:", context)
print("="*200)

# BM25 검색
retrieved_docs = retriever_bm25_kiwi.invoke(question)

# 검색 결과 출력 
for doc in retrieved_docs:
    print(f"BM25 점수: {doc.metadata["bm25_score"]:.2f}")    
    print(f"\n{doc.page_content}\n[출처: {doc.metadata['company']}]")
    print("-"*200)

- 출력

질문: Tesla, Inc.는 미국에서 어떤 역할을 하고 있으며, 이 회사의 주요 제품과 서비스는 무엇인가요?
========================================================================================================================================================================================================
관련 문서: ['Tesla, Inc.는 미국의 다국적 자동차 및 청정 에너지 회사입니다. 이 회사는 전기 자동차(BEV), 고정형 배터리 에너지 저장 장치, 태양 전지판, 태양광 지붕널 및 관련 제품/서비스를 설계, 제조 및 판매합니다. 2003년 7월 Martin Eberhard와 Marc Tarpenning이 Tesla Motors로 설립했으며, Nikola Tesla를 기리기 위해 명명되었습니다. Elon Musk는 2004년 Tesla의 초기 자금 조달을 주도하여 2008년에 회장 겸 CEO가 되었습니다.']
========================================================================================================================================================================================================
BM25 점수: 18.42

[출처] 이 문서는 테슬라에 대한 문서입니다.
----------------------------------
Tesla, Inc.는 미국의 다국적 자동차 및 청정 에너지 회사입니다. 이 회사는 전기 자동차(BEV), 고정형 배터리 에너지 저장 장치, 태양 전지판, 태양광 지붕널 및 관련 제품/서비스를 설계, 제조 및 판매합니다. 20037월 Martin Eberhard와 Marc Tarpenning이 Tesla Motors로 설립했으며, Nikola Tesla를 기리기 위해 명명되었습니다. Elon Musk는 2004년 Tesla의 초기 자금 조달을 주도하여 2008년에 회장 겸 CEO가 되었습니다.
[출처: 테슬라]
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BM25 점수: 17.78
...

K-RAG 활용 평가지표 계산

# 전체 테스트 데이터셋에 대하여 평가지표 계산
from langchain_core.retrievers import BaseRetriever
from krag.evaluators import RougeOfflineRetrievalEvaluators

def evaluate_qa_test(df_qa_test: pd.DataFrame, retriever: BaseRetriever, k=2) -> dict:
    """
    테스트 데이터셋에 대한 검색 결과 평가
    """

    context_docs = []
    retrieved_docs = []

    df_test = df_qa_test.copy()
    
    for idx, _ in df_test.iterrows():
        question = df_test['user_input'].iloc[idx]
        context_doc = [Document(page_content=doc) for doc in eval(df_test['reference_contexts'].iloc[idx])]
        context_docs.append(context_doc)
        retrieved_doc = retriever.invoke(question)  
        retrieved_docs.append(retrieved_doc)  


    # 평가자 인스턴스 생성
    evaluator = RougeOfflineRetrievalEvaluators(
        actual_docs=context_docs,
        predicted_docs=retrieved_docs, 
        match_method='rouge1',
        threshold=0.8,
    )

    # 평가지표 계산
    hit_rate = evaluator.calculate_hit_rate(k=k)['hit_rate']
    mrr = evaluator.calculate_mrr(k=k)['mrr']
    map_score = evaluator.calculate_map(k=k)['map']
    ndcg = evaluator.calculate_ndcg(k=k)['ndcg']

    print(f"K={k}")
    print("-"*200)
    print(f"Hit Rate: {hit_rate:.3f}")
    print(f"MRR: {mrr:.3f}")
    print(f"MAP: {map_score:.3f}")
    print(f"NDCG: {ndcg:.3f}")
    print("="*200)
    print()

    result = {
        'hit_rate': hit_rate,
        'mrr': mrr,
        'map': map_score,
        'ndcg': ndcg,
        
    }

    return pd.Series(result)

평가 ( k=1 )

# 평가 (k=1)
retriever_bm25_kiwi.k = 1
result_bm25_k1 = evaluate_qa_test(df_qa_test, retriever_bm25_kiwi, k=1)

- 출력

K=1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hit Rate: 0.286
MRR: 0.735
MAP: 0.510
NDCG: 1.000
========================================================================================================================================================================================================
  • k=2 , k=3, k=4, k=5 평가 진행

(2) Semantic Search (Chroma 벡터저장소 검색기)

검색기 초기화

# Chroma 검색기 초기화
retriever_chroma_db = chroma_db.as_retriever(
    search_kwargs={"k": 5},
)

평가 ( k = 1)

# 평가 (k=1)
result_chroma_db_k1 = evaluate_qa_test(df_qa_test, retriever_chroma_db, k=1)

- 출력

K=1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hit Rate: 0.286
MRR: 0.673
MAP: 0.480
NDCG: 1.000
========================================================================================================================================================================================================
  • k=2 , k=3 , k=4 , k=5 평가 진행

0개의 댓글