▶️16

[영상] 한글 BM25 + FAISS 하이브리드 검색

Day 1: 멀티모달 RAG

학습 목표
  • 한국어 BM25가 효과적인 이유와 형태소 분석의 필요성을 설명할 수 있다
  • FAISS IndexFlatL2와 IndexIVFFlat의 차이를 메모리·재현율 파라미터 관점에서 비교할 수 있다
  • BM25 + FAISS 하이브리드에서 RRF 점수 합산 방식을 설명할 수 있다
  • 멀티모달 RAG가 필요한 제조 시나리오를 두 가지 이상 제시할 수 있다

영상 핵심 정리 — 한글 BM25 + FAISS 하이브리드

한국어 BM25의 함정

한국어는 교착어라 동일 개념이 다양한 형태로 표현된다. 형태소 분석 없이 BM25 적용하면 '베어링의'와 '베어링을'을 다른 단어로 취급해 재현율 급락.

# 나쁨
tokenize = str.split

# 권장 (kiwipiepy) — pip install kiwipiepy
from kiwipiepy import Kiwi
kiwi = Kiwi()

# 뺄 태그를 나열하지 말고 남길 내용어 태그를 나열한다.
# 뺄 것을 적으면 목록에서 빠뜨린 조사가 그대로 코퍼스에 남는다.
# 예를 들어 보조사 JX 와 접속조사 JC 만 빼면 격조사 '의'(JKG)와
# '을'(JKO)이 살아남아, 없애려던 바로 그 조사가 토큰이 된다.
CONTENT_TAGS = ('NNG','NNP','NNB','NR','SL','SN','SH','VV','VA','MAG','XR')

def ko_tokenize(text):
    return [t.form for t in kiwi.tokenize(text) if t.tag in CONTENT_TAGS]

from rank_bm25 import BM25Okapi
bm25 = BM25Okapi([ko_tokenize(d) for d in corpus])

이 함수는 Day 2 의 해답 코드가 그대로 쓴다. 거기에서 공백 분리와 나란히 놓고 BM25 점수가 실제로 어떻게 달라지는지 직접 재 본다.

FAISS 인덱스 선택

인덱스방식원본 벡터재현율을 정하는 것속도
IndexFlatL2전수 탐색압축 없이 보관없음 (정확 탐색, 100%)느림
IndexIVFFlat클러스터 분할 후 일부만 탐색압축 없이 보관nprobe빠름
IndexHNSWFlat근접 그래프 탐색압축 없이 보관efSearch매우 빠름
IndexIVFPQ클러스터 분할 + 곱 양자화압축 저장nprobe + 압축률빠름

메모리를 빼고 말하면 안 된다. IVFPQ 를 뺀 앞의 셋은 이름 그대로 원본 벡터를 압축 없이 들고 있다. 768차원 float32 벡터 1억 개면 768 × 4바이트 × 1억 = 약 307GB 다. 한 대에 올라가지 않는다. 1억 규모는 IVFPQ·OPQ 같은 압축 인덱스나 샤딩이 전제이고, 그 말을 빼면 「IVFFlat 쓰면 1억까지 된다」는 잘못된 판단으로 이어진다.

재현율은 파라미터가 정한다. IVFFlat 은 몇 개 클러스터를 열어 볼지(nprobe), HNSW 는 탐색 폭(efSearch)이 재현율을 정한다. 이 값을 적지 않고 「정확도 97%」라고 쓰는 것은 의미가 없다. nprobe 를 올리면 재현율이 오르고 속도가 떨어진다 — 그 교환이 근사 최근접 탐색의 전부다.

import faiss, numpy as np
dim = 768
quantizer = faiss.IndexFlatL2(dim)
index = faiss.IndexIVFFlat(quantizer, dim, nlist=100)
index.train(np.array(embeddings, dtype='float32'))
index.add(np.array(embeddings, dtype='float32'))

RRF 점수 합산

BM25 순위:  베어링 → 1위, 모터 → 3위
벡터 순위:  베어링 → 2위, 모터 → 1위

RRF(베어링) = 1/(60+1) + 1/(60+2) = 0.016393 + 0.016129 = 0.032522
RRF(모터)   = 1/(60+3) + 1/(60+1) = 0.015873 + 0.016393 = 0.032266
→ 베어링 1위 (차이 0.000256)

함정 주의: FAISS 는 메타데이터가 1급 개념이 아니다. IDSelectorArray·IDSelectorBatch 로 ID 집합 기반 필터를 걸 수는 있지만, 「3호기 관련 문서만」 같은 조건을 미리 ID 집합으로 풀어 놓아야 한다. 조건이 자주 바뀌거나 조합이 많으면 그 준비 비용이 검색 비용을 넘는다. 그래서 Qdrant/Chroma 로 간다. 「기능이 없다」가 아니라 「조건을 미리 풀어 놓아야 한다」가 정확한 이유다.

다음 task와의 연결

'멀티모달 문서 처리 전략 3가지'에서 텍스트·이미지·표를 각각 어떻게 색인할지 세 전략으로 비교한다.

AI로 학습하기 — 꿀팁
BM25+FAISS 하이브리드 검색 구현 요청AI 학습 팁

AI 에이전트에게 한국어 형태소 분석기를 활용한 BM25와 FAISS IndexIVFFlat 하이브리드 검색 파이프라인을 구현하게 하고, RRF 공식 적용이 올바른지 검토하세요.

한국어 제조 설비 매뉴얼 검색 시스템을 위한 하이브리드 검색 파이프라인을 Python으로 구현해줘. Kiwi 형태소 분석기로 내용어 태그만 남겨 BM25 토크나이징하고(조사·어미 제거), FAISS IndexIVFFlat 으로 벡터 검색하는 코드를 작성해줘. 그리고 벡터 1억 개를 IVFFlat 으로 다룰 때 필요한 메모리를 768차원 float32 기준으로 계산해 주고, nprobe 값에 따라 재현율과 지연이 어떻게 교환되는지 설명해줘. RRF 공식(score = Σ 1/(k + rank_i), k=60)으로 두 검색 결과를 결합하는 방법도 포함해줘.
이 팁이 도움이 됐나요?
핵심 포인트
  • 한국어 BM25: Kiwi 형태소 분석기로 조사·어미를 걸러낸다. 뺄 태그가 아니라 남길 내용어 태그를 나열해야 격조사를 빠뜨리지 않는다
  • FAISS(Meta): 순수 벡터 라이브러리. IVFFlat·HNSW 는 원본 벡터를 압축 없이 보관하므로 1억 규모는 IVFPQ 같은 압축 인덱스나 샤딩이 전제다
  • ANN 인덱스의 재현율은 nprobe·efSearch 가 정한다. 파라미터 없이 말하는 정확도 수치는 의미가 없다
  • RRF 공식: score = Σ 1/(k + rank_i), k=60
  • 멀티모달 필요: CAD 도면 결함 분석, 설비 사진 이상 탐지, PDF 표 추출
용어