1. 교차 검증
- 과적합 : 모델이 학습 데이터에마 과도하게 최적화된 현상. 그로인해 일반화된 데이터에서는 예측 성능이 과하게 떨어지는 현상
- 지난번 와인 맛 평가에서 훈련용 데이터의 Acc는 72.94, 테스트용 데이터는 Acc가 71.61%였는데, 누가 이결과가 정말 괜찮은 것인지 묻는다면?
- 나에게 주어진 데이터에 적용한 모델의 성능을 정확히 표현하기 위해서도 유용하다
- train 데이터를 다시 나누어 validation 데이터로 사용함으로써 과적합을 막는 방법
- 검증이 끝나면 다시 train데이터로 학습 한 뒤 test로 마지막 결과를 확인한다
2. 교차검증 구현하기
2.1 simple example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
from sklearn.model_selection import KFold
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([1, 2, 3, 4])
kf = KFold(n_splits=2)
print(kf.get_n_splits(X))
print(kf)
for train_idx, test_idx in kf.split(X):
print('-----idx')
print(train_idx, test_idx)
print('-----train data')
print(X[train_idx])
print('-----test data')
print(X[test_idx])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
KFold(n_splits=2, random_state=None, shuffle=False)
-----idx
[2 3] [0 1]
-----train data
[[1 2]
[3 4]]
-----test data
[[1 2]
[3 4]]
-----idx
[0 1] [2 3]
-----train data
[[1 2]
[3 4]]
-----test data
[[1 2]
[3 4]]
- Kfold는 sklearn에 포함되어 있음.
2.2 와인맛 분류
1
2
3
4
5
6
7
8
9
10
import pandas as pd
red_wine = pd.read_csv(red_url, sep = ';')
white_wine = pd.read_csv(white_url, sep = ';')
red_wine['color'] = 1
white_wine['color'] = 0
wine = pd.concat([red_wine, white_wine])
2.3 와인맛 분류기를 위한 데이터 정리
1
2
3
4
wine['taste'] = [1. if grade > 5 else 0. for grade in wine['quality']]
X = wine.drop(['taste', 'quality'], axis = 1)
y = wine['taste']
2.4 의사결정 나무 모델
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=13)
wine_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
wine_tree.fit(X_train, y_train)
y_pred_tr = wine_tree.predict(X_train)
y_pred_test = wine_tree.predict(X_test)
print('Train Acc : ', accuracy_score(y_train, y_pred_tr))
print('Test Acc : ', accuracy_score(y_test, y_pred_test))
1
2
Train Acc : 0.7294593034442948
Test Acc : 0.7161538461538461
- Kfold를 진행하지 않고 바로 train으로 데이터를 학습 한뒤 결과를 도출해냄
- 데이터를 train_test_split으로 랜덤하게 분리하는게 맞는것인지? 혹은 이것이 최선인건가?
- 또한, 저 acc는 신뢰할수 있는지에 대한 검증이 필요함
2.5 KFold
1
2
3
4
from sklearn.model_selection import KFold
kfold = KFold(n_splits=5)
wine_tree_cv = DecisionTreeClassifier(max_depth=2, random_state=13)
- Kfold를 만들고, 분류기도 다시 생성
- cv = cross validation의 약자
2.6 KFold는 index를 반환함
1
2
for train_idx, test_idx in kfold.split(X):
print(len(train_idx), len(test_idx))
1
2
3
4
5
5197 1300
5197 1300
5198 1299
5198 1299
5198 1299
2.7 각각의 fold에 대한 학습후 acc
1
2
3
4
5
6
7
8
9
cv_accuracy = []
for train_idx, test_idx in kfold.split(X):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
wine_tree_cv.fit(X_train, y_train)
pred = wine_tree_cv.predict(X_test)
cv_accuracy.append(accuracy_score(y_test, pred))
cv_accuracy
1
2
3
4
5
[0.6007692307692307,
0.6884615384615385,
0.7090069284064665,
0.7628945342571208,
0.7867590454195535]
2.8 각 acc의 분산이 크지 않다면 평균을 대표값으로
1
np.mean(cv_accuracy)
1
0.709578255462782
2.9 StratifiedKFold
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn.model_selection import StratifiedKFold
skfold = StratifiedKFold(n_splits= 5)
wine_tree_cv = DecisionTreeClassifier(max_depth=2, random_state=13)
cv_accuracy = []
for train_idx, test_idx in skfold.split(X, y):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
wine_tree_cv.fit(X_train, y_train)
pred = wine_tree_cv.predict(X_test)
cv_accuracy.append(accuracy_score(y_test, pred))
cv_accuracy
1
2
3
4
5
[0.5523076923076923,
0.6884615384615385,
0.7143956889915319,
0.7321016166281755,
0.7567359507313318]
2.10 acc의 평균이 더 나쁨
1
np.mean(cv_accuracy)
1
0.6888004974240539
- 인덱스를 반환하기 때문에 for문으로 각각 데이터를 validation하였다.
- 하지만 이렇게 불편하게 코드를 하나하나 짜가면서 해야하는것일까?
2.11 cross validation을 보다 더 간편하게
1
2
3
4
5
6
from sklearn.model_selection import cross_val_score
skfold = StratifiedKFold(n_splits=5)
wine_tree_cv = DecisionTreeClassifier(max_depth=2, random_state=13)
cross_val_score(wine_tree_cv, X, y , scoring=None, cv=skfold)
1
array([0.55230769, 0.68846154, 0.71439569, 0.73210162, 0.75673595])
- 위의 예제처럼 for문 코드를 짜서 돌리는것이 아닌 이미 Sklean에 cross_val_score로 한다
- sklearn에 cross_val_score에 분류기, X, y, Fold객체를 넣어주면된다
- skfold의 n_splits = 5는 데이터를 5개로 나누어 validation한다는 뜻
2.12 depth가 높다고 무조건 acc가 높은것도 아님
1
2
3
4
5
6
from sklearn.model_selection import cross_val_score
skfold = StratifiedKFold(n_splits=5)
wine_tree_cv = DecisionTreeClassifier(max_depth=5, random_state=13)
cross_val_score(wine_tree_cv, X, y, scoring=None, cv=skfold)
1
array([0.50076923, 0.62615385, 0.69745958, 0.7582756 , 0.74903772])
- 의사결정 나무의 max_depth를 늘려서 accuracy를 보았으나, 무조건 높아지는것도 아님을 확인
2.13 train score와 함께 보고싶다면
1
2
from sklearn.model_selection import cross_validate
cross_validate(wine_tree_cv, X, y, scoring=None, cv=skfold, return_train_score= True)
1
2
3
4
{'fit_time': array([0.01485896, 0.01390481, 0.01378489, 0.01321983, 0.0129869 ]),
'score_time': array([0.00206208, 0.00191903, 0.00187325, 0.00179911, 0.00170922]),
'test_score': array([0.50076923, 0.62615385, 0.69745958, 0.7582756 , 0.74903772]),
'train_score': array([0.78795459, 0.78045026, 0.77568295, 0.76356291, 0.76279338])}
- cross_validate의 return_train_score를 쓰면됨
- train과 test의 격차를 보니 과적합이 있는듯 하다.