35분
BM25 알고리즘: 키워드 검색의 수학
Day 2: Hybrid Search
BM25 알고리즘: 키워드 검색의 수학
RAG 심화 > Day 2: Hybrid Search
학습 목표
BM25 공식의 각 요소(TF, IDF, 문서 길이 정규화)를 이해한다 k1, b 파라미터의 역할을 설명할 수 있다 제조 문서에 최적화된 BM25 파라미터를 선택할 수 있다
BM25: 30년 된 알고리즘이 아직도 강력한 이유
"임베딩이면 다 되는 거 아니야? 왜 옛날 알고리즘을 배워?"
BM25(Best Matching 25)는 1994년에 만들어졌다. 30년이 지났지만 정확한 키워드 매칭에서는 여전히 최고다.
Elasticsearch, Lucene, Solr... 모든 검색 엔진의 기본이 BM25다.
BM25 공식 해부
Score(D, Q) = Σ IDF(qi) × (f(qi,D) × (k1+1)) / (f(qi,D) + k1 × (1-b + b × |D|/avgdl))
복잡해 보이지만, 세 가지 요소의 곱이다:
1. IDF (역문서 빈도): "희귀한 단어일수록 중요"
IDF(qi) = log((N - n(qi) + 0.5) / (n(qi) + 0.5) + 1)
N = 전체 문서 수
n(qi) = 단어 qi가 등장하는 문서 수
제조 예시:
"설비" -> 거의 모든 문서에 등장 -> IDF 낮음 (0.1)
"OEE" -> 일부 문서에만 등장 -> IDF 높음 (2.3)
"E-4521" -> 특정 매뉴얼에만 등장 -> IDF 매우 높음 (5.1)
"설비"를 검색해도 별 도움이 안 되지만, "E-4521"은 딱 그 문서를 찾아준다. IDF 덕분이다.
2. TF (단어 빈도): "많이 나올수록 관련 있다"
TF 기여 = (f(qi,D) × (k1+1)) / (f(qi,D) + k1 × ...)
f(qi,D) = 단어 qi의 문서 D 내 출현 횟수
k1 = TF 포화 파라미터 (기본값: 1.2)
k1의 역할:
k1 = 0: TF를 무시 (출현 여부만)
k1 = 1.2 (기본): 적당한 포화 곡선
k1 = 3.0: TF를 더 많이 반영
TF가 증가하면:
출현 1회 -> 점수 1.0
출현 2회 -> 점수 1.5 (1.5배, 체감)
출현 5회 -> 점수 1.8 (거의 포화)
출현 10회 -> 점수 1.9 (포화)
"OEE"가 10번 나오는 문서가 1번 나오는 문서보다 관련성이 높지만, 10배 더 좋진 않다. 이것이 "포화(saturation)" 효과다.
3. 문서 길이 정규화: "긴 문서에 불이익"
정규화 = 1 - b + b × (|D| / avgdl)
|D| = 현재 문서 길이 (단어 수)
avgdl = 평균 문서 길이
b = 길이 정규화 강도 (기본값: 0.75)
b의 역할:
b = 0: 길이 무시 (짧은 문서나 긴 문서나 동등)
b = 0.75 (기본): 적당한 길이 페널티
b = 1.0: 최대 길이 페널티
예시 (avgdl = 500):
문서 A (100단어): 정규화 = 0.25 + 0.75 × 0.2 = 0.40 -> 유리
문서 B (500단어): 정규화 = 0.25 + 0.75 × 1.0 = 1.00 -> 기준
문서 C (2000단어): 정규화 = 0.25 + 0.75 × 4.0 = 3.25 -> 불리
제조 문서 최적화 파라미터
| 파라미터 | 기본값 | 제조 권장 | 이유 |
|---|---|---|---|
| k1 | 1.2 | 1.2 | 기본값이 대부분 적합 |
| b | 0.75 | 0.5 | 제조 문서 길이 편차가 크므로 약한 정규화 |
왜 b=0.5인가?
제조 문서의 특성:
- SOP: 200단어 (짧음)
- 장비 매뉴얼: 10,000단어 (김)
- 도면 설명: 50단어 (매우 짧음)
길이 차이가 극심하다. b=0.75면 짧은 도면 설명이 과도하게 유리해진다. b=0.5로 완화하면 문서 길이에 덜 민감해진다.
한국어 + 제조 용어 토크나이징 전략
BM25의 핵심은 토크나이저다. 한영 혼합이 많은 제조 문서에서는 주의가 필요하다.
# 잘못된 토크나이징
text = "CNC-M500 주축 베어링 교체 SOP"
tokens = text.split() # ['CNC-M500', '주축', '베어링', '교체', 'SOP']
# 문제: "CNC"와 "M500"을 분리 못 함
# 좋은 토크나이징
import re
def manufacturing_tokenize(text: str) -> list[str]:
"""제조 도메인 토크나이저"""
# 1. 장비 코드 보존 (CNC-M500 -> CNC-M500)
# 2. 에러 코드 보존 (E-4521 -> E-4521)
# 3. 규격 번호 보존 (ISO 9001 -> ISO_9001)
# 4. 한글 형태소 분석
# 장비/에러 코드 패턴 보존
preserved = re.findall(
r'[A-Z]+-[A-Z0-9]+|E-\d+|ISO\s*\d+', text
)
# 일반 토크나이징
tokens = re.findall(r'[가-힣]+|[a-zA-Z]+|\d+', text)
# 보존 패턴 추가
return list(set(tokens + preserved))
BM25 vs Semantic 정리
| 특성 | BM25 | Semantic |
|---|---|---|
| 정확한 코드/번호 | 강함 | 약함 |
| 동의어 이해 | 약함 | 강함 |
| 문맥 이해 | 약함 | 강함 |
| 속도 | 빠름 (인덱스) | 느림 (임베딩) |
| 설명 가능성 | 높음 (TF-IDF) | 낮음 (블랙박스) |
| 다국어 | 설정 필요 | 좋음 |
| 신조어/전문용어 | 바로 가능 | 학습 필요 |
AI로 학습하기 — 꿀팁
✅BM25 수식 구성 요소 개념 검증AI 학습 팁
BM25의 TF 포화 함수, IDF 가중치, 문서 길이 정규화가 제조 문서 검색에서 어떻게 작동하는지 이해한 내용이 정확한지 확인해 보세요.
BM25에 대한 아래 설명이 각각 정확한지 검증해줘. (1) 'k1 파라미터가 높을수록 단어 빈도(TF)의 영향이 커지고 포화 효과가 느려진다.' (2) 'b=0으로 설정하면 문서 길이 정규화가 완전히 비활성화된다.' (3) 'IDF 값은 검색 쿼리에 포함된 단어가 전체 문서에서 드물수록 낮아진다.' (4) '제조 매뉴얼처럼 문서 길이 편차가 큰 경우 b=0.75보다 b=0.9가 적합하다.' 틀린 항목은 수식 수준에서 정확한 설명으로 교정해줘.
이 팁이 도움이 됐나요?