본문 바로가기
머신러닝

[머신러닝][파이썬] 의사결정나무(DecisionTreeClassifier)

by 방구석 데이터사이언티스트 2022. 1. 17.
728x90
반응형

안녕하세요. 오늘은 파이썬을 통해 DecisionTreeClassifier를 구현해 보도록하겠습니다.

데이터는 2017-2018년 쏘카의 고객정보와이용내역을 병합한 데이터입니다.

 

1. 데이터 불러오기

1
2
3
import pandas as pd
pd.options.display.max_columns = 100
data = pd.read_csv("crm.csv")
cs

가정 먼저 pandas를 활용하여 csv파일을 불러왔습니다. 

2번째 줄의 pd.options.display.max_columns는 데이터프레임의 변수를 더 많이 보고 싶을 때 사용하는 옵션입니다. 

 

예를 들어 아래와 같이 변수가 많은 데이터프레임을 보면 중간에 ...으로 표기되어 중간에 있는 변수는 살펴볼 수 없습니다.

pd.options.display.max_columns 변경전

 

하지만 pd.options.display.max_columns= 100 , 즉 나는 최대 100개의 변수까지는 볼 수 있도록 허용하겠다고 지정하게 되면 아래와 같이 중간에 있는 모든 변수들을 상세히 살펴볼 수 있습니다.

pd.options.display.max_columns 변경후

 

2. 레이블 인코딩, Train/Test 분리, 불균형 처리

1
2
3
4
5
6
7
8
9
10
#레이블 인코딩
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
encoder.fit(data.주소)
data['주소'= encoder.transform(data.주소)
encoder_1 = LabelEncoder()
encoder_1.fit(data.등급)
data['등급'= encoder_1.transform(data.등급)
 
data['성별'= data['성별'].apply(lambda x : 1 if x =='남자'  else 0)
cs

 

다음으로는 모델에 데이터를 학습시키전 레이블 인코딩 작업을 했습니다.

모델은 문자에 대해서는 입력을 받지 않기 때문에 데이터의 문자형 입력값들을 숫자형을 바꿔주는 작업이 필요합니다.

저는 변수의 class가 많으면 LabelEncoder를 사용하고, 성별같이 0과1로 만들어야 하는 변수는 apply_lambda를 사용하여 인코딩을 했습니다.   

1
2
3
4
5
6
X_features = data.iloc[:, 1:-1]
y_label = data.iloc[:, -1]
 
from sklearn.model_selection import train_test_split
train_features, test_features , train_target, test_target = train_test_split(
    X_features, y_label, test_size = 0.2, random_state = 2021, stratify=y_label)
cs

 

의사결정나무는 지도학습 알고리즘이므로 독립변수와 종속변수를 나눈 후 Train/Test 데이터를 분리해야합니다. 

sklearn의 train_test-split을 활용하면 쉽게 X_train/y_train, X_test/y_test로 분리할 수 있습니다. 

test_size : test data 비율

random_state = 난수값, 재현성을 위한 작업

stratify = 데이터를 분리할 때 target의 class가 모두 포함되도록 하기 위함 

1
2
3
4
#불균형 처리
from imblearn.over_sampling import SMOTENC
smote_nc = SMOTENC(categorical_features=[0134], random_state=0)
X_resampled, y_resampled = smote_nc.fit_resample(train_features, train_target)
cs
 
만약 타겟의 분포가 불균형이라면 이를 해결해주는 것이 중요합니다. 
예를 들어) 0 : 1000개 , 1 : 10개 같은 데이터 있다면 이를 그대로 모델에 학습시킨다면 0에 대해서만 과적합된 모델이 생성될 수 있습니다. 
불균형을 처리하는 방법론들은 다양하지만 저는 범주형 변수를 고려해 불균형을 해결하는 SMOTENC 기법을 사용했습니다.
categorical_features = 변수들의 인덱스값(데이터프레임의 몇번째 변수가 categorical_feature인지 지정하는 것입니다.) 단 파이썬은 숫자를 셀 때 0부터시작하기에 주의!!
또한 불균형을 처리할 때 Test data에는 적용하지 않습니다. 그 이유는 이 모델이 일반화된 모델이 되기 위해서는 새로운 정보에 대해서도 잘 분리할 수 있어야합니다. 즉 새로운 정보는 불균형이기에 모델을 Test할 때도 unbalance data가 적용되야 되는 것입니다. 
 

3.  의사결정나무 파라미터 최적화(GridSearchCV)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import make_pipeline
pipe_tree = make_pipeline(DecisionTreeClassifier(random_state=2021))
from sklearn.model_selection import GridSearchCV
 
param_range1 = [1,2,3,4,5,6,7,8,9,10]
param_range2 = [1020304050]
param_range3 = ['gini''entropy']
 
param_grid = [{'decisiontreeclassifier__max_depth': param_range1,
              'decisiontreeclassifier__min_samples_leaf': param_range2,
              'decisiontreeclassifier__criterion': param_range3}]
 
gs = GridSearchCV(estimator = pipe_tree,
                 param_grid = param_grid, # 찾고자하는 파라미터. dictionary 형식
                 scoring = 'accuracy'#  Classification일때  'accuracy','f1', Regression 일때 'neg_mean_squared_error','r2' 등
                 cv=10,
                 n_jobs= -1# 병렬 처리갯수? -1은 전부를 의미
 
gs = gs.fit(X_resampled, y_resampled)
 
print(gs.best_score_)
print(gs.best_params_)
cs
 

머신러닝 모델들은 데이터에 맞게 파라미터를 조절하여 모델을 최적화 합니다. 

DecisionTreeClassifier에도 다양한 파라미터들이 있습니다. 

※ 자세한 사항 : sklearn.tree.DecisionTreeClassifier — scikit-learn 1.0.2 documentation

 

sklearn.tree.DecisionTreeClassifier

Examples using sklearn.tree.DecisionTreeClassifier: Classifier comparison Classifier comparison, Plot the decision surface of decision trees trained on the iris dataset Plot the decision surface of...

scikit-learn.org

저는 max_depth(나무의 깊이),  min_samples_leaf(리프노드에 필요한 최소 샘플 수), criterion(분리기준) 세 가지 파라미터를 GridSearchCV를 통해 최적화했습니다. 

GridSearchCV란? 지정한 파라미터의 범위내에서 모든 경우의 수를 고려해 최적의 파라미터를 뽑아내는 방법

※ 자세한 사항 : scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html

 

sklearn.model_selection.GridSearchCV

Examples using sklearn.model_selection.GridSearchCV: Release Highlights for scikit-learn 0.24 Release Highlights for scikit-learn 0.24, Feature agglomeration vs. univariate selection Feature agglom...

scikit-learn.org

마지막 두줄은 최적화된 모델의 성능과 파라미터를 뽑아내는 코드입니다. 성능과 파라미터는 17번째 줄을 보시면 cv= 10, 즉 10번의 split을 통해 교차검증을 거친 결과입니다. 

4. 모델 학습 및 test predict

1
2
3
4
5
6
7
# 최적의 모델 선택
 
best_tree = gs.best_estimator_ # 최적의 파라미터로 모델 생성
best_tree.fit(X_resampled, y_resampled)
 
y_pred = best_tree.predict(test_features)
 
cs

 

GridSeachCV를 통해 최적의 파라미터를 찾았다면 최적의 파라미터로 모델을 생성합니다.

그리고 Train data를 학습시키고 X_test로 y_predict 값을 추출합니다.

 

5. 모델 성능 평가

1
2
3
4
5
6
7
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
 
print('정확도 accuracy: %.3f' % accuracy_score(test_target, y_pred))
print('정밀도 precision: %.3f' % precision_score(y_true= test_target, y_pred=y_pred))
print('재현율 recall: %.3f' % recall_score(y_true=test_target, y_pred=y_pred))
print('F1-score: %.3f' % f1_score(y_true=test_target, y_pred=y_pred))
print('AUC: %.3f' % roc_auc_score(test_target, y_pred))
cs

 

모델의 성능 평가는 분류성능지표를 활용합니다.

이진분류성능지표는 confusion matrix을 바탕으로 따라갑니다. 

confusion matrix :  Training을 통한 Prediction 성능을 측정하기 위해 예측 값와 실제 값을 비교하는 지표  

accuracy : 정확도 = (TP + TN) / (TP + FN + FP + TN)

precision : 정밀도 = TP / TP + FP  즉, 내가 1로 예측한 값 중 실제 1의 값 

recall : 재현도 = TP / TP + FN 즉, 실제 1의 값 중 내가 맞춘 1의 값

F-1 score : 정밀도와 재현도의 조화평균 = 2*Precision*Recall / Precision + Recall

AUC  : ROC 곡선 아래 영역(넓을수록 좋음)

ROC 곡선 : 모든 임계값에서 분류 모델의 성능을 보여주는 그래프 -> (Roc 곡선을 보고 cut-off value 지정)

6. 변수중요도 및 모델 규칙 시각화

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
best_tree_for_graph = DecisionTreeClassifier(criterion='gini', max_depth=10, min_samples_leaf=10, random_state = 2021)
 
best_tree=best_tree_for_graph.fit(X_resampled, y_resampled)
 
# Feature Importance
import matplotlib.pyplot as plt
import seaborn as sns
 
import matplotlib as mpl
import matplotlib.font_manager as fm
import os
if os.name == 'posix'# Mac 환경 폰트 설정
    plt.rc('font', family='AppleGothic')
elif os.name == 'nt'# Windows 환경 폰트 설정
    plt.rc('font', family='Malgun Gothic')
 
plt.rc('axes', unicode_minus=False# 마이너스 폰트 설정
 
 
feature_importance_values = best_tree.feature_importances_
# Top 중요도로 정렬하고, 쉽게 시각화하기 위해 Series 변환
feature_importances = pd.Series(feature_importance_values, index=X_resampled.columns)
# 중요도값 순으로 Series를 정렬
feature_top10 = feature_importances.sort_values(ascending=False)[:10]
 
plt.figure(figsize=[86])
plt.title('Feature Importances Top 10')
sns.barplot(x=feature_top10, y=feature_top10.index)
plt.show()
cs

 

마지막으로 변수중요도를 시각화하는 코드입니다. 의사결정나무의 경우 정보이득을 극대화하는 방향으로 트리를 구성하기 때문에 변수중요도에서 높게 나온 변수는 가장 많이 순수한 집단을 분류해주는 변수라고 해석할 수 있습니다. 

※ 참고로 1, 3번 줄은 DecisionTreeClassifier 모델을  GridSeachCV로 찾은 파라미터로 모델을 생성하고 학습시키는 코드입니다. 위에서는 모델을 PiPline으로 만들어서 변수중요도 시각화에 적용할 수 없기 때문에 모델을 다시 지정하고 학습한 것입니다. 결국 똑같은 모델을 다른 방법으로 다시 만들었다고 생각해주시면 되겠습니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pydotplus import graph_from_dot_data
from sklearn.tree import export_graphviz
from IPython.display import Image # CART Tree 그림
import pydotplus
import os
 
os.environ['PATH'+= os.pathsep + 'C:/Program Files/Graphviz/bin' # 경로지정
 
import numpy as np
feature_names = X_resampled.columns.tolist()
target_name = np.array(['1''0'])
 
dot_data = export_graphviz(best_tree,
                          filled = True,
                          rounded = True,
                          class_names = target_name,
                          feature_names = feature_names,
                          out_file = None)
 
graph = graph_from_dot_data(dot_data)
graph.write_png('tree.png'#Tree 이미지를 저장
 
dt_graph = pydotplus.graph_from_dot_data(dot_data)
Image(dt_graph.create_png())
cs

 

진짜 마지막으로 트리규칙을 시각화로 도출하는 코드입니다. 의사결정나무의 가장 큰 장점은 해석의 용이성이죠. 

따라서 트리의 규칙을 시각화하고 규칙을 해석하면 유용하게 사용하실 수 있습니다. 

728x90
반응형

댓글