#!/usr/bin/env python3


다양한 교차 검증 방법


1. LOOCVLeave-one-out cross-validation

LOOCV 교차 검증폴드 하나샘플 하나만 들어 있는 k-겹 교차 검증

각 반복에서 하나의 데이터 포인트를 선택해 테스트 세트로 사용

특히 데이터셋이 클 때는 시간이 매우 오래 걸리지만, 작은 데이터셋에서는 좋은 결과를 만들어냄


# library load

from sklearn.datasets import load_iris

from sklearn.linear_model import LogisticRegression

from sklearn.model_selection import LeaveOneOut, cross_val_score, KFold


# dataset

iris = load_iris()


# create object

logreg = LogisticRegression() # model

loo = LeaveOneOut() # LeaveOneOut model


# test validation

# LOOCV

scores_loo = cross_val_score(logreg, iris.data, iris.target, cv=loo) 


# K-fold(5)

kfold = KFold(n_splits=5, random_state=0, shuffle=True)

scores_fold = cross_val_score(logreg, iris.data, iris.target, cv=kfold) # model, train, target, cross validation


# cv result

print('iris.data.shape \n{}'.format(iris.data.shape))

print('cv number of partition \n{}'.format(len(scores_loo)))

print('mean score_loocv \n{:.3f}'.format(scores_loo.mean()))

print('mean score_kfold \n{:.3f}'.format(scores_fold.mean()))


cv result


>> LOOCV가 K-fold보다 성능이 더 좋음을 알 수가 있음



2. shuffle-split cross-validation임의 분할 교차 검증

shuffle-split cross-validation에서는 

train_size만큼의 포인트로 train set를 만들고, test_size만큼(train set와 중첩되지 않는) 포인트로 test set를 만들도록 분할함

이 분할은 n_splits 횟수만큼 반복됨


# load library

import mglearn

import matplotlib.pyplot as plt

import matplotlib


# matplotlib 설정

matplotlib.rc('font', family='AppleGothic') # 한글출력

plt.rcParams['axes.unicode_minus'] = False # 축 -


mglearn.plots.plot_shuffle_split()

plt.show()


10개의 데이터 포인트에 train_size=5, test_size=2와 n_splits=4를 적용한 ShuffleSplit


>> train_size와 test_size에 정수를 입력하면 데이터포인트의 절대 갯수를

실수를 입력하면 전체 데이터에서의 비율을 의미


# iris데이터를 이용하여 shuffle-split cross-validation

# load library

from sklearn.model_selection import ShuffleSplit


# create object

shuffle_split = ShuffleSplit(test_size=0.5, train_size=0.5, n_splits=10) # ShuffleSplit 객체 생성


# cv test

scores = cross_val_score(logreg, iris.data, iris.target, cv=shuffle_split)


# cv test result

print('cv test score \n{}'.format(scores))

cv test score

>> 임의 분할 교차 검증은 반복 횟수를 훈련 세트나 테스트 세트의 크기와 독립적으로 조절해야 할때 유용함

또한 train_size와 test_size의 합을 전체와 다르게 함으로써 전체 데이터의 일부만 사용할 수 있음

이렇게 데이터를 subsampling부분 샘플링방식은 대규모 데이터셋으로 작업할 때 유용함



3. 그룹별 교차 검증

# load library

mglearn.plots.plot_group_kfold()

plt.show()

레이블에 기반한 GroupKFold분할


>> 이 그림에서 볼 수 있듯이 각 분할에서 한 그룹 전체가 훈련 세트 아니면 테스트 세트에 있음

그룹별 교차 검증의 자세한 정보는 아래 링크 참조

http://scikit-learn.org/stable/modules/cross_validation.html


데이터 안에 매우 연관된 그룹이 있을 때도 교차 검증을 널리 사용함

<<얼굴 사진에서 표정을 인식하는 시스템을 만들기 위해 100명의 사진을 모았다고 가정>>

한 사람을 찍은 여러 장의 사진이 각기 다른 표정을 담고 있음 ==>

이 데이터셋에 없는 사람의 표정을 정확히 구분할 수 있는 분류기를 만드는 것이 목표


같은 사람의 사진훈련 세트와 테스트 세트에 모두 나타날 수 있으므로 그룹별 교차겸증을 하는것이 타당


새 얼굴에 대한 일반화 성능을 더 정확하게 평가하려면

훈련 세트와 테스트 세트에 서로 다른 사람의 사진이 들어가도록 해야함


이를 위해 사진의 사람이 누구인지 기록한 배열을 

groups 매개변수로 전달 받을 수 있는 GroupKFold를 사용할 수 있음

groups 배열은 훈련 세트와 테스트 세트를 만들 때 분리되지 않아야 할 그룹을 지정하는 것이라 클래스 레이블과는 다름


데이터에 그룹에 있는 예로는 의료분야가 일반적

여기서는 같은 환자로부터 얻은 여러 샘플을 가지고 새로운 환자에게 일반화하는 것이 목적


비슷하게 음성 인식에서도 데이터셋에 같은 사람의 목소리가 여러 개 녹음되어 있을 수 있으며, 

관심사항은 새로운 사람의 대화를 인식하는 것


# load library

from sklearn.model_selection import GroupKFold

from sklearn.datasets import make_blobs


# datasets

x, y = make_blobs(n_samples=12, random_state=0)

plt.scatter(x[:, 0], x[:, 1], c=y)

plt.xlabel('feature 0')

plt.ylabel('feature 1')

plt.show()


make_blobs로 만든 인위적 데이터

>> 인위적으로 만든 데이터셋에 group 배열로 그룹을 지정하는 방법

데이터셋은 12개의 포인트로 이뤄져 있고 groups는 각 데이터 포인트에 대해 각 포인트가 어떤그룹(ex. 환자)에

속하는지를 나타냄

4개의 그룹을 나타내고 있으며 처음 3개의 샘플을 첫번째 그룹, 다음 4개는 두번째 그룹을 나타내는 식

샘플 데이터를 그룹으로 정렬할 필요는 없음


# create object

gkfold = GroupKFold(n_splits=3) # GroupKFold

groups = [0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3] # group class


# cv test

scores = cross_val_score(logreg, x, y, groups, cv=gkfold)


# cv test result

print('cv test score \n{}'.format(scores))

cv test score


#!/usr/bin/env python3


scikit-learn에서의 cross-validation(교차검증)


scikit-learn에서 교차 검증은 model_selection 모듈cross_val_score 함수로 구현되어 있음


1. iris 데이터셋에 cross-validation을 간단하게 적용하기

iris 데이터셋에 LogisticRegression을 평가해보면

LogisticRegression 이전 포스팅 참조하기: [1]Logistic Regression[1], [2]Logistic Regression[2]


# library import

from sklearn.model_selection import cross_val_score

from sklearn.datasets import load_iris

from sklearn.linear_model import LogisticRegression


# dataset

iris = load_iris()

logreg = LogisticRegression()


scores = cross_val_score(logreg, iris.data, iris.target, cv=5) # model, train, target, cross validation


print('cross-val-score \n{}'.format(scores))

print('cross-val-score.mean \n{:.3f}'.format(scores.mean()))


5겹 교차검증 결과와 평균


>> 교차 검증 평균값으로 우리는 이 모델의 정확도가 대략 96%일 것으로 기대할 수 있음

5겹 교차 검증이 만든 5개의 값을 모두 보면 100%에서 90%까지 폴드에 따라 비교적 차이가 큼

이는 모델이 훈련에 사용한 폴드에 매우 의존적이거나 데이터셋이 작기 때문일 수 있음



교차 검증의 장점은 train_test_split와 비교해보면

train_test_split는 데이터를 무작위로 나눔. 데이터를 무작위로 나눌 때

훈련 세트 ==>  분류하기 어려운 샘플

테스트세트 ==> 분류하기 쉬운 샘플

테스트 세트의 정확도는 비현실적으로 높게 나올 것입니다. 

반대의 경우라면 테스트 세트의 정확도는 비현실적으로 낮게 나옴


그러나 교차 검증을 사용하면 테스트 세트에 각 샘플이 정확하게 한번씩 들어가고

각 샘플은 폴드 중 하나에 속하며 각 폴드는 한번씩 테스트 세트가 됨 

그렇기 때문에 교차 검증의 점수를 높이기 위해서는 데이터 셋에 있는 모든 샘플에 대해 모델이 잘 일반화 되어야 함


데이터를 여러개로 나누면 모델이 훈련 데이터에 얼마나 민감한지 알 수가 있음 

iris데이터셋에서 90%~ 100%의 정확도를 얻음. 최악의 경우와 최선의 경우를 짐작할 수 있음


교차 검증의 주요 단점연산 비용이 늘어남

모델을 k개 만들어야 하므로 데이터를 한 번 나눴을때보다 대략 k배 더 느림



2. 계층별 k-겹 교차 검증과 그 외 전략들


# load library

import matplotlib.pyplot as plt

import matplotlib

from sklearn.datasets import load_iris

from sklearn.linear_model import LogisticRegression

from sklearn.model_selection import cross_val_score

import numpy as np


# data load

iris = load_iris()


print('iris label \n{}'.format(iris.target))

print('''iris label's bin \n{}'''.format(np.bincount(iris.target)))

iris label과 bin


>>첫번째 1/3은 클래스 0, 두번째 1/3은 클래스1 마지막 1/3은 클래스2 임을 확인할 수 있음

첫번째 폴드는 클래스 0만 가지고 있으므로, 첫 번째 반복에서 테스트세트는 클래스 0만을, 훈련세트는 클래스 1과 2만을 가지게 됨 

세번의 반복 모두 훈련 세트와 테스트 세트의 클래스가 다르므로 데이터셋에서 3-겹 교차 검증의 정확도는 0이 됨



단순한 k-겹 교차검증에는 문제가 있으니 stratified k-fold cross-validation계층별 k-겹 교차 검증을 사용함


# library import

import mglearn


# matplotlob 설정

matplotlib.rc('font', family='AppleGothic'# 한글출력

plt.rcParams['axes.unicode_minus']=False # 축 -


mglearn.plots.plot_stratified_cross_validation()

plt.show()

클래스 레이블 순서대로 정렬된 데이터에서 기본 교차 검증과 계층별 교차 검증 비교


계층별 교차 검증은 폴드 안의 클래스 비율이 데이터셋의 클래스 비율과 같도록 데이터를 나눔

예를 들어 샘플의 90%가 클래스 A이고 10%가 클래스 B에 속한다면, 계층별 교차 검증은 각 폴드에 클래스 A샘플이 90%, 클래스 B 샘플이 10%가 되도록 만듬



2. 교차 검증 상세 옵션


그러나 scikit-learn에서는 cv 매개변수에 교차 검증 분할기cross-vailidation splitter를 전달함으로써 데이터를 분할할 때 더 세밀하게 제어할 수 있음

대부분의 경우 회귀에서는 k-겹 교차 검증, 분류에서는 계층별 k-겹 교차검증의 기본값이 잘 작동함


하지만 조금 다른 전략이 필요할 때가 있음. 예를 들어 다른사람의 결과를 재현하기 위해 분류 데이터셋에 기본 k-겹 교차 검증을 사용해야 할때

이렇게 하려면 먼저 model_selection에서 KFold 분할기를 임포트하고 원하는 폴드 수를 넣어 객체를 생성해야 함


# library import

from sklearn.model_selection import KFold


kfold = KFold(n_splits=5) # KFold 객체 생성

logreg = LogisticRegression() # 모델 객체 생성


for n in [3, 5]:

    kfold = KFold(n_splits=n)

    scores = cross_val_score(logreg, iris.data, iris.target, cv=kfold)

    print('n_splits={}, cross validation score: {}'.format(n, scores))

n_splits에 따른 교차 검증 테스트 점수

>> n_splits=3일 때 각 폴드는 iris 데이터셋의 클래스 중 하나에 대응하므로 아무것도 학습할 수가 없음

계층별 폴드를 만드는 대신 이문제를 해결하는 방법은 

데이터를 섞어서 샘플의 순서를 랜덤하게 만드는것(KFold의 shuffle 매개변수True)


데이터를 섞을 때 random_state를 고정해서 똑같은 작업을 재현할 수있음

그렇지 않으면 cross_val_score를 실행할 때마다 폴드가 바뀌어 매번 결과가 달라짐


for n in [3, 5]:

    kfold = KFold(n_splits=n, shuffle=True, random_state=0)

    scores = cross_val_score(logreg, iris.data, iris.target, cv=kfold)

    print('n_splits={}, cross validation score: {}'.format(n, scores))


shuffle과 random_stat매개변수를 조정한 교차 검증 테스트 점수

>> n_splits에 상관없이 결과가 유의미하다는 것을 확인할 수가 있음



'모델 평가와 성능 향상 > 교차 검증' 카테고리의 다른 글

다양한 교차 검증 방법  (0) 2018.04.03
cross-validation  (0) 2018.04.02

+ Recent posts