트리 알고리즘

yiseonline·2023년 11월 5일
0

aistudy

목록 보기
7/8
post-thumbnail

결정 트리

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

<데이터 준비하기>

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

<데이터 불러오기>

wine.head()


4번째 열(class)는 타깃값으로 0이면 레드 와인, 1이면 화이트 와인임 -> 화이트 와인이 양성 클래스 => 전체 와인 데이터에서 화이트 와인 골라내기

<데이터 프레임의 유용한 메소드 2가지>

  1. info() 메소드 - 데이터프레임의 각 열의 데이터 타입과 누락된 데이터가 있는지 확인
wine.info()

output

<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

ㄴ 6,497개의 샘플 & 4개의 열

  1. describe() 메소드 - 열에 대한 간략한 통계를 출력해줌 (최소, 최대, 평균값)
wine.describe()

값의 스케일이 다 다르면 StandardScaler클래스를 사용해서 특성을 표준화 해야함.

<데이터 프레임을 넘파이 배열로 바꾸기>

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)
# test_size 를 지정하지 않으면 25%를 테스트 세트로 지정/ 여기선 샘플 개수가 충분히 많으므로 20%로 나눔

<훈련 세트와 테스트 세트 크기 확인하기>

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

<StandardScaler로 훈련 세트 전처리하기 & 테스트 세트 변환하기>

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)) # 0.7808350971714451
print(lr.score(test_scaled, test_target)) # 0.7776923076923077

ㄴ 과소적합된듯 & 점수도 안 높다

<로지스틱 회귀가 학습한 계수와 절편 출력하기>

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

결정 트리


사이킷런에서 DecisionTreeClassifier클래스로 결정 트리 모델을 훈련할 수 있다

<결정 트리 모델 훈련 및 정확도 평가하기>

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

ㄴ 과대 적합된 모델 ~~

  • plot_tree()함수 - 결정 트리를 그림으로 출력해준다

<plot_tree() 로 전달해서 트리 그려보기>

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

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

<트리의 깊이를 제한해서 출력하기>

-max_depth를 1로 주면 루트 노드 제외하고 하나의 노드를 더 확장해서 그림

  • filled 매개변수에서 클래스에 맞게 노드의 색 칠함
    feature_names 매개변수에는 특성의 이름을 전달할 수 있음
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth = 1, filled = True, feature_names = ['alcohol','sugar','pH'])
plt.show()

노드 안에는

이렇게 구성되어 있다.

ex)

당도가 -0.239와 같거나 작으면 왼쪽 가지로, 그렇지 않으면 오른쪽 가지로 감
= 왼쪽이 Yes, 오른쪽이 No

filled = True로 지정하면 어떤 클래스의 비율이 높아지면 점점 진한 색으로 표시됨

지니 불순도

ex) 루트노드는 총 5,197개의 샘플, 음성 클래스가 1,258개, 양성 클래스가 3,939개이다

지니불순도 = 1 - ((1258 / 5197)^2 + (3939 / 5197)^2) = 0.367

지니불순도는 0.5가 되면 최악이 되고 0이 되면 가장 작음 (=순수 노드)

가지 치기

가지치기 하는 가장 간단한 방법 = 자라날 수 있는 트리의 최대 깊이를 지정하는 것!

<최대 깊이 지정해서 DecisionTreeClassifier 모델 만들기>

dt = DecisionTreeClassifier(max_depth = 3, random_state = 42)

dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target)) # 0.8454877814123533
print(dt.score(test_scaled, test_target)) # 0.8415384615384616

<plot_tree()로 그리기>

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

결정 트리 알고리즘의 장점 = 표준화 전처리를 할 필요가 X

<전처리하기 전 훈련 세트와 테스트 세트로 결정 트리 모델을 훈련하기>

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()


ㄴ당도가 1.625 보다 크고 4.325 보다 작은 와인중에 알코올 도수가 11.025와 같거나 작은게 레드와인, 그 외는 다 화이트 와인으로 예측함

<특성 중요도 계산하기>

  • 특성 중요도는 feature_importances_ 속성에 저장되어있음
print(dt.feature_importances_) # [0.12345626 0.86862934 0.0079144 ]

두번째 특성인 당도가 가장 특성 중요도가 높고 그 다음이 알코올 도수, pH 순이다. 이 값들을 모두 더하면 1이 됨 !

특성 중요도를 활용하면 결정 트리 모델을 특성 선택에 활용할 수 있음

교차 검증과 그리드 서치

검증 세트


: 테스트 세트를 사용하지 않고 모델이 과대적합인지, 과소적합인지 판단하는 방법 (= 훈련 세트를 또 나누는 것 !)

테스트 세트와 훈련 세트를 나눠주고

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)

이번에는 subval로 나눠서 훈련 세트와 검증 세트로 나눠준다

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

<모델 평가하기>

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

ㄴ 과대적합됨

교차 검증

검증 세트를 뗄때, 너무 조금 떼면 검증 점수가 불안정함 !!
이럴 때 교차 검증을 이용하면 안정적인 검증 점수를 얻고 훈련에 더 많은 데이터를 사용할 수 있다

교차 검증은 검증 세트를 떼어 내어 평과하는 과정을 여러번 반복함 -> 평균을 내서 최종 검증 점수를 얻음

<3-폴드 교차 검증>

교차검증을 할 때는 cross_validate() 함수를 이용함
ㄴ 사용법
: 평가할 모델 객체를 첫 번째 매개변수로 전달함 -> 훈련 세트 전체를 cross_validate() 함수에 전달함

<사용해보기>

from sklearn.model_selection import cross_validate

scores = cross_validate(dt, train_input, train_target)
print(scores)

output

{'fit_time': array([0.01728225, 0.00762796, 0.00763679, 0.00999308, 0.00713062]), 'score_time': array([0.00136471, 0.00090051, 0.00106382, 0.00113153, 0.00095987]), 'test_score': array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}

ㄴ fit_time, score_time, test_score 키를 가진 딕셔너리 반환

교차 최종 검증 점수를 평균을 내서 얻음 !!

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

주의할 점 !!
cross_validate()는 훈련 세트를 섞어서 폴드를 나누지 않음. 그래서 훈련 세트를 섞으려면 분할기를 지정해야한다 !!
사이킷 런에서 분할기 지정할 때 회귀 모델이면 KFold 분할기를, 분류 모델이면 StratifiedKFold를 사용

<훈련 세트 섞어서 10-폴드 교차 검증 수행하기>

from sklearn.model_selection import StratifiedKFold

# n_splits 매개변수는 몇(k) 폴드 교차 검증을 할지 정함
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

하이퍼파라미터 튜닝

모델 파라미터 : 머신러닝 모델이 학습하는 파라미터
하이퍼 파라미터 : 모델이 학습할 수 없어서 사용자가 지정해야하는 파라미터

하이퍼 파라미터를 튜닝하는 작엄
: 라이브러리가 제공하는 기본 값을 그대로 사용해 모델 훈련 -> 검증 세트의 점수나 교차 검증을 통해서 매개변수를 조금씩 바꿔봄 -> 바꿔가면서 훈련하고 교차검증 수행하기

중요한 점
매개변수를 하나 바꾸고 최적값 찾고 그 다음 매개변수를 바꿔서 최적값 찾는 거 안됨 !!
-> 매개변수들을 동시에 바꿔가며 최적의 값을 찾아야 함 !

근데 매개변수 많아지면 복잡해지기 때문에 사이킷런에서 제공하는 그리드 서치를 사용하면 된다
GridSearchCV 클래스로 하이퍼파라미터 탐색과 교차 검증을 한 번에 수행 가능

<탐색할 매개변수와 탐색할 값의 리스트를 딕셔너리로 만들기>

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)

<훈련하기>
fit()메소드는 그리드 서치 객체에선 결정 트리 모델 min_impurity_decrease 값을 바꿔가며 총 5번 실행함

cv의 매개변수 기본 값은 5이기 때문에 min_impurity_decrease 값마다 5-폴드 교차 검증을 수행해서 결국 5 x 5 = 25 개의 모델을 훈련함

gs.fit(train_input, train_target)

훈련이 끝나면 모델 중 검증 점수가 가장 높은 모델의 매개변수 조합으로 전체 훈련 세트에서 자동으로 다시 모델을 훈련하고 이 모델이 best_estimator_에 저장되어있음

<점수 출력>

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

<최적의 매개변수가 저장되어 있는 bestparams속성 불러오기>

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

ㄴ 0.0001이 가장 좋은 값으로 선택됐구나 ~~

각 매개변수에서 수행한 교차 검증의 평균 점수는 cv_results_ 속성의 mean_test_score에 저장되어있음

<5번의 교차 검증으로 얻은 점수 출력하기>

print(gs.cv_results_['mean_test_score'])

output

[0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]

ㄴ 첫 번째 값이 가장 큰데, 수동으로 고르는 것 보다 넘파이 argmax() 함수를 이용해서 가장 큰 값의 인덱스를 추출할 수 있다

<argmax() 써보기>

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

ㄴ 동일하군

과정 정리

  1. 먼저 탐색할 매개변수 지정
  2. 훈련 세트에서 그리드 서치를 수행해서 최상의 평균 검증 점수가 나오는 매개변수 조합 찾기 (이는 그리드 서치 객체에 저장됨)
  3. 최상의 매개변수에서 전체 훈련세트를 사용해 최종 모델을 훈련함. (이 모델 역시 그리드 서치 객체에 저장됨)

랜덤 서치

매개변수의 값이 수치일 때 범위나 간격 미리 정하기 어렵기도 하고 너무 많은 매개변수 조건이 있어서 그리드 서치 수행시간이 오래걸릴 수 있다 !!
-> 랜덤 서치를 사용하자

랜덤서치에는 매개변수 값의 목록을 전달하는 것이 아니라 매개변수를 샘플링할 수 있는 확률분포 객체를 전달함

<확률 분포 클래스 임포트하기>

from scipy.stats import uniform, randint

randint는 정수값을, uniform은 실수 값을 주어진 범위에서 고르게 뽑음

ex) randint 사용법

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

output

(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
 array([113,  97,  99,  89, 111, 104, 101, 100,  91,  95]))

ex) uniform 사용법

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

output

array([0.97348211, 0.86306205, 0.90612032, 0.58106758, 0.69506978,
       0.74482672, 0.640922  , 0.10973736, 0.40981152, 0.7782244 ])

이제 탐색할 매개변수의 딕셔너리를 만들꺼고 여기에선 min_samples_leaf 매개변수를 탐색 대상에 추가할 것임 (=리프노드가 되기 위한 최소 샘플의 개수)

params = {
    'min_impurity_decrease' : uniform(0.0001, 0.001), # 0.0001 ~ 0.001 사이 실숫값 샘플링
    'max_depth' : randint(20, 50), # 20~50 사이의 정수값 샘플링
    'min_samples_split' : randint(2, 25),
    'min_samples_leaf' : randint(1, 25),
}

ㄴ 샘플링 횟수는 RandomizedSearchCVn_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_)

output

{'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

트리의 앙상블

정형 데이터와 비정형 데이터

  • 정형 데이터 : 어떤 구조로 되어있는 데이터들 ex) CSV, 데이터베이스, 엑셀

  • 비정형 데이터 : 데이터베이스나 엑셀로 표현하기 어려운 것들 ex) 음악, 사진

  • 앙상블 학습 : 정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘 -> 이는 대부분 결정 트리를 기반으로 만들어짐

랜덤 포레스트

: 앙상블 학습의 대표 주자로 안정적인 성능을 갖고 있음

ㄴ 결정 트리의 숲을 만드는 것과 비슷!! => 결정 트리의 예측을 사용해서 최종 예측을 만듦


랜덤 포레스트는 각 트리를 훈련하기 위한 데이터를 랜덤하게 만드는데 중복해서 샘플을 만든다 !! (부트스트랩 방식으로)
그리고 각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고른 후 이 중에서 최선의 분할을 찾음

랜덤 포레스트는 랜덤하게 선택한 샘플과 특성을 사용하기 때매 훈련 세트에 과대적합되는 것을 막아주고 검증 세트와 테스트 세트에서 안정적인 성능을 얻을 수 있음

<데이터셋 불러오고 훈련 세트와 테스트 세트로 나누기>

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)

<cross_validate()로 교차 검증 수행하기>

from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_jobs = -1, random_state = 42) # n_jobs = -1로 지정해서 병렬로 교차 검증 수행
scores = cross_validate(rf, train_input, train_target,
                        return_train_score = True, n_jobs = -1)
# return_train_score = True로 지정하면 검증 점수 랑 훈련 세트에 대한 점수도 같이 반환해줌, 얘의 기본 값은 False임
print(np.mean(scores['train_score']), np.mean(scores['test_score']))

output

0.9973541965122431 0.8905151032797809

ㄴ 과대적합됨..

결정 트리의 장점 !! = 특성 중요도를 계산한다 / 랜덤 포레스트의 특성 중요도 = 각 결정 트리의 특성 중요도 취합한 것

<훈련 세트 훈련하고 특성 중요도 출력하기>

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

RandomForestClassifier은 자체적으로 모델을 평가하는 점수를 얻을 수 있다 !!

<OOB점수 평균내서 출력하기>

rf = RandomForestClassifier(oob_score = True, n_jobs = -1, random_state = 42)
rf.fit(train_input, train_target)
print(rf.oob_score_) # 0.8934000384837406

OOB점수를 사용하면 교차 검증을 대신할 수 있어서 훈련 세트에 더 많은 샘플들을 사용할 수 있다 ~~

엑스트라 트리

엑스트라 트리는 랜덤 포레스트와 달리 부트스트랩을 사용하지 않는다 = 결정 트리를 만들 때 전체 훈련 세트를 사용함. 하지만 노드를 분할할 때 무작위로 분할한다 !!

성능은 좀 낮아져도 많은 트리를 앙상블하기 때문에 과대적합을 막고 검증 세트의 점수를 높이는 효과가 있음

<엑스트라트리로 모델 교차 검증점수 확인하기>

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

<특성 중요도>

  • 순서는 [알코올 도수, 당도, pH]이다
et.fit(train_input, train_target)
print(et.feature_importances_) # [0.20183568 0.52242907 0.27573525]

0개의 댓글