1 개요
backpropagation. 줄이면 역전파. 경사법을 배웠으면 대부분 떠올릴 생각이다. 수치미분은 속도도 느리지만, 정확도에서도 아주 약간 아쉽다. 때문에 각 함수들에 대해 미분값을 미리 해석적으로 계산해두고 단순히 해당 값을 넣기만 하면 많은 연산을 아낄 수 있다.
어떤 계산에서의 순전파가 특정 연산을 수행한다면 역전파는 그 연산에서의 미분을 구할 수 있다는 아이디어에서 출발한다. 재미난 트릭인데, 역전파를 통해 해당 노드를 지날 때마다 미분값이 어떻게 변하는지도 알 수 있다는 점에서 개별 층별 훈련이 빠르게 가능해진다.
국소 미분을 단순히 축적해가며 아랫쪽으로 역전파 해간다. 이는 미분의 연쇄법칙에 의해 가능한 트릭.
2 사용예
각 층을 클래스로 만들고, 해당 레이어의 전방전파, 후방전파 메서드를 만들어 구현한다.
2.1 Relu
class Relu:
def __init__(self):
self.mask = None # 원소값이 0 이하면 True를 반환해 저장하기 위한 것.
def forward(self, x): # 배열을 받아 진행한다.
self.mask = (x <=0 ) # x의 배열 중 0 이하를 True 체크해 저장.
out = x.copy() # 출력값을 저장할 변수.
out[self.mask] = 0 # True인 인덱스에 0을 넣는다.
return out
def backward(self, dout): # 미소변위라는 의미에서 d를 붙인 이름.
# 순전파에서 0이었다면, 역전파에서도 미분값은 0이 되어야 한다. 전달된 게 없으니.
dout[self.mask] = 0 # 역전파 입력값을 받아 순전파에서 0 이하였던 인덱스에 0을 넣는다.
return dout
2.2 sigmoid
순전파에서 결과는
- x에 -1을 곱하고( )
- exp 처리를 하고 ( )
- 여기에 1을 더한 후 ( )
- 분자, 분모를 뒤집는다.( )
역전파에선 다시 거꾸로
- 의 미분 으로 상류에서 흘러온 값을 제곱한 후 마이너스를 씌우고,
- 의 미분 1을 곱하고,(여기에서의 y는 1번에서의 최종 y와 다르다.)
- 의 미분 을 상류에서 흘러온 값에 처리하여 를 곱하고,(여기에서의 y는 1번에서의 최종 y와 다르다.)
- 의 미분 -1을 곱한다.
이들의 결과는 인데, 이므로 정리하면 가 된다.(사실, 그냥 미분을 계산해도 뭐;;) 즉, 순전파의 출력 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): # 미소변위라는 의미에서 d를 붙인 이름.
dx = dout * (1 - self.out) * self.out
return dx
2.3 softmax
2.4 affine
딥러닝의 중간 층들에선 행렬 계산이 이루어지는데, 이런 행렬의 곱을 affine변환이라 부른다. 때문에 중간 뉴런들에 대한 층은 어파인층이라 부르고, 이들의 역전파를 통해 어파인층 학습도 가능하다.
오차함수에 대하여 각 입력에 대한 편미분은 형태이다. 핵심은