Home [SeSAC]혼공 머신러닝+딥러닝 Ch5. 트리 알고리즘
Post
Cancel

[SeSAC]혼공 머신러닝+딥러닝 Ch5. 트리 알고리즘

05-1 결정 트리


Information Gain (정보 이득)

정의: Information Gain은 어떤 속성을 기준으로 데이터를 분할했을 때 얻을 수 있는 엔트로피의 감소량이다. 결정 트리 알고리즘에서는 Information Gain이 최대가 되는 속성으로 데이터를 분할한다.

수식: \(\text{Information Gain} = \text{Entropy(parent)} - \sum \left( \frac{\text{number of samples in child}}{\text{number of samples in parent}} \times \text{Entropy(child)} \right)\)

엔트로피 수식: \(\text{Entropy}(S) = -p_+ \log_2(p_+) - p_- \log_2(p_-)\) 여기서 ( p_+ )는 양성 샘플의 비율, ( p_- )는 음성 샘플의 비율이다.


Gini Impurity (지니 불순도)

정의: Gini Impurity는 임의로 선택된 샘플이 잘못 분류될 확률을 측정한다. 결정 트리 알고리즘에서는 Gini Impurity가 낮은 속성으로 데이터를 분할한다.

수식: \(\text{Gini Impurity}(S) = 1 - (p_+^2 + p_-^2)\) 여기서 ( p_+ )는 양성 샘플의 비율, ( p_- )는 음성 샘플의 비율이다.


모델과 알고리즘의 차이(Decision Tree는 사실 Decision Model이어야 한다?)

로지스틱 회귀로 와인 분류하기

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
30
31
32
33
34
35
import pandas as pd

wine = pd.read_csv('https://bit.ly/wine_csv_data')

wine.head()

wine.info()

wine.describe()

data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    data, target, test_size=0.2, random_state=42)

print(train_input.shape, test_input.shape)  # (5197, 3) (1300, 3)

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)

train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(train_scaled, train_target)

print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))

설명하기 쉬운 모델과 어려운 모델

1
print(lr.coef_, lr.intercept_)  # [[ 0.51270274  1.6733911  -0.68767781]] [1.81777902]

결정 트리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)

print(dt.score(train_scaled, train_target))  # 0.996921300750433
print(dt.score(test_scaled, test_target))  # 0.8592307692307692

import matplotlib.pyplot as plt
from sklearn.tree import plot_tree

plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()

1
2
3
4
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

가지치기(Pruning): 과적합 줄이기

1
2
3
4
5
6
7
8
9
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target)

print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))

plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show

1
2
3
4
5
6
7
8
9
10
11
12
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_input, train_target)

print(dt.score(train_input, train_target))  # 0.8454877814123533
print(dt.score(test_input, test_target))  # 0.8415384615384616


plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

print(dt.feature_importances_)  # [0.12345626 0.86862934 0.0079144 ]

Standard Scaling한것과 안한것의 차이가 거의 없음을 알 수 있음

확인문제

1
2
3
4
5
6
7
8
9
dt = DecisionTreeClassifier(min_impurity_decrease=0.0005, random_state=42)
dt.fit(train_input, train_target)

print(dt.score(train_input, train_target))  # 0.8454877814123533
print(dt.score(test_input, test_target))  # 0.8415384615384616

plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

min_impurity_decrease: superset과 subset impurity차이의 최솟값 설정

05-2 교차 검증과 그리드 서치

‘작은 차이가 명품을 만든다’ -Philips

교차 검증과 그리드 서치

검증 세트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pandas as pd

wine = pd.read_csv('https://bit.ly/wine_csv_data')

data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    data, target, test_size=0.2, random_state=42)

sub_input, val_input, sub_target, val_target = train_test_split(
    train_input, train_target, test_size=0.2, random_state=42)

print(sub_input.shape, val_input.shape)  # (4157, 3) (1040, 3)

from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(random_state=42)
dt.fit(sub_input, sub_target)

print(dt.score(sub_input, sub_target))  # 0.9971133028626413
print(dt.score(val_input, val_target))  # 0.864423076923077

교차 검증

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from sklearn.model_selection import cross_validate

scores = cross_validate(dt, train_input, train_target)
print(scores)
# {'fit_time': array([0.00931716, 0.00749564, 0.00773239, 0.00731683, 0.00710797]), 'score_time': array([0.00109315, 0.00111032, 0.00101209, 0.00106931, 0.00115085]), 'test_score': array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}
import numpy as np

print(np.mean(scores['test_score']))  # 0.855300214703487

from sklearn.model_selection import StratifiedKFold

scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))  # 0.855300214703487

splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
scores = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score']))  # 0.8574181117533719

하이퍼파라미터 튜닝

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
from sklearn.model_selection import GridSearchCV

params = {'min_impurity_decrease': [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}

gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
# n_jobs=-1: 병렬 실행에 사용할 CPU 코어 수: 가용한 모든 코어
gs.fit(train_input, train_target)

dt = gs.best_estimator_
print(dt.score(train_input, train_target))  # 0.9615162593804117

print(gs.best_params_)  # {'min_impurity_decrease': 0.0001}

print(gs.cv_results_['mean_test_score'])  # [0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]

best_index = np.argmax(gs.cv_results_['mean_test_score'])
print(gs.cv_results_['params'][best_index])  # {'min_impurity_decrease': 0.0001}

params = {'min_impurity_decrease': np.arange(0.0001, 0.001, 0.0001),
          'max_depth': range(5, 20, 1),
          'min_samples_split': range(2, 100, 10)
          }

gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)

print(gs.best_params_)  # {'max_depth': 14, 'min_impurity_decrease': 0.0004, 'min_samples_split': 12}

print(np.max(gs.cv_results_['mean_test_score']))  # 0.8683865773302731

min_samples_split: 는 홀수로 지정해 주는것이 좋다

랜덤 서치

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
from scipy.stats import uniform, randint

rgen = randint(0, 10)
rgen.rvs(10)

np.unique(rgen.rvs(1000), return_counts=True)

ugen = uniform(0, 1)
ugen.rvs(10)

params = {'min_impurity_decrease': uniform(0.0001, 0.001),
          'max_depth': randint(20, 50),
          'min_samples_split': randint(2, 25),
          'min_samples_leaf': randint(1, 25),
          }

from sklearn.model_selection import RandomizedSearchCV

gs = RandomizedSearchCV(DecisionTreeClassifier(random_state=42), params,
                        n_iter=100, n_jobs=-1, random_state=42)
gs.fit(train_input, train_target)

print(gs.best_params_)  # {'max_depth': 39, 'min_impurity_decrease': 0.00034102546602601173, 'min_samples_leaf': 7, 'min_samples_split': 13}

print(np.max(gs.cv_results_['mean_test_score']))  # 0.8695428296438884

dt = gs.best_estimator_

print(dt.score(test_input, test_target))  # 0.86

확인문제

1
2
3
4
5
6
7
8
9
10
11
## 확인문제

gs = RandomizedSearchCV(DecisionTreeClassifier(splitter='random', random_state=42), params,
                        n_iter=100, n_jobs=-1, random_state=42)
gs.fit(train_input, train_target)

print(gs.best_params_)  # {'max_depth': 43, 'min_impurity_decrease': 0.00011407982271508446, 'min_samples_leaf': 19, 'min_samples_split': 18}
print(np.max(gs.cv_results_['mean_test_score']))  # 0.8458726956392981

dt = gs.best_estimator_
print(dt.score(test_input, test_target))  # 0.786923076923077

05-3 트리의 앙상블

랜덤포레스트

  • 기본적으로 100개의 결정 트리 사용 ```python import numpy as np import pandas as pd from sklearn.model_selection import train_test_split

wine = pd.read_csv(‘https://bit.ly/wine_csv_data’)

data = wine[[‘alcohol’, ‘sugar’, ‘pH’]].to_numpy() target = wine[‘class’].to_numpy()

train_input, test_input, train_target, test_target = train_test_split(data, target, test_size=0.2, random_state=42)

from sklearn.model_selection import cross_validate from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_jobs=-1, random_state=42) scores = cross_validate(rf, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores[‘train_score’]), np.mean(scores[‘test_score’])) # 0.9973541965122431 0.8905151032797809

rf.fit(train_input, train_target) print(rf.feature_importances_) # [0.23167441 0.50039841 0.26792718]

rf = RandomForestClassifier(oob_score=True, n_jobs=-1, random_state=42)

rf.fit(train_input, train_target) print(rf.oob_score_) # 0.8934000384837406

1
2
3
4
5
6
7
8
9
10
11
12
13
### 엑스트라트리

```python
from sklearn.ensemble import ExtraTreesClassifier

et = ExtraTreesClassifier(n_jobs=-1, random_state=42)
scores = cross_validate(et, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores['train_score']), np.mean(scores['test_score']))  # 0.9974503966084433 0.8887848893166506

et.fit(train_input, train_target)
print(et.feature_importances_)  # [0.20183568 0.52242907 0.27573525]

그레이디언트 부스팅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from sklearn.ensemble import GradientBoostingClassifier

gb = GradientBoostingClassifier(random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores['train_score']), np.mean(scores['test_score']))  # 0.8881086892152563 0.8720430147331015

gb = GradientBoostingClassifier(n_estimators=500, learning_rate=0.2, random_state=42)
scores = cross_validate(gb, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores['train_score']), np.mean(scores['test_score']))  # 0.9464595437171814 0.8780082549788999

gb.fit(train_input, train_target)
print(gb.feature_importances_)  # [0.15872278 0.68010884 0.16116839]

히스토그램 기반 부스팅

  • 입력 특성을 256개의 구간으로 나눈다
    • 노드를 분할할 때 최적의 분할을 매우 빠르게 찾을 수 있다 ```python from sklearn.ensemble import HistGradientBoostingClassifier

hgb = HistGradientBoostingClassifier(random_state=42) scores = cross_validate(hgb, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores[‘train_score’]), np.mean(scores[‘test_score’])) # 0.9321723946453317 0.8801241948619236

from sklearn.inspection import permutation_importance

hgb.fit(train_input, train_target) result = permutation_importance(hgb, train_input, train_target, n_repeats=10, random_state=42, n_jobs=-1) print(result.importances_mean) # [0.08876275 0.23438522 0.08027708]

result = permutation_importance(hgb, test_input, test_target, n_repeats=10, random_state=42, n_jobs=-1) print(result.importances_mean) # [0.05969231 0.20238462 0.049 ]

hgb.score(test_input, test_target) # 0.8723076923076923

1
2
3
4
5
6
7
8
9
10
#### XGBoost
```python
from xgboost import XGBClassifier

xgb = XGBClassifier(tree_method='hist', random_state=42)
scores = cross_validate(xgb, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores['train_score']), np.mean(scores['test_score']))  # 0.9555033709953124 0.8799326275264677

LightGBM

1
2
3
4
5
6
from lightgbm import LGBMClassifier

lgb = LGBMClassifier(random_state=42)
scores = cross_validate(lgb, train_input, train_target, return_train_score=True, n_jobs=-1)

print(np.mean(scores['train_score']), np.mean(scores['test_score']))  # 0.935828414851749 0.8801251203079884

정리

Bagging과 Boosting

Bagging (Bootstrap Aggregating)

  • 정의: Bagging은 Bootstrap Aggregating의 줄임말이며, 복원추출 (bootstrapping)을 통해 생성된 여러 개의 서브셋을 활용해 독립적으로 여러 모델을 학습시키는 앙상블 방법이다.
  • 작동 원리:
    1. 원본 데이터 세트에서 복원추출을 통해 여러 개의 서브셋을 생성한다.
    2. 각 서브셋에 대해 독립적으로 모델을 학습시킨다.
    3. 회귀 문제에서는 예측값의 평균을, 분류 문제에서는 투표 방식을 사용해 최종 예측을 한다.
  • 대표적인 알고리즘: 랜덤 포레스트 (Random Forest)
  • 목적: 과적합을 방지하고 모델의 분산을 줄여 전반적인 예측 성능을 향상시키는 것이 목표이다.

Boosting

  • 정의: Boosting은 약한 학습기를 순차적으로 훈련시켜 강한 학습기를 만드는 앙상블 방법이다.
  • 작동 원리:
    1. 초기에는 모든 데이터 포인트에 동일한 가중치를 부여한다.
    2. 약한 학습기를 훈련시켜 오류를 줄인다.
    3. 잘못 분류된 데이터 포인트에 더 높은 가중치를 부여하고 다시 학습한다.
  • 대표적인 알고리즘: 아다부스트 (AdaBoost), 그래디언트 부스팅 (Gradient Boosting)
  • 목적: 모델의 편향을 줄이고 정확도를 높이기 위해 순차적으로 학습기를 개선하는 것이 목적이다.
  • 배깅에 비해 부스팅이 과적합에 취약하다

파마미터 정리

결정 트리 (Decision Tree)

  • max_depth: 트리의 최대 깊이. 너무 높으면 과적합의 위험이 있다
  • criterion: 분할 품질을 측정하는 기준. ‘gini’ 또는 ‘entropy’를 사용
  • splitter: 분할을 수행하는 전략. ‘best’ 또는 ‘random’
  • min_impurity_decrease: superset과 subset impurity차이의 최솟값 설정
  • min_samples_split:

랜덤포레스트 (Random Forest)

  • n_estimators: 앙상블에 사용할 결정 트리의 수 (default: 100)
  • max_depth: 각 트리의 최대 깊이 (default: None, 가능한 값: 정수)
  • random_state: 재현 가능성을 위한 랜덤 시드 (default: None)

엑스트라트리 (Extra Trees)

  • n_estimators: 생성할 트리의 수 (default: 100)
  • max_features: 각 트리에서 분할에 사용할 특성의 최대 수 (default: ‘auto’, 가능한 값: ‘auto’, ‘sqrt’, ‘log2’)
  • bootstrap: 무작위로 데이터를 샘플링할 것인지 여부 (default: False)

그레이디언트 부스팅 (Gradient Boosting)

  • learning_rate: 학습률 (default: 0.1)
  • n_estimators: 부스팅 단계의 수 (default: 100)
  • subsample: 각 부스팅 단계에서 사용할 훈련 샘플의 비율 (default: 1.0, 가능한 값: 0~1 사이의 실수)

히스토그램 기반 부스팅 (Histogram-based Boosting)

  • learning_rate: 학습률 (default: 0.1)
  • max_iter: 최대 반복 횟수 (default: 100)
  • max_leaf_nodes: 각 트리에서의 최대 리프 노드 수 (default: 31)

XGBoost

  • eta: 학습률 (default: 0.3)
  • max_depth: 트리의 최대 깊이 (default: 6)
  • subsample: 훈련 데이터의 하위 샘플 비율 (default: 1)

LightGBM

  • learning_rate: 학습률 (default: 0.1)
  • num_leaves: 하나의 트리가 가질 수 있는 최대 리프의 수 (default: 31)
  • max_depth: 트리의 최대 깊이 (default: -1, 의미: 제한 없음)
This post is licensed under CC BY 4.0 by the author.

기술 정리

Sesac 혼공 머신러닝+딥러닝 정리