[머신러닝] 분류(Classification)

kkiyou·2021년 7월 24일
0

Machine Laerning

목록 보기
3/10

Iris 품종 예측

1. Iris datasets를 Load하고 확인한다

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 구성요소

  • data
    각 데이터의 feature값이 저장되어 있다.
  • target
    Label로 각 데이터의 결정값이 숫자로 저장되어 있다.
  • target_names
    목표값을 정의한 배열로, 배열의 index가 target에 숫자로 저장되어 있다.
    * target과 target_names으로 구분한 것은 scikit learn에서 머신러닝을 학습할 때 숫자 데이터만 사용할 수 있기 때문이다.
  • feature_names
    데이터를 구성하는 속성 값의 구분으로 columns name이 저장되어 있다.
  • DESCR
    describe, datasets에 대한 설명이 저장되어 있다.

# 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
05.13.51.40.20
14.93.01.40.20
# 데이터 정보 확인
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개 씩 있음



2. datasets를 분리한다

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%



3. 의사 결정 트리 알고리즘을 활용해 머신러닝 모델을 학습한다

# 머신러닝 모델을 학습할 의사 결정 트리 클래스를 생성한다.
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)



4. 학습한 머신러닝 모델의 성능을 측정한다.

# 실제 test label과 예측된 label 값을 비교하여 머신러닝 모델의 성능을 평가한다.
# 1. 전체 중 맞춘 개수를 나타내는 accuracy(정확도) 평가 방법을 사용한다.
print("예측 정확도: {0:.2f}%".format(accuracy_score(test_l, pred) * 100))
  • stratify=None일 때
    예측 정확도: 100.00%

  • stratify=target일 때
    예측 정확도: 93.33%



5. 교차검증을 통해 머신러닝 모델의 성능을 향상시킨다.

5.1. KFold

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%

5.2. Stratified KFold

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%

5.3. cross_val_score

함수 학습(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))

### 6. Hyper Parameter 하이퍼 파라미터 값을 적용하여 머신러닝 모델을 학습하고, 모델의 정확도를 도출한다.
# 머신러닝 모델을 학습할 의사 결정 트리 클래스를 생성한다.
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%

0개의 댓글