내용
순전파와 역전파(forward & backward propagation)
학습과정
일반적으로 신경망은 가장 먼저 층(layer)을 구성하며 입력은 그 층을 통과하여 relu 등과 같은 활성함수에 의해 출력됩니다.
output=relu(dot(W, input)+b)
위 relu 함수에서 W, b는 층의 속성인 텐서호 입력과 출력의 관계를 결정하는 가중치와 편차로 위 과정을 반복하면서 학습되는 매개변수입니다. 각각은 커널과 편향 속성이라고 합니다.
다음에서 나타낸 과정과 같이 초기 W, b는 임의 값으로 지정되며 입렬(input)과 함께 층으로 입력되며 드 출력이 이렂ㅇ한 기준을 만족하지 않은 경우 다시 층으로 입력되는 과정을 반복합니다. 이 과정을 학습(training)이라고 하며 학습을 통해 W, b의 최적화가 이루어지며 그에 대응하는 결과물이 출력됩니다. 이 과정이 머신러닝 또는 딥러닝의 핵십이 됩니다.
W0, b0, Input | → | layers | ← |
↓ | ↑ | ||
activation, other operation | |||
↓ | |||
Woptim, boptim | |||
↓ | |||
Output | |||
→: forward | |||
→: backward |
위 학습과정은 다음의 단계로 구성됩니다.
- 1 단계. 레이어, 활성함수, 출력층을 구축합니다.
- 가중치, 편차, 입력을 조합하는 초기 모델을 설정합니다.
- 2 단계. 모델에 의한 y의 추정치(ypred)를 계산합니다.
- 이 단계를 forward propagation(순방향 전파)라고 합니다.
- 3 단계. ypred와 y간의 불일치 측정값인 비용(cost) 또는 손실(loss)을 계산합니다.
- 비용함수를 설정합니다.
- 4 단계. 손실을 최소화하기 위한 W, b를 업데이트 합니다.
- x의 변수에 대한 W, b의 기울기를 계산합니다.
- 각 기울기를 3 단계까지 사용된 W, b에 적용하여 업데이트 합니다.
- 이 업데이트 과정은 4단계에서 3, 2 단계로 역으로 추적되는 과정으로 backwawd propagation(역방향 전파)라고 합니다.
위 학습과정으로 인해 결국 ypred와 y간의 낮은 불일치, 즉, 손실이 매우 낮은 네트워크가 구축 됩니다. 즉, 특징(feature) x로 라벨 y를 적절하게 매핑하는 방법을 찾는 것입니다. 이 과정에서 가중치의 결정하는 4 단계가 난관이 됩니다. 만약 가중치 0.4일 경우 손실이 0.5이고 0.3일 경우 0.6이라면 가중치는 0.35로 지정되는 방식으로 진행 할 수 있지만 매우 비효율적입니다. 이에 반해 네트워크에서 사용되는 모든 연산이 미분 가능하다는 사실을 활용하고 네트워크 계수와 관련하여 손실의 기울기(gradient)를 계산하고 그 값을 가중치와 편차에 적용하는 과정을 반복하여 최적의 결과를 유도합니다. 즉, 손실을 최소화합니다.
미분
그림 1은 곡선(f) 위의 임의의 두 점, C, B를 연결하는 직선과 A와의 접선을 나타냅니다. 두 점 C와 B가 A쪽으로 접근한다면 기울기 c는 접선의 기울기 a에 근접할 것이며 결국에 기울기 c는 a와 같아질 것입니다. 결과적으로 A와 충분히 가까운 두 점 사이의 기울기를 A의 도함수 또는 미분(derivative)라고 합니다.
한 점에서 미분 결과의 절대값은 변화의 강도, 부호는 변화의 증가 또는 감소를 나타냅니다. 결과적으로 그 미분값은 증가 또는 감소의 속도(강도)를 알려줍니다.
모든 연속인 함수는 미분가능하며 일정한 지점에서의 미분값을 고려함으로서 목표로 하는 방향으로 그 지점의 이동을 유도할 수 있습니다. 즉, 다음 2차 함수의 그림에서 함수 위의 임의의 점 A를 아래 방향으로 이동시키기 위해 미분을 적용한 것입니다.
기울기(gradient): 자동미분
기울기는 학습에 참여하는 여러 텐서들의 연산에 대한 미분 결과입니다. 즉, 학습에서 생성되는 예측치와 실측치의 차이를 감소시키는 목적으로 그 둘의 차이 즉, 식 1의 비용(cost)이 미분의 대상이 됩니다.
$$\begin{align}\tag{1}&\hat{y_i}=x_i \cdot W + b\\ &\text{cost}=\frac{\sum^N_{i=1}(y_i-y)^2}{N} \end{align}$$식 1에서 N은 데이터의 샘플의 크기입니다. 식에서 x, y는 상수이므로 비용은 예측치($\hat{y_i}$)에 의존합니다. 이 예측치는 가중치 W와 편차 b의 함수로 결국 비용은 가중치와 편차의 함수가 됩니다.
식 1에서 나타낸 것과 같이 비용은 실측치와 예측치에 대한 2차 함수 즉, MSE(mean squared error)가 됩니다. 결국 위 그림 2에서 곡선을 비용함수로 간주할 수 있으므로 비용이 최소로 되는 지점까지의 이동은 가중치에 대한 비용의 미분을 고려함으로서 달성될 수 있습니다. 이 단계서 미분항의 값이 크다면 감소 속도가 증가되어 최소지점을 지나칠 가능성이 존재합니다. 이러한 가능성을 최소로 하기 위해서 감소율을 축소할 수 있습니다. 이를 위해 step factor 또는 학습율(learning factor, η)를 고려해줍니다.
W=W0 | → | cost=f(W) | ← |
↓ | ↑ | ||
W = W-η$\mathbf{\frac{d(f(W))}{dW}}$ | |||
↓ | |||
Woptim |
자동미분
위의 미분은 연산은 tensorflow에서 자동으로 진행할 수 있는 함수를 제공합니다. 즉, 입력변수에 대한 연산의 변화 즉, 기울기(gradient)를 계산하는 자동미분은 tf.GradientTape
클래스를 통해 실행됩니다. 실행되는 모든 연산이 tf.GradientTape에 기록됩니다. 이 과정은 그림 1의 역전파(backward propagation) 단계에서 이루어집니다.
다음은 2차원 텐서에 대한 역전파로 미분 연산 과정을 나타낸 것입니다.
$$\begin{align}x=\begin{bmatrix}x_{11}=1&x_{12}=1\\x_{21}=1&x_{22}=1\end{bmatrix} &\overset{+}{\Large{\Rightarrow}} & y=\sum x \qquad &\overset{\times}{\Large{\Rightarrow}} &z=y^2\\ &&&&\Large{\Downarrow} \\ \begin{aligned}\frac{dz}{dx}&=\frac{dz}{dy}\frac{dy}{dx}\\&=8\begin{bmatrix}1&1\\1&1\end{bmatrix}\\&=\begin{bmatrix}8&8\\8&8\end{bmatrix}\end{aligned} &\Large{\Leftarrow} &\frac{dy}{dx}=\begin{bmatrix}\frac{dx_{11}}{dx_{11}}&\frac{dx_{12}}{dx_{12}}\\\frac{dx_{21}}{dx_{21}}&\frac{dx_{22}}{dx_{22}}\end{bmatrix} &\Large{\Leftarrow} &\begin{aligned}\frac{dz}{dy}&=2y\\&=8 \end{aligned} \end{align}$$- tf.GradientTape()
- 이 클래스는 with 문을 사용하여 객체화합니다.
- tf.GradientTape.watch()
- 이 함수를 적용하기 위한 객체, 즉 미분인자가 될 객체는 상수가 아닌 변수형이어야 합니다. .watch()메소드에 의해 전환 합니다.
- tf.GradientTape.gradient(target, sources, output_gradients=None,...)
- 객체의 미분된 값을 호출
- persistent=True
- gradient() 메소드에 의해 결과가 호출되는 즉시 기록물은 삭제 됩니다. 그러나 persistent를 True로 지정함으로서 기록물들은 보존됩니다.
미분의 결과를 기록하기 위한 객체를 x라고 하면 이 객체를 변수로 지정해야 합니다. 위에서 언급한 것과 같이 tf.GradientTape.watch(x)를 적용합니다. 이 적용에 의해 객체 x에 대한 미분 결과들이 클래스 인스턴스에 기록됩니다. 다음은 x=5에 대한 미분 과정입니다.
$$\begin{align}&x=5, \quad y=x^5\\ &\frac{dy}{dx}=2x=10 \\&\frac{d^2y}{dx^2}=2 \end{align}$$import tensorflow as tf from tensorflow import keras import numpy as np import pandas as pd import matplotlib.pyplot as plt
x=tf.constant(5.0) with tf.GradientTape() as g: # 2차 미분(gradeient)를 위한 class instance g.watch(x) #2차 미분을 위한 전환 with tf.GradientTape() as gg:#1차 미분(gradient)를 위한 class instance gg.watch(x) y=x**2 dydx1=gg.gradient(y, x) #1차 미분 dydx2=g.gradient(dydx1, x) dydx1
<tf.Tensor: shape=(), dtype=float32, numpy=10.0 >
dydx2
<tf.Tensor: shape=(), dtype=float32, numpy=2.0 >
x=tf.constant(5.0) y=x**2 with tf.GradientTape() as f: f.watch(x) y=x**2 z=y**2 w=z**2 dwdx=f.gradient(w, x) dwdx
<tf.Tensor: shape=(), dtype=float32, numpy=625000.0>
f인 tf.GradientTape() 클래스 인스턴스에서 상수 x를 변수로 정의하고 객체 y, z, w를 선언하였스며 그 과정에서 각각의 그래디언트가 실행되었습니다. 즉, 객체 y, z, w를 변수 x 대해 미분이 시행된 상태입니다. 이 결과들 중 w의 미분을 호출한 것입니다.
다음으로 z의 미분결과를 호출하면 에러가 발생합니다. 이는 클래스의 매개변수 중 persistent=False로 지정하였으므로 결과를 호출한 즉시 f에 대한 모든 기록이 삭제되기 때문입니다. 대신에 기록들을 보존하기 위해서는 이 매개변수 persistent를 True로 지정합니다.
dzdx=f.gradient(z, x) dzdx
--------------------------------------------------------------------------- RuntimeError: A non-persistent GradientTape can only be used tocompute one set of gradients (or jacobians)
x=tf.constant(5.0) y=x**2 with tf.GradientTape(persistent=True) as f: f.watch(x) y=x**2 z=y**2 w=z**2 dwdx=f.gradient(w, x) dwdx
<tf.Tensor: shape=(), dtype=float32, numpy=625000.0>
f.gradient(z, x)
<tf.Tensor: shape=(), dtype=float32, numpy=500.0>
f.gradient(y, x)
<tf.Tensor: shape=(), dtype=float32, numpy=10.0>
기본적으로 GradientTape는 컨텍스트 내에서 접근되는 모든 학습 가능한 변수를 자동으로 감시합니다. 감시되는 변수를 세부적으로 제어하려면 watch_accessed_varaibles=False를 테이프 생산성자에 전달하여 자동 추적을 비활성화 할 수 있습니다. 이 경우 기록을 위해 지정된 변수에 대한 계산만 수행됩니다.
x=tf.constant(5.0) w=tf.constant(2.0) with tf.GradientTape(persistent=True, watch_accessed_variables=False) as g: g.watch(x) y=x**3 z=w**3 dydx=g.gradient(y, x) dydx.numpy()
75.0
dzdw=g.gradient(z, w) #w에 대한 미분을 실행되지 않음 dzdw
댓글
댓글 쓰기