본문으로 바로가기

Validation (Cross-val, learning-curve, GridSearch)

category AI/Machine Learning 2021. 2. 23. 00:47

Cross validation

    - 모델의 일반화 성능 (새로운 데이터에서 대한 예측 또는 분류)

 

Holdout cross-validation

    - 모델의 성능을 높이기 위해 hyper-parameter 튜닝을 통해 모델 선택 과정을 거쳐야 한다. 이때, train, validation, test 3 개로 나눈다.

    - validation dataset으로 모델 선택에 사용한다.

    - 다만, train dataset을 나누는 방법에 따라 성능 추정이 민감하다는 단점이 있다.

    - data의 양이 매우 많을 때 사용하는 것을 권장한다.

 

k-fold cross-validation

1) 개요

    - train_test_split을 여러 번 수행하는 것과 같은 방식으로 holdout방식에 비해 많은 computing power가 요구된다.

    - holdout 방법에 비해 train dataset split에 덜 민감하다.

    - train data가 충분하지 않다면 fold 개수를 늘리는 것이 좋다.

    - fold 개수가 터무니없이 크다면 train fold가 비슷해져 분산이 높아질 수 있다.

    - 경험적으로 좋은 k 기본값은 10이며, 대규모 dataset의 경우 5이다.

 

2) 과정

    (1) 중복을 허락하지 않고 train dataset을 k개의 fold로 랜덤하게 나눈다.

    (2) k - 1개의 fold로 훈련하고 나머지 하나의 fold로 성능을 평가한다.

        (k번 반복 -> k개의 모델과 성능 추정을 얻음)

    (3) k개의 성능 추정으로 모델의 평균 성능 계산

    (4) 최적의 hyper-parameter로 전체 train dataset으로 훈련하고 test dataset을 사용해 최종 성능추정

 

3) LOOCV (Leave-One-Out Cross-Validation)

    - fold 개수 k가 train sample 개수 n과 같다. (k = n)

    - 매우 작은 dataset에 사용된다.

 

4) Stratified k-fold cross-validation

    - 각 fold에서 class label 비율이 전체 dataset에서의 비율을 대표하도록 유지한다.

 

5) 코드 예시

'''작동 원리 코드'''
import numpy as np
from sklearn.model_selection import StratifiedKFold

kfold = StratifiedKFold(n_splits = 10, random_state = 1, shuffle = True).split(X_train, y_train)

scores = []
for k, (train, test) in enumerate(kfold):
    pipe_lr.fit(X_train[train], y_train[train])
    score = pipe_lr.score(X_train[test], y_train[test])
    scores.append(score)
    print(f"폴드: {k+1}, 클래스 분포: {np.bincount(y_train[train])}, 정확도: {round(score, 3)}")

print(f"CV 정확도: {np.mean(scores)} +/- {np.std(scores)}")

'''sklearn kfold cv'''
from sklearn.model_selection import cross_val_score

scores = cross_val_score(estimator = pipe_lr, X = X_train,
                         y = y_train, cv = 10, n_jobs = -1)

print(f"CV 정확도 점수 : {scores}")
print(f"CV 정확도: {np.mean(scores)} +/- {np.std(scores)}")

'''평가 지표 설정 후 train, test 데이터 점수'''
from sklearn.model_selection import cross_validate

scores = cross_validate(estimator = pipe_lr, X = X_train,
                        y = y_train, scoring = ['accuracy'],
                        cv = 10, n_jobs = -1, return_train_score = False)
print(f"CV 정확도 점수 : {scores['test_accuracy']}")
print(f"CV 정확도: {np.mean(scores['test_accuracy'])} +/- {np.std(scores['test_accuracy'])}")

학습곡선

    - 그래프를 통해 분산과 편향의 문제점을 시각적으로 확인할 수 있다.

import matplotlib.pyplot as plt
from sklearn.model_selection import learning_curve

pipe_lr = make_pipeline(StandardScaler(),
                        LogisticRegression(solver = 'liblinear',
                                           penalty = 'l2',
                                           random_state = 1))

train_sizes, train_scores, test_scores = learning_curve(estimator = pipe_lr,
                                                        X = X_train,
                                                        y = y_train,
                                                        # linspace로 일정간격 훈련세트의 비율선택
                                                        train_sizes = np.linspace(
                                                        0.1, 1.0, 10),
                                                        cv = 10,
                                                        n_jobs = 1)
train_mean = np.mean(train_scores, axis = 1)
train_std = np.std(train_scores, axis = 1)
test_mean = np.mean(test_scores, axis = 1)
test_std = np.std(test_scores, axis = 1)

plt.plot(train_sizes, train_mean,
         color = 'blue', marker = 'o',
         markersize = 5, label = 'training accuracy')

plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha = 0.15, color = 'blue')

plt.plot(train_sizes, test_mean,
         color = 'green', linestyle = '--',
         marker = 's', markersize = 5,
         label = 'validation accuracy')

plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha = 0.15, color = 'green')

plt.grid()
plt.xlabel('Number of training samples')
plt.ylabel('Accuracy')
plt.legend(loc = 'lower right')
plt.ylim([0.8, 1.03])
plt.tight_layout()
plt.show()

 

검증곡선

    - 학습곡선과 다르게 모델 parameter 값의 함수로 그린다.

# logistic regression's hyper-parameter : C
from sklearn.model_selection import validation_curve

param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0]
trian_scores, test_scores = validation_curve(estimator = pipe_lr,
                                             X = X_train,
                                             y = y_train,
                                             param_name = 'logisticregression__C',
                                             param_range = param_range,
                                             cv = 10)

train_mean = np.mean(train_scores, axis = 1)
train_std = np.std(train_scores, axis = 1)
test_mean = np.mean(test_scores, axis = 1)
test_std = np.std(test_scores, axis = 1)

plt.plot(param_range, train_mean,
         color = 'blue', marker = 'o',
         markersize = 5, label = 'training accuracy')

plt.fill_between(param_range,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha = 0.15, color = 'blue')

plt.plot(param_range, test_mean,
         color = 'green', linestyle = '--',
         marker = 's', markersize = 5,
         label = 'validation accuracy')

plt.fill_between(param_range,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha = 0.15, color = 'green')

plt.grid()
plt.xlabel('Parameter C')
plt.ylabel('Accuracy')
plt.xscale('log')
plt.legend(loc = 'lower right')
plt.ylim([0.8, 1.0])
plt.tight_layout()
plt.show()

Gridsearch

    - 최적의 hyper parameter를 찾기 위해 사용한다.

from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

pipe_svc = make_pipeline(StandardScaler(),
                         SVC(random_state = 1))

param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0]
param_grid = [{'svc__C':param_range,
               'svc__kernel':['linear']},
              {'svc__C':param_range,
               'svc__gamma':param_range,
               'svc__kernel':['rbf']}]

gs = GridSearchCV(estimator = pipe_svc,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 10,
                  n_jobs = -1)

gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)

'''위에서 나온 최적의 parameter로 바로 학습가능'''
clf = gs.best_estimator_
clf.fit(X_train, y_train)
print(f"테스트 정확도 : {round(clf.score(X_test, y_test), 3)}")

 

Randomizedserach

    - gridsearch의 계산 비용이 매우 크기 때문에 제한된 횟수 안에서 샘플링 분포로부터 랜덤한 매개변수 조합을 찾는다. (n_iter 매개변수를 통해 횟수를 설정할 수 있다.)


중첩 교차 검증 (nesting cross validation)

    - Gridsearch와 kfold cv를 함께 사용하여 여러 종류의 머신러닝 알고리즘을 비교할 수 있다.

'''SVM'''
gs = GridSearchCV(estimator = pipe_svc,
                  param_grid = param_grid,
                  scoring = 'accuracy',
                  cv = 2)

scores = cross_val_score(gs, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print("cv 정확도 : ", np.mean(scores), np.std(scores))

'''Decision Tree'''
from sklearn.tree import DecisionTreeClassifier

gs = GridSearchCV(estimator = DecisionTreeClassifier(random_state = 0),
                  param_grid = [{'max_depth': [1, 2, 3, 4, 5, 6, 7, None]}],
                  scoring = 'accuracy',
                  cv = 2)

scores = cross_val_score(gs, X_train, y_train,
                         scoring = 'accuracy', cv = 5)
print("cv 정확도 : ", np.mean(scores), np.std(scores))

참고

머신러닝 교과서 (길벗)

'AI > Machine Learning' 카테고리의 다른 글

Ensemble (Voting, Bagging, Boosting)  (0) 2021.02.24
Model evaluation metrics  (0) 2021.02.23
Model pipeline  (0) 2021.02.22
Regularization  (0) 2021.02.19
Random Forest  (0) 2021.02.19