본문 바로가기
ML/ML-Kaggle, 데이콘

Hold-out, 교차검증(K-Flod), Stratified K-fold

by lucian 2021. 12. 9.

먼저 Hold out에 대해 알아보자.

 

Hold-out

전체(train) 데이터를 훈련과 검증 데이터로 나뉘는 것을 말한다.(여기서 전체 데이터셋은 train과 test 데이터 중 train만 말함. - 즉 데이터가 train=(훈련, 검증), test=(테스트)로 나뉜다는 뜻)

기본적으로 8:2, 7:3으로 나눠 훈련(train)데이터는 모델을 훈련 시킬 때, 검증(valid) 데이터는 모델이 얼마나 잘 훈련되었는지 확인할 때 쓴다.

이렇게 하면 모델의 예측성능을 측정할 수 있다.이 모델이 과대적합이 일어났는지 제대로 훈련되고 좋은 성과를 냈는지 test 데이터를 넣기 전에 알 수 있기 때문에 나뉜다. 

 

하지만 이 Hold-out방식은 데이터 자원을 낭비한다는 문제가 있다.

왜냐면 검증으로 쓸 데이터는 훈련에 쓰이지 않기 때문에 아깝기 때문이다.

그렇기에 모든 데이터를 사용하기 위해 다양한 방법들이 나왔는데 그 중 하나가 "교차검증"이다.

 

 

 

교차검증(K-Fold)

전체 데이터를 K개로 나눈다.

예를 들어 데이터를 5개(K=5)로 나눈다. 그럼 1번 데이터를 검증으로 사용할 때, 나머지 2,3,4,5번 데이터를 학습에 쓰인다. 반복문을 통해서 이번엔 2번 데이터를 검증으로 사용하고 1,3,4,5번을 학습으로 쓴다.

이것을 반복해서 총 5번을 돌리다 보면 학습에 사용하지 않은 데이터가 없게 된다.

 

즉 5개의 학습 모델의 성능이 나오게 된다. 이 5개의 평균을 구한 것이 이 모델의 성능이라 할 수 있다.

 

https://jinnyjinny.github.io/deep%20learning/2020/04/02/Kfold/

 

 


!wget 'https://bit.ly/3i4n1QB'

import zipfile
with zipfile.ZipFile('3i4n1QB', 'r') as existing_zip:
    existing_zip.extractall('data')
import pandas as pd
from sklearn.preprocessing import MinMaxScaler, OneHotEncoder
from sklearn.ensemble import RandomForestClassifier

from sklearn.model_selection import KFold # 교차검증 (K-Fold cross validation)
from sklearn.metrics import accuracy_score # valid결과와 실제 값과 비교를 통해 정확도를 만들어주는 함수
import numpy as np

 

 

 

이제 데이터셋들을 정제해줘야한다. 분류모델이기 때문에 수치에 대해 민감하다. 그렇기 때문에

1. 수치 데이터를 정규화시켜주는 scailing을 시켜준다.

2. one-hot 인코딩으로 red, white로 분류되어 있는 컬럼을 덤프컬럼으로 만들어준다.

3. test 데이터셋도 똑같이 만들어 줘야지 모델의 predict함수를 쓸 수 있다.(열의 갯수와 이름이 다를 경우 당연하게도 오류가 발생한다.)

train = pd.read_csv('data/train.csv')
test = pd.read_csv('data/test.csv')

# Scailing
scaler = MinMaxScaler()
scaler.fit(train[['fixed acidity']])
train['Scaled fixed acidity'] = scaler.transform(train[['fixed acidity']])
test['Scaled fixed acidity'] = scaler.transform(test[['fixed acidity']])

# Encoding
encoder = OneHotEncoder()
encoder.fit(train[['type']])
onehot = encoder.transform(train[['type']])
onehot = onehot.toarray()
onehot = pd.DataFrame(onehot)
onehot.columns = encoder.get_feature_names()
train = pd.concat([train, onehot], axis = 1)
train = train.drop(columns = ['type'])

onehot = encoder.transform(test[['type']])
onehot = onehot.toarray()
onehot = pd.DataFrame(onehot)
onehot.columns = encoder.get_feature_names()
test = pd.concat([test, onehot], axis = 1)
test = test.drop(columns = ['type'])

test.head()


model=RandomForestClassifier()
valid_scores=[] # 교차검증을 반복할 때매다 나온 점수를 저장하는 리스트
test_predictions=[] # 교차 검증을 반복할 때마다 테스트 데이터셋으로 나온 결과를 저장하는 리스트

 

이제 진짜 이번 포스트의 메인인 교차검증에 대한 것이다.

from sklearn.model_selection import KFold


# n_splits : 데이터는 K개로 나눠주는 하이퍼 파라미터
# shuffle : 데이터를 섞어서 나눠준다.
# random_state : shuffle했을 때의 seed값. shuffle을 False로 했을 경우는 필요가 없음
kf=KFold(n_splits=5, shuffle=True, random_state=0

# for문을 실행하여 교차검증을 실행한다. 
# kf.split가 for문이 돌아갈 때마다 train_idx와 valid_idx가 바뀐다.
for train_idx, valid_idx in kf.split(X,y):
	X_tr=X.iloc[train_idx]
    y_tr=y.iloc[train_idx]
    
    X_val=X.iloc[valid_idx]
    y_val=y.iloc[valid_idx]
    
    model.fit(X_tr,y_tr)
    
    valid_prediction=model.predict(X_val)
    score=accuarcy_score(valid_prediction, y_val) #검증 데이터로 나온 결과와 실제 결과가 얼마나 정확한지 파악해주는 함수
    valid_scores.append(score)
    print(score)
    
    test_prediction=model.predict(test.drop(columns=['index']))
    test_predictions.append(test_prediction)
 

test_predictions=pd.DataFrame(test_predictions)
test_prediction=test_predictions.mode() # DF.mode()는 열별 최빈값을 확인해준다.

test_predictions.mode()의 결과. 1행은 최빈값을 2행은 NaN값이 표시된다.

 

mode()함수로 최빈값을 뽑아낸 df에 1행의 값 array형태로 가져온다.

데이터프레임은 값을 가져올 때, values나 columns로 불러온다.

values인 경우 행을 하나씩 리스트의 형태로 가져오고 [ 홍길동, 23, 010-2222-1111], [김갑수, 77, 010-2323-1121] ...]

columns인 경우 [이름, 나이, 전화번호] 이렇게 열의 이름을 가져와준다.

test_prediction = test_prediction.values[0]

 

 

# 이제 제출해야할 sample_submission을 불러온 후
# 우리가 뽑아낸 값을 'quality'열에 넣어준다.
# 마지막으로 저장.
sample_submission = pd.read_csv('data/sample_submission.csv')
sample_submission['quality'] = test_prediction
sample_submission.to_csv('data/submission_KFOLD.csv', index=False)

 


 

Stratified K-Fold

 

이렇게 K개로 나눠서 데이터를 검증하고 학습하는 방법을 K-fold라 한다.

그러나 k-fold에도 문제점이 있다.

 

k-fold의 경우 데이터셋을 일정한 간격으로 잘라서 사용한다.

그러다보니 target(레이블, 정답)의 비율이 일정하지 않게! 검증 데이터에 들어갈 수 있다.

이해하기 쉬운 예를 들어보자.

우리가 예측해야할 값은 1,2,3이라 가정한다.

그리고 데이터의 갯수가 300개이고 100개마다 1,2,3씩 target이 되어있다.

 

이 상황에서 k-fold=3으로 해서 200개를 학습데이터, 100개를 검증데이터로 두었는데 학습데이터에 target인 1,2만 있고 3은 없다. 그러면 검증데이터에만 3이 들어가 있는데 이러면 학습데이터로 학습한 모델은 검증 데이터의 3이라는 답을 절대 도출할 수가 없다.

 

이러한 점이 k-fold의 아주 치명적이 문제점이다.

이런 단점을 극복하기 위해 나온 것이 stratified K-fold이다.

 

Stratified K-fold는 이 target의 비율을 학습시 일정하게 유지시켜줘서 이 문제를 방지해준다.

즉 학습데이터에도 target이 1,2,3 고루 분배되어있고 검증데이터에도 1,2,3이 고루 분배되게하여 이 문제를 방지해준다.

 

 

하는 법은 k-fold와 같다. 그저 K-fold에서 Stratified K-fold로 바꿔주면 될 뿐이다.

from sklearn.model_selection import StratifiedKFold


# n_splits : 데이터는 K개로 나눠주는 하이퍼 파라미터
# shuffle : 데이터를 섞어서 나눠준다.
# random_state : shuffle했을 때의 seed값. shuffle을 False로 했을 경우는 필요가 없음
skf=StratifiedKFold(n_splits=5)

# for문을 실행하여 교차검증을 실행한다. 
# kf.split가 for문이 돌아갈 때마다 train_idx와 valid_idx가 바뀐다.
for train_idx, valid_idx in skf.split(X,y):
	X_tr=X.iloc[train_idx]
    y_tr=y.iloc[train_idx]
    
    X_val=X.iloc[valid_idx]
    y_val=y.iloc[valid_idx]
    
    model.fit(X_tr,y_tr)
    
    valid_prediction=model.predict(X_val)
    score=accuarcy_score(valid_prediction, y_val) #검증 데이터로 나온 결과와 실제 결과가 얼마나 정확한지 파악해주는 함수
    valid_scores.append(score)
    print(score)
    
    test_prediction=model.predict(test.drop(columns=['index']))
    test_predictions.append(test_prediction)
 

test_predictions=pd.DataFrame(test_predictions)
test_prediction=test_predictions.mode() # DF.mode()는 열별 최빈값을 확인해준다.

위의 코드처럼 kfold가 들어가는 자리에 StratifiedKFold를 넣어주면 된다.

'ML > ML-Kaggle, 데이콘' 카테고리의 다른 글

다중공산성  (0) 2021.12.17
Bayesian Optimization  (2) 2021.12.16
GBM(Gradient Boosting Model)  (0) 2021.10.08
EDA  (0) 2021.10.08
OneHot 인코딩-OneHotEncoder(), pd.get_dummies()  (3) 2021.10.08

댓글