* 모든 코드는 제 깃허브 (cdjs1432/DeepLearningBasic: Deep Learning from scratch)에서 확인할 수 있습니다.

 

원래는 바로 Optimizer를 제작하려 했으나, 생각해 보니 코드가 이래서는 바로 들어가기는 좀 그럴 것 같습니다.

 

따라서 이번에는 코드를 대충 정리를 하고 "예쁘게" 만들어 보겠습니다.

(이론 설명같은 건 없다는 뜻)

그냥 어떤 부분이 바뀌었는지, 어떻게, 왜 바꾸었는지 간결하게만 설명하고 넘어가겠습니다.

 

 

import numpy as np
import pandas as pd
from Model import Model
import Layers

# load data
train = pd.read_csv("../Data/MNIST_data/mnist_train.csv")
y_train = train["label"]
x_train = train.drop("label", 1)
x_train = x_train.values / x_train.values.max()
y_train = y_train.values

# y to one-hot
one_hot = np.zeros((y_train.shape[0], y_train.max() + 1))
one_hot[np.arange(y_train.shape[0]), y_train] = 1
y_train = one_hot

# initialize parameters
num_classes = y_train.shape[1]
hidden = 50

 

일단 ModelTest.py (테스트용 파일)을 설명하겠습니다.

저번과 아주 동일한 훈련 방식을 거칠 것이니, 데이터도 그대로 가져옵니다. (복붙)

model = Model()
model.addlayer(Layers.MulLayer(), input_size=(784, 32), name="w1")
model.addlayer(Layers.AddLayer(), input_size=32, name='b1')
model.addlayer(Layers.SigmoidLayer(), activation=True, name='sigmoid1')
model.addlayer(Layers.MulLayer(), input_size=(32, 10), name="w2")
model.addlayer(Layers.AddLayer(), input_size=10, name='b2')
model.addlayer(Layers.SoftmaxLayer(), activation=True, name='softmax')

model.train(x_train, y_train, 10000, 0.01, 128)

달라진 부분은 이 부분입니다.

직접 데이터를 하나하나 넣어주는 것이 아니라, Model이라는 모듈을 만들어서 예쁘게 만들어 주자는 것이 주요 내용입니다.

import numpy as np
from Layers import *


class Model:
    def __init__(self):
        self.params = {}
        self.grads = {}
        self.keys = []
        self.layers = {}
        self.num = 0
        self.loss = None
        self.pred = None

    def addlayer(self, layer, activation=False, input_size=None, name=None):
        if name is None:
            name = str(self.num)

        self.keys.append(name)
        self.num += 1
        self.layers[name] = layer

        if not activation:
            self.params[name] = np.random.uniform(-1, 1, input_size)
            self.layers[name].param = self.params[name]

    def predict(self, x, y):
        for i in range(len(self.keys) - 1):
            key = self.keys[i]
            x = self.layers[key].forward(x)
        self.loss = self.layers[self.keys[-1]].forward(x, y)
        self.pred = softmax(x)

    def train(self, x_train, y_train, optimizer, epoch, learning_rate, batch_size):
        for epochs in range(epoch):
            batch_mask = np.random.choice(x_train.shape[0], batch_size)
            x = x_train[batch_mask]
            y = y_train[batch_mask]

            self.predict(x, y)
            dout = self.layers[self.keys[-1]].backward()
            for i in reversed(range(len(self.keys) - 1)):
                key = self.keys[i]
                dout = self.layers[key].backward(dout)
                if key in self.params:
                    self.grads[key] = self.layers[key].grad
                    self.params[key] -= learning_rate * self.grads[key]

            if epochs % (epoch / 10) == 0:
                print("ACC on epoch %d : " % epochs, (self.pred.argmax(1) == y.argmax(1)).mean())
                print("LOSS on epoch %d : " % epochs, self.loss)

 Model.py 부분입니다.

원래 Main에 있었던 grads, params 등등을 모두 이 Model 안에 넣었습니다.

그 뒤, addlayer로 Layer을 모델에 각각 추가하는 방식으로 모델의 레이어가 구현되도록 하였습니다.

이 때, activation 여부를 확인해서 activation function인 경우에는 grads와 params가 존재하지 않게 막아주었습니다.

(저번 코드의 if 'Relu'... 부분을 바꾼 것입니다.)

 

predict는 원래 train하는 과정에서의 forward 하는 과정을 따 온 함수입니다.

원래는 train 안에 그대로 넣고자 하였으나, model에서 직접 predict를 해야 할 일이 있을 것 같아서 따로 뺐습니다.

 

그리고 train 부분은 원래 훈련시키는 부분과 매우 유사합니다.

차이점은, lastlayer를 따로 빼지 않는 대싱 반복문을 range(len(self.keys)-1)로 바꾸어서, 마지막 layer은 따로 빼서 연산하도록 했습니다.

 

기반은 SGD로 구현하였으나, 다음 차시에 바로 train을 Optimizer 모듈에 구현하게 될 것 같습니다.

그래서 다음 차시에는, SGD 말고도 RMSPROP과 같은 다른 훈련 방식을 사용해 보도록 하겠습니다.

class MulLayer:
    def __init__(self, param=None):
        self.x = None
        self.param = param
        self.grad = None

    def forward(self, x):
        self.x = x
        return x.dot(self.param)

    def backward(self, dout):
        self.grad = np.dot(self.x.T, dout)
        return np.dot(dout, self.param.T)


class AddLayer:
    def __init__(self, param=None):
        self.x = None
        self.param = param
        self.grad = None

    def forward(self, x):
        self.x = x
        return x + self.param

    def backward(self, dout):
        self.grad = dout.mean()
        return dout

 

이 부분은 Layers.py 부분입니다.

MulLayer와 AddLayer에서 w, b를 그냥 param으로 통일하고, __init__시에 직접 parameter를 집어넣지 않아도 되게 만들었습니다.

param으로 이름을 통일한 것은 반복문에서의 train때의 편의성을 위함이고, __init__시에 직접 param을 집어넣지 않아도 되게 만든 것은 Model에서의 AddLayer를 만들기 위함입니다.

(위에서 보시면 아시겠지만, model.addlayer()부분에서 MulLayer과 AddLayer에 따로 params를 집어넣지 않고, Input_size만을 넣어서  자동으로 랜덤 값이 들어가도록 했습니다.)

 

 

이렇게 코드를 갈아엎어 보았습니다.

다음 시간에는 드디어, Optimizer를 구현해 보겠습니다.

+ Recent posts