경사 하강법(Gradient Descent)
비용함수가 최소가 되는 회귀계수를 어떻게 구할 수 있을까?
솔루션이 바로 지금 소개할 경사 하강법(Gradient Descent, GD)이다.
경사 하강법은 비용함수 RSS를 최소화하는 방법을 직관적으로 제공하는 뛰어난 방식으로, 점진적인 반복 계산을 통해 회귀계수를 업데이트하면서 오류값이 최소가 되는 회귀계수를 구하는 방법이다.
그렇다면 솔루션인 '경사 하강법'을 통해 어떻게 하면 오류가 작아지는 방향으로 회귀계수를 업데이트할 수 있을까?
→ 미분을 통해 비용함수의 최소값을 찾음(미분은 증가 또는 감소의 방향성을 나타냄)
비용함수를 보면 제곱을 했기 때문에 회귀계수에 대한 2차 함수로서 아래 그림과 같은 포물선 형태이다. 2차 함수의 최저점은 2차 함수의 미분값인 1차 함수의 기울기가 가장 최소일 때다.
경사 하강법은 비용함수를 최초의 회귀계수(w) 값에서부터 미분을 적용한 뒤, 이 미분 값(기울기)이 계속 감소하는 방향으로 순차적으로 회귀계수를 업데이트한다.마침내 더 이상 미분된 1차 함수의 기울기가 감소하지 않는 지점을 비용함수가 최소인 지점으로 간주하고, 그때의 회귀계수를 반환한다.

경사 하강법 수식 정리 및 파이썬 코드 구현
경사 하강법 수식 정리 전 알아야 할 'Chain Rule'

<경사 하강법 수식 정리>
1. 비용함수 RSS(w0, w1)을 편의상 R(w)로 지칭. R(w)를 미분해 미분 함수의 최소값을 구해야 하는데, R(w)는 두 개의 회귀계수인 w0와 w1을 각각 가지고 있기 때문에 일반적인 미분을 적용할 수 없고, w0, w1 각 변수에 편미분을 적용해야 한다.

2. w1, w0의 편미분 결과값을 반복적으로 보정하면서 w1, w0값을 업데이트하면 비용함수 R(w)가 최소가 되는 w1, w0값을 구할 수 있다.
- 이때, 편미분 값이 너무 클 수 있기 때문에 보정 계수 η를 곱하는데, 이를 '학습률'이라고 함
- 업데이트는 이전 회귀계수에서 편미분 결과값을 빼면서 적용함(아래와 같음)

→ 새로운 w1, 새로운 w0를 반복적으로 업데이트 하면서 비용함수가 최소가 되는 값을 찾음
경사 하강법의 프로세스를 정리를 하자면

<경사 하강법 파이썬 코드 구현>
● 실습을 위한 y=4x+6 시뮬레이션 데이터 생성(단순 선형회귀로 예측할 만한 데이터 생성)
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
np.random.seed(0)
noise = np.random.randn(100,1)
X = 2 * np.random.rand(100,1)
y = 6 + 4 * X + noise
plt.scatter(X, y)

● 경사하강법
# w1과 w0를 업데이트할 w1_update, w0_update를 반환하는 함수 생성
def get_weight_updates(w1, w0, X, y, learning_rate=0.01):
N = len(y)
# w1_update, w0_update를 먼저 w1, w0의 shape와 동일한 크기를 가진 0값으로 초기화
w1_update = np.zeros_like(w1)
w0_update = np.zeros_like(w0)
# 예측 배열 계산, 예측과 실제값의 차이 계산
y_pred = np.dot(X, w1.T) + w0
diff = y - y_pred
# w0_update를 dot 행렬 연산으로 구하기 위해 모두 1값을 가진 행렬 생성
w0_factors = np.ones((N,1))
# w1과 w0를 업데이트할 w1_update, w0_update 계산
w1_update = -(2/N) * learning_rate * (np.dot(X.T, diff))
w0_update = -(2/N) * learning_rate * (np.dot(w0_factors.T, diff))
return w1_update, w0_update
# get_weight_updates 함수를 이용해 경사 하강 방식으로 반복적으로 수행하여 w1, w0를 업데이트하는 함수 생성
# 입력 인자 iters로 주어진 횟수만큼 반복적으로 w1과 w0를 업데이트
def gradient_descent_steps(X, y, iters=10000):
# w0와 w1을 모두 0으로 초기화
w0 = np.zeros((1,1))
w1 = np.zeros((1,1))
# 인자로 주어진 iters만큼 반복적으로 get_weight_updates()를 호출해 w1, w0 업데이트 수행
for ind in range(iters):
w1_update, w0_update = get_weight_updates(w1, w0, X, y, learning_rate=0.01)
w1 = w1 - w1_update
w0 = w0 - w0_update
return w1, w0
● 비용함수(RSS) 값 계산, 회귀계수 계산
# 비용함수 정의
def get_cost(y, y_pred):
N = len(y)
cost = np.sum(np.square(y - y_pred)) / N
return cost
# 회귀계수 계산
w1, w0 = gradient_descent_steps(X, y, iters=1000)
print('w1:{0:.3f}, w0:{1:.3f}'.format(w1[0,0], w0[0,0]))
# 비용함수 값 계산
y_pred = w1[0,0] * X + w0
print('cost:{0:.4f}'.format(get_cost(y, y_pred)))

● 앞에서 구한 y_pred에 기반해 회귀선 시각화
plt.scatter(X, y)
plt.plot(X, y_pred)

Reference)
예측값에 대한 설명 - 인프런 | 질문 & 답변
예측값에 대한 설명 부분에서, 100개의 데이터 X(1, 2, ... 100)이 있다면 예측값(y_pred)은 w0+X(1)w1, w0+X(2)w1, ... w0+X(100)w1 이 되어야하는것 아닌가요? - 질문 & 답변 | 인프런
www.inflearn.com
경사하강법 질문드립니다 - 인프런 | 질문 & 답변
강사님 안녕하세요 ㅎㅎ 경사하강법 강의를 듣다가 잘 이해가 가지 않아 질문드립니다. 강의 5.4의 get_weight_updates 함수에서 y_pred 에서 X의 개수가 여러개고 W1이 1개인데 왜 w1.T인지 설명해주실수
www.inflearn.com
get_cost 함수 w1[0,0]가 들어가는 이유 - 인프런 | 질문 & 답변
안녕하세요 강사님 수업 잘 듣고있습니다!파이썬 코드로 경사 하강법 구현하기에서 def get_cost 함수 y_pred = w1[0,0] * X + w0 부분에 질문이 있습니다.현재 코드가 w1와 w0 둘다 shape이 (1,1)이라서 그런
www.inflearn.com
손실함수가 최소가 되는 회귀계수를 찾아가는 경사 하강법에도 단점이 존재한다. 경사 하강법은 모든 학습 데이터에 대해 반복적으로 비용함수 최소화를 위한 값을 업데이트하기 때문에 수행 시간이 매우 오래 걸린다.
때문에 실전에서는 대부분 확률적 경사 하강법(SGD, Stochastic Gradient Descent)을 이용한다. 확률적 경사 하강법은 모든 학습 데이터가 아닌 일부 데이터만 이용해 회귀계수가 업데이트되는 값을 계산하므로 경사 하강법에 비해서 빠른 속도를 보장한다.
→ 일반적으로 말하는 SGD는 실제로 미니 배치 경사 하강법(mini-BGD)이므로, 앞으로 SGD를 떠올릴 때 미니 배치 경사 하강법을 떠올리면 된다.
Reference)
[혼공머] 배치와 미니 배치, 확률적 경사하강법
👩🔬 이번에는 혼공머 책의 챕터 4-2 파트입니다.📚 혼자공부하는머신러닝+딥러닝, 한빛미디어📄 Gradient Descent - 경사하강법, 편미분, Local Minimum📑 경사하강법(Gradient Descent)🔗 배치와 미니
velog.io
Optimizer 종류
위에서 언급했듯이, 경사 하강법의 문제점은 다음과 같다.
첫째, 한번 학습할 때마다 모든 데이터셋을 이용함
둘째, 학습률 정하기
셋째, 지역 최소점(Local Minima)
넷째, 메모리 한계
이런 경사 하강법의 문제들을 개선하기 위해 여러 Optimizer 종류들이 존재한다.
아래의 링크를 보면서 수식에 너무 얽매이지 말고 이런것들이 있구나 정도로만 이해하고, 추후 딥러닝을 공부할 때 집중적으로 다룰 것이다.
Reference)
[ML] 신경망에서의 Optimizer - 역할과 종류
지난 포스트에서 손실함수란 무엇이고, 어떻게 하면 손실함수의 최솟값이 되는 점을 찾는지 살펴보았다. 우리는 '최적화(optimization)'를 통해 파라미터를 잘 찾아서 문제 해결에 적합한 모델을 결
heeya-stupidbutstudying.tistory.com
4-3. 경사하강법 (Gradient Descent)
파이썬 머신러닝 완벽 가이드 - Chapter 5. 회귀
velog.io
● 확률적 경사하강법
- 전체 X, y 데이터에서 랜덤하게 batch_size만큼 데이터를 추출해 사용(일부 데이터)
def stochastic_gradient_descent_steps(X, y, batch_size=10, iters=1000):
w0 = np.zeros((1,1))
w1 = np.zeros((1,1))
for ind in range(iters):
np.random.seed(ind)
# 전체 X, y 데이터에서 랜덤하게 batch_size만큼 데이터를 추출해 sample_X, sample_y로 저장
stochastic_random_index = np.random.permutation(X.shape[0]) # shuffle된 배열 생성
sample_X = X[stochastic_random_index[0:batch_size]]
sample_y = y[stochastic_random_index[0:batch_size]]
# 랜덤하게 batch_size만큼 추출된 데이터 기반으로 w1_update, w0_update 계산 후 업데이트
w1_update, w0_update = get_weight_updates(w1, w0, sample_X, sample_y, learning_rate=0.01)
w1 = w1 - w1_update
w0 = w0 - w0_update
return w1, w0
w1, w0 = stochastic_gradient_descent_steps(X, y, iters=1000)
print('w1:', round(w1[0,0], 3), 'w0:', round(w0[0,0], 3))
y_pred = w1[0,0] * X + w0
print('cost:{0:.4f}'.format(get_cost(y, y_pred)))

지금까지는 단순 선형회귀에서 경사 하강법을 적용해 봤다. 피처가 여러개인 경우 어떻게 회귀계수를 도출할 수 있을까?
피처가 1개인 경우와 유사하게 내적을 이용한다.(page 319 ~ 320)
'파이썬 머신러닝 완벽가이드 > [5장] 회귀' 카테고리의 다른 글
| 다항회귀(Polynomial Regression) (0) | 2023.04.10 |
|---|---|
| LinearRegression을 이용해 보스턴 주택 가격 예측 (0) | 2023.03.20 |
| LinearRegression, 회귀 평가 지표 (0) | 2023.03.19 |
| 단순 선형 회귀를 통한 회귀 이해 (0) | 2023.03.08 |
| 회귀(Regression) (0) | 2023.03.07 |