작성자: admin 작성일시: 2016-06-05 11:51:43 조회수: 1961 다운로드: 79
카테고리: 머신 러닝 태그목록:

교차 검증

모형 검증

예측 모형의 최종 성능을 객관적으로 측정하려면 모수 추정(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:
X = np.arange(10).reshape((5, 2))
X
Out:
array([[0, 1],
       [2, 3],
       [4, 5],
       [6, 7],
       [8, 9]])
In:
y = np.arange(5)
y
Out:
array([0, 1, 2, 3, 4])
In:
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:
X_train
Out:
array([[4, 5],
       [0, 1],
       [6, 7]])
In:
y_train
Out:
array([2, 0, 3])
In:
X_test
Out:
array([[2, 3],
       [8, 9]])
In:
y_test
Out:
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:
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:
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:
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:
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:
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:
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:
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:
array([ 301.58271911,  341.91498985,  410.58098438,  499.68109613,
        461.00979825,  384.106544  ,  434.90159273,  377.65506997,
        366.60959935,  371.14031438])
In:
from sklearn.cross_validation import cross_val_score
cross_val_score(model, X, y, "mean_squared_error", cv)
Out:
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

질문/덧글

cross_val_score()함수에 sm.OLS.from_formula()방식을 적용할 수 없을까요? todh*** 2016년 10월 9일 4:28 오후

Pipeline과 scikit learn의 LinearRegression을 이용해 모델을 만든 후 cross_validation cost를 이용해 cost를 구하는 경우 PolynomialFeatures()같은 전처리를 이용할경우 모든 변수에 전처리를 적용해서 세밀한 조정이 어려운것 같습니다.
EX>>
pf = PolynomialFeatures(2)
pl = Pipeline([('FeatureUnion', FeatureUnion(
[('characters' , Pipeline([('change_str',str_extractor()),
('Label_encoding',MultiColumnLabelEncoder()),
('One_Hot_Encoding',ohe),
('Dense_transform',dt)]),),
('real_number',Pipeline([('float_extractor',float_extractor()),
('poly_nomial', pf),
('standar_scaler', StandardScaler())]))
])
)])
features = pl.fit_transform(df.ix[:,3:-1])
cv = cross_validation.KFold(len(df.price),n_folds= 5)
model = LinearRegression()
cross_val_score( model,features,df.price,'r2',cv)

LinearRegression 대신 sm.OLS.from_formula방식을 사용해서 cross_val_score()함수를 조금더 새밀하게 조정할 수는 없을까요?
model = sm.OLS.from_formula(''' np.log(price)~I(fanc**2)+ + distance_gn + distance_md +
distance_park +
room_type +parking + elevator
+heat_type +animal +aircondition +laundry +bed +desk +closet +tv +shoes
+refrigerator +gas +induction +microwave +doar_lock +bidet''',data=df)

답변: cross_val_score()함수에 sm.OLS.from_formula()방식을 적용할 수 없을까요? 관리자 2016년 10월 11일 8:53 오전

statsmodels 패키지와 scikit-learn 패키지는 모형 클래스가 다르므로 상호 호환이 되지 않습니다. 즉, statsmodels 패키지의 model은 scikit-learn 패키지의 cross_val_score 명령에서 사용할 수 없습니다. 사용하시려면 cross_val_score 에서 요구하는 조건을 갖추도록 Wrapper class를 만들어야 합니다.

교차검증이 된 후 새로운 데이터가 생겼다면 samk*** 2016년 12월 22일 2:47 오후

교차검증이 된 모델이 만들어 진 후 새로운 데이터를 얻게 되었을 때,
기존 모델의 예측값이 새로 생긴 데이터과 차이가 많이 날 경우 아래 2가지 방법 중 어느 것이 더 나은가 입니다.

1. 새로운 데이터를 포함한 모든 데이터를 새롭게 validation set, training set으로 모델을 만든 뒤 교차검증을 다시 수행

2. 새로운 데이터만 validation set, training set으로 나누고 기존 모델을 구성한 데이터에 분리해서 넣고 모델을 만든 뒤 교차검증을 수행

답변: 교차검증이 된 후 새로운 데이터가 생겼다면 관리자 2016년 12월 23일 9:54 오후

데이터를 기반으로 모형을 만들 때 가장 기본적인 가정은 모든 데이터가 같은 모형에 나왔다는 가정입니다.
즉 데이터가 수집되는 동안 모형의 모수 변화, 혹은 구조적 변화가 없다는 가정을 사용합니다.
만약 이 가정을 계속 유지할 수 있다고 생각한다면 방법 1을 쓸 수 있습니다.
하지만 신규 데이터와의 오차가 좀 더 구조적인 것이라는 합리적인 근거가 있다면 새로운 모형을 만들거나 적응형(adaptive) 모형을 사용합니다.