--- tags: - robotics - image-classification - openarm-sciedu --- # leaf_kind_dinov2_logreg OpenArm SciEdu 나뭇잎 분류 실험에서, 줄 위에 선 잎 카드가 복엽(compound)인지 단엽(simple)인지 판정한다. 카드가 없거나 가려졌으면 none이다(2026-10-04 둘째 판, 셋째 부류 추가). 로봇 PC 대화층의 판정기 자리에 쓴다. 판정 결과에 규칙을 적용해 왼쪽/오른쪽을 정하고, 그 쪽 문장을 pi0.5(`leapshared/SciEdu_pauses_3exp_210epi_leafdir_pi05`)에 보낸다. ## 구성 - **특징:** `facebook/dinov2-base`의 `pooler_output`(최종 layernorm 뒤 CLS 토큰, 768) - **분류기:** 표준화(mean, std) 뒤 3부류 소프트맥스 회귀(W 3×768, b 3). 부류는 `classes` = [compound, simple, none]이다. `classifier.json`에 숫자 3,843개가 있다. - **전처리:** `AutoImageProcessor(facebook/dinov2-base)`. 짧은 변 256으로 bicubic 줄이기, 가운데 224 자르기, /255, ImageNet mean/std, RGB - **입력:** `follower_d455f` 640×480 RGB 프레임에서 상자 (x0, y0, x1, y1) = (150, 100, 490, 440)을 자른다. - **찍는 때:** 카드가 줄 위에 혼자 서 있고 두 팔이 쉬고 있을 때. 학습 사진은 카드를 옮긴 팔이 출발하기 5프레임 전이다. ## 쓰는 법 ```python from leaf_kind import LeafKind clf = LeafKind() # CPU. classifier.json과 dinov2-base를 받는다 r = clf.judge([f1, f2, f3]) # 같은 때 찍은 프레임 몇 장 (H x W x 3 RGB uint8) # r["kind"]: "compound" | "simple" → 옮긴다 # "none" → 카드가 없거나 가려졌다(한 프레임이라도 none이면). 판정하지 않고 1초 뒤 다시 본다 # None → 카드는 있는데 프레임 답이 갈렸거나 평균 확신이 0.9보다 낮다. 선생님에게 묻는다 # r["probs"]: 프레임 평균 {compound, simple, none}, r["votes"]: 프레임별 판정 ``` ## 검증 (2026-10-04, H200) - 카드 사진: `Leaf_rules_60epi_labeled` 0~59번(합본 나뭇잎 0~59번) - 로봇 PC probe 142장 + H200이 그리퍼로 찾은 314장 - H200 검출에서 카드가 가려졌거나 놓이기 전인 20장(눈으로 확인)은 뺐다. - 정답은 에피소드 규칙(복엽 왼쪽 / 복엽 오른쪽) × 카드를 옮긴 팔이다. - none 사진 795장 - A 에피소드 끝 빈 줄 120 - B 그리퍼가 카드를 쥔 직후(가림) 338 - C 쥔 뒤 1.5초(카드를 옮기는 중이거나 빈 줄) 337. 사람 손이 카드를 놓는 1장은 뺐다. - 학습은 부류 균형 가중치로 했다. 검증 에피소드 6·7·46·47을 빼고 학습해 그 에피소드로 잰 결과(행: 정답, 열: 판정): | 정답 \ 판정 | compound | simple | none | |---|---|---|---| | compound | 21 | 0 | 0 | | simple | 0 | 22 | 0 | | none | 0 | 0 | 52 | 에피소드 단위 10겹(1,251장): | 정답 \ 판정 | compound | simple | none | |---|---|---|---| | compound | 238 | 0 | 1 | | simple | 0 | 216 | 1 | | none | 0 | 0 | 795 | - none을 카드로 본 것(없는 카드를 집으러 가는 실수)은 0이다. - 카드를 none으로 본 2장은 1초 뒤 다시 보는 쪽 실패다. - 처음 보는 카드 디자인에서의 복엽/단엽 정확도는 첫 판(2부류)으로 잰 대략값이 92~94%다. - 학습 사진은 AV1로 녹화한 영상을 풀어 쓴 것이다. 실기 카메라 프레임에서도 몇 장으로 확인하기를 권한다. ## 한계 - none은 녹화에 있던 장면(빈 줄, 그리퍼 가림, 카드를 옮기는 중)으로만 배웠다. 사람 손이 카드를 놓는 중인 사진은 따로 배우지 않았다. - 같은 때 찍은 프레임 중 하나라도 none이면 none을 낸다. 없는 카드를 집으러 가지 않는 쪽으로 정했다. - 왼쪽/오른쪽은 이 판정기가 아니라 대화층이 학생 규칙으로 정한다. ## CPU 속도 (H200 서버 CPU, x86_64 24코어. 로봇 PC에서는 다를 수 있다) | 스레드 | 1장 | 3장 한 번에 | |---|---|---| | 1 | 339 ms | 967 ms | | 4 | 97 ms | 285 ms | | 8 | 58 ms | 152 ms | 둘째 판 `judge` 3장은 CPU 8스레드로 181 ms였다(소프트맥스는 무시할 만큼 가볍다). ## 파일 - `classifier.json`: 숫자(mean, std, W, b), 부류, 자르기 상자, 전처리 설명, 검증 결과(혼동 행렬) - `leaf_kind.py`: 판정기(`LeafKind`) - 학습·평가 코드와 결과(H200): `scripts/leaf_kind/`, `logs/leaf_kind_local/`