[Deeplearning] Pytorch CIFAR-10 이미지 분류 튜토리얼
이번엔 Pytorch 공식 사이트에 있는 이미지 분류 튜토리얼에 대해서 알아보고자 합니다. 해당 튜토리얼에 대해 정리를 해서 포스트로 작성하는 이유는 튜토리얼이지만 Pytorch 에서만 사용하는 생소한 함수들로 인해 소스코드를 봐도 잘 이해가 가지 않아 포스트로 정리해 놓고 이후에 Pytorch 를 이용해 모듈 개발을 할 때 참고를 하기 위해서입니다.
1. 데이터 전처리(Transform)
모델에 데이터를 입력하기 전에 transform
으로 데이터 전처리를 먼저 진행합니다.
transforms.Compose
여러 변환을 순차적으로 적용하는 컨테이너입니다.
transforms.ToTensor()
- PIL Image 또는 numpy array 를 Pytorch Tensor 로 변환해 주는 함수입니다.
- PIL 은 Python Imaging Library 의 약자로, 파이썬에서 이미지를 열기, 처리하기, 변환하기, 저장하기 등을 할 수 있도록 도와주는 라이브러리입니다.
transforms.Normalize(mean, std)
- 각 채널(R, G, B) 값을 정규화
- 정규화 하는 이유 : 딥러닝 모델의 입력이 0중심(평균이 0)이고 표준편차가 1에 가깝게 분포될 때 학습이 더 잘 되기 때문
- 공식: (x - mean)/std
- 여기서는 각 채널을 [-1, 1] 범위로 변환하기 위해 (0.5, 0.5, 0.5)을 사용
import torchvision
import torchvision.transforms as transforms
# 데이터 전처리: 이미지를 tensor 로 변환하고, 픽셀값을 [-1, 1] 범위로 정규화
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))
])
2. CIFAR-10 데이터셋 로드
CIFAR-10 이란?
- 32x32 크기의 컬러 이미지 60,000장이 있는 데이터
- 10개의 클래스(카테고리)
- 비행기, 자동차, 새, 고양이, 사슴, 개, 개구리, 말, 배, 트럭
- 학습용: 50,000 장
- 테스트용: 10,000 장
데이터셋과 DataLoader
# 배치 사이즈 설정
batch_size = 4
# CIFAR10 데이터셋 불러오기
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
# 이미지에 대응되는 카테고리 지정
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
- DataLoader 는 데이터를 배치 단위로 불러옵니다.
- batch_size : 배치 크기
- shuffle=True : 학습 시 데이터 순서 섞기
- num_workers=2 : 데이터 로딩에 사용하는 subprocess 개수
3. CNN 모델 정의
모델은 nn.Module 을 상속받아 정의합니다.
import torch.nn as nn
import torch.nn.functional as F
# CNN 모델 정의: conv -> relu -> pool, fully connected 로 구성
class Net(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 6, 5) # 컬러 이미지(3채널) -> 6개의 특성
self.pool = nn.MaxPool2d(2, 2) # 2x2 맥스풀링
self.conv2 = nn.Conv2d(6, 16, 5) # 6채널 -> 16채널
self.fc1 = nn.Linear(16*5*5, 120) # 완전 연결 계층
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84,10) # 클래스 수(10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x))) # conv1 -> relu -> pool
x = self.pool(F.relu(self.conv2(x))) # conv2 -> relu -> pool
x = torch.flatten(x, 1) # 배치 차원 빼고 모두 평탄화
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x) # 마지막은 로짓 출력
return x
net = Net() # 모델 인스턴스 생성
모듈 설명
- nn.Conv2d(in_channels, out_channels, kernel_size)
- 2D 컨볼루션 레이어
- nn.MaxPool2d(kernel_size, stride)
- 맥스 풀링
- nn.Linear(in_features, out_features)
- 완전연결(fully connected) 레이어
- F.relu()
- ReLU 활성화 함수
- torch.flatten(input, start_dim)
- 배치를 제외한 차원을 1차원으로 평탄화
4. 손실 함 수 및 옵티마이저
import torch.optim as optim
# 손실 함수 및 옵티마이저 설정
criterion = nn.CrossEntropyLoss() # 분류용 크로스엔트로피
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
nn.CrossEntropyLoss()
- 다중 클래스 분류에 사용하는 손실 함수
- 내부적으로 SoftMax + Negative Log Likelihood
optim.SGD
- SGD(확률적 경사 하강법)
5. 모델 학습
# 학습 루프 : 총 2 epoch 동안 학습
for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data # 배치 입력/레이블
optimizer.zero_grad() # 기울기 초기화
outputs = net(inputs) # 순전파
loss = criterion(outputs, labels) # 손실 계산
loss.backward() # 역전파
optimizer.step() # 파라미터 갱신
running_loss += loss.item()
if i % 2000 == 1999: # 2000 mini-batch 마다 출력
print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
running_loss = 0.0
각 단계 설명
- optimizer.zero_grad()
- 이전 gradient 를 초기화
- outputs = net(inputs)
- 순전파(forward pass)
- loss = criterion(outputs, labels)
- 손실 계산
- loss.backward()
- 역전파(backpropagation)
- optimizer.step()
- 파라미터 업데이트
6. 모델 평가
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Accuracy on test images: {100 * correct / total:.2f}%')
torch.no_grad()
- 학습이 아닌 평가 단계에서 gradient 계산을 비활성화하여 메모리/속도 최적화
torch.max(outputs, dim)
- 클래스별 확률 중 가장 큰 값과 인덱스를 반환
dim=1
: 클래스 차원에서 최대값
마무리
Pytorch 에서 제공하는 CIFAR-10 분류 튜토리얼을 통해서 Pytorch 를 이용한 데이터 로드와 전처리, 간단한 CNN 모델 구성, CrossEntorypyLoss 와 SGD 최적화, 학습과 평가 과정에 대해서 알게 되었습니다. 다만 해당 사이트를 보시면 아시겠지만 코드에 대한 주석이 없고, 코드에 대한 설명을 해주곤 있지만 함수에 대한 세세한 설명이 없어 저는 해당 사이트를 봐도 코드 내용에 대해서 전혀 알 수가 없다고 느꼈습니다. 그래서 이번 기회에 해당 사이트에 있는 튜토리얼 코드에 사용된 함수에 대해서 알아봤고, 이를 토대로 코드에 대한 설명을 포스트로 정리를 해보았습니다.
Comments