25

Hybrid Search, 이렇게 하면 망한다

Day 2: Hybrid Search

학습 목표

Hybrid Search 구현 시 자주 발생하는 실수를 파악한다 각 실수에 대한 해결책을 설명할 수 있다

Hybrid Search, 이렇게 하면 망한다


실수 1: 점수 정규화를 안 한다

# 잘못된 코드
bm25_score = 12.5        # BM25 스케일: 0 ~ 수십
semantic_score = 0.87     # Cosine 스케일: 0 ~ 1

# 그냥 합산하면?
hybrid = 0.5 * semantic_score + 0.5 * bm25_score
# = 0.5 * 0.87 + 0.5 * 12.5 = 6.685
# BM25가 지배적! Semantic은 영향력 없음

해결: Min-Max 정규화

def normalize(scores):
    min_s, max_s = min(scores), max(scores)
    if max_s == min_s:
        return [1.0] * len(scores)
    return [(s - min_s) / (max_s - min_s) for s in scores]

# 정규화 후: 둘 다 0~1 범위
bm25_norm = 0.95    # 최고점 대비 95%
semantic_norm = 0.87

hybrid = 0.5 * semantic_norm + 0.5 * bm25_norm  # = 0.91 (공정한 합산)

실수 2: alpha를 고정한다

# 잘못된 코드
def search(query, alpha=0.5):  # 항상 0.5
    return hybrid_search(query, alpha)

# "E-4521 에러" -> alpha=0.5? BM25가 더 잘 찾는 쿼리인데!
# "장비 고장 원인" -> alpha=0.5? Semantic이 더 잘 찾는 쿼리인데!

해결: 쿼리 유형별 적응형 alpha

def get_alpha(query):
    if has_code_pattern(query):   # E-4521, ISO 9001
        return 0.3  # BM25 가중
    if has_numeric_query(query):  # 몇 도, 얼마
        return 0.4  # 약간 BM25
    return 0.7  # Semantic 가중 (기본)

실수 3: 토크나이징 불일치

# 잘못된 코드
# 인덱싱 시: "CNC-M500" -> ["CNC", "M500"]
# 검색 시:  "CNC-M500" -> ["CNC-M500"]
# 토큰이 다르니 BM25가 매칭 실패!

해결: 동일한 토크나이저 사용

# 인덱싱과 검색에서 동일 함수 사용
tokenizer = manufacturing_tokenize  # 하나만!
corpus = [tokenizer(doc) for doc in documents]
query_tokens = tokenizer(query)  # 같은 함수!

실수 4: 검색 후보 수가 부족

# 잘못된 코드
bm25_results = search_bm25(query, top_k=5)      # 5개만!
semantic_results = search_semantic(query, top_k=5) # 5개만!

# 합치면 최대 10개, 겹치면 더 적음
# 좋은 문서를 놓칠 확률 높음

해결: initial_k를 final_k의 4~10배로

final_k = 5
initial_k = final_k * 4  # 최소 20개 후보

bm25_results = search_bm25(query, top_k=initial_k)
semantic_results = search_semantic(query, top_k=initial_k)
# 넉넉한 후보에서 Hybrid로 정렬 후 5개 선택

실수 5: 메타데이터 필터를 안 쓴다

# 잘못된 코드
# "CNC-M500 에러 코드"를 검색하면 모든 장비의 에러 코드가 나옴
# CNC-M500 문서만 보고 싶은데!

해결: 사전 필터링

# 장비 코드가 쿼리에 있으면 메타데이터로 필터
import re
equipment = re.search(r'[A-Z]+-[A-Z0-9]+', query)
if equipment:
    results = collection.query(
        query_texts=[query],
        where={"equipment_code": equipment.group()},
        n_results=top_k
    )

Score Fusion vs RRF: 언제 뭘 쓸까?

기준Score FusionRRF
장점alpha로 세밀 조절 가능파라미터 최적화 불필요
단점정규화 필수, alpha 튜닝점수 차이 반영 약함
적합쿼리 유형이 예측 가능할 때다양한 쿼리가 올 때
제조 추천적응형 alpha와 함께빠른 프로토타이핑

체크리스트

  • 점수 정규화 (Min-Max) 적용
  • 쿼리 유형별 적응형 alpha 설정
  • 인덱싱과 검색에 동일 토크나이저 사용
  • initial_k를 final_k의 4배 이상으로 설정
  • 메타데이터 필터 활용 (장비 코드, 문서 유형)
AI로 학습하기 — 꿀팁
Hybrid Search 구현 실수 점검AI 학습 팁

Hybrid Search에서 BM25와 벡터 점수 스케일 차이, alpha 설정 오류, RRF 파라미터 오용 등이 없는지 점검해 보세요.

Hybrid Search 구현 시 흔한 실수에 대한 아래 주장이 맞는지 검증해줘. (1) 'BM25 점수와 코사인 유사도 점수는 같은 스케일이므로 가중 평균을 바로 적용해도 된다.' (2) 'alpha=0.5는 BM25와 벡터 검색을 동등하게 사용하는 완벽히 균형 잡힌 설정이다.' (3) 'RRF 공식에서 k=60은 임의값이며 도메인에 따라 1~1000 범위에서 자유롭게 조정한다.' (4) 'Hybrid Search는 항상 벡터 단독 검색보다 Recall이 높다.' 제조 RAG 구현 맥락으로 교정해줘.
이 팁이 도움이 됐나요?