검색 실험 구도
아카이브됨 — 실행 결과는 검색 개선이 정본 (2026-07-29)
이 문서는 측정 전 사전등록 설계서다. 사료로서 보존하되 현행 상태로 읽지 말 것.
이 문서 실제 진행 상황 “1단계 A·B·B′ ← 지금 여기” 1~3차 측정 완료 7변형(A~D + B′·E·F) 실행은 검색 범위(전체 vs 문서 한정) 축으로 진행 — B′·E·F 미실행 리랭커 모델 스윕 미착수. 유효 잔여분은 검색 개선 §18-1로 이관 완료 측정 대상 내역
기존 26 · 어휘형 9는 오기였고기존 27 · 어휘형 8로 고쳤다 — 검색 실험 골든셋 40문항 부록 원자료 기준(미검출 4 = 기존 2·어휘형 2, 대상 아님 1 = 기존 #21). 40/36/35 관계는 검색 개선 §6 참조.
BM25 하이브리드와 리랭커를 넣을지 말지 숫자로 판정하는 실험. 좋아 보여서 넣는 게 아니라, 미리 정한 선을 넘겨야 넣는다.
| 재는 것 | 검색 순위 (MRR@10 주 지표) |
| 대고 재는 것 | 35문항 / 정답 청크 72개 |
| 판정선 | A 대비 MRR@10 +0.05 |
| 지금 | 1단계 측정 직전 |
문항 전수 → 검색 실험 골든셋 40문항 · 데이터 손상 → 인덱스 데이터 손상 진단 · 경위 → 검색 개선 · 구조도 → 검색 품질 개선 (BM25·리랭커)
왜 다시 설계했나
Plans 6.1에서 6개 변형을 비교했는데 hit@5가 전부 1.000이었다. 검색을 바꿔도 숫자가 안 움직이면 판정이 불가능하다.
원인은 지표가 문서 단위라는 것. 청킹이 모든 청크 앞에 사업명+발주기관을 붙이니 어떤 청크가 뽑혀도 “정답 문서에서 나왔다”가 항상 참이 된다. 지표가 검색기가 아니라 청킹 접두어를 재고 있었다.
그래서 실험 코드보다 지표를 먼저 새로 만들었다. 지표가 변별을 못 하면 그 뒤는 전부 무의미하다.
무엇을 비교하나
뼈대 — 2×2 (사전등록)
BM25와 리랭커는 독립 축이라 하나씩이 아니라 네 칸을 전부 돈다. 그래야 “둘 다 켜야 좋아지는지, 하나로 충분한지”가 갈린다.
| 리랭커 OFF | 리랭커 ON | |
|---|---|---|
| BM25 OFF | A 베이스라인 | C |
| BM25 ON | B | D |
라벨링 뒤 3개 추가 (탐색)
실패 4건을 실측하니 A~D만으로는 답 못 하는 질문이 생겼다.
| 실측 | 답 못 하는 질문 |
|---|---|
| 전체 질문 160위 → 핵심어만 2위 | BM25가 나쁜가, 질문을 잘못 넣었나 |
| 정답이 dense 23·35·94위인데 top-20에서 잘림 | 알고리즘인가, 후보 깊이인가 |
| 정답이 표면 dense 10위, 줄글이면 4위 | 표 문항에서만 이득 날 가능성 |
최종 7변형
| BM25 | 리랭커 | 후보 K | 쿼리 | |
|---|---|---|---|---|
A_dense | ✗ | ✗ | 20 | 원문 |
B_hybrid | ✓ | ✗ | 20 | 원문 |
C_rerank | ✗ | ✓ | 20 | 원문 |
D_hybrid_rerank | ✓ | ✓ | 20 | 원문 |
B′_kw ᵉ | ✓ | ✗ | 20 | 핵심어 |
E_deep ᵉ | ✗ | ✓ | 40 | 원문 |
F_all ᵉ | ✓ | ✓ | 40 | 핵심어 |
ᵉ = 탐색(exploratory). A~D는 사전등록.
- 핵심어 쿼리 = BM25에 넣기 전 저IDF 토큰 제거. dense 쿼리는 원문 유지(임베딩은 문맥이 필요).
- 후보 K는
RERANK_CANDIDATE_K. 최종 반환은 전부 top-5. - 전 변형이 한 프로세스에서 config 플래그만 토글해 돈다. 인덱스·질문·정답이 동일하므로 차이는 검색 경로에서만 온다.
승격 판정은 A~D로만 한다. B′·E·F는 라벨링 결과를 본 뒤 추가했다. 이걸 판정에 넣으면 결과를 보고 기준을 만든 셈이 되어 실험이 무의미해진다. 셋은 “다음에 뭘 고칠지” 참고용이다.
리랭커 모델은 별도 축
한국어 전용 리랭커가 나은지도 봐야 하지만, 위 표에 곱하면 14변형이 된다. 별도로 뺄 수 있는 이유는 후보를 고정한 채 점수만 다시 매기면 되기 때문이다.
검색 1회 → 후보 20개 확정 ← 모델 바꿔도 불변
↓ 이 20개를 모델별로 재배열
문항 35 × 후보 20 = 모델당 700쌍
| 모델 | 역할 |
|---|---|
BAAI/bge-reranker-v2-m3 | 기준선 (현행) |
upskyy/ko-reranker-8k | 한국어 파인튜닝본 |
둘 다 2.27GB · 최대 8194토큰 · Apache-2.0. ko-reranker-8k는 모델 카드상 v2-m3를 한국어 데이터로 파인튜닝한 것이라, 구조·토크나이저·길이가 같고 변수가 “한국어 학습” 하나뿐인 통제된 비교가 된다. 코드는 RERANK_MODEL 한 줄만 바뀐다.
탈락한 셋: Dongjin-kr/ko-reranker(최대 514), bge-reranker-base(514 + 저성능), jina-reranker-v2(CC-BY-NC 비상업).
514가 왜 탈락 사유인가 — 리랭커 입력 토큰 길이 실측:
| 입력 | 중앙값 | 512 초과 |
|---|---|---|
| 청크 본문 (현행) | 148 | 0% |
parent_text (표 전문, 개선 후보) | 454 | 46% |
지금 입력이면 514 모델도 무해하다. 그러나 표 전문을 넣는 개선을 하면 절반이 잘린다. 실패 사례 상당수가 표 문항이라 그 길을 막을 수 없다.
모델 교체 기준: 동일 후보 위에서 MRR@10 +0.03 이상이면 교체, 미만이면 v2-m3 유지. 이 축도 A~D 판정에는 넣지 않는다.
융합은 RRF
코사인 유사도(0~1)와 BM25 점수(상한 없음)는 스케일이 달라 더할 수 없다. RRF는 점수를 버리고 순위만 쓴다.
score(청크) = Σ 1 / (60 + 그 검색기에서의 순위)
정규화 파라미터 튜닝이 없어서 “융합을 잘못 맞춰 졌다”는 변명이 안 나온다.
무엇으로 재나
| 지표 | 보는 것 |
|---|---|
| MRR@10 | 주 지표. 첫 정답이 몇 등인가 (1/등수) |
| nDCG@10 | 정답 여러 개가 위쪽에 몰렸나 (정답 2개 이상 22문항) |
| Recall@5 | 실제로 생성에 넘어가는 top-5의 품질 |
| hit@5 | 회귀 감시용. 1.0에서 안 떨어지는지만 |
구현은 src/eval/ir_metrics.py. 순수 함수 + stdlib.
무엇에 대고 재나
| 수 | ||
|---|---|---|
| 라벨링 전수 | 40 | 기존 30 · 어휘형 10 |
| − 검색 미검출 | 4 | 정답이 후보 풀 밖 |
| − 검색 대상 아님 | 1 | 문서에 근거 자체가 없음 |
| 측정 대상 | 35 | 기존 27 · 어휘형 8 |
정답 청크 72개(문항당 2.1개). 인덱스는 data/chroma_v2 — 30,002청크, 표 헤더 승격 반영본.
어휘형 10문항은 BM25를 이기게 하려고 만든 게 아니라, BM25가 이겨야 정상인 구간이 실제로 있는지 보려고 만들었다. 고유번호·법령 조항·정확한 기관명처럼 임베딩이 뭉개기 쉬운 표층 문자열 질문들이다. 여기서도 못 이기면 도입 근거가 없다.
보고는 두 축으로 쪼갠다
| 축 | 분할 | 이유 |
|---|---|---|
| 출처 | 전체 35 / 기존 27 / 어휘형 8 | 어휘형만 좋아지고 기존이 나빠지는 건 개선이 아니라 트레이드오프. 합치면 숨는다 |
| 정답 소재 | 표 / 줄글 | dense는 표에서 약하고(10위·70%) 줄글에서 강하다(4위·87%). 하이브리드 효과가 여기서 갈릴 것 |
추가로 중복 청크 문항 19개를 뺀 재계산도 함께 낸다. 그 문항들은 정답과 오답의 텍스트가 거의 같아 점수 차이가 운에서 나온다.
정답은 어떻게 정했나
30,000개 중에서 사람이 정답을 찾는 건 불가능하므로, 두 검색기가 건진 것만 후보로 놓고 고른다(TREC 풀링).
후보 = dense top-20 ∪ BM25 top-20 → 문항당 30~40개, 총 1,438개
| 후보 출처 | 수 |
|---|---|
| dense만 | 638 |
| BM25만 | 638 |
| 둘 다 | 162 |
겹침이 11%다. BM25가 dense는 못 보던 청크를 실제로 건져 올리고 있다 — 실험에 변별력이 있다는 뜻이다.
라벨링은 기계가 기준답변과의 내용어 겹침으로 상위 3개를 제안하고, 사람은 승인하거나 번호로 고치기만 했다. 겹침 점수는 질문이 아니라 기준답변에 대고 잰다. 질문에 대고 재면 BM25가 찾기 쉬운 청크가 정답으로 뽑히는 자기충족 편향이 생긴다.
라벨링 중 드러난 문제와 교훈은 검색 실험 골든셋 40문항.
언제 채택하나
A 대비 ① 전체 MRR@10 +0.05 이상, ② 기존 문항 hit@5 = 1.0 유지 → 둘 다 만족하면 프로덕션 반영 (플래그 ON) → 하나라도 미달하면 미채택으로 기록, 코드는 OFF 유지 → 여러 변형이 통과하고 차이가 0.02 이내면 더 단순한 쪽 (A < B,C < D)
결과 보기 전에 못 박은 이유 — 숫자를 본 뒤 기준을 정하면 어떤 결과든 성공으로 해석된다. +0.03이 나왔을 때 “그래도 개선이니까”라고 말하지 않기 위한 사전 구속.
단순한 쪽 우대 — 리랭커는 공짜가 아니다. 568M 모델을 매 질의마다 20개 청크에 돌리는 비용·지연·GPU 메모리를 0.02 차이로 정당화할 수 없다.
실행 순서
1단계 · 로컬 CPU — A B B′. 리랭커가 없어 GPU 불필요. BM25의 순수 효과와 쿼리 처리 효과를 먼저 본다.
B′ ≫ B→ 문제는 BM25가 아니라 쿼리 처리. 알고리즘 교체 불필요B′ ≈ B ≈ A→ BM25가 이 코퍼스에서 힘을 못 쓴다. 대안 검토
2단계 · VM L4 — C D E F. 리랭커 1,600~3,200회 추론. CUDA fp16, 맥 MPS는 쓰지 않는다(수치가 갈리면 비교가 무너진다).
선결: VM 디스크가 96G 중 1.7G만 남아 모델을 못 받는다. 두 모델이면 4.5GB 필요. 차선책은 맥 CPU 측정(20~40분).
3단계 · 모델 스윕 — 2단계 최고 후보 구성을 고정하고 두 모델만 갈아끼워 700쌍씩 재채점. 몇 분.
4단계 · 해석
| 관측 | 해석 | 행동 |
|---|---|---|
| E ≫ C | 병목은 후보 깊이 | RERANK_CANDIDATE_K 상향 |
| B′ ≫ B | 병목은 쿼리 처리 | 저IDF 필터 프로덕션 반영 |
| ko-8k > m3 +0.03 | 한국어 학습이 유효 | RERANK_MODEL 교체 |
| F ≈ D | 추가 조치 효과 없음 | 원안대로 판정 |
| 전부 A와 비슷 | 순위 문제가 아님 | 인제스트로 투자 전환 |
필요한 코드
| 작업 | 규모 |
|---|---|
핵심어 쿼리 필터 — 코퍼스 df 기반 저IDF 제거, hybrid.py | 소 |
변형 스윕 확장 — VARIANTS에 후보 K·쿼리 모드 필드 | 소 |
| 표/줄글 분할 집계 (출처 3분할은 이미 있음) | 소 |
모델 스윕 — 고정 후보 재채점, RERANK_MODEL 인자화 | 소 |
플래그 기본값은 계속 OFF. 측정 스크립트가 토글할 뿐 프로덕션 동작은 안 바뀐다.
비용 — OpenAI 호출 0건. 임베딩·BM25·리랭커 전부 로컬 모델이라 팀 키 $20 한도와 무관.
이 실험이 답하지 못하는 것
결과 보고서에 반드시 함께 적는다.
- 풀링의 한계 — 두 검색기 어느 쪽도 못 건진 청크는 영원히 정답이 될 수 없다. 실제 Recall은 여기서 재는 값보다 낮다.
- BM25는 승격만 시킨다 — BM25만 찾은 청크는 본문이 없어 리랭킹 대상에서 빠진다. 기여는 “dense가 이미 본 청크를 위로 올리는 것”까지다. 신규 청크를 넣으려면 Chroma 왕복이 한 번 더 필요한데 v1 범위 밖.
- 답변 품질은 안 잰다 — 검색 순위만 본다. 검색이 좋아져도 생성이 좋아진다는 보장은 없다(별도 골든셋 평가 몫).
보류 — BGE-M3 전환
spec.md/ADR-006이 임베딩을 multilingual-e5-small로 고정하고 있다. BGE-M3로 바꾸면 e5와 BM25를 한 모델로 대체할 수 있고 한국어 성능도 낫지만:
- 30,002청크 재인덱싱 + 라벨 전량 재생성
- 모델 2.2GB (VM 디스크 문제 동일)
- 임베딩·sparse·인덱스가 한꺼번에 바뀌어 무엇 때문에 좋아졌는지 구분 불가
→ 1단계 결과를 보고 결정한다. B′가 잘 나오면 바꿀 이유가 없다.
진행 상황
✅ 지표·하이브리드·리랭커 구현 PR #22 머지, 403테스트, 플래그 OFF
✅ 재인덱싱 chroma_v2, 30,002청크
✅ 후보 풀 재생성 1,438후보
✅ 라벨링 40문항 + 감사 측정 대상 35 확정
□ 코드 4건 (위 표)
□ 1단계 A·B·B′ ← 지금 여기
□ 2단계 C·D·E·F VM 디스크 선결
□ 모델 스윕 m3 ↔ ko-8k
□ 결과 기록 · 승격 판정 · PR
관련
검색 실험 골든셋 40문항 · 인덱스 데이터 손상 진단 · 검색 품질 개선 (BM25·리랭커) · 검색 개선 · 쿼리 파이프라인 개선안 · 쿼리 파이프라인