본문 바로가기
Tensorflow

[ANN] 10. 활성화함수's Back Propagation (ReLU, Sigmoid)

by 청양호박이 2020. 5. 21.

역전파(Back Propagation)를 시작하고, 그래도 나름 상세하려고 노력해본 설명과 예시를 통해서 이해를 높여보려고 했습니다. 제 노력이 전달됬는지 모르겠습니다...ㅠㅠ

 

그래도 일단 그 다음내용을 더 확인해보겠습니다. 저는 더 어려운 단계를 통해서 이전의 단계가 쉽게 느껴지고 이해가 갈 가능성이 높다고 신봉하는 사람중에 한명이라...ㅎㅎ

 

지금까지는 일반연산자를 가지고 역전파에 대해서 알아보았습니다. 하지만 사실 실제 인공신경망의 노드는 일반연산자가 일부 사용되기는 하지만 실제에 적용하기는 무리가 있습니다. 그래서 실제 활성화함수에 대해서 역전파를 알아보고 구현해 보도록 하겠습니다.

 

  • 인공신경망(ANN)의 개요 및 퍼셉트론과의 차이점
  • 인공신경망(ANN)의 활성화함수(AF, Activation Function)
  • 인공신경망(ANN)의 출력층 설계 및 순전파(Forward Propagation)
  • 인공신경망(ANN)의 손실함수(Loss) 구현
  • 수치미분 및 편미분, 그리고 GDA
  • 인공신경망(ANN)의 역전파(Back Propagation) 개념
  • 인공신경망(ANN)의 역전파(Back Propagation) 구현

 

 

1. ReLU(Rectified Linear Unit)


ReLU는 많이 사용되는 활성화함수입니다. 기존에 알아본 내용을 살짝 정리해보면, 해당 함수는

 

y = x (x > 0)

   = 0 (x <= 0) 

 

으로 0보다 큰 수는 input값을 그대로 output으로 보내고 0이하의 수는 input에 상관없이 0을 출력합니다.

 

[ReLU 코드]

import numpy as np
import matplotlib.pyplot as plt

def ReLU(x):
     return np.maximum(0, x)

[ReLU 그래프]

x = np.arange(-5.0, 5.0, 0.1)
y = ReLU(x)
plt.plot(x, y)
plt.show()

그렇다면, 해당 함수의 역전파(Back Propagation)는 어떻게 될까요?? 그냥 있는 그대로 x의 값을 기준으로 나뉘게 됩니다. 

 

df(x)/dx = 1 (x > 0)

           = 0 (x <= 0)

 

이렇게 되고, 순전파때 x가 0보다 클 경우 역전파는 Loss Function에 대한 미분결과를 그대로 돌려주고, x가 0보다 작으면 역전파는 0이 됩니다.

이에 대한 구현은 그래도 간단합니다. 순전파는 input값이 0이하인 애들을 0으로 만들고, 역전파는 input값이 0이하인 애들을 0으로 만들면서 input값이 1이상인 애들을 1로 만들면 됩니다.

 

전 이를 구현하기 위해서 x에 대해서 chk변수를 하나 생성해서, 0보다 큰 경우는 1 / 0보다 작은 경우는 0으로 세팅하고 진행하고자 합니다.

 

[ReLU 구현코드]

class ReLU:
    def __init__(self):
        self.chk = None
 
    def forward(self, x):
        self.chk = (x>0).astype(np.int)
        return x * self.chk
 
    def backward(self, dout):
        return dout * self.chk

코드에서 보듯이, 

(1) x > 0 을 통해서 해당 조건을 x의 각 인자에 적용해서 true / false를 채워서 리턴하고....

(2) astype(np.int)를 통해서 true는 1 / false는 0으로 바꾸어 줍니다. 

 

그리고 이 chk 신규 변수를 활용해서 곱해주는 방식으로 구현이 가능합니다.

 

[Test]

relu = ReLU()
 
x = np.array([-1.0, 3.0, -7.2, 5.6])
out = relu.forward(x)
dx = relu.backward(3)
 
print(out)
print(dx)
=====================================

[-0.   3.  -0.   5.6]
[0 3 0 3]

 

 

2. Sigmoid


ReLU의 경우는 함수가 간단하여 1개의 node graph로 표현이 가능합니다. 하지만 Sigmoid의 경우는 아래의 함수 식으로 구현되기 때문에 쉽게 구현이 되지는 않습니다.

 

f(x) = 1 / (1 + np.exp(-x))

 

[Sigmoid 코드]

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

 

[Sigmoid 그래프]

x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.show()

 

이를 graph로 표현하면 아래의 그림과 같습니다.

 

그럼 이 그래프를 따라가면서, 순전파와 역전파 동작에 대해서 자세히 알아보겠습니다. 되게 복잡해 보이지만 찬찬히 살펴보면 정말 별개 아니라는 것을 알 수 있습니다. 

 

[순전파 절차]

총 4개의 node로 구성되며, 순전파는 크게 어려움이 없습니다.

 

(1) 'X' 노드 : 변수 X와 상수 -1을 input으로 하고 그 결과를 output으로 도출

(2) 'exp' 노드 : 전 노드의 output을 input으로 하고 단지 np.exp( )를 한 결과를 output으로 도출

(3) '+' 노드 : 전 노드의 output과 상수 1을 input으로 하고 그 결과를 output으로 도출

(4) '/' 노드 : 전 노드의 output을 input으로 하고 단지 분수를 취한값을 output으로 도출

 

[역전파 절차]

Loss Function의 함수를 L(x)로 했을떄, 그 결과의 미분을 dL(out) / dout 이라고 할때,

 

(1) '/' 노드 : 이 노드의 f(x) = 1/x 이고, df(x)/dx는 (x^-1)의 미분... 결국 - (x^-2)가 됩니다. 이를 f(x) = y에 대해서

    치환하면, - (y^2)이 되는 것이지요.

df(x)/dx = - (x^-2) = - 1 / x^2 = - (y^2)

    따라서, 현재 역전파 값의 총 결과는...

        - (dL(out) / dout) * (y^2)

(2) '+' 노드 : 이 노드는 이전노드의 전파값을 그대로 broadcasting 해줍니다. 

    따라서, 현재 역전파 값의 총 결과는...

        - (dL(out) / dout) * (y^2)

(3) 'exp' 노드 : 이 노드는 exp(x)의 미분을 구해야하는데... exp(x)의 미분 결과는 그대로 exp(x) 입니다. 따라서 입력이

    음수로 들어오기 때문에 그 미분값도 음수가 되어, exp(-x)가 됩니다.

    따라서, 현재 역전파 값의 총 결과는...

        - (dL(out) / dout) * (y^2) * exp(-x)

(4) 'X' 노드 : 이 노드는 이전노드의 전파값에 상대방의 입력치를 곱한 결과를 리턴해줍니다.

    따라서, 현재 역전파 값의 총 결과는...

        (dL(out) / dout) * (y^2) * exp(-x)

 

결국 이 결과는 를 간략하게 그림으로 나타내면 아래와 같습니다.

 

최종 sigmoid의 역전파 결과를 보면 x, y 즉 input과 output의 결과가 혼재되어 있습니다. 그래서 편한 연산을 위해서 둘중에 한쪽으로 치환하여 구현할 코드를 간단하게 만들어 보겠습니다.

 

[sigmoid 역전파 치환]

최종 역전파는 (dL(out) / dout) * (y^2) * exp(-x)이고, y = 1 / (1 + np.exp(-x)) 입니다.

 

그렇다면, 역전파의 y에 이 값을 대입해 보겠습니다.

(dL(out) / dout) * ((1 / (1 + np.exp(-x)))^2) * exp(-x)

(dL(out) / dout) * (1 / (1 + np.exp(-x))) * (exp(-x) / (1 + np.exp(-x)))

(dL(out) / dout) * y * (1 - y)

 

결국, sigmoid의 역전파는 순전파의 output인 y만으로 계산이 가능합니다.

[Sigmoid 구현코드]

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

 

[Test]

sig = Sigmoid()
 
x = np.array([-1.0, 3.0, -7.2, 5.6])
out = sig.forward(x)
dx = sig.backward(3)
 
print(out)
print(dx)
====================================

[2.68941421e-01 9.52574127e-01 7.46028834e-04 9.96315760e-01]
[0.5898358  0.13552998 0.00223642 0.011012  ]

 

 

0. 다음 예고


실제 인공신경망(ANN)에서 사용하는 함수들은 생각해보면, Matrix Multiply / ADD / Sigmoid / ReLU / Softmax 정도가 있습니다. 지금까지는 ADD / Sigmoid / ReLU를 확인했고, 이어서 Matrix Multiply / Softmax를 알아보겠습니다.

 

너무 내용이 많아서 한번에 달리기가 너무 힘드네요...ㅠㅠ

 

- Ayotera Lab -

댓글