-
Notifications
You must be signed in to change notification settings - Fork 1
QA Generation
벤치마크에 쓰는 Q&A(dev 200개 / full90 2725개)는 PDF에서 규칙 기반으로 자동 생성한다.
LLM은 쓰지 않고, RNG도 없어 완전히 결정적이다.
이 문서는 "QA셋이 Evidence Chunker에 유리하게 만들어진 건 아닌가"라는 질문에 답하기 위한 설계 근거를 정리한다.
벤치마크가 신뢰할 만하려면 QA 생성 자체가 평가 대상(Evidence Chunker)에 유리하게 편향돼 있지 않아야 한다.
- 격리(isolation): QA 생성기는 EvidenceChunker/HybridChunker 등 평가 대상 모듈을 import하지 않는다.
- CI 불변식(invariant): 모든 문항은 내보내기 전에 스스로 검증되고, 통과하지 못하면 폐기된다.
Docling은 baseline(HybridChunker)과 treatment(EvidenceChunker) 두 arm의 공통 프론트엔드이기 때문에 QA 생성기도 그대로 사용한다. Docling이 표를 놓치면 양쪽 답하지 못하는 공통 맹점이지, arm 간 편향은 아니다.
허용: docling, docling_core, table.model_dump(), export_to_markdown(), doc.texts, prov, stdlib
금지: smart_chunker, interfaces, evidence_chunker, context_attacher, table_splitter,
token_counter, bbox_utils, langchain_wrapper, _table_utils, _caption_mapper
assert_isolation()이 sys.modules를 검사해 금지 모듈이 하나라도 로드돼 있으면 실행을 중단한다.
| 유형 | 질문 형태 | 정답 |
|---|---|---|
cell_value |
"In {표}, what is the {열 헤더} for {행 라벨}?" | 표 안의 특정 셀 값 |
table_about |
"In {표}, in the context of {희소 헤더1}와 {희소 헤더2}, what columns does it have?" | 전체 열 구성 |
context_dependent |
"In the context of {문단 고유 토큰1}과 {문단 고유 토큰2}, what is the {열} for {행}?" | 표에만 있고 문단에는 없는 셀 값 |
context_dependent가 이 프로젝트의 핵심 주장(표+문맥 결합)을 직접 시험하는 유형이다.
파서(Docling)를 공유하는 이상 "문자열 겹침 0"은 원리적으로 불가능하다. 대신 채점 우위가 없음을 불변식으로 보증하고, 모든 문항을 내보내기 전에 스스로 검증한다.
| 유형 | 표 markdown 단독 | 문단 단독 | 표+문단 |
|---|---|---|---|
cell_value / table_about
|
PASS | – | PASS |
context_dependent |
FAIL | FAIL | PASS |
cell_value/table_about은 baseline(HybridChunker)이 보는 텍스트(표 markdown)만으로도 풀려야 한다. 즉, EvidenceChunker 전용 통과 경로가 없어야 한다.
context_dependent는 표만으로도, 문단만으로도 풀리면 안 되고 반드시 둘을 합쳐야만 풀려야 한다. 즉, 구조상 HybridChunker는 통과할 수 없고 EU만 통과 가능하다.
검증에 실패한 문항은 폐기되고 사유가 _validation.json에 남는다.
context_dependent는 내부적으로 세 집합으로 나눠 만든다.
v1 = 표 ∩ 문단 (연결 증거로만 사용, 문항에는 사용하지 않음)
v2 = 표 − 문단 (정답 value)
k = 문단 − 표 (context_keys)
표만 담긴 청크는 k가 없어 실패하고, 문단만 담긴 청크는 v2가 없어 실패하며, 표+문단(EU)만 v2와 k를 모두 가져 통과한다.
EvidenceChunker의 context.py는 표 주변 문단을 bbox_threshold(300pt) AND 코사인 유사도(sim_threshold)로 골라 부착한다. QA 생성기가 같은 300pt를 문단 선택 게이트로 재사용하면, 벤치마크 문항이 전부 그 창 안에서만 나오게 되어 "이 임계값이 적절한가"라는 질문 자체가 반증 불가능해진다(circular).
그래서 QA 생성기는 거리를 게이트가 아니라 측정값으로만 사용한다.
모듈 동작 범위 : bbox 300pt ∧ cos 0.40
탐색 범위 : 같은 페이지 전체 ← 모듈 동작 범위를 진부분집합으로 포함
각 문항의 표-문단 수직 거리는 meta.dist_pt에 기록되고, 평가 후 "300pt 이내 vs 밖"으로 슬라이스해 창 크기가 적절한지 데이터로 검증한다.(Benchmark의 bbox 윈도우 검증이 이 필드를 사용).
대신 문단을 표와 연결하는 신호의 정밀도를 다음 두 조건으로 높인다.
- 표와 문단에 공통으로 등장하는 숫자 포함 토큰이 2개 이상(
MIN_LINK_TOKENS) - 문단이 "Table 3", "표 3"처럼 표 번호를 명시적으로 언급
전자는 우연의 일치를 걸러내고, 후자는 저자가 직접 선언한 근거라 신호 1개만으로도 인정한다. 벤치마크는 완전성(recall)이 아니라 정밀도가 필요하다는 판단에서 나온 설계이다 — 놓친 문단은 그냥 문항이 안 될 뿐이지만, 잘못 잡은 문단은 EU가 포함할 이유 없는 문단으로 불공정한 문제를 만든다.
def em_hit(spec: dict, chunk_text: str) -> bool:
c = normalize_for_em(chunk_text)
if not has_token(spec["value"], c):
return False
return all(has_token(k, c) for k in spec["context_keys"])정답 여부는 "값과 그 값을 특정하는 문맥 키가 같은 청크 안에 모두 있는가"로 판정한다. 문자열 연결이 아니라 집합 포함 검사라 순서·구분자·조사에 무관하다.
EU의 "행헤더 | 열헤더: 값" 형식과 baseline의 "| 행헤더 | 값 |" markdown 형식이 표현은 달라도 동일하게 판정된다. answer_spec.value는 채점용이라 정리하지 않고 표 markdown 원문과 일치시킨다(그래야 CI 불변식이 성립된다). 사람이 보는 answer 필드는 표시·검수용으로 별도 정리하며 채점에는 쓰이지 않는다.
- 값 유일성: 정답이 표 안에서 유일하지 않으면 제외한다. — 어느 셀이 정답인지 결정되지 않아 변별력이 0이 되기 때문이다.
- 정답 노출 차단: 행/열 라벨 자체에 정답 값이 포함돼 있으면(예: "ViT-L/14-336px의 Input?" → 정답 '336') 제외한다.
-
캡션 미사용: 질문에 캡션 텍스트를 쓰지 않는다. — 캡션 매핑이 평가 대상 모듈(팀원 1의
caption.py)이기 때문이다.table_about은 대신 표 안의 희소 헤더 토큰으로 표를 특정한다. -
문서 내 중복 억제: 판정 키를
question이 아니라question_delabeled(표 라벨을 뗀 버전)로 잡는다 — 실제로 임베딩되는 쪽이 delabeled이기 때문이다. - 숫자 헤더 행 배제: 헤더의 절반 이상이 숫자면 표가 페이지를 넘어가며 이어질 때 데이터 행이 헤더로 잘못 승격된 것으로 보고 제외한다.
- 링크 신호 정밀도(
MIN_LINK_TOKENS=2또는 명시적 표 언급)를 우선하는 설계라, 실제로는 관련 있지만 신호가 약한 문단-표 쌍은 문항으로 만들어지지 않는다. - 행 개수를 묻는 문항("how many data rows")은 만들지 않는다. 파서 간 행 분할 방식 차이를 알고리즘 실패로 잘못 해석할 수 있기 때문이다.