ㅅㅇ

머신러닝 _ 14_최적화 _ 경사하강법 본문

AI_STUDY/머신러닝

머신러닝 _ 14_최적화 _ 경사하강법

SO__OS 2022. 7. 14. 19:15
플레이데이터 빅데이터캠프 공부 내용 _ 7/8

머신러닝 _ 14_최적화 _ 경사하강법 

 

1. 최적화 (Optimize)     

     = > ' 학습 '에서 모델의 예측한 결과 값과 실제 정답 값의 차이 -- > ' 오차를 계산하는 함수 '  손실함수  를 만들고

            그 오차값이 최소가 되는 지점 그때의 Parameter 를 찾는 작업을 한다.

 

2. 최적화 문제

- 선형회귀 모델에서 오차는 w 에 영향을 받는데, 둘은 관계가 있는 것이다.
- 오차 구하는 함수 f(w) 의 값을   최소화(또는 최대화) 하는    arg 변수 w(파라미터)를 찾는 것.

 

- 예시 그래프 (설명을 위해 우리가 만든 손실함수 f(w) _ 원래는 우리가 만드는게 아니라, 모델이 학습하여 만드는 함수)

   아래 그래프와 같이 weigth 에 따라 loss 오차 값이 달라지는데,

    오차가 최소화 되는 arg 변수 w 파라미터를 찾아야 한다. = > 아래 그래프에서는 빨간점 (찾을 지점) 이다.

 

3. 목적함수(Object Function), 손실함수(Loss Function), 

    비용함수(Cost Function), 오차함수(Error Function)   

  - 손실 함수 loss function, 비용 함수 cost function 이라는 용어를 가장 많이 씀.

 

  - 모델의 예측한 값과 실제값 사이의 차이를 정의하는 함수모델이 학습할 때 사용된다.
  - 이 함수의 반환값(Loss)을 최소화 하는 파라미터을 찾는 것이 최적화의 목적

  - 해결하려는 문제에 맞춰 Loss 함수를 정의한다.


      - Classification(분류)의 경우 cross entropy를 사용한다.
      - Regression(회귀)의 경우 MSE(Mean Squared Error)를 사용한다.
               - 같은 계산 함수를 쓰는 것뿐이지, 평가함수 MSE과 동일하게 보면 안됨.

 

 

4. 최적화 문제를 해결하는 방법

   1) Loss 함수 최적화 함수를 찾는다.
       - Loss를 최소화하는 weight들을 찾는 함수(공식)을 찾는다.
       - Feature와 sample 수가 많아 질수록 계산량이 급증한다.
       - 최적화 함수가 없는 Loss함수도 있다.
    
    
   2) 경사하강법 (Gradient Descent)
       - 값을 조금씩 조금씩 조정해나가면서 최소값을 찾는다.

 

5. 경사하강법 (Gradient Descent)

- 다양한 종류의 문제에서 최적의 해법을 찾을 수 있는 '일반적인 최적화 알고리즘'

- 손실함수를 최소화하는 파라미터를 찾기위해 반복해서 조정해 나간다. 

    1) w 초기값을 랜덤하게 정해 loss 를 구한다.


    2) 파라미터 벡터 W 에 대해 미분을 통해 손실함수의 현재 gradient(경사,기울기, 순간변화율)를 계산한다.


    3) gradient 의 크기 가 감소하는 방향 (0 이 되는)으로 다음 벡터 W를 조정하여 loss 를 구한다.

        - gradient가 양수이면 loss와 weight가 비례관계란 의미이므로 loss를 더 작게 하려면 weight가 작아져야 한다.    
        - gradient가 음수이면 loss와 weight가 반비례관계란 의미이므로 loss를 더 작게 하려면 weight가 커져야 한다.


    4) gradient가 0이 될때 까지 반복한다.  그때의 w 값이 최적화된 파라미터

 

 

 

5.1 경사하강법의 파라미터 

 


- 위 식을 통해 다음 W 값을 결정한다.   W = W - α 

- 미분한 값을 그냥 빼기에 너무 클 수 있어 이를 학습률로 조정한다.

 


- 하이퍼 파라미터 : 학습률 (Learning rate)


    - 기울기에 따라 이동할 step의 크기. 경사하강법 알고리즘에서 지정해야하는 하이퍼 파라미터이다. (0 ~ 1 사이 실수)
   

    - 학습률을 너무 작게 잡으면

                    최소값에 수렴하기 위해 많은 반복을 진행해야해 시간이 오래걸린다.


    - 학습률을 너무 크게 잡으면 

                    왔다 갔다 하다가 오히려 더 큰 값으로 발산하여 최소값에 수렴하지 못하게 된다.

 

 

5.2 예제 

 

# 예제는 최적화 작업의 개념을 설명하기 위해 우리가 직접 loss 함수를 만들고 도함수를 구하여 시각화 한 것뿐이다.

 실제로 이 일은 데이터를 가지고 머신러닝 모델이 하는 일. 설명을 위한 코드이지, 머신러닝 작업이 아님.

 

 

1) loss funtion 과 graident 를 구하는 loss 함수의 도함수 만들어주기

# loss 함수
def loss(weight):
    return (weight-1)**2 + 2 
    
# loss 함수의 도함수 - 각 weight에 대한 graident 를 계산해줌.
def derived_loss(weight):
    return 2*(weight-1)

 

 

2) loss function 과 최적의 weight (gradient = 0) 을 찾는 연산 및 결과를 시각화

 

- 학습율 파라미터 설정

     learning_rate = 0.4
     learning_rate = 0.01   
     learning_rate = 1.1    

 

- weight 설정

   초기값 = 0    :  이 값 또한 원래는 머신러닝에서 random 하게 결정한다.  지금은 우리가 지정해준 것.

 

 

[ 코드 설명 ]

 

- loss function 그리기 (weight 에 따른 loss 값)

- 초기값 w = 0 일 때의 loss 값 마킹,  w = 0  일 때  기울기 line plot

- 다음 weight 연산하여 결정.   w = w - learning_rate * derived_loss(w)

- 아래 그래프는 위 과정을 6차 시도까지 반복하여 시각화 결과 보기.

- 학습율 파라미터를 조정해보며 결과 차이 보기

 

weights = np.linspace(-3,4, 100) # weights 후보들 

plt.figure(figsize=(20,10))

# 손실함수 - weight에 따른 loss(손실, 오차) 값을 line plot으로 시각화
plt.plot(weights, loss(weights), 'k-')

# 학습율 설정 - weight지점에서 미분한 값에 곱해줄 값
learning_rate = 0.4
# learning_rate = 0.01
# learning_rate = 1.1 


# 시작 weight (random inital value)
w = 0
plt.plot(w, loss(w), 'go', markersize=10) # w가 0일 때 loss 값에 마킹 (g:green, o:마커모양)
plt.text(w+0.1, loss(w)+0.1, '1차시도')
plt.plot(weights, derived_loss(w)*(weights) + loss(w), 'g--') # w=0 일 때 기울기(gradient, 미분값)를 line plot으로 시각화
print('1차시도: w={:.2f}, 기울기 = {:.2f}'.format(w, derived_loss(w)))

# 다음 weight를 결정하는 연산
w = w - learning_rate*derived_loss(w) 
plt.plot(w, loss(w), 'ro', markersize=10)
plt.text(w+0.1, loss(w)+0.1, '2차시도')
plt.plot(weights, derived_loss(w)*(weights-w) + loss(w), 'r--') # red --선
print('2차시도: w={:.2f}, 기울기 = {:.2f}'.format(w, derived_loss(w)))


w = w - learning_rate*derived_loss(w)
plt.plot(w, loss(w), 'bo', markersize=10)
plt.text(w+0.1, loss(w)+0.1, '3차시도')
plt.plot(weights, derived_loss(w)*(weights-w) + loss(w), 'b--')
print('3차시도: w={:.2f}, 기울기 = {:.2f}'.format(w, derived_loss(w)))


w = w - learning_rate*derived_loss(w)
plt.plot(w, loss(w), 'mo', markersize=10)
plt.text(w+0.1, loss(w)+0.1, '4차시도')
plt.plot(weights, derived_loss(w)*(weights-w) + loss(w), 'm--')
print('4차시도: w={:.2f}, 기울기 = {:.2f}'.format(w, derived_loss(w)))

w = w - learning_rate*derived_loss(w)
plt.plot(w, loss(w), 'co', markersize=10)
plt.text(w+0.1, loss(w)+0.1, '5차시도')
plt.plot(weights, derived_loss(w)*(weights-w) + loss(w), 'c--')
print('5차시도: w={:.2f}, 기울기 = {:.2f}'.format(w, derived_loss(w)))

w = w - learning_rate*derived_loss(w)
plt.plot(w, loss(w), 'yo', markersize=10, alpha=0.5)
plt.text(w+0.1, loss(w)+0.1, '6차시도')
plt.plot(weights, derived_loss(w)*(weights-w) + loss(w), 'y--')
print('6차시도: w={:.2f}, 기울기 = {:.2f}'.format(w, derived_loss(w)))

plt.ylim(0,15)
plt.xlim(-4,5)
plt.xlabel('W')
plt.ylabel('Cost')
plt.grid(True)
plt.show()

 

 

- learning_rate = 0.4 일 때

 

   = > w = 1 : 최적의 weight.   (gradient = 0 이 되는 지점)

 

   = > 초기 w = 0 일 때 부터 경사하강법을 통해 기울기를 줄여가며 기울기가 0 인 w 값을 구하였다.

         이 값이 오차를 최소로 만드는 weight 값이다.   -- > 이 작업을 머신러닝 모델 학습에서 이뤄진다.

 

1차시도: w=0.00, 기울기 = -2.00
2차시도: w=0.80, 기울기 = -0.40
3차시도: w=0.96, 기울기 = -0.08
4차시도: w=0.99, 기울기 = -0.02
5차시도: w=1.00, 기울기 = -0.00
6차시도: w=1.00, 기울기 = -0.00

 

 

- learning_rate = 0.01 일 때   = > 너무 작게 잡음. 6차 시도 만에 찾지 못했다.

1차시도: w=0.00, 기울기 = -2.00
2차시도: w=0.02, 기울기 = -1.96
3차시도: w=0.04, 기울기 = -1.92
4차시도: w=0.06, 기울기 = -1.88
5차시도: w=0.08, 기울기 = -1.84
6차시도: w=0.10, 기울기 = -1.81

 

 

- learning_rate = 0.4 일 때 = > 너무 크게 잡음. 

                                                  왔다 갔다 하다가 오히려 더 큰 값으로 발산하여 최소값에 수렴하지 못하게 된다.

 

1차시도: w=0.00, 기울기 = -2.00
2차시도: w=2.20, 기울기 = 2.40
3차시도: w=-0.44, 기울기 = -2.88
4차시도: w=2.73, 기울기 = 3.46
5차시도: w=-1.07, 기울기 = -4.15
6차시도: w=3.49, 기울기 = 4.98

 

 

 

 

5.3 반복문을 이용해 gradient가 0이 되는 지점의 weight 찾기

 

    # 5.2 예제를 반복문을 통해 구현 (여기서는 초기 weight 값을 랜덤하게 지정함.)

 

w = np.random.randint(-3,3) # 초기 weight를 랜덤하게 선택 (-3 ~ (3-1)사이의 정수)

learning_rate = 0.4
# learning_rate = 0.001
# learning_rate = 10

max_iter = 100 # 최대 반복횟수(최적의 weight 를 찾으면 멈추고 못찾으면 최대 100번까지만 찾아본다.) 시간이 얼마나 걸릴지 모르니
w_list = [w] # weight 값들을 저장할 리스트
cnt = 0  # 몇 번 반복했는지 저장할 변수

while True:
    if derived_loss(w)==0: # 기울기가 0이 되면 멈춘다.
        break
    if cnt==max_iter:   # 반복횟수가 max_iter와 같으면 멈춘다.
        break
    w = w - learning_rate*derived_loss(w) # 새로운 weight 를 계산
    w_list.append(w) # 새로운 wieght를 리스트에 추가
    cnt += 1 # 반복횟수 1 증가

 

 

- 24 번 반복 끝에 찾음.

print('반복횟수:', cnt)

 

 

- 지정되었던 weight 값들을 저장한 리스트 

     = > 가장 마지막 값 1.0 이 최적의 weight 값이다.

w_list

 

[-1,
 0.6000000000000001,
 0.92,
 0.984,
 0.9968,
 0.99936,
 0.999872,
 0.9999744,
 0.99999488,
 0.999998976,
 0.9999997952,
 0.99999995904,
 0.999999991808,
 0.9999999983616,
 0.99999999967232,
 0.999999999934464,
 0.9999999999868928,
 0.9999999999973785,
 0.9999999999994758,
 0.9999999999998952,
 0.999999999999979,
 0.9999999999999958,
 0.9999999999999991,
 0.9999999999999998,
 1.0]