cs
경사하강법(Gradient Descent)
DL·ML

경사하강법(Gradient Descent)

• gradient descent(경사 하강법)

· gradient descent 

신경망의 최적화(optimization) 과정에서는 gradient descent를 사용한다. gradient는 결과값에 대한 weight의 편미분 벡터이다. 기하적으로는 기울기가 가장 가파른 접선 벡터를 의미하여 steepest descent(최급경사법)이라고도 한다.

 

예컨대 다음과 같은 간단한 이변수 함수를 정의하자.

 

이하 function 1로 칭한다.

 

이 함수 f의 gradient는 다음과 같이 쓸 수 있다.

 

 

 

gradient가 가리키는 방향으로 접근함으로써, 극소값 또는 극대값을 얻을 수 있다. (반드시 최소값 또는 최대값이 되는 것은 아니다)

 

위의 예를 시각화해 보자. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
 
def function1(x1, x2) :
    return 3 * x1**2 + 4 * x2**2
 
 
fig = plt.figure(figsize=(9,9)) #figure을 만든다. figure의 크기는 9*9(inch)이다.
ax = fig.add_subplot(projection='3d'#plot을 만든다. subplot의 개수는 1*1이고, projection parameter는 '3d'이다.
 
= np.arange(-3,3,0.1)
y= np.arange(-3,3,0.1)
 
X, Y = np.meshgrid(x,y) #numpy meshgird 메서드는 multi-dimensional numpy array를 만든다. 
 
= function1(X,Y) #만든 function에 값을 넣어준다. 우리가 사용할 function은 3x_1^2 + 4x_2^2 이다.
 
 
# Plot a basic wireframe.
ax.plot_wireframe(X, Y, Z, rstride=3, cstride=3#wireframe 형태로 plotting을 한다. stride는 격자의 밀도를 조절한다.
plt.show()
cs

matplotlib를 이용한 이변수 함수 그리기 코드 [각주:1] 

 

다음과 같이 그려진다.

 

matplotlib를 이용해 그린 이변수 함수(function 1)

 

 

이제 gradient descent를 이용하여 극솟값을 구해보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
 
def function1(x1, x2) :
    return 3 * x1**2 + 4 * x2**2
 
def gradient(f, x1, x2) : #partial derivative vector를 return한다.
    h = 10**-4
    dx1 = ((f(x1+h,0- f(x1-h,0))/(2*h)) #근사 미분값의 중간 차분이다.
    dx2 = ((f(0,x2+h)-f(0,x2-h))/(2*h))
    return dx1, dx2
 
fig = plt.figure(figsize=(9,9))
ax = fig.add_subplot(projection='3d')
 
# Grab some test data.
= np.arange(-3,3,0.1)
y= np.arange(-3,3,0.1)
X, Y = np.meshgrid(x,y)
= function1(X,Y)
 
#X, Y, Z = axes3d.get_test_data(0.05)
 
# Plot a basic wireframe.
ax.plot_wireframe(X, Y, Z, rstride=3, cstride=3)
 
 
n=40 # 40번 이동한다.
x1s = 3
x2s= 3
zs =function1(xs,ys)
 
learning_rate = 0.01 #learning late는 0.01로 설정하였다.
 
for idx in range (1,n) : 
    dx1, dx2 = gradient(function1, x1s,x2s)
    x1s -= learning_rate * dx1; # -delta값을 이용하는 것이 중요하다.
    x2s -= learning_rate * dx2;
    zs = function1(x1s, x2s)
    ax.scatter(x1s,x2s,zs)
    #print(x1s, x2s, zs)
    print(dx1, dx2)
 
plt.show()
cs

 

이 코드로 다음과 같은 그래프를 그릴 수 있다.

한 점에서 수렴하는 해

위 그림에서 볼 수 있듯이, (3, 3, 63)에서 시작한 점이 gradient 방향으로 learning rate만큼 40번 이동하여 극솟값인 (0, 0, 0)에 수렴하는 것을 볼 수 있다. 이 코드에서는 learning rate를 0.01로 설정하였다.

 

gradient 값

 

위 사진은 gradient 값의 변화이다. 초반에는 큰 값이었다가 점점 줄어드는 것을 볼 수 있다. 시행을 반복하면 극소점에서 수렴하여 0에 가까운 값이 나오게 된다.

 

이제 gradient descent를 이용하여 신경망의 objective function를 optimization하면 된다.

 

 

 

· 왜 objective function으로 accuracy를 사용하지 않는가?

그럼 이제 신경망의 objective function을 설정하여 gradient descent로 최적해를 찾으면 된다. 단순히 생각하면 우리의 신경망의 정확도를 확인하기 위해서 단순히 다음과 같은 accuracy를 objective function으로 설정하여 optimization을 하면 될 것 같다.

 

학습 데이터 j에 대한 k번째 layer의 뉴런의 결과값 y와 학습 데이터 j에 대한 정답 x가 같지 않을 확률 E로 objective function을 정의한 경우이다.


하지만 accuracy를 objective function으로 사용하지 않고, loss function을 사용한다.

 

이는 간단한 이유이다. gradient descent를 이용하려면 미분가능한 함수여야 한다. 그러나 위 함수는 미분가능하지 않다. [각주:2]

 

좀 더 직관적으로 설명하자면, accuracy는 값이 연속적이지 않다. 100개의 학습 데이터라면 accuracy는 26%, 72%, 82% ... 와 같이 나온다. 82.246812...% 따위는 있을 수 없다.

 

따라서 parameter를 조정하여도 정확도의 변화가 클 수 없다. parameter의 조정은 매우 작은 값만큼씩만 이루어진다. 이 경우 accuracy의 변화를 기대할 수 없는 것이다. 따라서 parameter를 어떻게 최적화할지도 알 수 없다. 이런 이유로 accuracy는 objective function으로서 적절하지 않다.

 

 

 

 

 

 

 

· 참고문헌

김의중, 『알고리즘으로 배우는 인공지능, 머신러닝, 딥러닝 입문』, 위키북스(2016)

사이토 고키, 『밑바닥부터 시작하는 딥러닝』, 이복연 옮김, 한빛미디어(2017)