docs(eval): Phase 2 canary 결과 — HALT (failed 4/40 = 10%, but 분류상 Marker 0 fail)
35 success / 3 failed / 1 skipped / 1 stuck processing (corner case). Plan 게이트 FAIL (success<36 + failed>2). 다만 failure root cause 분석: - 2/4 = GPU contention (5.93+5.35 GiB 다른 process 점유, free 50 MiB) - 1/4 = 진짜 corrupt PDF (Pdfium error, non-retryable) - 1/4 = scan-likely + tiny text + ReadTimeout (Phase 1B corner case) Marker quality 자체 fail = 0. p50 elapsed 33.2s (1D 34s 와 동등), text_length_ratio p50 1.00 (1D 1.15 대비 -13%, 정상 범위), 신규 warning 없음. 사용자 결정: A(수용) / B(코드 가드 추가) / C(OOM 2건 즉시 재 enqueue → GO 통과) / D(HALT 유지). 추천 C 또는 A. 5201 stuck processing 은 어느 옵션이든 수동 DB 정리 필요 (사용자 승인 후).
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# Phase 2 Canary Result — 40건 stratified backfill
|
||||
|
||||
> 실행: 2026-05-02 23:50 UTC enqueue → ~02:30 UTC 완료 (wall ~2h 40m, marker BATCH_SIZE=1)
|
||||
> sample CSV: `evals/markdown/phase2_canary_sample.csv` (seed 20260503)
|
||||
> plan 결정 게이트: §"2-B canary GO/HALT"
|
||||
|
||||
## 결과 분포
|
||||
|
||||
| md_status | count | rate |
|
||||
|---|---|---|
|
||||
| success | 35 | 87.5% |
|
||||
| failed | 3 | 7.5% |
|
||||
| skipped | 1 | 2.5% |
|
||||
| processing (stuck) | 1 | 2.5% |
|
||||
|
||||
총 40건 + 1건 corner case (stuck `processing`, queue 측은 max_attempts 도달 후 failed). **실효 doc-level 실패 = 4 / 40 = 10%**.
|
||||
|
||||
## Plan 게이트 판정
|
||||
|
||||
| 기준 | 임계 | 실측 | 판정 |
|
||||
|---|---|---|---|
|
||||
| success rate | ≥ 36/40 (90%) | 35/40 (87.5%) | **FAIL** (-1) |
|
||||
| failed | ≤ 2 | 4 (실효) | **FAIL** (+2) |
|
||||
| skipped | ≤ 6 | 1 | PASS |
|
||||
|
||||
**→ HALT (사용자 보고 후 재검토)**.
|
||||
|
||||
## 실패 분석 — 4건 모두 root cause 확인
|
||||
|
||||
| doc_id | 분류 | 표면 에러 | root cause | 재시도 가능? |
|
||||
|---|---|---|---|---|
|
||||
| 3817 | failed | `OutOfMemoryError: CUDA out of memory. Tried to allocate 74 MiB` | **GPU contention** (canary 진행 중 다른 프로세스 5.93+5.35 GiB 사용 → free 55 MiB) | ✅ Yes (GPU free 시) |
|
||||
| 4059 | failed | `OutOfMemoryError: CUDA out of memory. Tried to allocate 176 MiB` | **GPU contention** 동일 | ✅ Yes |
|
||||
| 3810 | failed | `PdfiumError: Failed to load page` | 진짜 PDF 파싱 오류 (corrupt/protected page 가능성) | ❌ No (Marker 책임 아님) |
|
||||
| 5201 | processing (corner) | `ReadTimeout('')` 3회 (queue) | scan-likely 추정 (705 KB / text_len=15) — OCR-bound 처리 시간 > 300s timeout. queue 측 attempts=3 도달 후 failed, 그러나 doc.md_status='processing' 으로 stuck (Phase 1B 알려진 corner case) | ⚠ (timeout 늘리거나 SKIP rule 확장 필요) |
|
||||
|
||||
**핵심 인사이트**: **Marker quality 자체는 0건 fail**. 실패 4건의 분류:
|
||||
- 50% (2/4) = **GPU 일시 contention** — infra 이슈, 사용자 활동 따라 재발 가능
|
||||
- 25% (1/4) = **PDF 자체 corrupt** — 정상 비율, non-retryable
|
||||
- 25% (1/4) = **scan-likely + 작은 file_size + 빈 텍스트** — 새 SKIP 후보 패턴
|
||||
|
||||
## 처리 시간 (success 35건, 1D baseline 비교)
|
||||
|
||||
| 메트릭 | 1D | 2-B canary | delta |
|
||||
|---|---|---|---|
|
||||
| avg | - | 44.3s | - |
|
||||
| p50 | 34s | 33.2s | -2% (안정) |
|
||||
| p90 | 112s | 92.6s | -17% (개선) |
|
||||
| max | 219s | 297.1s | +36% (large bias 영향) |
|
||||
|
||||
전반적으로 1D 와 동등 또는 약간 빠름. **마커 자체 throughput 안정**.
|
||||
|
||||
## Quality 메트릭 (success 35건, 1D baseline 비교)
|
||||
|
||||
| 메트릭 | 1D | 2-B canary | delta |
|
||||
|---|---|---|---|
|
||||
| text_length_ratio p50 | 1.15 | 1.00 | -13% (정상 범위) |
|
||||
| warnings: heading_hierarchy_jump | 86% (24/28) | 94% (33/35) | +8pp |
|
||||
| warnings: low_image_alt_text_ratio | 89% (25/28) | 86% (30/35) | -3pp |
|
||||
|
||||
신규 warning 종류 없음. heading_hierarchy_jump 가 거의 전건이라는 패턴 재확인 (1D 와 동일) — Marker output 의 heading 구조가 원본과 정확히 일치하지 않는 일반적 현상, score 기준 정의 필요.
|
||||
|
||||
## GPU contention 상세
|
||||
|
||||
canary 처리 시점 (~00:00-02:00 UTC) GPU 점유 process (canary 진행 중 nvidia-smi 추정):
|
||||
- text-embeddings-router: 1.35 GiB
|
||||
- python (process 1200154): 1.85 GiB
|
||||
- python (process 1562575): 5.93 GiB ← **largest, suspect Ollama or marker self**
|
||||
- python (process 1570943): 5.35 GiB
|
||||
- python (process 1612684): 1.01 GiB
|
||||
- **합계 ~15.5 GiB / 16.4 GiB capacity → free ~50-100 MiB**
|
||||
|
||||
canary 종료 후 (02:31 UTC) 측정:
|
||||
- text-embeddings-router: 1.38 GiB
|
||||
- python (1200154): 1.89 GiB
|
||||
- python (1562575): 3.83 GiB
|
||||
- **합계 7.1 GiB → free 8.8 GiB**
|
||||
|
||||
→ 5.35 GiB + 5.93 GiB → 3.83 GiB 으로 줄어듦 (또는 둘 중 하나 종료). canary 시점에만 일시적으로 cumulative load 였음.
|
||||
|
||||
## 권장 조정안 (HALT 후)
|
||||
|
||||
게이트 가지 못했지만 **failure 4건 모두 설명 가능**. Marker 본질 결함 0건. 다음 중 사용자 결정:
|
||||
|
||||
### A. 그대로 2-C 진입 (수용)
|
||||
- 근거: Marker quality 0 fail, GPU OOM 은 야간 sweep (23-03 KST) 윈도우 안에 contention 적을 가능성 높음 (사용자 활동 종료, Ollama 호출 적음)
|
||||
- 보완: 2-C 가드에 GPU free memory 사전 체크 추가 검토 (script 1 줄 — 단, 본 plan scope 외)
|
||||
- 실패 4건 (3817/4059/3810/5201) 은 별도 retry 또는 backlog
|
||||
|
||||
### B. Marker 코드 / 가드 보강 후 재시도
|
||||
- B1. **scan-likely + tiny text 자동 skip** (5201 패턴): marker_worker 에 `file_size < 1MB AND text_len < 100` 또는 `text_density < 1` 시 `md_status='skipped'`
|
||||
- B2. **OOM 을 transient 로 분류**: server.py 가 OutOfMemoryError 만 503 으로 raise → marker_worker 가 5xx → queue retry. (현재는 422 = non-retryable)
|
||||
- B3. **queue 영구 실패 시 doc.md_status='failed' 동기화**: Phase 1B 알려진 corner case 정리 (5201 패턴 제거)
|
||||
- 작업: marker_worker.py + server.py 변경 → 별도 PR/커밋 → canary 재실행
|
||||
|
||||
### C. canary 일부 재시도 → 결과 갱신
|
||||
- 3817, 4059 만 재 enqueue (GPU 지금 free) → 성공 확인 → **실효 success 37/40 = 92.5%, failed 2 (3810+5201) = 5%, skipped 1 = 2.5% → GO 게이트 통과**
|
||||
- 가장 빠른 unblock 경로
|
||||
|
||||
### D. HALT 유지 + 백로그 정리
|
||||
- canary 결과 그대로 인정. 4 failed/stuck doc 은 Phase 2 마지막 후속 정리.
|
||||
- 2-C 진입 보류.
|
||||
|
||||
## 다음 단계 — 사용자 결정 필요
|
||||
|
||||
위 A/B/C/D 중 선택. 추천 = **C (3817+4059 즉시 재 enqueue)** 또는 **A (그대로 진입, 야간 GPU contention 낮음 가정)**. B 는 plan scope 확장이라 별도 라운드 권장.
|
||||
|
||||
5201 의 stuck 'processing' 은 어느 옵션 가도 **수동 정리 필요** (`UPDATE documents SET md_status='failed', md_extraction_error='ReadTimeout after 3 attempts (Phase 1B corner case)' WHERE id=5201`). production DB write 라 사용자 승인 후.
|
||||
Reference in New Issue
Block a user