Machine Learning

머신러닝 모델 - 학습곡선(다항 회귀의 차원 설정)

BEOKS 2020. 4. 16. 12:58

들어가며

이전 글에서는 데이터 간의 비선형관계를 구하기 위한 다항회귀에 대해서 알아보았습니다. 이제 데이터에 적합한 차수(degree( 2차, 3차 등 ))를 어떻게 구할지 고민해야합니다. 이번에는 학습곡선을 통해서 다항회귀를 할때 차수를 적절하게 구하는 방법에 대해서 알아보도록 하겠습니다.

목차

1. 차수의 중요성

2. 학습곡선을 통한 적합성파악

3. 모델의 일반화 오차

 

1. 차수의 중요성

출처 : https://github.com/ageron/handson-ml2/blob/master/04_training_linear_models.ipynb

위 그림은 이전 글(다항회귀)에서 사용한 2차곡선형의 데이터 샘플입니다. 만약 Degree를 300이나 1로 지정할 경우 어떻게 되는지 한번 알아보도록 하겠습니다.

from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

for style, width, degree in (("g-", 1, 300), ("b--", 2, 2), ("r-+", 2, 1)):
    polybig_features = PolynomialFeatures(degree=degree, include_bias=False)
    std_scaler = StandardScaler()
    lin_reg = LinearRegression()
    polynomial_regression = Pipeline([
            ("poly_features", polybig_features),
            ("std_scaler", std_scaler),
            ("lin_reg", lin_reg),
        ])
    polynomial_regression.fit(X, y)
    y_newbig = polynomial_regression.predict(X_new)
    plt.plot(X_new, y_newbig, style, label=str(degree), linewidth=width)

plt.plot(X, y, "b.", linewidth=3)
plt.legend(loc="upper left")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([-3, 3, 0, 10])
save_fig("high_degree_polynomials_plot")
plt.show()

코드 구현결과를 보면 300차인 경우 2차에 비해서 데이터에 최대한 맞추려고 계속 지그재그로 진행하는 모습이 보입니다. 

반면 1차의 경우 2차에 비해서 데이터에 비적합한 모습을 보입니다. 즉 너무 degree가 높은 경우 데이터에 과대적합되며 너무 낮은 경우 과소적합이 될 수 있습니다. 이번에는 학습곡선을 이용해서 이 적합도를 한 눈에 파악하도록 구현하겠습니다.

2. 학습곡선을 통한 적합성파악

데이터에서 훈련,검증 세트를 추출하고 훈련세트를 하나부터 점차 학습시키면서 RMSE를 구하여 이를 그래프로 나타내는 학습곡선을 구현하도록 하겠습니다. (여기서는 PolynomialFeatures를 사용하지 않으므로 degree를 1차로 진행하는 직선방정식이라고 생각하시면됩니다.)

from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split

def plot_learning_curves(model, X, y):
    X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=10)
    train_errors, val_errors = [], []
    for m in range(1, len(X_train)):
        model.fit(X_train[:m], y_train[:m])
        y_train_predict = model.predict(X_train[:m])
        y_val_predict = model.predict(X_val)
        train_errors.append(mean_squared_error(y_train[:m], 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")
    plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")
    plt.legend(loc="upper right", fontsize=14)   # not shown in the book
    plt.xlabel("Training set size", fontsize=14) # not shown
    plt.ylabel("RMSE", fontsize=14)
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)
plt.axis([0, 80, 0, 3])                         # not shown in the book
save_fig("underfitting_learning_curves_plot")   # not shown
plt.show() 

곡선을 보니 사이즈가 15이하인 경우까지는 학습이 점차 진행되어 훈련세트와 검증세트의 괴리가 줄어드는 것이 보입니다. 그러나 그 이후 만나는 지점이 1.5 ~ 2.0으로 둘 다 상당한 오차가 발생하는 것으로 보입니다. 이는 전형적인 과소적합의 형태입니다. 이번에는 degree를 10정도로 지정하여 학습곡선을 그려보겠습니다.

from sklearn.pipeline import Pipeline

polynomial_regression = Pipeline([
        ("poly_features", PolynomialFeatures(degree=10, include_bias=False)),
        ("lin_reg", LinearRegression()),
    ])

plot_learning_curves(polynomial_regression, X, y)
plt.axis([0, 80, 0, 3])           # not shown
save_fig("learning_curves_plot")  # not shown
plt.show()   

이번에는 두 곡선의 평균점이 1내외로 이전보다는 나아진것으로 보입니다. 그러나 45부터는 훈련 세트가 증가해도 검증세트와 훈련세트의 RMSE 괴리가 줄어들지 않는 것을 알 수 있습니다. 이는 전형적인 과대적합 모델입니다. 이렇게 degree를 바꿔가며 학습곡선을 파악하면 최적화된 degree를 구할 수 있습니다.

3. 모델의 일반화 오차

모델의 예측값과 결과값 사이의 오차는 일반적으로 3가지 요인을 들 수 있습니다.

1. 편향 : 데이터를 분석할 모델을 잘못 가정하는 것을 의미합니다. 모델 자체의 종류를 잘못 선택하거나 위에서처럼 하이퍼파라미터(degree)를 잘못 설정한 경우입니다. 보통 모델이 복잡할 수록 편향 또한 증가합니다.

2. 분산 : 데이터의 분포정도에 따른 오차발생 정도입니다. 보통 모델이 간단할 경우 예측값과의 분산 정도가 증가합니다.

3. 가우시안 노이즈 : 데이터 자체의 노이즈 정도입니다.