DL in Python/Pytorch 기초
[Pytorch 기초 - 4] MNIST data를 활용하여 CNN모델의 학습과 Optimizer, Evaluation
SuHawn
2020. 9. 1. 21:23
PyTorch: Optimization & Training¶
In [1]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import numpy as np
In [2]:
seed = 1 # 난수 고정
batch_size = 64
test_batch_size = 64
no_cuda = False
In [3]:
# gpu 사용?
use_cuda = not no_cuda and torch.cuda.is_available()
device = torch.device("cuda" if use_cuda else "cpu")
Preprocess¶
In [4]:
torch.manual_seed(seed)
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('dataset', train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('dataset', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=test_batch_size, shuffle=True)
Model¶
In [5]:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 20, 5, 1)
self.conv2 = nn.Conv2d(20, 50, 5, 1)
self.fc1 = nn.Linear(4*4*50, 500)
self.fc2 = nn.Linear(500, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 2, 2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2, 2)
x = x.view(-1, 4*4*50)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return F.log_softmax(x, dim=1)
Optimization¶
- Model과 Optimization 설정
In [6]:
model = Net().to(device)
In [7]:
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.5)
Before Training¶
- 학습하기 전에 Model이 Train할 수 있도록 Train Mode로 변환
- Convolution 또는 Linear 뿐만 아니라, DropOut과 추후에 배우게 될 Batch Normalization과 같이 parameter를 가진 Layer들도 학습하기 위해 준비
In [9]:
# parameter shape 확인
params = list(model.parameters())
for i in range(8):
print(params[i].size())
In [10]:
model.train() # train mode
Out[10]:
- 모델에 넣기 위한 첫 Batch 데이터 추출
In [11]:
data, target = next(iter(train_loader))
In [12]:
data.shape, target.shape
Out[12]:
- 추출한 Batch 데이터를 cpu 또는 gpu와 같은 device에 compile
In [13]:
data, target = data.to(device), target.to(device)
In [14]:
data.shape, target.shape
Out[14]:
- gradients를 clear해서 새로운 최적화 값을 찾기 위해 준비
In [15]:
optimizer.zero_grad()
- 준비한 데이터를 model에 input으로 넣어 output을 얻음
In [16]:
output = model(data)
- Model에서 예측한 결과를 Loss Function에 넣음
- 여기 예제에서는 Negative Log-Likelihood Loss 라는 Loss Function을 사용
In [17]:
loss = F.nll_loss(output, target)
- Back Propagation을 통해 Gradients를 계산
In [18]:
loss.backward()
- 계산된 Gradients는 계산된 걸로 끝이 아니라 Parameter에 Update
In [19]:
optimizer.step()
Start Training¶
위의 최적화 과정을 반복하여 학습 시작
In [20]:
epochs = 1
log_interval = 100
In [41]:
for epoch in range(1, epochs+1):
# Train mode
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % log_interval == 0 :
print('Train Epoch: {} [ {}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100 * batch_idx / len(train_loader), loss.item()
))
# 한 EPOCH에서 학습 후 바로 TEST 진행, TEST는 아래에 과정 있음.
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader :
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.argmax(dim=1, keepdim = True)
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('Test set: Average Loss: {:.4f}, Accuracy: {}/{} ( {:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset), 100 * correct / len(test_loader.dataset)))
Evaluation¶
- 앞에서 model.train() 모드로 변한 것처럼 평가 할 때는 model.eval()로 설정
- Batch Normalization이나 Drop Out 같은 Layer들을 잠금
In [22]:
# 평가모드
model.eval()
Out[22]:
- autograd engine, 즉 backpropagatin이나 gradient 계산 등을 꺼서 memory usage를 줄이고 속도를 높임
In [24]:
test_loss = 0
correct = 0
with torch.no_grad():
data, target = next(iter(test_loader))
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction = 'sum').item()
# sum을 해줌으로 batch_size, shape에 관계없이 하나의 scalar로 return하고 이를 더해줌.
pred = output.argmax(dim=1, keepdim = True)
# keepdim을 사용해 차원수를 계속 유지
correct = pred.eq(target.view_as(pred)).sum().item()
In [25]:
test_loss
Out[25]:
In [37]:
# output은 softmax를 거친 아직 argmax에 들어가지 않은 값이기에 확률값으로 출력됨.
output.shape
Out[37]:
In [32]:
# pred와 target의 shape을 맞춰 비교, 동일하다는 것을 확인 가능
pred.shape, target.view_as(pred).shape
Out[32]:
In [35]:
pred.eq(target.view_as(pred)).sum().item() / 64 # 몇 퍼센트가 동일하게 예측이 되었나?
Out[35]:
In [38]:
test_loss /= len(test_loader.dataset)
In [39]:
test_loss
Out[39]:
Evaluation 정리¶
In [40]:
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader :
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.argmax(dim=1, keepdim = True)
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('Test set: Average Loss: {:.4f}, Accuracy: {}/{} ( {:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset), 100 * correct / len(test_loader.dataset)))
In [ ]:
In [ ]:
In [ ]: