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)을 통해 생성된 여러 개의 서브셋을 활용해 독립적으로 여러 모델을 학습시키는 앙상블 방법이다.
- 작동 원리:
- 원본 데이터 세트에서 복원추출을 통해 여러 개의 서브셋을 생성한다.
- 각 서브셋에 대해 독립적으로 모델을 학습시킨다.
- 회귀 문제에서는 예측값의 평균을, 분류 문제에서는 투표 방식을 사용해 최종 예측을 한다.
- 대표적인 알고리즘: 랜덤 포레스트 (Random Forest)
- 목적: 과적합을 방지하고 모델의 분산을 줄여 전반적인 예측 성능을 향상시키는 것이 목표이다.
Boosting
- 정의: Boosting은 약한 학습기를 순차적으로 훈련시켜 강한 학습기를 만드는 앙상블 방법이다.
- 작동 원리:
- 초기에는 모든 데이터 포인트에 동일한 가중치를 부여한다.
- 약한 학습기를 훈련시켜 오류를 줄인다.
- 잘못 분류된 데이터 포인트에 더 높은 가중치를 부여하고 다시 학습한다.
- 대표적인 알고리즘: 아다부스트 (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, 의미: 제한 없음)