다양한 계층 구현을 통한 오차역전파법 구현하기

가중치 매개변수의 기울기를 구하는 방법은 이전 포스팅에서 구성한 간단한 방법으로도 할 수 있지만 복잡해도 고속으로 진행 가능한 방법도 존재한다. 바로 오차역전파법이다. 

오차 역전파법을 말하기 전에 선제적으로 알아야 할 중요한 내용이 있다. 바로 Chain Rule이다.

간단하게 설명하면 합성함수의 미분은 합성함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있다는 말이다. 수식으로 설명하면 다음과 같다.


라는 식이 있을때 z는 x,y로 표현이 가능하다. 그리고 이때 x에 대한 z의 미분은 t에 대한 z의 미분과 x에 대한 t의 미분으로 나타낼 수 있다. 즉

이런 식으로 계산이 가능한 것이 바로 Chian Rule이다.

자 그럼 이제 오차 역전파법을 몇가지 계층의 구현을 들어가며 설명해보겠다.

1. +(Add Node)
더하기 노드의 경우의 역전파 이다. 더하기 노드의 경우 입력되는 x,y의 값에 대해 출력값은 x+y로 나오게 된다. 여기서 출력 값을 각각의 변수로 편미분 해보면

이런 식으로 나온다. 이 결과 값이 의미하는 것은 바로 덧셈노드의 역전파는 값을 1을 곱한다. 즉 들어온 값을 그대로 흘린다는 말을 의미한다.
이를 파이썬으로 구현하면 다음과 같다.

class Add():
def __init__(self):
self.x = None
self.y = None

def Addfo(self,x,y):
self.x = x
self.y = y
return x+y

def Addback(self,d):
dx = self.x * 1
dy = self.y * 1
return dx,dy
가장 먼저 생성자를 통해 x,y값을 초기화 해준다. 그 후 순전파의 경우 두 값(경우에 따라서는 늘려갈 수 있다.)의 합을 출력한다. 역전파의 경우 아까 계산대로 들어오는 d(이전까지의 loss의 미분값)을 *1을 해 (그대로) 다음에게 넘겨준다.

2. *(Multiplication Node)
곱셈 노드의 경우의 역전파이다. 곱셈노드의 경우 입력되는 x,y 값에 대해 출력값은 x*y로 나오게 된다. 이
여기서 출력값을 편미분하게 되면 

가 된다. 즉 반대로 x는 y로 y는 x로 넘어간다는 뜻이다. 이 때문에 역 전파시 x,y값이 서로 바뀌게 되는 것을 볼 수 있다. 이를 구현하면 다음과 같다. 

class Mul:
def __init__(self):
self.x = None
self.y = None

def Mulfo(self, x, y):
self.x = x
self.y =y
return x*y

def Mulback(self, d):
dx = d * self.y
dy = d * self.x
return dx,dy
보다싶이 dx,dy가 서로 반대의 input을 받는 것을 확인할 수 있다.

3. ReLU Layer
ReLU의 입력,출력은 간단했다. 0보다 크면 그대로 아니면 0을 출력하는 방법이었다. 그리고 각각을 편미분하면 다음과 같다.

즉 0보다 작은 값이 입력값으로 들어올 경우 역전파시 다시 그쪽으로 0이라는 신호를 보낸다. (0을 신호로 보내는 것은 신호를 보내지 않는다고 생각해도 된다)
파이썬 코드로 구현하면 다음과 같다. 
class ReLU():
def __init__(self):
memo = None

def relufo(self, x):
out = x.copy()
self.memo = (x>=0)
return out

def reluback(self,d):
d[self.memo] = 0
return d
코드에서 현재 값의 상태를 저장할 memo 변수 하나를 생성한다. 값은 각각의 값이 0이상인지 아닌지 구분한 후 나중에 역 전파시 입력 값이 0이하인 값들에게 접근해 모두 0으로 바꾸꾸는 방식으로 구현한다.

4. Sigmoid Layer  
시그모이드 함수는 기본적으로 자연상수에 지수가 x로 달려있는 복잡한 형태이다. 이는 
1. (*-1) 2. (exp) 3. (+1) 4. (1/)노드로 나누어서 구할 수 있다
각각의 노드의 계산은 생략하고 전체적인 편미분 요약만 표시하면
가 된다. 이때 L을 y(출력값)로 편미분 한 것은 지금까지의 Loss를 현재의 ReLU 노드 바로 전까지 편미분 한 것이다.  또한 중간과정에서 생략되었지만 x(입력값)역시 y(출력값)로 표현이 가능하다. 따라서 x를 생략하면 
로 간단하게 정리 가능하다. 파이썬 구현은 생략하도록 하겠다.

5.Affine(행렬곱 노드)
Affine은 순전파시 수행하는 행렬의 dot곱을 의미한다. 여기서는 Affine이라고 칭하겠다. 순전파는 행렬의 곱으로 진행하면 되지만 역전파시에는 어떨까? 선형대수학을 이용해 계산하면 수식은 다음과 같이 나온다. 
여기서 X^T는 전치행렬이라는 뜻이고 행을 열로 열을 행으로 바꾸었다고 생각하면 편하다.
또한 행렬은 곱셈을 하려면 앞쪽의 행과 뒤쪽의 열의 수를 일치시켜야 함으로 변수의 형상에 주의해서 진행해야한다. 그럼 Affine을 파이썬으로 구현하면 다음과 같다.
class Affine():
def __init__(self,W,b):
self.W = W
self.b = b
self.x = None
self.dW =None
self.db = None
def AFfor(self,x):
self.x = x
out = np.dot(x, self.W) + self.b
return out

def AFback(self, d):
d = np.dot(d, self.W.T)
self.dW = np.dot(self.x.T, d)
self.db = np.sum(d,axis=0)

return d
6.Softmax(소프트맥스-로스 노드)
마지막으로 오차역전파법을 구현하기 전 구현할 레이어는 출력층에서 사용하는 소프르맥스 함수이다. 간단하게는 소프트 맥스 함수는 출력값을 총합이 1이 되도록 정규화 한다고 생각하면 된다. 즉 우리가 한 결과값을 소프트 맥스 함수로 정규화 한 후에 교차 엔트로피 오차 함수를 사용해 오차값을 구하는데 이때 나오는 역전파는 깔끔하게 결과값과 정답의 차이이다!
이는 교차 엔트로피 오차가 Softmax함수와 맞물려 이렇게 되도록 설계되어있기 때문이다.

7. 신경망 학습의 전체적인 흐름
1. 미니배치: 데이터 중 일부를 무작위로 가져오는 단계
2. 기울기 산출: 미니배치의 손실 값을 줄이기 위한 각 가중치의 매개변수의 기울기를 구합니다.
3. 매개변수 갱신: 가중치의 매개변수를 기울기 방향으로 갱신합니다
4. 반복: 일정 횟수를 1~3을 반복합니다. 
이렇게 진행되는데 2단계의 기울기 산출을 바로 오차 역전파법을 이용해 진행합니다. 그럼 다음 포스팅에서 오차역전파법을 구현하도록 하겠습니다.




댓글

이 블로그의 인기 게시물

다양한 계층 구현을 통한 오차역전파법 구현하기(2)

퍼셉트론(Perceptron)

[논문리뷰] 3. CNN에 대하여