본문 바로가기

Codestates AI 부트캠프/2. Machine Learning

[머신러닝] 1-2 일반화 (Generalization)

1. 일반화란?

모델에 test set을 넣어보면서 정확도 높은 예측을 할 수 있도록 설계하는 것. 

지난 시간에는 데이터셋을 넣어 모델을 훈련시켰다. 이를 통해 만들어진 모델은 이미 제공받은 데이터들에 대해서는 정확도가 높은 대답을 내놓을 것이다. 하지만 학습에 쓰이지 않은, 완전히 새로운 데이터를 넣었을 때의 답변은 어떠할까? 이때도 훈련 때처럼 정확도가 높아야 '모델의 성능이 좋다'고 말할 수 있을 것이다. 모델을 만드는 목적이 '예측'이기 때문이다. 훈련 데이터에서의 성능과 테스트 데이터에서의 성능이 유사하게 나온 것을 '일반화'가 잘 된 모델이라고 한다.

2. 일반화 방법

A. 2 way hold out

일반화는 어떻게 하는걸까? 바로 test 데이터셋을 통해 모델의 성능을 검증하고 가늠해보는 것이다. 예를 들어 10000개의 데이터를 이용해 모델을 만든다고 하자. 이중 2000개를 떼어내 test 데이터셋으로 삼는다. 나머지 8000개로 모델을 훈련시킨 뒤, test 데이터셋으로 모델을 테스트해보는 것이다. test 데이터셋과 training 데이터셋의 비율은 25:75, 20:80 등으로 다양할 수 있고, 이는 데이터셋의 크기에 따라 달라진다. 이렇게 데이터셋을 떼어내 test용으로 삼는 것을 Hold Out 기법이라고 한다. 

*test 데이터셋은 모델링 과정에서 꼭 한번만 쓰여야 한다. 그렇지 않으면 모델이 test set을 학습해버려 원래의 의미를 희석시킬 수 있다.

 

B. 3 way hold out

2 way가 test와 training 데이터셋으로 나눈다면, 3 way에서는 validation set을 더해 총 3개로 나눈다. 수능 준비에 비유하자면 "train set : 학습 / validation set : 모의고사 / test set : 수능"이라고 말할 수 있다. 

훈련데이터로 모델을 학습 시키고, 만들 때 사용되고 검증 데이터로 모델의 하이퍼파라미터를 조정한 여러 모델들의 성능을 비교하여 최적의 모델을 선택(model selection)하며, 테스트 데이터는 모델을 최종적으로 1회 평가하는 용도로 사용한다.

 

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
x_train, x_val, y_train, y_val = train_test_split(x_train, y_train, test_size=0.2)


C. K Fold Cross Validation

위의 hold out 방식은 두가지의 문제가 있다.

 

  • 데이터 사이즈 문제 : 데이터 크기가 작을 때는 적절하지 않다. 데이터를 나누면서 training set이 너무 작아져버리기 때문이다.
  • 모델 선택의 문제 : 무작위로 선택된 traing 또는 test 데이터에 따라 학습 결과가 달라질 수 있다.이는 모델의 신뢰성에 의문을 제기한다.

이러한 이유로 모델을 개선해야할 때 cross validation을 이용할 수 있다. 과정은 아래와 같다.

① 데이터를 k개로 분할 
② 1개의 파트는 validation set로 이용되고 나머지 k-1개의 파트는 training set으로 이용 
③ k번의 검증 결과를 종합하여 전체 모델의 성능을 평가

from sklearn.model_selection import cross_val_score

# 데이터를 5개로 분할해 5번 시행
cv_results = cross_val_score(
    model, 
    x_train_cv, 
    y_train_cv, 
    cv=5,        
    scoring="neg_mean_absolute_error",
    n_jobs=-1
    )

print('mae for each fold : ', -np.round(cv_results, 2))
print('average mae for model : ', -np.mean(cv_results).round(2))
print('std of result : ', np.std(cv_results).round(2))

'''
>>>
mae for each fold :  [28295.36 29845.32 26597.92 24041.59 32435.82]
average mae for model :  28243.2
std of result :  2846.33
'''

 

*K Fold Cross Validation는 모델 학습법이 아니다. 출력되는 평가지표의 평균을 통해 모델을 평가하는 것이다.

*시계열 데이터는 training과 validation set 사이의 시간 순서가 중요하기 때문에 무작위로 섞는 k-fold cross-validation을 이용할 수 없다.

*왜 'neg_mean_absolute_error' 파라미터를 써서 mae를 음수로 받는가?
scikit-learn 라이브러리에서는 score가 클수록 좋다고 평가하기 때문에 RMSE, MAE와 같이 수치가 작을수록 좋은 지표에는 neg를 붙여야한다. Ex) MAE 100, 200 -> 100이 더 좋은 지표인데 200으로 선택. 따라서, -100, -200을 만들어줌으로써 원래 의도대로 100이라는 수치가 선택되도록 함.

 

3. 일반화 결과 분류

일반화 작업 후 test 데이터에서 만들어내는 오차를 일반화 오차라고 한다. 일반화 오차가 큰 모델은 '좋은 성능을 가졌다'라고 말하기 어렵다. 학습이 잘 이루어지지 않은 이유를 두가지로 나눌 수 있다.


A. 과적합 (overfitting)

  • 데이터셋의 디테일과 노이즈까지 모두 학습하여 새로운 데이터를 잘 예측하지 못하는 현상
  • training set에서의 성능 : 매우 높음 / test set에서의 성능 : 매우 낮음
  • 분산이란 서로 다른 데이터가 들어왔을 경우 모델의 예측값이 변동되는 양. 과적합에서는 패턴이 복잡하여 결과값이 둘쭉날쭉하기 때문에 분산 값이 크다

B. 과소적합

 

  • training set도 제대로 학습하지 못해서 새로운 데이터를 잘 예측하지 못하는 현상
  • training set에서의 성능, test set에서의 성능 : 둘 다 매우 낮음
  • 편향이란 모델의 예측값과 실제 값과의 차이. 과소적합에서는 데이터를 잘 못맞추기 때문에 이 편향이 크다

*분산과 편향은 trade-off 관계다.

아래의 그래프를 보면 모델의 복잡도(complexity)가 작으면 training을 잘 못해 과소적합이 나타난다. 복잡도를 올리면 training을 잘하지만 과적합이 올 수 있다. 복잡도를 변경해보며 과소적합도, 과적합도 아닌 일반화가 잘되는 지점을 찾아야한다.