결정 트리
결정트리를 사용하면 모델을 어떤 기준에 따라서 분류가 진행되었는지 확인할 수 있어 결과에 대한 이유를 설명하기 쉽다.
위의 모형에서 볼 수 있듯이 결정트리는 먼저 루트노드에서 시작한다.
그리고 각 노드에서 조건을 만족하면 왼쪽 자식노드로 만족하지 않으면 오른쪽 자식노드로 이동한다.
이때 자식노드를 가지지 않는 리프노드라면 추가적인 검사를 하지 않는다.
그리고 각각의 노드 안에 있는 요소는 아래와 같다.
- sample: 얼마나 많은 데이터를 해당 노드에서 분류를 할 건지 나타낸다.
- value: 각 데이터들이 해당 노드에서 어떤 클래스에 속해있는지 나타낸다.
- gini: 불순도로 아래와 같은 공식의 값에 따라 표기한다.
$G_i = 1 - \sum\limits_{k=1}^n p_{i,k}^2 \quad$ ($p_{i,k}$는 i번째 노드에 있는 샘플 중 클래스 k에 속한 샘플의 비율이다.)
위의 루트노드를 예시로 살펴보면 전체 5197개의 데이터 중에서 suger가 4.325이하인 경우 왼쪽으로 이동하고 아닌 경우 오른쪽으로 이동한다.
이때 4.325이하인 데이터의 수는 1258개이고, 아닌 경우는 3939개이다.
불순도의 경우 $1 - (\frac{1258}{5197})^2 - (\frac{3939}{5197})^2 = 0.3669 $가 나오게 된다.
기본적으로 결정 트리에서 gini분순도를 사용하지만, 엔트로피 불순도를 사용할 수도 있다.
$\text{엔트로피 불순도: } H_i = - \sum\limits_{k=1}^n p_{i,k} log_2(p_{i,k}) \quad 단, P_{i,k} \neq 0$
지니 불순도와 엔트로피의 실제값은 차이가 거의 없어 둘다 비슷한 트리를 만들어 낸다.
다만, 지니 불순도는 로그를 계산할 필요가 없어서 계산이 조금 더 빠르고 가장 빈도가 높은 클래스를 한쪽 가지로 고립시키는 경향이 있다.
반면에 엔트로피는 균형잡힌 트리를 만든다.
결정 트리에서는 샘플을 클래스 비율로 나눌 때 특성값의 스케일이 계산의 영향을 미치지 않기 때문에 표준화 전처리 과정이 필요 없는 장점이 있다.
결정트리에서 특정 샘플이 어떤 클래스에 속하는지 추정하려면 해당 데이터의 값을 트리에 넣었을 때 나오는 최종 노드의 각 클래스별 확률을 비교하여 가장 확률이 높은 클래스로 예측하면 된다.
검증 세트
모델을 학습한 다음에 테스트를 하지 않으면 모델이 과대적합인지 과소적합인지 판다 할 수 없다.
따라서 검증을 위해 테스트를 진행해야하는데 테스트 세트를 이용하여 검증을 하는 경우 실제 서비스 사용 시 오차율이 큰 경우가 있다.
이는 훈련을 하면서 테스트세트에 최적화된 모델이 만들어 졌기 때문이다.
이러한 문제를 막기 위해서 대표적으로 훈련 세트를 검증할 세트로 또 나눠서 학습하는 홀드아웃 검증이라는 것을 사용할 수 있다.
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 데이터를 한 번 더 나눈다
train_input, train_target, test_size=0.2, random_state=42)
교차 검증
검증세트를 너무 작게 고를경우 모델의 평가가 부정확해지고 검증세트가 너무 클 경우 남은 훈련세트가 너무 작아져서 잘못된 모델이 만들어질 경우가 있다.
이를 해결하기 위해서 교차검증을 사용하는데, 이는 작은 검증세트를 여러 개 사용해여 반복적으로 교차 검증을 수행해 검증세트마다 나머지 데이터에서 훈련한 모델을 해당 검증세트에서 평가하는 것이다.
이에 단점은 검증세트를 훈련세트를 n등분한다했을 때 n배의 시간이 걸려 세트수가 많을수록 걸리는 시간이 배로 늘어나는 것이다.
from sklearn.model_selection import cross_validate
import numpy as np
from sklearn.model_selection import StratifiedKFold
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']))
위 코드는 교차 검증으로 K-Fold를 사용하였는데 이중에서도 startifiedKFold를 사용하였다.
startifiedKFold는 target의 샘플 비율을 보존시켜 K-Fold를 진행시키기 때문에 데이터가 한 곳으로 몰리는 것을 방지할 수 있어 유용하다.
하이퍼파라미터 튜닝
하이퍼파라미터는 사용자가 지정해야만 하는 파라미터를 의미한다.
모델을 학습히기 위해서는 이런 하이퍼파리미터를 선택해야 하는데 값에 따라서 학습이 잘 진행될 수도 있고, 잘못될 수도 있기 때문에 적절한 값으로 잘 골라야 한다.
- 그리드 서치
GridSerchCV를 이용해서 탐색하고자 하는 하이퍼파라미터와 시도해볼 값을 지정하여 가능한 모든 하이퍼파라미터 조합에 대해 교차검증을 사용해서 평가하게 된다.
다만 많은 수의 조합을 탐구할때는 시간이 매우 오래걸려서 비효울적이다.
from sklearn.model_selection import GridSearchCV
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_)
print(np.max(gs.cv_results_['mean_test_score']))
그리드 서치에서 각각의 탐색하고 싶은 그리드를 사용자가 정할 수 있다.
따라서 탐색을 원하는 그리드를 params값으로 넣어주면 해당하는 모든 경우에 대한 조합을 넣어 결과를 얻을 수 있다.
- 랜덤 서치
변수의 값의 범위나 간격을 미리 정하기 어렵거나 너무 많은 변수 조건이 있어 그리드 서치를 사용하기 어려운 경우 사용한다.
이는 가능한 모든 조합을 시도하는 대신 각 반복마다 임의의 수를 대입해서 지정한 횟수만큼만 평가한다.
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform, randint
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),
}
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_)
print(np.max(gs.cv_results_['mean_test_score']))
랜덤 서치도 그리드 탐색과 유사하게 사용되지만 uniform과 randint를 사용해서 해당하는 범위의 임의의 값을 넣어서 원하는 횟수만큼만 반복이 된다.
이때 uniform으로 뽑은 값은 해당하는 범위 내의 실수 값이 나오고, randint는 해당하는 범위 내의 정수 값이 나온다.
랜덤 포레스트
랜덤포레스트는 일반적으로 배깅 방법을 적용한 결정 트리의 앙상블이다.
랜덤 포레스트는 트리의 노드를 분할할 때 전체 특성중에서 최선의 특성을 찾는 대신 무작위로 선택한 특성 후보 중에서 최적의 특성을 찾는 식으로 무작위성을 더 주입한다.
이는 트리를 다양하게 만들고 편향을 손해보는 대신 분산을 낮춰 전체적으로 훌륭한 모델을 만든다.
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']))
엑스트라 트리
랜덤 포레스트와 다르게 부트스트랩을 사용하지 않아 결정 트리를 만들 때 전체 훈련 세트를 사용한다.
긜고 노드를 분할할 때 무작위로 분할하여 많은 트리를 앙상블 하기 때문에 과대적합을 막고 검증 세트의 점수를 높인다.
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']))
그레디언트 부스팅
깊이가 얕은 결정 트리를 사용해서 이전 예측기가 만든 잔여 오차에 새로운 예측기를 학습시켜 이전까지의 오차를 보정하는 방식이다.
그레디언트 부스팅에서 Learning_rate 매개변수는 각 트리의 기여 정도를 조절한다.
만약 Learning_rate가 낮게 설정하면 훈련 세트에 학습시키기 위해 많은 트리가 필요하지만 예측의 성능은 좋아진다.
이는 축소(shrinkage)라고하는 규제 기법이다.
다만 너무 많은 트리의 수가 생길 수 있어 조기 종료 기법을 활용하면 최적의 트리 수를 찾을 수 있다.
from sklearn.ensemble import GradientBoostingClassifier
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']))
히스토그램 기반 그레디언트 부스팅
정형 데이터를 다루는 머신러닝 알고리즘 중에서 가장 인기가 높은 알고리즘으로 입력 특성을 256개의 구간으로 나눠 노드를 분할할 때 최적의 분할을 매우 빠르게 찾을 수 있다.
- XGBoost
가장 대표적인 라이브러리로 기존의 경사하강법의 단점을 보완한 알고리즘이다.
그리고 과대 적합을 방지하기 위한 정규화가 포함되어 있어 모델의 성능을 향상시킬 수 있다.
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']))
- LightGBM
메모리 사용량을 줄이고 학습 속도를 높여 대규모 데이터세트를 사용할 경우에 빠르게 처리가 가능해 유용하다.
그리고 데이터 손실을 최소화 하는 leaf-wise방식을 사용해서 복잡한 모델에서 더 좋은 정확도를 제공한다.
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']))
'컴퓨터 > 머신러닝' 카테고리의 다른 글
[혼자 공부하는 머신러닝] 6. 비지도 학습 (1) | 2024.02.24 |
---|---|
[혼자 공부하는 머신러닝] 4. 다양한 분류 알고리즘 (1) | 2024.02.10 |
[혼자 공부하는 머신러닝] 3. 회귀 알고리즘과 모델 규제 (0) | 2024.02.04 |
[혼자 공부하는 머신러닝] 1. 나의 첫 머신러닝 & 2. 데이터 다루기 (1) | 2024.01.30 |
PRML 2. 확률 분포 (0) | 2023.11.08 |