일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- dictionary
- 선적분
- Class
- 함수
- 벡터 해석
- 자바
- java
- 2P1L
- 파이썬
- 델
- 딕셔너리
- BST
- 신경망
- cURL
- 자료형
- auto-encoder
- 백트래킹
- 소행성
- 회로이론
- 딥러닝
- 강화학습
- 피보나치 수열
- 이진탐색트리
- 벡터해석
- Asteroid RL
- 계단 오르기
- 코드업
- 최단 경로
- Python
- 미분 방정식
- Today
- Total
Zeta Oph's Study
다항 회귀 (Polynomial Regression) 본문
이 글에서는 다항 회귀(polynomial regression)에 대해 설명하겠습니다.
흔히들 아는 선형 회귀는 $y=ax+b$꼴의 일변수 일차식을 회귀하는 것입니다. 다항 회귀는, 아래와 같은 n차 다항식에 대해 회귀를 하는 것이라고 할 수 있습니다.
$$y=a_0+a_1x+a_2x^2+...+a_nx^n$$
다항 회귀는 다중 선형 회귀 방법에 기반합니다. 다중 선형 회귀는 $y=a_0+a_1x_1+a_2x_2+...$의 다변수 일차식을 회귀하는 것인데, 다항 회귀는 $x^2, x^3, ...$ 자체를 하나의 변수로 보고 다중 선형 회귀를 진행하는 것입니다. 그렇다면 다중 선형 회귀는 어떻게 진행할까요?
회귀는 기본적으로 예측값과 실제값의 오차를 줄이는 것이 목표입니다. 이때 양의 오차와 음의 오차가 있으므로 오차의 크기만을 고려하기 위해 제곱을 하여 다 더한 것의 평균을 평균 제곱 오차(Mean Squared Error, MSE)라고 합니다. 이 MSE가 최소가 되도록 하는 계수를 찾는 것이 회귀라고 할 수 있습니다. 좀 더 자세히, MSE는 아래와 같이 나타낼 수 있습니다.
$$MSE = \sum{(y_0 - y)} = \sum{(y_0 - (a_0+a_1x_1+a_2x_2+...))}$$
우리는 MSE가 최소가 되도록 하는 과정에 경사 하강법을 사용할 수 있습니다. 계수 $a$에 대해, 그리고 함수 $f$에 대해 경사하강법을 진행하는 것은 아래와 같은 식을 계속해서 반복하는 것입니다.
$$a_{i+1}=a_i - \gamma\frac{\partial f}{\partial a}$$
여기서 $\gamma$는 경사 하강을 진행하며 한 step 당 얼마나 이동할 것인가를 결정하는 상수로, 딥러닝에서는 이를 학습률이라고 부릅니다. 이 경사 하강을 모든 계수에 대해 진행해주어 MSE가 최소가 되도록 하는 계수를 찾는 것는 것을 '학습한다'라고 합니다.
우리는 MSE에 대해 경사하강을 진행하므로, MSE를 편미분하여 식을 아래와 같이 구체적으로 쓸 수 있습니다.
$$a_{n, i+1}=a_{n, i} + \gamma\frac{2}{n}\sum{(x_n(y_0-y))}$$
이 식을 이용하여 모든 계수에 대해 매 반복마다 위 연산을 해주어 MSE에 대해 최적화를 진행하는 것입니다.
위에서 설명한 배경이론을 코드로 짜보기 위해, 먼저 순서도를 그리면 아래와 같습니다.
위 순서도를 바탕으로, python으로 코드를 작성해봅시다.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
df = pd.read_csv("C:/Users/dlgkr/Desktop/code/informatics/test_data.csv", encoding='CP949')
df = pd.DataFrame(df, columns=['x','y'])
x = df['x'].to_numpy()
y = df['y'].to_numpy()
print(x)
x2 = x**2
#coefficient initializationi
a0 = 0
a1 = 0
a2 = 0
lr = 0.002 #learning rate
epoch = 201
def f(val1, val2):
return a0 + a1*val1 + a2*val2
def err(y_val, pre_val):
return y_val - pre_val
for i in range(epoch):
y_pre = f(x, x2)
error = err(y, y_pre)
#partial diff. for each coef.
mse_diff_a1 = -(2/len(x))*sum(x*error)
mse_diff_a2 = -(2/len(x))*sum(x2*error)
mse_diff_a0 = -(2/len(x))*sum(error)
#gradient descent
a1 = a1 - lr*mse_diff_a1
a2 = a2 - lr*mse_diff_a2
a0 = a0 - lr*mse_diff_a0
if i%20 == 0:
print("epoch=%d, a1=%.04f, a2=%.04f, a0=%.04f"%(i, a1, a2, a0))
#show graph
plt.plot(x, y, 'b.')
plt.plot(x, f(x, x2), 'r-')
plt.show()
간단히 코드를 설명하겠습니다. 학습을 진행할 함수는 f로 정의해주었고, 예측값과 실제값 사이 오차 또한 함수로 정의하여 사용하였습니다. 학습률 lr=0.002로 작게 설정해주어 이차함수를 회귀하여 값이 크게 나와 오버플로우가 일어나는 것을 방지해주었습니다. 반복 횟수 epoch만큼 경사하강 과정을 반복해주었는데, 매 반복마다 y값과 error를 새로 계산하고, 각 계수에 대한 MSE의 편미분계수를 계산하고, 경사하강 식을 이용하여 계수들의 값들을 업데이트 해주었습니다. 또한 중간중간 20번마다 경사하강 진행 상황을 출력해주었습니다.
아래는 임의로 오차를 주어 만든 2차함수 데이터를 학습한 결과입니다. 보다시피, 학습이 잘 되었음을 확인할 수 있었습니다.
추가로 주의할 점이 있습니다. 학습률이 경사하강에서 한 번에 움직이는 step size라고 했죠. 그래서 학습률을 잘 정해야 합니다. 학습률이 너무 작다면 학습이 효과적으로 진행되지 않을 것이고, 너무 크다면 극솟값을 찾지 못하고 발산해버려 오버플로우가 날 것입니다. 저 같은 경우에는 이차함수를 회귀하고 데이터 값의 크기가 커서 그런지 lr=0.02로도 오버플로우가 쉽게 났었고, lr=0.002로 변경하여 이 문제를 해결했습니다.
'프로그래밍' 카테고리의 다른 글
신경망의 구현 - 딥러닝 공부 (2) (1) | 2023.05.31 |
---|---|
퍼셉트론과 신경망 - 딥러닝 공부 (1) (1) | 2023.05.31 |
(연립) 미분 방정식의 수치적 해법 (2) : 룽게-쿠타 방법(Runge-Kutta Method)과 로트카-볼테라 방정식(Lotka-Volterra Equation) (1) | 2023.04.01 |
미분 방정식의 수치적 해법 (1) : 오일러 방법(Euler's Method)과 종단속력(Terminal Velocity) (1) | 2023.04.01 |
matplotlib을 이용한 우주 거대 구조 지도 그리기 (1) | 2023.03.22 |