경사하강법 : 함수의 극솟값을 찾는 하나의 알고리즘. 함수의 그래프 위 한 점에서 여러 변수 중 어떤 변수를 늘이고 줄여야 함숫값이 가장 많이 감소하는지 확인하고 그에 따라 변수들의 값을 늘이고 줄이는 과정.
하이퍼 파라미터 : 모델을 학습시키는 과정에 영향을 주는 파라미터. 직접 결정해야하는 값
경사하강법의 파라미터
- Wold의 초기값 : 보통 정규분포 등으로 부터 무작위 추출(Random)
- 학습률 : 학습률이 너무 크면 목표지점 지나칠 수 있음. 너무 작으면 변화량이 너무 작아 목표지점까지 도달하기 힘들 수 있음
- 알고리즘의 종료조건
파이썬으로 구현해보기
# 경사하강법을 이용하여 선형 회귀모델 학습
1. 필요한 라이브러리 import, 데이터 가져오기
``` python
!pip install xarray
from pathlib import Path
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib notebook
import numpy as np
import xarray as xr
import mygrad as mg
# 데이터 불러오기
draft_data = xr.load_dataset(Path.cwd() / 'nba_draft_measurements.nc')
fig, ax = plt.subplots()
x = draft_data.height_no_shoes.data
y = draft_data.wingspan.data
ax.scatter(x,y)
ax.grid()
```
2. 선형회귀
``` python
#최소 제곱법을 통해 최적의 기울기와 y절편값 구함
def ordinary_least_squares(x,y):
N = x.size
m = (np.matmul(x,y)-x.sum()*y.sum()/N)/(np.matmul(x,x)-(1/N)*x.sum()**2)
b = y.mean()-m*x.mean()
return m,b
```
3. 여러 m,b 값에 대한 손실함수(MeanSquaredError) 그래프 시각화하기
``` python
def graph_linear_regression_mse(
x,
y,
trajectory=None,
m_scale = 10,
b_scale = 10,
sample_density=500,
):
def(x,y,m,b):
m = np.atleast_1d(m)
b = np.atleast_1d(b)
return ((x*m[None]+b[None]-y)**2).mean(axis=1)
A = np.vstack([x, np.ones(len(x))]).T
m_opt, b_opt = np.linalg.lstsq(A, y, rcond=None)[0] #최소 제곱 해 구해주는 함수
I_opt = mse(x,y,m_opt, b_opt)
center_m = m_opt
center_b = b_opt
#MSE 그래프의 figure 생셩
fig = plt.figure()
ax = fig.gca(projection='3d')
#MSE 그래프의 최소점을 그래프 위에 검은 점으로 표시
ax.plot(
[m_opt],
[b_opt],
I_opt,
c='black',
marker='o',
zorder=3,
markersize=7,
)
#그래프를 그리는데 사용할 m,b 값들을 추출하여 numpy 배열의 형태로 생성, 이들 값에 대응되는 MSE 값들 계산
m_series = np.linspace(center_m- m_scale, center_m + m_scale, sample_density)
b_series = np.linspace(
center_b-b_scale, center_b+b_scale, sample_density
).reshape(-1,1)
Z = (b_series+x.reshape(-1,1,1) * m_series) - y.reshape(-1,1,1)
Z = np.mean(z**2, axis=0)
#그래프 그리기
m_series, b_series = np.meshgrid(m_series, b_series)
ax.set_xlabel('slobe:m')
ax.set_ylabel('Intercept:b')
ax.set_zlabel('MSE LOSS')
ax.ticklabel_format(style='sci', scilimits=(-1,2))
ax.dist = 11
surf = ax.plot_surface(m_series, b_series, Z, cmap = plt.get_cmap('GnBu'))
#지정한 trajectory를 그래프 위에 나타내기
if trajectory is not None:
trajectories = np.atleast_2d(trajectory)
if trajectories.ndim == 2:
trajectories = trajectories[np.newaxis]
for trajectory in trajectories:
m_values, b_values = trajectory.T
I_values = ((x*m_values[:,None]+ b_values[:,None]-y)**2).mean(axis=1)
ax.plot(m_values, b_values, I_values, marker='*', zorder=3, markersize=7,)
return fig, ax
```
4. 선형 회귀 모델을 경사하강법으로 학습시키기 위한 기초 함수 만들기
``` python
def gradient_step(tensors, learning_rate): #경사하강법 구현 함수
if isinstance(tensors, mg.Tensor):
tensors = [tensors]
for tensor in tensors:
if tensor.grad is not None:
tensor.data -= learning_rate*tensor.grad
import mygrad as mg
def mean_squared_error_mygrad(y_pred, y_true):
#MSE 계산 함수
return mg.mean((y_pred - y_true)**2)
true_params = np.array(ordinary_least_squares(height, wingspan))
def dist_from_true(model_params, true_params)-> float:#->은 주석
params = np.array([i.item() for i in model_params])
return np.sqrt(np.sum((true_params-params)**2))
```
5. 선형 모델 클래스 만들기
``` python
from mygrad.nnet.initializers import uniform
#모델 파라미터의 초기화(균등분포로부터 랜덤 추출)를 위한 uniform 함수 import
class LinearModel:
def initialize_params(self):
self.m = uniform(1, lower_bound=-10, upper_bound = 10)
self.b = uniform(1, lower_bound=-10, upper_bound = 10)
def __init__(self, m=None, b=None):
self.initialize_params()
if m is not None:
self.m = m
if b is not None:
self.b = b
def __call__(self, x):
y = self.m*x+self.b
return y
@property
def parameters(self):
t = (self.m, self.b)
return t
#클래스 생성
model = LinearModel()
```
6. 경사하강법을 통해 모델 학습시키기
``` python
heights = draft_data.height_no_shoes.data
wingspans = draft_data.wingspan.data
model = LinearModel()
trajectory = [] #모델 파라미터를 저장할 배열
num_epochs = 10
learning_rate = 1E-4
from noggin import create_plot # create_plot 함수를 import
plotter, fig, ax = create_plot(["loss", "m", "b", "dist_from_target"], ncols=2)
for n in range(num_epochs):
y_pred = model(heights)
loss = mean_squared_error_mygrad(y_pred, wingspans)
loss.backward()
plotter.set_train_batch(dict(loss=loss, m=model.m.item(), b=model.b.item(), dist_from_target = dist_from_true(model.parameters, true_params),), batch_size = len(y_pred),) #nogging 라이브러리를 활용하여 그래프를 실시간으로 채움
trajectory.append((model.m.item(), model.b.item()))
gradient_step(model.parameters, learning_rate)
plotter.plot()
graph_linear_regression_mse(height, wingspan, trajectory=trajectory)
# 데이터의 산점도, 이상적인 선형 회귀 모델, 직접 구한 선형 회귀 모델 시각화
fig, ax = plt.subplots()
ax.scatter(height, wingspan)
m, b = ordinary_least_squares(height, wingspan)
x = np.linspace(height.min(), height.max(), 1000)
ax.plot(x, model.m * x + model.b, c="orange", label="Learned Fit")
ax.plot(x, m * x + b, c="red", ls= "--", label="Ideal Fit")
ax.legend()
ax.grid(True)
ax.set_xlabel("Height [inches]")
ax.set_ylabel("Wingspan [inches]")
```
- 선형회귀를 그래프로 그렸지만 b값이 맞지 않음
- 초기값에 따라 학습된 모델의 결과가 달라지기 때문
7. 데이터 전처리(표준화)
- 이전에 학습이 원활히 되지 않은 것은 b보다 m(기울기)이 더욱 민감하게 변화하기 때문
``` python
def norm(x):
normed_x = (x-x.mean())/x.std()
return normed_x
```
``` python
normed_height = norm(height)
normed_wingspan = norm(wingspan)
fig, ax = plt.subplots()
ax.scatter(normed_height, normed_wingspan)
ax.grid()
```
``` python
# 여기에 코드 작성
plotter, fig, ax = create_plot(['loss','m','b','dist_from_target'], ncols=2, last_n_batches=50)
# trajectory 리스트 초기화
trajectory=[]
# class instance 선언
model = LinearModel()
# epochs, learning rate 적절히 설정
# epochs 최대 50회 설정 가능, 50회 넘어갈 시 Step 8 점수 0점
# learning rate 값을 변화시켜 가며, dist_from_taret 함수의 값을 감소시킬 것
num_epochs=40
learning_rate = 0.1
# ordinary_least_squares 함수 이용해 normalized height, normalized wingspan 에 대해
# m*, b* 값 계산 후 true_params_normed 에 저장
true_params_normed = np.array(ordinary_least_squares(normed_height, normed_wingspan))
for n in range(num_epochs):
# normalized height 데이터를 이용해 모델의 예측값 계산
y_pred = model(normed_height)
# 오차함수 계산
loss = mean_squared_error_mygrad(y_pred, normed_wingspan)
# 오차함수 역전파
loss.backward()
# noggin 그래프 그리기
plotter.set_train_batch(
dict(
loss = loss,
m = model.m.item(),
b = model.b.item(),
dist_from_target = dist_from_true(model.parameters, true_params_normed),
),
batch_size=len(height),
)
# (m, b)의 값을 trajectory list 에 append
trajectory.append((model.m.item(), model.b.item()))
# 경사하강법 1 step 진행
gradient_step(model.parameters, learning_rate)
plotter.plot()
'''
Displaying Errors (No blank).
아래에는 빈칸 없습니다. 그대로 실행하시면 됩니다.
'''
train_metrics = plotter.to_xarray("train").batch
train_metrics.dist_from_target[-1]
graph_linear_regression_mse(normed_height, normed_wingspan, trajectory=trajectory)
fig, ax = plt.subplots()
ax.scatter(normed_height, normed_wingspan)
m, b = ordinary_least_squares(normed_height, normed_wingspan)
x = np.linspace(normed_height.min(), normed_height.max(), 1000)
ax.plot(x, model.m * x + model.b, c="orange", label="Learned Fit", lw="4")
ax.plot(x, m * x + b, c="red", ls= "--", label="Ideal Fit")
ax.legend()
ax.grid(True)
ax.set_xlabel("Normed Height")
ax.set_ylabel("Normed Wingspan");
num_models = 10
trajectories = [[] for i in range(num_models)]
models = [LinearModel() for i in range(num_models)]
num_epochs = 100
step_size = 1E-1
for n in range(num_epochs):
for model_id, model in enumerate(models):
y_pred = model(normed_height)
loss = mean_squared_error_mygrad(y_pred, normed_wingspan)
loss.backward()
trajectories[model_id].append((model.m.item(), model.b.item()))
gradient_step(model.parameters, learning_rate=learning_rate)
trajectories = np.array(trajectories)
fig, ax = graph_linear_regression_mse(normed_height, normed_wingspan, trajectory=trajectories)
```
9. 표준화한 데이터를 통해 얻은 모델을 원래 모델에 맞게 조정하기
``` python
def processed_predictions(model, x, height_mean = height.mean(), height_std = height.std(), wingspan_mean = wingspan.mean(), wingspan_std = wingspan.std(),):
new_x = (x-height_mean)/height_std
return mg.asarray(model(new_x))*wingspan_std + wingspan_mean
fig, ax = plt.subplots()
ax.scatter(height, wingspan)
x = np.linspace(height.min(), height.max(), 1000)
# 모델을 통해 최종적으로 얻는 y 계산
# 여기에 코드 작성
y = processed_predictions(model,x)
ax.plot(x, y, color="orange", lw=4, label="Learned Model")
m, b = ordinary_least_squares(height, wingspan,)
ax.plot(x, m * x + b, c="red", label="Ideal Fit")
ax.grid(True)
ax.legend()
ax.set_xlabel("Height [inches]")
ax.set_ylabel("Wingspan [inches]")
```