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

교차 검증

모형 검증

앞서 이야기 했듯이 모형의 모수 갯수를 증가시킨다든가 커널 모형, 신경망 모형과 같은 비선형 모형을 사용하게 되면 과최적화가 발생하여 트레이닝 데이터에 대한 예측 성능이 높아진다. 이러한 과최적화(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) 모형을 사용합니다.