'데이터분석' 카테고리의 다른 글
3 - 데이터 살펴보기 (0) | 2023.01.23 |
---|---|
1 - 판다스 입문 (0) | 2023.01.23 |
3 - 데이터 살펴보기 (0) | 2023.01.23 |
---|---|
1 - 판다스 입문 (0) | 2023.01.23 |
3 - 데이터 살펴보기 (0) | 2023.01.23 |
---|---|
2 - 데이터 입출력 (0) | 2023.01.23 |
앙상블 : 프랑스어로 '함께, 동시에, 한꺼번에, 협력하여' 등을 의미하는 부사
앙상블 학습(Ensemble Learning)은 여러 개의 분류기를 생성하고, 그 예측을 결합함으로써 보다 정확한 예측을 도출하는 기법을 말합니다.
강력한 하나의 모델을 사용하는대신 보다 약한 모델 여러개를 조합하여 더 정확한 예측에 도움을 주는 방식입니다.
현실세계로 예를 들면, 어려운 문제를 해결하는데 한 명의 전문가보다 여러명의 집단지성을 이용하여 문제를 해결하는 방식을 앙상블 기법이라 할 수 있습니다.
2. 앙상블 학습 유형
앙상블 학습은 일반적으로 보팅(Voting), 배깅(Bagging), 부스팅(Boosting) 세 가지의 유형으로 나눌 수 있습니다.
정형 데이터 / 비정형 데이터
여태까지 사용했던 데이터들 (도미/빙어/와인 등..)은 길이,무게,높이 등 csv파일에 가지런한 데이터로 되어있었다. 이런 데이터들을 정형 데이터라고 부른다.[어떠한 구조로 되어있다는 뜻] CSV / XLSX 등에 저장하기 쉽다. 반대로 비정형 데이터는 책과 같은 텍스트 데이터, 사진 , 음악, 영상 등이 있다.
정형 데이터들을 다루는 데 가장 뛰어난 성과를 내는 알고리즘이 앙상블 학습 이라고한다.
그렇다면 비정형 데이터는 어떤 알고리즘을 사용해야할까? (+어떻게 정리?) > 7장
랜덤 포레스트 알고리즘
랜덤 포레스트는 앙상블 학습의 대표격으로, 안정적 성능을 자랑한다.
좋은 이유 ==> 1. 배깅의 이점을 살리고, 2. 변수를 랜덤으로 선택하는 과정을 추가함으로 개별나무들의 상관성을 줄여 예 측력이 좋기 때문
1 : 배깅 : 데이터 샘플링(Bootstrap) 을 통해 모델을 학습시키고 결과를 집계(Aggregating) 하는 방법
2 : 변수를 랜덤 으로 선택
랜덤 포레스트는 결정 트리를 랜덤하게 만들어 결정 트리의 숲을 만들고, 각 트리의 예측을 사용해 최종 예측을 만듦.
예측력이 좋은 이유 : 다른 알고리즘과 달리 변수들의 상관관계 확연히 줄어듦.개별 나무 분리할 때마다 계속해서 랜덤한 데이터가 뽑히므로!
와인 데이터셋으로 해보자
import numpy as np
import pandas as pd
from sklearn.model_selection 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)
cross_validate()으로 교차검증을 해보자!랜덤포레스트 기본 결정트리 개수 = 100개n_jobs = -1 (오래걸리니까?)
return_train_score = True : 검증점수 뿐만아니라 훈련 세트에 대한 점수도 같이 반환한다
(과대 /과소 파악하는데 쉬우므로)
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_jobs=-1, random_state=42)
scores = corss_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
훈련세트에 과대적합
도수 / 당도 / pH
특성 중요도 < 단순 결정 트리에서는 0.123 / 0.868 / 0.00791 > 였다.!
rf.fit(train_input, train_target)
print(rf.feature_importances_)
[0.23167441 0.50039841 0.26792718]
랜덤 포레스트 알고리즘에서의 특성 중요도는 당도가 줄어들고 도수 pH가 모두 증가
>>>랜덤하게 선택하여 개별 나무들의 상관성이 줄어들었기 때문 ( 예측력 상승)<<<
OOB 샘플 (out of bag) 샘플
말그대로 사용되지 않고 남은 샘플을 의미,
이 샘플들을 이용해서 부트스트랩 샘플로 훈련한 트리를 평가할 수 있다!! ( 검증 세트의 역할과 비슷)
rf = RandomForestTreeClassifier(oob_score=True, n_jobs=-1, random_state=42)
rf.fit(train_input, train_target)
print(rf.oob_score)
0.8934000384837406
엑스트라 트리
엑스트라 트리는 랜덤 포레스트와 비슷하게 동작한다.
차이점: 부트스트랩 샘플 사용 X , 결정트리를 만들 때 전체 훈련 세트를 사용한다. 대신 노드를 분할할 때 가장 좋은 분할을 찾는 것이 아니라 무작위로 분할한다. 기존의 결정 트리에서 splitter='random' 사용한것과 같.
성능은 낮아지지만 검증 세트의 점수를 높일 수 있다.(왜?) + 속도 빠르다 ( 기준이 랜덤) +
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_scores']), np.mean(scores['test_score']))
0.9974503966084433 0.8887848893166506
비슷하다 (특성 많지않아서) 특성 중요도를 살펴보자.
ef.fit(train_input, train_target)
print(ef.feature_importances_)
[0.20183568 0.52242907 0.27573525]
오 비슷하다.
다음은 랜덤포레스트 / 엑스트라 트리와는 다른 앙상블 학습을 알아보자.
그레디언트 부스팅(Gradient boosting)
( 경사하강법 조금씩 이동 ) <=> ( 그레디언트 - 길이 얕은 트리)
https://www.youtube.com/watch?v=3CC4N4z3GJc
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_scores']))
0.8881086892152563 0.8720430147331015
굿! 개수를 더 높여보자 (개수 500)
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_scores']))
0.9464595437171814 0.8780082549788999
특성 중요도
gb.fit(train_input,train_target)
print(gb.feature_importances_)
[0.15872278 0.68010884 0.16116839]
+새로운 매개변수 subsample : 트리 훈련에 사용할 훈련 세트 비율을 정함 (default=1 [전체])
subsample을 낮추면???? <<< 미니배치 경사 하강법과 비슷함 >>>
그레디언트 부스팅은 랜덤 포레스트보다 더 높은 성능! 하지만 속도 느리고 과적합 가능성 높다.
그레디언트 부스팅의 속도와 성능을 개선한 모델
정형 데이터를 다루는 머신러닝 알고리즘 중 가장 인기가 높은 알고리즘이래.
입력 특성을 256개의 구간으로 나눈다. 따라서 노드를 분할할 때 최적의 분할을 빠르게 찾을 수 있다.
히스토그램 기반 그레디언트 부스팅은 256개의 구간 중에서 하나를 떼어 놓고 누락된 값을 위해서 사용한다.
따라서 입력에 누락된 특성이 있더라도 이를 따로 전처리할 필요가 없다! (왜?)
from sklearn.experimental import enable_hist_gradient_boosting
from sklearn.ensemble import HistGradientBoostingClassifier
hgb = HistGradientBoostingClassifier(random_state=42)
scores = cross_validate(hgb, train_input, train_target,
return_train_score=True)
print(np.mean(scores['train_score']),np.mean(scores['test_score']))
0.9321723946453317 0.8801241948619236
과대적합 더 억제하면서 그레디언트부스팅보다 성능이 좋다.
여기서 특성중요도는 feature_importnaces_가 아니라 permutation_importance 라고 한다.
from sklearn.inspection import permutatin_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]
n_repeats는 랜덤하게 섞을 횟수 (default=5)
성능을 확인해보자.
hgb.score(test_input,test_target)
0.8723076923076923
사이킷런 말고 그레디언트 부스팅 알고리즘을 쓰고싶다면 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)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
0.8824322471423747 0.8726214185237284
5 - 2 교차 검증과 그리드 서치 (0) | 2023.01.10 |
---|---|
5 - 1 결정 트리 (1) | 2023.01.09 |
여태까지 배웠던 최근접 이웃, 로지스틱 회귀, SGD, 트리 등 다양한 머신러닝 모델들이 있었지만,
모든 모델들은 결국 실전에 투입시키기 위해 사용한다. 따라서 성능을 높이기 위해 반복적으로 테스트 세트를 활용하여 점수를 높여가야하는데, 이는 목적과 다르게 모델이 테스트 세트에 더 적합해지는 불상사가 생길 수 있다.
이를 해결하기 위해 검증 세트를 활용해보자.
검증 세트 (validation set)
테스트 세트를 마지막에 한 번 사용하기위해 훈련 세트를 또 나누어서 검증세트를 만들고 이 세트를 테스트 세트와 똑같이 사용할 수 있다.
import pandas as pd
wine = pd.read_csv("https://bit.ly/wine_csv_data")
# class 열을 타겟으로 설정하고 나머지 열은 특성으로 저장 ( .to_numpy() )
data = wine[["alcohol","sugar","pH"]].to_numpy()
target = wine["class"].to_numpy()
# 이제 훈련 테스트 세트 나누기
from sklearn.model_selection import test_train_split
train_input , test_input, train_target, test_target = test_train_split(data,target,test_size=0.2, random_state=42)
# 검증 세트 만들기
sub_input,val_input, sub_target,val_target = test_train_split(train_input,train_target,test_size=0.2, random_state=42)
print(sub_input.shape, val_input.shape)
(4157, 3) (1040, 3) # <최종 훈련세트수 4157개, 검증세트 수 1040개>
이제 평가해보자.
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier( random_state=42 )
df.fit(sub_input, sub_target)
print(dt.score(sub_input,sub_target))
print(dt.score(val_input,val_target))
0.9971133028626413
0.864423076923077
교차 검증
검증세트를 배워 보았는데, 확실히 성능 향상에 좋은 모델을 만들 수 있을것 같다!
하지만 훈련에 많은 데이터를 활용할수록 좋은데, 검증세트로 인해 훈련세트가 적어지는 단점이 있다.
그렇다고 검증세트를 적게하면 그만큼 점수가 불안정하다.
이럴 때 교차검증을 이용하면 안정적인 검증 점수를 얻고 훈련에 더 많은 데이터를 사용할 수 있다.
교차검증은 검증 세트를 떼어 내어 평가하는 과정을 여러 번 반복한다. 그 다음 이 점수를 평균하여 최종 검증 점수를 얻는다. 위 그림과 같이 3개로 나누는것을 3-폴드 교차 검증이라 한다.
보통 실전에서는 5-폴드나 10-폴드 교차검증을 주로 사용한다. 이렇게 하면 데이터의 8~90%까지 훈련에 사용할 수 있다.
사이킷런의 cross_validate() 교차 검증 함수를 사용하여 코드를 실행해보자.
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
print(scores)
{'fit_time': array([0.01978898, 0.01216435, 0.01753545, 0.01066256, 0.03117847]), 'score_time': array([0.00559211, 0.00121975, 0.00127912, 0.00102139, 0.00114012]), 'test_score': array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}
cross_validate() 는 fit_time , score_time, test_score 각각의 키를 가진 딕셔너리를 반환한다.
fit_time: 모델을 훈련하는 시간
score_time: 모델을 검증하는 시간
test_score: 교차 검증 폴드의 점수들
교차 검증의 최종점수는 교차 검증 폴드의 점수들 (test_score) 의 평균이다.
print(np.mean(scores['test_score']))
0.855300214703487
※ 교차 검증은 데이터를 섞을 필요가 없다. ※
※ 교차 검증을 수행하면 모델에서 얻을 수 있는 최상의 검증 점수를 얻을 수 있다. ※
훈련 세트를 섞으려면 분할기를 지정해야 한다.
cross_validate()에서 사용하는 분할기- 회귀일 때는 KFold / 분류일 때는 StrarifiedKFold
여기에서는 분류이므로
from sklearn.model_selection import StratifiedKFold
scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))
0.855300214703487
위 두개는 결과가 같다 == cross_validate 할 때는 따로 섞지 않아도 된다!
만약 훈련 세트를 섞고 10-폴드 교차 검증을 하고 싶으면
from sklearn.model_selection import StratifiedKFold
scores = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score']))
0.8574181117533719
모델파라미터 : 모델이 학습하는 파라미터
하이퍼파라미터 : 모델이 학습할 수 없어서 사용자가 지정해야 하는 파라미터
사이킷런과 같은 머신러닝 라이브러리를 사용할 때 하이퍼파라미터는 모두 클래스/메소드의 매개변수로 표현됨
< 하이퍼파라미터 : 머신러닝 클래스/함수의 파라미터(변수)>
머신러닝 모델에서 최적의 파라미터를 찾는다고 가정해보자. DecisionTreeClassifier을 예를들면,
최적의 파라미터들을 찾는다고 할 때, 먼저 최적의 max_depth를 찾고, 고정한 후 최적의 min_samples_split를 찾고.. 이런 방법으로 하이퍼파라미터 튜닝을 할 수 있을까?
불행하게도 이런식으로 찾을 수 없다. max_depth의 최적값은 min_samples_split 변수의 값이 바뀌면 따라 바뀐다.즉, 변수들을 동시에 바꿔가며 최적의 값을 찾아야 한다. ( 변수의 값이 많을 수록 매우 복잡해진다.)
이렇게 동시에 변수들의 값을 바꿔가며 최적의 값을 찾아주는 기능이 있다.
그리드 서치는 위에 설명한 최적값을 교차 검증을 통해 찾아내 준다!!
따라서 cross_validate()를 따로 호출할 필요가 없다.
from sklearn.model_selection import GridSearchCV
params = {'min_impurity_decrease' : [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}
#그리드서치는 클래스에 탐색 대상 모델과 params 변수(딕셔너리로)를 전달하여 실행
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params , n_jobs= -1)
# n_jobs = 사용할 CPU 코어 수 { -1은 제한없이 최대로 }
gs.fit(train_input,train_target) (그리드 서치를 통해 25개의 모델을 훈련시킨다)
dt = gs.best_estimator_ # 24개 모델 중 최고의 모델! 이 모델로 학습시키면된다 (dt)
print( dt.score(train_input, train_target))
0.9615162593804117
위 코드를 실행하면 그리드 서치는
prarams 변수 (5개)를 바꿔가며 5번(default=5) 실행한다. 따라서 총 25가지의 경우의 수가 나온다.
그 중 최적의 하이퍼파라미터를 찾으면 best_estimator_를 통해서 그 모델로 학습시키면 될 것 같다.
또한 최적의 매개변수는 best_params_에 저장되어 있다.
# best_ estimator_
# best_params_
print(dt.best_params_)
{'min_impurity_decrease': 0.0001}
여기서는 0.0001 ( 첫 번째 변수) 변수가 최적이다.
각 매개변수에서 수행한 교차 검증의 평균 점수또한 cv_results_속성의 mean_test_score에 저장되어 있다.
a = gs.cv_results_['mean_test_score']
print(a)
[0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
# gs.cv_results_['mean_test_score'] : 평균 점수 나열
변수 개수가 많거나 찾기 힘들때는
np.argmax() 메소드를 통해 가장 큰 값의 인덱스를 추출할 수 있다.
best_index = np.index[np.argmax(gs.cv_results_['mean_test_score']] # 인덱스 번호 반환
print(gs.cv_results_['params'][best_index]) # 키 값을 통해 params value 출력
{'min_impurity_decrease': 0.0001}
정리하자면
더 복잡하게 해보자. <<min_impurity_decrease = 노드를 분할하기 위한 불순도 감소 최소량을 지정>>
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)
}
# 총 경우의 수 (?) = 9 x 15 x 10 x 5(default =5) = 6,750 개
최적의 매개변수 조합을 확인해보자.
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_hobs = -1)
gs.fit(train_input, train_target)
print(gs.best_estimator_)
DecisionTreeClassifier(max_depth=14, min_impurity_decrease=0.0004,
min_samples_split=12, random_state=42)
max_depth= 14, min_impurity=0.0004, min_samples_split= 12 일 때가 최적이다!
print(np.max(gs.cv_results_['mean_test_score']))
0.8683865773302731
랜덤 서치
위 코딩처럼 매개 변수처럼 범위나 간격을 일일히 설정해주기 힘들 수 있다. 변수가 많거나 할 수 없는 환경일 때 !
따라서 랜덤 서치를 이용할 수 있다. by using 싸이파이
랜덤 서치에는 매개변수 값의 목록이 아닌 매개변수를 샘플링할 수 있는 확률 분포 객체를 전달한다.
싸이파이에서 2개의 확률 분포 클래스를 임포트해보자.
from scipy.stats import uniform, randint
randint = 정수 / uniform = 실수
scipy.stats 에 있는 uniform과 randint 클래스는 모두 주어진 범위에서 고르게 값을 뽑는데, 이를
"균등 분포에서 샘플링한다"라고 한다.
rgen = randint(0,10)
rgen.rvs(10)
array([6, 9, 4, 9, 5, 1, 0, 3, 6, 1])
데이터 10개 중에서 10개를 뽑아서 균일하게 뽑혔는지 잘 모른다.
rgen = randint(0,10)
a = rgen.rvs(1000)
np.unique(a, return_counts = True)
(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
array([ 98, 107, 107, 93, 88, 95, 98, 102, 106, 106]))
대략적으로 골고루 뽑히는것을 확인할 수 있다.
일종의 난수 발생기와 유사하다고 볼 수 있다.
이제 균등 분포에서 샘플링을 해보자!
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),
}
샘플링 횟수는 사이킷런의 랜덤 서치 클래스인 RandomizedSearchCV의 n_iter = 에 지정 가능
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}
위 params에서 정의된 매개변수 범위에서 총 100번을 샘플링하여 교차 검증을 수행하고 최적의 매개변수 조합을 찾는다.
앞서 그리드 서치보다 훨씬 교차 검증 수를 줄이면서 넓은 영역을 효과적으로 탐색할 수 있다.
최고의 교차 검증 점수도 확인해보자.
print(np.max(gs.cv_results_['mean_test_score']))
0.8695428296438884
최적의 모델은 이미 전체 훈련세트로 훈련되어 best_estimator_ 속성에 저장되어 있다.
이 모델을 최종 모델로 결정하고 이제 테스트 세트로 성능을 확인해보자!!!!
dt = gs.best_estimator_
print(dt.score(test_input,test_target))
0.86
5 - 3 : 트리의 앙상블 (0) | 2023.01.17 |
---|---|
5 - 1 결정 트리 (1) | 2023.01.09 |
로지스틱 회귀 모델을 적용하여 이진 분류를 해보자.
import pandas as pd
wine = pd.read_csv("https://bit,ly/wine_csv_data")
wine.head()
데이터를 보면 첫 번째 열부터 차례대로 도수, 당도, pH를 나타내며, 마지막 열인 class는 타깃값이 0이면 레드와인, 1이면 화이트 와인이라고 한다.
wine.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6497 entries, 0 to 6496
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 alcohol 6497 non-null float64
1 sugar 6497 non-null float64
2 pH 6497 non-null float64
3 class 6497 non-null float64
dtypes: float64(4)
memory usage: 203.2 KB
describe() 메소드를 이용하여 간략한 통계를 출력해보자.
wine.describe()
alcohol sugar pH class
count 6497.000000 6497.000000 6497.000000 6497.000000
mean 10.491801 5.443235 3.218501 0.753886
std 1.192712 4.757804 0.160787 0.430779
min 8.000000 0.600000 2.720000 0.000000
25% 9.500000 1.800000 3.110000 1.000000
50% 10.300000 3.000000 3.210000 1.000000
75% 11.300000 8.100000 3.320000 1.000000
max 14.900000 65.800000 4.010000 1.000000
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))
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))
0.7808350971714451
0.7776923076923077
낮은 score() + 학습원리 설명하기 어려움
--결정 트리 이용
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state = 42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))
0.996921300750433
0.8592307692307692
점수가 높아졌다. !
이 트리를 시각화해보자. by using plot tree()
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10,7))
plt_tree(dt)
plt.show()
노드의 길이가 너무 길어서 제한해보자 by using max_depth = 1 하면 하나의 노드를 더 그림
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth = 1 , filled = True, feature_names = ['alcohol','sugar','pH'])
plt.show()
그림이 담고 있는 정보는 조건 (sugar), 불순도(gini), 샘플 수, 클래스 별 샘플 수로 이루어져 있다.
루트 노드는 당도가 -0.239이하인지...yes <--- / No --->
gini는 지니 불순도를 의미하는데,
<<<<<클래스개수에 따른 케이스들의 불순한 정도를 나타내는 척도라고 생각하면 될 것 같다.
지니불순도가 필요한 이유는 의사결정을 하는데 있어서 최적의 분류를 위한 결정을 계속해서 맞이하는데 이 결정에 사용되기 때문이다. 이 변수로 인해서 분류를 거쳤을 때 지니불순도가 얼마나 되는가? 를 생각할 수 있게된다. 또다른 불순도지표로는 엔트로피가 있다.>>>>>>
DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값이 gini이다.
지니 불순도(gini) = 1 - ( 음성 클래스 비율^2 + 양성 클래스 비율^2 )
결정 트리 모델은 부모와 자식 노드의 불순도 차이가 커지도록 트리를 성장시킨다.
(자식 노드의 불순도를 샘플 개수에 비례하여 모두 더하고 그 이후 부모 노드의 불순도에서 뺀다.)
=부모 gini - (왼쪽 샘플 수 / 부모 샘플 수) x 왼쪽 gini - (오른쪽 샘플 수 / 부모 샘플 수) x 오른쪽 gini
= 0.367 - [(2922/5197) x 0.481] - [(2275/5197) x 0.069] = 0.066
부모와 자식 노드간의 불순도 차이를 정보 이득이라고 한다.
따라서 정보 이득이 최대가 되도록 데이터를 나누고, 그 기준으로 불순도 (gini)를 사용한다.
한편, 사이킷런에서는 또 다른 불순도 기준이 존재한다. ( 엔트로피 불순도)
DeicisionTreeClassifier 클래스에서 criterion='entropy'
엔트로피 불순도 기준은 아래에 log 2를 붙여서 사용한다.
가지치기
끝도 없이 자라나는 트리가 생길 수 있기 때문에 가지치기는 필수적이다.
가장 간단한 방법은 아까 했던 것처럼 max_depth = 으로 지정할 수 있다.
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))
0.8454877814123533
0.8415384615384616
plt.figure(figsize=(20,15))
plot_tree(dt, filled= True, feature_names=['alcohol', 'sugar','pH'])
plt.show()
위 그림을 보면 깊이 0~3 중 깊이 1은 당도를 기준, 2는 당도,도수,pH 순으로 기준을 사용했다.
최종 노드를 보면 왼쪽에서 세번째 노드만 음성클래스가 더 많은것을 볼 수 있다.
따라서 이 노드 (주황)에 도착해야만 레드와인으로 분류하는것 같다.
즉, 당도는 -0.802~-0.239 사이, 도수는 0.454보다 작아야 레드와인으로 분류한다.
그런데 당도가 음수라는것을 어떻게 해석해야할까.( 문제는 전처리)
결정 트리는 표준화 전처리 과정을 하지 않아도 된다!!!
dt = DecisionTreeClassifier( max_depth = 3, random_state = 42)
dt.fit(train_input, train_target)
print(dt.score(train_input, train_target))
print(dt.score(test_input, test_target))
0.8454877814123533
0.8415384615384616
트리를 그려보자 (no 전처리)
plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol','sugar','pH'])
plt.show()
이제 가시적으로 해석하기 더 쉽다.
마지막으로, 결정 트리에는 어떤 특성이 가장 유용한지 나타내는 특성 중요도를 계산해 줍니다. 이 트리의 루트 노드와 깊이1에서 당도를 사용했기 때문에 당도가 가장 유용한 특성일 것 같다.
메서드는 DecisionTreeClassifier.feature_importances_
print(dt.feature_importances_)
[0.12345626 0.86862934 0.0079144 ]
5 - 3 : 트리의 앙상블 (0) | 2023.01.17 |
---|---|
5 - 2 교차 검증과 그리드 서치 (0) | 2023.01.10 |