파이썬 사이킷런 K Fold cross validation 방법
안녕하세요. 이번 글에서는 파이썬에서 k fold 교차 검증을 진행하는 방법을
scikit-learn의 KFold 및 StratifiedKFold 함수의 사용법 예제를 통하여 살펴보도록 하겠습니다.
데이터셋 로드
여기서는 사이킷런에서 제공하는 iris 데이터셋을 불러와보도록 하겠으며,
랜덤 포레스트를 통한 분류 모델에서 k-fold 교차 검증을 적용해보겠습니다.
from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
# 데이터셋 로드
iris = load_iris()
df = pd.DataFrame(data= np.c_[iris.data, iris.target] ,
columns= ['sepal length', 'sepal width', 'petal length', 'petal width', 'class'])
df
총 150개의 데이터가 각각 4가지의 feature와 class 정보를 가지고 있는 형태입니다.
KFold 함수 이용 데이터셋 분할 방법
먼저, 가장 기본적인 k fold 교차 검증을 수행하는 함수인 KFold의 사용법을 살펴보겠습니다.
KFold 함수에서 설정할 수 있는 argument의 목록은 아래와 같습니다.
n_splits : 분할할 세트의 개수, 1세트만 test 데이터로 사용하고 나머지는 train 데이터로 사용
shuffle : True로 설정 시 데이터셋 내의 순서를 섞어서 샘플링, False인 경우 순서대로 분할
random_state : seed 설정, 특정 정수로 지정 시 샘플링 결과 고정
n_splits는 5, shuffle은 False가 기본 값으로 주어져 있습니다.
5개의 세트로 train/test 셋을 분할하는 예시 코드는 아래와 같습니다.
데이터는 총 150개이므로, 매 스텝마다 train 셋은 120개, test 셋은 30개로 구성될 것입니다.
from sklearn.model_selection import KFold
X = np.array(df.iloc[:, :-1]) # class 열 제외한 feature 열들 모음 -> array 변환
y = df['class']
# split 개수, 셔플 여부 및 seed 설정
kf = KFold(n_splits = 5, shuffle = True, random_state = 50)
# split 개수 스텝 만큼 train, test 데이터셋을 매번 분할
for train_index, test_index in kf.split(X):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
feature 부분을 나타내는 X를 np.array 형태로 변환해주어야 오류가 발생하지 않습니다.
KFold 객체 변수 선언 후, kf.split(X) 형태에서 인덱스를 순회하며
각 스텝마다 train/test 세트를 추출해주시면 됩니다.
K-Fold 교차 검증을 통한 분류기 학습 방법
위에서 다루었던 KFold 함수의 분할 방법을 통하여
실제 랜덤포레스트 분류기를 교차 검증 학습하는 방법에 관한 예시 코드는 아래와 같습니다.
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
accuracy_history = []
# K-fold 검증 과정으로 실제 랜덤 포레스트 모델을 학습하여 정확도 평균을 내는 방법
for train_index, test_index in kf.split(X):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
model = RandomForestClassifier(n_estimators=5, random_state=0) # 모델 선언
model.fit(X_train, y_train) # 모델 학습
y_pred = model.predict(X_test) # 예측 라벨
accuracy_history.append(accuracy_score(y_pred, y_test)) # 정확도 측정 및 기록
print("각 분할의 정확도 기록 :", accuracy_history)
print("평균 정확도 :", np.mean(accuracy_history))
# 출력 결과
각 분할의 정확도 기록 : [0.9666666666666667, 0.9666666666666667, 0.9666666666666667, 1.0, 0.9]
평균 정확도 : 0.96
각 분할 시마다 학습할 모델을 다시 선언해주어야 한다는 점을 유의해주세요.
매 스텝에서 분할된 train/test 세트로 학습 및 검증을 통하여 정확도를 기록하고,
모든 스텝의 정확도 평균을 출력하여 분류기의 전반적인 성능을 테스트해 보았습니다.
class 별 동일 비율 분할 교차 검증 : StratifiedKFold 함수
교차 검증 시 주의해야할 사항으로 train/test 셋에서 각 클래스의 비율이 동일하게
분포해야한다는 점입니다.
예를 들어, 150개의 데이터에 0번, 1번, 2번 클래스가 50개씩 포함되어 있다고 가정하면,
30개의 테스트 셋에는 0, 1, 2번 클래스가 각 10개씩 포함되어야 이상적일 것입니다.
이렇게 각 라벨의 비율을 동일하게 뽑고 싶다면, scikit-learn의 또 다른 교차검증 함수인
StratifiedKFold 함수를 통하여 데이터셋 분할을 진행해주시면 됩니다.
StratifiedKFold 함수의 argument 종류와 전반적인 사용법은 KFold 함수와 거의 동일합니다.
StratifiedKFold를 이용한 데이터셋 분할 예시 코드는 아래와 같습니다.
from sklearn.model_selection import StratifiedKFold
# split 개수, 셔플 여부 및 seed 설정
str_kf = StratifiedKFold(n_splits = 5, shuffle = True, random_state = 50)
# split 개수 스텝 만큼 train, test 데이터셋을 매번 분할
for train_index, test_index in str_kf.split(X, y):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
다만, 분할 과정에서 라벨 분포의 정보도 받아와야 하므로 for 문 내에서
kf.split(X) 대신 kf.split(X, y) 형태로 선언한 점에서 차이가 있었습니다.