Skip to content

Experiments

dnjsgkfka edited this page Aug 18, 2026 · 3 revisions

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에 실린 최종 수치

01. Version 01

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가지

  1. 다중 뷰 색인 미통제 — baseline은 청크 1개=벡터 1개, EU는 retrieval_units를 펼쳐 청크 1개=벡터 여러 개로 색인. EU가 단지 검색 기회가 많아서 이기는 건 아닌지 통제 필요 → §03에서 rowsplit 대조군으로 검증.
  2. context_dependent에서 base2048에 밀림 — EU EM 0.200 < base2048 EM 0.222. 표가 커서 행 단위로 분할될 때(split_eu()) context_before는 첫 조각에만, context_after는 마지막 조각에만 들어가고 중간 조각은 문맥이 없는 구조가 원인 → §02-2에서 수정.
  3. 페이지 라벨 ±1 오차EvidenceUnit.page_no가 표 자신의 페이지 하나뿐이라, 인접 페이지에서 끌어온 문맥이 있어도 그 사실이 metadata에 안 남음. 정답을 찾고도 페이지 라벨만 달라 오답 처리되는 경우 존재 → §02-3에서 수정.

02. 실험

dev20(20개 PDF, 430문항) 기준 소규모 반복 실험.

02-1. arm 추가

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 우위를 설명하지 못 한다.

02-2. 문맥 정보 반복 삽입

split_eu()가 분할된 모든 조각에 context_before/context_after를 반복 삽입하도록 수정.

유형 이전 EU EM 이후 EU EM
context_dependent 0.167 0.222

base2048(0.222)과 동률까지 회복. 문항 단위 검증 결과 악화 0건, 개선 5건 — 순수 개선.

02-3. page_span

인접 페이지에서 끌어온 문맥의 페이지 번호를 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 소폭 상승

효과는 작지만 방향은 일관되게 개선되므로 반영.


03. Version 02

§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에서 원인 규명.

04. 실험

dev20 기준 파라미터 스윕과 §03에서 발견한 Recall@10 문제의 원인 규명.

04-1. bbox_threshold 스윕

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 표본으로는 늘릴 근거가 부족.

04-2. Label Boosting (참고용, 미채택)

사용자가 페이지를 명시했다고 가정하고 검색 랭킹을 부스팅하면 얼마나 개선되는지 측정.

조건 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의 구조적 이점을 확인하는 정도의 참고 자료로만 보관하고, 라이브러리 기능으로는 반영하지 않기로 한다.

04-3. max-pooling 재랭킹

§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로 합치는 옵션으로 반영한다.

04-4. 처리 속도

CPU 기준 처리 속도 측정.

dev20 전체(20개 PDF) 평균 2.01초/페이지 → 10페이지 환산 약 20.1초. baseline(HybridChunker) 대비 EU 빌드(build_evidence_units+split_oversized_units) 자체의 오버헤드는 대부분 1초 미만

docling 파싱 시간이 전체의 90% 이상을 차지하고, EU 구성 로직은 병목이 아니다.

04-5. 경량 임베더 비교

지금까지 모든 실험이 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는 그대로 유지하기로 한다.

04-6. sim_threshold 스윕

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 없이도 동작하도록 구현한다.


05. Version 04 (FINAL)

모든 변경사항을 반영해 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 참고).

Clone this wiki locally