Backpropagation을 한 번에 계산하는 것은 불가능한 일입니다. 모델들이 너무 거대해서 계산 수가 너무 많기 때문입니다. 그래서 각 모델의 모듈별로 계산을 해줘야 합니다.
다음과 같은 방법으로 기울기를 구합니다. 정방향 진행 순전파는 fp, forward pass라고 하고 역방향 진행 역전파는 bp, backward pass라고 합니다. 그래서 bp를 구할 때는 출력단에서부터 내려오면 됩니다.
$\frac {df} {df}$ : indentity function으로 자기 자신을 나누니 1이 나오게 됩니다. 이후에 $\frac {df} {dz}$를 구하고 $\frac {df} {dq}$를 구한 다음 체인 룰을 사용해서 구하면 됩니다.
$\frac {df} {dy} = \frac {df} {dq} \times \frac {dq} {dy}$ ($\frac {df} {dy}$ y를 a만큼 증가했을 때 $ a \times \frac {df} {dy}$만큼 증가합니다. 기울기)
체인 룰에서 $\frac {df} {dy} = \frac {df} {dq} \times \frac {dq} {dy}$라고 했는데 여기서 $\frac {dq} {dy}$는 우리가 구하려고 하는 직접적인 graident value로 Local gradient라고 합니다. 나머지는 Global gradinet입니다. Local gradient는 forward pass에서 바로 구할 수 있습니다. (Loss에 대한 것이 아니라서 연산을 진행하면서 바로 구할 수 있습니다.)
그래서 forward pass를 진행하면서 Local gradient를 수해서 메모리에 저장해줍니다. Global gradient는 backward pass 동안에만 구할 수 있습니다. 그래서 backward pass 때 구해서 저장이 됩니다.
gradient를 구하는 데 있어서 $x, y, z$가 벡터일 때 $\frac {dl} {dx} = \frac {dl} {dz} \tiems \frac {dz} {dx}$ 다음과 같아지는데 이때 $\frac {dl} {dz}$는 벡터가 될 것이고 $\frac {dz}{dx}$는 Jacobian matrix가 됩니다.
- Global gradinet는 Loss function과 연관이 있고 forward pass의 output과 관련이 있는 것이 Local gradient
다음과 같은 graph에서 backward pass를 진행한다면 출력단부터 천천히 계산을 해주면 됩니다.
$f(x) = e^x \to \frac {df} {dx} = e^x$
$f(x) = \frac 1 x \to \frac {df} {dx} = - \frac {-1} {x^2}$
$f_a(x) = ax \to \frac {df} {dx} = a$
$f_e(x) = e + x \to \frac {df} {dx} = 1$
더하기 연산은 그냥 기울기를 흘려주고 곱하기 연산은 서로를 바꿔서 체인 룰을 진행해주면 됩니다. sigmoid function의 경우 미분 시 독특한 형태를 가지는데
$\frac {1 + e^{-x} -1} {1+e^{-x}} = (1 - \sigma (x))\sigma (x)$로 어떤 값과 연계 없이 자기 자신만이 영향을 줍니다.
이 과정을 코드로 표현해보자면 다음과 같습니다. 곱셈은 다음과 같이 서로 바꾸어 진행이 됩니다.
# Rough psuedo code
class ComputationalGraph(x):
def forward(inputs):
# 입력값에 대한 손실을 반환
# 1. pass inputs to input gates
# 2. forward the computational graph
for gate in self.graph.nodes_topologically_sorted():
gate.forward()
return loss # the final gate in the graph outputs the loss
def backward():
# 입력값에 대한 기울기를 반환
for gate in reversed(self.graph.nodes_topologically_sorted()):
gate.backward() # little piece of backprop (chain rule applied)
return inputs_gradients
class MultiplyGate(object):
"곱연산"
def forward(x, y):
z = x * y
return z
def backward(dz):
dx = self.y * dz #dl/dz * dz/dy = dz * y
dy = self.x * dz #dl/dz * dz/dx = dz * x
return [dx, dy]
Neural Network
Neural Network는 레이어가 쌓이면서 진행이 됩니다. 그래서 이전 레이어의 출력이 다음 레이어의 입력이 되는 형태로 진행이 되는데 이때 레이어는 파라미터를 가지고 있어야 합니다. 그렇기 때문에 input 부분은 레이어에서 제외가 됩니다.
- 레이어에서는 여러 개의 히든 노드가 존재합니다. 여기서 노드는 하이퍼 파라미터입니다.
- 모든 레이어가 연결되는 곳은 Fully connected layer라고 합니다.
- 레이어 단위로 연산을 끊을 수 있어 계산상의 편의성을 얻을 수 있습니다.
$(Before) Linear socre function: f = Wx$
$(Now) 2-layer Neural Network: f = W_2 max(0, W_1x)$ - ReLU를 사용
- 저는 노드들이 classifier 역할을 해주고 층이 쌓이면 그러한 정보를 종합할 수 있어진다라고 이해를 하고 있습니다.
레이어의 수가 많으면 많을수록 분류 능력이 좋아집니다. 하지만 Task에 적합한 모델보다 너무 많은 레이어를 가진 모델을 사용하면 overfitting이 일어날 확률이 높아지고 연산량이 존재하기 때문에 적절한 모델을 찾는 것이 중요합니다.
Neural Network의 size가 regularization 역할을 하지 않기 때문에 다른 방법을 통해서 regularization을 강화해줘야 합니다. $\lambda = 0.1$ 일 때 일반화가 더 잘되는 모습을 볼 수 있습니다. 여기서 $\lambda$는 regularization 기능을 Loss에 더하는 역할을 해줍니다. regularization을 잘해준다는 가정하에 network는 클수록 좋습니다.
Activation function
예전에는 활성화 함수로 sigmoid가 가장 많이 사용되었습니다. 어떤 값이 들어오던지 0~1 사이 값으로 확률처럼 output을 줄 수 있었기 때문입니다. 하지만 sigamoid는 이미지 분야에서는 잘 사용이 되지 않고 있습니다.
이미지 분야에서 가장 많이 사용하는 것은 ReLU이지만 개선사항이 많아서 여러 종류의 ReLU가 존재합니다. 일반적으로는 다른 레이어에서도 같은 활성화를 사용하는 경우가 많습니다. - 마지막 레이어의 경우에는 다른 방법도 많이 사용합니다.
'cs231n' 카테고리의 다른 글
4. Training Neural Networks part 1 (0) | 2021.11.04 |
---|---|
2. Loss function and Optimization (0) | 2021.10.11 |
1. Image classification (0) | 2021.09.19 |