Skip to content

QA Generation

dnjsgkfka edited this page Aug 23, 2026 · 3 revisions

벤치마크에 쓰는 Q&A(dev 200개 / full90 2725개)는 PDF에서 규칙 기반으로 자동 생성한다.

LLM, RNG 없이 완전히 결정적인 구조로 설계하였다.


생성 핵심 규칙

신뢰할 만한 Benchmark를 위해서는 QA 생성 자체가 평가 대상(Evidence Chunker)에 유리하게 편향돼 있지 않아야 한다.

  1. 격리(isolation): QA 생성기는 EvidenceChunker/HybridChunker 등 평가 대상 모듈을 import하지 않는다.
  2. 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 {행}?" 표에만 있고 문단에는 없는 셀 값

CI 불변식 자체 검증

채점 우위가 없음을 불변식으로 보증하고, 모든 문항을 내보내기 전에 스스로 검증하고, 검증에 실패한 문항은 폐기한다.

유형 표 markdown 단독 문단 단독 표+문단
cell_value / table_about PASS PASS
context_dependent FAIL FAIL PASS
  • cell_value/table_about은 baseline(HybridChunker)이 보는 텍스트(표 markdown)만으로도 풀려야 한다. 즉, EvidenceChunker 전용 통과 경로가 없어야 한다.

  • context_dependent는 표만으로도, 문단만으로도 풀리면 안 되고 반드시 둘을 합쳐야만 풀려야 한다. 즉, 구조상 HybridChunker는 통과할 수 없고 EU만 통과 가능하다.

    • 이를 위해 내부적으로 세 집합으로 나누어 문항을 만든다.

      v1 = 표 ∩ 문단  (연결 증거로만 사용, 문항에는 사용하지 않음)
      v2 = 표 − 문단  (정답 value)
      k  = 문단 − 표  (context_keys)
      

      표만 담긴 청크는 k가 없어 실패하고, 문단만 담긴 청크는 v2가 없어 실패하며, 표+문단(EU)만 v2k를 모두 가져 통과한다.


bbox/sim_threshold와 독립 설계

EvidenceChunker의 context.py는 표 주변 문단을 bbox_threshold(300pt) AND 코사인 유사도(sim_threshold)로 골라 부착한다.

QA 생성기가 같은 조건을 문단 선택 게이트로 재사용하면, 벤치마크 문항이 전부 그 창 안에서만 나오게 되어 "이 임계값이 적절한가"라는 질문 자체가 반증 불가능해진다.

따라서 QA 생성기는 거리를 게이트가 아닌 측정값으로만 사용한다.

모듈 동작 범위 : bbox 300pt ∧ cos 0.40
탐색 범위      : 같은 페이지 전체        ← 모듈 동작 범위를 진부분집합으로 포함

각 문항의 표-문단 수직 거리는 meta.dist_pt에 기록되고, 평가 후 "300pt 이내 vs 밖"으로 슬라이스해 창 크기가 적절한지 데이터로 검증한다.

대신 문단을 표와 연결하는 신호의 정밀도를 다음 두 조건으로 높인다.

  • 표와 문단에 공통으로 등장하는 숫자 포함 토큰이 2개 이상(MIN_LINK_TOKENS)
  • 문단이 "Table 3", "표 3"처럼 표 번호를 명시적으로 언급

전자는 우연의 일치를 걸러내고, 후자는 저자가 직접 선언한 근거이기 때문에 신호 1개만으로도 인정한다.

벤치마크의 Recall@k(검색 정확도)와는 별개로, QA 생성기의 문단-표 링크 탐지는 완전성보다 정밀도를 우선한다.

  • 놓친 문단-표 쌍은 문항이 안 만들어질 뿐이지만, 잘못 연결된 쌍은 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') 제외한다.
  • 캡션 미사용: 질문에 캡션 텍스트를 쓰지 않는다.
    • 캡션 매핑이 평가 대상 모듈(caption.py)이기 때문이다. table_about은 대신 표 안의 희소 헤더 토큰으로 표를 특정한다.
  • 문서 내 중복 억제: 판정 키를 question이 아니라 question_delabeled(표 라벨을 뗀 버전)로 잡는다. (실제로 임베딩되는 쪽이 delabeled)
  • 숫자 헤더 행 배제: 헤더의 절반 이상이 숫자면 표가 페이지를 넘어가며 이어질 때 데이터 행이 헤더로 잘못 승격된 것으로 보고 제외한다.

한계

  • 링크 신호 정밀도(MIN_LINK_TOKENS=2 또는 명시적 표 언급)를 우선하는 설계라, 실제로는 관련 있지만 신호가 약한 문단-표 쌍은 문항으로 만들어지지 않는다.
  • 행 개수를 묻는 문항("how many data rows")은 만들지 않는다. 파서 간 행 분할 방식 차이를 알고리즘 실패로 잘못 해석할 수 있기 때문이다.

Clone this wiki locally