본문 바로가기

Programming Project/Pytorch Tutorials

Pytorch 머신러닝 튜토리얼 강의 7 (Wide and Deep)


투명한 기부를 하고싶다면 이 링크로 와보세요! 🥰 (클릭!)

바이낸스(₿) 수수료 평생 20% 할인받는 링크로 가입하기! 🔥 (클릭!)

2018/07/02 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 1 (Overview)

2018/07/02 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 2 (Linear Model)

2018/07/03 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 3 (Gradient Descent)

2018/07/03 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 4 (Back-propagation and Autograd)

2018/07/07 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 5 (Linear Regression in the PyTorch way)

2018/07/09 - [Programming Project/Pytorch Tutorials] - Pytorch 머신러닝 튜토리얼 강의 6 (Logistic Regression)





이번 글에서는 어떻게 하면 넓고 깊은 neural network를 만들 수 있는지에 대해서 다뤄 볼 것입니다.



이전 글에서 우리는 linear modelsigmoid를 포함하여 logistic regression 을 구현하는 방법에 대해서 알아 보았습니다.


한개의 x -> linear -> Sigmoid를 통과시킨 결과를 가지고 cross entropy loss를 minimize해주는 방식으로 구했었습니다.


하지만 만약, 한개의 x 데이터가 아닌 두개 이상의 데이터동시에 입력으로 받아서, output을 구하는 경우에는 어떻게 식을 구성할 수 있을까요?






Matrix Multiplication


간단하게, 행렬식으로 수식들을 표현함으로써 우리는 이 문제를 해결할 수 있습니다.


예를 들어, data가 총 4개이고, 각 데이터마다 2개의 input, 1개의 output일 경우,


아래의 그림과 같이 4x2의 input 데이터 행렬과 4x1 output데이터 행렬로 표현할 수 있습니다.






그렇다면 우리가 만들 모델의 경우에는 어떻게 하면 될까요?


저 그림의 물음표가 찍혀져 있는 w에 들어갈 행렬을 찾으면 됩니다.


이것이 우리의 기존 linear model부분의 역할을 하게 될 것입니다.



어떤 행렬이 w에 들어가야 할지 감이 잡히시나요?



우리가 쓸 행렬은

바로 이 행렬입니다.


a , b에 각각 대응되는 w1 , w2를 행렬곱을 할 수 있도록 2x1 행렬로 만들어 줍니다.


(행렬식으로 Nx2 와 2x1을 곱하면 Nx1 행렬이 나와 행렬 y의 모양과 같아지죠)







조금 더 일반화 해서 생각하면, input이 단순히 2개가 아니라 여러개일 때도 a b 대신 x1 x2 x3... 이런 식으로 넘버링을 붙여서 행렬 식을 만들면


XW = Y라는 식의 행렬 곱 연산으로 표현할 수 있게 됩니다.







그렇다면 2개의 input을 받아서 1개의 output을 내보내는 코드는 어떻게 될까요?


바로 다음과 같습니다.




저 Linear(2,1)의 의미는 2x1짜리 행렬이라는 의미인데요, 


따라서 , 2개의 input으로 1개의 output을 내보내는 linear layer이라고도 할 수 있겠습니다.


(아마 배경지식이 없으신 분들은 여기서 많이 헤메실 텐데요, 직접 행렬이 곱해지는 과정을 그려보시면 조금 편하리라고 생각됩니다.)






그렇다면 기존의 모델을 더 Wide하게 (넓게) 펼쳐볼 수 있겠죠?


다른 말로는, 더 많은 input을 통해 output을 뽑아낼 수 있다는 뜻입니다.


위 그림과 같이 x1,x2...xn으로 표시하여 임의의 n개의 input을 받는 linear model을 생각할 수 있을 것입니다.




그렇다면 단순히 넓어지는 것을 넘어서, 우리가 1차적으로 만든 output으로 정답을 내는 것이 아니라, 


output을 input으로 받는 또 다른 linear model을 만들어서 이어 붙이면 어떻게 될까요?







위의 그림과 같이 세 개의 linear model을 이어 붙여서 하나의 모델을 만들 수도 있습니다.


이제, Model이라는 용어가 중복되니까, 전체적인것을 모델이라고 하고 저 세개는 각각 하나의 Layer이라고 부르기로 합니다.




일단 l1 l2 l3에 각각 linear model을 정의 해 줍시다.


그리고 out1 에는 l1의 결과를 sigmoid에 통과시킨 결과를 저장하고,


out2에는 out1을 l2에 통과시킨 결과를 sigmoid에 통과시킨 결과를 저장하고


마지막으로 out2를 통해 l3 , sigmoid를 통과시켜 우리의 결과를 만들어내는 복잡한 계산식을 구현할 수 있게 되는것이죠!


주의해야 할 부분이 있습니다.


서로 이어져 있는 Linear layer의 input size와  output size가 일치한다는 것입니다.


직전 layer의 output dimension이 그 직후 layer의 input size가 됩니다.







여기서 중요하게 눈여겨 봐야 할 것중 하나는 맨 처음 l1의 input은 여전히 2차원이고, l3의 output또한 여전히 1이라는 것입니다.






우리는 지금까지 Sigmoid Activation Function을 가지고 학습을 진행했습니다.



하지만 Sigmoid로 너무 깊은 layer을 쌓게 되면 vanishing gradient problem이 나타납니다.




Vanishing Gradient Problem



vanishing gradient problem이란, sigmoid의 미분값이 0~1 사이이기 때문에 무한히 곱하게 되면 0에 근사해져 가는 것을 의미합니다.


0~1 사이의 수를 상당히 많은 횟수를 거듭하여 곱하게 되면 0에 근접하는 것을 알 수 있으실겁니다.




그리고 이를 해결하기 위해서 고려할 수 있는 한가지 방법은 다른 Activation Function을 쓰는 것입니다. 


https://dashee87.github.io/data%20science/deep%20learning/visualising-activation-functions-in-neural-networks/



여러 Activation Function들 중 Relu 등이 흔하게 쓰이곤 합니다.






In Code


그렇다면 이제 프로그래밍을 해보도록 합시다.


이번에 작성하게 될 코드는 흥미롭게도, 8가지 수치를 주고 이 환자가 당뇨 환자인지 아닌지를 맞춰보는 과제입니다.


이 과제는 단순히 이전처럼 linear model 하나만을 가지고는 정확하게 예측하기가 비교적 어렵습니다.


https://github.com/hunkim/PyTorchZeroToAll


이 깃허브의 /data 폴더를 복사해서 가져온 후 , 본인의 파이썬 코드와 같은 폴더에 넣고 진행해보도록 합시다.



import torch 
from torch.autograd import Variable
import numpy as np 
xy = np.loadtxt('./data/diabetes.csv.gz', delimiter=',', dtype=np.float32) 
x_data = torch.from_numpy(xy[:, 0:-1]) 
y_data = torch.from_numpy(xy[:, [-1]])


우선 데이터를 Load해보도록 합시다.


csv 파일을 받아오도록 합니다.


그리고 데이터를 적당히 잘라서 x_data 와 y_data에 넣어줍니다.



class MyModel(torch.nn.Module):
    
    def __init__(self):
        super(MyModel,self).__init__()
        self.l1=torch.nn.Linear(8,4)
        self.l2=torch.nn.Linear(4,6)
        self.l3=torch.nn.Linear(6,1)
        
        self.sigmoid=torch.nn.Sigmoid()
        
    def forward(self,x):
        out1 = self.sigmoid(self.l1(x))
        out2 = self.sigmoid(self.l2(out1))
        y_pred = self.sigmoid(self.l3(out2))
        return y_pred

그리고 우리가 이전에 다루었던 Model과 같이 3개의 linear layer의 코드를 짜줍시다


다만 이번의 데이터는 input dimension이 8 이기 때문에, 


l1의 시작이 ( 8 , 6 ) 임을 볼 수 있습니다.




이렇게 정의를 내리고 한번 돌려볼까요?



model = MyModel()

criterion = torch.nn.BCELoss(size_average = True)
optimizer = torch.optim.SGD(model.parameters(),lr=0.1)

for epoch in range(1000):
    y_pred = model(x_data)
    
    loss=criterion(y_pred,y_data)
    
    if(epoch%100==0):
        print("loss : ",loss)
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()




이제 제대로 나오는지 결과를 확인해 봅시다.




cnt=0
for it in range(y_data.size()[0]):
    if( (y_pred[it][0]>0.5)==y_data[it][0].type(torch.ByteTensor)):
        cnt= cnt+1
        
print(cnt)
print("Accuracy : ",cnt*100/y_data.size()[0])


이 평가 코드는 제가 급히 만든 코드라서(...) 조금 더러울 수 있는데요.


여튼 말하자면 0.5이상은 1이라고 예측하고 0.5이하는 0이라고 예측했습니다.


그리고 결과를 비교하니 65%정도의 Accuracy가 나오네요!





숙제


3 layer 이상 (뭐 .. 한 10개 이상도 도전?!) 레이어 쌓아보기.


다른 activation functions 사용 해 보기.


등을 통해서 모델을 변형시켜 보시고, 결과나 Accuracy가 어떻게 달라지는지 보시죠!