Data Analysis & ML/Machine Learning

[Machine Learning][머신러닝] Decision Tree(결정트리)와 RandomForest(랜덤포레스트)

YSY^ 2020. 9. 2. 16:18

이번 포스팅에서는 머신러닝 모델 중에서 가장 많이 쓰이는 모델 중 하나인 랜덤포레스트와 그 기반이 되는 결정트리에 대해 알아봅니다.

Decision Tree(결정트리)

Decision Tree 원리 및 용어

  • 데이터를 잘 분류할 수 있는 질문을 던져 가며 대상을 좁혀가는 '스무고개'와 비슷한 형식의 알고리즘.
  • 분기해 나가는 구조가 Tree 구조와 같기 때문에 Decision Tree 라고 한다.
    • 불순도 최대한 감소하는 방향으로 조건을 만들어 학습을 진행한다.
    • 하위노드는 yes/no 두 개로 분기 된다.
  • 머신러닝 모델들 중 몇안되는 White box 모델로 결과에 대한 해석이 가능하다.
  • 과대적합(Overfitting)이 발생하기 쉽다.
  • 앙상블기반 알고리즘인 랜덤 포레스트와 많은 부스팅(Boosting)기반 앙상블 모델들의 기반 알고리즘으로 사용된다.

순도(purity) / 불순도(impurity)

  • 서로 다른 종류의 값들이 섞여 있는 비율
  • 한 종류(class)의 값이 많을 수록 순도가 높고 불순도는 낮다.

Decision Tree(결정트리) 용어

  • Root Node : 시작 node
  • Decision Node (Intermediate Node): 중간 node
  • Leaf Node(Terminal Node) : 마지막 단계의 노드로 최종결과를 가진다.

과대적합(Overfitting) 문제

  • 모든 데이터셋이 모두 잘 분류 되어 불순도가 0이 될때 까지 분기해 나간다.
  • Root에서 부터 하위 노드가 많이 만들어 질 수록 모델이 복잡해져 과대적합이 발생할 수 있다.
  • 과대적합을 막기 위해서는 적당한 시점에 하위노드가 더이상 생성되지 않도록 해야 한다.
    • 가지치기(Pruning)라고 한다.

Decision Tree 하이퍼파라미터

  • 가지치기 관련 하이퍼파라미터
    • max_depth : 최대 깊이
    • max_leaf_nodes : 생성될 최대 Leaf Node 개수 제한
    • min_samples_leaf: 가지를 칠 최소 sample 수. sample 수가 지정한 값보다 작으면 불순도와 상관없이 가지를 치지 않는다.
  • criterion (크라이티어리언-판단기준)
    • 불순도 계산 방식을 하이퍼파라미터
    • gini (기본값)
    • entropy

Feature(컬럼) 중요도 조회

  • feature_importances_ 속성
    • 모델을 만들때 각 Feature의 중요도를 반환
    • input data 에서 중요한 feature를 찾기 위해 decision tree를 이용하기도 한다.

 

Decision Tree(결정트리) 모델링 예시

wine color 분류

  • https://archive.ics.uci.edu/ml/datasets/Wine+Quality
  • features
    • 와인 화학성분들
      • fixed acidity : 고정 산도
      • volatile acidity : 휘발성 산도
      • citric acid : 시트르산
      • residual sugar : 잔류 당분
      • chlorides : 염화물
      • free sulfur dioxide : 자유 이산화황
      • total sulfur dioxide : 총 이산화황
      • density : 밀도
      • pH : 수소 이온 농도
      • sulphates : 황산염
      • alcohol : 알콜
    • quality: 와인 등급 (A>B>C)
  • target - color
    • 0: white, 1: red

데이터 세팅 및 전처리

import pandas as pd
import numpy as np

wine = pd.read_csv('data/wine.csv')

wine['color'].value_counts()
# 0    4898
# 1    1599

wine['quality'].value_counts()
# B    2836
# C    2384
# A    1277

# quality 를 LabelEncoder를 이용해 숫자로 변경.
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
wine['quality'] = encoder.fit_transform(wine['quality'])

quility 칼럼이 Encoding 되었다.

Decision Tree(결정트리) 모델링 및 성능평가

# 데이터셋과 타겟 분리
y = wine['color'] #output/target/label
X = wine.drop(columns='color') #input/feature

from sklearn.model_selection import train_test_split

#train/test set 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y)


from sklearn.tree import DecisionTreeClassifier
# 모델 생성
dt = DecisionTreeClassifier()
# 학습
dt.fit(X_train, y_train)

# ==>DecisionTreeClassifier(ccp_alpha=0.0, class_weight=None, criterion='gini',
#                       max_depth=None, max_features=None, max_leaf_nodes=None,
#                       min_impurity_decrease=0.0, min_impurity_split=None,
#                       min_samples_leaf=1, min_samples_split=2,
#                       min_weight_fraction_leaf=0.0, presort='deprecated',
#                       random_state=None, splitter='best')


# 예측, 검증
from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score, confusion_matrix
pred_train = dt.predict(X_train)
pred_test = dt.predict(X_test)

Decision Tree(결정트리) 모델링 시각화

from sklearn.tree import export_graphviz
from graphviz import Source
eg = export_graphviz(dt, 
                     out_file=None, 
                     feature_names = X.columns,
                     class_names = ['White','Red'],
                     rounded=True, filled=True)
graph = Source(eg)
graph

결정트리의 세부 내용을 알수있다.

Decision Tree(결정트리) 중요 feature 조회

# 중요한  feature(컬럼)들 조회 
dt.feature_importances_

# array([0.02757233, 0.0141966 , 0.00671037, 0.00773827, 0.66640735,
       0.00553605, 0.21855338, 0.02588049, 0.01621922, 0.00813591,
       0.00305004, 0.        ])

f_i = pd.Series(dt.feature_importances_, index=X_train.columns)
f_i.sort_values(ascending=False)

import matplotlib.pyplot as plt
f_i.sort_values().plot(kind='barh', figsize=(10,6))
plt.show()

Decision Tree(결정트리)  GridSerchCV 모델링

from sklearn.model_selection import GridSearchCV
param_grid = {
    'max_depth':range(3,15), 
    'min_samples_leaf':range(5,51,5)
}

gs = GridSearchCV(DecisionTreeClassifier(),
                  param_grid = param_grid,
                  cv=5,
                  n_jobs=-1,
                  scoring=['accuracy','recall','precision', 'f1'],
                  refit='accuracy'
                 )
# GridSearch/Random Search 에서 평가지표를 여러개 선택한 경우 best param을 선택할때 사용할 평가지표를 알려줘야 한다.
# - refit='평가지표'

Feature(컬럼) 중요도 조회

 

 

Random Forest (랜덤포레스트)와 앙상블(Ensemble) 

앙상블(Ensemble) 기법

  • 하나의 모델만을 학습시켜 사용하지 않고 여러 모델을 학습시켜 결합하는 방식으로 문제를 해결하는 방식
  • 개별로 학습한 여러 모델을 조합해 과적합을 막고 일반화 성능을 향상시킬 수 있다.
  • 개별 모델의 성능이 확보되지 않을 때 성능향상에 도움될 수 있다.

앙상블의 종류

1. 투표방식

  • 여러개의 추정기(Estimator)가 낸 결과들을 투표를 통해 최종 결과를 내는 방식
  • 종류
    1. Bagging - 같은 유형의 알고리즘들을 조합하되 각각 학습하는 데이터를 다르게 한다.
    2. Voting - 서로 다른 종류의 알고리즘들을 결합한다.

2. 부스팅(Boosting)

  • 약한 학습기(Weak Learner)들을 결합해서 보다 정확하고 강력한 학습기(Strong Learner)를 만든다.
  • 각 약한 학습기들은 순서대로 일을 하며 뒤의 학습기들은 앞의 학습기가 찾지 못한 부분을 추가적으로 찾는다.

 

Random Forest (랜덤포레스트)

  • Bagging 방식의 앙상블 모델이며 결정 Tree를 기반으로 한다.
  • 다수의 결정트리를 사용해서 성능을 올린 앙상블 알고리즘의 하나
    • 학습데이터를 샘플링해서 다수의 결정트리를 생성하고 이를 기반으로 다수결로 결과를 결정하는 방식
    • 다수의 결정트리를 만드는데서 랜덤포레스트라고 부른다.
  • 처리속도가 빠르며 분류 성능도 높은 모델로 알려져 있다.
  • 랜덤포레스트의 절차
    • 결정트리의 개수를 하이퍼파라미터로 받는다.
    • 랜덤 포레스트를 구성하는 모든 결정트리가 서로 다르도록 만든다.
      • 각 트리는 부트스트랩 샘플링(중복을 허용하면서 랜덤하게 샘플링하는 방식)으로 데이터셋을 준비한다. (총데이터의 수는 원래 데이터셋과 동일 하지만 일부는 누락되고 일부는 중복된다.)
      • 각 트리는 전체 피처중 일부의 피처만 랜덤하게 가지게 된다.
    • 각 트리별로 예측결과를 내고 분류의 경우 그 예측을 모아 다수결 투표로 클래스 결과를 낸다.
    • 회귀의 경우는 예측 결과의 평균을 낸다.
  • 주요 하이퍼파라미터
    • n_estimators
      • tree의 개수
      • 시간과 메모리가 허용하는 범위에서 클수록 좋다.
    • max_features
      • 각 트리에서 선택할 feature의 개수
      • 클수록 각 트리간의 feature 차이가 없어지고 작을 수록 차이가 많이 나게 된다.
    • max_depth, min_samples_leaf, ..
      • DecisionTreeClassifier의 하이퍼파라미터들
      • 트리의 최대 깊이, 가지를 치기 위한 최소 샘플 수 등 결정 트리에서 과적합을 막기 위한 파라미터들을 랜덤 포레스트에 적용할 수 있다.

 

Random Forest 모델링 예시 (1)

wine color 분류

from sklearn.ensemble import RandomForestClassifier

rfc = RandomForestClassifier(n_estimators=200, #결정트리 모델의 개수.
                             max_depth=3, #각 결정트리의 max_depth 하이퍼파라미터 지정.
                             max_features=5 #Train 데이터셋을 샘플링할때 컬럼(feature)의 개수.
                            )
rfc.fit(X_train, y_train)

#==> RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,
                       criterion='gini', max_depth=3, max_features=5,
                       max_leaf_nodes=None, max_samples=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=2,
                       min_weight_fraction_leaf=0.0, n_estimators=200,
                       n_jobs=None, oob_score=False, random_state=None,
                       verbose=0, warm_start=False)


pred_train = rfc.predict(X_train)
pred_test = rfc.predict(X_test)

accuracy_score(y_train, pred_train), accuracy_score(y_test, pred_test)
(0.9883004926108374, 0.9821538461538462)

f1_score(y_train, pred_train), f1_score(y_test, pred_test)
(0.9760201935212451, 0.9633375474083439)

feature 중요도 확인

f_i = rfc.feature_importances_
f_i_s = pd.Series(f_i, index=X_train.columns).sort_values(ascending=False)
f_i_s

 

Random Forest 모델링 예시 (2)

TODO 위스콘신 유방암 데이터셋 분류

데이터셋 로드 및 train/test set 분리

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.ensemble import RandomForestClassifier

cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data,cancer.target,test_size = 0.3, stratify=cancer.target)

모델(Random Forest) 생성 및 학습

from sklearn.model_selection import GridSearchCV
ran_for = RandomForestClassifier()
gr_ran = GridSearchCV(ran_for, param_grid = {'max_depth':range(1,10)},cv=5,n_jobs=-1)
gr_ran.fit(X_train,y_train)

#==> GridSearchCV(cv=5, error_score=nan,
             estimator=RandomForestClassifier(bootstrap=True, ccp_alpha=0.0,
                                              class_weight=None,
                                              criterion='gini', max_depth=None,
                                              max_features='auto',
                                              max_leaf_nodes=None,
                                              max_samples=None,
                                              min_impurity_decrease=0.0,
                                              min_impurity_split=None,
                                              min_samples_leaf=1,
                                              min_samples_split=2,
                                              min_weight_fraction_leaf=0.0,
                                              n_estimators=100, n_jobs=None,
                                              oob_score=False,
                                              random_state=None, verbose=0,
                                              warm_start=False),
             iid='deprecated', n_jobs=-1,
             param_grid={'max_depth': range(1, 10)}, pre_dispatch='2*n_jobs',
             refit=True, return_train_score=False, scoring=None, verbose=0)


pred_train = gr_ran.predict(X_train) 
pred_test = gr_ran.predict(X_test) 

분류 정확도 검증

from sklearn.metrics import accuracy_score
accuracy_score(y_train, pred_train), accuracy_score(y_test, pred_test)
(0.9974874371859297, 0.9181286549707602)

특성 중요도 (Feature importance) 조회 및 시각화

from sklearn.model_selection import GridSearchCV
ran_for = RandomForestClassifier(n_estimators=200, max_depth=3, max_features=5)
ran_for.fit(X_train,y_train)
pred_train = ran_for.predict(X_train) 
pred_test = ran_for.predict(X_test) 
accuracy_score(y_train, pred_train), accuracy_score(y_test, pred_test)'


import numpy as np
import pandas as pd
f_i = ran_for.feature_importances_
f_i_s = pd.Series(f_i, index=cancer.feature_names).sort_values(ascending=False)
f_i_s.sort_values().plot(kind='barh',figsize=(10,5))

 

728x90
반응형