[교차검증] 홀드아웃, K-Fold, Stratified K-Fold, cross_val_score()

Jia Kang·2023년 3월 20일
0

[공부] 머신러닝

목록 보기
3/3
post-thumbnail

교차 검증

교차 검증(Cross Validation)별도의 여러 세트로 구성된 학습 데이터와 검증 데이터 세트에서 학습과 평가를 수행하는 것입니다.

교차검증을 통해 각 세트에서 수행한 평가 결과에 따라, 이후 하이퍼 파라미터 튜닝 등의 모델 최적화를 더욱 쉽게 할 수 있습니다.

교차검증을 비유적으로 표현하자면, 수능을 치르기 전에 모의고사를 여러 번 보는 것과 같습니다. 수능이 테스트 데이터 세트에 대해 평가하는 거라면, 모의고사는 교차검증을 통해 많은 학습과 검증 세트에서 알고리즘 학습과 평가를 수행하는 것입니다.

따라서 대부분의 머신러닝 모델은 교차 검증을 기반으로 1차 성능 평가를 한 뒤, 최종적으로 테스트 데이터 세트에 적용하며 평가하는 과정을 거칩니다.

 

✅ 홀드 아웃

▶ 개념

홀드아웃(Hold-Out)은 전통적인 데이터 분할 방법입니다. 데이터를 무작위로 학습 데이터와 테스트 데이터 두 가지로 구분한 다음, 다시 학습 데이터는 학습 데이터와 검증 데이터로 분할됩니다.

이때 학습 데이터로 모델을 학습시키고, 검증 데이터로 모델의 성능을 평가하며 최적 파라미터를 찾아나갑니다. 그런 다음, 테스트 데이터를 이용해 최종적으로 모델의 성능을 평가하는 것입니다.

💻 실습

사이킷런 패키지를 이용한 홀드아웃 구현 문법은 다음과 같습니다.

  • train_test_split(X, y, test_size=ㅁ, random_state=ㅁ)

    test_size : 전체 데이터에서 테스트 데이터의 크기를 얼마로 샘플링할 것인지를 결정하는 파라미터
    random_state: 호출할 때마다 동일한 학습/테스트용 데이터 세트를 생성하기 위해 주어지는 난수

코드

# scikit-learn 패키지의 train_test_split()을 불러옵니다.
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, 
													random_state=42)

▶ 특징

  • 홀드아웃 방법은 데이터가 충분히 많으면 잘 작동할 수 있지만, 반대로 데이터가 적은 경우에는 어떤 데이터가 검증용으로 선택되느냐에 따라 큰 차이를 보일 수 있습니다.

  • 즉 데이터가 적을 때, 검증 세트와 테스트 세트의 샘플이 너무 적어 주어진 전체 데이터를 통계적으로 대표하지 못할 수 있습니다.

그래서 이러한 차이를 최소화하는 방법으로 다음의 K-Fold 교차검증을 사용하게 됩니다.

 

✅ K-Fold 교차검증

▶ 개념

K-Fold 교차검증은 가장 보편적으로 사용되는 교차검증 기법이라고 할 수 있습니다.
K개의 분할된 데이터 폴드 세트를 만들어 K번만큼 각 폴드 세트에 학습과 검증 평가를 반복적으로 수행하는 방법입니다.

▶ 절차

이번에는 K-Fold 교차검증의 절차를 알아보겠습니다 😀

① 먼저 k=5라고 가정했을 때, 데이터를 5등분으로 분할합니다.
② 그런 다음, 분할된 데이터 세트를 학습과 검증을 위한 데이터 세트로 변경하면서 5번 평가를 수행합니다.
③ 마지막으로, 이 5개의 평가를 평균한 결과를 가지고 예측 성능을 평가합니다.

위 그림처럼, 학습 데이터 세트와 검증 데이터 세트를 점진적으로 변경하면서 마지막 다섯 번째까지 학습과 검증을 수행합니다. 이를 통해 5개의 예측 평가 값을 구했다면, 마지막으로 이를 평균해서 K-Fold의 평가 결과로 반영하면 됩니다.

▶ 특징

  • 교차검증을 사용하는 이유는 데이터를 학습용과 검증용 세트로 여러 번 나눈 것의 평균적인 성능을 계산하면, 한 번 나눠서 학습하는 것에 비해 일반화된 성능을 얻을 수 있기 때문입니다.
  • 따라서 홀드아웃 방법과 비교했을 때, 교차검증은 보다 안정적이고 정확한 방법이라고 할 수 있습니다.

💻 문법

기본적인 문법 형태는 다음과 같습니다.

  • KFold(n_splits=ㅁ, shuffle=True/False, random_state=ㅁ)

    n_splits: K-Fold 교차검증을 몇 번 수행할 것인지 지정함 (기본값=5)
    shuffle: 데이터를 분리하기 전에 미리 무작위로 섞을지를 결정함 (기본값=True)

이때 shuffle은 데이터를 분산시켜서 좀 더 효율적인 학습 및 테스트 데이터 세트를 만들기 위해 사용되는 파라미터입니다.

 

✅ 계층적 K-Fold 교차검증

▶ 개념

일반적으로 회귀(Regression) 문제가 아닌 분류(Classification) 문제에서는 K-Fold가 아닌 계층적 K-Fold 방법을 사용하는 것이 좋습니다 😮

계층적(Stratified) K-Fold 교차검증불균형한 분포를 가진 레이블 데이터 집합을 위한 K-Fold 방식입니다. 즉, 특정 레이블 값이 매우 많거나 적어서 값의 분포가 한쪽으로 치우친 경우 사용합니다.

▶ 예시

예를 들어, 대출 사기 데이터를 예측한다고 가정해보겠습니다.

데이터는 총 1억 건이고, 여러 개의 변수와 대출 사기 여부를 의미하는 레이블(정상: 0, 대출사기: 1)로 구성되어 있습니다.

1억 건의 데이터 중에서 대출 사기가 1,000건만 존재한다면, 즉 1의 값이 아주 작은 비율이라면 K-Fold로 랜덤하게 학습 및 테스트 데이터 세트를 고르더라도 레이블 값인 0과 1의 비율을 제대로 반영하지 못하는 경우가 쉽게 발생할 것입니다.

그런데 대출 사기인 경우의 데이터 수는 매우 적지만, 모델이 대출 사기 여부를 예측하기 위해서는 아주 중요한 데이터일 것입니다.

따라서 원본 데이터와 유사한 대출 사기 레이블 값의 분포를 학습 및 테스트 데이터 세트에도 유지하는 게 매우 중요합니다.

이럴 때 계층적 K-Fold 교차검증을 사용하면, 원본 데이터의 레이블 분포를 우선 고려한 뒤 이 분포와 동일하게 학습 및 검증 데이터 세트를 분배합니다.

 

✅ cross_val_score()

사실, 교차 검증을 파이썬에서 구현하려면 코드가 꽤나 복잡한데요. 😂
그래서 사이킷런에서는 교차 검증을 좀 더 편리하게 수행할 수 있게 해주는 API가 존재합니다.

▶ 개념

대표적으로 cross_val_score()KFold 교차검증을 위한 일련의 과정을 한꺼번에 수행해주는 API입니다. 즉 API 내부에서 학습, 예측, 평가를 해주므로 교차 검증을 아주 간단하게 수행할 수 있어요.

  • cross_val_score()는 분류 모형에서는 계층적 K-Fold 교차검증 방식으로 레이블 값의 분포에 따라 학습/테스트 데이터를 분할합니다.

  • 반대로 회귀 모형의 경우에는 K-Fold 교차검증 방식에 따라 분할되어요.

 

▶ 실습

기본적인 문법 형태는 다음과 같습니다.

  • cross_val_score(모델이 할당된 객체, X, y, scoring='평가지표', cv=ㅁ)

    scoring : 예측 성능을 평가하는 지표를 기술하는 파라미터
    cv : 교차검증 폴드 개수
  • 여기서 주의할 점은, cross_val_score() 함수를 사용하면 KFold의 파라미터를 제어할 수 없게 된다는 것입니다. 그러니 여기서는 kfold 객체를 만들어 cross_val_score 함수의 cv 파라미터로 전달하겠습니다.
  • scoring 지표로는 MSE에 (-)를 붙인 'neg_mean_squared_error'를 사용하겠습니다. 도출된 값에 (-)를 곱하면 MSE 값을 구할 수 있고, 이 값에 다시 루트를 씌우면 RMSE가 계산됩니다.
  • 마지막으로, 도출된 5개의 RMSE 값의 평균을 출력하겠습니다.

코드

# scikit-learn 패키지의 cross_val_score()를 불러옵니다.
from sklearn.model_selection import cross_val_score
# scikit-learn 패키지의 K-Fold 교차검증을 위한 클래스를 불러옵니다.
from sklearn.model_selection import KFold
# 5개의 Fold를 분리하여 kfold 객체에 할당합니다.
kfold = KFold(n_splits=5, shuffle=True, random_state=42)

# 5 Fold 세트로 MSE 값을 구한 뒤, 이를 기반으로 다시 RMSE를 계산합니다.
neg_mse_scores = cross_val_score(model_rf, X_train, y_train, scoring = 'neg_mean_squared_error', cv=kfold)
rmse_scores  = np.sqrt(-1 * neg_mse_scores)
avg_rmse = np.mean(rmse_scores)
# 5 Fold의 평균 RMSE 값을 출력합니다.
print(avg_rmse)

참고

《파이썬 머신러닝 완벽 가이드》(2022)

profile
데이터 분석가가 되기 위한 여정

0개의 댓글