Skip to content

Cross Validation

Cross Validation의 필요성.

  • 전체 데이터가 적은 경우, test dataset에 최소한의 데이터를 나누어 주었다고 해도 training dataset에 있는 데이터의 양이 적어질 수 밖에 없음.
  • 이같이 적은 데이터에서 hyper parameter tuning을 위한 validation dataset을 다시 나눌 경우, 통계적으로 신뢰할 수 있는 evaluation이 어렵다.
  • 지나치게 적은 validation dataset으로는 적절한 model selection이나 hyper parameter tuning이 수행될 수 없게 된다.

Cross Validation (CV) 이란?

이를 해결하기 위한 방안 중 하나가 k-fold cross validation으로, training dataset을 k개의 subset으로 나누고, 각 subset을 validation set으로 삼고 나머지로 학습을 한 model에 대해 evaluation하는 것을 k번 반복한다.

이 경우, k번의 evaluation의 결과를 얻게되므로 1번만 수행하는 evaluation에 비해 보다 높은 통계적 신뢰성을 확보할 수 있다.

즉, 하나하나의 evaluation이 적당한 양의 validation dataset을 가지기 어려워 신뢰도가 떨어지니, 이를 여러번 반복 측정하여 신뢰도를 올리는 것이다.

극한의 k-fold CV는 Leave One Out Cross Validation (LOOCV)로

  • 1개의 sample만을 validation dataset으로 처리하고
  • 해당 1개의 sample을 제외한 나머지로 학습한다.
  • 이후 해당 sample로만 evaluation 수행한다.
  • 위의 과정을 전체 sample수만큼 반복하는 CV(교차검증)이다.


Split 객체 사용하기.

train_test_split 함수와 달리, split 객체들로 처리하는 방법은 앞서 언급한 cross validation과 연계가 보다 간편함.

sklearn.model_selection.StratifiedKFold

n_splits에 할당하는 argument로 CV에서 사용할 number of folds를 지정한다.

  • 만약 1을 사용하면, train_test_split함수와 같은 역할을 수행하게 된다 (물론 이후 split를 호출).

StratifiedKFold는 `k-Fold`` 기법을 구현한 것으로 test dataset으로 지정된 index들이 겹쳐지지 않는다.

from sklearn.model_selection import StratifiedKFold

kfold = StratifiedKFold(
    n_splits,  # num of folds
    shuffle = False,  # index를 나누기 전 랜덤하게 섞을지 여부.
    random_state, # seed value for pseudo-random (shuffle이 True인 경우만 설정)
)

for fold_idx, (train_idx, test_idx) in enumerate(kfold.split(X_data, y_data)):
    ...

sklearn.model_selection.StratifiedShuffleSplit

n_splits에 할당하는 argument에 따라 CV에서 이루어질 evaluation이 몇 번 이루어질지를 결정한다.

  • k-Fold와 달리 램덤하게 test dataset이 결정 되며
  • test에 선택된 index들이 겹칠 수 있다.
  • k-Fold에서는 subset이 나누어지는 구조이기 때문에 test dataset이 겹칠 수가 없다(모든 샘플은 test dataset이 될 수 있는 것은 오직 하나의 fold로만 한정됨).
from sklearn.model_selection import StratifiedShuffleSplit

shuffle_splitter = StratifiedShuffleSplit(
    n_splits,  # num of evaluations
    random_state, # seed value for pseudo-random
    test_size = 0.3 # k-Fold와 달리 랜덤하게 test dataset을 만들므로 한번의 evaluation에 몇개를 사용할지를 test_size로 결정해줌.
)

for fold_idx, (train_idx, test_idx) in enumerate(shuffle_splitter.split(X_data, y_data)):
    ...

예제 : index를 통한 확인

import numpy as np
from sklearn.model_selection import StratifiedKFold

np.random.seed(23)

X_data = np.array([ -10, -8, 1 ,2 ,3, 4 , 5, 6, 7, 0])
y_data = np.array([ 0, 0, 1, 1, 1, 1, 1, 1, 1, 0])

n_split = 3


kfold = StratifiedKFold(
    n_splits=n_split,  # num of folds
    # shuffle = False,  # index를 나누기 전 랜덤하게 섞을지 여부.
    # random_state = 23, # seed value for pseudo-random
)

for fold_idx, (train_idx, test_idx) in enumerate(kfold.split(X_data, y_data)):
    print('----------------------')
    print(f'fold idx : {fold_idx}')
    print(f'train_idx = {train_idx}')
    print(f'test_idx = {test_idx}')

결과는 다음과 같다.

----------------------
fold idx : 0
train_idx = [1 5 6 7 8 9]
test_idx = [0 2 3 4]
----------------------
fold idx : 1
train_idx = [0 2 3 4 7 8 9]
test_idx = [1 5 6]
----------------------
fold idx : 2
train_idx = [0 1 2 3 4 5 6]
test_idx = [7 8 9]

각 sample들이 단 한번만 test dataset에서 사용됨을 확인할 수 있다. (각 fold당 겹쳐지지 않는다)

이에 비해 StratifiedShuffleSplittest_size로 지정하기 때문에 여러번의 CV가 가능하다.

from sklearn.model_selection import StratifiedShuffleSplit

shuffle_spliter = StratifiedShuffleSplit(
    n_splits = n_split,  # num of evaluations
    random_state = 23, # seed value for pseudo-random
    test_size = 0.3 # k-Fold와 달리 랜덤하게 test dataset을 만들므로 한번의 evaluation에 몇개를 사용할지를 test_size로 결정해줌.
)

for shuffle_idx, (train_idx, test_idx) in enumerate(shuffle_spliter.split(X_data, y_data)):
    print('----------------------')
    print(f'shuffle idx : {shuffle_idx}')
    print(f'train_idx = {train_idx}')
    print(f'test_idx = {test_idx}')

결과는 다음과 같다.

----------------------
shuffle idx : 0
train_idx = [1 5 7 0 4 6 8]
test_idx = [9 3 2]
----------------------
shuffle idx : 1
train_idx = [6 4 1 5 3 8 9]
test_idx = [7 0 2]
----------------------
shuffle idx : 2
train_idx = [4 1 7 0 2 3 8]
test_idx = [6 5 9]

관련 URLs


cross_val_scores() 를 사용하면 간단하게 cv 수행 가능.

scores = cross_val_score(
    estimator,            # CV를 수행할 estimator
    X,                    # Predictors (or feature vectors)
    scoring=None,         # CV에 사용할 metric. 간단하게는 'accuracy'등이 사용됨.
    cv=None,              # num of folds.
    n_jobs=1,             # 사용할 cpu수.
    verbose=0,            # 출력할 메시지의 정도를 결정.
    fit_params=None,
    pre_dispatch=2*n_jobs
)

내부적으로 StratifiedKFold를 사용.
좀 더 세밀한 반환값이 필요하다면, cross_val_predict를 사용할 것.