Yet Never Lose Faith

- Good to Great , Jim Collins

How To Preprocess Image Data 자세히보기

Deep Learning/[Books] Do it! 정직하게 코딩하며 배우는 딥러닝 입문

[모델 구축] 경사하강법을 구현하는 방법 - ① 직접 변화율 계산하기

Kellyyyy 2020. 6. 25. 08:00

 

 

 

 

지난 포스팅에서 지도학습의 목표는 2가지로 나뉜다고 했다.

또한 2가지 목표 중 하나인 '회귀'에서 쓰이는 대표 알고리즘이 '선형회귀'이고,

선형회귀란 1차함수의 절편과 기울기를 찾는 것이라고 했다.

 

자세한 내용은 더보기의 걸어둔 이전 포스팅을 확인바란다.

 

이번 포스팅에서는 

선형회귀 알고리즘에서 어떻게 절편과 기울기를 찾는지

그 방법을 알아보도록 한다.

 

선형회귀에서 절편과 기울기를 찾는 방법은 여러가지가 있다.

 

그 중에서도 오늘은 경사하강법에 대하서 알아볼 것이다.

경사하강법은 많은 양의 데이터에 사용하기 좋은 알고리즘이며,

이외에도 정규방정식, 결정트리, 서포트벡터머신 등이 있다. 

 


 

|  경사하강법이란 ?

 

경사하강법(Gradient descent)이란

 

모델이 데이터를 잘 표현할 수 있도록 변화율을 사용하여(①) 모델을 조금씩 조정(②)하는 최적화 알고리즘이다. 

 

 

② 모델을 조금씩 조정한다.

 

모델을 조금씩 조정한다는 것은

머신러닝 모델의 규칙 즉, 가중치를 수정한다는 의미이다.

 

선형회귀에서는 절편(b)과 기울기(a)를 수정한다고 볼 수 있다.

 

 

변화율을 사용하여

 

변화율이란 무엇일까?

 

변화율을 이해하기 전에

'예측값'에 대해 알아야 한다. 

 

우리가 입력과 출력 데이터를 통해 규칙을 발견하면 모델을 만들었다고 한다.

그 모델에 새로운 입력값을 넣으면 어떤 출력이 나오는데, 

이 값이 모델을 통해 예측한 예측값이다.

 

예를 들어, y= 7x+3이라는 모델의 x값에 7을 넣으면 53이라는 값이 나오는데,

이때 53이 예측값이 된다.

예측값은 y_hat으로 표현한다.

 

+) 딥러닝 분야에서는 기울기 a를 종종 가중치를 의미하는 w로 표현한다. 

그래서 선형회귀 식은 아래와 같이 다시 표현할 수 있다.

y_hat = wx + b

 

자, 그럼 다시 변화율이란 무엇일까?

변화율이란 기울기(절편) 변화량에 대한 예측값 변화량의 비율을 의미한다.

식으로 표현하면 변화율 = (예측값 변화량) / (기울기(절편) 변화량)이다.

 

경사하강법에서는 가중치 조정에 이 변화율을 사용하는 것으로 정의했다.

 


 

| 경사하강법에서 변화율 사용하는 방법?

 

그렇다면 어떻게 사용할까?

결론부터 말하면 기존 기울기(절편)에 기울기(절편)의 변화율을 더한다.

 

i) 기울기
w_new = w_rate + w
(w_new : 변화한 기울기 / w_rate : 변화율 / w : 기존 기울기)

ii ) 절편
b_new = 1 + b
(b_new : 변화한 절편 / b_rate : 변화율 / b : 기존 기울기)
*절편의 변화율은 항상 1이다 (아래에서 증명한다)

 

어떻게 이렇게 도출되는지 알아보자.

우선, 가중치 조정 프로세스는 아래와 같다.

 

이 프로세스는 다른 알고리즘에서도 계속 사용할 것이므로 익혀두도록 하자!!

 

[가중치 조정 프로세스]

1. 무작위로 w와 b를 정한다.
2. 훈련데이터에서 샘플 하나(x)를 선택하여 예측값(y_hat)을 계산한다.
3. y_hat과 샘플의 진짜 y를 비교한다.  
4. y_hat이 y와 더 가까워지도록 변화율을 사용하여 w와 b를 조정한다. 
5. 모든 샘플을 처리할 때 까지 2~4를 반복한다. 

 

구체적으로 실습을 해보도록 하겠다.

 

우선 훈련데이터를 확보한다.

훈련데이터는 파이썬에서 제공하는 당뇨병 데이터셋을 사용하겠다.

 

load_diabetes() 함수를 사용하여 다운로드 받을 수 있다. 

from sklearn.datasets import load_diabetes
diabetes = load_diabetes()

 

입력데이터와 출력데이터를 분리한다.

x = diabetes.data[:,0]
y = diabetes.target

 

훈련데이터 세팅이 됐다면,

첫 단계부터 진행해보자.

 

1. 무작위로 w와 b를 정한다.

간단하게 두 값을 모두 실수 1.0으로 정하겠다.

 

 

w = 1.0
b = 1.0

 

 

2. 훈련데이터에서 샘플 하나를 선택하여 y_hat을 계산한다.

 

 

y_hat = x[0] * w + b
print(y_hat)
// 1.0380759064334242

 

 

예측값을 구해보니 1.0380759064334242다.

 

3. y_hat과 샘플의 진짜 y를 비교한다. 

 

 

print(y[0])
//151.0

 

 

실제값은 151.0으로 예측값에 비해서 현저히 높은 것을 알 수 있다.

 

4. y_hat이 y와 더 가까워지도록 변화율을 사용하여 w와 b를 조정한다. 

 

변화율은 가중치 증가량에 대한 예측값 변화량의 비율이라고 했다.

그렇데 첫번째 샘플에 대해서는 아직 변화율을 알 수 없다.

(아직 한번도 변화시킨 적이 없기 때문이다.)

 

그래서 임의로 0.1만큼 증가시킨 후 y_hat을 구한다.

 

 

w_inc = w + 0.1
y_hat_inc = x[0] * w_inc + b
print(y_hat_inc)
// 1.0418834970767665

 

 

자 그러면 이제 변화율을 구할 수 있다.

변화율은 가중치 증가량에 대한 예측값 변화량의 비율이므로

y_hat이 증가한 양을 w가 증가한 양으로 나누면 된다.

 

 

w_rate = (y_hat_inc - y_hat) / (w_inc - w)
print(w_rate)
// 0.03807590643342348

 

 

그런데 여기서 신기한 점이 있다.

w_rate의 값이 x[0]과 동일하다는 것이다.

이는 수식으로 증명가능하다.

 

 

 

 

 

자, 고등학교로 돌아가보자.

우리는 두 변수사이의 변화율이 상수일 때, 

그 두변수의 관계가 1차함수의 관계를 가진다는 것을 배웠었다.

 

y_hat과 w는 1차함수의 관계이므로 아래와 같이 그릴 수 있다.

 

1) 변화율이 양수인 경우

 

 



2) 변화율이 음수인 경우

 

 

 



자, 그럼 y_hat을 증가시키기 위해서 어떻게 해야하는지 보이는가?

 

변화율이 양수이면 양의 방향으로,

음수이면 음의 방향으로 이동해주면 된다.

 

경사하강법에서는 변화율만큼 이동한다고 정의했으므로

결론적으로 아래와 같이 정리할 수 있다.

w_new = w + w_rate 

 

절편의 경우도 살펴보자.

절편도 가중치와 마찬가지로 변화율을 구해봤다.

 

b_inc = b + 0.1
y_hat_inc = x[0] * w + b_inc
print(y_hat_inc)

b_rate = (y_hat_inc - y_hat) / (b_inc - b)
print(b_rate)
// 1.0

 

b의 변화율 역시 상수값으로 1이 나왔다.

 

 

 



절편의 경우는 항상 1, 즉 상수이므로 

기울기와 마찬가지로

b_new = b+1로 정리할 수 있다.

 

하지만 이렇게 변화율만 가지고 가중치를 조정하면

2가지 문제점이 생긴다. 

 

1. y_hat이 y에 한참 미치지 못하는 경우, w와 b를 더 큰 폭으로 수정할 수 없다.
2. y_hat이 y보다 커지면 감소시키지 못한다. 

 

이를 해결하기 위해 등장한 개념이

오차역전파이다.

 


 

| 오차역전파 (backpropagation)

 

오차 역전파는 y_hat과 y의 차이를 이용하여 w와 b를 업데이트하는 방법이다.

 

변화율에 y_hat과 y의 차이인 err를 곱한다.

 

이렇게 하면 y_hat과 y의 차이가 크면 w와 b가 많이 증가되고,

y_hat이 y보다 커지면 w와 b가 감소하는 방향으로 수정된다.

 

오차역전파를 적용하여 다시 가중치를 업데이트해보자.

 

 

err = y[0] - y_hat
w_new = w + w_rate * err
b_new = b + 1 * err
print(w_new, b_new)
// 6.709936190362795 150.96192409356658

 

w와 b는 각각 6.709936190362795, 150.96192409356658로 업데이트 되었다.

 

가중치 조정 프로세스 5번째 단계에서

모든 샘플에 대해서 반복한다고 했으므로 

두 번째 샘플에 대해서도 구해보겠다.

 

 

y_hat = x[1] * w_new + b_new
err = y[1] - y_hat
w_rate = x[1]
w_new = w_new + w_rate * err
b_new = b_new + 1 * err
print(w_new, b_new)
// 6.995811344329849 75.01289722216944

 

 

결과값을 보면 w는 0.2만큼 커지고 b는 절반으로 줄었다. 

 

 

이렇게 모든 샘플을 반복하며 가중치를 수정한다. 

for x_i, y_i in zip(x,y) :
  y_hat = x_i * w + b
  err = y_i - y_hat
  w_rate = x_i
  w = w + w_rate * err
  b = b + 1 * err
print(w,b)
 // 273.52893422082 69.1005185686104

 

최종적으로 가중치는 위와 같이 수정되었다.

 


 

그럼 마지막으로 우리가 처음 무작위로 세팅한 가중치로 그린 그래프와

모든 샘플을 통해 조정한 가중치로 그린 그래프가

얼마나 다르게 데이터를 표현하는지 확인해보겠다.

 

1) 가중치 조절 전

 

 

import matplotlib.pyplot as plt
// 가중치 조절 전
plt.scatter(x,y)
pt1 = (-0.1, -0.1 * 1.0 + 1.0) // w = b = 1.0
pt2 = (0.15, 0.15 * 1.0 + 1.0) // w = b = 1.0
plt.plot([pt1[0],pt2[0]],[pt1[1],pt2[1]])
plt.xlabel('x')
plt.ylabel('y')
plt.show()

 

 

 

가중치 조절 전

 

 

 

2) 가중치 조절 후

 

 

// 가중치 조절 후
plt.scatter(x,y)
pt1 = (-0.1, -0.1 * w + b)
pt2 = (0.15, 0.15 * w + b)
plt.plot([pt1[0],pt2[0]],[pt1[1],pt2[1]])
plt.xlabel('x')
plt.ylabel('y')
plt.show()

 

 

 

 

가중치 조절 후

 

 

 

w와 b가 1.0일 때보다 훨씬 훈련데이터의 경향을 잘 표현한다고 할 수 있다.

 

이렇게 경사하강법은 선형회귀 뿐만 아니라 모든 회귀문제에서 동일한 원리로 사용할 수 있다.