교차 검증

모형 검증

회귀 모형을 만든 다음에는 모수 추정(parameter fitting) 즉 트레이닝(training)에 사용되지 않은 새로운 독립 변수 데이터를 사용하여 최종 검증(final test)을 하게 된다. 최종 검증 후 모형의 성능은 다음과 같은 요소에 의해 결정된다.

  • 편향 오차(bias): 새로운 데이터(test data)에 대해서 평균 오차의 크기가 얼마나 작은가?
  • 오차 분산(variance): 새로운 데이터(test data)에 대해 오차의 크기가 얼마나 달라지는가?

교차 검증

모형 성능 중에서 분산을 계산하려면 검증 데이터 셋이 복수개가 있어야 한다. 그러나 실제로는 데이터의 수는 유한하므로 이 데이터를 복수개의 데이터 셋으로 나누어 여러번 검증하는 방식을 사용한다. 이를 교차 검증(cross-validation)이라고 한다.

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.]
================================================================================

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.]
================================================================================

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.]
================================================================================

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