본문 바로가기

머신러닝

경사하강법(Gradient Discent)

반응형

배경

1차원 선형 함수에 대해서 직관 + 수동적인 방법으로 선형모델의 w, b 값을 찾아보았었다

그 과정에서 손실(loss)값이 줄어드는 방향으로 진행하였고 이것은 곧 선형대수에서 말하는 함수의 편미분(기울기) 값이 0인 부분을 찾는다는 것을 뜻한다

따라서, 이번엔 경사하강법(Gradient Descent)을 사용하여 모델을 트레이닝시켜보도록 한다

 

 

경사하강법(Gradient Descent)

f(x), f^(x) 를 비교하여, 손실 경향성(기울기)을 이용, 극값(global minimum)을 찾기 위한 방법론

→ 함수의 편미분(기울기)를 이용하므로 직관적이고 적용하기 쉽다는 장점

→ 단, epoch(세대) 이 진행되는 과정에서 학습률(learning rate)을 얼마나 반영할지, 다차원 함수의 경우 극값을 찾기 어려운 것을 어떻게 고려해야할지, 영향력이 작은 피쳐들을 어떻게 판별하고 제거해나갈지 등 다양한 이슈들이 남아있다

 

 

 

다차원 경사하강법 추가조사

선형함수의 극점은 단 한개이기 때문에 학습하기 좋았지만 실제 함수들은 더 복잡한 형태를 가진다. 다차원 함수에 경사하강법을 적용하는 것은 훨씬 복잡한 문제로 보인다

다차원 경사하강법 + 데모 시뮬레이션

→ 피쳐가 많을경우 초기값을 잡기도 어렵고 피팅해나가기도 어렵다는 것을 지난 실험과 이번 실험을 통해 알 수 있었다

→ 글로벌 미니멈 지점(극점)을 찾길 원하는데 이때 함수(모델)의 기울기를 활용한다는 것을 배웠다. 하지만 로컬 미니멈들의 존재 여부가 극점을 찾는데 어려움이 생긴다

→ 추가적으로, (위 레퍼런스와 관련 자료들에서)기울기에 대한 편미분값인 곡률(기울기의 기울기)을 활용하여 극점 여부를 판단, 나아가서 모델 최적화를 할 수 있다는 것처럼 말하고 있는것 같다 (내용이 어려워 느낌적으로)

→ 헤세 함수, 헤세 행렬(곡률 정보)을 통해 극점인지, 로컬 미니멈인지 판단할 수 있다는 것 같다

 

 

실습

모델, 트레이닝 추상화 적용

 

import numpy as np
from lib.models.model import Model
class Linear(Model):
def __init__(self, w: float=.0, b:float=.0) -> None:
self.w = w
self.b = b
def get_params(self) -> dict:
return {'w': self.w, 'b': self.b}
def set_params(self, w: float, b:float) -> None:
self.w = w
self.b = b
def forward(self, x:np.array) -> np.array:
self._x = x
return self.w * x + self.b
def backward(self, grad:np.array) -> np.array:
self._d_w = self._x * grad
self._d_b = grad
return self.w * grad
def update(self, lr:float) -> None:
self.w -= lr * self._d_w.mean()
self.b -= lr * self._d_b.mean()
class Model(object):
def __init__(self) -> None:
raise NotImplementedError
def set_params(self) -> None:
raise NotImplementedError
def get_params(self) -> dict:
raise NotImplementedError
import pandas as pd
import numpy as np
import plotly.express as px
import os
import sys
root_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir))
lib_path = os.path.join(root_path, "lib")
sys.path.append(root_path)
sys.path.append(lib_path)
from lib.models.model import Model
from lib.models.linear import Linear
from lib.models.training import Training
class Trainer(Training):
def __init__(self, model: Model, history: list = []) -> None:
self.model = model
self.history = history
def train(self, x: np.array, y: np.array, epochs: int, lr: float) -> None:
if len(x) != len(y):
raise ValueError(f"x and y must have the same length. Got {len(x)} and {len(y)} instead.")
if len(self.history) > 0:
self.history = []
# TODO (개선)동일한 데이터로만 학습하고 있다.
for epoch in range(epochs):
y_pred = self.model.forward(x)
loss = self.forward(y_pred, y)
w = self.model.get_params()['w']
b = self.model.get_params()['b']
print(f"Epoch: {epoch} | Loss: {loss} | w={w:.4f} | b={b:.4f}")
self.model.backward(self.backward())
self.model.update(lr)
self.history.append({'w':w, 'b':b, 'loss':loss, 'epoch':epoch})
def forward(self, y_hat:np.array, y_true:np.array) -> np.array:
self._hat = y_hat
self._true = y_true
return ((y_hat - y_true)**2).mean()
def backward(self) -> np.array:
return 2*(self._hat - self._true)
def history_df(self) -> pd.DataFrame:
df = pd.DataFrame(self.history)
return df
def simulrate(self, xs: np.array, ys: np.array, epochs: int, lr: float):
"""
prerequisite: plotly, pandas, nbformat
피팅 과정을 시각화
"""
df = pd.DataFrame(self.history, columns=['w','b'])
df = df.set_index(df.index.set_names('epoch')).reset_index()
df0 = df.copy()
df1 = df.copy()
df0['x'] = xs.min()
df1['x'] = xs.max()
df = pd.concat([df0, df1]).reset_index(drop=True)
df['y'] = df.w * df.x + df.b
fig = px.line(df, x='x', y='y', animation_frame="epoch", width=500, height=500)
fig.layout.updatemenus[0].buttons[0].args[1]['frame']['duration'] = 0.1
fig.layout.updatemenus[0].buttons[0].args[1]['transition']['duration'] = 0.1
fig.layout.updatemenus[0].buttons[0].args[1]['frame']['redraw'] = True
fig.add_scatter(x=xs.flatten(), y=ys.flatten(), mode='markers', name='data', marker={'size':2})
for i, frame in enumerate(fig.frames):
frame['layout']['title_text'] = f"Prediction: y = {self.history[i]['w']:.4f}x{'' if self.history[i]['b'] < 0 else '+'}{self.history[i]['b']:.4f}"
fig.update_layout(template='plotly_dark')
fig.show()
if __name__ == '__main__':
model = Linear(w=0, b=0)
t = Trainer(model)
xs = np.random.rand(1000)
ys = model.forward(xs) + np.random.normal(0, 0.1, 1000)
t.simulrate(xs, ys, epochs=200, lr=0.1)
import numpy as np
class Training():
def forward(self, x:np.array) -> np.array:
raise NotImplementedError
def backward(self, grad:np.array) -> np.array:
raise NotImplementedError
# %%
from lib.models.linear import Linear
f = lambda x: x*2.0 + 1.0
weight = 0.
bias = 0.
lin_model = Linear(w=weight, b=bias)
# %%
import numpy as np
xs = np.random.rand(1000, 1) # 1000x1
ys = f(xs) + 0.1*np.random.randn(1000, 1)
import matplotlib.pyplot as plt
plt.scatter(xs, ys, marker='.')
plt.plot(xs, f(xs), label=f"y = 2x + 1")
plt.legend()
plt.show()
# %%
from lib.models.trainer import Trainer
trainer = Trainer(model=lin_model)
trainer.train(xs, ys, epochs=200, lr=0.2)
# %%
df = trainer.history_df()
df
# %%
trainer.simulrate(xs, ys, epochs=200, lr=0.2)
view raw test.py hosted with ❤ by GitHub
반응형

'머신러닝' 카테고리의 다른 글