# plan: 도메인 특화 소형 검출기 — OWL-v2 대체

> 작성: 2026-08-01 · 상태: **승인됨 · Step 0 완료** · 방향: (A) heatmap 기본 + (C) 병행 실험
> 근거: CH64 64-18 / 64-20 / 64-21 · 관련: `docs/v5/owlv2_grounder_brief.html`

---

## 0. 왜 하는가 (한 줄)

실기 성패가 검출률로 결정되는데(gnd%≥80 → 98.8%), 그 검출을 담당하는 OWL-v2가
**전체 지연의 97%(1901.7ms)를 먹으면서도 실패 프레임의 절반 이상을 놓친다**.
대상이 "회색 바구니" 단일 클래스로 고정이므로 범용 open-vocabulary 능력은 불필요하다.

---

## 1. 목표와 비목표

**목표**
- OWL-v2를 대체하는 단일 클래스 검출기. `cx, cy, area, has_bbox` 동일 인터페이스 유지
  → 액션 헤드·서빙 로직 무수정으로 교체 가능
- **1차 지표**: threshold 0.20 등가 조건에서의 검출률 (실기와 직결되는 유일한 지표)
- **2차 지표**: 지연. 목표는 라즈베리파이에서 동작 가능한 수준

**비목표 (이번 범위 아님)**
- 임의 객체 검출(open-vocabulary) — 언어 조건화는 이미 사용하지 않음
- 액션 헤드 변경 — 이미 충분함이 확인됨(0.866M)
- 라즈베리파이 실제 이식 — 본 계획은 "검출기 확보"까지

---

## 2. 핵심 함정 — 이걸 먼저 정하지 않으면 실패한다

> **OWL-v2의 출력을 라벨로 써서 OWL-v2를 이기는 검출기를 만들 수 없다.**

우리가 고치려는 대상은 **OWL-v2가 실패하는 프레임**(score 0.10~0.20 구간, 미검출의 54.8%)이다.
그 프레임에서 OWL-v2의 출력은 정확히 신뢰할 수 없는 값이다. 온라인 OWL 출력을
pseudo-label로 쓰면 **실패를 그대로 복제**하게 된다.

### 발견: 강한 교사가 이미 존재한다

`docs/v5/bbox_frame_level/bbox_dataset_v6_pg448_cx.json` (학습용 V6 주석)을 확인한 결과:

| 항목 | 값 |
|---|---|
| 프레임 수 | **16,599** (225 에피소드) |
| `has_bbox=True` | **100.0%** |
| `confidence` 중앙값 | **1.000**, 0.20 미만 **0.0%** |
| `area_det` 중앙값 | 0.0574 — **45.0%가 area<0.05 (작은/먼 객체)** |
| `consistent` | 100.0% |

**즉 오프라인 주석 파이프라인은 "작고 먼 객체"에도 라벨을 붙이는 데 성공했다.**
온라인 OWL-v2가 놓치는 바로 그 구간이다. 오프라인은 시간 제약이 없어
PG448 + 2배 줌 재그라운딩(CH57) + 재시도를 쓸 수 있기 때문으로 보인다.

→ **따라서 이 과제는 "새 검출기를 처음부터 학습"이 아니라
"느리고 정확한 오프라인 파이프라인을 빠른 소형 모델로 증류(distill)"** 다.
이 프레이밍이 라벨 확보 문제를 대부분 해소한다.

### ✅ Step 0 완료 (2026-08-01) — 결론: 교사는 쓸 만하다. 단 전제 3개를 정정한다

**① `confidence = 1.0`은 placeholder였다 (내 가정이 틀렸음)**
`scripts/gen_v6_pg448_annotation.py`를 읽어보니 **`confidence` 필드를 아예 쓰지 않는다** —
`cx_det/cy_det/area_det/detected/has_bbox/grounding_cached`만 기록하고, `confidence`는
이전 단계에서 `dict(fr)`로 복사돼 온 잔존값이다. 전 16,599프레임이 정확히 1.0이다.
→ **"confidence 중앙 1.000이니 교사가 강하다"는 근거로 쓸 수 없다.** 위 표에서 해당 행 무효.

**② 실사용 가능 라벨은 16,599가 아니라 5,752건 (34.7%)**
| 구분 | 건수 | 비고 |
|---|---|---|
| 전체 | 16,599 | |
| `grounding_cached=True` | 10,847 (65.3%) | **직전 LIVE 결과를 상속한 값 — 독립 라벨 아님** |
| **LIVE & detected (실사용)** | **5,752 (34.7%)** | 에피소드당 중앙 25장 (18~36) |

캐시 프레임을 라벨로 쓰면 같은 답을 3번 학습시키는 꼴이라 반드시 제외한다.

**③ 라벨 자체는 정확하다 — 육안 24/24 확인**
`area<0.05` LIVE 라벨(2,491건)에서 무작위 24장을 렌더링해 직접 확인한 결과
**24장 전부 회색 바구니를 정확히 잡고 있었다**(area 0.0254~0.0485).
fallback 값(`cx=0.5,cy=0.5,area=0.05`) 정확 일치 프레임은 **0건**으로, 미검출이
라벨로 섞여 들어간 오염도 없다. → **"오프라인 파이프라인이 작고 먼 객체에도 라벨을
붙였다"는 핵심 전제는 유지된다.**
검증 이미지: `docs/v5/ch64_figs/step0_label_check_small.png`

**④ 주의 — 공간 prior가 박혀 있다**
오탐 필터가 `cy<0.35 | area<0.010 | area>0.9`를 제거하므로 라벨의
**cy 최솟값이 0.582**다. 즉 학습셋에는 화면 아래쪽 40% 구간의 타겟만 존재한다.
바닥에 놓인 바구니라 자연스럽지만, **검출기가 이 prior를 그대로 학습**하게 되므로
평가 시 이 점을 명시해야 한다(상단 배치는 원리적으로 못 잡음).

**실사용 라벨의 area 분포**: 중앙 0.0609 · `<0.05` 43.3%(2,491건) ·
**목표 구간 0.05~0.09 16.9%(972건)** — 우선 개선 대상 표본이 충분하다.

---

## 3. 데이터

| 출처 | 규모 | 성격 | 용도 |
|---|---|---|---|
| V6 주석 (`bbox_dataset_v6_pg448_cx.json`) | 16,599 프레임 | 오프라인 교사 라벨, 조작 데이터 | **학습 주력** |
| 실기 세션 H5 (176개, 회수 완료) | ~3,250 프레임 | 실제 배포 분포 | **평가 + 도메인 적응** |
| 백필 confidence (`backfill_scores/`) | 1,084 프레임 | 온라인 OWL 실측 score | **난이도 자동 선별** |

**난이도 가중 — ⚠️ confidence 단독 기준은 틀렸다 (soda 지적, 2026-08-01 정정)**

백필 score로 "OWL이 실패한 프레임"을 자동 선별할 수 있지만, **confidence만으로 뽑으면
실패 원인이 아닌 곳에 자원을 쏟게 된다.** 실측:

| 위치 | 실패 기여도 | confidence 단독 가중 | 배분 오차 |
|---|---|---|---|
| 강우 | 18.2% (2/11) | **50.7%** | **2.8배 과대** |
| 강좌 | 36.4% (4/11) | 17.9% | 2.0배 과소 |
| 약좌 | 36.4% (4/11) | 19.3% | 1.9배 과소 |
| 약우 | 9.1% | 10.7% | 적정 |
| 중앙 | 0.0% | 1.4% | — |

강우는 저confidence 프레임이 71개로 가장 많지만(전체의 50.7%) **주행 실패 기여는 18%뿐**이다
(64-21의 2축 발견: 강우는 검출 어려움/주행 쉬움). 반면 **강좌+약좌가 실패의 73%를 차지**하는데
confidence 가중으로는 37%밖에 못 받는다.

→ **샘플링 기준: `confidence 낮음` × `위치별 실패 기여도`**. 실무적으로는 강좌·약좌를
2배 가중하고 강우를 0.35배로 낮춘다. 우선 area 구간(0.05~0.09)은 유지하되 위치 가중을 곱한다.

**⚠️ 분할 규칙**: V6의 `SPLIT_SEED=42 / VAL_RATIO=0.15`를 **그대로 재사용**한다.
액션 헤드와 같은 val 에피소드를 써야 나중에 end-to-end 비교가 오염되지 않는다.
실기 176세션은 **학습에 절대 쓰지 않고 평가 전용**으로 둔다(테스트셋 부재 문제 재발 방지).

---

## 4. 아키텍처 후보 — 사용자 결정 필요

우리 문제는 **단일 클래스 · 단일 인스턴스 · 출력이 (cx, cy, area)** 라 일반 검출기보다
단순하다. 세 가지 후보:

### (A) Heatmap 회귀 (CenterNet 축소판) — **추천**
- 백본: MobileNetV3-Small 또는 이미 로드 중인 Kosmos-2 비전 피처 재사용
- 출력: 중심 heatmap 1채널 + size 2채널. argmax → cx,cy / size → area
- `has_bbox` = heatmap peak 값에 threshold (기존 confidence와 동일 의미 유지)
- 장점: 구조 단순, 단일 인스턴스에 최적, NMS 불필요, 파라미터 ~2-5M
- 단점: 다중 인스턴스 불가(우리 문제엔 무관)

### (B) YOLO 계열 (YOLOv8n 등)
- 장점: 성숙한 학습 레시피, 양자화/엣지 배포 도구 풍부
- 단점: 다중 클래스·NMS 등 불필요한 기계장치, 외부 의존성 추가

### (C) Kosmos-2 비전 피처 위의 경량 헤드
- 이미 매 프레임 Kosmos-2 비전(53.7ms)을 돌리고 있으므로 **검출기 추가 비용이 거의 0**
- 장점: 최소 추가 연산, 파이프라인 단순화(모델 1개 제거)
- 단점: 224×224 mean-pool 피처로는 작은 객체 위치 해상도가 부족할 수 있음
  → patch-level 피처(257토큰)를 써야 함. 실현 가능성 검증 필요

> **결정 필요**: (A)/(B)/(C) 중 어느 방향으로 갈지. 저는 **(A)를 기본, (C)를 병행 실험**으로
> 제안합니다 — (C)가 되면 경량화 효과가 압도적(모델 하나가 통째로 사라짐)이라
> 먼저 실현 가능성만 싸게 확인해볼 가치가 있습니다.

---

## 5. 단계

| 단계 | 내용 | 산출물 | 되돌리기 |
|---|---|---|---|
| **0** | 라벨 품질 검증 — `confidence=1.0`이 실측인지 placeholder인지 확인, 소형 area 구간 라벨을 사람이 육안 표본 검사(기존 labeler 재사용) | 검증 리포트 | 쉬움 |
| **1** | 데이터셋 구축 — V6 16,599 프레임 → (이미지, cx,cy,area) 페어. 난이도 가중 샘플링 | `detector_dataset_v1.pt` | 쉬움 |
| **2** | (C) 실현가능성 — Kosmos-2 patch 피처로 heatmap 회귀가 되는지 1일 실험 | go/no-go 판단 | 쉬움 |
| **3** | (A) 학습 — 3 seed, val은 V6 seed42 분할 | 체크포인트 | 중간 |
| **4** | 오프라인 평가 — **실기 176세션 프레임**에서 threshold 0.20 등가 검출률을 OWL-v2와 비교. 특히 area 0.05~0.09 구간 | 비교표 + 그림 | 쉬움 |
| **5** | 지연·메모리 실측 — 로컬 + soda 젯슨 | 실측표 | 쉬움 |
| **6** | soda 실기 A/B — 동일 위치에서 검출기만 교체 | 실기 성공률 | **비쌈** |

**중단 기준**: 단계 4에서 area 0.05~0.09 구간 검출률이 OWL-v2를 **넘지 못하면 중단**하고
아키텍처를 재검토한다. 지연이 아무리 빨라도 검출률이 낮으면 fp16과 같은 잘못된 트레이드다(64-16).

---

## 6. 평가 프로토콜 (미리 고정 — 사후 변경 금지)

- **주 지표**: 실기 176세션 프레임에서 has_bbox 재현율 (OWL-v2 fp32 대조군)
- **구간별 필수 보고**: area <0.05 / 0.05~0.09 / ≥0.09 — 전체 평균만 보고하지 않는다
- **위치별 필수 보고**: 강우 포함 5위치 — 강우가 최난이도임이 확정됨(64-21)
- **오탐 측정**: 타겟 부재 프레임에서의 오검출률. `owlv2_threshold_roc.py`의
  296프레임 라벨셋(객체없음 79장)을 그대로 재사용
- **동률 판정**: 검출률이 동등하면 지연이 빠른 쪽 채택

---

## 7. 리스크

| 리스크 | 대응 |
|---|---|
| 교사 라벨(V6 주석)이 사실은 부정확 | Step 0에서 육안 검증. 부정확하면 사람 라벨링으로 전환(비용 증가) |
| 조작 데이터(V6)와 실기 분포가 다름 | 실기 176세션을 평가 전용으로 분리해 과적합 조기 발견 |
| 단일 클래스 특화로 조명/배경 변화에 취약 | 실기 세션이 여러 날·여러 장소라 자연 증강. 부족하면 augmentation |
| 검출률은 오르는데 실기가 안 오름 | 64-21의 강우 예외처럼 정책 prior가 개입. 단계 6 A/B로만 확정 가능 |

---

## 8. 결정이 필요한 항목

1. **아키텍처**: (A) heatmap 단독 / (A)+(C) 병행 / (B) YOLO
2. **Step 0 육안 검증 범위**: 표본 몇 장을 볼지 (제안: area<0.05 구간 100장)
3. **단계 6 실기 A/B를 이번 범위에 넣을지** — soda 일정과 맞물림

> 승인 전까지 구현 착수하지 않습니다.
