2023. 3. 9. 16:24ㆍ카테고리 없음
1. 결정트리
1. 로지스틱 회귀로 와인 분류하기
💻 데이터 컴온
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
wine.head()
📍 info() : 데이터프레임 전체를 돌아다니면서 해당 요약값을 만들어줌 (pandas)
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()
👨🏻💻 결정 트리는 사실 표준화 할 필요 없어!
💻 데이터 넘파이 배열로 바꾸고 훈련 세트와 테스트 세트로 분류
data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()
from sklearn.model_selection import train_test_splittrain_input, test_input, train_target, test_target = train_test_split(data, target, test_size=0.2, random_state=42)
🔎 train_test_split() 함수는 설정값을 지정하지 않으면 25%를 테스트 세트로 지정됨
20%만 테스트 세트로 사용하길 원해서 test_size를 0.2로 지정
💻 만들어진 훈련 세트와 테스트 세트의 크기 확인
print(train_input.shape, test_input.shape)
>>> (5197, 3) (1300, 3)
🔎 실제로 80%에 해당하는 5197개가 train_input에, 20%에 해당하는 1300개는 test_input에 속함을 확인할 수 있다.
💻 표준화 작업 : 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))
print(lr.score(test_scaled, test_target))
>>> 0.7808350971714451
>>> 0.7776923076923077
🔎 훈련 세트와 테스트 세트의 score이 모두 낮으니 과소적합
💻 로지스틱 회귀가 학습한 계수와 절편 출력
print(lr.coef_, lr.intercept_)
>>> [[ 0.51270274 1.6733911 -0.68767781]] [1.81777902]
2. 결정 트리
📍 DecisionTreeClassifier : 사이킷런의 결정 트리 클래스
💻 표준화된 데이터 훈련 및 평가
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
🔎 훈련 데이터에 대한 score 높은데, 그에 비해 낮은 테스트 데이터의 score ➡ 과대적합
📍 plot_tree() 함수 : 결정 트리를 이해하기 쉬운 트리 그림으로 출력, 사이킷런이 제공해주는함수
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()
🔎 맨 위의 노드는 root node, 맨 아래의 노드는 leaf node 라고 부른다.
📍 max_depth : 루트 노드를 제외하고 몇 개의 노드를 보여줄지 지정
filled : True로 설정 시, 노드 색칠
feature_names : 특성의 이름 전달, 생략 시 임의의 변수값을 지정됨
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar','pH'])
plt.show()
📌 지니 불순도 = 1 - (음성 클래스 비율² + 양성 클래스 비율²)
💻 최대 3개의 노드까지만 가지치기
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
🔎 훈련 세트의 score이 변화했지만, 테스트 세트의 성능은 그대로
💻 트리 그래프
plt.figure(figsize=(20,15))
plot_tree(dt,filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
💻 전처리하기 전 데이터로 훈련 및 평가
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
🔎 전처리한 데이터와 결과 동일
💻 트리 생성
plt.figure(figsize=(20,15))
plot_tree(dt,filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
🔎 표준화를 거치지 않았기 때문에, 기준값을 이해하기 편하다.
📍 feture_importances_ 속성 : 특정 중요도
print(dt.feature_importances_)
>>> [0.12345626 0.86862934 0.0079144 ]
🔎 2번째 특성인 당도가 제일 중요하고, 그 다음은 1번째 특성인 알코올, 마지막으로 pH값이 중요함을 확인할 수 있다.
2. 교차 검증과 그리드 서치
1. 검증 세트
💻데이터 읽은 후 타깃 데이터 지정
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))
print(dt.score(val_input, val_target))
>>> 0.9971133028626413
>>> 0.864423076923077
2. 교차 검증 cross validation
📌 보통 5-폴드 교차 검증이나 10-폴드 교차 검증 많이 사용
📍 cross_validate() : 교차 검증 함수. 결과값이 딕셔너리 형태로 만들어짐
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
print(scores)
>>> {'fit_time': array([0.00698662, 0.00614285, 0.00613952, 0.00655127, 0.00845551]),
'score_time': array([0.00071287, 0.00071335, 0.00070405, 0.00131798, 0.00080347]),
'test_score': array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}
🔎 처음 2개의 키는 각각 모델을 훈련하는 시간과 검증하는 시간으로 각 키마다 5개의 숫자가 담겨 있다.
💻 test_score 키에 담긴 5개의 점수를 평균하여 얻는 교차 검증의 최종 점수
import numpy as np
print(np.mean(scores['test_score']))
>>> 0.855300214703487
📍 분할기 splitter : 교차 검증을 할 때, 훈련 세트를 섞기 위해 지정하는 것
from sklearn.model_selection import StratifiedKFold
scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))
>>> 0.855300214703487
🔎 분류 모델인 경우 StratifiedKFold, 회귀 모델인 경우에는 KFold 사용
💻 훈련 세트를 10-폴드 교차 검증 수행하려면 객체를 만들어야 함
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
# 이렇게 한 번에 할 수 있어욥
dt1 = DecisionTreeClassifier()
splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
scores = cross_validate(dt1, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score']))
>>> 0.8587672298799467
🔎 훈련을 거치지 않은 데이터를 넣어도 됨!
3. 하이퍼파라미터 튜닝
📌 하이퍼파라미터 : 머신러닝 모델이 학습하는 파라미터
📍 GridSearchCV : 하이퍼파라미터 탐색과 교차 검증을 한 번에 수행하는 사이킷런의 클래스
💻 탐색할 매개변수와 탐색할 값의 리스트 딕셔너리로 만들기
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)
📍 best_estimator_ : 최적화된 훈련 모델이 저장되어 있음 (결정트리모델)
dt = gs.best_estimator_
print(dt.score(train_input, train_target))
>>> 0.9615162593804117
📍 best_params_ : 최적의 매개변수 저장
print(gs.best_params_)
>>> {'min_impurity_decrease': 0.0001}
📍 mean_test_score : 5번의 교차 검증으로 얻은 점수
print(gs.cv_results_['mean_test_score'])
>>> [0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
📍 argmax : 가장 큰 값의 인덱스 추출 (넘파이꺼)
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
🔎 앞에서 설정한 매개변수의 간격을 더 좁거나 넓은 간격으로 시도할 수 있는 방법은 없을까?
4. 랜덤 서치
📌 매개변수의 값이 수치일 때의 단점
- 값의 범위나 간격을 미리 정하기 어려움
- 너무 많은 매개 변수 조건이 있어서 그리드 서치 수행 시간이 긺
📌 랜덤 서치
: 매개변수 값의 목록을 전달하는 것이 아니라 매개변수를 샘플링 할 수 있는 확률 분포 객체 전달
💻 싸이파이의 확률 분포 클래스 임포트
from scipy.stats import uniform, randint
🔎 uniform은 실수값을 뽑고, randint는 정숫값을 뽑음
💻 0에서 10 사이의 범위를 갖는 randint 객체를 만들기
➡ 객체로 생성되기 때문에 print로 볼 수 없음
➡ .rvs()로 호출해야 함
rgen = randint(0, 10)
rgen.rvs(10) # 10개 뽑아쥬
>>> array([6, 1, 0, 1, 2, 4, 7, 0, 3, 8])
💻 1000개 샘플링 후, 각 숫자 개수 세기
np.unique(rgen.rvs(1000), return_counts=True)
>>> (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
array([ 90, 111, 103, 80, 104, 94, 89, 110, 103, 116]))
🔎 return_counts= 를 통해 각 숫자의 개수를 셌다.
💻 0~1 사이에서 10개의 실수 추출
ugen = uniform(0,1)
ugen.rvs(10)
>>> array([0.07479982, 0.53667649, 0.27615805, 0.03354079, 0.48866032,
0.74964592, 0.53307528, 0.98201321, 0.81466177, 0.87383583])
💻 탐색할 매개변수 딕셔너리 생성
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)}
🔎 탐색 대상에 min_samples_leaf 매개변수 탐색 대상에 추가
💻 샘플링 횟수를 매개변수에 지정
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}
💻 최고의 교차 검증 score
print(np.max(gs.cv_results_['mean_test_score']))
>>> 0.8695428296438884
💻 테스트 세트의 성능 확인
dt = gs.best_estimator_
print(dt.score(test_input, test_target))
>>> 0.86
3. 트리의 앙상블
1. 정형 데이터와 비정형 데이터
📌 정형 데이터 : 형식이 정해져 있다. 표 같은 것
비정형 데이터 : 데이터베이스나 엑셀 같이 정형화 되어 있지 않은 것
👨🏻💻 몽고DB, 마리아 DB는 마음만 먹으면 호딱 배울 수 있어
📌 앙상블 학습 : 정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘
👨🏻💻 딥러닝은 비정형 데이터를 다루는 데 도가 터 있어
2. 랜덤 포레스트
📌 랜덤 포레스트 : 결정 트리의 예측을 통해 최종 예측을 만든다. (숲 안의 트리)
📍 RandomForestClassifier : 기본저긍로 전체 특성 개수의 제곱근만큼의 특성 선택
💻 판다스로 와인 데이터셋을 불러오고, 훈련 세트와 테스트 세트 나누기
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]
🍯 누군가에겐 재밌는 기능
: 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
3. 엑스트라 트리
📌 엑스트라 트리
- 결정 트리가 제공하는 대부분의 매개변수 지원
- 전체 특성 중, 일부 특성을 랜덤하게 선택하여 노드 분할하는 데 사용
- 랜덤 포레스트와의 차이점
- 부트스트랩 샘플을 사용하지 않음
➡ 각 결정 트리를 만들 때 전체 훈련 세트 사용
➡ 성능은 낮아지겠지만, 많은 트리를 앙상블 하기 때문에 과대적합 막고 검증 세트의 점수 높일 수 있음
- 부트스트랩 샘플을 사용하지 않음
- ExtraTreesClassfier : 사이킷런에서 제공하는 엑스트라 트리
ExtraTreesRegressor 클래스 : 엑스트라 트리의 회귀 버전
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]
🔎 순서는 [알코올 도수, 당도, pH].
엑스트라 트리도 결정 트리보다 당도에 대한 의존성이 작음
4. 그레이디언트 부스팅
📌 그레이디언트 부스팅
- 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식으로 앙상블
- 깊이가 얕은 결정 트리 사용
- 과대적합에 강함
- 일반적으로 높은 일반화 성능 기대 가능
💻 GradientBoostring Classifier를 사용해 와인 데이터셋의 교차 검증 점수 확인
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
💻 학습률 500으로 증가
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
🔎 결정 트리 개수를 5배나 늘렸는데도, 과대적합을 작 억제한다.
💻 특성 중요도 확인
gb.fit(train_input, train_target)
print(gb.feature_importances_)
>>> [0.15872278 0.68010884 0.16116839]
🔎 랜덤 포레스트보다 일부 특성(당도)에 더 집중
5. 히스토그램 기반 그레이디언트 부스팅
📌 히스토그램 기반 그레이디언트 부스팅
- 정형 데이터를 다루는 머신러닝 알고리즘 중에 가장 인기가 높은 알고리즘
- 입력 특성을 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
🔎 과대 적합을 잘 억제하면서 그레이디언트 부스팅보다 조금 더 높은 성능 제공..
성능의 판단 기준은 훈련 데이터와 테스트 데이터 평가 결과의 차인 듯?
📍 permutation_importance() 함수 : 히스토그램 기반 그레이디언트 부스팅의 특성 중요도 계산
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 ]
📍 HistGradientBoostingClassifier : 테스트 세트에서의 성능 확인
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, n_jobs=-1)
print(np.mean(scores['train_score']), np.mean(scores['test_score']))
>>> 0.9555033709953124 0.8799326275264677
📍 LightGBM
: 그레이디언트 부스팅 알고리즘을 구현한 라이브러리
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.9338079582727165 0.8789710890649293