모델 평가

Yeo Myung Ro·2021년 9월 19일
0

머신러닝을 통해 모델을 학습시킬 경우, 모델이 어느정도의 성능을 내느냐를 측정하는 것은 매우 중요하다. 또한, 모델은 이전에 본 적이 없는 데이터에도 일반화(Generalization)이 잘 되어 있어야 한다. 이번 포스트에서는 학습시킨 모델에 대한 성능 평가와 유효성 검증에 대해 알아보자.

  • 모델의 성능 : 해결해야할 문제의 종류 - 분류(Classification) / 회귀(Regression) - 에 따른 평가 지표 선택
  • 모델의 일반화(Generalization) : Bias-Variance / Overfitting 문제

모델 선정

모델을 선정하는 것은 머신러닝으로 어떠한 문제를 해결할 것인가에 달려있다.
대표적인 머신러닝 문제 유형과 그 문제 유형을 해결하기 위한 모델들로는

  • 분류
    • Naive Bayes
    • SVM
    • Decision Tree
    • Logistic Regression
    • ETC
  • 회귀
    • Linear Regression
    • Genenralized Linear Regression
    • Decision Tree
    • ETC
  • 비지도학습
    • K-Means
    • Association Rules
    • ETC

등이 있다.

모델 성능 평가

모델 성능 평가는 모델의 성능을 검증하는 것이다. 이를 위해서는 모델의 목표와 사용된 모델링 기법 모두에 적절한 성능 평가 지표를 선택해야 한다.

회귀 모델 평가 지표

회귀 모델은 수치를 예측하는 모델으로, 잔차(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(Root Mean Square Error)

RMSE는 가장 일반적인 회귀 모델 평가 지표로, 실제 값과 예측 값의 차이를 제곱하고 평균을 구한 후 다시 제곱근을 적용해서 구한다

RMSE=1ni=1n(xixi^)2\mathbf{RMSE} = \sqrt{\frac{1}{n}\sum_{i=1}^n (x_i - \hat{x_i})^2}

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

결정계수(R2)

결정 계수는 회귀 모델의 설명력은 표현하는 지표로, 모델에 의해 설명되는 y 분산의 비율로 간주된다. 0에 가까울수록 설명력이 낮고, 1에 가까울수록 설명력이 높다.

R2=SSRSST=1SSESST\mathbf{R^2} = \frac{\mathbf{SSR}}{\mathbf{SST}} = 1 - \frac{\mathbf{SSE}}{\mathbf{SST}}

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개 또는 그 이상의 범주를 예측하는 모델이다.
분류 모델의 평가 지표로는

  • 정확도(Accuracy)
  • 정밀도(Precision)
  • 재현율(Recall)
  • F1 Score
  • 민감도와 특이도

등으로, 평가 지표의 선택은 해결해야 하는 문제에 따라 결정해야 한다.
분류 모델의 성능을 측정하기 위해 사용하는 유용한 도구로 혼동 행렬(Confusion Matrix)를 사용한다.

혼동 행렬(Confusion Matrix)

혼동 행렬은 실제 알려진 데이터 범주에 대해 분류 모델의 예측을 정리한 표로, 각각의 예측 유형별로 실제 데이터가 얼마나 발생했는지 확인할 수 있다.

Confusion MatrixP'(Predict)N'(Predict)
P(Actual)True Positive(TP)False Negative(FN)
N(Actual)False Positive(FP)True Negative

혼동 행렬에서 True / False는 예측이 맞았는지/틀렸는지를 나타내고, Positive / Negative는 예측한 값이 양성(=1)인지 / 음성(=0)인지를 나타낸다.
즉,

  • TP : 1이라고 예측하고 실제 값이 1인 경우
  • FP : 1이라고 예측하고 실제 값이 0인 경우
  • TN : 0이라고 예측하고 실제 값이 0인 경우
  • FN : 0이라고 예측하고 실제 값이 1인 경우

이다. 주로 관심이 가는 범주를 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)이다.

정확도(Accuracy)

정확도는 분류 모델의 성능을 측정하는데 사용하는 가장 일반적인 지표로, 정확히 분류된 항목의 숫자를 전체 항목의 숫자로 나눠서 구한다.

Accuracy=(TP+TN)(TP+TN+FP+FN)\mathbf{Accuracy} = \frac{(\mathbf{TP} + \mathbf{TN})}{(\mathbf{TP} + \mathbf{TN} + \mathbf{FP} + \mathbf{FN})}

위의 예제에서 정확도를 계산해보면,

(52316+4820)(52316+2263+4820+601)=0.9523\frac{(52316 + 4820)}{(52316 + 2263 + 4820 + 601)} = 0.9523

약 95.23%의 정확도를 가지고 있다.

클래스 불균형(Unbalanced Class) 문졔

암 진단과 같은 1과 0의 숫자가 매우 크게 차이가 나는 경우를 클래스 불균형 문제라고 한다. 이러한 문제에서는 대부분의 경우(99% 이상)이 0이기 때문에 정확도를 평가 지표로 하는 것은 좋은 방법이 아니다.

정밀도(Precision)

정밀도는 양성(=1)으로 예측한 것이 얼마나 정확한지를 판별하는 지표로, 확인을 위한 측정 도구이다.

Precision=TP(TP+FP)\mathbf{Precision} = \frac{\mathbf{TP}}{(\mathbf{TP} + \mathbf{FP})}

위의 예제에서 정밀도를 계산해보면,

4820(4820+2263)=0.6805\frac{4820}{(4820 + 2263)} = 0.6805

로, 약 68,05%의 정밀도를 가지고 있다.

from sklearn.metrics import precision_score
precision_score(y_train_5, y_train_pred)
0.6805026118876183

재현율(Recall) / 민감도(Sensitivity) / 참양성률(True Positive Rate)

재현율은 실제 양성(=1)인 것들 중에서 양성이라고 예측한 비율을 나타내는 지표로, 유용성에 대한 측정 도구이다.

Recall=TP(TP+FN)\mathbf{Recall} = \frac{\mathbf{TP}}{(\mathbf{TP} + \mathbf{FN})}

위의 예제에서 정밀도를 계산해보면,

4820(4820+601)=0.8891\frac{4820}{(4820 + 601)} = 0.8891

으로, 약 88.91%의 재현율을 가지고 있다.

from sklearn.metrics import recall_score
recall_score(y_train_5, y_train_pred)
0.8891348459693783

F1 Score

F1 Score는 정밀도와 재현율의 조합으로 된 지표이다.

F1=2×Precision×Recall(Precision+Recall)\mathbf{F1} = 2\times\frac{\mathbf{Precision} \times \mathbf{Recall}}{(\mathbf{Precision} + \mathbf{Recall})}

위의 예제에서 F1 Score를 계산해보면,

2×0.6805×0.8891(0.6805+0.8891)=0.77092\times\frac{0.6805 \times 0.8891}{(0.6805 + 0.8891)} = 0.7709

from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)
0.7709532949456175

특이도(Specificity) / 참음성률(True Negative Rate)

특이도는 실제 음성(=0)인 것들 중에서 음성이라고 예측한 비율을 나타내는 지표이다.

Specificity=TN(TN+FP)\mathbf{Specificity} = \frac{\mathbf{TN}}{(\mathbf{TN} + \mathbf{FP})}

위의 예제에서 특이도를 계산해보면,

52316(52316+2263)=0.9585\frac{52316}{(52316 + 2263)} = 0.9585

으로, 약 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가지 형태로 구분한다. 훈련 데이터 : 모델의 적합과 파라미터의 추정 검증 데이터 : 파라미터 튜닝, 변수 선택 및 모델 선택 * 테스트 데이터 : 모델 적합과 선택이 끝난 후 최종 모델의 성능을 측정

과적합(Overfitting)

편향(Bias) - 분산(Variance) Trade-off

머신러닝에서 모델의 에러는 두 가지로 분류할 수 있다. 바로 편향(Bias)와 분산(Variance)이다. 편향이 올라가면 분산이 내려가고, 편향이 내려가면 분산이 올라가는데 이를 편향 - 분산 trade-off라 한다.

이미지 출처

  • 편향
    • 실제 문제를 단순한 모델로 근사시킴으로 인해 발생되는 오차
    • 모델의 복잡도(complexity)가 높을수록 편향은 작고, 단순한 모델일수록 편향은 높음
  • 분산
    • 훈련 데이터가 아닌 다른 데이터를 사용하여 예측하는 경우, 예측 값이 변동되는 정도
    • 훈련 데이터는 모델을 학습시키는데 사용되므로, 다른 데이터를 사용하면 예측 값이 변동됨
    • 그러나 분산이 높으면 훈련 데이터의 변화가 작아도 예측 값이 크게 변할 수 있음
    • 모델의 복잡도가 높을수록 분산은 높음

이미지 출처

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)

교차 검증(Cross Validation)

검증 데이터 기법

앞서 얘기한 것처럼 데이터를 훈련 데이터(50-60%) / 검증 데이터(20-25%) / 테스트 데이터(20-25%)로 구분하여 모델을 만들고 검증 및 성능 평가를 하는 방법이다. 이 방법은 데이터가 어떻게 나눠지는가에 따라 성능의 변동이 클수가 있다.

K-fold Cross Validation

이미지 출처

전체 데이터를 크기가 같은 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
profile
# data engineering

0개의 댓글