엽채류 이미지에서 작물 종류와 생육단계를 판별하는 딥러닝 모델을 연구·개발하는 프로젝트입니다. 이 문서 하나로 설치부터 첫 실험까지 끝납니다.
| 항목 | 내용 |
|---|---|
| 목적 | 엽채류 이미지에서 작물 종류와 생육단계를 판별하는 모델을 연구·개발 |
| 대상 작물 | 상추 · 케일 · 겨자채 · 근대 (4종) |
| 생육단계 | 정식기 · 생육기 · 수확기 (3단계) |
| 문제 유형 | 이미지 분류 — 백본 공유 멀티헤드 (작물 4 + 단계 3) |
| 프레임워크 | PyTorch + torchvision |
| 산출물 | 학습된 모델 · 연구 기록 · 시각 자료 · 모델 번들 |
AI 허브 — 수직농장 통합데이터(엽채류)
| 항목 | 내용 |
|---|---|
| 형식 | 이미지 tar + 어노테이션 JSON tar (이미지 1장당 JSON 1개) |
| 구성 | {작물}/{training|validation}/{labeled|source}/*.tar |
| 규모 | 137.59 GB — 아카이브 1개당 177~500장 |
| 라벨 | JSON 의 crops(작물)·growth_stage(생육단계) — 별도 라벨링 불필요 |
원본 데이터는 저장소에 커밋하지 않습니다. 용량이 크고 재배포 조건이 있으므로, AI 허브에서 각자 직접 내려받습니다 (
03_팀_협업_규약.md§4). 내려받은 tar 를 어디에 두는지는 §4.1, 학습용 인덱스로 바꾸는 방법은 §4.2 에 있습니다.
데이터 구조·JSON 스키마·실측으로 확인한 함정 10선은 04_데이터_명세서.md 에 있습니다.
산출물은 "정확한 모델 하나"가 아니라 연구 과정 전체입니다.
| 축 | 목표 |
|---|---|
| A. 재사용 가능한 뼈대 | 과제가 바뀌어도 살아남는 학습 하네스. 모델 교체가 config 한 줄 |
| B. 연구 기록 | 어떤 모델·설정을 만져 정확도를 어떻게 올렸는지가 남습니다 |
| C. 시각 자료 | 연구 과정에서 보여줄 수 있는 그림이 자동 축적됩니다 |
핵심 설계를 한 문장으로:
run 이 끝날 때
report.md와figures/가 자동으로 생기고, 모델은 config 한 줄로 바뀝니다.
lab_gui.py (Tkinter 콘솔)
│ 화면 값을 config.json 으로 저장하고 별도 프로세스를 띄웁니다
▼
train_worker.py ──> runs/exp_.../ ──> make_figures.py · make_report.py
학습 결과 파일 그림 9종 · report.md
콘솔은 학습 로직을 소유하지 않습니다. 값을 config.json 으로 쓰고 프로세스를 띄운 뒤
결과 파일을 읽어 보여줄 뿐이므로, 콘솔 없이 터미널에서 같은 실험이 그대로 재현됩니다.
여러 모델을 같은 조건에서 비교할 때는 run_study.py 가 순차로 돌리고 STUDY.md 에
비교표를 남깁니다. 콘솔의 Study 실행 모드가 이것을 그대로 씁니다.
실험을 시작하기 전에 알아야 합니다. 특히 N2 를 놓치면 그 뒤의 모든 실험이 무의미해집니다.
| # | 함정 | 내용 |
|---|---|---|
| N1 | 클래스 불균형 | 수확기가 6% 수준 — 아카이브마다 편차가 극심합니다 |
| N2 | 개체 단위 시계열 | 한 개체를 한 달간 27~40장 연속 촬영 → 무작위로 나누면 정확도 99% 같은 헛된 수치가 나옵니다 |
| N3 | 아카이브별 단계 편중 | 한 아카이브에 한 단계만 있는 경우가 실제로 존재합니다 |
| N4 | 생육정지 현상 | 정식기와 생육기의 외형이 겹쳐 이미지만으로는 원리적으로 구분 불가한 구간이 있습니다 |
그래서 정확도(accuracy)를 주 지표로 쓰지 않습니다. 생육기가 다수라 전부 생육기로 찍어도 높은 값이 나옵니다. 주 지표는 생육단계 macro-F1 과 정식기 recall 입니다.
| 문서 | 언제 읽나 |
|---|---|
| README.md (이 문서) | 처음 — 설치·첫 실행·사용법 |
01_기획서.md |
왜 이렇게 하는가 — 목표·모델 전략·평가 지표·연구 계획 |
02_구조도.md |
코드가 어떻게 짜여 있는가 — 계층·레지스트리·산출물 규약 |
03_팀_협업_규약.md |
실험을 시작하기 전에 — run ID·분담·공유 규칙 |
04_데이터_명세서.md |
데이터가 이상할 때 — 구조·JSON 스키마·분할 정책·함정 10선 |
git clone https://github.com/PEANUTBUTTER1001/leafscan.git
cd leafscan
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python scripts/check_env.py
python lab_gui.pyPyTorch 는 GPU 환경에 따라 설치 명령이 다릅니다. §2단계 3 을 반드시 확인하세요.
§1 → §2 → §3 까지 하면 학습이 한 번 돌아갑니다. 나머지는 필요할 때 찾아보면 됩니다.
| § | 내용 |
|---|---|
| 1 | 사전 준비 — 무엇이 필요한가 |
| 2 | 설치 — 5단계 |
| 3 | 첫 실행 — 5분 안에 학습 한 번 |
| 4 | 실제 데이터 연결하기 |
| 5 | 콘솔 사용법 |
| 6 | 실험 한 사이클 |
| 7 | 결과 파일 읽는 법 |
| 8 | 자주 발생하는 문제 |
| 9 | 하지 말아야 할 것 |
| 10 | 도움 받기 |
| 항목 | 권장 | 최소 | 확인 방법 |
|---|---|---|---|
| OS | Windows 10/11 | macOS·Linux도 가능 | — |
| Python | 3.11 | 3.10 ~ 3.12 | python --version |
| Git | 최신 | — | git --version |
| GPU | NVIDIA (VRAM 6GB↑) | 없어도 됨 (CPU 학습 가능, 느림) | nvidia-smi |
| RAM | 16GB | 8GB | — |
Microsoft Store 버전 Python은 쓰지 말 것. Tkinter(콘솔 GUI)가 빠져 있어 lab_gui.py가 실행되지 않습니다.
python.org 공식 설치본을 쓰고, 설치 화면에서 아래 두 개를 반드시 체크합니다.
- ☑ Add python.exe to PATH
- ☑ tcl/tk and IDLE (Tkinter 포함)
저장소를 받을 위치로 이동합니다.
git clone https://github.com/PEANUTBUTTER1001/leafscan.git
cd leafscanpython -m venv .venv활성화:
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# Windows (cmd)
.venv\Scripts\activate.bat
# macOS / Linux
source .venv/bin/activate프롬프트 앞에 (.venv)가 붙으면 성공입니다.
PowerShell에서 "스크립트를 실행할 수 없습니다" 오류가 나면
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned를 한 번 실행하고 다시 시도합니다.
먼저 내 GPU가 어떤 CUDA를 지원하는지 확인합니다.
nvidia-smi출력 오른쪽 위의 CUDA Version: 12.x 를 봅니다. (이건 드라이버가 지원하는 최대 버전이며, 이보다 낮은 CUDA 빌드는 설치해도 동작합니다.)
그 다음 pytorch.org 에서 자기 환경에 맞는 명령을 복사해 실행합니다. 예시:
# CUDA 12.1 계열 GPU
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
# GPU 없음 (CPU 전용)
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
⚠️ 그냥pip install torch하면 안 되나? 됩니다. 다만 환경에 따라 CPU 전용 버전이 설치될 수 있습니다. 그러면 GPU가 있어도 안 쓰고 학습이 10배 느려지는데, 에러가 안 나서 눈치채기 어렵습니다. 반드시 아래 2단계 5의 확인을 거치세요.
pip install -r requirements.txt포함되는 것: numpy, scikit-learn, matplotlib, Pillow
(torch·torchvision 은 2단계 3 에서 이미 설치했습니다. mlflow·wandb 는 선택 — 없어도 학습은 정상 완주합니다.)
python scripts/check_env.py조용히 잘못될 수 있는 것들을 잡는 단계입니다. CPU 빌드가 깔렸거나 Tkinter 가 빠졌거나 한글 폰트가 없어도 에러 없이 진행되다가 나중에 문제가 드러납니다.
정상 출력 예:
[OK] Python 3.12.10
[OK] torch 2.13.0+cu132
[OK] torchvision 0.28.0+cu132
[OK] CUDA 사용 가능 — NVIDIA GeForce GTX 1650 (4.0GB)
[OK] Tkinter 사용 가능 (Tk 8.6)
[OK] matplotlib 한글 폰트 — Malgun Gothic
[OK] 필수 패키지 6종 확인
→ 준비 완료. `python lab_gui.py` 를 실행하세요.
문제가 있으면 [!!] 와 함께 해결 방법이 나옵니다.
[!!] CUDA 사용 불가 — CPU로 학습됩니다
torch 가 CPU 빌드입니다 (2.4.1+cpu).
GPU 를 쓰려면 README.md §2단계 3 을 다시 하세요.
[!!] Tkinter 없음 — 콘솔 GUI를 실행할 수 없습니다
Microsoft Store 버전 Python일 가능성이 높습니다. README.md §1.2 참조.
학습 자체는 train_worker.py 로 계속 쓸 수 있습니다.
| 구분 | 어떤 항목 | 종료 코드 |
|---|---|---|
| 문제 — 고쳐야 진행 가능 | torch·torchvision 없음, 필수 패키지 누락 | 1 |
| 경고 — 학습은 가능 | GPU 없음(CPU 학습), 한글 폰트 없음, Python 버전 범위 밖 | 0 |
GPU 가 없어도 됩니다. CUDA 항목의 [!!] 는 "GPU 를 못 쓴다"는 알림이지 실패가 아닙니다.
python lab_gui.py실행 모드가단일 실행인지 확인 (맨 위 · 기본값)① 데이터의 데이터셋을fake로 선택 — 인터넷·데이터 파일 없이 돌아갑니다② 모델 구조의 arch 를simple_cnn으로 선택 — 가장 빨리 끝납니다③ 학습 설정의 Epoch 슬라이더를1로 내립니다④ 실행의 가설 입력란에 아무거나 적기 (예:설치 확인)- ▶ 학습 실행 클릭
무슨 일이 일어나는가:
왼쪽 값 → runs/exp_.../config.json 으로 저장
→ train_worker.py 를 별도 프로세스로 실행
→ epoch마다 로그가 실시간으로 흐름
→ 끝나면 지표 카드·혼동행렬·손실곡선이 채워짐
→ figures/ 에 그림이 생기고 report.md 가 만들어짐
여기까지 되면 설치는 끝입니다.
콘솔은 편의 도구일 뿐입니다. 같은 실험이 터미널에서 그대로 재현됩니다.
python train_worker.py --config runs/exp_260812_ab_01/config.json --out runs/exp_260812_ab_01이게 이 프로젝트의 핵심 설계입니다.
config.json이 실험의 유일한 원본이고, GUI는 그 파일을 만드는 편집기입니다.
매번 터미널을 열고 python lab_gui.py를 치는 게 번거롭다면, 바탕화면 바로가기를 만들 수 있습니다.
저장소 폴더에서 바탕화면_아이콘_만들기.bat 을 더블클릭합니다.
정상 출력 예:
바탕화면에 LeafScan Lab 아이콘을 만듭니다...
[완료] 바탕화면에 아이콘을 만들었습니다.
사용한 파이썬 : C:\Users\user\AppData\Local\Programs\Python\Python312\pythonw.exe
바로가기 위치 : C:\Users\user\Desktop\LeafScan Lab.lnk
작업 표시줄에 고정하려면 아이콘 우클릭 > '작업 표시줄에 고정' 을 선택하세요.
계속하려면 아무 키나 누르십시오 . . .
바탕화면에 LeafScan Lab 아이콘(leafscan.ico)이 생기고, 더블클릭하면 검은 콘솔 창 없이 콘솔 GUI가 바로 뜹니다. 작업 표시줄에 두려면 아이콘 우클릭 → 작업 표시줄에 고정.
⚠️ 가상환경(.venv)을 쓴다면 주의. 위.bat은 시스템 PATH의 파이썬을 잡기 때문에, 패키지를.venv에만 설치한 경우 아이콘을 눌러도ModuleNotFoundError가 납니다. 해결 —.venv를 활성화한 PowerShell에서 스크립트를 직접 실행하면 그 가상환경의 파이썬으로 바로가기가 만들어집니다..venv\Scripts\Activate.ps1 .\make_shortcut.ps1출력의
사용한 파이썬경로에.venv가 들어 있으면 제대로 된 것입니다.
아이콘 없이도 됩니다. 저장소 안의 LeafScan Lab.bat 을 더블클릭해도 같은 방식으로 콘솔이 뜹니다. 바로가기는 이 .bat이 하는 일을 바탕화면으로 꺼내 놓은 것뿐입니다.
바로가기를 지우고 싶으면 바탕화면 아이콘만 삭제하면 됩니다. 저장소에는 아무 영향이 없습니다.
§3 의 더미 데이터로 학습이 한 번 돌았다면, 다음은 실제 데이터를 붙이는 것입니다. 여기까지가 준비이고, 콘솔 화면을 자세히 뜯어보는 것은 §5 에 있습니다. 화면 구성이 궁금하면 §5 를 먼저 읽어도 됩니다.
데이터는 저장소에 커밋하지 않습니다 (.gitignore 대상). 저장소를 새로 clone하면
data/ 자체가 없으므로, 먼저 뼈대 폴더부터 만듭니다.
python scripts/setup_data_dirs.py작물 4종 × {training,validation} × {labeled,source} = 16개 폴더(+_extracted/)가
한 번에 생성됩니다. 그 다음 공유 위치에서 받은 tar를 정해진 위치에 그대로 넣습니다.
(받는 위치는 팀 채널 참조)
원본은 tar 아카이브이며 작물별 폴더로 나뉩니다.
data/
├─ lettuce/ # 상추
│ ├─ training/
│ │ ├─ labeled/ TL_3.상추32.tar ← 어노테이션 JSON
│ │ └─ source/ TS_3.상추32.tar ← 이미지 JPG
│ └─ validation/
│ ├─ labeled/ VL_3.상추10.tar
│ └─ source/ VS_3.상추10.tar
├─ chard/ # 근대
├─ kale/ # 케일
└─ leaf_mustard/ # 겨자채
⚠️ labeled와source의 끝 번호가 일치하는지 확인 합니다.
python scripts/prepare_dataset.py --data-root data --out data/index.csv동작 설명:
① 아카이브 짝 검증 labeled 번호 == source 번호인지
② 추출 data/_extracted/ 로 (이미 있으면 건너뜀)
③ JSON 파싱·조인 파일명 stem 기준
④ 품질 검사 짝 없는 파일·손상 이미지 제외
⑤ 산출 index.csv · labels.json · MANIFEST.md
정상 출력 예:
[OK] lettuce/validation VL_3.상추10 ↔ VS_3.상추10 177/177 일치
[!!] lettuce/training TL_3.상추32 ↔ TS_3.상추42 교집합 0건
→ TS_3.상추32.tar 를 받아 주세요. 번호가 같아야 합니다.
원본 데이터에 짝이 깨진 아카이브가 섞여 있으면 인제스트가 중단 되며 아래와 같은 메시지가 나옵니다.
[중단] 짝이 맞지 않는 아카이브가 있습니다. 위 항목을 해결하거나
--skip-broken 으로 해당 짝만 건너뛰고 진행하세요.
이때 건너뛰고 진행하고 싶으면 아래 명령을 실행합니다.
python scripts/prepare_dataset.py --data-root data --out data/index.csv --skip-broken깨진 짝만 빼고 나머지로 index.csv 를 만듭니다. 몇 개가 빠졌는지 화면에 나옵니다.
[진행] --skip-broken: 깨진 짝 32개를 제외하고 계속합니다.
| 선택 | 언제 |
|---|---|
--skip-broken 으로 진행 |
지금 당장 학습을 돌려봐야 할 때. 대부분 여기에 해당합니다 |
| tar 를 다시 받아 해결 | 제외된 양이 많아 학습 데이터가 부족할 때 |
빠진 데이터는 그냥 없는 셈이 됩니다. 학습이 틀어지지는 않지만 쓸 수 있는 장수가 줄어듭니다. 몇 쌍이 빠졌는지는 기억해 두고, 결과를 해석할 때 감안합니다. 제외 내역은
data/MANIFEST.md에 남으므로 나중에 확인할 수 있습니다.
디스크 — tar와 추출본을 함께 보관하므로 원본 용량의 약 2배가 필요합니다. 부족하면 작물별로 나눠 진행합니다.
콘솔 ① 데이터 에서 데이터셋을 index_csv 로 바꾸고, 그 아래 경로를 data/index.csv 로
지정합니다. 장수·클래스 구성은 학습을 시작하면 ⑧ 로그 영역 첫머리에 이렇게 찍힙니다.
분할 정책=resplit_all · 그룹중복 없음(0건) · train/val/test 그룹=85/19/19
train: 16895장 · 단계비율 {'정식기': 0.274, '생육기': 0.663, '수확기': 0.063}
val: 3377장 · 단계비율 {'정식기': 0.278, '생육기': 0.66, '수확기': 0.062}
그룹중복 없음(0건) 이 아니면 즉시 중단합니다. 데이터 누수입니다(§4.4 아래 경고 참조).
학습을 걸기 전에 미리 규모와 분포를 보려면 §4.4 를 봅니다.
index.csv는 13개 컬럼이며 상세는 04_데이터_명세서.md §6.5. 학습에서 중요한 것은 세 가지입니다.
| 컬럼 | 의미 |
|---|---|
crop / stage |
라벨 2종 |
group_id |
개체 ID. 같은 개체가 train/val에 섞이는 것을 막습니다 |
split_source |
원본이 training인지 validation인지 |
인제스트가 끝나면 학습을 걸기 전에 데이터가 어떻게 생겼는지부터 봅니다.
방법은 두 가지이고, 같은 index.csv 를 읽으므로 결과는 같습니다.
GUI 콘솔 ① 데이터 하단의 분포 보기 (학습 전 점검) 버튼을 누릅니다.
데이터셋이 index_csv 여야 하며, 학습 결과와 무관하게 동작하므로 학습 중에도 열립니다.
작물×단계 교차표까지 한 화면에 나옵니다.
규모 : 23,633장 · 개체 123개 · 아카이브 53개
개체당 : 최소 1장 · 중앙 208장 · 최대 504장
[작물 × 단계]
작물 생육기 정식기 수확기
---------------------------------
상추 15,665 6,490 1,478
단계 비율 : 생육기 66.3% 정식기 27.5% 수확기 6.3%
[split_source × 단계] (분할 정책 결정 근거)
원본 생육기 정식기 수확기
---------------------------------------
training 13,550 5,588 1,300
validation 2,115 902 178
[!] 확인할 것 1건
- 작물이 '상추' 하나뿐입니다 — 작물 분류는 고를 것이 하나라 정확도가 항상 100% 가 됩니다.
생육단계 지표만 의미가 있습니다.
무엇을 보는가
| 줄 | 판단 |
|---|---|
규모 |
개체(group_id) 수가 충분한가. 개체가 적으면 분할 자체가 불안정합니다 |
개체당 |
편차가 큰가. 1장짜리 개체는 한쪽 split 에만 들어가 층화를 흔듭니다 |
[작물 × 단계] |
특정 조합이 극단적으로 적은가. 그 조합은 학습도 평가도 안 됩니다 |
단계 비율 |
불균형 정도. 위 예시는 수확기가 6.3% 라 macro-F1 이 이 클래스에 좌우됩니다 |
[split_source × 단계] |
제공된 분할을 쓸지(respect_provided) 다시 나눌지(resplit_all) 정하는 근거 |
맨 아래 [!] 확인할 것 은 아래 규칙에 걸린 사실만 알립니다. 어떻게 할지는 사람이 정합니다.
| 알림 | 조건 |
|---|---|
| 개체 수 부족 | group_id 가 30개 미만 — 그룹 분할이 불안정 |
| 작물이 하나뿐 | 작물 분류의 선택지가 하나 — 정확도가 항상 100% |
| 희소 단계 | 특정 단계가 전체의 5% 미만 — macro-F1 이 이 클래스에 좌우됨 |
| 희소 조합 | 작물×단계 조합이 100장 미만 — 평가가 불안정 |
python scripts/check_data.py규모·개체·작물·단계 합계와 데이터 해시가 나옵니다. 교차표는 없습니다.
index : data\index.csv
hash : sha256:afd2f4ae...
규모 : 23633장 · 개체 123개
작물 : {'상추': 23633}
단계 : {'정식기': 6490, '생육기': 15665, '수확기': 1478}
이쪽을 쓰는 이유는 hash 줄 하나입니다. 팀원과 이 값이 다르면 서로 다른 데이터로
실험한 것이라 결과를 나란히 놓고 비교할 수 없습니다 (03_팀_협업_규약.md).
방법 1 과 같은 표를 터미널에서 보려면 이렇게도 됩니다.
python -c "from core.data_summary import *; print(format_summary(summarize_index('data/index.csv')))"그림으로 된 분포는 학습 후에 나옵니다.
figures/data_distribution.png는 run 이 끝날 때metrics.json의 분할 정보를 읽어 생성되므로 학습 전에는 볼 수 없습니다. 학습 전 점검은 위의 방법 1·2 로 합니다.
이 데이터에서 가장 조심할 것은 데이터 누수입니다. 한 개체를 약 한 달간 27~40장 연속 촬영해 이미지들이 매우 유사합니다. 무작위로 나누면 정확도 99% 같은 헛된 수치가 나옵니다. 분할은 반드시
group_id기준으로 이뤄지며, 콘솔이 분할 후 그룹 중복 건수를 표시합니다. 0이 아니면 무언가 잘못된 것입니다.
좌측은 손(조작), 우측은 눈(결과).
┌──────────────────────────────────────────────────┐
│ 전역 바 : 기준 run 선택 · 실행 상태 │
├─────────────┬────────────────────────────────────┤
│ ① 데이터 │ ⑤ 지표 카드 4종 (기준 대비 델타) │
│ ② 모델 │ ⑥ 혼동행렬 (head 탭 · 셀 클릭) │
│ ③ 학습 │ ⑦ 오분류 샘플 그리드 │
│ ④ 가설·실행 │ ⑧ 손실 곡선 · 로그 │
│ │ ⑨ 결론 입력 │
└─────────────┴────────────────────────────────────┘
| 조작 | 설명 |
|---|---|
| 실행 모드 | 사이드바 맨 위. 단일 실행(기본) / Study 실행(여러 모델 순차 비교) |
| 분포 보기 | ① 데이터 맨 아래. 학습 전 데이터 점검 (§4.4) — 실행 중에도 열립니다 |
| 아키텍처 | 백본 모델 선택. 이것만 바꿔도 완전히 다른 실험 |
| 입력 해상도 | 224 기본. 낮추면 빠르지만 정확도가 떨어질 수 있음 |
| 빠른 학습 | 입력 해상도 옆 체크박스. 체크 시 원본을 256px 축소 캐시로 빠르게 학습 ↔ 해제(기본) 시 원본 그대로. 아래 상세 |
| pretrained | ImageNet 사전학습 가중치 사용. 거의 항상 켜는 게 유리 |
| Learning rate | 로그 슬라이더 — 극단값이 들어갈 수 없음 |
| freeze epoch | 초반 N epoch 동안 백본을 고정. 전이학습 2단계 |
| head 가중치 | crop과 stage 중 어디에 비중을 둘지 |
| class weight | 소수 클래스(정식기)를 얼마나 챙길지 |
| 기준 run | 선택하면 모든 지표에 델타가 붙음 |
| wandb 보기 | 우측 상단 버튼. 보고 있는 run → 실행 중 run → 프로젝트 대시보드 순으로 wandb 웹페이지를 엶. 아래 상세 |
| threshold 슬라이더 | 즉시 반응. 재학습 없이 판정 기준만 바꿔봄 |
| 혼동행렬 셀 | 클릭하면 그 조합의 실제 오분류 이미지가 뜸 |
학습이 wandb 서버에 실시간으로 기록되고, 우측 상단 "wandb 보기" 버튼으로 바로 열어볼 수 있습니다.
| 기록 시점 | 내용 |
|---|---|
| epoch 마다 | loss · val_loss · acc · val_acc + head 별 정확도 (val_acc_crop, val_acc_stage) |
| 학습 종료 | stage macro-F1 · 정식기 recall · crop accuracy 등 최종 요약 + metrics.json 첨부 |
| Study 실행 | study 이름으로 run 들이 그룹으로 묶여 모델 비교가 한 화면에 |
셋업 (PC 당 최초 1회):
pip install wandb
wandb login # https://wandb.ai/authorize 의 API 키 입력 (화면에 안 보이는 게 정상)알아둘 것:
- 미설치·미로그인이어도 학습은 정상 완주합니다. 미로그인이면 로컬
wandb/폴더에 offline 기록되고, 나중에wandb login후wandb sync wandb/offline-run-...으로 올릴 수 있습니다. - 켜고 끄기:
③ 학습 설정 › 고급 › "wandb 실험 추적"체크박스 (기본 켜짐). - 구현 상세와 문제 해결은
05_wandb_연동.md참조.
② 모델 구조 › 입력 해상도 옆의 빠른 학습 체크박스로 이미지 준비 방식을 고릅니다.
| 상태 | 동작 | 언제 쓰나 |
|---|---|---|
| ✔ 체크 | 원본(915×1060급)을 최초 epoch 에 256px 로 축소해 캐시(data/_cache/)하고 이후 재사용 |
빠른 반복 실험. 원본 JPEG 디코딩이 epoch 시간의 병목이라 훨씬 빠름 |
| ☐ 해제 (기본) | 캐시 없이 원본 이미지를 매번 그대로 디코딩해 학습 — 기존 방식 그대로 | 평상시·최종 검증 run (품질 우선) |
알아둘 것:
- 어느 쪽이든 모델에 들어가는 최종 입력은 위의 입력 해상도(기본 224) 설정을 따릅니다. 차이는 그 해상도로 줄이기 전의 출발 화질(256px 캐시 vs 원본)뿐입니다.
- 체크 상태의 첫 실행은 캐시 생성 때문에 최초 epoch 이 느립니다. 두 번째 epoch 부터 빨라집니다.
- 캐시는
data/_cache/폴더에만 생기고 원본 파일은 절대 건드리지 않습니다. - 두 모드는 픽셀 값이 미세하게 달라 지표도 미세하게 다를 수 있습니다. 비교 실험(Study) 안에서는 한 모드로 통일하세요.
실행 모드 를 Study 실행 으로 바꾸면 여러 모델을 같은 조건에서 순서대로 학습합니다.
| 항목 | 설명 |
|---|---|
| Study 이름 | 번호가 자동으로 매겨집니다 (study_01 → study_02 …). 단일 실행의 exp_001 과 같은 방식입니다 |
| 주제 붙이기 | 번호 뒤에 붙이면 됩니다 — study_02_lr. 번호만 읽으므로 다음 번호에 영향이 없습니다 |
| 재개 | 같은 이름을 다시 입력하면 완료된 모델은 건너뛰고 남은 것만 실행합니다. Study 가 끝나면 입력칸이 다음 번호로 바뀌므로, 이어서 하려면 이전 이름을 직접 넣습니다 |
| 비교 모델 | 최소 2개. simple_cnn 은 회귀 픽스처라 목록에 없습니다 (§01_기획서.md §4.1) |
| 결과 | 모델마다 report.md·figures/ + 전체 STUDY.md (§7) |
| 등급 | 소요 | 대상 |
|---|---|---|
| 즉시 (<50ms) | 바로 | threshold, 혼동행렬 셀 선택, head 탭 |
| 단시간 (초~분) | 진행 표시 | 그림 재생성 |
| 장시간 (분~시간) | 진행바 + 중단 가능 | 학습 |
조작 전에 이 구분을 확인하면 몇 시간을 아낍니다.
validation 결과로 확정한 모델을 재학습하거나 다시 선택하지 않고, 기존 best.pt를
고정된 분리 test split에 한 번 평가할 수 있습니다. 최종 테스트는 모델 선택·Study
순위 결정에 사용하지 않습니다.
- 완료된
index_csvrun을 상단기준 run목록에서 선택합니다. Study 멤버는study_03/study_03__arch-resnet18처럼 표시됩니다. - 결과 화면의 FINAL TEST 버튼을 누릅니다.
이 run 보기를 먼저 누르지 않아도 선택한 run이 평가 대상으로 사용됩니다. - 기존 평가가 있으면 재평가 확인창에서 예를 선택합니다.
- 평가 중에는 스피너·경과시간·실시간 로그가 표시되며, ■ 테스트 중단으로 중단할 수 있습니다.
python evaluate_test.py --run runs/study_03/study_03__arch-resnet18재평가는 기존 결과를 덮어쓰지 않고 새 이력으로 저장합니다.
python evaluate_test.py `
--run runs/study_03/study_03__arch-resnet18 `
--reevaluate --reason "재현성 확인"대상 run은 dataset=index_csv, status=done, best.pt, config.json, 고정
split.json, 원본 index.csv를 모두 갖춰야 합니다. exp_###처럼 학습 당시
고정 split.json이 없는 예전 run은 데이터 누수를 막기 위해 평가를 중단합니다.
평가가 끝나면 run 루트의 기존 report.md에 ## 10. 최종 테스트 평가가 추가되고,
head별 지표가 표로 기록됩니다.
| Head | Accuracy | Macro-F1 | Weighted-F1 |
|---|---:|---:|---:|
| crop | 1.0 | 1.0 | 1.0 |
| stage | 0.8677 | 0.7984 | 0.8708 |평가별 원본과 테스트 전용 그림은 기존 validation 산출물과 분리해 보관합니다.
runs/<run>/test_evaluations/<evaluation_id>/
├─ test_metrics.json
├─ evaluation_metadata.json
├─ logits_*.npy · labels_*.npy
└─ figures/
├─ confusion_*.png
├─ f1_per_class_*.png
├─ calibration_*.png
└─ confidence_hist_*.png
best.pt, 기존 metrics.json, validation logits/labels·figures, split.json은
수정하거나 덮어쓰지 않습니다. 별도 evaluation 폴더에는 report.md를 만들지 않고,
run 루트의 report.md가 최신 평가를 요약합니다.
① 가설을 적는다 "class weight를 켜면 정식기 recall이 0.62 이상 될 것"
② 값을 조정한다 class weight: 없음 → 자동
③ 실행한다 결과가 나올 때까지 다른 일을 해도 된다
④ 자동 관찰을 읽는다 report.md §6 — 사람이 놓치기 쉬운 사실을 짚어줌
⑤ 원인을 추적한다 혼동행렬 진한 셀 클릭 → 오분류 이미지 확인
⑥ 결론을 적는다 "가설대로 올랐으나 오분류가 한 방향에 몰림. 라벨 문제로 의심"
⑦ 다음 실험을 정한다 report.md §8 후보 중 채택
3주 뒤에는 본인도 왜 그 실험을 했는지 기억하지 못합니다. 이 프로젝트의 산출물은 모델 하나가 아니라 연구 과정 전체이므로, 가설이 없는 run은 숫자 더미가 됩니다.
가설이 비어 있으면 실행 시 확인 대화상자가 뜹니다. 형식은 자유이고 한 줄이어도 됩니다.
| 좋은 예 | 나쁜 예 |
|---|---|
| "정식기가 10%뿐이라 학습 신호가 부족해 보임. class weight로 보정하면 recall이 오를 것" | "테스트" |
| "resnet50이 과적합되는 듯. augmentation을 강으로 올리면 val_loss 상승이 늦춰질 것" | "돌려봄" |
결론이 비면 index.csv와 RESEARCH_LOG.md에 ⚠️ 미작성으로 표시됩니다. 3~5줄이면 충분합니다.
runs/exp_260812_ab_01/
├─ report.md ← ★ 먼저 여기를 본다
├─ figures/ ← 그림 9장
├─ config.json 설정 (실험의 원본)
├─ diff.json 부모 run과 뭐가 달랐는지
├─ metrics.json 지표 원본 (기계용)
├─ train.log 로그 원문
├─ env.json 환경 (재현용)
├─ logits_*.npy 예측값 (threshold 재계산용)
└─ best.pt 모델 가중치
| 파일 | 언제 보나 |
|---|---|
report.md |
항상 여기부터. 사람이 읽으라고 만든 문서 |
figures/ |
자료가 필요할 때 — 이미 만들어져 있습니다 |
train.log |
학습이 실패했을 때 |
diff.json |
"이전 실험과 뭐가 달랐지?" |
env.json |
결과가 남과 다를 때 |
Study 실행 결과도 같습니다. 모델마다 report.md 와 figures/ 가 따로 생기고, 그 위에
비교 결과가 얹힙니다.
runs/study_02_backbone/
├─ STUDY.md ← ★ 비교 결론 (모델 간 순위)
├─ index.csv 이 study 의 모델 목록·지표 한눈에
├─ figures/ 비교 그림 4종
├─ study_02_backbone__arch-resnet18/ ← 모델 하나 = 단일 실행과 같은 구조
│ ├─ report.md ← 이 모델만의 리포트
│ └─ figures/ 이 모델만의 그림 8종
└─ study_02_backbone__arch-convnext_tiny/
└─ …
| 무엇이 궁금한가 | 어디를 보나 |
|---|---|
| 어느 모델이 좋았나 | STUDY.md · figures/cost_accuracy.png |
| 그 모델이 왜 그랬나 | 멤버 폴더의 report.md — 클래스별 F1·혼동행렬·자동 관찰 |
| 특정 모델의 오분류 패턴 | 멤버 폴더의 figures/confusion_stage.png |
멤버 리포트는 모델이 하나 끝날 때마다 만들어집니다. 5개짜리 Study 라면 3번째 모델이 도는 중에 이미 앞선 2개의 리포트를 열어볼 수 있습니다.
| 지표 | 의미 | 주의 |
|---|---|---|
| stage macro-F1 | 주 지표 | 이 값으로 판단합니다 |
| 정식기 recall | 소수 클래스를 얼마나 놓치지 않는가 | 이 프로젝트의 실질 난이도 |
| accuracy | 전체 정답률 | |
| 미판정률 | 판정을 보류한 비율 | threshold와 함께 봄 |
왜 accuracy를 안 보나 — 생육기가 60%다. 아무 생각 없이 전부 생육기라고 답하는 모델도 accuracy 60%가 나옵니다. macro-F1은 클래스별 성능을 평균하므로 이런 속임수가 통하지 않습니다.
| 증상 | 원인 | 해결 |
|---|---|---|
python: command not found |
PATH 미등록 | Python 재설치 시 "Add to PATH" 체크 |
Activate.ps1 실행할 수 없음 |
PowerShell 실행 정책 | Set-ExecutionPolicy -Scope CurrentUser RemoteSigned |
ModuleNotFoundError: torch |
가상환경 미활성화 | 프롬프트에 (.venv)가 있는지 확인 |
ModuleNotFoundError: tkinter |
Microsoft Store Python | python.org 공식 설치본 + tcl/tk 체크 |
| 학습이 유난히 느림 | CPU 빌드 torch | python scripts/check_env.py로 확인 → §2단계 3 재실행 |
scripts/check_env.py에서 CUDA 불가 |
드라이버 구버전 또는 CPU 빌드 | nvidia-smi 확인 → 드라이버 업데이트 또는 torch 재설치 |
| 그림의 한글이 □로 깨짐 | 한글 폰트 없음 | scripts/check_env.py 가 확인해 줍니다. Windows 는 Malgun Gothic 이 기본 탑재, 그 외에는 나눔고딕을 설치하세요 |
아이콘 만들기가 [실패] 파이썬을 찾을 수 없습니다 |
PATH 미등록 | Python 재설치 시 "Add to PATH" 체크 (§1.2) |
아이콘 만들기가 [실패] lab_gui.py 가 이 폴더에 없습니다 |
.bat을 저장소 밖에서 실행 |
저장소 폴더 안의 .bat을 그대로 더블클릭 |
| 바탕화면 아이콘을 눌러도 창이 안 뜸 | 바로가기가 시스템 파이썬을 가리킴 (.venv에만 설치) |
.venv 활성화 후 .\make_shortcut.ps1 재실행 (§3.4) |
| 증상 | 원인 | 해결 |
|---|---|---|
| 인제스트가 "교집합 0건"으로 중단 | labeled/source 묶음 번호 불일치 | 같은 명령에 --skip-broken 을 붙여 다시 실행하면 깨진 짝만 빼고 진행합니다 (§4.2). 데이터를 온전히 쓰려면 같은 번호의 tar 를 받습니다 (§4.1) |
| 인제스트 중 디스크 부족 | tar + 추출본 = 원본 2배 | 작물별로 나눠 진행하거나 공간 확보 |
| 학습 시작 시 "이미지를 찾을 수 없음" | 추출 경로 문제 / 인제스트 미실행 | python scripts/check_data.py |
CUDA out of memory |
배치가 큼 / 모델이 무거움 | 배치를 절반으로. 그래도 안 되면 해상도 160으로. 콘솔의 권장 배치 참고 |
| Windows에서 무한 대기 / 프로세스 폭증 | num_workers > 0 |
Windows는 num_workers=0부터 시작 |
loss가 NaN |
lr이 너무 큼 | lr을 1/10로. ConvNeXt는 특히 낮게 |
| loss가 전혀 안 줄어듦 | lr이 너무 작거나 optimizer 문제 | 자동 관찰이 🔴 학습이 진행되지 않음으로 잡아줍니다 |
| 정확도가 이상하게 높음 (99%+) | 데이터 누수 — 같은 개체가 train/val에 섞임 | 분할 로그의 그룹 중복 건수가 0인지 확인. 0이 아니면 group_id 설정 문제 |
| val_loss만 계속 오름 | 과적합 | epoch을 줄이거나 augmentation 강화. early stop이 자동 중단 |
| 학습이 갑자기 죽음 | OOM 또는 데이터 파일 손상 | train.log 마지막 부분 확인 |
| 증상 | 원인 | 해결 |
|---|---|---|
| 그림이 안 생김 | matplotlib 오류 | 수동 재생성: python make_figures.py --run runs/exp_... (학습 결과는 무사합니다) |
report.md가 없음 |
리포트 생성 실패 | python make_report.py --run runs/exp_... |
| 혼동행렬이 비어 있음 | logits_*.npy 미생성 |
학습이 정상 종료됐는지 status.json 확인 |
| 델타가 안 나옴 | 기준 run 미선택 | 전역 바에서 기준 run 선택 |
| 남과 결과가 미묘하게 다름 | GPU·라이브러리 버전 차이 | env.json 대조. 정상적인 현상이며 경향이 같으면 문제 없음 |
| 금지 | 왜 |
|---|---|
runs/ 폴더를 임의로 삭제 |
연구 기록이 사라집니다. 정리가 필요하면 팀과 상의 |
data/를 Git에 커밋 |
저장소가 수 GB로 불어납니다. .gitignore에 이미 있습니다 |
best.pt·*.npy를 Git에 커밋 |
같은 이유. 커밋 대상은 config·metrics·report·figures뿐 |
| 가설 없이 실행 | 나중에 그 run이 왜 있는지 아무도 모릅니다 |
| 학습 중 콘솔 강제 종료 | 체크포인트가 손상될 수 있습니다. 중단 버튼을 쓸 것 |
config.json을 손으로 고쳐 재실행 |
가능은 하지만, 어떤 값이 실제로 쓰였는지 추적이 흐려집니다. 콘솔에서 조정 |
| test set을 보고 설정 선택 | 성능이 부풀려집니다. val로만 판단 |
| 다른 사람 run을 덮어쓰기 | run ID 규칙(03_팀_협업_규약.md)을 지킬 것 |
| 여러 학습을 한 PC에서 동시 실행 | GPU 메모리 경쟁으로 둘 다 느려지거나 OOM |
| 경로에 한글·공백 | 일부 라이브러리에서 오류 |
1. python scripts/check_env.py 환경 문제인가?
2. runs/.../train.log 마지막 40줄 무슨 에러인가?
3. §8 자주 발생하는 문제 이미 알려진 문제인가?
4. runs/.../env.json 내 환경이 남과 다른가?
5. 팀에 문의
| 항목 | 이유 |
|---|---|
python scripts/check_env.py 출력 |
환경 파악 |
train.log 마지막 40줄 |
실제 에러 |
config.json |
어떤 설정이었는지 |
| 무엇을 하려다 언제 발생했는지 | 재현 조건 |
| 문서 | 내용 |
|---|---|
01_기획서.md |
이 프로젝트가 무엇을 왜 하는가 |
02_구조도.md |
코드가 어떻게 구성돼 있는가 |
03_팀_협업_규약.md |
실험을 시작하기 전에 반드시 읽을 것 — run ID·분담·공유 규칙 |
04_데이터_명세서.md |
데이터가 이상할 때 — 구조·JSON 스키마·분할 정책·함정 10선 |