교차 검증

모형 검증

예측 모형의 최종 성능을 객관적으로 측정하려면 모수 추정(parameter fitting) 즉 트레이닝(training)에 사용되지 않은 새로운 데이터, 즉 테스트 데이터를 사용해야 한다. 모형의 모수 갯수를 증가시킨다든가 커널 모형, 신경망 모형과 같은 비선형 모형을 사용하게 되면 트레이닝 데이터에 대한 예측 성능을 얼마든지 높일 수 있기 때문이다. 이러한 방법에 의해 과최적화(overfitting)가 일어나면 트레이닝 데이터에 대해서는 예측이 잘되지만 테스트 데이터에 대해서는 예측 성능이 급격히 떨어지는 현상이 발생한다.

교차 검증

위에서 지적한 바와 같이 모형 성능을 정상적으로 검사하려면 테스트 데이터가 별도로 있어야 하기 때문에 현실에서는 확보한 데이터 중 일부를 떼어내어 테스트 데이터로 사용한다. 그런데 테스트 데이터를 어떻게 골라내느냐에 따라 모형의 성능이 달라지므로 한 개의 테스트 데이터만 사용하는 것이 아니라 각기 다른 방법으로 서로 다른 테스트 데이터를 여러번 골라내서 복수의 테스트를 실시하는 것이 일반적이다.

이러한 테스트 방법을 교차 검증(cross validation)이라고 한다. 교차 검증을 통한 모형 성능은 보통 다음과 같은 두 가지 값으로 나타난다.

  • 오차 평균(mean performance): 트레이닝에 사용되지 않은 테스트 데이터(test data)에 대해서 평균 오차의 크기가 얼마나 작은가?
  • 오차 분산(variance): 트레이닝에 사용되지 않은 테스트 데이터(test data)에 대해 오차의 크기가 얼마나 달라지는가?

이 중에서 오차 분산을 계산하려면 테스트 데이터 셋이 최소한 세 개 세트가 있어야 한다.

Scikit-Learn의 교차 검증 기능

Scikit-Learn에서는 교차 검증을 위해 전체 데이터 셋에서 트레이닝용 데이터나 테스트용 데이터를 분리해 내는 여러가지 방법을 제공한다.

  • data를 train set과 test set으로 단순 분리
    • data splitter
      • train_test_split() 명령
  • 복수의 test set 준비
    • cross validation iterator
      • KFold
      • StratifiedKFold
      • LabelKFold
      • LeaveOneOut
      • LeavePOut
      • LeaveOneLabelOut
      • LeavePLabelOut
      • ShuffleSplit
      • LabelShuffleSplit
  • 복수의 test set 사용하여 평가 과정 반복
    • cross validation calculator
      • cross_val_score()

단순 데이터 분리

train_test_split() 명령은 데이터를 단순히 트레이닝 데이터와 테스트 데이터로 분리한다.

  • 인수

    • arrays : 데이터
    • test_size : 테스트 데이터 사이즈
    • train_size : 사이즈
    • random_state : 난수 시드
  • 반환값

    • 배열 리스트

In [1]:
X = np.arange(10).reshape((5, 2))
X


Out[1]:
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7],
       [8, 9]])

In [2]:
y = np.arange(5)
y


Out[2]:
array([0, 1, 2, 3, 4])

In [3]:
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

In [4]:
X_train


Out[4]:
array([[4, 5],
       [0, 1],
       [6, 7]])

In [5]:
y_train


Out[5]:
array([2, 0, 3])

In [6]:
X_test


Out[6]:
array([[2, 3],
       [8, 9]])

In [7]:
y_test


Out[7]:
array([1, 4])

K-fold CV

K-fold CV(cross-validation) 방법은 데이터 셋을 K개의 sub-set로 분리하는 방법이다. 분리된 K개의 sub-set 중 하나만 제외한 K-1개의 sub-sets를 training set으로 이용하여 K개의 모형 추정한다.

Scikit-Learn 의 cross_validation 서브 패키지는 K-Fold를 위한 KFold 클래스를 제공한다.


In [8]:
N = 5
X = np.arange(8 * N).reshape(-1, 2) * 10
y = np.hstack([np.ones(N), np.ones(N) * 2, np.ones(N) * 3, np.ones(N) * 4])
print("X:\n", X, sep="")
print("y:\n", y, sep="")


X:
[[  0  10]
 [ 20  30]
 [ 40  50]
 [ 60  70]
 [ 80  90]
 [100 110]
 [120 130]
 [140 150]
 [160 170]
 [180 190]
 [200 210]
 [220 230]
 [240 250]
 [260 270]
 [280 290]
 [300 310]
 [320 330]
 [340 350]
 [360 370]
 [380 390]]
y:
[ 1.  1.  1.  1.  1.  2.  2.  2.  2.  2.  3.  3.  3.  3.  3.  4.  4.  4.
  4.  4.]

In [9]:
from sklearn.cross_validation import KFold
cv = KFold(len(X), n_folds=3, random_state=0)
for train_index, test_index in cv:
    print("test  y:", y[test_index])
    print("." * 80 )        
    print("train y:", y[train_index])
    print("=" * 80 )


test  y: [ 1.  1.  1.  1.  1.  2.  2.]
................................................................................
train y: [ 2.  2.  2.  3.  3.  3.  3.  3.  4.  4.  4.  4.  4.]
================================================================================
test  y: [ 2.  2.  2.  3.  3.  3.  3.]
................................................................................
train y: [ 1.  1.  1.  1.  1.  2.  2.  3.  4.  4.  4.  4.  4.]
================================================================================
test  y: [ 3.  4.  4.  4.  4.  4.]
................................................................................
train y: [ 1.  1.  1.  1.  1.  2.  2.  2.  2.  2.  3.  3.  3.  3.]
================================================================================

Stratified K-Fold

  • target class가 어느 한 data set에 몰리지 않도록 한다

In [11]:
from sklearn.cross_validation import StratifiedKFold
cv = StratifiedKFold(y, n_folds=3, random_state=0)
for train_index, test_index in cv:
    print("test X:\n", X[test_index])
    print("." * 80 )        
    print("test y:", y[test_index])
    print("=" * 80 )


test X:
 [[  0  10]
 [ 20  30]
 [100 110]
 [120 130]
 [200 210]
 [220 230]
 [300 310]
 [320 330]]
................................................................................
test y: [ 1.  1.  2.  2.  3.  3.  4.  4.]
================================================================================
test X:
 [[ 40  50]
 [ 60  70]
 [140 150]
 [160 170]
 [240 250]
 [260 270]
 [340 350]
 [360 370]]
................................................................................
test y: [ 1.  1.  2.  2.  3.  3.  4.  4.]
================================================================================
test X:
 [[ 80  90]
 [180 190]
 [280 290]
 [380 390]]
................................................................................
test y: [ 1.  2.  3.  4.]
================================================================================

Leave-One-Out (LOO)

  • 하나의 sample만을 test set으로 남긴다.

In [10]:
from sklearn.cross_validation import LeaveOneOut
cv = LeaveOneOut(5)
for train_index, test_index in cv:
    print("test X:", X[test_index])
    print("." * 80 )        
    print("test y:", y[test_index])
    print("=" * 80 )


test X: [[ 0 10]]
................................................................................
test y: [ 1.]
================================================================================
test X: [[20 30]]
................................................................................
test y: [ 1.]
================================================================================
test X: [[40 50]]
................................................................................
test y: [ 1.]
================================================================================
test X: [[60 70]]
................................................................................
test y: [ 1.]
================================================================================
test X: [[80 90]]
................................................................................
test y: [ 1.]
================================================================================

Label K-Fold

  • 같은 label이 test와 train에 동시에 들어가지 않게 조절
  • label에 의한 영향을 최소화

In [12]:
from sklearn.cross_validation import LabelKFold
cv = LabelKFold(y, n_folds=3)
for train_index, test_index in cv:
    print("test  y:", y[test_index])
    print("." * 80 )        
    print("train y:", y[train_index])
    print("=" * 80 )


test  y: [ 1.  1.  1.  1.  1.  4.  4.  4.  4.  4.]
................................................................................
train y: [ 2.  2.  2.  2.  2.  3.  3.  3.  3.  3.]
================================================================================
test  y: [ 3.  3.  3.  3.  3.]
................................................................................
train y: [ 1.  1.  1.  1.  1.  2.  2.  2.  2.  2.  4.  4.  4.  4.  4.]
================================================================================
test  y: [ 2.  2.  2.  2.  2.]
................................................................................
train y: [ 1.  1.  1.  1.  1.  3.  3.  3.  3.  3.  4.  4.  4.  4.  4.]
================================================================================

ShuffleSplit

  • 중복된 데이터를 허용

In [13]:
from sklearn.cross_validation import ShuffleSplit
cv = ShuffleSplit(5)
for train_index, test_index in cv:
    print("test X:", X[test_index])
    print("=" * 20 )


test X: [[80 90]]
====================
test X: [[ 0 10]]
====================
test X: [[ 0 10]]
====================
test X: [[60 70]]
====================
test X: [[60 70]]
====================
test X: [[80 90]]
====================
test X: [[40 50]]
====================
test X: [[20 30]]
====================
test X: [[ 0 10]]
====================
test X: [[40 50]]
====================

교차 평가 시행

CV는 단순히 데이터 셋을 나누는 역할을 수행할 뿐이다. 실제로 모형의 성능(편향 오차 및 분산)을 구하려면 이렇게 나누어진 데이터셋을 사용하여 평가를 반복하여야 한다. 이 과정을 자동화하는 명령이 cross_val_score() 이다.

  • cross_val_score(estimator, X, y=None, scoring=None, cv=None)
    • cross validation iterator cv를 이용하여 X, y data 를 분할하고 estimator에 넣어서 scoring metric을 구하는 과정을 반복
  • 인수

    • estimator : ‘fit’메서드가 제공되는 모형
    • X : 배열
      • 독립 변수 데이터
    • y : 배열
      • 종속 변수 데이터
    • scoring : 문자열
      • 성능 검증에 사용할 함수
    • cv : Cross Validator
      • None 이면 디폴트인 3-폴드 CV
      • 숫자 K 이면 K-폴드 CV
      • Cross Validator 클래스 객체
  • 반환값

    • scores
      • 계산된 성능 값의 리스트

In [14]:
from sklearn.datasets import make_regression
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
X, y, coef = make_regression(n_samples=1000, n_features=1, noise=20, coef=True, random_state=0)
model = LinearRegression()
cv = KFold(1000, 10)

scores = np.zeros(10)
for i, (train_index, test_index) in enumerate(cv):
    X_train = X[train_index]
    y_train = y[train_index]
    X_test = X[test_index]
    y_test = y[test_index]
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    scores[i] = mean_squared_error(y_test, y_pred)

scores


Out[14]:
array([ 301.58271911,  341.91498985,  410.58098438,  499.68109613,
        461.00979825,  384.106544  ,  434.90159273,  377.65506997,
        366.60959935,  371.14031438])

In [15]:
from sklearn.cross_validation import cross_val_score
cross_val_score(model, X, y, "mean_squared_error", cv)


Out[15]:
array([-301.58271911, -341.91498985, -410.58098438, -499.68109613,
       -461.00979825, -384.106544  , -434.90159273, -377.65506997,
       -366.60959935, -371.14031438])

회귀 분석에 사용되는 성능 함수들

  • r2_score(y_true, y_pred[, ...]): R^2 (coefficient of determination) regression score function.
  • explained_variance_score(y_true, y_pred): Explained variance regression score function
  • mean_squared_error(y_true, y_pred[, ...]): Mean squared error regression loss
  • mean_absolute_error(y_true, y_pred): Mean absolute error regression loss
  • median_absolute_error(y_true, y_pred): Median absolute error regression loss