자주 잊어버리는 딥러닝 기초 내용을 여러 포스팅에 걸쳐 간단하게 정리해보려 한다. 다룰 내용은 크게 아래와 같다.
- Introduction
- Elements of ML
- Multi-layer Perceptron
- Model Selection
- CNN
- GNN
- RNN
목차
Backpropagation and Gradient
이전 글에 이어 Backpropagation과 Gradient에 대해 알아보자.
딥러닝에서는 loss function을 통해 모델이 예측한 값과 실제 값 간의 차이를 구하고, 이를 줄이기 위해 gradient descent라는 방법을 사용한다.
Gradient descent란, Fig 1과 같이 loss function을 나타냈을 때, gradient(경사)를 따라 loss 가장 작은 곳(optimal point)을 찾아 가는 방법이다.
이를 위해서는 gradient를 계산해야 할 것이다.
Backpropagation
\(x, y, z\)에 대한 다음 함수의 gradient를 구한다고 해보자.
\( f(x, y, z) = x \sqrt{\text{exp}\left(-\cfrac{\sin z}{y^2}\right)} \)
\( \nabla{f} = (\partial_x f, \partial_y f, \partial_z f) = ? \)
직접 계산하는 게 가장 좋겠지만, 계산이 느리다. 특히 MLP는 layer가 여러 개일 텐데, layer가 깊어질수록 계산 시간이 기하급수적으로 늘어날 것이다.
그렇다고 작은 값 \(\epsilon\)에 대해
\(\nabla{f} = \left( \frac{f(x+\epsilon, y, z) - f(x, y, z)}{\epsilon}, \frac{f(x, y+\epsilon, z) - f(x, y, z)}{\epsilon}, \frac{f(x, y, z+\epsilon) - f(x, y, z)}{\epsilon} \right) \)
위와 같이 difference method를 사용하면, layer를 거치면서 error가 너무 많이 쌓이게 된다.
따라서, gradient 계산 시 backpropagation(역전파) 알고리즘을 사용한다.
\(x=5, y=2, z=3\)이 주어졌을 때, \(f(x, y, z)\)의 값은 computation graph를 사용하여 다음과 같이 구할 수 있다.
이 과정은 forward propagation이라 한다. MLP에서 output을 구하는 과정과 같다.
이를 반대로 계산하면 각각의 편미분 값을 구하여 최종적으로 gradient를 구할 수 있는데, 이를 backpropagation 과정이라 한다.
먼저, \(x\)에 대한 편미분 값을 구하는 과정을 보자.
\(\cfrac{\partial(ab)}{\partial a} = b\)이므로, Fig 3의 computation graph를 통해 \(\cfrac{\partial f}{\partial x} = 1 \times 0.9825\)임을 쉽게 알 수 있다. (chain rule 때문에 1 * 0.9825인데, 자세한 내용은 y, z에 대해 구할 때 생각하자.)
다음으로, \(y\)와 \(z\)의 편미분 계산 과정을 보자.
공통적으로 \( \cfrac{\partial(\sqrt{a})}{\partial a} = \cfrac{1}{2\sqrt{a}} \)에서 0.5089, \(\cfrac{(\text{exp}(a))}{\partial a} = \text{exp}(a)\)에서 0.9653, \(\cfrac{\partial(-a)}{\partial a} = -1\)에서 -1로 계산된다.
그리고, \(y\)방향에서 \(\cfrac{\partial(a/b)}{\partial b} = \cfrac{-a}{b^2}\)에서 -0.0088, \(\cfrac{\partial(a^2)}{\partial a} = 2a\)에서 4를 구할 수 있고,
\(z\)방향에서 \(\cfrac{\partial(a/b)}{\partial a} = \cfrac{1}{b}\)에서 0.25, \(\cfrac{\partial(\sin(a))}{\partial a} = \cos(a)\)에서 -0.9899를 구할 수 있다.
Computation graph를 사용하여 최종적으로 편미분 값을 구하기 위해서는 chain rule을 사용해야 한다. Computation graph에서 중간에 계산된(거치는 경로) 값들을 각각 \(x_1, y_1, y_2, \dots, y_6, z_1, z_2, \dots, z_6\)이라 하면, 다음과 같이 위에서 구한 각 편미분 값들의 곱으로 나타낼 수 있다.
\( \cfrac{\partial f}{\partial x} = \cfrac{\partial x_1}{\partial x} \cfrac{\partial f}{\partial x_1} \)
\( \cfrac{\partial f}{\partial y} = \cfrac{\partial y_1}{\partial y} \cfrac{\partial y_2}{\partial y_1} \cdots \cfrac{\partial f}{\partial y_6} \)
\( \cfrac{\partial f}{\partial z} = \cfrac{\partial z_1}{\partial z} \cfrac{\partial z_2}{\partial z_1} \cdots \cfrac{\partial f}{\partial z_6} \)
\( \therefore \cfrac{\partial f}{\partial x} = 1 \times 0.9825 = 0.9825, \)
\( \cfrac{\partial f}{\partial y} = 1 \times 5 \times 0.5089 \times 0.9653 \times (-1) \times (-0.0088) \times 4 = 0.0865, \)
\( \cfrac{\partial f}{\partial x} = 1 \times 5 \times 0.5098 \times 0.9653 \times (-1) \times 0.25 \times (-0.9899) = 0.6079 \)
참고로, pytorch에서 'torchviz'라는 모듈을 통해 computation graph를 시각화해볼 수 있다.
Instability of Gradients
딥러닝의 parameter update 과정에서 최종 gradient는 hidden variable(layer)들의 gradient의 곱이다.
\( \mathbf{h}^{(k+1)} = f_k(\mathbf{h}^k) \overset{\text{multi-layer}}{\Rightarrow} y = f_L \circ \cdots \circ f_1(\mathbf{x}) \)
\( \partial_{\boldsymbol{\theta}} y = \partial_{\boldsymbol{\theta}} \mathbf{h}^l \prod\limits_{k=l+1}^L \partial_{\mathbf{h}^{k-1}} \mathbf{h}^k \)
따라서, 만약 activation function으로 sigmoid 함수를 사용한다면, 모든 gradient의 값이 0에서 1 사이이므로 여러 번 곱하면서 최종 gradient가 0에 가까워지는 현상이 발생한다. 이를 gradient vanishing problem이라 한다.
반대로, 각 layer의 gradient 값들이 커지면서 최종 gradient 값이 발산해버리는 문제를 gradient explosion problem이라 한다.
보통 gradient explosion은 gradient clipping(gradient 값이 threshold 이상으로 올라가지 않도록 조절)으로 쉽게 해결이 가능하지만, gradient vanishing은 해결하기 힘들다. Gradient vanishing problem 해결을 위해서는 hidden layer에서는 sigmoid 대신 ReLU를 활성 함수로 사용하거나, 적절한 weight initialization을 해주거나, batch normalization이나 layer normalization을 적용해준다.
모델을 학습시킬 때 gradient vanishing 혹은 explosion이 발생하였는지를 확인하기 위해서는 matplotlib, tensorboard, wandb 등을 통해 학습 및 평가 과정을 그려보아야 한다.
Multi-Layer Perceptron (MLP)
MLP는 말 그대로 perceptron 여러 층으로 구성된 모델을 말한다.
\( \text{MLP}(\mathbf{x}; \boldsymbol{\theta}) := f(\mathbf{h}^{(L)} \circ \cdots \circ \mathbf{h}^{(1)}, \quad \text{where } l \in \{ 1, \dots, L \} \)
\( \mathbf{h}^{(l)} := \varphi(\mathbf{w}^{(l)} \mathbf{h}^{(l-1)} + \mathbf{b}^{(l)}), \; \mathbf{h}^{(0)} = \mathbf{x} \in \mathbb{R}^d \)
- \(l\) : layer index
- \(\boldsymbol{\theta} \) : parameters (\([ \mathbf{w}^{(l)}, \mathbf{b}^{(l)}]_{l=1}^L\))
- \(\mathbf{h}^{(l)}\) : \(l\)번째 layer의 hidden vector
- \(\varphi\) : activation function → activation function에는 learnable parameter가 없음
- \(\mathbf{w}^{(l)}\) : weight parameter (matrix)
- \(\mathbf{b}^{(l)}\) : bias parameter (vector)
- \(\mathbf{x}\) : input vector
MLP에서 parameter 개수는 다음과 같이 구할 수 있다.
\( \lvert \boldsymbol{\theta} \rvert \approx \sum\limits_{l=1}^L \text{dim}(\mathbf{h}^{(l)}) \cdot [\text{dim}(\mathbf{h}^{(l-1)}) + 1] \)
Properties of MLP
MLP는 다음과 같은 특성을 갖고 있다.
- 장점
- 구현이 간단하다.
- Inductive bias가 없다. (Inductive bias가 적당히 있어야 특정 task에 유용하다. Inductiva bias에 대한 설명은 다음 글을 참고하자.)
- 단점
- Fully connected : layer가 너무 dense해서 차원의 저주(curse of dimension)에 취약하다.
- Computational cost가 너무 높다. (계산 시간 너무 오래 걸림, 메모리 부족)
- Overparameterization에 의해 overfitting되는 경향이 있다.
Universal Approximation Theorem (UAT)
Universal Approximation Theorem은 딥러닝의 꽃으로 불릴 만큼 중요한 이론이다.
Sigmoid activation function을 가지면서, 하나의 hidden layer를 갖는 feed-forward neural network는 적절한 weights만 주어진다면 어떤 함수든 근사화 할 수 있다는 이론이다.
Width(뉴런 개수)에 따라 임의의 연속 함수를 근사할 수 있고, depth(layer 개수)에 따라 임의의 integrable function(적분 가능한 함수, 불연속이어도 상관 없음)을 근사할 수 있다. 단, integrable function이 \(f:\mathbb{R}^n \rightarrow \mathbb{R}^m\)일 때, 근사하려면 network의 width가 \(\max\{n+1, m\}\)보다 커야 한다.
최근댓글