본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

BM25는 아직 쓸모 있어 — 벡터와 함께 써

~22 min · hybrid, bm25, keyword

Level 0Scout
0 XP0/41 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

벡터 검색만으로 일부 쿼리를 놓치는 이유

임베딩은 의미에는 강하지만 토큰의 정확한 형태에는 평범해. 'ERR_CONNECTION_REFUSED'라는 오류 코드가 그대로 들어 있는 청크를, 같은 개념을 다른 말로 설명한 청크보다 항상 위에 놓지는 못해. 제품 이름, SKU, 버전 번호, 희귀 기술 용어는 글자 자체가 중요하고, 바로 그 검색을 위해 1994년에 나온 확률적 키워드 점수기 BM25가 만들어졌어.

BM25가 실제로 계산하는 것

BM25는 쿼리의 각 단어를 문서 안의 출현 빈도, 전체 문서에서의 희귀도, 문서 길이로 평가해. 정확히 일치하는 표현을 높게 보고 불필요하게 긴 문서는 낮게 보지만 의미 자체는 이해하지 못해. 벡터 검색과 실패 방식이 정반대인 셈이야.

가장 저렴한 하이브리드 구성

  1. 벡터 검색에서 상위 N개 후보를 받아.
  2. BM25에서도 상위 N개 후보를 받아.
  3. ID를 합쳐 중복을 제거하고, 결합 점수나 cross-encoder로 다시 순위를 매겨.

검색기 두 개와 병합 단계 하나만 더해도 사실 확인형 쿼리에서 벡터 검색만 쓸 때보다 10–20% 나아지는 경우가 흔해.

Code

rank-bm25 로 BM25 (순수 Python)·python
from rank_bm25 import BM25Okapi

corpus = [doc.lower().split() for doc in documents]
bm25 = BM25Okapi(corpus)

def bm25_search(query: str, k: int = 10):
    scores = bm25.get_scores(query.lower().split())
    top = scores.argsort()[::-1][:k]
    return [(int(i), float(scores[i])) for i in top]
Postgres 의 하이브리드 (pgvector + tsvector)·sql
-- full-text 컬럼 한 번 추가
ALTER TABLE chunks ADD COLUMN tsv tsvector
    GENERATED ALWAYS AS (to_tsvector('english', text)) STORED;
CREATE INDEX chunks_tsv_idx ON chunks USING gin(tsv);

-- 하이브리드 query: 두 ranking union 후 re-rank
WITH vec AS (
    SELECT id, 1 - (embedding <=> $1) AS vscore
    FROM   chunks
    ORDER BY embedding <=> $1
    LIMIT 50
),
bm AS (
    SELECT id, ts_rank_cd(tsv, plainto_tsquery('english', $2)) AS bscore
    FROM   chunks
    WHERE  tsv @@ plainto_tsquery('english', $2)
    ORDER BY bscore DESC
    LIMIT 50
)
SELECT  c.id,
        COALESCE(vec.vscore, 0) AS vscore,
        COALESCE(bm.bscore,  0) AS bscore,
        c.text
FROM    chunks c
LEFT JOIN vec ON vec.id = c.id
LEFT JOIN bm  ON bm.id  = c.id
WHERE   vec.id IS NOT NULL OR bm.id IS NOT NULL;

External links

Exercise

같은 문서 1000개로 벡터 검색기와 BM25 검색기를 만들어. 쿼리 20개를 실행하고, 쿼리마다 양쪽의 상위 다섯 결과와 합집합을 기록해 직접 관련성을 표시해. 벡터 검색만 쓸 때와 합쳤을 때의 적중률 차이가 하이브리드 검색에 투자할 근거가 돼.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.