머신러닝을 통해 모델을 학습시킬 경우, 모델이 어느정도의 성능을 내느냐를 측정하는 것은 매우 중요하다. 또한, 모델은 이전에 본 적이 없는 데이터에도 일반화(Generalization)이 잘 되어 있어야 한다. 이번 포스트에서는 학습시킨 모델에 대한 성능 평가와 유효성 검증에 대해 알아보자.
모델을 선정하는 것은 머신러닝으로 어떠한 문제를 해결할 것인가에 달려있다.
대표적인 머신러닝 문제 유형과 그 문제 유형을 해결하기 위한 모델들로는
등이 있다.
모델 성능 평가는 모델의 성능을 검증하는 것이다. 이를 위해서는 모델의 목표와 사용된 모델링 기법 모두에 적절한 성능 평가 지표를 선택해야 한다.
회귀 모델은 수치를 예측하는 모델으로, 잔차(residuals)라고하는 실제 값과 예측 값의 차이를 가지고 모델의 성능을 평가한다.
잔차 그래프
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(housing_prepared, housing_labels)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)
RMSE는 가장 일반적인 회귀 모델 평가 지표로, 실제 값과 예측 값의 차이를 제곱하고 평균을 구한 후 다시 제곱근을 적용해서 구한다
RMSE 외에도 MAE(Mean Absolute Error), MAPE(Mean Absolute Percentage Error) 등이 있다.
from sklearn.metrics import mean_squared_error
housing_predictions = lin_reg.predict(housing_prepared)
lin_mse = mean_squared_error(housing_labels, housing_predictions)
lin_rmse = np.sqrt(lin_mse)
print("RMSE : {}".format(lin_rmse))
RMSE : 68628.19819848922
결정 계수는 회귀 모델의 설명력은 표현하는 지표로, 모델에 의해 설명되는 y 분산의 비율로 간주된다. 0에 가까울수록 설명력이 낮고, 1에 가까울수록 설명력이 높다.
SST와 SSR이 얼마나 비슷한지, SSE가 얼마나 다른지에 따라 결정된다.
from sklearn.metrics import r2_score
lin_r2 = r2_score(housing_labels, housing_predictions)
print("R2 Score : {}".format(lin_r2))
R2 Score : 0.6481624842804428
분류 모델은 2개 또는 그 이상의 범주를 예측하는 모델이다.
분류 모델의 평가 지표로는
등으로, 평가 지표의 선택은 해결해야 하는 문제에 따라 결정해야 한다.
분류 모델의 성능을 측정하기 위해 사용하는 유용한 도구로 혼동 행렬(Confusion Matrix)를 사용한다.
혼동 행렬은 실제 알려진 데이터 범주에 대해 분류 모델의 예측을 정리한 표로, 각각의 예측 유형별로 실제 데이터가 얼마나 발생했는지 확인할 수 있다.
Confusion Matrix | P'(Predict) | N'(Predict) |
---|---|---|
P(Actual) | True Positive(TP) | False Negative(FN) |
N(Actual) | False Positive(FP) | True Negative |
혼동 행렬에서 True / False는 예측이 맞았는지/틀렸는지를 나타내고, Positive / Negative는 예측한 값이 양성(=1)인지 / 음성(=0)인지를 나타낸다.
즉,
이다. 주로 관심이 가는 범주를 1로 간주하고 아닌 것은 0으로 간주한다.
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state = 42)sgd_clf.fit(X_train, y_train_5)
SGDClassifier(alpha=0.0001, average=False, class_weight=None,
early_stopping=False, epsilon=0.1, eta0=0.0, fit_intercept=True,
l1_ratio=0.15, learning_rate='optimal', loss='hinge',
max_iter=1000, n_iter_no_change=5, n_jobs=None, penalty='l2',
power_t=0.5, random_state=42, shuffle=True, tol=0.001,
validation_fraction=0.1, verbose=0, warm_start=False)
y_train_pred = sgd_clf.predict(X_train)
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
array([[52316, 2263],
[ 601, 4820]])
Confusion Matrix의 행을 실제 클래스를 나타내고 열은 예측한 클래스를 나타낸다. 이 행렬의 첫 번째 행은 ‘5가 아님’ 클래스(Negative Class)이고, 두 번째 행은 ‘5’ 클래스(Positive Class)이다.
정확도는 분류 모델의 성능을 측정하는데 사용하는 가장 일반적인 지표로, 정확히 분류된 항목의 숫자를 전체 항목의 숫자로 나눠서 구한다.
위의 예제에서 정확도를 계산해보면,
약 95.23%의 정확도를 가지고 있다.
암 진단과 같은 1과 0의 숫자가 매우 크게 차이가 나는 경우를 클래스 불균형 문제라고 한다. 이러한 문제에서는 대부분의 경우(99% 이상)이 0이기 때문에 정확도를 평가 지표로 하는 것은 좋은 방법이 아니다.
정밀도는 양성(=1)으로 예측한 것이 얼마나 정확한지를 판별하는 지표로, 확인을 위한 측정 도구이다.
위의 예제에서 정밀도를 계산해보면,
로, 약 68,05%의 정밀도를 가지고 있다.
from sklearn.metrics import precision_score
precision_score(y_train_5, y_train_pred)
0.6805026118876183
재현율은 실제 양성(=1)인 것들 중에서 양성이라고 예측한 비율을 나타내는 지표로, 유용성에 대한 측정 도구이다.
위의 예제에서 정밀도를 계산해보면,
으로, 약 88.91%의 재현율을 가지고 있다.
from sklearn.metrics import recall_score
recall_score(y_train_5, y_train_pred)
0.8891348459693783
F1 Score는 정밀도와 재현율의 조합으로 된 지표이다.
위의 예제에서 F1 Score를 계산해보면,
from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)
0.7709532949456175
특이도는 실제 음성(=0)인 것들 중에서 음성이라고 예측한 비율을 나타내는 지표이다.
위의 예제에서 특이도를 계산해보면,
으로, 약 95.85%이다.
민감도와 특이도는 어느 범주를 양성(=1)로 두느냐에 따라 서로 바뀌게 된다. 또한 null 분류 모델(모든 값을 양성 또는 음성으로 예측하는 모델)에서 민감도 또는 특이도 둘 중 하나는 항상 0이 된다. 그래서 유용하지 않은 분류 모델에서는 이 두 가지 중 적어도 하나의 값은 항상 낮은 값을 갖게 된다.
지표 | 수식 | 예시 |
---|---|---|
정확도 | (TP+TN)/(TP+FP+TN+FN) | 0.9523 |
정밀도 | TP/(TP+FP) | 0.6805 |
재현율 | TP/(TP+FN) | 0.8891 |
특이도 | TN/(FP+TN) | 0.9585 |
모델은 훈련 데이터에서 잘 동작할 뿐만 아니라 새로운 데이터에 대해서도 잘 작동되어야 한다. 즉, 일반화(Generalization)이 잘 되어있어야 좋은 모델이 될 수 있다. 훈련 데이터에서는 잘 동작하지만 새로운 데이터에 대해서는 잘 동작하지 않는 경우, 모델은 훈련 데이터에 과적합(Overfitting)되어있다고 한다.
과적합을 방지하고 일반화 능력이 좋은 모델을 선택하기 위해서, 데이터를 3가지 형태로 구분한다. 훈련 데이터 : 모델의 적합과 파라미터의 추정 검증 데이터 : 파라미터 튜닝, 변수 선택 및 모델 선택 * 테스트 데이터 : 모델 적합과 선택이 끝난 후 최종 모델의 성능을 측정
머신러닝에서 모델의 에러는 두 가지로 분류할 수 있다. 바로 편향(Bias)와 분산(Variance)이다. 편향이 올라가면 분산이 내려가고, 편향이 내려가면 분산이 올라가는데 이를 편향 - 분산 trade-off라 한다.
Bias가 높고(Inaccurate) Variance가 낮은(Robust)한 모델은 Underfitting되어 있다. Underfitting의 경우, 훈련 데이터를 사용해 예측한 값과 다른 데이터를 사용해 예측한 값의 차이는 적지만, 실제 값과의 차이는 크다.
Bias가 낮고(Flexible) Variance가 높은(Susceptible)한 모델은 Overfitting 되어 있다. Overfitting의 경우, 훈련 데이터를 사용해 예측한 값은 실제 값과 차이가 적으나, 다른 데이터를 사용해 예측한 값은 실제 값과 차이가 크다.
위의 예시에서, 25차 다항식 모델(Overfitting)은 훈련 데이터에서는 높은 성능을 보이지만, 테스트 데이터에서는 아주 낮은 성능을 보일 것이다. 즉, 훈련 데이터와 테스트 데이터의 성능에 큰 차이가 있다. 이는 일반화 능력이 매우 낮다고 할 수 있다. 반면 1차 다항식 모델(Underfitting)은 훈련 데이터나 테스트 데이터 모두에서 낮은 성능을 보일 것이다. 3차 다항식 모델(Best)은 훈련 데이터에 대한 성능은 25차 다항식 모델보다 낮겠지만, 테스트 데이터에서는 높을 것이다. 결국, 3차 다항식 모델은 훈련 데이터와 테스트 데이터의 성능 차이가 작아 일반화 능력이 뛰어나다고 할 수 있다.
# Generate data for best model
true <- poly(1:100, 3) %*% c(-2, 1, 2)
# Input noise
y <- true + rnorm(100, sd = 0.2)
temp <- data.frame(x = 1:100, actual = y)
# Input true model
temp$true <- true
# Make best model
best <- lm(y ~ poly(x, 3), data = temp)
temp$best <- predict(best, temp)
# Make underfit model
underfit <- lm(y ~ x, data = temp)
temp$underfit.pred <- predict(underfit, temp)
# Make overfit model
overfit <- lm(y ~ poly(x, 25), data = temp)
temp$overfit.pred <- predict(overfit, temp)
# Plot overfitting & underfitting
ggplot(temp) + geom_point(aes(x, actual), col = 'gray') +
geom_line(aes(x, true, col = 'True Function'), size = 1) +
geom_line(aes(x, best, col = 'Best Model (df = 3)'), size = 1) +
geom_line(aes(x, underfit.pred, col = 'Underfit Model (df = 1)'), size = 1) +
geom_line(aes(x, overfit.pred, col = 'Overfit Model (df = 25)'), size = 1) +
xlab("") + ylab("") + theme_minimal() +
scale_colour_manual(name = '',
values =c('True Function'='black',
'Best Model (df = 3)'='steelblue',
'Underfit Model (df = 1)'='palegreen3',
'Overfit Model (df = 25)'='darkorange')) +
theme(legend.position = "top")
# Plot overfitting & underfitting
plot(temp$x, temp$actual, col = "gray", lwd = 2, xlab = "", ylab = "")
lines(temp$x, temp$true, lwd = 3, col = "black")
lines(temp$x, temp$underfit.pred, lwd = 3, col = "palegreen3")
lines(temp$x, temp$overfit.pred, lwd = 3, col = "darkorange")
lines(temp$x, temp$best, lwd = 3, col = "steelblue")
legend(x = "topright",
legend = c("True Function",
"Underfit Model (df = 1)",
"Overfit Model (df = 25)",
"Best Model (df = 3)"),
lwd = rep(3, 3), col = c("black", "palegreen3",
"darkorange", "steelblue"),
text.width = 32, cex = 0.85)
앞서 얘기한 것처럼 데이터를 훈련 데이터(50-60%) / 검증 데이터(20-25%) / 테스트 데이터(20-25%)로 구분하여 모델을 만들고 검증 및 성능 평가를 하는 방법이다. 이 방법은 데이터가 어떻게 나눠지는가에 따라 성능의 변동이 클수가 있다.
전체 데이터를 크기가 같은 k개의 fold로 분할한다. 그리고 첫 번째 fold를 테스트 데이터, 나머지 k-1개 fold를 훈련 데이터로 구성하여 모델을 만들고 성능을 평가한다. 그 후 테스트 데이터가 되는 fold를 바꿔가면서 k번 반복 후, 성능의 평균값을 최종 성능으로 간주한다.
from sklearn.model_selection import cross_val_score
scores = cross_val_score(lin_reg, housing_prepared, housing_labels,
scoring = "neg_mean_squared_error", cv = 10)
lin_rmse_scores = np.sqrt(-scores)
print("점수 : {}".format(lin_rmse_scores))
print("평균 : {}".format(lin_rmse_scores.mean()))
print("표준편차 : {}".format(lin_rmse_scores.std()))
점수 : [66782.73843989 66960.118071 70347.95244419 74739.57052552
68031.13388938 71193.84183426 64969.63056405 68281.61137997
71552.91566558 67665.10082067]
평균 : 69052.46136345083
표준편차 : 2731.674001798348