- ํผ์ ํธ๋ก (Perceptron)์ด๋?
- ๋ค์ธตํผ์ ํธ๋ก (Multi Layer Perceptron)์ด๋?
- Pytorch๋ก Multi Layer Perceptron ๊ตฌํํด๋ณด๊ธฐ
๋ด๋ฐ์ด ํ๋๋ฟ์ธ ๊ฐ์ฅ ๊ฐ๋จํ ํํ์ ์ ๊ฒฝ๋ง์ด๋ค. ์ธ๊ฐ์ ๋ด๋ฐ๊ณผ ๋น์ทํ๊ฒ ์๋ํ๋๋ก ๊ณ ์๋ ๋ฐฉ๋ฒ์ด๋ค.
๐ ํผ์
ํธ๋ก ์ ์ธ๊ฐ์ ๋ด๋ฐ๊ณผ ๋น์ทํ๊ฒ ์๋ํ๋ค๊ณ ํ์๋๋ฐ ์ธ๊ฐ์ ๋ด๋ฐ์ ์ด๋ค ๋ฐฉ์์ผ๋ก ์๋ํ ๊น?
๐
ฐ ์๋ฌผํ์ ๋ด๋ฐ์ ์ฌ๋ฌ ๊ฐ์ ์์๋๊ธฐ๊ฐ ์๋ก ๋ค๋ฅธ ์ธ๊ธฐ์ ์ ๊ธฐ์ ์ ํธ๋ฅผ ๋ฐ๊ณ ์ด ์ ํธ ์ธ๊ธฐ์ ํฉ์ด ์ ํด์ง ์๊ณ๊ฐ์ ๋์ผ๋ฉด ์๋
์ค๋ฅผ ํตํด ์ถ๋ ฅ ์ ํธ๋ฅผ ๋ณธ๋ธ๋ค.
๐ ํผ์
ํธ๋ก ์ ์ด๋ป๊ฒ ์ธ๊ฐ์ ๋ด๋ฐ์ ๋ชจ๋ฐฉํ์์๊น?
๐
ฐ ํผ์
ํธ๋ก ์ ์ ์ฒด ์
๋ ฅ ์ ํธ ์ธ๊ธฐ์ ํฉ์ ๊ตฌํ๋ ์ ํ๊ฒฐํฉ(linear combination)๊ณผ ์
๋ ฅ์ ์ ํธ ์ธ๊ธฐ์ ํฉ์ด ์๊ณ๊ฐ์ ์ด๊ณผํ ๋๋ง ์ถ๋ ฅ ์ ํธ๋ฅผ ๋ณด๋ด๋ ํ์ฑํ ํจ์(activate function)๋ฅผ ํตํด ์ธ๊ฐ์ ๋ด๋ฐ์ ๋ชจํํํ๋ค.
๐ ์ ํ๊ฒฐํฉ(linear combination)์ด๋?
์
๋ ฅ๊ฐ์ ๊ฐ๊ฐ ๊ฐ์ค์น๋ฅผ ๊ณฑํ ๊ฐ๋ค์ ํฉ์ bias(ํธํฅ)์ ๋ํ ๊ฐ์ผ๋ก ์ ์๋๋ค.
์ด๋ ํธํฅ์ ๋ํด์ฃผ๋ ์ด์ ๋ ๊ฐ์คํฉ์ ํจ์๋ก ์ ์ํ๋ฉด ํธํฅ์ y์ ํธ์ผ๋ก ๋ณผ ์ ์์ผ๋ฉฐ ํธํฅ์ ์กฐ์ ํด์ ๋ฐ์ดํฐ์ ๋ํ ์์ธก์ด ๋์ฑ ์ ํํ๋๋ก ์ง์ ์ ์์น๋ฅผ ์์๋๋ก ์กฐ์ ํ ์ ์๋ค.
์ฆ, ํธํฅ์ด ์๋ค๋ฉด ์ง์ ์ ํญ์ ์์ ์ ์ง๋๋ฏ๋ก ๊ทธ๋งํผ ๋ถ์ ํํ ์์ธก ๊ฒฐ๊ณผ๋ฅผ ์ป๊ฒ ๋๋ค
๐ ํ์ฑํ ํจ์(activate function)์ด๋?
๐ ํผ์ ํธ๋ก ์ ํ์ต ๋ฐฉ๋ฒ
๐ ํ๋์ ๋ด๋ฐ์ผ๋ก ๊ตฌ์ฑ๋ ํผ์
ํธ๋ก ์ ์์๋ดค๋๋ฐ ์ธ์์ ๋ณต์กํ ๋ฌธ์ ๋ค์ ํ๋์ ๋ด๋ฐ๋ง์ผ๋ก ํด๊ฒฐ๊ฐ๋ฅํ ๊น?
๐
ฐ ํผ์
ํธ๋ก ์ ๊ฒฐ๊ตญ ์ ํํจ์๋ก ํ์ต๋ ๋ด๋ จ์ด ๋ฐ์ดํฐ๋ฅผ ๋๋๋ ๊ฒฝ๊ณ๊ฐ ์ง์ ์ผ๋ก ๋ณต์กํ ๋ฌธ์ ๋ค์ธ ๋น์ ํ ๋ฐ์ดํฐ๋ฅผ ์ ํํ๊ฒ ๋ถ๋ฆฌํ ์ ์๋ค. ๋ฐ๋ผ์ ํ๋์ ๋ด๋ฐ๋ณด๋จ ์ฌ๋ฌ๊ฐ์ ๋ด๋ฐ์ ์ถ๊ฐํด์ผ ๋น์ ํ๋ฐ์ดํฐ์ ๋ ์ ํฉํ ๊ฒฝ๊ณ๋ฅผ ์ป์ ์ ์๋ค.
๋ค์ธตํผ์ ํธ๋ก (Multi Layer Perceptron)์ ํผ์ ํธ๋ก ์ ์ด๋ฃจ์ด์ง ์ธต(Layer) ์ฌ๋ฌ ๊ฐ๋ฅผ ์์ฐจ์ ์ผ๋ก ๋ถ์ฌ ๋์ ํํ ์ด๋ค.
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
%matplotlib inline # ์
์ ๊ทธ๋ํ ์ถ๋ ฅํ๋๋ก ์ค์
%config InlineBackend.figure_format='retina' # ๋ ํฐ๋ ์ค์ - ํฐํธ ์ฃผ๋ณ์ด ํ๋ฆฟํ๊ฒ ๋ณด์ด๋ ๊ฒ์ ๋ฐฉ์งํด ๊ธ์จ๊ฐ ์ข ๋ ์ ๋ช
ํ๊ฒ ๋ณด์
print ("PyTorch version:[%s]."%(torch.__version__)) # ํ ์น ๋ฒ์ ํ์ธ
# device์ ์ผ๋ฐ GPU or M1 GPU or CPU๋ฅผ ํ ๋นํด์ฃผ๋ ์ฝ๋
if torch.cuda.is_available() : # ์ผ๋ฐ GPU ์ฌ์ฉ์
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
elif torch.backends.mps.is_available(): # ๋งฅ M1 GPU ์ฌ์ฉ์
device = torch.device('mps:0' if torch.backends.mps.is_available() else 'cpu')
else:
device = torch.device('cpu')
print ("device:[%s]."%(device))
'''
<์
์ถ๋ ฅ>
PyTorch version:[1.12.1].
device:[mps:0].
'''
# MNIST ๋ฐ์ดํฐ์
๋ค์ด๋ก๋
from torchvision import datasets,transforms
mnist_train = datasets.MNIST(root='./data/',train=True,transform=transforms.ToTensor(),download=True)
mnist_test = datasets.MNIST(root='./data/',train=False,transform=transforms.ToTensor(),download=True)
# DataLoader ์์ฑ
BATCH_SIZE = 256
train_iter = torch.utils.data.DataLoader(mnist_train,batch_size=BATCH_SIZE,shuffle=True,num_workers=1)
test_iter = torch.utils.data.DataLoader(mnist_test,batch_size=BATCH_SIZE,shuffle=True,num_workers=1)
class MultiLayerPerceptronClass(nn.Module):
"""
Multilayer Perceptron (MLP) Class - nn.Module์ ์์ํ๋ ํด๋์ค์
__init__ : ๋ณ์ ์ด๊ธฐํ
name : ๋ชจ๋ธ๋ช
xdim : input ๋ฐ์ดํฐ ํฌ๊ธฐ
hdim : ํ๋ ๋ ์ด์ด ํฌ๊ธฐ
ydim : output ๋ฐ์ดํฐ ํฌ๊ธฐ
lin_1 : input - hidden1 ์ ํ๋ณํ
lin_2 : hidden1 - output ์ ํ๋ณํ
init_param : ํ๋ผ๋ฏธํฐ ์ด๊ธฐํ
init_param : ํ๋ผ๋ฏธํฐ ์ด๊ธฐํ
nn.init.kaiming_normal_(weight) : ๊ฐ์ค์น ํ
์์ ์ ๊ท๋ถํฌ N(0, std^2) ๋ฅผ ๋ฐ๋ฅด๋ He ์ด๊ธฐํ๋ฅผ ์คํํจ
nn.init.zeros_(bia) : ํธํฅ ํ
์์ ์ค์นผ๋ผ 0์ผ๋ก ์ฑ์
forward : ์์ ํ ์คํ
input - ์ ํ๋ณํ1 - ํ์ฑํํจ์(๋ ๋ฃจ) - ์ ํ๋ณํ2 - output
"""
def __init__(self,name='mlp',xdim=784,hdim=256,ydim=10):
super(MultiLayerPerceptronClass,self).__init__()
self.name = name
self.xdim = xdim
self.hdim = hdim
self.ydim = ydim
self.lin_1 = nn.Linear(self.xdim, self.hdim)
self.lin_2 = nn.Linear(self.hdim, self.ydim)
self.init_param() # initialize parameters
def init_param(self):
nn.init.kaiming_normal_(self.lin_1.weight)
nn.init.zeros_(self.lin_1.bias)
nn.init.kaiming_normal_(self.lin_2.weight)
nn.init.zeros_(self.lin_2.bias)
def forward(self,x):
net = x
net = self.lin_1(net)
net = F.relu(net)
net = self.lin_2(net)
return net
M = MultiLayerPerceptronClass(name='mlp',xdim=784,hdim=256,ydim=10).to(device)
loss = nn.CrossEntropyLoss() # ๊ต์ฐจ ์ํธ๋กํผ ์์ค
optm = optim.Adam(M.parameters(),lr=1e-3) # ์ตํฐ๋ง์ด์ : ์๋ด, ํ์ต๋ฅ 1e-3
def func_eval(model,data_iter,device):
'''
model
๋ชจ๋ธ ๋ณ์ ์ง์
data_iter
torch.utils.data.DataLoader๋ก ์ง์ ๋ ๋ณ์ ์ง์
device
GPU or CPU ์ง์
'''
with torch.no_grad():
# with torch.no_grad()์ ์ฃผ๋ ๋ชฉ์ ์ autograd์ ๋์ผ๋ก์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ค์ด๊ณ ์ฐ์ฐ์๋ ํฅ์์ํด
model.eval() # evaluate (affects DropOut and BN)
n_total,n_correct = 0,0
for batch_in,batch_out in data_iter:
# ๋ฐ์ดํฐ์
์ batch_size ๊ฐฏ์๋งํผ feed -> X.shape : (256,1,28,28), Y.shape : (256)
y_trgt = batch_out.to(device)
model_pred = model(batch_in.view(-1,28*28).to(device))
# model(x๊ฐ) (256, 1*28*28)์ผ๋ก ๋ณํ, 1,28,28์ธ ๋ฐ์ดํฐ๋ฅผ ํ์ค๋ก ํผ์น๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค
_,y_pred = torch.max(model_pred.data,1)
# ์์ธก๊ฐ ์ค ๊ฐ์ฅ ๋์ ๊ฐ 1๊ฐ๋ฅผ ๋ฐํ
n_correct += (y_trgt == y_pred).sum().item()
# ์ ๋ต๊ณผ ์์ธก๊ฐ์ด ๊ฒฝ์ฐ๋ง ์นด์ดํธ
n_total += batch_in.size(0)# feed๋ ๋ฐ์ดํฐ ์ ์นด์ดํธ
val_accr = (n_correct/n_total) # ์ ํ๋ : ์ผ์น๊ฐ ์ / feed๋ ๋ฐ์ดํฐ ์
model.train() # back to train mode -> ์ญ์ ํ์คํ
return val_accr
# batch_in.view(-1,1*28*28) ์์
num = [1,2,3,4,5,6,7,8]
tensor = torch.tensor(num)
a = tensor.view(-1,1,2,2) # a.shape : (2, 1, 2 ,2)
print('๋ณํ ์ ')
print(a)
print('๋ณํ ํ')
a.view(-1,2*2*1)
'''
<์ถ๋ ฅ>
๋ณํ ์
tensor([[[[1, 2],
[3, 4]]],
[[[5, 6],
[7, 8]]]])
๋ณํ ํ
tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
'''
print ("Start training.")
M.init_param() # initialize parameters
M.train()
EPOCHS,print_every = 10,1 # ํ์ต ํ์, ์ถ๋ ฅ ์กฐ๊ฑด
for epoch in range(EPOCHS):
loss_val_sum = 0
for batch_in,batch_out in train_iter:
# batch_in - X, batch_out - Y
# Forward path
y_pred = M.forward(batch_in.view(-1, 28*28).to(device)) # ์์ธก๊ฐ ์ถ์ถ
loss_out = loss(y_pred,batch_out.to(device)) #์ ๋ต๊ฐ๊ณผ ์์ธก๊ฐ์ loss ๊ณ์ฐ
# Update - backward path
optm.zero_grad() # reset gradient - ์๋ก ๊ณ์ฐํ ๋ฏธ๋ถ๊ฐ์ ๋ฃ์ด์ฃผ๊ธฐ ์ ์ ๊ธฐ์กด์ ๊ตฌํ ๋ฏธ๋ถ๊ฐ์ reset
loss_out.backward() # backpropagate - ๋ฏธ๋ถ๊ฐ ๊ณ์ฐ
optm.step() # optimizer update - ๊ตฌํด์ง ๋ฏธ๋ถ๊ฐ์ผ๋ก ๊ฐ์ค์น ์
๋ฐ์ดํธ
# loss ์ ์ฅ
loss_val_sum += loss_out
loss_val_avg = loss_val_sum/len(train_iter)
# print_every ๋จ์๋ก loss์ ์ ํ๋ Print
if ((epoch%print_every)==0) or (epoch==(EPOCHS-1)):
train_accr = func_eval(M,train_iter,device)
test_accr = func_eval(M,test_iter,device)
print ("epoch:[%d] loss:[%.3f] train_accr:[%.3f] test_accr:[%.3f]."%
(epoch,loss_val_avg,train_accr,test_accr))
print ("Done")
'''
<์ถ๋ ฅ>
Start training.
epoch:[0] loss:[0.387] train_accr:[0.941] test_accr:[0.941].
epoch:[1] loss:[0.173] train_accr:[0.963] test_accr:[0.960].
epoch:[2] loss:[0.123] train_accr:[0.972] test_accr:[0.967].
epoch:[3] loss:[0.094] train_accr:[0.979] test_accr:[0.971].
epoch:[4] loss:[0.076] train_accr:[0.983] test_accr:[0.974].
epoch:[5] loss:[0.062] train_accr:[0.987] test_accr:[0.976].
epoch:[6] loss:[0.051] train_accr:[0.989] test_accr:[0.978].
epoch:[7] loss:[0.042] train_accr:[0.991] test_accr:[0.978].
epoch:[8] loss:[0.035] train_accr:[0.993] test_accr:[0.979].
epoch:[9] loss:[0.030] train_accr:[0.995] test_accr:[0.979].
Done
'''
# test ๋ฐ์ดํฐ์
์์ ๋๋ค์ผ๋ก 25๊ฐ๋ฅผ ์ถ์ถ
n_sample = 25
sample_indices = np.random.choice(len(mnist_test.targets), n_sample, replace=False)
test_x = mnist_test.data[sample_indices]
test_y = mnist_test.targets[sample_indices]
# ๋๋ค์ถ์ถํ test ๋ฐ์ดํฐ์
์์ธก๊ฐ ์ถ์ถ
with torch.no_grad():
y_pred = M.forward(test_x.view(-1, 28*28).type(torch.float).to(device)/255.)
y_pred = y_pred.argmax(axis=1)
# ์ ๋ต๊ฐ,์์ธก๊ฐ,์ด๋ฏธ์ง ์๊ฐํ
plt.figure(figsize=(10,10))
for idx in range(n_sample):
plt.subplot(5, 5, idx+1)
plt.imshow(test_x[idx], cmap='gray')
plt.axis('off')
plt.title("Pred:%d, Label:%d"%(y_pred[idx],test_y[idx]))
plt.show()
print ("Done")