딥러닝

[Pytorch] 간단한 신경망 구현

jschang 2020. 3. 16. 00:13

Pytorch 공식 튜토리얼 중 신경망 관련 내용에 대한 저의 코드입니다.

Medium https://medium.com/dair-ai/a-simple-neural-network-from-scratch-with-pytorch-and-google-colab-c7f3830618e0 예제에서는 직접 설정한 도함수를 이용해 BackPropagation을 실행하지만 저는 Pytorch의 backwatd 함수를 사용하였습니다.

 

본 예제에서 만드는 딥러닝 모델은 다음과 같은 간단한 모델입니다.

모델 구조

보시는 바와 같이 1개의 Hidden Layer로 구성된 매우 간단한 신경망입니다.

본 예제에서는 1x2 크기의 벡터를 입력으로 하고 1x1 텐서 (스칼라)를 출력으로 합니다.

 

먼저 학습 데이터입니다. X 텐서에 학습 데이터와 각각에 대응되는 결과값을 y 텐서에 넣었습니다. xPredicted는 실행시키고자 하는 데이터입니다.

X = torch.tensor(([7, 1], [5, 2], [6, 4], [7, 6], [8, 9]), dtype=torch.float)
y = torch.tensor(([23], [40], [60], [62], [90]), dtype=torch.float)
xPredicted = torch.tensor(([5, 5]), dtype=torch.float)

다음으로는 X텐서와 y텐서에 값들을 스케일링하겠습니다.

X_max, _ = torch.max(X, 0)
xPredicted_max, _ = torch.max(xPredicted, 0)

X = torch.div(X, X_max)
xPredicted = torch.div(xPredicted, xPredicted_max)
y = torch.div(y, torch.tensor(([100]), dtype=torch.float))

y의 경우는 최대를 100으로 설정하였습니다.

 

다음으로는 핵심인 신경망입니다.

import torch.nn as nn

class NeuralNetwork(nn.Module):
    def __init__(self, ):
        self.inputSize = 2
        self.outputSize = 1
        self.hiddenSize = 3
        
        self.w1 = torch.randn(self.inputSize, self.hiddenSize, requires_grad=True)
        self.w2 = torch.randn(self.hiddenSize, self.outputSize, requires_grad=True)
        
        self.learning_rate = 0.001
        
    def forward(self, X):
        self.z1 = torch.matmul(X, self.w1)
        self.z2 = torch.sigmoid(self.z1)
        self.z3 = torch.matmul(self.z2, self.w2)
        out = torch.sigmoid(self.z3)
        return out
        
    def train(self, X, y):
        out = self.forward(X)
        error = ((y-out)**2).mean()
        error.backward()
        self.w1.data += self.learning_rate*(self.w1).grad
        self.w2.data += self.learning_rate*(self.w2).grad
        
    def saveWeights(self, model):
        torch.save(model, "NerualNetwork")
    
    def predict(self):
        print("Input Data: ", str(xPredicted))
        print("Predicted: ", str(self.forward(xPredicted)))

Pytorch의 nn 을 사용하여 구현하였으며 hidden layer는 한 개이므로 두 개의 weight을 사용하였습니다.

이때 학습 과정에서는 단순화를 위해 bias는 사용하지 않았으며 각각의 wieght를 조정하여 학습하기 위해 requires_grad=True으로 설정하였습니다. train 과정에서는 에러를 계산하고 이 에러를 편미분 하여 각각의 weight에 대한 변화율을 계산합니다. 이때 self.w1.data와 같이 설정한 것은 self.w1으로 설정할 경우 runtime error가 나기 때문입니다. 이는 leaf variable을 replace 할 경우 생기는 문제입니다. 

 

결과를 확인하기 위한 과정은 다음과 같습니다.

NN = NeuralNetwork()
trainIdx = 1000
for idx in range(trainIdx):
    if idx%100==0:
        print("#"+str(idx)+" Loss: "+str(torch.mean((y-NN.forward(X))**2).detach().item()))
    NN.train(X, y)
    
NN.saveWeights(NN)
NN.predict()
#0 Loss: 0.07525341957807541
#100 Loss: 0.08975683152675629
#200 Loss: 0.15257836878299713
#300 Loss: 0.2301979959011078
#400 Loss: 0.2512035667896271
#500 Loss: 0.25329679250717163
#600 Loss: 0.25344881415367126
#700 Loss: 0.2534591853618622
#800 Loss: 0.25345999002456665
#900 Loss: 0.25345999002456665
Input Data:  tensor([1., 1.])
Predicted:  tensor([1.], grad_fn=<SigmoidBackward>)

원하는 결과를 얻은 것을 볼 수 있습니다.