-
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) 기준 최초 측정
| 지표 | 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 |
| 유형 | 청크 토큰 수 | 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 |
- 다중 뷰 색인 미통제
-
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으로 남기도록 수정하였다.
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)
- 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보다 낮고, 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 |
부스팅을 걸어도 baseline과 EU의 EM 갭이 오히려 벌어지는 것(+27.9pp → +31.4pp)은 확인되었다. 부스팅이 EU의 구조적 이점을 확인하는 정도의 참고 자료로만 보관하고, 라이브러리 기능으로는 반영하지 않기로 한다.
§03에서 발견한 Recall@10 손해의 원인 규명.
가설: EU 하나가 retrieval_units로 여러 벡터를 만드는데, 기존 채점 방식(flat)은 이 벡터들을 개별적으로 top-k에 넣는다. 같은 EU의 유닛 여러 개가 top-10 슬롯을 몰아 차지하면 다른 근거를 가진 EU가 밀려날 수 있다.
즉, Recall@10 손해가 EU 구조 자체의 결함이 아닌 랭킹 집계 방식의 문제일 가능성이 있다.
같은 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 랭킹(유닛 단위 집계) 방식의 문제였다.
→ LangChain/LlamaIndex 래퍼에 같은 chunk_id를 max-pool로 합치는 옵션으로 반영한다.
CPU 기준 처리 속도 측정.
dev20 전체(20개 PDF) 평균 2.01초/페이지 → 10페이지 환산 약 20.1초, baseline(HybridChunker) 대비 EU 빌드 자체의 오버헤드는 대부분 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에 정리.
여기서는 그 수치에 이르는 과정에서 확인한 것만 요약한다.
| 지표 | baseline | Evidence Chunker | 갭 |
|---|---|---|---|
| Recall | 0.574 | 0.625 | +5.1pp |
| EM | 0.314 | 0.569 | +25.5pp |
- base1024/base2048: 청크를 억지로 키워도 R@1/EM이 오히려 떨어진다.
- rowsplit: baseline 대비 EM +3.8pp(0.314→0.352)만 회복된다. 다중 뷰 색인 자체의 기여는 작고 캡션+문맥 결합(EU 고유 구조)이 개선분의 대부분을 설명한다.
-
semantic chunking:
SemanticChunker는 R@1 0.121, EM 0.145로 baseline보다도 성능이 떨어진다. -
문항 단위 승패: EU만 정답 521건, baseline만 정답 382건
- 문서 안에 구조적으로 비슷한 표가 여러 개 있어 검색 단계에서 엉뚱한 표를 골라온 경우로, 캡션/문맥 추출 자체의 실패는 아니다. (Limitations & Roadmap #2 참고)