-
Notifications
You must be signed in to change notification settings - Fork 1
Experiments
sim_threshold, bbox_threshold, max-pool dedupe 등 최종 설정에 이르기까지의 전체 실험 히스토리.
최종 결과는 Benchmark 참고.
| # | 실험 | 한 줄 요약 |
|---|---|---|
| 01 | 최초 측정 (full90) | EU가 baseline을 앞서지만 context_dependent에서 base2048에 밀림 |
| 02 | 문맥 반복 삽입 + page_span | 분할 조각 문맥 손실, 페이지 라벨 오차 수정 |
| 03 | 02 반영 후 재측정 (full90) |
context_dependent 역전 성공, Recall@10 손해 발견 |
| 04 | 파라미터 스윕 + max-pool | bbox/sim_threshold 확정, Recall@10 손해를 max-pool로 해소 |
| 05 | 최종 측정 (full90) | Benchmark에 실린 최종 수치 |
90개 PDF·2725문항(full90) 기준 최초 측정
Headline
| 지표 | baseline | EU | 갭 |
|---|---|---|---|
| Recall | 0.574 | 0.625 | +5.2pp |
| EM | 0.314 | 0.537 | +22.2pp |
유형별
| 유형 | n | baseline EM | EU EM | baseline Recall | EU Recall |
|---|---|---|---|---|---|
| cell_value | 1727 | 0.415 | 0.667 | 0.629 | 0.688 |
| table_about | 462 | 0.290 | 0.442 | 0.374 | 0.392 |
| context_dependent | 536 | 0.009 | 0.200 | 0.565 | 0.625 |
baseline 청크 크기 대조군
| 유형 | 청크 토큰 수 | R(first) | R(any) | EM |
|---|---|---|---|---|
| base | 202 | 0.574 | 0.591 | 0.314 |
| base1024 | 493 | 0.418 | 0.464 | 0.301 |
| base2048 | 484 | 0.304 | 0.372 | 0.274 |
| eu | 200 | 0.625 | 0.633 | 0.537 |
발견한 문제 3가지
-
다중 뷰 색인 미통제 — baseline은 청크 1개=벡터 1개, EU는
retrieval_units를 펼쳐 청크 1개=벡터 여러 개로 색인. EU가 단지 검색 기회가 많아서 이기는 건 아닌지 통제 필요 → §03에서 rowsplit 대조군으로 검증. -
context_dependent에서 base2048에 밀림 — EU EM 0.200 < base2048 EM 0.222. 표가 커서 행 단위로 분할될 때(
split_eu())context_before는 첫 조각에만,context_after는 마지막 조각에만 들어가고 중간 조각은 문맥이 없는 구조가 원인 → §02-2에서 수정. -
페이지 라벨 ±1 오차 —
EvidenceUnit.page_no가 표 자신의 페이지 하나뿐이라, 인접 페이지에서 끌어온 문맥이 있어도 그 사실이 metadata에 안 남음. 정답을 찾고도 페이지 라벨만 달라 오답 처리되는 경우 존재 → §02-3에서 수정.
dev20(20개 PDF, 430문항) 기준 소규모 반복 실험.
Recall@1/5/10 측정, semantic chunker 대조군, rowsplit(표만 행 단위 다중 색인) 대조군을 벤치마크 코드에 추가.
| 지표 | baseline | EU | 갭 |
|---|---|---|---|
| Recall | 0.586 | 0.684 | +9.8pp |
| EM | 0.344 | 0.612 | +26.7pp |
| 대조군 | R(first) | EM |
|---|---|---|
| base | 0.586 | 0.344 |
| rowsplit | 0.623 | 0.379 |
| semantic | – | 0.123 |
| base1024 | 0.407 | 0.333 |
| base2048 | 0.265 | 0.284 |
| eu | 0.684 | 0.612 |
rowsplit(다중 색인 자체의 효과만 분리)도 baseline보다 살짝 나을 뿐 EU에는 크게 못 미치지 못한다. 즉, 다중 뷰 색인만으로는 EU 우위를 설명하지 못 한다.
split_eu()가 분할된 모든 조각에 context_before/context_after를 반복 삽입하도록 수정.
| 유형 | 이전 EU EM | 이후 EU EM |
|---|---|---|
| context_dependent | 0.167 | 0.222 |
base2048(0.222)과 동률까지 회복. 문항 단위 검증 결과 악화 0건, 개선 5건 — 순수 개선.
인접 페이지에서 끌어온 문맥의 페이지 번호를 EvidenceUnit.page_span으로 남기도록 수정(unit.py에 필드 추가, context.py가 채택 단락의 페이지를 함께 추적, split.py가 분할 조각에도 부모의 page_span을 전파).
Recall 회수 효과: R(first) 0.684 → R(any) 0.688 (+0.5pp, 430문항 중 2건). Recall@1/5/10도 각각 +0.4/+0.7/+0.5pp 소폭 상승
효과는 작지만 방향은 일관되게 개선되므로 반영.
§02의 수정 사항을 반영해 90개 PDF로 재측정.
| 지표 | baseline | EU | 갭 |
|---|---|---|---|
| Recall | 0.574 | 0.625 | +5.2pp |
| EM | 0.314 | 0.546 | +23.2pp |
| 유형 | n | baseline EM | EU EM | baseline Recall | EU Recall |
|---|---|---|---|---|---|
| cell_value | 1727 | 0.415 | 0.667 | 0.629 | 0.688 |
| table_about | 462 | 0.290 | 0.444 | 0.374 | 0.392 |
| context_dependent | 536 | 0.009 | 0.246 | 0.565 | 0.625 |
| Recall@k | R@1 | R@5 | R@10 |
|---|---|---|---|
| base | 0.574 | 0.831 | 0.904 |
| base1024 | 0.418 | 0.650 | 0.739 |
| base2048 | 0.304 | 0.497 | 0.588 |
| rowsplit | 0.628 | 0.863 | 0.920 |
| semantic | 0.121 | 0.245 | 0.314 |
| EU | 0.638 | 0.777 | 0.824 |
결과 요약
-
context_dependent 역전 — §01에서 발견한 "EU가 base2048에 밀린다"는 문제(0.200 vs 0.222)가
split_eu()수정 후 EU 0.246 vs base2048 0.222로 완전히 역전(+2.4pp). - 다중 뷰 색인 대조군(rowsplit) — baseline과 완전히 같은 청크 텍스트를 표만 행 단위로 다중 벡터 색인. EM 0.314→0.352(+3.7pp)가 다중 뷰 색인 자체의 효과이고, 나머지 +19.5pp가 EU 고유 구조(캡션·문맥 결합)의 효과.
-
semantic chunker 대조군 — 같은 Docling 파싱에 LangChain
SemanticChunker(기본 하이퍼파라미터)를 붙여도 EM 0.145로 매우 낮음. "파서만 고정하면 semantic chunking으로도 된다"는 반론에 대한 실측 근거. -
새로 발견한 문제: Recall@10에서 EU가 baseline에 짐. 90개 문서 중 20개에서 EU recall이 baseline보다 낮고(
baseline_win_eu_lose=378건, 전체의 13.9%), EM은 여전히 EU가 앞서지만 recall만 놓고 보면 지는 구간이 존재 → §04-3에서 원인 규명.
dev20 기준 파라미터 스윕과 §03에서 발견한 Recall@10 문제의 원인 규명.
100~1000pt로 bbox_threshold 스윕.
| bbox(pt) | 100 | 150 | 200 | 250 | 300 | 400 | 500 | 700 | 1000 |
|---|---|---|---|---|---|---|---|---|---|
| 전체 EM | 0.605 | 0.609 | 0.609 | 0.621 | 0.623 | 0.628 | 0.628 | 0.626 | 0.626 |
| context_dependent EM | 0.139 | 0.167 | 0.194 | 0.208 | 0.222 | 0.250 | 0.250 | 0.250 | 0.250 |
결론: 기본값 300pt 유지.
400pt 이상에서 context_dependent EM이 0.250으로 포화돼 300pt(0.222) 대비 +2.8pp 개선처럼 보이지만, 95% CI 반폭이 ±11.55pp(n=72)라 통계적으로 유의미하지 않다. dev20 표본으로는 늘릴 근거가 부족.
사용자가 페이지를 명시했다고 가정하고 검색 랭킹을 부스팅하면 얼마나 개선되는지 측정.
| 조건 | baseline R | baseline EM | EU R | EU EM |
|---|---|---|---|---|
| delabeled(기준선) | 0.586 | 0.344 | 0.684 | 0.623 |
| 원본 질문 그대로 | 0.588 | 0.342 | 0.656 | 0.586 |
| 소프트 부스팅(δ=0.02) | 0.626 | 0.367 | 0.721 | 0.653 |
| δ=0.20 | 0.893 | 0.502 | 0.916 | 0.812 |
| 하드 필터(δ=0.50) | 0.916 | 0.509 | 0.937 | 0.823 |
페이지 힌트가 잡힌 361/430(84%) 문항을 들여다보면, cell_value/table_about은 QA 생성기가 질문 템플릿에 정답 페이지를 그대로 넣어서 100% 힌트가 잡히는 것이지 실사용자의 행동이 아니다. 즉 위 수치를 "실사용자 힌트를 쓰면 좋아진다"로 인용할 수는 없다. 다만 부스팅을 걸어도 baseline과 EU의 EM 갭이 오히려 벌어지는 것(+27.9pp→+31.4pp)은 확인되었다. 부스팅이 EU의 구조적 이점을 확인하는 정도의 참고 자료로만 보관하고, 라이브러리 기능으로는 반영하지 않기로 한다.
§03에서 발견한 Recall@10 손해의 원인 규명.
가설: EU 하나가 retrieval_units로 여러 벡터를 만드는데, 기존 채점 방식(flat)은 이 벡터들을 개별적으로 top-k에 넣는다. 같은 EU의 유닛 여러 개가 top-10 슬롯을 몰아 차지하면 다른 근거를 가진 EU가 밀려날 수 있다 — Recall@10 손해가 EU 구조 자체의 결함이 아니라 랭킹 집계 방식의 문제일 가능성이 있다.
방법: flat(유닛 단위 랭킹) vs max_pool(같은 chunk_id끼리 최고 점수로 묶어 그룹 단위로 랭킹, top-k에 같은 EU 중복 불가)
| baseline | flat | max_pool | Δ(mp−flat) | |
|---|---|---|---|---|
| R@1 | 0.586 | 0.695 | 0.693 | −0.2pp |
| R@5 | 0.856 | 0.833 | 0.886 | +5.3pp |
| R@10 | 0.928 | 0.872 | 0.951 | +7.9pp |
결론: 가설 확인, 한계 해소. max_pool이 R@5, R@10에서 baseline을 실제로 역전.
Recall@10 손해는 EU 구조 자체의 결함이 아니라 flat 랭킹(유닛 단위 집계) 방식의 문제였다. EM은 flat과 거의 동일(2725건 중 개선 8건/퇴보 5건, net +3 — 표 분할 시 캡션/문맥이 조각마다 복제되며 생기는 동률을 두 정렬 방식이 다르게 깨는 노이즈로 판단).
→ LangChain/LlamaIndex 래퍼에 같은 chunk_id를 max-pool로 합치는 옵션으로 반영한다.
CPU 기준 처리 속도 측정.
dev20 전체(20개 PDF) 평균 2.01초/페이지 → 10페이지 환산 약 20.1초. baseline(HybridChunker) 대비 EU 빌드(build_evidence_units+split_oversized_units) 자체의 오버헤드는 대부분 1초 미만
docling 파싱 시간이 전체의 90% 이상을 차지하고, EU 구성 로직은 병목이 아니다.
지금까지 모든 실험이 all-MiniLM-L6-v2로 embedder를 고정해온 것에 대한 robustness 체크. bge-small-en-v1.5/e5-small-v2는 공식 권장 query/passage 프리픽스를 적용해 공정 비교.
| embedder | baseline EM | EU EM | 갭 | EU R@1 | EU R@10 |
|---|---|---|---|---|---|
| all-MiniLM-L6-v2 | 0.344 | 0.600 | +25.6pp | 0.661 | 0.854 |
| bge-small-en-v1.5 | 0.407 | 0.614 | +20.7pp | 0.672 | 0.854 |
| e5-small-v2 | 0.435 | 0.644 | +20.9pp | 0.677 | 0.872 |
세 embedder 전부에서 갭이 20pp 이상 확고히 유지되었다. 특히 context_dependent에서 baseline EM이 세 embedder 모두 정확히 0.0417로 동일한 반면 EU는 embedder가 좋아질수록 같이 개선된다.
baseline의 실패가 임베딩 품질 문제가 아니라 "정답이 애초에 청크 안에 없어서(캡션·표·문맥이 갈라져 있어서) 못 푸는" 구조적 문제라는 핵심 주장을 뒷받침하는 결과이다. 구조적 우위가 embedder에 의존하지 않으므로 기본 embedder는 그대로 유지하기로 한다.
0.00~0.60(0.05 간격, 13개 지점).
| sim_threshold | 전체 EM | cell_value EM | table_about EM | context_dependent EM |
|---|---|---|---|---|
| 0.00 | 0.635 | 0.756 | 0.494 | 0.319 |
| 0.20 | 0.619 | 0.749 | 0.494 | 0.250 |
| 0.40(구 기본값) | 0.600 | 0.735 | 0.494 | 0.194 |
| 0.60 | 0.579 | 0.724 | 0.494 | 0.111 |
13개 지점 전 구간에서 threshold를 낮출수록 세 유형 EM이 단조 증가하거나 유지 — 특히 context_dependent는 0.00에서 0.319, 0.40에서 0.194로 +12.5pp 차이.
결론: sim_threshold 기본값을 0.0으로 낮춘다(코사인 게이트를 사실상 무력화). 사용자가 필요시 값을 올릴 수 있게 남겨두되, 기본값(0.0)에서는 model.encode() 호출 자체를 건너뛰어 sentence-transformers/torch 없이도 동작하도록 구현한다.
모든 변경사항을 반영해 90개 PDF로 재측정한 최종 결과.
이 실험에서 나온 최종 수치는 Benchmark에 정리.
여기서는 그 수치에 이르는 과정에서 확인한 것만 요약한다.
- base1024/base2048: 청크를 억지로 키워도 R@1/EM이 오히려 떨어진다.
- rowsplit: baseline 대비 EM +3.8pp(0.314→0.352)만 회복된다. EU는 rowsplit 대비도 +21.7pp 더 높아, 다중 뷰 색인 자체의 기여는 작고 캡션+문맥 결합(EU 고유 구조)이 개선분의 대부분을 설명한다.
-
semantic chunking:
SemanticChunker는 R@1 0.121, EM 0.145로 baseline보다도 나쁘다. 문서를 마크다운으로 펼쳐 재청킹하는 과정에서 표 구조 자체가 깨지는 것으로 추정된다. -
bbox 윈도우:
context_dependent문항을 표-문단 거리 기준으로 나누면 창 안(≤300pt, n=409)은 EU EM 0.455, 창 밖(>300pt, n=127)은 0.016 — bbox_threshold 밖 문단은 애초에 EU에 편입이 안 되므로 EU도 못 푼다. §04-1에서 검증한 bbox_threshold=300pt가 실제로 의미 있는 경계임을 재확인하였다. - 문서 단위 안정성: 전체 90개 문서 평균(macro) 갭 +30.9pp, 10문항 이상 문서(78개)만 보면 +28.6pp — micro(+25.5pp)와 같은 방향으로 안정적, 특정 문서 몇 개가 결과를 왜곡하는 게 아니다.
- 문항 단위 승패: EU만 정답 521건, baseline만 정답 382건 — net 139건(5.1%p)이 Recall 갭(5.1pp)과 정확히 일치한다. EU가 지는 382건 대부분은 "EU wrong page"(829건) — 문서 안에 구조적으로 비슷한 표가 여러 개 있어 검색 단계에서 엉뚱한 표를 골라온 경우로, 캡션/문맥 추출 자체의 실패는 아니다. (Limitations & Roadmap #2 참고).