[pytorch] MNIST 예제
기존 텐서플로로 공부를 해왔다. 그런데 최근 트렌드가 텐서플로에서 파이토치로 많이 넘어간다고 한다.
파이썬과 많이 비슷하며 연구진들이 정말 많이 사용해 논문 같은 예시 코드가 파이토치로 많이 구현되있어서 이왕 제대로 각잡고 공부하는거 토치를 한 번 공부해 보려고 한다.
실제로 간단한 기본 MNIST를 하면서 느낀점은 ...
더 어렵다. 확실히 더 어렵다... 아직 잘 몰라서 이런 말 하기도 조심스럽지만 텐서플로 같은 경우에는 모델을 돌릴 때 아주 간단하게 돌릴 수 있었다. 하지만 디테일하게 들어가서 구조를 들여다 보고 모델이 돌아가는 방식을 살피기는 쉽지 않았다. 그냥 필요한 값만 넣어주면 알아서 돌아가는 시스템이였다.
근데 파이토치는 모델의 구조 순서에 따라 직접 구성하는 방식이고 원리를 잘 이해한 상태라면 그냥 그 원리 그대로가 반영 되있는 느낌이였다. 그리고 파이썬에 가깝다는 말도 대충 무슨 말인지 알 것 같았다
일단은 예제 이기 때문에 코드와 결과만 설명하고 넘어가겠다.
import torch
is_cuda = torch.cuda.is_available()
device = torch.device('cuda' if is_cuda else 'cpu')
print(device)
import torch.nn as nn #딥러닝 네트워크의 기본 구성요소
import torch.nn.functional as F # 딥러닝에 자주 쓰는 함수를 F로 불러오겠다
import torch.optim as optim # 가중치 추정에 필요한 최적화 알고리즘을 포함한 모듈을 optim으로 지정
from torchvision import datasets, transforms #torchvision에서 데이터셋과 transform을 가져온다
from matplotlib import pyplot as plt
%matplotlib inline
HyperParameter 지정
batch_size = 50
epoch_num = 15
learning_rate = 0.0001
train_data = datasets.MNIST(root = './data', train = True, download = True, transform = transforms.ToTensor())
test_data = datasets.MNIST(root = './data', train = False, transform = transforms.ToTensor())
# transform : MNIST 저장과 동시에 전처리를 할 수 있는 옵션. 이미지를 텐서로 변형하는 전처리여서 ToTensor()를 써준다.
print('number of traning data: ', len(train_data))
print('number of test data: ', len(test_data))
root = ./data (현재 디텍토리에 data라는 폴더를 만들어서 저장하겠다.)
transforms.ToTensor() : 이미지를 받음으로 동시에 tensor로 받는 전처리를 하는 것이다.

image, label = train_data[0] # image 와 label 을 받아온다.
# MNIST는 단일 채널로 [1,28,28] 3차원 텐서이다. 3차원 텐서를 2차원으로 줄이기 위해 image.squeeze()를 사용한다.
plt.imshow(image.squeeze().numpy(), cmap = 'gray') #squeeze() : 크기가 1인 차원을 없애는 함수로 2차원 [28,28]로 만든다.
plt.title('label : %s' % label)
plt.show()
[미니 배치 구성하기]
# DataLoader는 손쉽게 배치를 구성하며 학습과정을 반복 시행 할때 마다 미니배치를 불러오는 유용한 함수
train_loader = torch.utils.data.DataLoader(dataset = train_data,
batch_size = batch_size,
shuffle = True)
test_loader = torch.utils.data.DataLoader(dataset = test_data,
batch_size = batch_size,
shuffle = True)
first_batch = train_loader.__iter__().__next__()
print('{:15s} | {:<25s} | {}'.format('name', 'type', 'size'))
print('{:15s} | {:<25s} | {}'.format('Num of Batch', '',
len(train_loader)))
print('{:15s} | {:<25s} | {}'.format('first_batch', str(type(first_batch)),
len(first_batch)))
print('{:15s} | {:<25s} | {}'.format('first_batch[0]',
str(type(first_batch[0])),
first_batch[0].shape))
print('{:15s} | {:<25s} | {}'.format('first_batch[1]',
str(type(first_batch[1])),
first_batch[1].shape))

[CNN 구조 설계하기]
class CNN(nn.Module):
def __init__(self): #모델에 사용되는 가중치를 정의
super(CNN,self).__init__() #super()를 통해 nn.Module 클래스의 속성을 상속 받고 초기화
self.conv1 = nn.Conv2d(1,32,3,1) #in_channel / out_channel / kernel_size / stride
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.dropout1 = nn.Dropout2d(0.25) # 0.25 확률의 드랍아웃을 지정
self.dropout2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(9216,128) # 9264 -> 128 -> 10
self.fc2 = nn.Linear(128,10)
def forward(self,x) : # 입력이미지와 정의한 가중치를 이용해 feed forward 계산
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x,2) # (2x2 크기의 필터로 맥스 풀링 한다. 이건 가중치 할 필요가 없어서 위에서 정의 안했다)
x = self.dropout1(x)
x = torch.flatten(x,1)
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1) #softmax 말고 log_softmax()를 쓰면 연산속도를 올릴 수 있다.
return output
구조를 그림으로 그리면

[ 모델 최적화]
model = CNN().to(device)
optimizer = optim.Adam(model.parameters(), lr = learning_rate)
criterion = nn.CrossEntropyLoss() # 손실함수로 크로스엔트로피

[모델 학습]
model.train()
i = 0
for epoch in range(epoch_num):
for data, target in train_loader:
data = data.to(device)
target = target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target) # 계산값과 결과값에 대해 손실을 구해보자
loss.backward()
optimizer.step() # 역전파로 계산해서 모델 가중치 업데이트
if i % 1000 == 0 :
print('Train Step : {}\tLoss: {:.3f}'. format(i, loss.item()))
i += 1

[모델 평가]
model.eval()
correct = 0
for data, target in test_loader:
data = data.to(device)
target = target.to(device)
output = model(data)
prediction = output.data.max(1)[1]
correct += prediction.eq(target.data).sum()
print('Test set: Accuracy : {:.2f}%'.format(100 * correct / len(test_loader.dataset)))
정확도 99.09 % 가 나왔다.
이게 너무 잘 정형화된 사기 데이터여서 당연히 이런 결과가 나온다고 한다.
파이토치에 빨리익숙해지고 싶다.