모델의 훈련이란
✔ 모델이 훈련 데이터에 잘 맞도록 모델 파라미터를 설정하는 것
✔ 모델이 훈련 데이터에 얼마나 잘 들어맞는지 측정해야 함
모델 훈련에 필요한 비용함수 종류
✔ MSE (Mean Squared Error)
1. 회귀 모델의 주요 손실 함수
2. 참값과 예측값의 차이인 오차들의 제곱 평균으로 정의
3. 제곱을 해주기 때문에 이상치(outlier)에 민감
✔ MAE (Mean Absolute Error)
1. 참값과 예측값의 차이인 오차들의 절대값 평균
2. MSE보다 이상치에 덜 민감
✔ RMSE (Root Mean Squared Error)
1. MSE에 root을 취해 준 것
2. 참값과 비슷한 값으로 변환하기 때문에 해석이 쉬워짐
👍 보통 quadratic(2차 곡선형태) 형태의 미분 편의성이 좋기 때문에, 회귀 모형의 비용함수로 MSE를 많이 사용한다.
1.정규방정식
import matplotlib.pyplot as plt import numpy as np x = 2 * np.random.rand(100,1) # [0, 1) 범위에서 균일한 분포 100 X 1 array y = 4 + 3*x + np.random.randn(100,1) # normal distribution(mu=0,var=1)분포 100 X 1 array plt.scatter(x,y) plt.show()
x_b = np.c_[np.ones((100,1)),x] # 모든 샘플에 index 0번에 1을 추가 # np.linalg.inv는 넘파이 선형대수 모듈(linalg)의 inv(역함수) # .dot은 행렬 곱셈 theta_best = np.linalg.inv(x_b.T.dot(x_b)).dot(x_b.T).dot(y) theta_best > array([[ 4.93296992], [-0.41991499]])
# theta_best를 사용해서 y 값 예측 x_new = np.array([[0],[2]]) x_new_b = np.c_[np.ones((2,1)),x_new] prediction = x_new_b.dot(theta_best) prediction > array([[4.93296992], [4.09313993]]) plt.plot(x_new,prediction,"r-") plt.plot(x,y,"b.") plt.axis([0,2,0,15]) # x축 범위 0~2, y축 범위 0~15 plt.show()
from sklearn.linear_model import LinearRegression lin_reg = LinearRegression() lin_reg.fit(x,y) print(lin_reg.intercept_,lin_reg.coef_) > [3.88883297] [[2.98499978]] print(lin_reg.predict(x_new)) >array([[3.88883297], [9.85883253]])
2.경사 하강법
여러 종류의 문제에서 최적의 방법을 찾을 수 있는 매우 일반적인 최적화 알고리즘
기본 메커니즘은 지정한 비용 함수를 최소화하기 위해 파라미터를 반복적으로 수정하는 것
짙은 안개 속, 앞이 전혀 보이지 않고 오로지 발 끝에 산의 기울기만 느낄 수 있다고 생각해보자. 이 숲을 벗어나기 위한 가장 좋은 방법은 가장 기울기가 급한 길을 따라서 내려가는 것이다. 이것이 경사 하강법 원리다.
무작위로 벡터 θ를 초기화한다.
파라미터 벡터 θ에 대해 비용 함수(Loss Function)의 현재 그래디언트를 계산한다.
그리고 그래디언트가 감소하는 방향으로 진행하면서, 최종적으로 계산된 그래디언트가 0이 되면 최솟값에 도달하도록 해야 한다.
위 그림처럼 경사 하강법에서 최적화 시키는 방향으로 가게 하는 중요한 하이퍼파라미터 step(learning rate)를 결정해야 한다.
실제 모든 비용함수는 위와 같이 quadratic(이차원)하게 표현되지 않고 울긋불긋하게 솟았다가 내려앉았다가 한다.
학습률이 너무 작은 경우에는 local minumum(지역 최소값)에 빠지게 된다.
학습률이 너무 큰 경우에는 수렴이 되지 않게 된다.
그래서 위 그림처럼 학습률을 잘 조정해야지, global minumum(최적의 값)으로 잘 수렴할 수 있다.
다행히도 선형 회귀의 MSE 비용 함수는 convex function(볼록 함수)이기 때문에, local minum이 없고, global minimum만 존재한다.
그래서 충분한 시간과 적절한 학습률만 주어진다면, global minimum에 최대한 근접할 수 있다.
위 그림처럼 오른쪽의 경사 하강법은 곧장 global minimum으로 내려갈 수 있다.
왼쪽의 그림에서 완만한 경사를 만나게 되면 global minimum으로 내려갈 수는 있지만 더 오래 걸린다.
경사 하강법 전에는 반드시 모든 특성을 같은 스케일을 사용하여서 데이터 변환을 하여야 한다.
scikit learn(사이킷런) 라이브러리에서 각 특성에서 평균을 빼고 표준편차로 나누어 평균을 0 분산을 1로만드는 StandardScaler을 사용하곤 한다.
✔ 배치 경사 하강법
# 경사 하강법 구현(implementation) import numpy as np x = 2 * np.random.rand(100,1) # 100 x 1 크기의 0~1의 균일분포 x_b = np.c_[np.ones((100,1)),x] # bias(1)를 전체 데이터에 추가 y = 4 + 3*np.random.randn(100,1) # 100 x 1 크기의 표준정규분포 추출 learning_rate = 0.001 iterations = 1000 m = x_b.shape[0] # 100개 (x 데이터) theta = np.random.randn(2,1) # 2x1 크기의 평균 0, 분산1 정규 분포 추출 for iteration in range(iterations): gradients = 2/m * x_b.T.dot(x_b.dot(theta)-y) theta = theta - (learning_rate * gradients) # 정규방정식으로 찾은 것과 정확히 일치한다. theta > array([[ 4.93296992], [-0.41991499]])
✔ 확률적 경사 하강법
# 확률적 경사 하강법 구현(implementation) epochs = 1000 t0,t1 = 5,50 # 학습 스케쥴 (하이퍼 파라미터) m = x_b.shape[0] # 100개 (x 데이터) def learning_schedule(t): return t0 / (t+t1) theta = np.random.randn(2,1) # 2x1 크기의 평균 0, 분산1 정규 분포 추출 for epoch in range(epochs): for i in range(m): random_index = np.random.randint(m) # 0 ~ m-1까지 랜덤 숫자 1 xi = x_b[random_index:random_index:+1] # 1 x 2 크기 yi = y[random_index:random_index+1] # 1 x 1 크기 gradients = 2 * xi.T.dot(xi.dot(theta)-yi) # 1 => mini_m learning_rate = learning_schedule(epoch*m + i) theta = theta - learning_rate * gradients
✔ 미니배치 경사 하강법
사이킷런의 SGDRegressor와 SGDClassifier에서 partial_fit 메서드를 사용하여 모델 파라미터를 초기화하지 않고 미니배치 학습을 위해 반복적으로 호출할 수 있다. 하지만 partial_fit 메서드는 fit 메서드와 동일하게 미니배치의 샘플을 하나씩 적용하므로 엄밀히 말하면 미니배치 경사 하강법 알고리즘은 아니다. - 핸즈온 머신러닝 -
import numpy as np data_num = 1000 x = 3 * np.random.rand(data_num,1) - 1 y = 0.2 * (x**2) + np.random.randn(1000,1) from sklearn.preprocessing import PolynomialFeatures poly_features = PolynomialFeatures(degree=2,include_bias=False) x_poly = poly_features.fit_transform(x) print(x[0]) print(x_poly[0]) >[0.35316774] >[0.35316774 0.12472745] from sklearn.linear_model import LinearRegression lin_reg = LinearRegression() lin_reg.fit(x_poly,y) print(lin_reg.intercept_,lin_reg.coef_) >[-0.0427474] [[-0.05998741 0.25104612]]
예측 모델의 식은 y_hat = 0.25x^2 - 0.05x^1 + 1 이다. 실제 원래 홤수와 거의 비슷해졌다.
특성이 여러 개 일 때 다항 회귀는 이 특성 사이의 관계를 찾을 수 있습니다.(PolynomialFeatures를 통해서 주어진 차수까지 특성 간의 모든 교차항을 추가할 수 있기 때문)
훈련 세트와 검증 세트의 모델 성능을 살펴 보는 것, (모델 과적합을 가시적으로 확인 하는 법)
from sklearn.metrics import mean_squared_error from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt def plot_learning_curves(model,x,y): x_train,x_val,y_train,y_val = train_test_split(x,y,test_size=0.2) train_errors,val_errors = [],[] for num in range(1,len(x_train)): model.fit(x_train[:num],y_train[:num]) y_train_predict = model.predict(x_train[:num]) y_val_predict = model.predict(x_val) train_errors.append(mean_squared_error(y_train[:num],y_train_predict)) val_errors.append(mean_squared_error(y_val,y_val_predict)) plt.plot(np.sqrt(train_errors),'r-+',linewidth=2,label='train_set') plt.plot(np.sqrt(val_errors),'b-',linewidth=3,label='val_set') plt.legend() plt.show()
from sklearn.pipeline import Pipeline data_num = 100 x = 3 * np.random.rand(data_num,1) - 1 y = 0.2 * x**2 + np.random.randn(100,1) polynomial_regression = Pipeline([ ("poly_features",PolynomialFeatures(degree=4,include_bias=False)), ("lin_reg",LinearRegression()) ]) plot_learning_curves(polynomial_regression,x,y)
from sklearn.linear_model import Lasso lasso_reg = Lasso(alpha=0.1) lasso_reg.fit(x,y) lasso_reg.predict([[1.5]]) >array([0.22365566])
# 안드레 루이 숄레스키가 발견한 행렬 분해(matrix factorization) 사용 # 숄레스키 분해의 장점은 성능이다. 원래 ridge의 solver default값은 'auto'이며 희소 행렬이나 특이 행렬이 아니면 'cholesky'가 된다. from sklearn.linear_model import Ridge ridge_reg = Ridge(alpha=0.1,solver='cholesky') ridge_reg.fit(x,y) ridge_reg.predict([[1.5]]) >array([[0.29217567]])
from sklearn.linear_model import ElasticNet elastic_net = ElasticNet(alpha=0.1,l1_ratio=0.5) elastic_net.fit(x,y) elastic_net.predict([[1.5]]) >array([0.23234511])
from sklearn.base import clone from sklearn.preprocessing import PolynomialFeatures,StandardScaler from sklearn.linear_model import SGDRegressor from sklearn.pipeline import Pipeline from sklearn.metrics import mean_squared_error x = 3 * np.random.rand(data_num,1) - 1 y = 0.2 * x**2 + np.random.randn(100,1) poly_scaler = Pipeline([ ("poly_features",PolynomialFeatures(degree=90,include_bias=False)), ('std_scaler',StandardScaler()) ]) x_train,x_val,y_train,y_val = train_test_split(x,y,test_size=0.2) x_train_poly_scaled = poly_scaler.fit_transform(x_train) x_val_poly_scaled = poly_scaler.transform(x_val) # warm_start=True 이면 fit 메서드가 호출될 때 처음부터 다시 하지 않고 이전 모델 파라미터에서 훈련 이어짐 # penalty : {‘l2’, ‘l1’, ‘elasticnet’}, default=’l2’ # n_iter_no_change : Number of iterations with no improvement to wait before stopping fitting # 'constant' : eta = eta0 # 'optimal' : eta = 1.0 / (alpha * (t + t0)) # 'invscaling' : eta = eta0 / pow(t, power_t) # 'adaptive' : eta = eta0, as long as the training keeps decreasing sgd_reg = SGDRegressor(n_iter_no_change=1,warm_start=True,penalty=None, learning_rate='constant',eta0=0.0005) SGDRegressor() minimum_val_error = float('inf') best_epoch = None best_model = None for epoch in range(1000): sgd_reg.fit(x_train_poly_scaled,y_train.ravel()) y_val_predict = sgd_reg.predict(x_val_poly_scaled) val_error = mean_squared_error(y_val,y_val_predict) if val_error<minimum_val_error: minimum_val_error = val_error best_epoch = epoch best_model = clone(sgd_reg) print('best_epoch : ',best_epoch) print('best_model : ',best_model)
오늘 배운 내용들은 뒤에서도 계속해서 반복되면서, 앞으로 모든 머신러닝의 원리에 적용되는 부분이니 잘 이해해두면 좋을 것이고, 오늘 긴 이 내용을 본 당신은 머신러닝의 첫 발을 내딛은 것이다.