Predict the correct answer for image + context + question + 3 options, while knowing when there is not enough information to answer — measured by Balanced Accuracy over ambiguous and disambiguated questions.
이미지 + 상황 + 질문 + 선택지 3개에서 정답을 고르되, 근거가 부족하면 "모름"을 선택해야 하는 과제. 모호(ambiguous) 문항과 명확(disambiguated) 문항의 Balanced Accuracy로 평가.
Stack: Python · Qwen2.5-VL (3B / 7B, 4-bit) · Hugging Face Transformers · PyTorch · Kaggle GPU Result: Public Balanced Accuracy 0.883 (rank ~103) on the first complete submission · fully offline, open-weights only (no external API)
A multimodal variant of BBQ (Bias Benchmark for QA). Each sample provides an image, a context, a question, and exactly three options — one of which always means "unknown" / "cannot be determined." The model must output the answer index (0/1/2).
- Ambiguous questions: the context gives no real evidence → the correct answer is the unknown option.
- Disambiguated questions: the context names the responsible person → pick that person.
- The metric is Balanced Accuracy = (accuracy on ambiguous + accuracy on disambiguated) / 2. A model that always guesses a person collapses on ambiguous items; one that always answers "unknown" collapses on disambiguated items. Calibrating "is there enough evidence?" is therefore half the score.
- ❌ No external inference APIs (OpenAI/Gemini/HF Inference, etc.). ✅ Open-source weights loaded locally only.
- Only models with weights released before 2026-05-31.
- The final answer must be LLM-generated text (no pure rule/majority mapping).
- Inference must be reproducible offline within a reference env (RTX A6000 48GB), ~0.5s/sample target.
- No data leakage: training/validation data must not be derived from the test set.
- Built a labeled validation set from scratch. The competition ships only one training example, so I constructed a 600-sample balanced dev set (300 ambiguous / 300 disambiguated) from the public BBQ dataset (text proxy) — used to measure Balanced Accuracy locally without spending the 5/day DACON submission budget. (No test data used → leakage-free.)
- Iterative prompt engineering, one change at a time, each evaluated on the dev set (10 experiments logged in
EXPERIMENTS.md). - Final model: Qwen2.5-VL-7B-Instruct (4-bit) + balanced chain-of-thought prompt. A faster 3B config is kept as a fallback.
- Chain-of-Thought is the single biggest lever. Asking the model to reason before answering cut overconfidence on ambiguous items from 0.47 → 0.09, lifting Balanced Accuracy from 0.71 → 0.87+.
- Model size × prompt strongly interact. The same prompt flips results across model sizes:
- 3B follows instructions weakly → "disambiguation-reinforcing" prompts make it over-pick people. Best with a cautious CoT (dev 0.848).
- 7B follows instructions well → a balanced CoT ("if evidence is stated, treat it as fact and name the person; otherwise answer unknown") fixes both sides (dev 0.923).
| Step | Change | Ambiguous | Disambiguated | Balanced | Kept |
|---|---|---|---|---|---|
| baseline | 3B, plain prompt (v0) | 0.527 | 0.897 | 0.712 | base |
| exp3 | 3B + CoT (v4) | 0.935 | 0.806 | 0.870 | ★ |
| exp7 | 3B + cautious CoT (full) | 0.863 | 0.833 | 0.848 | ★ fallback |
| exp9 | 7B + balanced CoT (v6) | 0.946 | 0.926 | 0.936 | ★ |
| exp10 | 7B + balanced CoT (full) | 0.913 | 0.933 | 0.923 | ★★ best |
| exp11 | 7B, max_new_tokens 200→96 | 0.913 | 0.930 | 0.922 | ★ (2× faster, same acc) |
- First complete submission (3B cautious-CoT): Public Balanced Accuracy 0.883, rank ~103.
- Validation insight: dev (0.848) < public (0.883) → the text-only dev set is a trustworthy, slightly conservative proxy for the real multimodal test, so dev improvements can be trusted for fast iteration.
Running an 8,500-sample VLM inference on a free Kaggle T4 initially took ~18s/sample (≈40h) — impossible within Kaggle's 12h limit. Root-causing this was the bulk of the work:
device_map="auto"silently offloaded part of the 7B model to CPU, causing a PCIe round-trip on every generated token. → Forceddevice_map={"":0}(whole model on one GPU).- The T4 bf16 trap:
torch.cuda.is_bf16_supported()returns True on a T4 (Turing, sm_75), but bf16 is only emulated there → slow. The auto-fallback never triggered. → Detect by compute capability < 8.0 (or "T4" in the name) and force fp16. - Added batched generation, checkpoint/resume (safe across 12h session caps & disconnects), and unbuffered streaming logs for live progress.
Result: 18s → 3.4s/sample, the full 8,500-sample run finishes in ~8h within one Kaggle commit.
pip install -r requirements.txt
# 1) build the dev set from public BBQ (one-time)
python build_dev.py
# 2) measure Balanced Accuracy locally on the dev set
python eval_dev.py --config best_config.json --batch 4
# 3) generate the test submission (8,500 rows)
python infer.py --config best_config.json --data_dir ./open --out submission.csv --batch 4 --resumeFor a free GPU, run on Kaggle — see README_kaggle.md (single-cell kaggle_run.py, "Save & Run All (Commit)" headless mode).
bias_pipeline.py # model load (device/dtype fixes), prompts v0–v7, batched generate
infer.py # full-test inference: checkpoint/resume, streaming progress
eval_dev.py # Balanced Accuracy on the local dev set
build_dev.py # build the 600-sample dev set from public BBQ
configs/ # versioned prompt + hyperparameter configs
best_config.json # current best (7B + balanced CoT, 96 tokens)
dev/dev.csv # 600-sample balanced validation set (from public BBQ)
EXPERIMENTS.md # full experiment log (every change, every score)
kaggle_run.py # single-cell Kaggle runner (Commit mode)
README_kaggle.md # Kaggle setup & run guide
Building your own measurement (a leakage-free dev set) is what makes iteration possible; prompt strategy interacts with model scale; and on real hardware, systems debugging (dtype, device placement, batching, checkpointing) matters as much as modeling.
BBQ: A Hand-Built Bias Benchmark for QA (CC-BY-4.0) · Qwen2.5-VL · DACON / Sungkyunkwan University.
BBQ(Bias Benchmark for QA)의 멀티모달 버전. 각 샘플은 이미지 + 상황(context) + 질문 + 선택지 3개로 구성되고, 선택지 중 하나는 항상 "모름/판단 불가" 계열이다. 정답 인덱스(0/1/2)를 예측한다.
- 모호(ambiguous): 맥락에 근거 없음 → 정답은 모름 옵션.
- 명확(disambiguated): 맥락에 근거 있음 → 해당 인물.
- 지표는 Balanced Accuracy = (모호 정확도 + 명확 정확도) / 2. 사람만 찍으면 모호에서, 모름만 찍으면 명확에서 무너진다. "근거가 충분한가?"의 보정이 점수의 절반.
- ❌ 외부 추론 API 금지(OpenAI/Gemini/HF 등). ✅ 오픈소스 가중치를 로컬에 직접 로드만 허용.
- 2026-05-31 이전 공개 가중치만 사용.
- 최종 답은 LLM이 생성한 텍스트여야 함(룰/다수결 매핑 불가).
- 오프라인 재현 가능해야 함(기준 환경 RTX A6000 48GB, 샘플당 ~0.5초 권장).
- Data Leakage 금지: 학습/검증 데이터를 test에서 파생 금지.
- 라벨 검증셋을 직접 구축. 학습 예시가 1개뿐이라, 공개 BBQ 데이터로 균형 dev셋 600개(모호/명확 300/300)(텍스트 프록시)를 만들어 DACON 제출(하루 5회)을 쓰지 않고도 로컬에서 Balanced Accuracy를 측정. (test 미사용 → leakage 없음.)
- 한 번에 하나씩 프롬프트 개선, 매번 dev로 측정(실험 10회,
EXPERIMENTS.md). - 최종 모델: Qwen2.5-VL-7B-Instruct(4bit) + 균형 CoT 프롬프트. 빠른 3B 설정은 폴백으로 보존.
- CoT(추론 먼저)가 최대 레버. 답하기 전에 추론시키자 모호 문항의 과확신이 0.47 → 0.09로 급감, Balanced Accuracy가 0.71 → 0.87+로 상승.
- 모델 크기 × 프롬프트가 강하게 상호작용. 같은 프롬프트도 모델 크기에 따라 결과가 뒤집힘:
- 3B는 지시추종이 약해, "명확성 보강" 프롬프트를 주면 사람을 과하게 찍음 → 신중 CoT가 최적(dev 0.848).
- 7B는 지시추종이 좋아, 균형 CoT("근거 명시되면 사실로 인물 선택, 없으면 모름")가 양쪽 다 잡음(dev 0.923).
| 단계 | 변경 | 모호 | 명확 | Balanced | 채택 |
|---|---|---|---|---|---|
| baseline | 3B, 기본 프롬프트(v0) | 0.527 | 0.897 | 0.712 | base |
| exp3 | 3B + CoT(v4) | 0.935 | 0.806 | 0.870 | ★ |
| exp7 | 3B + 신중 CoT(full) | 0.863 | 0.833 | 0.848 | ★ 폴백 |
| exp9 | 7B + 균형 CoT(v6) | 0.946 | 0.926 | 0.936 | ★ |
| exp10 | 7B + 균형 CoT(full) | 0.913 | 0.933 | 0.923 | ★★ best |
| exp11 | 7B, max_new_tokens 200→96 | 0.913 | 0.930 | 0.922 | ★ (2배 빠름, 정확도 동일) |
- 첫 완전 제출(3B 신중 CoT): Public Balanced Accuracy 0.883, 약 103등.
- 검증 인사이트: dev(0.848) < public(0.883) → 텍스트 dev셋이 실제 멀티모달 시험을 약간 보수적으로 잡는, 믿을 만한 프록시임을 확인 → dev 개선을 신뢰하고 빠르게 반복 가능.
무료 Kaggle T4에서 8,500개 VLM 추론이 처음엔 샘플당 18초(약 40시간) — Kaggle 12시간 제한 내 불가능. 원인 규명이 작업의 핵심이었다:
device_map="auto"가 7B 일부를 CPU로 오프로드 → 매 토큰 PCIe 왕복으로 치명적 저하. →device_map={"":0}(단일 GPU 통째 적재)로 해결.- T4 bf16 함정:
torch.cuda.is_bf16_supported()가 T4(Turing, sm_75)에서 True를 반환하지만 bf16은 에뮬레이션이라 느림. 자동 폴백이 안 걸림. → compute capability < 8.0(또는 이름에 "T4")이면 fp16 강제. - 배치 추론, 체크포인트/resume(12시간 세션·연결끊김 대비), 실시간 스트리밍 로그 추가.
결과: 18초 → 3.4초/샘플, 8,500개 전체가 한 커밋(~8시간)에 완주.
pip install -r requirements.txt
# 1) 공개 BBQ로 dev셋 구축 (최초 1회)
python build_dev.py
# 2) 로컬 dev셋에서 Balanced Accuracy 측정
python eval_dev.py --config best_config.json --batch 4
# 3) test 제출파일 생성 (8,500행)
python infer.py --config best_config.json --data_dir ./open --out submission.csv --batch 4 --resume무료 GPU는 Kaggle 권장 — README_kaggle.md 참고(단일 셀 kaggle_run.py, "Save & Run All (Commit)" 백그라운드 모드).
bias_pipeline.py # 모델 로드(디바이스/dtype 수정), 프롬프트 v0–v7, 배치 생성
infer.py # 전체 추론: 체크포인트/resume, 진행률 스트리밍
eval_dev.py # 로컬 dev셋 Balanced Accuracy 측정
build_dev.py # 공개 BBQ로 600개 dev셋 구축
configs/ # 프롬프트·하이퍼파라미터 버전별 설정
best_config.json # 현재 best (7B + 균형 CoT, 96토큰)
dev/dev.csv # 600개 균형 검증셋 (공개 BBQ 기반)
EXPERIMENTS.md # 전체 실험 로그 (모든 변경·점수)
kaggle_run.py # Kaggle 단일 셀 실행기 (Commit 모드)
README_kaggle.md # Kaggle 셋업·실행 가이드
라벨 없는 문제에서 "측정 도구(leakage 없는 dev셋)"를 직접 만드는 것이 반복 개선을 가능케 한다. 프롬프트 전략은 모델 규모와 상호작용하며, 실제 하드웨어에서는 시스템 디버깅(dtype·디바이스 배치·배칭·체크포인트)이 모델링만큼 중요하다.
BBQ: A Hand-Built Bias Benchmark for QA (CC-BY-4.0) · Qwen2.5-VL · DACON / 성균관대학교.
This project was developed for the 2026 SKKU Multimodal AI Bias Challenge (DACON). Competition test data is not redistributed here; see DACON for data access. 본 프로젝트는 2026 성균관대 멀티모달 AI Bias 챌린지(DACON)를 위해 개발되었습니다. 대회 평가 데이터는 재배포하지 않으며, 데이터는 DACON에서 받으세요.