학술 검색 엔진 내부: Indexing, Ranking, and Retrieval
Source: Dev.to
실제로 무엇을 만들고 있나요?
실용적인 학술 검색 엔진을 구축하기 위해 단순한 데이터베이스 쿼리를 넘어 역색인(Inverted Index) 을 구현했습니다. 이는 대부분의 현대 검색 엔진이 사용하는 핵심 자료구조입니다.
스택
- 핵심: Python (논리 및 자료구조)
- 웹 프레임워크: Flask (API 및 UI)
- 프론트엔드: HTML, CSS & 순수 JavaScript (경량, 단일 파일)
비밀 소스
맞춤형 역색인과 BM25 순위 알고리즘 을 결합한 형태입니다.
“아하!” 순간: 단순 빈도만으로는 안 되는 이유
용어 빈도만으로 순위를 매기면 단어가 많기만 한 문서가 유리해집니다.
BM25는 다음을 통해 이를 보완합니다:
- 흔한 단어에 패널티 부여 (역문서 빈도, IDF).
- 문서 길이에 따라 정규화 (길이 정규화).
BM25 점수 함수 (Python)
import math
def score_bm25(n, f, qf, r, N, dl, avdl, k1=1.5, b=0.75, k2=100):
"""
n – number of documents containing the term
f – term frequency in the document
qf – term frequency in the query
r – number of relevant documents containing the term
N – total number of documents
dl – document length
avdl– average document length
"""
# Scaling factor based on document length
K = k1 * ((1 - b) + b * (dl / avdl))
# Relevance component
first = math.log(((r + 0.5) / (N - r + 0.5)) /
((n - r + 0.5) / (N - n - (N - r) + 0.5)))
second = ((k1 + 1) * f) / (K + f)
third = ((k2 + 1) * qf) / (k2 + qf)
return first * second * third
인덱싱: 무거운 작업
역색인은 교과서의 색인과 같은 방식으로 동작합니다: 각 용어가 나타나는 문서 ID 목록에 매핑됩니다.
간소화된 인덱싱 과정 (Python)
from collections import defaultdict
inverted_index = defaultdict(list)
for doc_id, text in corpus.items():
tokens = preprocess(text) # strip punctuation, lowercase, etc.
for term in tokens:
inverted_index[term].append(doc_id)
트레이드‑오프:
- 장점: 메모리 내 인덱스 → 서브밀리초 수준 조회.
- 단점: 높은 RAM 사용량; 작은 규모에서는 허용 가능
fetch('response.json')
.then(res => res.json())
.then(data => {
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = ''; // Clear old results
data.forEach(paper => {
const item = `
<h3>${paper.title}</h3>
<p>${paper.abstract}</p>
`;
resultsDiv.innerHTML += item;
});
})
.catch(err => console.error('Search error:', err));
직접 해보기
전체 소스 코드는 오픈소스입니다. 저장소를 클론하고 README에 따라 설정한 뒤 로컬에서 애플리케이션을 실행하면 인덱싱 및 순위 파이프라인을 직접 탐색할 수 있습니다.