참고 서적
도서명: 모두의 딥러닝 저자 : 조태호 출판 : 길벗 발매 : 2020.01.27 |
경사 하강법(Gredient Descent) : 오차 수정하기 (오차가 가장 작은 방향으로)
미분 기울기를 이용하는 경사 하강법은 오차를 비교하여 가장 작은 방향으로 이동시키는 방법이다.
위와 같이 각 점에서의 순간 기울기가 그려지면 우리가 찾는 최솟값 m에서의 순간 기울기를 눈여겨 봐야 한다. 그래프가 이차 함수 포물선이므로 꼭짓점의 기울기는 x축과 평행한 선이 된다. 즉, 기울기가 0이다. 따라서 우리가 할 일은 '미분 값이 0인 지점'을 찾는 것이 된다. 이를 위해서 다음과 같은 과정을 거친다.
- a1에서 미분을 구한다.
- 구해진 기울기의 반대 방향(기울기가 +면 음의 방향, -면 양의 방향)으로 얼마간 이동시킨 a2에서 미분을 구한다.
- 위에서 구한 미분 값이 0이 아니면 위 과정을 반복한다.
그러면 다음 그림처럼 기울기가 0인 한 점(m)으로 수렴한다.
경사 하강법은 이렇게 반복적으로 기울기 a를 변화시켜서 m(최솟값)의 값을 찾아내는 방법을 말한다.
여기서 우리는 학습률(learning rate)이라는 개념을 알 수 있다. 기울기의 부호를 바꿔 이동시킬 때 적절한 거리를 찾지 못해 너무 멀리 이동시키면 a 값이 한 점으로 모이지 않고 a 값이 한 점으로 모이지 않고 다음 그림처럼 위로 치솟아 버린다.
따라서 어느 만큼 이동시킬지를 신중히 결정해야 하는데, 이때 이동 거리를 정해주는 것이 바로 학습률이다. 딥러닝에서 학습률의 값을 적절히 바꾸면서 최적의 학습률을 찾는 것은 중요한 최적화 과정 중 하나이다.
다시 말해서, 경사 하강법은 오차의 변화에 따라 이차 함수 그래프를 만들고 적절한 학습률을 설정해 미분 값이 0인 지점을 구하는 것이다. -> 최솟값을 구하기 위해서는 이차 함수에서 미분을 해야 하고, 그 이차 함수는 평균 제곱 오차를 통해 나온다.
y 절편 b의 값도 이와 같은 성질을 가지고 있다. b 값이 너무 크면 오차도 함께 커지고, 너무 작아도 오차가 커진다. 그래서 최적의 b 값을 구할 때 역시 경사 하강법을 사용한다.
코딩으로 확인하는 경사 하강법
최솟값을 구하기 위해서는 이차 함수에서 미분을 해야 하고, 그 이차 함수는 평균 제곱 오차를 통해 나온다.
*평균 제곱 오차: 오차를 평가하는 방법
로 된다.
이 값을 미분할 때 우리가 궁금한 것은 a와 b라는 것에 주의해야 한다. 식 전체를 미분하는 것이 아니라 필요한 값을 중심으로 미분해야 하기 때문이다. 이렇게 특정한 값, 예를 들어 a와 b를 중심으로 미분할 때 이를 a와 b로 ‘편미분한다’고 한다.
a로 편미분한 결과 유도 과정
b로 편미분한 결과 유도 과정
이를 각각 파이썬 코드로 바꾸면 다음과 같다.
y_pred = a * x_data + b # 오차 함수인 y = ax + b를 정의한 부분
error = y_data - y_pred # 실제값 – 예측값, 즉 오차를 구하는 식
# 평균 제곱 오차를 a로 미분한 결과
a_diff = -(2 / len(x_data)) * sum(x_data * (error))
# 평균 제곱 오차를 b로 미분한 결과
b_diff = -(2 / len(x_data)) * sum(y_data - y_pred)
여기에 학습률을 곱해 기존의 a값과 b값을 업데이트 해준다.
a = a - lr * a_diff # 미분 결과에 학습률을 곱한 후 기존의 a값을 업데이트
b = b - lr * b_diff # 미분 결과에 학습률을 곱한 후 기존의 b값을 업데이트
경사하강법 실습
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 공부 시간 X와 성적 Y의 리스트를 만들기
data = [[2, 81], [4, 93], [6, 91], [8, 97]]
x = [i[0] for i in data]
y = [i[1] for i in data]
# 그래프로 나타내기
plt.figure(figsize=(8,5))
plt.scatter(x, y)
plt.show()
# 리스트로 되어 있는 x와 y 값을 넘파이 배열로 바꾸기(인덱스를 주어 하나씩 불러와 계산이 가능하게 하기 위함)
x_data = np.array(x)
y_data = np.array(y)
# 기울기 a와 절편 b의 값 초기화
a = 0
b = 0
# 학습률 정하기
lr = 0.05
# 몇 번 반복될지 설정(0부터 세므로 원하는 반복 횟수에 +1)
epochs = 2001
# 경사 하강법 시작
for i in range(epochs): # 에포크 수만큼 반복
y_pred = a * x_data + b # y를 구하는 식 세우기
error = y_data - y_pred # 오차를 구하는 식
# 오차 함수를 a로 미분한 값
a_diff = -(1/len(x_data)) * sum(x_data * (error))
# 오차 함수를 b로 미분한 값
b_diff = -(1/len(x_data)) * sum(y_data - y_pred)
a = a - lr * a_diff # 학습률을 곱해 기존의 a값 업데이트
b = b - lr * b_diff # 학습률을 곱해 기존의 b값 업데이트
if i % 100 == 0: # 100번 반복될 때마다 현재의 a값 , b값 출력
print("epoch=%.f, 기울기=%.04f, 절편=%.04f" % (i, a, b))
# 앞서 구한 기울기와 절편을 이용해 그래프를 다시 그리기
y_pred = a * x_data + b
plt.scatter(x, y)
plt.plot([min(x_data), max(x_data)], [min(y_pred), max(y_pred)])
plt.show()
실습 코드 실행 결과
**여기서 에포크(epoch)는 입력 값에 대해 몇 번이나 반복하여 실험했는지를 나타낸다. 우리가 설정한 실험을 반복하고 100번마다 결과를 내놓는다.
기울기 a가 2.3에 수렴하는 것과 y 절편 b의 값이 79에 수렴하는 과정을 볼 수 있다. 기울기 2.3과 y 절편 79는 앞서 우리가 최소 제곱법을 통해 미리 확인한 값과 같다. 이렇게 해서 최소 제곱법을 쓰지 않고 평균 제곱 오차와 경사 하강법을 통해 원하는 값을 구할 수 있었다.
다중 선형 회귀(Multiple linear regression)
예측 직선을 그리고자 기울기 a와 y 절편 b를 이용해도 실제 성적 사이에는 약간의 오차가 생길 수도 있다.
4시간 공부한 친구는 88점을 예측했는데 이보다 좋은 93점을 받았고, 6시간 공부한 친구는 93점을 받을 것으로 예측했지만 91점을 받았다.이러한 차이가 생기는 이유는 공부한 시간 이외의 다른 요소가 성적에 영향을 끼쳤기 때문이다.
만약 성적에 과외 횟수가 영향을 끼치는지 확인해보도록 하자.
그럼 지금부터 두 개의 독립 변수 x1과 x2가 생긴 것이다. 이를 사용해 종속 변수 y를 만들 경우 기울기를 두 개 구해야 하므로 다음과 같은 식이 나온다.
더 정확한 예측을 하려면 추가 정보를 입력해야 하며, 정보를 추가해 새로운 예측 값을 구하려면 변수의 개수를 늘려 다중 선형 회귀를 만들어 주어야 한다.
코딩으로 확인하는 다중 선형 회귀
이번에는 x의 값이 두 개이므로 다음과 같이 data 리스트를 만들고 x1과 x2라는 두 개의 독립 변수 리스트를 만들어 준다.
data = [[2, 0, 81], [4, 4, 93], [6, 2, 91], [8, 3, 97]]
x1 = [i[0] for i in data]
x2 = [i[1] for i in data]
y = [i[2] for i in data]
x2라는 독립 변수 리스트로 만들어서 data 리스트에 값이 하나씩 추가된 것을 볼 수 있다.
따라서 1, x2, y 이렇게 세 개의 축이 필요하다. 따라서 3D 그래프를 그려주는 라이브러리를 불러와야한다.
from mpl_toolkits import mplot3d # 3D 그래프 그리는 라이브러리
이 라이브러리를 적용하여 그래프를 그려본다면
다음과 같이 3D로 그래프가 그려지는 것을 확인할 수 있다.
x1과 x2에 대한 두가지 변수가 생겼으므로 기울기 a값도 각각 a1, a2 두가지로 만들어준다.
앞서 했던 방법들과 같은 방법으로 경사 하강법을 이용하고 학습률을 곱해 기존의 값을 업데이트하면
y_pred = a1 * x1_data + a2 * x2_data + b # y를 구하는 식을 세우기
error = y_data - y_pred # 오차를 구하는 식
a1_diff = -(1/len(x1_data)) * sum(x1_data * (error)) # 오차 함수를 a1로 미분한 값
a2_diff = -(1/len(x2_data)) * sum(x2_data * (error)) # 오차 함수를 a2로 미분한 값
b_diff = -(1/len(x1_data)) * sum(y_data - y_pred) # 오차 함수를 b로 미분한 값
a1 = a1 - lr * a1_diff # 학습률을 곱해 기존의 a1 값 업데이트
a2 = a2 - lr * a2_diff # 학습률을 곱해 기존의 a2 값 업데이트
b = b - lr * b_diff # 학습률을 곱해 기존의 b 값 업데이트
다중선형회귀 실습
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
# 공부 시간 X와 성적 의 리스트 만들기
data = [[2, 0, 81], [4, 4, 93], [6, 2, 91], [8, 3, 97]]
x1 = [i[0] for i in data]
x2 = [i[1] for i in data]
y = [i[2] for i in data]
# 그래프로 확인
ax = plt.axes(projection='3d')
ax.set_xlabel('study_hours')
ax.set_ylabel('private_class')
ax.set_zlabel('Score')
ax.dist = 11
ax.scatter(x1, x2, y)
plt.show()
# 리스트로 되어 있는 x와 y 값을 넘파이 배열로 바꾸기(인덱스로 하나씩 불러와 계산할 수 있도록 하기 위함)
x1_data = np.array(x1)
x2_data = np.array(x2)
y_data = np.array(y)
# 기울기 a와 절편 b의 값 초기화
a1 = 0
a2 = 0
b = 0
# 학습률
lr = 0.05
# 몇 번 반복할지 설정(0부터 세므로 원하는 반복 횟수에 +1)
epochs = 2001
# 경사 하강법 시작
for i in range(epochs): # epoch 수 만큼 반복
y_pred = a1 * x1_data + a2 * x2_data + b # y를 구하는 식 세우기
error = y_data - y_pred # 오차를 구하는 식
# 오차 함수를 a1로 미분한 값
a1_diff = -(1/len(x1_data)) * sum(x1_data * (error))
# 오차 함수를 a2로 미분한 값
a2_diff = -(1/len(x2_data)) * sum(x2_data * (error))
# 오차 함수를 b로 미분한 값
b_diff = -(1/len(x1_data)) * sum(y_data - y_pred)
a1 = a1 - lr * a1_diff # 학습률을 곱해 기존의 a1 값 업데이트
a2 = a2 - lr * a2_diff # 학습률을 곱해 기존의 a2 값 업데이트
b = b - lr * b_diff # 학습률을 곱해 기존의 b값 업데이트
if i % 100 == 0: # 100번 반복될 때마다 현재의 a1, a2, b 값 출력
print("epoch=%.f, 기울기1=%.04f, 기울기2=%.04f, 절편=%.04f" % (i, a1, a2, b))
실습코드 실행결과
다중 선형 회귀 문제에서의 기울기 a1, a2와 절편 b의 값을 찾아 확인할 수 있다. 참고로 이를 그래프로 표현하면 위 그림과 같다.
1차원 예측 직선이 변수를 추가하고, 그에 따른 기울기 변수도 추가하면서 3차원 ‘예측 평면’으로 바뀌어 과외 수업 횟수(private class)라는 새로운 변수가 추가되었다.
1차원 직선에서만 움직이던 예측 결과가 더 넓은 평면 범위 안에서 움직이게 되었고, 이로 인해 좀 더 정밀한 예측을 할 수 있게 된 것이다.
'공부정리 > 모두의 딥러닝 (교재 정리)' 카테고리의 다른 글
[deeplearning] 신경망의 이해 - 오차 역전파 (0) | 2022.04.29 |
---|---|
[deeplearning] 신경망의 이해 - 퍼셉트론, 다층 퍼셉트론 (0) | 2022.04.29 |
[deeplearning] 딥러닝의 동작 원리 - 로지스틱 회귀 (0) | 2022.04.14 |
[deeplearning] 딥러닝의 동작 원리 - 선형 회귀 (0) | 2022.04.14 |
[deeplearning] 딥러닝을 위한 기초 수학 (0) | 2022.04.07 |