BM25 b(Best Matching 25)와 Opensearch
BM25 (Best Matching 25)는 OpenSearch와 Elasticsearch에서 기본적으로 사용하는 문서와 쿼리 간의 관련성 점수 계산 알고리즘입니다.
Lexical Search에서 match 쿼리를 실행했을 때, 각 문서의 _score가 바로 이 BM25 점수입니다.
✅ 핵심 개념 먼저 요약
용어 설명
TF (Term Frequency) | 쿼리 단어가 문서 내에 얼마나 많이 나왔는가 |
IDF (Inverse Document Frequency) | 해당 단어가 전체 문서 중 얼마나 희귀한가 |
문서 길이 보정 | 너무 긴 문서는 단어 많이 포함되기 쉬우므로 패널티 줌 |
최종 점수 = TF × IDF × 보정계수 |
🧮 BM25 수식
단어 qiq_i에 대해 문서 DD와 쿼리 QQ 간의 점수:
$score(D,Q)=∑i=1nIDF(qi)⋅f(qi,D)⋅(k1+1)f(qi,D)+k1⋅(1−b+b⋅∣D∣avgD)\text{score}(D, Q) \\ = \sum_{i=1}^{n} IDF(q_i) \cdot \frac{f(q_i, D) \cdot (k_1 + 1)}{f(q_i, D) + k_1 \cdot (1 - b + b \cdot \frac{|D|}{avgD})}$
각 요소 설명:
기호 의미
$f(qi,D)f(q_i, D)$ | 문서 D에서 단어 qiq_i의 빈도 (TF) |
( | D |
$avgDavgD$ | 전체 문서들의 평균 길이 |
$k1k_1$ |
TF에 대한 민감도 조절 (보통 1.2~2.0, 기본값 1.2) |
$bb$ | 문서 길이에 대한 보정 정도 (보통 0.75) |
$IDF(qi)IDF(q_i)$ | 단어 qiq_i의 역문서빈도 값 (아래 참고) |
📌 IDF 수식
$IDF(qi)=log(N−n(qi)+0.5n(qi)+0.5+1)IDF(q_i) \\ = \log \left( \frac{N - n(q_i) + 0.5}{n(q_i) + 0.5} + 1 \right)$
기호 의미
$NN$ | 전체 문서 수 |
$n(qi)n(q_i)$ | 단어 qiq_i를 포함한 문서 수 |
→ 흔한 단어는 IDF 값이 낮아지고, 드문 단어는 IDF가 커져서 점수가 올라갑니다.
✅ 예제로 이해하기
🔹 상황
- 전체 문서 수: 10,000개
- 평균 문서 길이: 100개 단어
- 문서 A:
- 길이: 120단어
- "search"라는 단어가 4번 등장
- "search" 단어가 전체 문서 중 500개에 등장
🔹 계산
- TF (빈도): 4
- 문서 길이 보정:
$4⋅(1.2+1)4+1.2⋅(1−0.75+0.75⋅120100) \\ =8.84+1.2⋅(0.25+0.9) \\=8.84+1.2⋅1.15≈8.85.38≈1.64\frac{4 \cdot (1.2 + 1)}{4 + 1.2 \cdot (1 - 0.75 + 0.75 \cdot \frac{120}{100})} \\ = \frac{8.8}{4 + 1.2 \cdot (0.25 + 0.9)} \\ = \frac{8.8}{4 + 1.2 \cdot 1.15} \approx \frac{8.8}{5.38} \approx 1.64$
- IDF:
$log(10000−500+0.5500+0.5+1)=log(9500.5500.5+1) \\ ≈log(19.0+1) \\ ≈log(20) \\ ≈3.0\log \left( \frac{10000 - 500 + 0.5}{500 + 0.5} + 1 \right) \\ = \log \left( \frac{9500.5}{500.5} + 1 \right) \approx \log(19.0 + 1) \approx \log(20) \approx 3.0$
- BM25 점수:
$3.0⋅1.64=4.923.0 \cdot 1.64 = 4.92$
✅ 왜 중요한가?
특징 효과
단어가 많이 등장할수록 | 점수 ↑ |
드문 단어일수록 (높은 IDF) | 점수 ↑ |
짧은 문서일수록 | 점수 ↑ (정확한 문서일 가능성 ↑) |
흔한 단어 or 너무 긴 문서 | 점수 ↓ |
BM25는 단순한 단어 포함 여부를 넘어서, 얼마나 핵심적인 단어인가를 반영해서 점수를 매기는 것이 장점입니다.
✅ OpenSearch에서 어떻게 쓰이는가?
{
"match": {
"text": {
"query": "search engine",
"operator": "or"
}
}
}
이 쿼리를 날리면 OpenSearch는:
- "search", "engine" 단어를 Analyzer로 토큰화
- 각 단어가 문서에 얼마나 자주, 얼마나 희귀하게 등장했는지 계산
- BM25 점수를 구해 _score 필드에 저장
- _score 높은 순으로 결과 반환
궁금한 점:
- k1, b 파라미터를 어떻게 조정하는지?
- BM25 대신 다른 점수 모델 사용 가능한가?
- BM25 튜닝해서 내 서비스에 맞추고 싶은데 어떻게 해야 하나?
이런 부분도 알려드릴 수 있어요! 더 보고 싶은 내용 있으실까요?