티스토리 뷰
훈련만 하고 검증하지 않으면 두가지 문제 발생.
1. 모델 과대적합.
훈련데이터와 테스트 데이터는 고정되어 있으므로 훈련 데이터에만 과대적합
퍼블릭 리더보드에서 높아도 프라이빗에서는 점수가 떨어진다.
2. 제출 전까지 모델 성능 확인 어려움
일일 제출 횟수 제한.
무작정 제출하기 힘든 환경.
검증 데이터를 성능 가늠이 가능하나, 훈련에 사용하지 못해 손실.
실무에서도 마찬가지. 아직 주어지지 않은 미래 데이터로 미리 테스트 불가능.
위 두 문제를 해결하기 위한 게 교차 검증
가장 일반적인 건 K 폴드 교차 검증
1. 전체 훈련 데이터를 K개 그룹으로 나눈다.
2. 그룹 하는 검증 데이터로, 나머지 K-1개는 훈련 데이터로 지정.
3. 훈련 데이터로 모델을 훈련, 검증 데이터로 평가
4. 평가점수 기록
5. 검증 데이터를 다른 그룹으로 바꿔가며 2~4 절차를 K번 반복
6. K개 검증 평가점수의 평균을 구한다.
K개 검증 평가점수의 평균이 최종 평가점수
https://www.kaggle.com/werooring/ch5-cross-validation
import numpy as np
from sklearn.model_selection import KFold
data = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
folds = KFold(n_splits=5, shuffle=False) # 1
for train_idx, valid_idx in folds.split(data):
print(f'훈련 데이터: {data[train_idx]}, 검증 데이터: {data[valid_idx]}')
훈련 데이터: [2 3 4 5 6 7 8 9], 검증 데이터: [0 1]
훈련 데이터: [0 1 4 5 6 7 8 9], 검증 데이터: [2 3]
훈련 데이터: [0 1 2 3 6 7 8 9], 검증 데이터: [4 5]
훈련 데이터: [0 1 2 3 4 5 8 9], 검증 데이터: [6 7]
훈련 데이터: [0 1 2 3 4 5 6 7], 검증 데이터: [8 9]
KFold( n_splits(데이터를 몇 개로 나눌 건지) , Shuffle= 데이터를 편향되지 않게 섞어줌)
이왕이면 shuffle을 쓰는 게 좋다.
folds = KFold(n_splits=5, shuffle=True)
for train_idx, valid_idx in folds.split(data):
print(f'훈련 데이터: {data[train_idx]}, 검증 데이터: {data[valid_idx]}')
훈련 데이터: [0 1 2 3 5 6 7 9], 검증 데이터: [4 8]
훈련 데이터: [2 3 4 5 6 7 8 9], 검증 데이터: [0 1]
훈련 데이터: [0 1 2 4 5 6 7 8], 검증 데이터: [3 9]
훈련 데이터: [0 1 3 4 5 6 8 9], 검증 데이터: [2 7]
훈련 데이터: [0 1 2 3 4 7 8 9], 검증 데이터: [5 6]
#shuffle이 true인 경우
층화 K 폴드 교차 검증
타깃값이 불균형하게 분포되어 있는 경우 사용.
타깃값이 골고루 분포되게 폴드를 나눈다.
스팸 메일 1000개인데 스팸이 10개 -> 특정 폴드에는 스팸이 아예 없을 수 있음.
특정 타깃값이 다른 타깃값보다 굉장히 적은 경우에 유용하다.
폴드가 5개면 각 폴드에 스팸 데이터 2개씩 골고루 분배.
분류 문제에만 쓰인다. 타깃값이 유한해야 하기 때문.
아래는 일반 K 폴드 예시
y = np.array(['스팸']*5 + ['일반']*45)
folds = KFold(n_splits=5, shuffle=True) # K 폴드 교차 검증
for idx, (train_idx, valid_idx) in enumerate(folds.split(y)):
print(f'Fold {idx+1} 검증 데이터 타깃값:')
print(y[valid_idx], '\n')
Fold 1 검증 데이터 타깃값:
['스팸' '스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 2 검증 데이터 타깃값:
['일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 3 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 4 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 5 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 2에 안들어감
from sklearn.model_selection import StratifiedKFold
X = np.array(range(50))
y = np.array(['스팸']*5 + ['일반']*45)
folds = StratifiedKFold(n_splits=5) # '층화' K 폴드 교차 검증
for idx, (train_idx, valid_idx) in enumerate(folds.split(X, y)):
print(f'Fold {idx+1} 검증 데이터 타깃값:')
print(y[valid_idx], '\n')
Fold 1 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 2 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 3 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 4 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
Fold 5 검증 데이터 타깃값:
['스팸' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반' '일반']
전체에 골고루 들어갔다.
KFold의 split()에는 데이터 하나만 전달해도 된다. 임의로 K개로 분할해서.
StratifiedKFold의 split()에는 피처와 타깃값 모두를 전달해야 한다.
위의 코드에서, folds.split(X, y) 에 X는 np.array(range(50)), y는 분류하고자 하는 함수가 들어갔다.
.X는 독립변수, y는 종속변수이다.
예시 데이터에서 X는 .data, y는 .target으로 구해진다.
인수를 하나만 전달하면 오류 발생.
출처 : 머신러닝 딥러닝 문제해결 전략