In [1]:
# %load /home/sjkim/.jupyter/head.py
%matplotlib inline
%load_ext autoreload
%autoreload 2
from importlib import reload
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import os
#os.environ["CUDA_VISIBLE_DEVICES"]="0"
# seaborn
#import seaborn as sns
#sns.set( style = 'white', font_scale = 1.7)
#sns.set_style('ticks')
#plt.rcParams['savefig.dpi'] = 200
# font for matplotlib
#import matplotlib
#import matplotlib.font_manager as fm
#fm.get_fontconfig_fonts()
#font_location = '/usr/share/fonts/truetype/nanum/NanumGothicBold.ttf'
#font_name = fm.FontProperties(fname=font_location).get_name()
#matplotlib.rc('font', family=font_name)
In [2]:
##############################################
# Plotting
##############################################
import matplotlib.pyplot as plt
def plot_acc(history, title=None):
# summarize history for accuracy
if not isinstance(history, dict):
history = history.history
plt.plot(history['accuracy'])
plt.plot(history['val_accuracy'])
if title is not None:
plt.title(title)
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Training', 'Verification'], loc=0)
# plt.show()
def plot_loss(history, title=None):
# summarize history for loss
if not isinstance(history, dict):
history = history.history
plt.plot(history['loss'])
plt.plot(history['val_loss'])
if title is not None:
plt.title(title)
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Training', 'Verification'], loc=0)
# plt.show()
In [9]:
class History:
def __init__(self):
self.history = {'accuracy': [], 'loss': [], 'val_accuracy': [], 'val_loss': []}
In [10]:
class Metrics_Mean:
def __init__(self): self.reset_states()
def __call__(self, loss): self.buff.append(loss.data)
def reset_states(self): self.buff = []
def result(self): return np.mean(self.buff)
class Metrics_CategoricalAccuracy:
def __init__(self):
self.reset_states()
def __call__(self, labels, predictions):
decisions = predictions.data.max(1)[1]
self.correct += decisions.eq(labels.data).cpu().sum()
self.L += len(labels.data)
def reset_states(self):
self.correct, self.L = 0, 0
def result(self): return float(self.correct) / self.L
이번에는 케라스가 기본으로 내장된 Tensorflow 2.0을 이용해 작성한 코드를 PyTorch로 바꾸어 보았다.
모델을 만들기 위해 torch, torch.nn 그리고 torch.nn.functional을 임포트한다.
그리고 모델링을 위한 클래스를 만들 때는 nn.Module을 상속한다. Dense 계층을 만들기 위해서는 nn.Linear()를 활용한다. 케라스의 Dense()와 차이는 각 계층의 입력과 출력의 노드 수를 모두 적어야 한다는 점이다. 케라스는 출력만 적고 입력의 길이는 이전 계층에서 자동으로 계산하여 가져온다. 케라스의 모델링 방식은 입력을 줄여서 편하지만 파이토치의 방식에 비해 가독성을 약간 떨어뜨린다는 말도 있다. 파이토치에서는 출력을 계산하는 함수명은 call() 대신에 forward()를 사용한다. rulu와 softmax는 torch.nn.functional 즉 F에서 가져온다. 이렇게 바꾸고 보면 용어만 일부틀리고 케라스와 상당히 유사한 형태로 구성이 가능함을 알 수 있다.
In [11]:
##############################################
# Modeling
##############################################
import torch
from torch import nn
import torch.nn.functional as F
class ANN_models_class(nn.Module):
def __init__(self, Nin, Nh, Nout):
super().__init__()
self.hidden = nn.Linear(Nin, Nh)
self.last = nn.Linear(Nh, Nout)
self.Nin = Nin
def forward(self, x):
x = x.view(-1, self.Nin)
h = F.relu(self.hidden(x))
y = F.softmax(self.last(h), dim=1)
return y
# model = ANN_models_class(784, 100, 10)
In [12]:
##############################################
# Data
##############################################
from torchvision import datasets, transforms
batch_size = 100
def Data_func():
train_dataset = datasets.MNIST('~/pytorch_data',
train=True, download=True, transform=transforms.ToTensor())
test_dataset = datasets.MNIST('~/pytorch_data',
train=False, transform=transforms.ToTensor())
train_ds = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size, shuffle=True)
test_ds = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size, shuffle=False)
return train_ds, test_ds
train_ds, test_ds = Data_func()
텐서플로 2.0에서는 tf2.GradientTape()를 활용해 학습에 필요한 미분값(gradients)을 구하지만 파이토치에서는 Optimizer.zero_grad()만으로 학습을 시작할 수 있다. 변수가 미분이 가능한 모드로 되어 있을 경우, loss.backward() 함수를 통해 자동으로 미분값을 구할 수 있게 되어 있기 때문이다. 그리고 미분한 결과로 가중치들과 바이어스값들을 업데이트할 때 대상을 명시할 필요가 없다. 이미 Optimizer를 정의할 때 업데이트 할 파라메터인 model.parameters()를 명시했기 때문이다.
In [6]:
##############################################
# Main
##############################################
Nin = 784
Nh = 100
number_of_class = 10
Nout = number_of_class
model = ANN_models_class(Nin, Nh, Nout)
# model.build(input_shape=[None,Nin])
train_ds, test_ds = Data_func()
train_loss = Metrics_Mean()
train_accuracy = Metrics_CategoricalAccuracy()
test_loss = Metrics_Mean()
test_accuracy = Metrics_CategoricalAccuracy()
history = History()
Optimizer = torch.optim.Adam(model.parameters(), lr = 0.01)
Loss_object = torch.nn.CrossEntropyLoss()
for epoch in range(5):
for images, labels in train_ds:
Optimizer.zero_grad()
predictions = model(images)
loss = Loss_object(predictions, labels)
loss.backward()
Optimizer.step()
train_loss(loss)
train_accuracy(labels, predictions)
for images, labels in test_ds:
predictions = model(images)
t_loss = Loss_object(predictions, labels)
test_loss(t_loss)
test_accuracy(labels, predictions)
history.history['accuracy'].append(train_accuracy.result())
history.history['loss'].append(train_loss.result())
history.history['val_accuracy'].append(test_accuracy.result())
history.history['val_loss'].append(test_loss.result())
template = 'Epoch {}, Loss: {:.2f}, Accuracy: {:.2f}%, Test Loss: {:.2f}, Test Accuracy: {:.2f}%'
print(template.format(epoch+1,
train_loss.result(),
train_accuracy.result()*100,
test_loss.result(),
test_accuracy.result()*100))
# Reset the metrics for the next epoch
train_loss.reset_states()
train_accuracy.reset_states()
test_loss.reset_states()
test_accuracy.reset_states()
############################################
# Training
##############################################
plot_loss(history)
plt.show()
plot_acc(history)
plt.show()