본문 바로가기
Data Science/문과생도 이해하는 딥러닝

문과생도 이해하는 딥러닝 (6) - 오차역전파법 실습 1

by 싸코 2018. 1. 5.


2017/09/27 - 문과생도 이해하는 딥러닝 (1) - 퍼셉트론 Perceptron

2017/10/18 - 문과생도 이해하는 딥러닝 (2) - 신경망 Neural Network

2017/10/25 - 문과생도 이해하는 딥러닝 (3) - 오차 역전파, 경사하강법

2017/12/24 - 문과생도 이해하는 딥러닝 (4) - 신경망구현, 활성화함수, 배치

2017/12/26 - 문과생도 이해하는 딥러닝 (5) - 신경망 학습 실습

 


지난 시간에는 구축한 신경망을 어떻게 학습해야 하는지 학습에 필요한 개념들에 대해서 다루었고 실제로 학습을 진행해보았다. 경사하강법을 이용해 수치미분으로 기울기를 계산하는 것은 계산 시간이 너무나 오래 걸려서 모델의 학습 시간이 굉장히 길었다. 학습을 할 때 사용되는 하이퍼파라미터(hyperparameter) 들에 대해서 알 수 있었으며 왜 가중치 갱신을 할 때 기울기를 구하는지, 손실함수는 왜 계산하는지, epoch의 정확한 의미는 무엇인지, 미니배치를 왜 사용하는지 등등 딥러닝의 핵심 개념들을 제대로 알 수 있었다.

 

이번에는 지난 포스팅에서 신경망을 학습하면서 수치미분으로 기울기를 계산하면서 학습시간이 굉장히 길어졌는데 이를 극복할 방법으로 오차역전파법을 살펴보고자 한다.


 


오차역전파법 실습 1

문과생도 이해하는 딥러닝 (6)

 지난 오차역전파 관련 포스팅에서는 오차역전파법이 순전파(foward propagation)로 가중치 학습이 되고 이를 갱신하기 위해서 오차를 반영하여 반대 방향에서 다시 가중치를 업데이트 한다는 식으로만 설명을 했다. 역전파를 사용하는 또 다른 중요한 이유는 역전파를 통해서 '미분'을 효율적으로 계산할 수 있다는 것이다.

 

 

먼저 순전파와 역전파 기능을 가진 클래스를 구현한다

class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None
        
    def forward(self, x, y):
        self.x = x
        self.y = y
        out = x * y
        return out
    
    def backward(self, dout):
        dx = dout * self.y
        dy = dout * self.x
        return dx, dy



순전파 방식으로 구매한 사과 가격을 구하는 예시

apple = 100
apple_num = 2
tax = 1.1

mul_apple_layer = MulLayer()
mul_tax_layer = MulLayer()

# forward propagation
apple_price = mul_apple_layer.forward(apple, apple_num)
price = mul_tax_layer.forward(apple_price, tax)

print(price)
220.00000000000003
# back propagation
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print(dapple, dapple_num, dtax)

2.2 110.00000000000001 200





본 교재에서는 아래와 같은 계산 그래프 방식으로 신경망 학습 모델을 구현하였다.

신경망의 층을 하나의 클래스로 인스턴스를 만들면서 추가하는 방식이다.


class AddLayer:
    def __init__(self):
        pass
    
    def forward(self, x, y):
        out = x + y
        return out
    
    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1
        return dx, dy






1. ReLU 오차역전파법 구현

ReLU는 활성화 함수로

x > 0 일 때, x를 그대로 반환하고,
x <= 0 일 때, 0을 반환한다.

따라서 이를 미분해주면 x -> 1 이 되므로 오차역전파에서는

x > 0 일 때, 1을 전파하고
x <= 0 일 때, 을 전파한다.

class Relu:
    def __init__(self):
        self.mask = None
        
    def forward(self, x):
        '''
        순전파 시 x <= 0 일 때 값을 0으로 치환한다
        기본적으로 x는 numpy 배열임을 가정한다
        '''
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0
        return out
    
    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout
        return dx






2. Sigmoid 오차역전파법 구현

시그모이드 함수는 미분을 하면 dout * y(1-y)로 결과가 나온다. 출력값 y만으로 오차 역전파를 계산할 수 있다.

class Sigmoid:
    def __init__(self):
        self.out = None
    
    def forward(self, x):
        out = 1 / (1 + np.exp(-x))
        self.out = out
        
        return out
    
    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out
        return dx




3. Affine/Softmax 오차역전파법 구현

Affine 계층

Affine이라 함은 순전파에서 수행하는 행렬의 내적을 기하학에서 부르는 말로 Input 값과 Weight 값들을 행렬 곱하여 계산하고 거기에 편향(Bias)를 추가하여 출력값 Y를 최종적으로 반환하는 첫 포스팅(퍼셉트론)에서 배웠던 내용에 대한 것을 생각하면 된다

# Affine 계층

X = np.random.rand(2)    # Input
W = np.random.rand(2, 3) # Weight
B = np.random.rand(3)    # Bias

print(X.shape)
print(W.shape)
print(B.shape)

Y = np.dot(X,W) + B
(2,)
(2, 3)
(3,)
class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b
        self.x = None
        self.dW = None
        self.db = None
        
    def forward(self, x):
        self.x = x
        out = np.dot(x, self.W) + self.b
        
        return out
    
    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis = 0)
        
        return dx




Softmax-with-Loss 계층

output layer에서 사용하는 소프트맥수 함수는 입력 값을 확률의 형태로 normalize하여 결과값을 출력한다.


신경망에는 1)학습, 2)추론 이 있다.


추론에는 softmax 함수 같은 활성화함수를 사용하지 않고 affine layer에서 나온 결과 값을 그대로 사용하는 것이 일반적이며 우리가 흔히 말하는 score라고 한다. 추론에는 답을 내리기만 하면 되기 때문에 다른 활성화함수 들을 거쳐 정규화할 필요가 없고 높은 점수가 무엇인지만 파악할 수 있으면 되기 때문이다.


학습에는 softmax 함수를 사용하는데 이는 정규화한 출력 값을 이용해서 모델을 다시 업데이트해야 하기 때문에 정규화가 필요하다.



Softmax with Loss는 마지막 출력 층인 소프트맥스 함수의 결과 값을 손실함수로 오차도 계산하겠다는 의미이다.



<< 활성화함수 별 적합한 손실함수 >>

    • Softmax Function ==> Cross Entropy Error
    • Identity Function ==> Mean Squared Error

활성화 함수마다 적합한 손실함수가 있어(그렇게 설계되어 있음) 오차의 역전파가 말끔하게 계산된다.



class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None   # Loss
        self.y = None      # Output
        self.t = None      # Target
        
    def foward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        return self.loss
    
    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size
        return dx








댓글