데이터를 분리하는 이유
위 그림을 보면 왼쪽부터 과소적합, 적절, 과대적합 입니다.
과소적합이란, 데이터를 충분히 학습시키지 않은 상태를 의미합니다.
Train data를 잘 맞추지 못 한다면 과소적합상태입니다.
과대적합이란, 너무 훈련데이터에 맞춰진 모델을 의미합니다.
훈련데이터를 지나치게 학습시켜 다른 데이터가 들어오면 잘 맞추지 못하는 상태입니다.
Train data의 정확도는 좋지만 Test data를 잘 맞추지 못한다면 과대적합니다.
Data Split
위의 문제점을 해결하기위해 데이터를 분리 해야 합니다.
훈련, 검증, 테스트 데이터로 분리합니다.
훈련데이터는 학습에 사용되는 데이터이고,
검증데이터는 학습이 완료된 모델을 검증하기 위한 데이터입니다.(학습에는 사용되지 않음)
테스트데이터는 최종 모델의 성능을 검증하기 위한 데이터입니다.
하지만 검증데이터는 학습에는 사용되지 않아도 관여는 하는 데이터이기 때문에, 이 검증데이터에 Overfitting이 될 수 있습니다.
교차 검증(KFold)
위의 문제를 해결하려면 교차검증(Cross Validation)을 사용하는데, Valid 데이터를 고정하지 않고 계속해서 변경함으로써 Overfitting 되는 것을 막는 것입니다. 좀더 간략히 설명하자면 본고사를 치기 전 모의고사를 여러 번 보는 것이라고 생각하면 됩니다.
KFold
가장 보편적으로 사용되는 교차 검증 기법입니다. K번만큼 각 폴드 세트에 학습과 검증 평가를 반복적으로 수행하는 방법입니다. 5개의 폴드된 데이터를 학습, 검증을 위한 데이터 세트로 변경하면서 5번 평가를 수행한 뒤, 5개의 평가를 평균한 결과를 가지고 예측 성능을 평가합니다. 붓꽃 데이터 세트로 예시를 들겠습니다.
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
import numpy as np
iris = load_iris()
features = iris['data']
label = iris['target']
dt_clf = DecisionTreeClassifier()
# 5개의 폴드
kfold = KFold(n_splits=5)
cv_accuracy = []
print('붓꽃 데이터 세트 크기:', features.shape[0])
데이터 세트의 크기가 150개이고, KFold가 5개이므로 앞에서부터 30개씩 검증할것입니다.
n_iter = 0
for train_idx, valid_idx in kfold.split(features):
# 학습, 검증 데이터세트 분리
X_train, X_valid = features[train_idx], features[valid_idx]
y_train, y_valid = label[train_idx], label[valid_idx]
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_valid)
n_iter += 1
acc = np.round(accuracy_score(y_valid, pred), 4)
train_size = X_train.shape[0]
valid_size = X_valid.shape[0]
print('\n#{0} 교차 검증 정확도 :{1}, 학습 데이터 크기: {2}, 검증 데이터 크기: {3}'
.format(n_iter, acc, train_size, valid_size))
print('#{0} 검증 세트 인덱스:{1}'.format(n_iter, valid_idx))
cv_accuracy.append(acc)
print('\n## 평균 검증 정확도:', np.mean(cv_accuracy))
교차 검증 시 마다 검증 세트의 인덱스가 다 달라지고, 정확도도 다른것을 볼 수 있습니다.
Stratified K Fold
Stratified K 폴드는 불균형한 분포도를 가진 레이블 데이터 집합을 위한 K 폴드 방식입니다. 특정 레이블 값이 특이하게 많거나 매우 적어서 한쪽으로 치우치는 것을 말합니다.'
보이스피싱 데이터를 예측한다고 가정해 보겠습니다. 데이터 세트가 1억 건이라 가정하고, 수십 개의 피처와 사기 여부를 뜻하는 레이블로 구성돼 있습니다. 99%가 정상 1%가 사기라고 가정하고, KFold를 10개로 가져간다고 가정하면,
10%정상 | 10%정상 | 10%정상 | 10%정상 | 10%정상 | 10%정상 | 10%정상 | 10%정상 | 10%정상 | 9%정상, 1%사기 |
앞의 9개의 Fold는 정상 데이터만 학습하여 보이스피싱 데이터가 없습니다. 그래서 9개의 폴더는 의미가 없어집니다.
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
9.9%정상 0.1%사기 |
정확히 분배가 잘 되었습니다. 10개의 폴드에서 모두 사기와 정상 데이터를 학습할 수 있게 되었습니다. 이를 가능하게 하는것이 Stratified K Fold 입니다. 아래 코드를 보겠습니다.
import pandas as pd
iris = load_iris()
iris_df = pd.DataFrame(data=iris['data'], columns=iris.feature_names)
iris_df['label'] = iris['target']
iris_df['label'].value_counts()
0, 1, 2 3개의 라벨들이 50개씩 균등하게 분포 되어 있습니다. 자 여기서 KFold를 3으로 하고 데이터 분포가 어떻게 되는지 확인 해보겠습니다.
kfold = KFold(n_splits=3)
for iter, (train_index, valid_index) in enumerate(kfold.split(iris_df), 1):
label_train = iris_df['label'].iloc[train_index]
label_valid = iris_df['label'].iloc[valid_index]
print('## 교차 검증: {0}'.format(iter))
print('학습 레이블 데이터 분포:\n', label_train.value_counts())
print('학습 레이블 데이터 분포:\n', label_valid.value_counts())
아까 말씀드린 설명처럼 균등하게 분포가 되지 않았습니다. 자 이제 StratefiedKFold를 사용해보겠습니다.
StratefiedKFold는 label을 균등하게 분배해야 하기 때문에 split의 인자에 (데이터, label) 2개를 입력해주어야 합니다.
from sklearn.model_selection import StratifiedKFold
skfold = StratifiedKFold(n_splits=3)
for iter, (train_index, valid_index) in enumerate(skfold.split(iris_df, iris_df['label']), 1):
label_train = iris_df['label'].iloc[train_index]
label_valid = iris_df['label'].iloc[valid_index]
print('## 교차 검증: {0}'.format(iter))
print('학습 레이블 데이터 분포:\n', label_train.value_counts())
print('검증 레이블 데이터 분포:\n', label_valid.value_counts())
'머신러닝' 카테고리의 다른 글
[머신러닝] 의사결정트리(Decision Tree) 알고리즘 (2) | 2022.05.05 |
---|---|
[머신러닝] 성능 평가 지표 (0) | 2022.05.04 |
[머신러닝] Logistic Regression (0) | 2022.05.04 |
[머신러닝] 회귀(Regression) (0) | 2022.05.03 |
[머신러닝] 머신러닝이란? (0) | 2022.05.03 |
댓글