핵심 요약
검색 엔진에서 문서의 관련성을 판단하기 위해 수십 년간 BM25 알고리즘이 표준으로 사용되어 왔다. BM25는 단어 빈도, 역문서 빈도, 문서 길이를 조합하여 점수를 계산하며 단어 빈도 포화 기법을 통해 키워드 조작을 방지한다. 반면 벡터 검색은 텍스트를 고차원 수치 벡터로 변환하여 단어가 일치하지 않아도 의미적 유사성을 기반으로 문서를 찾아낸다. 내부 테스트 결과 BM25는 빠르고 설명 가능성이 높지만 문맥 이해가 부족하며 벡터 검색은 의미 파악에 능하지만 비용이 발생한다. 현대적인 프로덕션 시스템에서는 두 방식의 장점을 결합한 하이브리드 검색이 표준으로 자리 잡고 있다.
배경
기본적인 Python 프로그래밍 지식, OpenAI API 키 사용 권한, TF-IDF 및 벡터 공간 모델에 대한 기초 이해
대상 독자
RAG 시스템 성능 최적화를 고민하는 AI 엔지니어 및 데이터 과학자
의미 / 영향
검색 기술의 양대 산맥인 키워드 기반과 의미 기반 검색의 차이를 명확히 이해함으로써 서비스 특성에 맞는 최적의 검색 엔진 아키텍처를 설계할 수 있는 가이드를 제공한다.
섹션별 상세
def tokenize(text: str) -> list[str]:
"""Lowercase and split on non-alphanumeric characters."""
return re.findall(r'\w+', text.lower())
# Build BM25 index over the corpus
tokenized_corpus = [tokenize(chunk) for chunk in CHUNKS]
bm25 = BM25Okapi(tokenized_corpus)텍스트를 소문자로 변환하고 토큰화하여 BM25 인덱스를 구축하는 과정
def embedding_search(query: str, top_k: int = 3) -> list[dict]:
"""Return top-k chunks ranked by cosine similarity to the query embedding."""
query_emb = get_embedding(query)
scores = [cosine_similarity(query_emb, emb) for emb in chunk_embeddings]
ranked = np.argsort(scores)[::-1][:top_k]
return [
{"chunk_id": int(i), "score": round(float(scores[i]), 4), "text": CHUNKS[i]}
for i in ranked
]OpenAI 임베딩을 사용하여 코사인 유사도 기반의 벡터 검색을 수행하는 함수
실무 Takeaway
- 시스템 프롬프트나 고유 명사가 중요한 검색 환경에서는 BM25를 우선적으로 고려하여 연산 비용을 절감할 수 있다.
- 사용자의 질문 의도가 다양하고 유의어 사용이 빈번한 경우 벡터 검색을 도입하여 검색 정확도를 높여야 한다.
- 프로덕션 환경에서는 BM25의 속도와 벡터 검색의 의미 이해력을 결합한 하이브리드 검색 아키텍처를 구축하는 것이 가장 효율적이다.
언급된 리소스
AI 요약 · 북마크 · 개인 피드 설정 — 무료
출처 · 인용 안내
인용 시 "요약 출처: AI Trends (aitrends.kr)"를 표기하고, 사실 확인은 원문 보기 기준으로 진행해 주세요. 자세한 기준은 운영 정책을 참고해 주세요.