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

교차 검증

모형 검증

앞서 이야기 했듯이 모형의 모수 갯수를 증가시킨다든가 커널 모형, 신경망 모형과 같은 비선형 모형을 사용하게 되면 과최적화가 발생하여 트레이닝 데이터에 대한 예측 성능이 높아진다. 이러한 과최적화(overfitting)가 일어나면 트레이닝 데이터에 대해서는 예측이 잘되지만 학습에 쓰이지 않았던 데이터에 대해서는 예측 성능이 급격히 떨어지는 현상이 발생한다.

따라서 모형의 최종 성능을 객관적으로 측정하려면 트레이닝에 사용되지 않은 새로운 데이터, 즉 검증(validation)용 혹은 테스트(test)용 데이터를 사용해서 예측한 결과를 기반으로 성능을 계산해야 한다.

교차 검증

일반적으로 검증용 데이터가 별도로 존재하는 경우가 많지 않기 때문에 보통은 원래 학습용으로 확보한 데이터를 학습용과 검증용으로 분리하여 학습용 데이터로 학습한 후 검증용 데이터로 검증한다. 이 때 데이터를 어떻게 분리하느냐에 따라 검증 성능이 조금씩 달라질 수 있으므로 여러가지 방식으로 데이터를 분리하여 검증을 실시하고 평균 성능(mean performance)과 성능 분산(performance variance)를 모두 구한다.

이러한 검증 방법을 교차 검증(cross validation)이라고 한다.

Scikit-Learn의 교차 검증 기능

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

  • data를 train set과 test set으로 단순 분리
    • data splitter
      • train_test_split() 명령
  • 복수의 test set 준비

    • cross validation generator
      • KFold
      • LeaveOneOut
      • ShuffleSplit
  • 복수의 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.model_selection 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])

Cross-Validation

KFold 클래스를 비롯한 교차 검증 클래스 객체들은 Cross Valiation Generator 들로서 트레이닝/테스트용 데이터 인덱스를 내보내는 iterator를 출력하는 split 메서드를 제공한다.

K-fold CV

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

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.model_selection import KFold

cv = KFold(n_splits=3, shuffle=True, random_state=0)
for train_index, test_index in cv.split(X):
    print("test index :", test_index)
    print("." * 80 )        
    print("train index:", train_index)
    print("=" * 80 )
test index : [ 1  6  8 10 17 18 19]
................................................................................
train index: [ 0  2  3  4  5  7  9 11 12 13 14 15 16]
================================================================================
test index : [ 2  4  5  7  9 13 14]
................................................................................
train index: [ 0  1  3  6  8 10 11 12 15 16 17 18 19]
================================================================================
test index : [ 0  3 11 12 15 16]
................................................................................
train index: [ 1  2  4  5  6  7  8  9 10 13 14 17 18 19]
================================================================================

Leave-One-Out (LOO)

  • 하나의 sample만을 test set으로 남긴다.
In:
from sklearn.model_selection import LeaveOneOut

cv = LeaveOneOut()

for train_index, test_index in cv.split(X):
    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.]
================================================================================
test X: [[100 110]]
................................................................................
test y: [ 2.]
================================================================================
test X: [[120 130]]
................................................................................
test y: [ 2.]
================================================================================
test X: [[140 150]]
................................................................................
test y: [ 2.]
================================================================================
test X: [[160 170]]
................................................................................
test y: [ 2.]
================================================================================
test X: [[180 190]]
................................................................................
test y: [ 2.]
================================================================================
test X: [[200 210]]
................................................................................
test y: [ 3.]
================================================================================
test X: [[220 230]]
................................................................................
test y: [ 3.]
================================================================================
test X: [[240 250]]
................................................................................
test y: [ 3.]
================================================================================
test X: [[260 270]]
................................................................................
test y: [ 3.]
================================================================================
test X: [[280 290]]
................................................................................
test y: [ 3.]
================================================================================
test X: [[300 310]]
................................................................................
test y: [ 4.]
================================================================================
test X: [[320 330]]
................................................................................
test y: [ 4.]
================================================================================
test X: [[340 350]]
................................................................................
test y: [ 4.]
================================================================================
test X: [[360 370]]
................................................................................
test y: [ 4.]
================================================================================
test X: [[380 390]]
................................................................................
test y: [ 4.]
================================================================================

ShuffleSplit

  • 중복된 데이터를 허용
In:
from sklearn.model_selection import ShuffleSplit

cv = ShuffleSplit(n_splits=5, test_size=.5, random_state=0)

for train_index, test_index in cv.split(X):
    print("test X:\n", X[test_index])
    print("=" * 20 )        
test X:
 [[360 370]
 [ 20  30]
 [380 390]
 [160 170]
 [200 210]
 [340 350]
 [120 130]
 [260 270]
 [ 80  90]
 [ 40  50]]
====================
test X:
 [[220 230]
 [ 20  30]
 [360 370]
 [340 350]
 [ 40  50]
 [240 250]
 [380 390]
 [320 330]
 [200 210]
 [  0  10]]
====================
test X:
 [[300 310]
 [260 270]
 [240 250]
 [100 110]
 [220 230]
 [ 40  50]
 [160 170]
 [120 130]
 [ 60  70]
 [340 350]]
====================
test X:
 [[360 370]
 [  0  10]
 [260 270]
 [ 40  50]
 [ 60  70]
 [340 350]
 [140 150]
 [240 250]
 [280 290]
 [320 330]]
====================
test X:
 [[140 150]
 [ 20  30]
 [ 40  50]
 [380 390]
 [100 110]
 [360 370]
 [160 170]
 [340 350]
 [300 310]
 [320 330]]
====================

교차 평가 시행

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 r2_score

X, y, coef = make_regression(n_samples=1000, n_features=1, noise=20, coef=True, random_state=0)

model = LinearRegression()
cv = KFold(10)

scores = np.zeros(10)
for i, (train_index, test_index) in enumerate(cv.split(X)):
    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] = r2_score(y_test, y_pred)

scores
Out:
array([ 0.95636425,  0.94908323,  0.93880683,  0.92906829,  0.93119768,
        0.95362566,  0.93217768,  0.94308775,  0.94579305,  0.94749884])
In:
from sklearn.model_selection import cross_val_score

cross_val_score(model, X, y, scoring="r2", cv=cv)
Out:
array([ 0.95636425,  0.94908323,  0.93880683,  0.92906829,  0.93119768,
        0.95362566,  0.93217768,  0.94308775,  0.94579305,  0.94749884])

질문/덧글

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) 모형을 사용합니다.

적응형(adaptive) 모형이 무엇인지 찾지못해 질문드립니다. samk*** 2017년 12월 2일 2:13 오후

바로 위의 답변 덕분에 이해가 쉽게 되었습니다. 그런데 적응형(adaptive) 모형이 무엇인지 잘 모르겠어서 질문남깁니다.

사용자에 의해 삭제되었습니다. urim*** 2018년 3월 29일 4:13 오후

사용자에 의해 삭제되었습니다.

train / validation / test셋 분할에 대해 질문드립니다. urim*** 2018년 3월 29일 4:14 오후

본문에서는 데이터셋을 3개로 나누지 않고 train/test로 나눠서 test셋이 validation의 기능을 한다고 설명하셨는데요.
sklearn에서도 KFold 클래스를 확인해보니 train과 test의 인덱스를 리턴해주는 걸로 나오구요.

그런데, 제가 이해하고 있던 부분과 좀 달라서 질문 드립니다.
제가 알기로는 머신러닝을 하는 과정에서 데이터를 보통 3개로 나눠서
1. train으로 학습,
2. validation으로 오버피팅 막으며 model selection 기능
3. test는 건드리지 않고 있다가 최종 스코어 산출

validation set으로 최적 성능의 모델을 찾는 것 과정 자체도 validation셋에 대한 학습이 어느정도 되는걸로 보기 때문에
최종 스코어 산출을 위한 test셋을 따로 빼놓는 것으로 이해하고 있었습니다.

제가 이해한 것에 어떤 잘못된 것이 있는지 조언 부탁드립니다.