AI 머신러닝 딥러닝/파이썬 머신러닝 입문 공부일지

파이썬 머신러닝 입문 공부일지 12. train_test_split(), 교차 검증 (1) KFold 클래스

Tomitom 2023. 1. 5. 12:43
반응형

 

사이킷런의 Model Selection 모듈은 학습 데이터와 테스트 데이터 세트를 분리하거나 교차 검증 분할 및 평가, 그리고 Estimator 의 하이퍼 파라미터 튜닝을 위한 함수와 클래스 등을 제공합니다. 

 

우선 그동안 학습/테스트 데이터 세트를 분리할 때 썼던 train_test_split() 함수부터 알아보겠습니다. 

 

train_test_split()

 

train_test_split() 함수를 통해 원본 데이터 세트에서 학습 및 테스트 세트를 쉽게 분리할 수 있습니다. 

우선 sklearn.model_selection  모듈에서 train_test_split 을 import 합니다. 

 

train_test_split() 은 첫 번째 파라미터로 피처 데이터 세트, 두 번째 파라미터로 레이블 데이터 세트를 입력 받습니다.

그 다음으로는 선택적으로 다음의 파라미터들을 입력 받습니다. 

 

  • test_size: 전체 데이터에서 테스트 데이터 세트 크기를 얼마로 샘플링 할 것인가를 결정합니다.(디폴트는 0.25)
  • train_size: 전체 데이터에서 학습용 데이터 세트 크기를 얼마로 샘플링 할 것인가를 결정합니다. 하지만 통상적으로 test_size 를 사용하기 때문에 train_size 는 잘 사용되지 않습니다. 
  • shuffle: 데이터를 분리하기 전에 데이터를 미리 섞을지를 결정합니다. (디폴트는 True) 데이터를 분산시켜서 좀 더 효율적인 학습 및 테스트 데이터 세트를 만들어 줍니다.
  • random_state: 호출할 때마다 동일한 학습 및 테스트용 데이터 세트를 생성하기 위해 주어지는 난수 값입니다. 이 파라미터를 지정하지 않으면 수행할 때마다 다른 학습 및 테스트 용으로 데이터를 생성하게 됩니다.
  • train_test_slpit() 반환 값은 튜플 형태로 순차적으로 '학습용 데이터의 피처 데이터 세트' , '테스트용 데이터의 피처 데이터 세트', '학습용 데이터의 레이블 데이터 세트', '테스트용 데이터의 레이블 데이터 세트' 가 반환됩니다. 

 

머신러닝에서는 데이터를 train, validation, test 이렇게 세가지로 나눕니다. 

 

즉, 머신러닝 data데이터의 구조는 다음과 같습니다.  (자세한 구조는 하단에 교차 검증에서 참고1 이미지를 확인해주세요)

 

 

train과 validation 은 training 과정에서 사용하며, 트레이닝 과정에서 모델을 중간중간 평가해야 하기 때문에 

train data가 트레이닝을 진행하고 validation data가 데이터를 평가하여 최종 모델을 생성합니다. 

 

모델이 훈련되었다면 최종적으로 '한 번도 보지 못한 데이터' 에 대한 평가를 해야하는데, 

이 때 사용하는 것이 test data 입니다. 

test data는 최종적으로 한 번 사용되며 모델 평가의 지표가 됩니다. 

 

일반적으로 데이터는 train.csv 와 test.csv (단 한번 사용하는!)  로 나누어져 있습니다. 

train.csv 에서 train 과 validation 을 나누어야 하므로 사이킷런은 train_test_splite() 메소드를 제공합니다. 

 

train_test_splite() 메소드를 통해 다음과 같은 데이터가 return 반환됩니다. 

=> X_train, X_test, y_train, y_test  

 

X_train, y_train  == train data

X_test, y_test == validation data 

 

Q. 저는 여기서 개인적으로 왜 X_test 의  X는 대문자인데 y_test 의 y는 소문자인가 의아했었는데요.

X는 행렬 이고 y는 벡터이기 때문이라고 합니다.  (보통 수학에서는 행렬을 대문자, 벡터를 소문자로 표현합니다!)

즉 피처feature는 X이고, label 은 y입니다. 여러 X들로 정해지는 값이 y가 되는 것이죠. 

가령 붓꽃 데이터 품종 3개(0, 1, 2)가 있는데 이것을 y라고 할 때 그것을 결정해주는 피처가 X가 되는 것입니다. 

x들의 성향으로 y가 정해져요. 

 

예제 확인

 

이제 붓꽃 데이터 세트를 load 해서 train_test_split() 을 이용해 데이터 세트를 전체의 30%로, 학습 데이터 세트를 70%로 분리하겠습니다. 

 

from sklearn.datasets import load_iris   # 붓꽃 데이터 로드 
from sklearn.metrics import accuracy_score # 평가모듈 중 정확도 
from sklearn.tree import DecisionTreeClassifier # 결정트리 모델 
from sklearn.model_selection import train_test_split # 학습/테스트 데이터 세트 분리 

df_clf = DecisionTreeClassifier() # 결정트리 alias 별칭
iris_data = load_iris()  # 붓꽃 로드 데이터 alias 별칭 

X_train, X_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target,
                                                   test_size=0.3, random_state=1)

 

데이터 세트를 분리하고 훈련을 시작할 준비가 되었으니

이제 훈련 학습을 시작하고, 훈련이 완료된 결정트리객체로 예측을 수행합니다. 

그 뒤 예측 결과를 기반으로 레이블 데이터 세트의 정확도를 측정해봅니다. 

 

df_clf.fit(X_train, y_train)   # 훈련 자료로 훈련 시작
pred = df_clf.predict(X_test)   # 훈련이 완료된 결정트리객체를 예측 수행
result = accuracy_score(y_test, pred)  # 예측 결과를 기반으로 레이블 데이터 세트의 정확도를 평가

print('예측 정확도 : ' , round(result,4))

 

테스트 데이터로 예측 수행결과 95%의 정확도를 얻었습니다.

 

이렇듯 알고리즘을 학습 시키는 학습 데이터와 이에 대한 예측 성능을 평가하기 위해서는 별도의 테스트용 데이터가 필요합니다. 하지만 이 방법은 학습 데이터에만 과도하게 최적화 되어, 실제 예측을 다른 데이터로 수행할 때 예측 성능이 과도하게 떨어지는 문제점이 발생할 수 있습니다.  이를 '과적합Overfitting' 이라고 합니다. 

 

이러한 과적합의 문제를 개선하기 위해 생긴 것이 바로 교차 검증입니다. 

 

   

교차 검증

 

교차 검증은 전체 샘플 데이터 갯수가 많지 않을 때, 데이터를 부풀리는 방법입니다. 

마치 실제 시험 전에 모의시험을 여러 번 보는 것처럼, 교차 검증에서 여러 개의 학습/검증 세트에서 알고리즘 학습과 평가를 수행합니다. 

 

대부분의 머신러닝 모델의 성능 평가는 교차 검증을 기반으로 1차 평가를 한 뒤에 (모의시험을 치르듯!) 최종적으로 테스트 데이터 세트에 적용해 평가를 합니다. 

 

 

참고 1 이미지!

 

 

상기 그림에서 보는 것과 같이 학습 데이터 Train Dataset을 다시 분할하여 학습데이터Train Dataset와 검증 데이터Validation Dataset 으로 나눕니다. (검증 데이터는 학습된 모델의 성능을 1차로 평가합니다.) 

 

이후에 모든 학습과 검증 과정이 완료된 후 최종적으로 성능을 평가하기 위한 데이터 세트인 Test Dataset로 평가합니다. 

 

K 폴드 교차 검증

 

K 폴드 교차 검증은 가장 보편적으로 사용되는 교차 검증입니다. 

K 개의 데이터 폴드 세트를 만들어서 K번만큼 각 폴드 세트에 학습과 검증 평가를 반복적으로 수행하는 방법입니다. 

여기에서 폴드는 그냥 덩어리라고 생각하시면 편해요! 

저는 마치 케이크 조각을 자르듯 하나의 데이터를 여러 개로 조각낸 덩어리같은 것이라 이해했습니다. 

 

아래의 그림은 5번의 교차 검증을 수행한 것입니다. (즉 K가 5 일 때!) 

5개의 폴드 된 데이터 세트를 학습과 검증을 위한 데이터 세트로 변경하면서 5번 평가를 수행하고, 

이 5개의 평가를 평균한 결과를 가지고 예측 성능을 평가합니다. 

 

 

(1) 데이터 세트를 5 (K) 등분 한다. 

(2) 첫 번째 반복에서 처음부터 4번째 등분의 학습 데이터 세트, 마지막 5번째 등분을 검증 데이터 세트로 설정하고 학습과 검증을 수행한다. 

(3) 두 번째 반복에서 처음부터 3번째 등분, 5번째 등분을 학습 데이터 세트, 마지막 4번째 등분을 검증 데이터 세트로 설정하고 학습과 검증을 수행한다. 

(4) 상기 학습과 검증을 반복… 

(5) 이렇게 점진적으로 변경하면서 마지막 5번째 까지 학습과 검증을 수행한다 .

(6) 이에 대한 평균이 K 폴드 평가 결과로 반영된다. 

 

사이킷런에서는 이러한 K 폴드 교차 검증 프로세스를 위한 클래스로 KFold, StartifiedKFold 클래스를 제공합니다. 

각각의 클래스를 아래에서 직접 사용하고 알아볼게요. 

 

(1) KFold 클래스 

 

붓꽃 데이터와 결정트리를 생성하여 5개 폴드 세트를 생성해봅니다. 

 

KFold(n_splits=n) 

폴드를 n개 나눌 수 있습니다. 

 

from sklearn.model_selection import KFold
import numpy as np

iris = load_iris()
features = iris.data
label = iris.target
dt_clf = DecisionTreeClassifier(random_state=1)

Kfold = KFold(n_splits=5)
cv_accuarcy = []
print( '전체 붓꽃 데이터 세트 크기 : ', features.shape[0])

 

잠깐!

여기에서 등장한 shape[0] 에 대해서 잠깐 알아보고 갈게요! (개인적으로 제가 궁금했었거든요 (^^)) 

파이썬 numpy 에서 등장한 shape 은 exam = [ [6, 3, 2,], [4, 3, 2 ] ]  의 2행 3열 행렬인 exam 이 있을 때

exam.shape 으로  exam 의 행렬의 값을 알 수 있습니다. 

(2, 3) 으로 출력 되어요. 

 

여기에서 shape[0] 은 행의 개수를 표현하고, 

shape[1] 은 열의 개수를 표현합니다. 

즉 전체 붓꽃 데이터 세트의 크기는 피처가 아닌 레이블의 개수를 구해야하기 때문에 shape[0] 으로 행(X)의 개수를 구한 것입니다. 

 

이제 KFold = 5 로 5 개의 폴드 데이터 세트를 분리했으니,

전체 150개의 데이터 중 120개는 학습용 데이터 세트, 30는 검증용 테스트 데이터 세트가 됩니다. 

 

 

 

 

반응형

 

 

 

KFold 폴드 객체 split() 분리 

 

KFold 를 통해 만들어진 폴드 객체는 학습용과 검증용 데이터가 자동으로 나뉘지 않기 때문에 개발 코드에서 직접 분리를 해주어야 합니다. 

 

Kfold.split() 를 호출해서 학습용/검증용 데이터로 분할할 수 있는 인덱스 index 를 반환합니다.

for 반복문을 통해 해당 피처들을 모두 하나씩 불러서 train_index 와 test_index 에 대입합니다. 

그리고 그 인덱스에서 학습용과 검증용 데이터 세트들을 분리하겠습니다. 

 

아래 코드에서 n_iter = 0 을 선언한 뒤 반복문을 실행하는 이유는 

for 문에 대한 loop 루프를 몇 번이나 반복했는지 확인하기 위해 만든 변수 입니다. 

교차 검증을 위해서 for loop 를 돌 때마다 값이 하나씩 += 1 증가합니다. 

 

 

from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score
from sklearn.tree import DecisionTreeClassifier
import numpy as np

iris = load_iris()
features = iris.data
label = iris.target
dt_clf = DecisionTreeClassifier(random_state=1)

kfold = KFold(n_splits=5)
cv_accuracy = []

n_iter = 0

for train_index, test_index in kfold.split(features):
    X_train, X_test = features[train_index], features[test_index]
    y_train, y_test = label[train_index], label[test_index]
    
    dt_clf.fit(X_train, y_train)
    pred = dt_clf.predict(X_test)
    n_iter += 1    # n_iter는 for loop시 몇번 반복했는지 확인하기 위해서 만든 변수
                    #교차검증을 위해서 for loop를 돌때마다 값이 하나씩 증가합니다
    accuracy = np.round(accuracy_score(y_test, pred), 4)
    train_size = X_train.shape[0]  # 학습 데이터 크기를 알아보기 위한 것 (몇 개인지)
    test_size = X_test.shape[0]   # 검증 데이터 크기를 알아보기 위한 것 (몇 개인지)
    
    print(n_iter, " 번째 실행")
    print("교차 검증 정확도는 : ", accuracy)
    print("학습 데이터 크기 : ", train_size)
    print("검증 데이터 크기 : ", test_size)
    print("")
    cv_accuracy.append(accuracy)
    
    
print("\n평균 검증 정확도 :", np.mean(cv_accuracy))

 

만약 저기에서 for문이 이해하기 어렵다면 우선 붓꽃데이터 iris.data 를 kfold.split 으로 나눈 index 값을 확인해보면 좋습니다. 

 

for train_index, test_index in kfold.split(features):

    print("train_index :", train_index)
    print("")
    print("test_index :", test_index)
    print("")
    print("----------------------------\n")

 

이렇게 해서 train_index 와 test_index 를 확인해보면 30개를 기준으로 5개의 폴드가 나뉘어진 것을 확인할 수 있습니다. 

5개의 폴드에서 각각 X_train 과 X_test, y_train 과 y_test 를 나누어 5번의 검증과 평가를 하는 거예요. 

 

 

 

 

 

 

교차 검증 중 KFold 방식에 대해서 알아보았습니다.

이 다음에는 두 번째 교차 검증인 Startified K 폴드 방식에 대해서 알아보겠습니다. 

 

 

 

 

 

반응형