이전 챕터에서는 numerical differentiation을 이용해 gradient를 구했다.
다만, 이 구현방법 때문에 학습 시간이 매우 오래걸렸다.
따라서 이번 챕터에서는 gradient를 효율적으로 계산하기 위해 backpropagation을 알아볼 것이다!
5.1 계산 그래프 Computational Graph
- directed graph 이므로 방향(direction)을 가진다.
- 계산 과정을 그래프로 표현한 것. 그래프이므로 Node와 Edge를 가진다.
- 계산 그래프에서 계산을 진행해 답을 도출하는 단계를 순전파(Forward Propagation),
그 반대 방향으로 전파하는 것을 역전파(Back Propagation)이라고 한다.
- 계산 그래프의 가장 중요한 특징은 국소적 계산을 전파해 최종 결과를 얻는다는 것이다.
이를 통해 복잡한 계산도 각 단계에 집중하여 계산이 가능하다.
이 단원에서 계산 그래프를 사용하는 이유 ?
Back propagation, 즉 역전파와 chain rule(연쇄 법칙)을 사용해서 미분값을 효율적으로 계산하기 위해!
5.2.1 Computational graph의 Back propagation

미분 값을 생각해 봤을 때,
- 덧셈 노드의 경우 미분 값이 1 이므로 역방향 입력 신호를 그대로 전달한다.
- 곱셈 노드의 경우 곱해진 값이 필요하다.(순방향 입력 신호의 값)
5.4 단순한 Layer 구현하기
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, bin):
dx = bin * self.y
dy = bin * self.x
return dx, dy
class AddLayer:
def __init__(self):
pass
def forward(self, x, y):
out = x + y
return out
def backward(self, bin):
dx = bin * 1
dy = bin * 1
return dx, dy
#적용 예시
x = 100
y = 2
z = 1.1
mul_Layer1 = MulLayer()
mul_Layer2 = MulLayer()
out1 = mul_Layer1.forward(x, y)
out2 = mul_Layer2.forward(out1, z) # 220
#back propagation
db = 1
dout1, dz = mul_Layer2.backward(1)
dx, dy = mul_Layer1.backward(dout1)
print(dx, dy, dz) #2.2, 110, 200
5.5 활성화 함수 Layer 구현하기
5.5.1 ReLU layer

즉, forward propagation 신호의 값이 양수일 때만 역방향에서 하류로 신호를 보낸다.
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <=0)
out = x.copy()
out[self.mask] = 0
return out
def backward(self, bin):
din[self.mask] = 0 #음수인 친구들은 값을 0으로 만들어 버림
dx = bin
return dx
5.5.2 Sigmoid layer

y에 대한 식으로만 정리하면 y(1-y)로도 표현 가능하다.
즉 , forward propagation의 output만으로도 backward 값을 계산 가능하다.
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, bin):
dx = bin * (1.0 - self.out) * self.out
return dx
5.6.1 Affine Layer
신경망의 forward propagation에서 수행하는 행렬의 곱을, 기하학에서는 affine transformation 이라 칭한다.
* affine transform이란, 점, 직선, 평면을 보존하는 linear mapping으로 대표적으로 확대, 축소, 회전 등이 존재한다.
선형변환에 이동 벡터가 합쳐진 형태라고 생각하면 될 듯 하다.



데이터가 1개일 때 총 과정을 그림으로 정리하면 다음과 같다.
이 게시물의 위에 설명한 것 처럼 여기서 add 노드는 다음 계층으로 그대로 흘려보내고,
X와 W에서 propagation되는 부분만 주의 깊게 살펴보면 될 것 같다.
또한, 여기서 형상 표시는 무시하는 것이 좋을 것 같다.. 벡터의 경우 --> X의 경우는 (1,2), Y는 (1,3) 으로 생각하면 이해가 쉬울 것 같다.
5.6.2 Batch Affine Layer

여기서 주의할 점은, dB의 경우 shape가 (1,3)이 된다! :0
따라서 np.sum(dY, axis = 0)과 같이 dY에서 세로 방향으로 합해서 구한다.
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, bin):
dx = np.dot(bin, self.W.T)
self.dW = np.dot(self.x.T, bin)
self.db = np.sum(bin, axis=0)
return dx
5.6.3 Softmax-with-Loss Layer
이전에 보았듯이 Softmax 함수는 출력을 정규화 시켜 확률처럼 만든다.
신경망은 크게 학습(Training)과 추론(Inference) 두 가지 일을 하는데,
그냥 가장 큰 출력 값을 알면 추론이 가능하기 때문에 추론에서는 Softmax layer가 필요하지 않다.
반면에 학습의 경우 Softmax layer가 필요하다.
이 챕터에서는 손실 함수 (Cross-Entropy)까지 포함해 softmax-with-loss layer를 구현한다.

이 Layer의 역전파는 y-t, 즉 출력 값과 레이블의 차이로 말끔히 떨어진다.
따라서 레이블과의 오차가 크면 이전 계층으로 큰 역전파를 보내고, 반대의 경우 작은 역전파를 보낸다 :)
*마찬가지로 항등 함수 - SSE(오차 제곱합)을 회귀의 출력층에서 사용할 때에도 역전파가 y-t로 말끔히 떨어진다.
class SoftmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None
self.t = None
def forward(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, din=1):
batch_size = self.t.shape[0]
dx = (self.y - self.t) / batch_size #데이터 1개당 오차를 앞으로 전달
return dx
5.7 Back Propagation 구현하기
신경망 학습의 단계
1. Mini Batch
훈련 데이터중 일부를 랜덤으로 선별해, 이 미니 배치의 손실함수를 줄이는 것을 목표로 한다.
2. Gradient 산출
손실함수 최소화를 위해 weight parameter의 gradient를 산출한다. --> 여기에서 back propagation을 이용함!
3. Parameter 갱신
gradient를 이용해 parameter를 갱신한다.
4. 1~3 반복
나머지 과정은 생략하고, 핵심 부분만 코드로 나타내면 다음과 같다.
이전에 구현했던 numerical_gradient 함수 대신 레이어를 이용해 아래 코드와 같은 gradient 함수를 구현할 수 있당~
def gradient(self, x, t):
#forward
self.loss(x, t)
#backward
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
grads = {}
grads['W1'] = self.layers['Affine1'].dW
grads['b1'] = self.layers['Affine1'].db
grads['W2'] = self.layers['Affine2'].dW
grads['b2'] = self.layers['Affine2'].db
return grads
'Deep Learning' 카테고리의 다른 글
| CH7 - CNN(Convolutional Neural Network) (0) | 2022.08.12 |
|---|---|
| CH6 - 학습 관련 기술들 (2) | 2022.07.20 |
| CH4 - 신경망 학습(Training Neural Network) (0) | 2022.07.06 |
| CH3 - 신경망(Neural Network) (0) | 2022.06.30 |
| CH2 - 퍼셉트론(Perceptron) (0) | 2022.06.30 |