GitHub - rickiepark/hg-mldl: <혼자 공부하는 머신러닝+딥러닝>의 코드 저장소입니다.
<혼자 공부하는 머신러닝+딥러닝>의 코드 저장소입니다. Contribute to rickiepark/hg-mldl development by creating an account on GitHub.
github.com
혼자 공부하는 머신러닝+딥러닝
이 책은 수식과 이론으로 중무장한 머신러닝, 딥러닝 책에 지친 ‘독학하는 입문자’가 ‘꼭 필요한 내용을 제대로’ 학습할 수 있도록 구성 되어 있습니다. 구글 머신러닝 전문가(Google ML expert)
www.youtube.com
Chapter 05
5-1. 결정 트리
- 결정 트리(Decision Tree) : 질문을 하나씩 던져 정답을 맞춰가며 학습하는 알고리즘으로 비교적 예측 과정을 이해하기 쉬움.
- 로지스틱 회귀로 와인 분류하기
#데이터 불러오기
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) # 테스트 세트 20%로 설정
# 훈련 세트와 테스트 세트의 크기 확인
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)
표준점수로 변환된 train_scaled 와 test_scaled를 사용해 로지스틱 회귀 모델 훈련
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
# 로지스틱 회귀가 학습한 계수와 절편 출력
print(lr.coef_, lr.intercept_)
- 결정 트리
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
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()
- 불순도
지니 불순도(Gini impurity)
순수 노드: 지니 불순도가 0인 노드
결정 트리 모델은 부모 노드와 자식 노드의 불순도 차이(정보 이득)가 가능한 크도록 트리를 성장시킴
엔트로피(entropy)
- 가지치기
자라날 수 있는 트리의 최대 깊이 지정
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()
결정 트리는 표준화 전처리를 할 필요가 없음!
# 전처리 전 데이터로 다시 훈련
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()
#어떤 특성이 가장 유용한지 나타내는 특성 중요도를 계산해줌
print(dt.feature_importances_)
[0.12345626 0.86862934 0.0079144 ]
5-2. 교차 검증과 그리드 서치
- 검증 세트(validation set) : 하이퍼파라미터 튜닝을 위해 모델을 평가할 때, 테스트 세트를 사용하지 않기 위해 훈련 세트에서 다시 떼어 낸 데이터 세트.
- 교차 검증(cross validation) : 훈련 세트를 여러 폴드로 나눈 다음 한 폴드가 검증 세트의 역할을 하고 나머지 폴드에서는 모델을 훈련함. 나눈 모든 폴드에 대해 검증 점수를 얻어 평균하는 방법으로 교차 검증을 이용하면 검증 점수가 안정적이며, 훈련에 더 많은 데이터를 사용할 수 있음.
- 그리드 서치(Grid Search) : 하이퍼파라미터 탐색을 자동화해 주는 도구
- 랜덤 서치(Random Search) : 연속적인 매개변수 값을 탐색할 때 유용
- 검증 세트
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
#class 열을 타깃으로 사용하고 나머지 열은 특성 배열에 저장
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
- 교차 검증
사이킷런의 cross_validate() 교차 검증 함수
rom 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
# 사이킷런의 분할기 -> 교차 검증에서 폴드를 어떻게 나눌지 결정. StratifiedKFold 사용
from sklearn.model_selection import StratifiedKFold
scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))
0.855300214703487
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
- 하이퍼파라미터 튜닝
사이킷런의 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)
gs.fit(train_input, train_target)
검증 점수가 가장 높은 모델의 매개변수 조합으로 전체 훈련 세트에서 자동으로 다시 모델을 훈련함
-> 이 모델은 gs 객체의 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}
#평균 검증 점수
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
- 랜덤 서치
싸이파이(scipy) : 적분, 보간, 선형 대수, 확률 등을 포함한 수치 계산 전용 라이브러리
#확률 분포 클래스 임포트 - 주어진 범위에서 고르게 값을 뽑음(균등 분포에서 샘플링)
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),
}
# 샘플링 횟수 지정-> 교차 검증 -> 최적의 매개변수 조합 찾기
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
5-3. 트리의 앙상블
- 정형 데이터(structured date) : 특정 구조로 이루어진 데이터(csv / 데이터베이스)
- 비정형 데이터(unstructured data) : 정형화되기 어려운 사진이나 음악 등의 데이터
- 앙상블 학습(ensemble learning) : 여러 알고리즘을 합쳐서 성능을 높이는 머신러닝 기법
- 랜덤 포레스트(Random Forest) : 대표적인 결정 트리 기반의 앙상블 학습 방법. 안정적인 성능 덕분에 널리 사용됨. 부트스트랩 샘플을 사용하고 랜덤하게 일부 특성을 선택하여 트리를 만드는 것이 특징.
- 부트스트랩 샘플(bootstrap sample) : 데이터 세트에서 중복을 허용하여 데이터를 샘플링하는 방식
- 랜덤포레스트
각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고른 다음 그 중에서 최선의 분할을 찾음.
기본적으로 전체 특성 개수의 제곱근만큼의 특성을 선택.
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) #n_jobs = -1 : 모든 CPU 코어 사용
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]
-> 하나의 특성에 과도하게 집중하지 않고 좀 더 많은 특성이 훈련에 기여할 기회를 얻음.
OOB(out of bag) 샘플 : 부트스트랩 샘플에 포함되지 않고 남는 샘플. 이것을 사용해 부트스트랩 샘플로 훈련한 결정 트리 평가 가능(검증 세트 역할)
#각 결정 트리의 OOB 점수 평균하여 출력
rf = RandomForestClassifier(oob_score=True, n_jobs=-1, random_state=42)
rf.fit(train_input, train_target)
print(rf.oob_score_)
0.8934000384837406
- 엑스트라 트리
- 엑스트라 트리(extra trees) : 랜덤 포레스트와 비슷하게 동작하며 결정 트리를 사용하여 앙상블 모델을 만들지만 브트스트랩 샘플을 사용하지 않는 대신 랜덤하게 노드를 분할하여 과대적합을 감소시킴.
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]
- 그레이디언트 부스팅
- 그레이디언트 부스팅(gradient boosting) : 깊이가 얕은 결정트리를 사용하여 이전 트리의 오차를 보완하는 방식으로 앙상블하는 방법. 깊이가 얕은 결정 트리를 사용하기 때문에 과대적합에 강하고 일반적으로 높은 일반화 성능을 기대할 수 있음.
- 분류에서는 로지스틱 손실 함수, 회귀에서는 평균 제곱 오차 함수를 사용
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]
-> 랜덤 포레스트보다 일부 특성에 집중
-> 랜덤 포레스트보다 더 높은 성능, but 속도 느림
- 히스토그램 기반 그레이디언트 부스팅
- 히스토그램 기반 그레이디언트 부스팅(Histogram-baesd Gradient Boosting) : 그레이디언트 부스팅의 속도를 개선한 것으로 과대적합을 잘 억제하며 그레이디언트 부스팅보다 조금 더 높은 성능을 제공. 안정적인 결과와 높은 성능으로 매우 인기가 높음.
- 먼저 256개의 구간으로 입력 특성을 나눔 -> 최적의 분할을 매우 빠르게 찾을 수 있음
# 사이킷런 1.0 버전 아래에서는 다음 라인의 주석을 해제하고 실행하세요.
# 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, 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)
# hgb.fit(train_input, train_target)
# print(rf.feature_importances_)
[0.08876275 0.23438522 0.08027708]
hgb.score(test_input, test_target)
0.8723076923076923
사이킷런 외 히스토그램 기반 그레이디언트 부스팅 알고리즘 구현한 라이브러리
- XGBoost
tree_method 매개변수 'hist' 로 지정해서 사용 가능.
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.935828414851749 0.8801251203079884
'혼공 ML+DL' 카테고리의 다른 글
5주차 Chapter 06 (비지도 학습) (0) | 2024.02.27 |
---|---|
3주차 Chapter 04 (다양한 분류 알고리즘) (1) | 2024.02.13 |
2주차 Chapter 03 (회귀 알고리즘과 모델 규제) (1) | 2024.02.06 |
1주차 Chapter 01 (나의 첫 머신러닝), Chapter 02 (데이터 다루기) (0) | 2024.01.31 |