PyTorch로 처음부터 만드는 CNN 이미지 분류기 학습 프로젝트입니다.
mini-class/
├── data/ # 데이터 저장 공간
│ ├── raw/ # 원본 데이터 (CIFAR-10 자동 다운로드됨)
│ ├── processed/ # 전처리된 데이터
│ └── train/val/test/ # 데이터 분할
│
├── src/ # 소스 코드
│ ├── data/
│ │ └── dataset.py # 🔨 [구현 필요] 데이터 로딩 및 전처리
│ ├── models/
│ │ └── cnn.py # 🔨 [구현 필요] CNN 모델 정의
│ ├── train.py # 🔨 [구현 필요] 학습/평가 함수
│ └── main.py # 전체 파이프라인 실행
│
└── outputs/ # 실험 결과 저장
├── checkpoints/ # 모델 체크포인트
├── logs/ # 학습 로그
└── best_models/ # 최고 성능 모델
-
PyTorch 기본 사용법 익히기
- 데이터 로딩 (Dataset, DataLoader)
- 모델 정의 (nn.Module)
- 학습 루프 작성
-
딥러닝 파이프라인 이해하기
- 데이터 전처리 → 모델 정의 → 학습 → 평가 흐름 체험
-
실험과 튜닝 경험하기
- 하이퍼파라미터 변경이 성능에 미치는 영향 관찰
PyTorch는 공식 Python만 지원합니다!
- ✅ Python 3.8-3.12 (python.org, conda, Windows Store)
- ❌ MSYS2/MinGW Python (지원 안 됨!)
Python 확인:
python -c "import platform; print(platform.python_compiler())"
# 'MSC' = ✅ OK | 'GCC' = ❌ 안 됨자세한 내용: INSTALL.md
자동 설치 스크립트가 Python 호환성을 체크하고 GPU를 감지하여 최적의 PyTorch 버전을 설치합니다.
Windows (PowerShell):
# 가상환경 활성화
.venv\Scripts\activate
# 자동 설치
.\setup.ps1Linux/Mac:
# 가상환경 활성화
source .venv/bin/activate
# 자동 설치
python setup.py1) PyTorch 설치 (환경에 맞게 선택)
# CPU only
pip install torch torchvision torchaudio
# NVIDIA GPU (CUDA 11.8)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# NVIDIA GPU (CUDA 12.1)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# AMD GPU (ROCm)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.62) 기타 의존성 설치
pip install -r requirements.txtpython -c "import torch; print(f'CUDA: {torch.cuda.is_available()}'); print(f'MPS: {torch.backends.mps.is_available() if hasattr(torch.backends, \"mps\") else False}')"프로그램이 완성되면 다음과 같은 순서로 동작합니다:
[main.py 실행]
↓
① 하이퍼파라미터 설정 & GPU/CPU 선택
↓
② 데이터 로딩 (dataset.py)
- CIFAR-10 다운로드 (최초 1회)
- Transform 적용 (정규화, 증강)
- DataLoader 생성 (배치 단위로 묶기)
↓
③ 모델 생성 (cnn.py)
- SimpleCNN 인스턴스 생성
- GPU/CPU로 모델 이동
↓
④ 손실함수 & 옵티마이저 초기화
- CrossEntropyLoss
- SGD or Adam
↓
⑤ 학습 루프 (에폭 반복)
│
├─→ [학습 단계] train_one_epoch()
│ - 전체 학습 데이터를 배치 단위로 순회
│ - 각 배치마다:
│ 1. 데이터 → GPU/CPU 이동
│ 2. 그래디언트 초기화
│ 3. Forward: 모델 예측
│ 4. Loss 계산
│ 5. Backward: 그래디언트 계산
│ 6. 파라미터 업데이트
│ - 평균 loss와 정확도 반환
│
├─→ [평가 단계] evaluate()
│ - 전체 테스트 데이터를 배치 단위로 순회
│ - 그래디언트 계산 없이 Forward만 수행
│ - Loss와 정확도 계산
│ - 평균 loss와 정확도 반환
│
├─→ [결과 출력]
│ - 학습 loss/정확도
│ - 테스트 loss/정확도
│
└─→ [모델 저장]
- 최고 성능 모델 → outputs/best_models/
- 정기 체크포인트 → outputs/checkpoints/
↓
⑥ 학습 완료 & 최종 결과 출력
전체 파이프라인을 조율하는 엔트리 포인트입니다.
- 하이퍼파라미터 설정
- 모든 모듈 임포트 및 초기화
- 학습 루프 실행
- 결과 저장 관리
모델에게 데이터를 공급하는 역할입니다.
- CIFAR-10 다운로드 및 로딩
- 이미지 전처리 (정규화, 증강)
- 배치 단위로 데이터 제공
왜 분리? 데이터 처리 로직을 독립적으로 관리하면, 다른 데이터셋으로 교체하거나 전처리 방법을 변경할 때 이 파일만 수정하면 됩니다.
신경망 구조를 정의합니다.
- CNN 레이어 구성
- Forward pass 정의
- 모델 아키텍처
왜 분리? 모델 구조를 독립적으로 관리하면, 여러 모델을 실험하거나 아키텍처를 변경할 때 다른 코드에 영향을 주지 않습니다.
학습과 평가의 실제 로직을 담당합니다.
- 학습 루프 (forward → backward → update)
- 평가 루프 (forward → metrics)
- 체크포인트 저장/로드
왜 분리? 학습 로직을 재사용 가능하게 만들어, 다른 프로젝트에서도 동일한 학습 함수를 사용할 수 있습니다.
[DataLoader]
↓
images: (64, 3, 32, 32) ← 64개 이미지, 3채널, 32x32 크기
labels: (64,) ← 64개의 정답 레이블
↓
[GPU 이동]
↓
[Model Forward]
↓
outputs: (64, 10) ← 64개 샘플의 10개 클래스 점수
↓
[Loss 계산]
loss = CrossEntropyLoss(outputs, labels)
↓
[Backward]
각 파라미터의 그래디언트 계산
↓
[Optimizer Step]
파라미터 업데이트: weight = weight - lr * gradient
에폭 1:
배치1 → Forward → Loss → Backward → Update
배치2 → Forward → Loss → Backward → Update
...
배치782 (50000/64) → Forward → Loss → Backward → Update
[평균 loss, 정확도 계산]
[테스트 데이터로 평가]
에폭 2:
(반복...)
...
에폭 10:
(반복...)
[최고 성능 모델 저장]
- CIFAR-10 데이터셋을 다운로드하고 전처리
- DataLoader로 배치 단위로 묶기
get_cifar10_transforms() 함수
# 찾아볼 키워드:
# - transforms.Compose
# - transforms.ToTensor()
# - transforms.Normalize()
# - CIFAR-10 정규화 값: mean=[0.4914, 0.4822, 0.4465], std=[0.2470, 0.2435, 0.2616]질문해보세요:
- Q1. 왜 이미지를 정규화(Normalize)해야 하나요?
- Q2. 학습용과 테스트용 transform을 다르게 설정하는 이유는?
- Q3. Data Augmentation(데이터 증강)이란? 언제 적용할까?
load_cifar10() 함수
# 찾아볼 키워드:
# - datasets.CIFAR10
# - DataLoader
# - shuffle의 역할
# - batch_size가 성능에 미치는 영향cd src
python data/dataset.py
# 출력 예시:
# 배치 이미지 shape: torch.Size([64, 3, 32, 32])
# 전체 학습 데이터 개수: 50000- 간단한 CNN 모델을 직접 설계하고 구현
SimpleCNN 클래스
입력/출력 정보:
- 입력:
(batch_size, 3, 32, 32)- CIFAR-10 이미지 - 출력:
(batch_size, 10)- 10개 클래스 점수
추천 구조 (자유롭게 변경 가능!):
입력 (3x32x32)
↓
Conv2d → ReLU → MaxPool2d # 첫 번째 합성곱 블록
↓
Conv2d → ReLU → MaxPool2d # 두 번째 합성곱 블록
↓
Flatten # (batch, C, H, W) → (batch, C*H*W)
↓
Linear → ReLU # Fully Connected 1
↓
Linear # Fully Connected 2 (출력)
↓
출력 (10)
찾아볼 키워드:
nn.Conv2d- 파라미터: in_channels, out_channels, kernel_size, paddingnn.MaxPool2d- 파라미터: kernel_size, stridenn.Linear- 파라미터: in_features, out_featuresF.relu- 활성화 함수x.view()- Flatten 처리
설계 시 고민해볼 점:
- Conv 레이어의 필터 개수를 얼마로? (16, 32, 64, 128...)
- Pooling을 몇 번 적용할까? (적용 시마다 크기가 절반으로)
- FC 레이어의 노드 수는?
32x32 이미지 → MaxPool(2,2) → 16x16
16x16 이미지 → MaxPool(2,2) → 8x8
8x8 이미지 → MaxPool(2,2) → 4x4
예: Conv로 32채널까지 증가 + 8x8 크기
→ Flatten 후 크기 = 32 * 8 * 8 = 2048
cd src
python models/cnn.py
# 모델 구조와 파라미터 개수 확인- 학습 루프와 평가 루프 구현
train_one_epoch() 함수 - 1 에폭 학습
# 의사코드:
model.train() # 학습 모드 설정
for 배치 in train_loader:
1. 데이터를 device(GPU/CPU)로 이동
2. optimizer.zero_grad() # 이전 그래디언트 초기화
3. outputs = model(images) # Forward pass
4. loss = criterion(outputs, labels) # 손실 계산
5. loss.backward() # Backward pass (그래디언트 계산)
6. optimizer.step() # 파라미터 업데이트
7. 정확도 계산 (예측값과 실제 레이블 비교)
return 평균_loss, 정확도찾아볼 키워드:
.to(device)- GPU/CPU로 데이터 이동optimizer.zero_grad()- 왜 필요할까?.backward()- 역전파torch.max(outputs, 1)- 예측 클래스 구하기
evaluate() 함수 - 평가
# 의사코드:
model.eval() # 평가 모드 (Dropout, BatchNorm 동작 변경)
with torch.no_grad(): # 그래디언트 계산 비활성화 (메모리 절약, 속도 향상)
for 배치 in test_loader:
1. 데이터를 device로 이동
2. outputs = model(images) # Forward만 수행
3. loss = criterion(outputs, labels)
4. 정확도 계산
return 평균_loss, 정확도질문해보세요:
- Q1.
model.train()과model.eval()의 차이는? - Q2.
torch.no_grad()는 왜 사용하나요? - Q3. 정확도는 어떻게 계산하나요?
- 모든 모듈을 연결하여 학습 진행
-
주석 해제 및 코드 완성
- 각 TODO 부분의 주석을 해제하며 진행
- 옵티마이저 선택 (SGD vs Adam)
-
하이퍼파라미터 설정
BATCH_SIZE = 64 # 배치 크기 LEARNING_RATE = 0.001 # 학습률 NUM_EPOCHS = 10 # 에폭 수
-
학습 실행
cd src python main.py
Device: cuda (또는 cpu)
Epoch [1/10]
Train - Loss: 1.8234, Acc: 32.45%
Test - Loss: 1.6521, Acc: 38.12%
Epoch [2/10]
Train - Loss: 1.5234, Acc: 43.21%
...
-
데이터 증강 효과 확인
# dataset.py의 train_transform에 추가 transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding=4),
- 정확도가 얼마나 향상되나요?
-
옵티마이저 비교
- SGD vs Adam 성능 차이는?
- Learning rate를 변경하면?
-
모델 구조 변경
- Conv 레이어를 더 깊게 쌓으면?
- 필터 개수를 늘리면?
- Dropout 추가 (
nn.Dropout) - Batch Normalization 추가 (
nn.BatchNorm2d) - Learning Rate Scheduler 사용
- 학습 곡선 그래프 그리기 (matplotlib)
- 1단계: 데이터 로딩 구현 완료
- 2단계: CNN 모델 설계 완료
- 3단계: 학습/평가 함수 구현 완료
- 4단계: 전체 학습 성공 (정확도 50% 이상 목표!)
- 실험: 데이터 증강 적용 및 성능 비교
- 실험: 하이퍼파라미터 튜닝
- 정규화(Normalization): 학습 안정화 및 수렴 속도 향상
- Data Augmentation: 과적합 방지, 일반화 성능 향상
- Optimizer: SGD(안정적), Adam(빠른 수렴)
- Learning Rate: 너무 크면 발산, 너무 작으면 느림
- "CIFAR-10 정규화 값은 어떻게 구하나요?"
- "DataLoader의 shuffle은 왜 필요한가요?"
- "배치 크기를 어떻게 정해야 하나요?"
- "Conv2d의 padding은 왜 사용하나요?"
- "Flatten 후 크기가 안 맞아요! (크기 계산 방법)"
- "활성화 함수를 왜 써야 하나요?"
- "loss.backward()가 하는 일은?"
- "optimizer.zero_grad()를 빼먹으면?"
- "학습이 너무 느려요! (GPU 사용, batch_size 조정)"
-
한 번에 다 하지 마세요
- 1단계씩 완성하고 테스트
- 에러가 나면 그 단계에서 해결
-
print 디버깅 활용
print(f"입력 shape: {images.shape}") print(f"출력 shape: {outputs.shape}")
-
작은 실험부터
- NUM_EPOCHS=2로 먼저 돌려보기
- 학습이 되는지 확인 후 에폭 늘리기
-
모르는 건 검색하고 질문하세요!
- PyTorch 공식 문서가 가장 정확
- 에러 메시지를 구글에 검색
- ResNet 같은 유명 모델 구현해보기
- 전이 학습(Transfer Learning) 시도
- 다른 데이터셋 적용 (MNIST, Fashion-MNIST 등)
- PyTorch Lightning으로 코드 리팩토링
Good Luck! 🚀