scikit-learn을 해봤으니 한 단계 업그레이드 된 프레임워크 'pytorch'로 구현을 해보자. 좀 더 깊은 이해를 위해 scikit-learn을 참고하고 와도 좋다!
pytorch 도 MNIST dataset을 기본적으로 제공한다. 웬만한 인공지능 관련 라이브러리나 프레임워크는 MNIST dataset을 제공한다. scikit-learn은 파이썬 기반 라이브러리이고, pytorch는 프레임워크이다. 라이브러리는 기본적으로 기본적인 파이썬 파일 안에서 모듈을 끌어다 쓰지만, 프레임워크는 정해진 약속, 규칙이 존재하고 익숙해지는 데에 시간이 필요하니, 인내심을 갖고 공부해야 한다.
Pytorch의 이해를 돕기 위해 간단한 소개를 진행한다. 머신러닝 프레임워크 중에서는 비교적 쉬운 프레임워크에 속하며 다음 게시글에 나올 tensor flow 보다는 느리다. 그리고 배포에도 어려움이 존재한다. 그래서 연구나, prototype 버전은 pytorch로 하고 실제 production 단계에서는 tensor flow로 개발을 하는 경우가 많다. Pytorch는 원래 우리가 파이썬 프로그래밍을 해왔던 것처럼 class를 통해 객체 지향적인 프로그래밍을 할 수 있다. 기본적인 단위는 tensor이며 N차원 array이다. (numpy와 유사하다). 그리고 back propagation(역전파)을 위해 variable이 tensor를 감싸고 있으며, tensor에 실행되는 operation을 추적하여, gradient가 거꾸로 흘러갈 수 있게 한다.
자 이제 본격적으로 MNIST 데이터를 분석해보자! (여기서 실행되는 코드들은 모두 Google Colab에서 진행된다)
1. 데이터 불러오기 전, torchvision.transforms을 통해 데이터 전처리를 준비해 놓는다.
import torchvision.transforms as transforms
# 데이터를 평균 0.5, 표준편차 0.1로 평균화 시킨다
mnist_transform = transforms.Compose([
transforms.ToTensor(), # tensor로 바꾸기
transforms.Normalize((0.5,), (1.0,))
])
기본 단위가 tensor이기 때문에 tensor로 바꾸고 평균화한다. transforms.Compose를 통해 모든 전처리를 묶을 수 있다!
2. 데이터 전처리 준비가 끝났으니 데이터 다운로드!
from torchvision.datasets import MNIST
# 다운로드 경로
root = './MNIST'
train_data = MNIST(root, train=True, transform=mnist_transform, download=True)
test_data = MNIST(root, train=False, transform=mnist_transform, download=True)
3. 다운로드된 데이터를 섞고(shuffle) batch로 묶는다! batch는 training을 한 번 반복할 때 data를 전부 다 사용하면 시간이 너무 오래 걸리니, 한번 반복할 때마다 몇 개의 데이터를 사용하는지 정하는 parameter이다.
from torch.utils.data import DataLoader
BATCH_SIZE = 50
train = DataLoader(dataset=train_data,
batch_size=BATCH_SIZE,
shuffle=True)
test = DataLoader(dataset=test_data,
batch_size=BATCH_SIZE,
shuffle=True)
4. Neural Network 모델 만들기. 여기서는 CNN을 이용한 Neural Network를 만든다. 기본적인 틀은 짜여있다.
첫번째, torch.nn.Module을 부모 class로 갖는 class를 만든다. 두 번째, class 안에 def forward(parameter)를 만들어서 모델이 어떻게 전개되는지 만든다. CNN을 간략히 설명하면 convolution 레이어를 통해 이미지의 특징을 추출하고 그 추출한 특징을 pooling 레이어를 통해 압축하고 이 과정을 반복하여 마지막에는 fully connected 레이어를 통해 output을 산출한다.
nn.Conv2d(in_channels, out_channels, kernel_size, stride=1) : channel의 예시를 들면, 색깔을 들 수 있다. RGB는 red, green, blue로 모든 색을 표현하므로 channel이 3이고. 흑백 사진은 channel이 1이 된다. 참고로 depth(이미지의 깊이)도 channel의 일종이 될 수 있으니 channel은 색으로 한정되지는 않는다.
kernel_size는 합성곱(convolution)을 실행하기 위한 윈도우 사이즈를 말한다. 3은 (3x3)을 의미한다.
stride는 filter가 몇칸씩 움직이며 convolution을 실행할 지에 대한 parameter이다.
nn.MaxPool2d(kernel_size): kernel_size는 위에서도 나왔다. 2는 2x2를 의미한다.
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class Net(nn.Module): # Neural Network 만들기
def __init__(self):
super(Net, self).__init__() # 부모 class 생성자
self.conv1 = nn.Conv2d(1, 6, 5, 1) # 첫번째 convolution 레이어
self.pool1 = nn.MaxPool2d(2) # 첫번째 pooling 레이어
self.conv2 = nn.Conv2d(6, 16, 5, 1) # 두번째 convolution 레이어
self.pool2 = nn.MaxPool2d(2) # 두번째 pooling 레이어
self.fc1 = nn.Linear(256, 64) # 첫번째 fully connected 레이어
self.fc2 = nn.Linear(64, 10) # 두번째 fully connected 레이어
5. def forward() class 안에서 정의하기
쭉 따라가다 보면 x.view라는게 보인다. torch.Tensor.view 함수이다!
tensor 'x'의 shape을 바꾸는 함수로, 앞에 -1은 뒤에 숫자 256에 맞춰서 tensor의 shape을 변경한다.
F.relu()를 해주는 이유는, convolution이나 fully-connected layer에서 activation 함수가 따로 없기에 지정을 해준다.
def forward(self, x): # 어떻게 각각의 layer를 사용해서 forward 할 것인지
# 첫번째 convolution layer-relu-pool
x = self.conv1(x)
x = F.relu(x)
x = self.pool1(x)
# 두번째 convolution layer-relu-pool
x = self.conv2(x)
x = F.relu(x)
x = self.pool2(x)
# (N, 256)
x = x.view(-1, 256)
# 첫번째 fully-connected layer
x = F.relu(self.fc1(x))
# 두번째 fully-connected layer
x = self.fc2(x)
return F.softmax(x, dim=1)
6. 직접 만들어준 neural network 모델의 객체 생성하기.
net = Net()
7. 최소화 해야 하는 loss function과 최적화 방법 optimization 선택하기.
여기서는 가장 많이 쓰이는 loss function 중 하나인 nn.cross entropy loss를 사용하고 backpropagation 알고리즘에는 SGD(stochastic gradient descent)를 사용한다. (lr : Learning Rate)
loss_func = nn.CrossEntropyLoss()
# net.parameters() : 'net' neural network에 train 가능한 모든 변수에 SGD 적용
algo = optim.SGD(net.parameters(), lr=0.01)
8. 생성한 모델 train 시키고 test data 로 검증하기.
num_epochs = 10
for epoch in range(num_epochs):
train_loss = 0.0
# Iteration over the train dataset
for i, data in enumerate(train_loader):
# feature 값, target 값 분리
x, label = data
# gradient 값을 0으로 설정
algo.zero_grad()
# 2. 순전파
model_output = net(x)
# 3. loss function 구하기
loss = loss_func(model_output, label)
# 4. loss function을 이용한 역전파
loss.backward()
# 5. Weight 수정
algo.step()
# 총 loss
train_loss += loss.item()
# train loss와 test 정확도 표시
with torch.no_grad(): # test 데이터는 gradient를 계산할 필요가 없으므로 추가!
corr_num = 0
total_num = 0
# for문을 통한 test 데이터 정확도 판단!
for _, test in enumerate(test_loader):
test_x, test_label = test
test_output = net(test_x)
pred_label = test_output.argmax(dim=1)
corr = test_label[test_label == pred_label].size(0)
corr_num += corr
total_num += test_label.size(0)
print("[Epoch: %d] train loss: %.4f, test acc: %.2f" \
% (epoch + 1, train_loss / len(train_loader), corr_num / total_num * 100))
train_loss = 0.0
결과