sklearn의 Model selection module 살펴보기
- sklearn의 model_selection module에서 제공하는 함수/클래스
- train data/test data의 분리 (train_test_split())
- 교차 검증 분할 및 평가(KFold, Stratified_KFold)
- Estimator의 하이퍼 파라미터를 튜닝
In [10]:
# train/test를 분할하지 않았을 때의 문제 : 모의고사만 똑같은 거 2번 보는 셈
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
iris = load_iris()
dt_clf = DecisionTreeClassifier()
train_data = iris.data
train_label = iris.target
dt_clf.fit(train_data, train_label)
# 학습 데이터 세트로 예측
pred = dt_clf.predict(train_data)
print('예측 정확도 : ', accuracy_score(train_label, pred))
1. train_test_split()
- 학습/테스트 데이터 세트 분리 - train_test_split()
- sklearn.model_selection module에서 train_test_split 로드
- Parameter
- 1st : feature data set
- 2nd : label data set
- other
- test_size: 전체 데이터 중 test data의 크기. default는 0.25
- shuffle : default는 True. 데이터 분리 전 미리 섞을 지 결정함.
데이터를 분산시켜서 좀 더 효율적인 학습/테스트 데이터 세트 생성가능
- random_state : 호출할 때마다 동일한 학습/테스트용 데이터 세트를 생서하기 위한 것
- Parameter
- 반환값 : tuple 형태. 순서가 지정되어 있음.
- 학습용 데이터의 피처 데이터 세트, 테스트용 데이터의 피처 데이터 세트
- X_train, X_test
- 학습용 데이터의 레이블 데이터 세트, 테스트용 데이터의 레이블 데이터 세트
- y_train, y_test
In [11]:
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris_data = load_iris()
dt_clf = DecisionTreeClassifier()
X_train, X_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, \
test_size=0.3, random_state=121)
In [12]:
# 학습 데이터를 기반으로 dt_clf 학습, 모델을 이용해 예측 정확도 측정
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
print('예측 정확도:{0:.4f}'.format(accuracy_score(y_test, pred))) # 순서 : 실제, 예측
2. 교차 검증
- Overfitting(과적합)의 문제
- 과적합 : 모델이 학습데이터에만 과도하게 최적화되어 실제 예측을 다른 데이터로 수행 시, 예측 성능이 과도하게 떨어지는 현상
- 문제점을 개선하기 위한 것이 교차 검증
- 교차 검증의 개념
- 본 고사를 치루기 전 최대한 여러번의 모의고사를 보는 것
- 본고사 = test data set
- 모의 고사
- train data를 더 쪼개서 진행
- train(학습)
- Verification(검증)
- test
- 교차 검증에서 많은 학습과 검증 세트에서 알고리즘 학습과 평가를 수행하는 것
- train data를 더 쪼개서 진행
- ML은 데이터에 기반 -> 데이터는 이상치, 분포도, 다양한 속성값, 피처 중요도 등 여러가지 ML에 영향을 미치는 요소를 가짐
- 데이터 편중을 막기 위해 별도의 여러 세트로 구성된 학습 데이터 세트와 검증 데이터 세트에서 학습과 평가를 수행
- 대부분의 ML model의 성능 평가는 교차 검증 기반으로 1차 평가 -> 최종적으로 test data set에 적용해 평가하는 precess
- ML에 사용되는 data set를 세분화하여 train(학습)/Verification(검증)/test data set로 나눔
- test data set외에 별도의 Verification data set를 둬서 최종 평가 이전에 다양한 평가를 수행
2.1 KFold 교차 검증
- 가장 보편적으로 사용되는 교차 검증 기법
- K개의 데이터 폴드 세트를 생성
- K번만큼 각 폴드 세트에 학습과 검증 평가를 반복적으로 수행
- K번의 평가를 평균내어 예측 성능을 평가
In [13]:
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
from sklearn.datasets import load_iris
import numpy as np
iris = load_iris()
features = iris.data
label = iris.target
dt_clf = DecisionTreeClassifier(random_state=156)
# 5개의 폴드 세트로 분리하는 KFold 객체와 폴드 세트별 정확도를 담을 리스트 객체 생성
kfold = KFold(n_splits=5)
cv_accuracy=[] # 각각의 정확도를 담을 리스트 객체
print('붓꽃 데이터 세트 크기 : ', features.shape[0])
In [14]:
# 전체 붓꽃 데이터를 5개의 폴드 데이터 세트로 분리
# 1/5는 검증 테스트 데이터 세트, 4/5는 학습용 데이터 세트
# 학습용/검증용 데이터 추출은 반환된 인덱스를 기반으로 개발코드에서 직접 수행
n_iter = 0
# KFold 객체의 split()를 호출하면 폴드 별 학습용, 검증용 테스트의 로우 인덱스를 array로 반환
for train_index, test_index in kfold.split(features): # KFold는 매개변수로 feature data set가 들어감.
# kfold.split()으로 반환된 인덱스를 이용해 학습용, 검증용 테스트 데이터 추출
X_train, X_test = features[train_index], features[test_index]
y_train, y_test = label[train_index], label[test_index]
# 학습 및 예측
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
n_iter += 1
# 반복 시마다 정확도 측정
accuracy = np.round(accuracy_score(y_test, pred), 4) # np.round(a,4) : a를 소수점 4째자리까지 출력
train_size = X_train.shape[0]
test_size = X_test.shape[0]
print('\n#{0} 교차 검증 정확도 : {1}, 학습 데이터 크기 : {2}, 검증데이터 크기: {3}'
.format(n_iter, accuracy, train_size, test_size))
print('#{0} 검증 세트 인덱스 : {1}'.format(n_iter, test_index))
cv_accuracy.append(accuracy)
# 개별 iteration별 정확도를 합하여 평균 정확도 계산
print('\n## 평균 검증 정확도 : ', np.mean(cv_accuracy))
2.2 Stratified K Fold
- 불균형한(imbalanced) 분포도를 가진 label(결정 클래스) data set을 위한 K Fold 방식
- 불균형한 분포도를 가진 label data set : 특정 레이블 값이 더 많거나 적어서 분포가 치우친 상태
- ex) 대출 사기 데이터 예측. 정상 대출(0) 대비 사기(1) 건수는 0.001% 정도
- 1 label이 너무 적음. KFold에서는 이 1 label이 한 data set에 몰려버릴 수 있음.
- 원본 데이터와 유사한 대출 사기 label 값의 분포를 학습/테스트 세트에도 유지해야함
- 불균형한 분포도를 가진 label data set : 특정 레이블 값이 더 많거나 적어서 분포가 치우친 상태
- Stratified KFold는 원본 데이터의 label 분포를 먼저 고려함. 해당 분포와 동일하게 학습/검증 데이터 세트를 분해함.
- 일반적으로 분류(Classification)에서의 교차 검증은 KFold가 아니라 StratifiedKFold로 분할되어야 함.
- 회귀(Regression)에서는 Stratified KFold가 지원되지 않음 -> 회귀의 결정값은 이산값 형태의 label이 아니라 연속된 숫자값이므로 분포를 결정할 필요가 없음
In [15]:
import pandas as pd
from sklearn.datasets import load_iris
iris = load_iris()
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
iris_df['label'] = iris.target
iris_df['label'].value_counts()
Out[15]:
In [16]:
# KFold의 이슈 현상을 직접 확인.
# 3개의 폴드를 KFold로 생성, 각 교차 검증 때마다 생성되는 학습/검증 label data 값의 분포도를 확인
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
import numpy as np
kfold = KFold(n_splits=3)
n_iter = 0
for train_index, test_index in kfold.split(iris_df):
n_iter += 1
label_train = iris_df['label'].iloc[train_index]
label_test = iris_df['label'].iloc[test_index]
print('## 교차 검증 : {0}'.format(n_iter))
print()
print('학습 레이블 데이터 분포:\n', label_train.value_counts())
print()
print('검증 레이블 데이터 분포:\n', label_test.value_counts())
print()
print()
# 각 교차 검증마다 label이 편중되어, 학습하지 못하는 label이 생김.
# 교차 검증 1의 경우, label 1과 2는 학습했으나, 0은 학습하지 못했음. 근데 검증은 label 0으로 해야하므로 크게 성능저하 나타남.
KFold와 StrtifiedKFold 코드의 차이는?
- KFold는 for train_index, test_index in KFold.split(): 에서 매개변수에 feature data set만 들어가면 됨
- Stratified KFold는 레이블 데이터 분포도에 다라 학습/검증 데이터를 나누기 때문에 skf.split() 매개변수 자리에 feature data set + label data set도 포함!
In [17]:
# Stratified KFold로 학습/검증 레이블 데이터 분포를 살펴보면, 균일하게 분포된 것을 확인할 수 있음.
# 학습 레이블과 검증 레이블 데이터 값의 분포도가 동일하게 할당되었음.
from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=3)
n_iter = 0
for train_index, test_index in skf.split(iris_df, iris_df['label']):
n_iter +=1
label_train = iris_df['label'].iloc[train_index]
label_test = iris_df['label'].iloc[test_index]
print('## 교차 검증 : {0}'.format(n_iter))
print()
print('학습 레이블 데이터 분포:\n', label_train.value_counts())
print()
print('검증 레이블 데이터 분포:\n', label_test.value_counts())
print()
print()
In [18]:
# Stratified KFold를 통한 iris classification 학습 및 평가
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import StratifiedKFold
import numpy as np
from sklearn.datasets import load_iris
iris = load_iris()
features = iris.data
label = iris.target
dt_clf = DecisionTreeClassifier(random_state=156)
skfold = StratifiedKFold(n_splits = 3)
n_iter = 0
cv_accuracy=[]
# StratifiedKFold의 split() 호출 시 반드시 레이블 데이터 세트도 추가 입력 필요
for train_index, test_index in skfold.split(features, label):
# split()으로 반환된 인덱스를 이용해 학습/검증용 테스트 데이터 추출
X_train, X_test = features[train_index], features[test_index]
y_train, y_test = label[train_index], label[test_index]
# 학습 및 예측
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
# 반복 시마다 정확도 측정
n_iter += 1
accuracy = np.round(accuracy_score(y_test, pred), 4)
train_size = X_train.shape[0]
test_size = X_test.shape[0]
print('\n#{0} 교차 검증 정확도 : {1}, 학습 데이터 크기 : {2}, 검증 데이터 크기 : {3}'.format(n_iter, accuracy, train_size, test_size))
print('#{0} 검증 세트 인덱스 : {1}'.format(n_iter, test_index))
cv_accuracy.append(accuracy)
# 교차 검증별 정확도 및 평균 정확도
print('\n## 교차 검증별 정확도 : ', np.round(cv_accuracy, 4))
print('## 평균 검증 정확도 : ', np.mean(cv_accuracy))
2.3 편리한 교차 검증, cross_val_score()
- KFold의 방식
-
- 폴드 세트를 설정
- for loop에서 반복으로 학습/테스트 데이터의 인덱스 추출
- 반복적으로 학습/예측을 수행한 후 예측 성능 반환
- -> 이 과정을 한꺼번에 해주는 것이 cross_val_score()
-
- cross_val_score() API는 내부에서 Estimator를 학습(fit) / 예측(predict) / 평가(evaluation) 시켜주므로 간단히 교차 검증을 할 수 있음
- cross_val_score()는 cv로 지정된 횟수만큼 평가, scoring 파라미터로 지정된 평가 지표로 평과결괏값을 배열로 반환 -> 평균한 값을 평가 수치로 사용
- cross_val_score(estimator, X, y=None, scoring=None, cv=None, n_jobs=1, verbose=0, fit_params=None, pre_dispatch='2*n_jobs')
- 주요 파라미터 : estimator, X, y, scoring, cv
- estimator : 사이킷런의 분류 AL Class인 Classifier / Regression을 의미
- estimator에 classifier가 입력되면 -> Stratified KFold 방식으로 학습/테스트 세트 분할
- 회귀의 경우에는 Stratified KFold 지원 X -> KFold 방식으로 분할
- X : feature data set
- y : label data set
- scoring : 예측 성능 평가 지표
- cv : 교차 검증 폴드 수
- estimator : 사이킷런의 분류 AL Class인 Classifier / Regression을 의미
- 반환값 : scoring parameter로 지저오딘 성능 지표 측정값을 배열 형태로 반환
- 주요 파라미터 : estimator, X, y, scoring, cv
In [19]:
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score, cross_validate
from sklearn.datasets import load_iris
iris_data = load_iris()
dt_clf = DecisionTreeClassifier(random_state=156)
data = iris_data.data
label = iris_data.target
# 성능 지표는 정확도(accuracy), 교차 검증 세트는 3개
scores = cross_val_score(dt_clf, data, label, scoring='accuracy', cv=3)
print('교차 검증별 정확도 : ', np.round(scores,4))
print('평균 검증 정확도 : ', np.round(np.mean(scores), 4)) # 결과값이 위의 Stratified KFold 코드와 동일함
cross_validate() API
- cross_val_score는 1가지 종류의 평가 지표를 반환
- cross_validata는 여러 종류의 평가 지표를 반환
- 학습 데이터에 대한 성능 평가 지표와
- 수행시간도 함께 제공
- https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_validate.html
- sklearn.model_selection.cross_validate(estimator, X, y=None, , groups=None, scoring=None, cv=None, n_jobs=None, verbose=0, fit_params=None, pre_dispatch='2n_jobs', return_train_score=False, return_estimator=False, error_score=nan)
2.4 GridSearchCV - 교차 검증, 최적 하이퍼 파라미터 튜닝을 한번에 할 수 있는 API
- 하이퍼 파라미터 : ML AL을 구성하는 주요 구성 요소, 이 값을 조정해 AL의 예측 성능을 개선할 수 있음.
- sklearn은 GridSearchCV API를 이용해 Classifier나 Regressor와 같은 알고리즘에 사용되는 하이퍼 파라미터를 순차적으로 입력 -> 편리하게 최적 파라미터를 도출할 수 있음
- Grid(격차) : 촘촘하게 파라미터를 입력하면서 테스트 하는 방식
- ex) 결정 트리 AL의 여러 하이퍼 파라미터를 순차적으로 변경하면서 최고 성능을 가지는 파라미터 조합을 찾는 예
- 파라미터의 집합을 만든다
- 순차적으로 적용하면서 최적화를 수행한다
- 교차 검증을 기반으로 하이퍼 파라미터의 최적값을 찾게 해준다.
- GridSearchCV Class의 생성자로 들어가는 주요 파라미터
- estimator : classifier, regressor, pipeline
- param_grid : key + 리스트 값을 가지는 딕셔너리가 주어짐. estimator의 튜닝을 위해 파라미터명과 사용될 여러 파라미터 값을 지정함. key가 파라미터 이름 문자열, value는 list 형태로 된 파라미터의 범위
- scoring : 예측 성능을 측정할 평가 방법을 지정함. 보통은 sklearn의 성능 평가 지표를 지정하는 문자열(예:정확도의 경우 'accuracy')로 지정하나, 별도의 성능 지표 함수도 지정가능
- cv : 교차 검증을 위해 분할되는 학습/테스트 세트의 개수를 지정
- refit : 디폴트가 True, True로 생성 시 가장 최적의 하이퍼 파라미터를 찾은 뒤 입력된 estimator 객체를 해당 하이퍼 파라미터로 재학습시킴
- process
- data 로딩
- train_test_split으로 train/test data 분리
- parameter dictionary 생성
- 학습 데이터 세트를 GridSearchCV 객체의 fit() 메서드에 인자로 입력
- fit 수행 -> 학습 데이터를 CV에 기술된 폴딩 세트로 분할함 -> parameter dictionary에 기술된 하이퍼 파라미터를 순차적으로 변경하면서 학습/평가 수행 -> cv_results에 기록
In [20]:
# param_grid의 예시.
# 아래와 같이 parameter의 집합을 만들고 이를 순차적으로 적용하면서 최적화를 수행
grid_parameters = {'max_depth' : [1,2,3],
'min_samples_split' : [2,3]
}
In [21]:
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
# 데이터를 로딩하고 train/test data 분리
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target,
test_size=0.2, random_state=121)
dtree = DecisionTreeClassifier()
### parameter 들을 dictionary 형태로 설정
parameters = {'max_depth':[1,2,3], 'min_samples_split':[2,3]} # 주의 : 1개만 넣더라도 list 형태로, 즉 [1] 이렇게 입력해야 함~!
In [22]:
import pandas as pd
# param_grid의 하이퍼 파라미터들을 3개의 train, test set fold 로 나누어서 테스트 수행 설정.
### refit=True 가 default 임. True이면 가장 좋은 파라미터 설정으로 재 학습 시킴.
grid_dtree = GridSearchCV(dtree, param_grid=parameters, cv=3, refit=True)
# 붓꽃 Train 데이터로 param_grid의 하이퍼 파라미터들을 순차적으로 학습/평가 .
grid_dtree.fit(X_train, y_train)
# GridSearchCV 결과 추출하여 DataFrame으로 변환
scores_df = pd.DataFrame(grid_dtree.cv_results_)
scores_df[['params', 'mean_test_score', 'rank_test_score', \
'split0_test_score', 'split1_test_score', 'split2_test_score']]
Out[22]:
- cv_results_는 gridsearchcv의 결과 세트로서 dictionary 형태로 key값과 리스트 형태의 value값을 가짐
- cv_results_를 pandas의 dataframe으로 변환 시 해석이 용이함
- 칼럼별 의미
- params : 수행할 때마다 적용된 개별 하이퍼 파라미터 값
- mean_test_score : 개별 하이퍼 파라미터별로 CV 폴딩 테스트 세트에 대해 총 수행한 평가 평균값
- rank_test_score : 하이퍼 파라미터별로 성능이 좋은 score순위를 나타냄. 1이 가장 뛰어난 순위, 이때의 파라미터가 최적의 파라미터
- split0_test_score, split1_test_score, split2_test_score : CV가 3인 경우, 즉 3개의 폴딩 세트에서 각각 테스트한 성능 수치
-> mean_test_score가 이 3개 성능 수치를 평균한 것
- GridSearchCV 객체의 fit()을 수행
-> 최고 성능을 나타낸 하이퍼 파라미터 값은 bestparams 속성에 기록
-> 그때의 평가 결과값은 bestscore속성에 기록됨
-> (즉, cv_results_의 rank_test_score가 1일 때의 값임)
-> refit은 default값이 True이므로, 최적의 하이퍼 파라미터로 알아서 학습된 모델은 bestestimator에 기록됨.
In [23]:
# best_params_와 best_score_ 속성을 이용하면 최적 하이퍼 파라미터의 값과 그때의 정확도를 알 수 있음
print('GridSearchCV 최적 파라미터:', grid_dtree.best_params_)
print('GridSearchCV 최고 정확도: {0:.4f}'.format(grid_dtree.best_score_))
# max_depth가 3, min_samples_split이 2일 때, 검증용 폴드 세트에서 평균 최고 정확도가 97.50 %로 기록됨.
In [24]:
# GridSearchCV 객체의 생성 파라미터로 refit = True가 default임
# refit = True면 GridSearchCV가 최적 성능을 나타내는 하이퍼 파라미터로 알아서 Estimator를 학습해 best_estimator_로 저장함
# 이미 학습된 best_estimator_를 이용해 앞에서 train_test_split()로 분리한 test data set에 대해 예측하고 성능을 평가해볼 수 있음
# GridSearchCV의 refit으로 이미 학습이 된 estimator 반환
estimator = grid_dtree.best_estimator_
# GridSearchCV의 best_estimator_는 이미 최적 하이퍼 파라미터로 학습이 됨
pred = estimator.predict(X_test)
print('테스트 데이터 세트 정확도: {0:.4f}'.format(accuracy_score(y_test,pred)))
- 일반적으로 train data를 GridSearchCV를 이용해 최적 하이퍼 파라미터 튜닝을 수행한 뒤,
- 별도의 test data set에서 이를 평가하는 것이 일반적인 머신러닝 모델 적용 방법임.
'AI Study > Machine Learning' 카테고리의 다른 글
데이터 전처리 기본 (2) - feature scailing (0) | 2021.11.21 |
---|---|
데이터 전처리 기본 (1) - data encoding (0) | 2021.11.21 |
sklearn 기반 프레임워크 살펴보기 (0) | 2021.09.06 |
머신러닝을 위한 Pandas 기초 (0) | 2021.08.24 |
머신러닝을 위한 Numpy 기초 (0) | 2021.08.23 |