핸즈온머신러닝

핸즈온 머신러닝[4] 모델 훈련(1)

kk_eezz 2022. 5. 13. 18:58

https://www.youtube.com/watch?v=6omvN1nuZMc&list=PLJN246lAkhQjX3LOdLVnfdFaCbGouEBeb&index=13 

 

선형회귀

MSE를 최소화하는 모델 파라미터를 찾는다.

행렬 곱셈: [θ0,θ1,..,θn][x0,x1,...,xn]^(T)

 

선형 회귀 계산 방식1: 정규 방정식

 

import numpy as np

X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

rand 함수로 0과 1 사이의 난수를 발생, 100x1 행렬이 생긴다.

randn -> 표준 분포 난수

plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([0, 2, 0, 15])
save_fig("generated_data_plot")
plt.show()

식 4-4: 정규 방정식

X_b = np.c_[np.ones((100, 1)), X]  # 모든 샘플에 x0 = 1을 추가합니다.

theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
# (2, 100)(100, 2) --> (2, 2)
# (2, 2)(2, 100) --> (2, 100)
# (2, 100)(100, 1) --> (2, 1)

c_ 함수: 여러 행렬을 column으로 이어붙임 -> X_b 행렬은 100x2 행렬이 됨

정규 방정식을 사용해서 theta_best를 구한다.

numpy의 linearalgebra 밑의 inverse 함수를 통해 역행렬을 구함

theta_best

array([[4.21509616], [2.77011339]]) -> 절편, 기울기

X_new = np.array([[0], [2]])
X_new_b = np.c_[np.ones((2, 1)), X_new]  # 모든 샘플에 x0 = 1을 추가합니다.
y_predict = X_new_b.dot(theta_best)
y_predict

array([[4.21509616], [9.75532293]])

plt.plot(X_new, y_predict, "r-")
plt.plot(X, y, "b.")
plt.axis([0, 2, 0, 15])
plt.show()
plt.plot(X_new, y_predict, "r-", linewidth=2, label="Predictions")
plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.legend(loc="upper left", fontsize=14)
plt.axis([0, 2, 0, 15])
save_fig("linear_model_predictions_plot")
plt.show()

from sklearn.linear_model import LinearRegression

lin_reg = LinearRegression()
lin_reg.fit(X, y)
lin_reg.intercept_, lin_reg.coef_

(array([4.21509616]), array([[2.77011339]]))

lin_reg.predict(X_new)

array([[4.21509616], [9.75532293]]) -> 예측값

# 싸이파이 lstsq() 함수를 사용하려면 scipy.linalg.lstsq(X_b, y)와 같이 씁니다.
theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)
# residual: (yhat - y)**2, svd--->특잇값*rcond > 특잇값: 모두 0으로 바꿈
theta_best_svd

array([[4.21509616], [2.77011339]])

np.linalg.pinv(X_b).dot(y)

유사역행렬을 구하는 함수 (역행렬을 구하지 못하는 경우에 역행렬 근삿값을 구할 수 있다.)

array([[4.21509616], [2.77011339]])

 

경사 하강법(Gradient Descent)

비용 함수를 최소화하기 위해 반복적으로 모델 파라미터를 조정

경사 하강법의 문제 -> 이차함수가 아니기 때문에 지역 최솟값이 존재

경사하강법을 사용할 때는 사용하는 특성의 scale을 맞춰주어야 함

 

배치 경사 하강법

전체 샘플을 이용해서 경사 하강법 단계를 수행하는 것

미분값 -> 경사, 미분값을 이용해서 경사를 줄여나감

 

eta = 0.1  # 학습률
n_iterations = 1000
m = 100

theta = np.random.randn(2,1)  # 랜덤 초기화

for iteration in range(n_iterations):
    gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)
    theta = theta - eta * gradients
theta

array([[4.21509616], [2.77011339]])

X_new_b.dot(theta)

array([[4.21509616], [9.75532293]]) -> 예측값

theta_path_bgd = []

# 그래프를 그리는 함수
def plot_gradient_descent(theta, eta, theta_path=None):
    m = len(X_b)
    plt.plot(X, y, "b.")
    n_iterations = 1000
    for iteration in range(n_iterations):
        if iteration < 10:
            y_predict = X_new_b.dot(theta)
            style = "b-" if iteration > 0 else "r--"
            plt.plot(X_new, y_predict, style)
        gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)
        theta = theta - eta * gradients
        if theta_path is not None:
            theta_path.append(theta)
    plt.xlabel("$x_1$", fontsize=18)
    plt.axis([0, 2, 0, 15])
    plt.title(r"$\eta = {}$".format(eta), fontsize=16)
np.random.seed(42)
theta = np.random.randn(2,1)  # random initialization

plt.figure(figsize=(10,4))
plt.subplot(131); plot_gradient_descent(theta, eta=0.02)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.subplot(132); plot_gradient_descent(theta, eta=0.1, theta_path=theta_path_bgd)
plt.subplot(133); plot_gradient_descent(theta, eta=0.5)

save_fig("gradient_descent_plot")
plt.show()

 

확률적 경사 하강법

* 모델 파라미터 랜덤 초기화

* 에포크 시작

   -> 훈련 세트 섞기

       * 훈련 세트에서 샘플 하나 꺼내기

       * 경사 하강법 수행

       * 모델 파라미터 수정

     * 반복

* 종료

 

theta_path_sgd = []
m = len(X_b)
np.random.seed(42)
n_epochs = 50 # 50번 반복
t0, t1 = 5, 50  # 학습 스케줄 하이퍼파라미터

def learning_schedule(t):
    return t0 / (t + t1)

theta = np.random.randn(2,1)  # 랜덤 초기화

for epoch in range(n_epochs):
    for i in range(m):
        if epoch == 0 and i < 20:                    # 책에는 없음
            y_predict = X_new_b.dot(theta)           # 책에는 없음
            style = "b-" if i > 0 else "r--"         # 책에는 없음
            plt.plot(X_new, y_predict, style)        # 책에는 없음
        random_index = np.random.randint(m)	     # 실제로는 중복을 허용하지 않음
        xi = X_b[random_index:random_index+1]
        yi = y[random_index:random_index+1]
        gradients = 2 * xi.T.dot(xi.dot(theta) - yi) # gradient 값을 점점 줄임, 정답에 가까워질 수록 조심
        eta = learning_schedule(epoch * m + i)
        theta = theta - eta * gradients
        theta_path_sgd.append(theta)                 # 책에는 없음

plt.plot(X, y, "b.")                                 # 책에는 없음
plt.xlabel("$x_1$", fontsize=18)                     # 책에는 없음
plt.ylabel("$y$", rotation=0, fontsize=18)           # 책에는 없음
plt.axis([0, 2, 0, 15])                              # 책에는 없음
save_fig("sgd_plot")                                 # 책에는 없음
plt.show()                                           # 책에는 없음

theta

array([[4.21076011], [2.74856079]])

from sklearn.linear_model import SGDRegressor

sgd_reg = SGDRegressor(max_iter=1000, tol=1e-3, penalty=None, eta0=0.1, random_state=42)
sgd_reg.fit(X, y.ravel())

SGDRegressor(eta0=0.1, penalty=None, random_state=42)

sgd_reg.intercept_, sgd_reg.coef_

(array([4.24365286]), array([2.8250878]))

 

미니배치 경사 하강법

 

theta_path_mgd = []

n_iterations = 50
minibatch_size = 20

np.random.seed(42)
theta = np.random.randn(2,1)  # 랜덤 초기화

t0, t1 = 200, 1000
def learning_schedule(t):
    return t0 / (t + t1)

t = 0
for epoch in range(n_iterations):
    shuffled_indices = np.random.permutation(m)
    X_b_shuffled = X_b[shuffled_indices]
    y_shuffled = y[shuffled_indices]
    for i in range(0, m, minibatch_size):
        t += 1
        xi = X_b_shuffled[i:i+minibatch_size]
        yi = y_shuffled[i:i+minibatch_size]
        gradients = 2/minibatch_size * xi.T.dot(xi.dot(theta) - yi)
        eta = learning_schedule(t)
        theta = theta - eta * gradients
        theta_path_mgd.append(theta)

theta

array([[4.25214635], [2.7896408 ]])

theta_path_bgd = np.array(theta_path_bgd)
theta_path_sgd = np.array(theta_path_sgd)
theta_path_mgd = np.array(theta_path_mgd)
plt.figure(figsize=(7,4))
plt.plot(theta_path_sgd[:, 0], theta_path_sgd[:, 1], "r-s", linewidth=1, label="Stochastic")
plt.plot(theta_path_mgd[:, 0], theta_path_mgd[:, 1], "g-+", linewidth=2, label="Mini-batch")
plt.plot(theta_path_bgd[:, 0], theta_path_bgd[:, 1], "b-o", linewidth=3, label="Batch")
plt.legend(loc="upper left", fontsize=16)
plt.xlabel(r"$\theta_0$", fontsize=20)
plt.ylabel(r"$\theta_1$   ", fontsize=20, rotation=0)
plt.axis([2.5, 4.5, 2.3, 3.9])
save_fig("gradient_descent_paths_plot")
plt.show()

문제

1. 미니 배치 경사하강법, 확률적 경사하강법, 배치 경사하강법의 다른점을 서술하시오

2. rand 함수와 randn 함수는 어떻게 다른가?

3. 유사역행렬이 무엇이며 선형 모델의 파라미터를 구할 때 유사역행렬이 사용되는 경우는 무엇인가?

4. SVD가 무엇인지 서술하시오

5. SGD가 무엇인지 서술하시오