from sklearn.datasets import load_iris # Iris datasets
from sklearn.model_selection import train_test_split # 데이터 분리
from sklearn.tree import DecisionTreeClassifier # 머신러닝 알고리즘
from sklearn.metrics import accuracy_score # 모델 평가
from sklearn.model_selection import KFold, StratifiedKFold, cross_val_score # 교차 검증
from sklearn.model_selection import GridSearchCV # Hyper Parameter 찾기
from sklearn.preprocessing import LabelEncoder, OneHotEncoder # 데이터 전처리(Encoding)
import pandas as pd
import numpy as np
Iris datasets 개요
3가지 품종의 Iris(붓꽃) 총 150개의 크기를 측정한 자료이다.
Iris 구성요소
* target과 target_names으로 구분한 것은 scikit learn에서 머신러닝을 학습할 때 숫자 데이터만 사용할 수 있기 때문이다.
# Iris datasets를 Load한다. iris = load_iris()
# Iris 데이터는 sklearn.utils.Bunch 형태로 저장되어 있다. sklearn.utils.Bunch class는 dictionary 형태로 데이터가 저장되어 있기 때문에 key 값으로 데이터를 확인할 수 있다. print(tpye(iris)) ## <class 'sklearn.utils.Bunch'> print(iris.keys()) ## dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])
# data type print(type(iris.data)) ## <class 'numpy.ndarray'>
# feature 값 print("feature 값:", iris.feature_names) ## feature 값: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
# target 값 print("target 값:", iris.target_names) ## target 값: ['setosa' 'versicolor' 'virginica']
# 데이터를 시각적으로 이해하기 용이하고, 결측치와 이상치 등을 파악하기 쉬운 2차원 표로 변환한다. iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names) iris_df["label"] = iris.target # 데이터 형태 확인 print(iris_df.head(2))
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) label 0 5.1 3.5 1.4 0.2 0 1 4.9 3.0 1.4 0.2 0
# 데이터 정보 확인 print(iris_df.info()) # 결측치 확인 print(iris_df.isnull().sum()) # 결측치 없음 # label 각 데이터 개수 확인 print(pd.DataFrame(iris.target).value_counts()) ## 0 50 ## 1 50 ## 2 50 ## dtype: int64 # ['setosa' 'versicolor' 'virginica']가 각각 50개 씩 있음
sklearn.model_selection.train_test_split
를 활용해 iris datasets을 머신러닝 training(학습)에 사용할 데이터와 학습한 모델로 test(검증)할 데이터로 나눈다.
<span
결과값으로 training feature datasets, test feature datasets, training label, test label로 나뉜 n() = 4인 List를 반환한다.
공식문서는 [X_train, X_test, y_train, y_test]
로 나누나,
본 블로그에서는 [train_f, test_f, train_l, test_l]
로 나눈다.
print(type(train_test_split(iris.data, iris.target, test_size=0.2, random_state=9))) ## <class 'list'> train_f, test_f, train_l, test_l = train_test_split(iris.data, iris.target, test_size=0.2, random_state=9) print("training label 빈도:\n", pd.DataFrame(train_l).value_counts(), sep="", end="\n\n") ## training label 빈도: ## 2 42 ## 1 41 ## 0 37 ## dtype: int64 print("test label 빈도:\n", pd.DataFrame(test_l).value_counts(), sep="") ## test label 빈도: ## 0 13 ## 1 9 ## 2 8 ## dtype: int64
stratify로 분리했을 때와의 차이
stratify를 적용하고 분류했을 때 raw datasets의 label과 동일한 비율로 training datasets와 test datasets가 나뉜 것을 알 수 있다.
# stratify=None일 때, training datasets와 test datasets로 나뉜 비율 for i in range(3): res = pd.DataFrame(train_l).value_counts()[i] / pd.DataFrame(train_l).value_counts().sum() print("training label에서 {0}의 비율: {1:.2f}%".format(i, res*100)) print() for i in range(3): res = pd.DataFrame(test_l).value_counts()[i] / pd.DataFrame(test_l).value_counts().sum() print("test label에서 {0}의 비율: {1:.2f}%".format(i, res*100))
training label에서 0의 비율: 30.83% training label에서 1의 비율: 34.17% training label에서 2의 비율: 35.00% test label에서 0의 비율: 43.33% test label에서 1의 비율: 30.00% test label에서 2의 비율: 26.67%
# stratify=target일 때, training datasets와 test datasets로 나뉜 비율 train_d, test_d, train_l, test_l = train_test_split(iris.data, iris.target, test_size=0.2, random_state=9, stratify=iris.target) for i in range(3): res = pd.DataFrame(train_l).value_counts()[i] / pd.DataFrame(train_l).value_counts().sum() print("training label에서 {0}의 비율: {1:.2f}%".format(i, res*100)) print() for i in range(3): res = pd.DataFrame(test_l).value_counts()[i] / pd.DataFrame(test_l).value_counts().sum() print("test label에서 {0}의 비율: {1:.2f}%".format(i, res*100))
training label에서 0의 비율: 33.33% training label에서 1의 비율: 33.33% training label에서 2의 비율: 33.33% test label에서 0의 비율: 33.33% test label에서 1의 비율: 33.33% test label에서 2의 비율: 33.33%
# 머신러닝 모델을 학습할 의사 결정 트리 클래스를 생성한다. dtree_c = DecisionTreeClassifier(random_state=9) # training feature datasets와 training label를 의사 결정 트리 알고리즘을 활용해 학습한다. dtree_c.fit(train_f, train_l) # 학습된 머신러닝 모델을 활용해 test feature datasets label을 예측한다. 예측한 label array를 반환한다. pred = dtree_c.predict(test_f)
# 실제 test label과 예측된 label 값을 비교하여 머신러닝 모델의 성능을 평가한다. # 1. 전체 중 맞춘 개수를 나타내는 accuracy(정확도) 평가 방법을 사용한다. print("예측 정확도: {0:.2f}%".format(accuracy_score(test_l, pred) * 100))
- stratify=None일 때
예측 정확도: 100.00%
- stratify=target일 때
예측 정확도: 93.33%
KFold를 활용한 교차검증을 통해 머신러닝 모델의 성능을 향상시킨다.
# 머신러닝 모델을 학습할 의사 결정 트리 클래스를 생성한다. dtree_c = DecisionTreeClassifier(random_state=9) # KFold를 사용해 datasets를 K개로 분리한 KFold 객체를 생성한다. kfold = KFold(n_splits=5) # 나눈 Fold datasets를 바탕으로 머신러닝 모델을 학습한 후 모델의 정확도를 평가한 값을 저장할 리스트 객체를 생성한다. cv_accuracy = []
KFold를 통해 datasets를 분리하면 valid dataset가 index(0)부터 차례대로 나뉨을 알 수 있다.
# kfold를 K(n_splits)개의 train sets와 valid set로 분리한다. 반환 값은 datasets의 index이다. for i, [train_index, valid_index] in enumerate(kfold.split(iris.data)): print(f"fold_{i}_valid index sets: {valid_index}")
fold_0_valid index sets: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29] fold_1_valid index sets: [30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59] fold_2_valid index sets: [60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89] fold_3_valid index sets: [ 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119] fold_4_valid index sets: [120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149]
KFold 교차검증 학습을 수행한 머신러닝 모델의 정확도를 산출한다.
for i, [train_index, valid_index] in enumerate(kfold.split(iris.data)): train_f = iris.data[train_index] train_l = iris.target[train_index] valid_f = iris.data[valid_index] valid_l = iris.target[valid_index] # 머신러닝 모델 학습 dtree_c.fit(train_f, train_l) # 모델 적용 pred = dtree_c.predict(valid_f) # 모델 정확도 평가 accuracy = accuracy_score(valid_l, pred) print("{0} fold 정확도: {1:6.2f}%".format(i, accuracy * 100)) print("-" * 20) # 정확도 값 저장 cv_accuracy.append(accuracy) # stratify=None일 때, training datasets와 valid datasets로 나뉜 비율 for j in range(3): res = pd.DataFrame(train_l).value_counts()[j] / pd.DataFrame(train_l).value_counts().sum() print("training label에서 {0}의 비율: {1:.2f}%".format(j, res * 100)) print() for j in pd.DataFrame(valid_l).value_counts().keys(): res = pd.DataFrame(valid_l).value_counts()[j[0]] / pd.DataFrame(valid_l).value_counts().sum() print("valid label에서 {0}의 비율: {1:.2f}%".format(j[0], res * 100)) print("=" * 20) # 평균 정확도 print("평균 교차검증 정확도: {0:.2f}%".format(np.mean(cv_accuracy) * 100))
0 fold 정확도: 100.00% -------------------- training label에서 0의 비율: 16.67% training label에서 1의 비율: 41.67% training label에서 2의 비율: 41.67% valid label에서 0의 비율: 100.00% ==================== 1 fold 정확도: 100.00% -------------------- training label에서 0의 비율: 25.00% training label에서 1의 비율: 33.33% training label에서 2의 비율: 41.67% valid label에서 0의 비율: 66.67% valid label에서 1의 비율: 33.33% ==================== 2 fold 정확도: 86.67% -------------------- training label에서 0의 비율: 41.67% training label에서 1의 비율: 16.67% training label에서 2의 비율: 41.67% valid label에서 1의 비율: 100.00% ==================== 3 fold 정확도: 93.33% -------------------- training label에서 0의 비율: 41.67% training label에서 1의 비율: 33.33% training label에서 2의 비율: 25.00% valid label에서 2의 비율: 66.67% valid label에서 1의 비율: 33.33% ==================== 4 fold 정확도: 83.33% -------------------- training label에서 0의 비율: 41.67% training label에서 1의 비율: 41.67% training label에서 2의 비율: 16.67% valid label에서 2의 비율: 100.00% ==================== 평균 교차검증 정확도: 92.67%
KFold로 fold를 나누면 데이터의 분포가 일정하지 않음을 알 수 있다. 머신러닝 모델 학습 시 원본 데이터의 데이터 분포를 제대로 반영하지 못하기 때문에 신뢰성있는 결과를 얻을 수 없으며, 최악의 경우 하나의 데이터 집단이 완전히 빠져있는 경우가 있을 수 있다. 예컨대 1,000개의 datasets에서 label값이 n(0) = 995, n(1) = 5이고 K가 5이면 어느 한 쪽에만 1인 데이터가 몰려있을 수 있다. 따라서 StratifiedKFold를 사용해 데이터의 분포를 일정하게 맞추는 것이다.
dtree_c = DecisionTreeClassifier(random_state=9) # StratifiedKFold를 사용해 datasets를 K개로 분리한 s_KFold 객체를 생성한다. s_kfold = StratifiedKFold(n_splits=5) # 나눈 Fold datasets를 바탕으로 머신러닝 모델을 학습한 후 모델의 정확도를 평가한 값을 저장할 리스트 객체를 생성한다. cv_accuracy = [] for i, [train_index, valid_index] in enumerate(s_kfold.split(iris.data, iris.target)): train_f = iris.data[train_index] train_l = iris.target[train_index] valid_f = iris.data[valid_index] valid_l = iris.target[valid_index] # 머신러닝 모델 학습 dtree_c.fit(train_f, train_l) # 모델 적용 pred = dtree_c.predict(valid_f) # 모델 정확도 평가 accuracy = accuracy_score(valid_l, pred) print("{0} fold 정확도: {1:6.2f}%".format(i, accuracy * 100)) print("-" * 20) # 정확도 값 저장 cv_accuracy.append(accuracy) # stratify=None일 때, training datasets와 valid datasets로 나뉜 비율 for j in range(3): res = pd.DataFrame(train_l).value_counts()[j] / pd.DataFrame(train_l).value_counts().sum() print("training label에서 {0}의 비율: {1:.2f}%".format(j, res * 100)) print() for j in pd.DataFrame(valid_l).value_counts().keys(): res = pd.DataFrame(valid_l).value_counts()[j[0]] / pd.DataFrame(valid_l).value_counts().sum() print("valid label에서 {0}의 비율: {1:.2f}%".format(j[0], res * 100)) print("=" * 20) # 평균 정확도 print("평균 교차검증 정확도: {0:.2f}%".format(np.mean(cv_accuracy) * 100))
0 fold 정확도: 96.67% training label에서 0의 비율: 33.33% training label에서 1의 비율: 33.33% training label에서 2의 비율: 33.33% valid label에서 0의 비율: 33.33% valid label에서 1의 비율: 33.33% valid label에서 2의 비율: 33.33% ==================== 1 fold 정확도: 96.67% training label에서 0의 비율: 33.33% training label에서 1의 비율: 33.33% training label에서 2의 비율: 33.33% valid label에서 0의 비율: 33.33% valid label에서 1의 비율: 33.33% valid label에서 2의 비율: 33.33% ==================== 2 fold 정확도: 90.00% training label에서 0의 비율: 33.33% training label에서 1의 비율: 33.33% training label에서 2의 비율: 33.33% valid label에서 0의 비율: 33.33% valid label에서 1의 비율: 33.33% valid label에서 2의 비율: 33.33% ==================== 3 fold 정확도: 96.67% training label에서 0의 비율: 33.33% training label에서 1의 비율: 33.33% training label에서 2의 비율: 33.33% valid label에서 0의 비율: 33.33% valid label에서 1의 비율: 33.33% valid label에서 2의 비율: 33.33% ==================== 4 fold 정확도: 100.00% training label에서 0의 비율: 33.33% training label에서 1의 비율: 33.33% training label에서 2의 비율: 33.33% valid label에서 0의 비율: 33.33% valid label에서 1의 비율: 33.33% valid label에서 2의 비율: 33.33% ==================== 평균 교차검증 정확도: 96.00%
함수 학습(fit), 예측(predict), 평가(evaluation)한 결과를 반환하기 때문에 코드가 간결하다.
dtree_c = DecisionTreeClassifier(random_state=9) # cross_val_score에서 반환되는 성능 평가 결과 cv_score = cross_val_score(dtree_c, iris.data, iris.target, scoring="accuracy", cv=5) # 각 fold별 정확도 for i, accuracy in enumerate(cv_score): print("{0} fold 정확도: {1:6.2f}%".format(i, accuracy * 100)) # 평균 정확도 print("=" * 20) print("평균 교차검증 정확도: {0:.2f}%".format(np.mean(cv_score) * 100))
# 머신러닝 모델을 학습할 의사 결정 트리 클래스를 생성한다. dtree_c = DecisionTreeClassifier(random_state=9) # parameter 값 설정 parameter = {"max_depth": [1, 2, 3], "min_samples_split": [2, 3]} # Hyper parameter 값 도출 후, # 의사 결정 트리 알고리즘에 Hyper parameter 값을 적용시킨다.(refit=True) gscv = GridSearchCV(dtree_c, param_grid=parameter, cv=5, scoring="accuracy", refit=True) # datasets을 머신러닝 training(학습)에 사용할 데이터와 학습한 모델로 test(검증)할 데이터로 나눈다. train_f, test_f, train_l, test_l = train_test_split(iris.data, iris.target, test_size=0.2, random_state=9) # Hyper parameter 값을 적용한 머신러닝 모델을 학습시킨다. gscv.fit(train_f, train_l) # GridSearchCV의 결과 값 중 colums(keys)를 출력한다. gscv.cv_results_.keys() ## dict_keys(['mean_fit_time', ## 'std_fit_time', ## 'mean_score_time', ## 'std_score_time', ## 'param_max_depth', # 세부 파라미너 값 ## 'param_min_samples_split', # 세부 파라미너 값 ## 'params', # 적용한 파라미터 값 ## 'split0_test_score', # Fold 0 ## 'split1_test_score', # Fold 1 ## 'split2_test_score', # Fold 2 ## 'split3_test_score', # Fold 3 ## 'split4_test_score', # Fold 4 ## 'mean_test_score', # Fold의 평균 ## 'std_test_score', # Fold의 분산 ## 'rank_test_score' # mean_test_score 순위 ## ]) # 최적의 하이퍼 파라미터 값 print(gscv.best_params_) ## {'max_depth': 3, 'min_samples_split': 2} # 하이퍼 파라미터 값을 적용한 정확도 print("{0:.2f}%".format(gscv.best_score_ * 100)) ## 95.00% # 최적의 하이퍼 파라미터 값을 적용한 의사 결정 트리(Estimator) 알고리즘 print(gscv.best_estimator_) ## DecisionTreeClassifier(max_depth=3, random_state=9) # 최적의 하이퍼 파라미터 값을 적용하여 학습한 머신러닝 모델을 가지고 결과값 예측 pred = gscv.best_estimator_.predict(test_f) # 정확도 평가 print("예측 정확도: {0:.2f}%".format(accuracy_score(test_l, pred) * 100)) ## 예측 정확도: 100.00%