촉촉한초코칩
[혼공머신] Ch05 본문
https://colab.research.google.com/drive/1sfIIY16vBHCjrX5VPn4HrCZR5UYLzgpy#scrollTo=D4p969zjRt35
Ch05. 트리 알고리즘
화이트 와인 찾기
- 성능이 좋고 이해하기 쉬운 트리 알고리즘을 배운다.
- 알고리즘의 성능을 최대화하기 위해 하이퍼파라미터 튜닝을 실습한다.
- 여러 트리를 합쳐 일반화 성능을 높일 수 있는 앙상블 모델을 배운다.
05-1 결정 트리
결정 트리 알고리즘을 사용해 새로운 분류 문제 다루기
문제 : 알코올 도수, 당도, pH 값으로 와인 종류 구별하기
import pandas as pd
wine = pd.read_csv("https://bit.ly/wine_csv_data")
#알코올 도수, 당도, pH, 타깃값 (0이면 레드와인, 1이면 화이트와인)
# alcohol sugar pH class
# 0 9.4 1.9 3.51 0.0
# 1 9.8 2.6 3.20 0.0
# 2 9.8 2.3 3.26 0.0
이진 분류 문제 : 레드/화이트와인 구분
→ 화이트와인이 양성 클래스이므로 전체 와인 데이터에서 화이트와인을 골라내면 된다.
#데이터프레임의 각 열 데이터타입과 누락된 데이터가 있는지 확인
#전체 샘플 데이터 개수와 Non-Null Count 값이 같으므로 누락된 값이 없다는 것을 알 수 있다.
wine.info()
누락된 값이 있다면 그 데이터를 버리거나 / 평균값으로 채운 후 사용한다.
→ 훈련 세트의 통계 값(평균값)으로 테스트 세트를 변환(누락된 값 채우기)해야 한다.
#열에 대한 간략한 통계 출력
wine.describe()
알코올 도수, 당도, pH 값의 스케일이 다르기 때문에 특성을 표준화한다.
#훈련/데이터 세트 나누기
#처음 3개의 열을 넘파일 배열로 바꿔서 data 배열에 저장, 마지막 class 열을 넘파일 배열로 바꿔서 target 배열에 저장
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)
* train_test_split() : 설정값을 지정하지 않으면 25%를 테스트 세트로 지정한다.
훈련 세트 전처리 후 테스트 세트 변환
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)) #0.7808350971714451
print(lr.score(test_scaled, test_target)) #0.7776923076923077
훈련/테스트 세트 점수 모두 낮음 → 모델 과소적합
해결방법 : 규제 매개변수의 C 값 바꾸거나, solver 매개변수에서 다른 알고리즘 선택, 다항 특성 만들어서 추가
로지스틱 회귀가 학습한 계수와 절편 출력하기
print(lr.coef_, lr.intercept_)
#[[ 0.51270274 1.6733911 -0.68767781]] [1.81777902]]
#알코올 도수 값에 0.5127..를 곱하고 당도에는 1.6733.., pH값에 -0.6876..을 곱하고 모두 더한다.
#마지막으로 1.8177..을 더한다.
#이 값이 0보다 크면 화이트와인, 적으면 레드와인으로 분류된다.
결정 트리 : 질문을 하나씩 던져서 정답과 맞춰가는 것
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() 함수 사용해서 결정 트리를 이해하기 쉬운 트리 그림으로 만들기
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()
루트 노드
- 당도(sugar)가 -0.239 이하인지 질문 → True일 때 왼쪽, False일 때 오른쪽
- samples 개수 519개 중 음성 클래스(레드 와인)은 1258개, 양성 클래스(화이트 와인)은 3939개이다.
왼쪽 노드
- 당도(sugar)가 -0.802 이하인지 질문 → True일 때 왼쪽, False일 떄 오른쪽
- 음성 클래스(레드) : 1177개, 양성 클래스(화이트) : 1745개 → 루트보다 양성 클래스의 비율이 줄어들었다.
오른쪽 노드
- 음성 클래스(레드) : 81개, 양성 클래스(화이트) : 2194개
결정 트리에서 예측하는 방법 : 리프 노드에서 가장 많은 클래스가 예측 클래스가 된다.
→ 여기서 멈추게 되면 왼쪽 노드와 오른쪽 노드에 도달한 샘플 모두 양성 클래스로 예측된다. (양성클래스 개수가 더 많기 때문)
→ 만약 결정 트리를 회귀 문제에 적용하면 리프 노드에 도달한 샘플의 타깃을 평균하여 예측값으로 사용한다.
불순도 (Gini Impurity)
- DecisionTreeClassifier 클래스의 criterion 매개변수의 기본 값
- criterion 매개변수의 용도 : 노드에서 데이터를 분할할 기준을 정하는 것
→ 앞의 노드에서 당도 -0.239를 기준으로 나눈 것과 동일 - 지니 불순도 계산하는 방법 : 클래스의 비율을 제곱해서 더한 다음 1에서 뺀다.
지니 불순도 = 1 - (음성 클래스 비율^2 + 양성 클래스 비율^2)
→ 1 - ((1258 / 5197)^2 + (3939/5197)^2) = 0.367 - 순수 노드 : 노드에 하나의 클래스가 있어 0이 되는 것
결정 트리 모델 : 부모 노드와 자식 노드의 불순도 차이가 가능한 크도록 트리 성장
부모 노드와 자식 노드의 불순도 차이 계산하는 방법 : 자식 노드의 불순도를 샘플 개수에 비례하여 모두 더한 다음 부모 노드의 불순도에서 뺀다.
→ 부모의 불순도 - (왼쪽 노드 샘플 수 / 부모 샘플 수) * 왼쪽 노드 불순도 - (오른쪽 노드 샘플 수 / 부모 샘프 수) * 오른쪽 노드 불순도
→ 정보 이득 (Information Gain) : 부모와 자식 노드 사이의 불순도 차이
DecisionTreeClassifier 클래스의 엔트로피 불순도
- 노드의 클래스 비율을 사용하며 밑이 2인 로그를 사용하여 곱한다.
- 계산하는 방법 : -음성 클래스 비율 * log2(음성 클래스 비율) - 양성 클래스 비율 * log2(양성 클래스 비율)
- 보통 기본값인 지니 불순도와 엔트로피 불순도 결과 차이는 크지 않다.
결론
- 불순도 기준을 사용해 정보 이득이 최대가 되도록 노드를 분할한다.
- 노드를 순수하게 나눌수록 정보 이득이 커진다.
- 새로운 샘플에 대해 예측할 때에는 노드의 질문에 따라 트리를 이동한다.
- 마지막에 도달한 노드의 클래스 비율을 보고 예측을 만든다.
가지치기
- 가지치기를 해야 무작정 자라나는 트리를 멈출 수 있다. → 테스트 세트 성능이 떨어지게 됨 → 일반화가 잘 되지 않음
- 가지치기 하는 방법 : 트리의 최대 깊이 지정하기
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
plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol','sugar','pH'])
plt.show()
훈련 세트 성능은 낮아졌지만 테스트 성능은 그대로다.
레드와인으로 예측하려면
- 당도 : -0.239~-0.802
- 알코올 도수 : 0.454보다 작아야 함
문제점
- 불순도를 기준으로 샘플 나눔
- 불순도는 클래스별 비율을 가지고 계산
- 샘플을 어떤 클래스 비율로 나누는지 계산할 때 특성값의 스케일이 계산에 영향을 미칠까? → No
특성값의 스케일은 결정 트리 알고리즘에 영향을 미치지 않으므로 표준화 전처리를 할 필요가 없다.
전처리하기 전 훈련/테스트 세트로 결정 트리 모델 다시 훈련
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와 같거나 작을 때
- 화이트와인 : 그 외
특성 중요도 계산
- 특성 중요도 : 어떤 특성이 가장 유용한지 나타낸다.
- 각 노드의 정보 이득과 전체 샘플에 대한 비율을 곱한 후 특성별로 더하여 계산한다.
print(dt.feature_importances_)
# 알코올도수 당도 pH
#[0.12345626 0.86862934 0.0079144 ] --> 당도가 가장 높다.
정리
- 알코올 도수, 당도, pH 데이터를 기준으로 화이트 와인을 골라내는 이진 분류 로지스틱 회귀 모델 훈련
- 문제점 : 이해하기 어려움
- 해결 방법 : 결정 트리 사용하여 레드 와인과 화이트 와인 분류 문제 풀기
- 결정 트리
- 특성을 더 추가하지 않아도 로지스틱 회귀 모델보다 성능이 좋음
- 깊이가 너무 깊지 않다면 비교적 설명하기 쉬움
- 불순도 개념과 정보 이득, 특성 중요도 - 불순도 : 결정 트리가 최적의 질문을 찾기 위한 기준
- 정보 이득 : 부모 노드와 자식 노드의 불순도 차이 → 정보 이득이 최대화되도록 학습
- 가지치기 : 결정 트리의 성장 제한
- 특성 중요도 : 결정 트리에 사용된 특성이 불순도를 감소하는데 기여한 정도를 나타내는 값
05-2 교차 검증과 그리드 서치
검증 세트가 필요한 이유를 이해하고 교차 검증에 대해 배운다.
그리드 서치와 랜덤 서치를 이용해 최적의 성능을 내는 하이퍼파라미터를 찾는다.
문제 : 테스트 세트를 사용해 성능을 확인하다 보면 점점 테스트 세트에 맞추게 된다.
해결방법 : 테스트 세트로 일반화 성능을 올바르게 예측하려면 가능한 테스트 세트를 사용하지 말아야 한다. (마지막에 한번만 사용하는 것이 좋다.)
검증세트 (Validation Set)
- 테스트 세트를 사용하지 않고 훈련 세트를 또 나눈다.
- 훈련 세트에서 모델을 훈련하고 검증세트로 모델을 평가한다. → 테스트 하고 싶은 매개변수를 바꿔가며 가장 좋은 모델을 고른다.
- 매개변수를 사용하여 훈련세트+검증세트를 합쳐 전체 훈련 데이터에서 모델을 다시 훈련한다.
- 테스트 세트에서 최종 점수를 평가한다.
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)
#train_input과 train_target을 다시 train_test_split()함수에 넣어 훈련세트 sub_input, sub_target과 검증세트 val_input, val_target을 만든다.
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)
#모델 만들고 평가하기
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
→ 검증 세트를 만들면 훈련 세트가 줄어든다. 보통 많은 데이터를 훈련에 사용할수록 좋은 모델이 만들어진다.
→ 훈련 세트에 과대적합되어 있음 매개변수를 바꾼다!
교차 검증 (Cross validation)
- 안정적인 검증 점수를 얻고 훈련에 더 많은 데이터를 사용한다.
- 검증세트를 떼어 내어 평가하는 과정을 여러 번 반복한다. 그 다음 점수를 평균하여 최종 검증 점수를 얻는다.
* 폴드 교차 검증(k-폴드 교차 검증) : 훈련 세트를 세 부분으로 나눠서 교차 검증을 수행하는 것 (훈련 세트를 몇 부분으로 나누냐에 따라 다르게 부른다.) → 데이터의 80~90%까지 훈련에 사용할 수 있다.
#cross_validate() 교차 검증 함수 사용
#평가할 모델 객체를 첫번째 매개변수로 전달한다.
#직접 검증 세트를 떼어 내지 않고 훈련 세트 전체를 cross_validate() 함수에 전닳나다.
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
print(scores)
#처음 2개 키는 각각 모델을 훈련하는 시간과 검증하는 시간을 의미한다.
#5개의 숫자 = 5-폴드 교차 검증 수행
# {'fit_time': array([0.01119566, 0.01033807, 0.01084685, 0.01071763, 0.01045966]),
#'score_time': array([0.00186539, 0.00176477, 0.00162721, 0.0017283 , 0.00176835]),
#'test_score': array([0.86923077, 0.84615385, 0.87680462, 0.84889317, 0.83541867])}
#교차 검증 최종 점수는 test_score 키에 담긴 5개의 점수를 평균하여 얻을 수 있다.
import numpy as np
print(np.mean(scores['test_score'])) #0.855300214703487
* cross_validate()는 훈련 세트를 섞어 폴드를 나누지 않는다.
train_test_split() 함수로 전체 데이터를 섞은 후 훈련 세트를 준비했기 때문에 섞을 필요가 없지만 교차 검증을 할 때 훈련 세트를 섞으려면 분할기(Spliter)를 지정해야 한다.
#사이킷런의 분할기 : 교차 검증에서 폴드를 어떻게 나눌지 결정해준다.
#cross_validate() : 기본적으로 회귀 모델일 경우 KFold 분할기를 사용하고 분류 모델일 경우 타깃 클래스를 골고루 나누기 위해 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)
scors = cross_validate(dt, train_input, train_target, cv=splitter)
print(np.mean(scores['test_score'])) #0.855300214703487
결정 트리의 매개변수 바꿔가면서 가장 좋은 성능이 나오는 모델 찾아보기 → 테스트 세트를 사용하지 않고 교차 검증을 통해 좋은 모델을 고른다.
하이퍼파라미터 튜닝
- 하이퍼파라미터 : 사용자가 지정해야만 하는 파라미터
- 라이브러리가 제공하는 기본 값을 그대로 사용해 모델 훈련한다.
검증 세트의 점수나 교차 검증을 통해 매개변수를 조금씩 바꾼다. - 문제점 : 두개의 매개변수를 동시에 바꿔가며 최적의 값을 찾아야할 수 있다. 또한 매개변수가 많아지면 문제가 복잡해진다.
- 해결방법 : 그리드 서치(Grid Search) 사용하여 매개변수가 여러 개일 때 자동으로 최적의 값을 찾아주게 한다.
GridSearchCV : 하이퍼파라미터 탐색과 교차 검증을 한번에 수행한다.
- 탐색할 매개변수와 탐색할 값의 리스트를 딕셔너리로 만든다.
- fit() : 그리드 서치 객체는 결정 트리 모델 min_impurity_decrease 값을 바꿔가며 총 5번 실행한다.
GridSearchCV의 cv 매개변수 기본 값은 5이므로, min_impurity_decrease 값마다 5-폴드 교차 검증 수행→ 5*5 = 25개의 모델 훈련 - GridSearchCV 클래스의 n_jobs 매개변수에서 병렬 실행에 사용할 CPU 코어 수 지정 (기본값 1) → -1로 지정하면 시스템에 있는 모드 코어 사용
#최적의 하이퍼파라미터 찾기
from sklearn.model_selection import GridSearchCV
#1. 탐색할 매개변수 지정
params = {'min_impurity_decrease':[0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}
#2. 훈련 세트에서 그리드 서치를 수행하여 최상의 평균 검증 점수가 나오는 매개변수 조합 찾기 -> 그리드 서치 객체에 저장
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)
#3. 25개의 모델 중에서 검증 점수가 가장 높은 모델의 매개변수 조합으로 전체 훈련 세트에서 자동으로 다시 모델을 훈련한다
dt = gs.best_estimator_
print(dt.score(train_input, train_target)) #0.9615162593804117
#최적의 매개변수는 best_params_ 속성에 저장되어 있다.
print(gs.best_params_) #{'min_impurity_decrease': 0.0001}
#각 매개변수에서 수행한 교차 검증의 평균 점수 : cv_results_ 속성의 mean_test_score 키에 저장되어 있다.
print(gs.cv_results_['mean_test_score']) #[0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
#numpy.argmax() : 가장 큰 값의 인덱스 추출
best_index = np.argmax(gs.cv_results_['mean_test_score'])
#인덱스 사용하여 params 키에 저장된 매개변수 출력 --> 최상의 검증 점수를 만든 매개변수 조합
print(gs.cv_results_['params'][best_index]) #{'min_impurity_decrease': 0.0001}
조금 더 복잡한 매개변수 조합 탐색하기
- min_impurity_decrease : 노드를 분할하기 위한 불순도 감소 최소량 지정
- max_depth : 트리 깊이 제한
- min_samples_split : 노드를 나누기 위한 최소 샘플 수 고르기
#넘파이 arange() : 첫번째 매개변수 값에서 시작~두번째 매개변수에 도달할 때까지 세번째 매개변수를 계속 더한 배열 만들기
#파이썬 range() : arange()와 동일하지만 정수만 사용 가능
#min_samples_split() : arange()와 동일
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 * 15 * 20 = 1350개
#5-폴드 교차 검증 수행 -> 만들어지는 총 모델 개수 : 6750개
#그리드 서치 실행
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
문제점 : 매개변수 간격 어떻게 설정?
랜덤 서치 (Random Search) RandomizedSearchCV
- 매개변수를 샘플링할 수 있는 확률 분포 객체를 전달한다.
- 랜덤 서치에 randint과 uniform 클래스 객체를 넘겨주고 총 몇번을 샘플링해서 최적의 매개변수를 찾도록 한다.
#uniform, randint : 주어진 범위에서 고르게 값을 뽑는다. = 균등 분포에서 샘플링한다.
from scipy.stats import uniform, randint
#탐색할 매개변수의 딕셔너리 만들기
#min_samples_leaf 매개변수를 탐색 대상에 추가 (리프 노드가 되기 위한 최소 샘플의 수) -> 자식 노드의 샘플 수가 이 값보다 작을 경우 분할하지 않는다.
params = {'min_impurity_decrease':uniform(0.0001, 0.0001),
'max_depth':randint(20,50),
'min_samples_split':randint(2,25),
'min_samples_leaf':randint(1,25),
}
#샘플링 횟수는 n_iter 매개변수에 저장한다.
#params에 정의된 매개변수 범위에서 총 100번(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': 47, 'min_impurity_decrease': 0.00015788648955075588,
#'min_samples_leaf': 23, 'min_samples_split': 23}
#최고의 교차 검증 점수 확인
print(np.max(gs.cv_results_['mean_test_score'])) #0.8687702672688236
#최적 모델은 이미 전체 훈련 세트로 훈련되어 best_estimator_에 저장되어 있다.
#이 모델을 최종 모델로 결정하고 테스트 세트의 성능을 확인한다.
dt = gs.best_estimator_
print(dt.score(test_input, test_target)) #0.8569230769230769
정리 [최적의 모델을 위한 하이퍼파라미터 탐색]
- 문제 : 테스트 세트를 사용하지 않고 모델을 평가하기 → 훈련 세트 중 일부를 덜어내어 만듦 = 검증 세트/개발 세트
- 교차 검증 : 검증 세트를 여러번 나눠서 모델을 평가하는 것
나누어진 한 덩어리를 폴드라고 부르며 한 폴드씩 돌아가면서 모델을 평가한다. (5 or 10)
최종 검증 점수 = 모든 폴드의 검증 점수 평균하여 계산 - 하이퍼파라미터 : 교차 검증을 사용해 다양한 하이퍼파라미터 탐색
클래스와 메서드의 매개변수를 바꾸어 모델을 훈련하고 평가한다. → 테스트 하고 싶은 매개변수 리스트를 만들어서 이 과정을 자동화하는 그리드 서치 사용
매개변수 값이 수치형이고 연속적인 실수값이라면 싸이파이의 확률 분포 객체를 전달하여 특정 범위 내에서 지정된 횟수만큼 매개변수 후보 값을 샘플링(랜덤 서치)하여 교차 검증 시도
05-3 트리의 앙상블
앙상블에 대해 이해하고 다양한 앙상블 학습 알고리즘 실습을 통해 배우기
문제 : 베스트 머신러닝 알고리즘 찾기 → 랜덤 포레스트 (Random Forest) 알고리즘
정형 데이터와 비정형 데이터
- 정형 데이터 (Structured Data) : 어떤 구조로 되어 있는 데이터 CSV, 데이터베이스 ,엑셀에 저장하기 쉽다.
→ 앙상블 사용 (결정 트리를 기반으로 만들어짐)하면 성능이 좋아진다. - 비정형 데이터 (Unstructured Data) : 데이터베이스나 엑셀로 표현하기 어려운 데이터 텍스트 데이터, 사진, 음악 등
→ 신경망 알고리즘 사용
랜덤 포레스트 (Random Forest)
- 앙상블 학습의 대표적인 알고리즘
- 결정 트리를 랜덤하게 만들어서 결정 트리의 숲을 만든다. 그리고 각 결정 트리의 예측을 사용해 최종 예측을 만든다.
- 각 트리를 훈련하기 위한 데이터를 랜덤하게 만든다.
→ 훈련 데이터에서 랜덤하게 샘플 추출하여 훈련 데이터를 만든다. (중복될 수 있음) = 부트스트랩 샘플 (Bootstrap Sample) - 각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고른 다음 이 중에서 최선의 분할을 찾는다.
RandomForestClassifier : 전체 특성 개수의 제곱근만큼 특성을 선택한다. → 트리의 클래스별 확률 평균하여 가장 높은 확률을 가진 클래스 선택
RandomForestRegressor : 전체 특성 사용 → 단순히 각 트리의 예측 평균 - 랜덤하게 선택한 샘플과 특성을 사용하기 때문에 훈련 세트에 과대적합되는 것을 막아주고 검증/테스트 세트에서 안정적인 성능을 얻을 수 있다.
* 분류 : 샘플을 몇 개의 클래스 중 하나로 분류
회귀 : 임의의 어떤 숫자 예측
#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)
cross_validate() : 교차 검증 수행
- n_jobs : -1로 지정하여 모든 CPU 코어를 사용하고 최대한 병렬로 교차 검증 수행
- 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 = 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
→ 훈련 세트에 과대적합 됨
특성 중요도 출력
- DecisionTreeClassifier는 중요한 매개변수를 모두 제공한다.
- 랜덤 포레스트의 특성 중요도 = 각 결정트리의 특성 중요도 취합
rf.fit(train_input, train_target)
print(rf.feature_importances_)
# 알코올 도수, 당도, pH
#결정트리에서 만든 특성 중요도 : 0.12345626, 0.8686, 0.0079
#랜덤포레스트 만든 특성 중요도 : 0.23167441 0.50039841 0.26792718
- 랜덤 포레스트가 특성의 일부를 랜덤하게 선택하여 결정 트리를 훈련했기 때문에 그냥 훈련한 값의 특성 중요도와는 다르다.
OOB (Out of Bag)
- 부트스트랩 샘플에 포함되지 않고 남는 샘플 → 남는 샘플을 사용하여 부트스트랩 샘플로 훈련한 결정 트리를 평가한다. (검증 세트 역할)
- oob_score 매개변수를 True로 지정하고 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) ExtraTreesClassifier
- 랜덤 포레스트와 동일하게 결정 트리가 제공하는 대부분의 매개변수를 지원하며 전체 특성 중 일부 특성을 랜덤하게 선택하여 노드를 분할하는 데 사용한다.
- 랜덤 포레스트와 엑스트라 트리의 차이점 : 부트스트랩 샘플 사용하지 않음
→ 각 결정 트리를 만들 때 전체 훈련 세트 사용. 대신 노드를 분할할 때 무작위로 분할한다. - 장점 : 많은 트리를 앙상블 하기 때문에 과대적합을 막고 검증 세트의 점수를 높일 수 있다. 랜덤하게 노드를 분할하기 땜누에 계산 속도가 빠르다.
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_)
#알코올 도수, 당도, pH
#[0.20183568 0.52242907 0.27573525]
엑스트라 트리의 회귀 버전 : ExtraTreesRegressor 클래스
그레이디언트 부스팅 (Gradient Boosting) GradientBoostingClassifier
- 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식의 앙상블 방법
- 장점 : 결정 트리 개수를 늘려도 과대적합에 강하고 높은 일반화 성능 → 학습률을 증가시키고 트리 개수를 늘리면 조금 더 향상될 수 있다.
- 회귀 : 경사 하강법을 사용하여 트리를 앙상블에 추가한다. → 평균제곱오차함수사용
분류 : 로지스틱 손실 함수 사용 - 경사 하강법 : 손실 함수를 산으로 정의하고 가장 낮은 곳을 찾아 내려오는 과정 → 모델의 가중치와 절편 바꾸기
그레이디언트 부스팅 : 결정 트리를 계속 추가하면서 가장 낮은 곳을 찾아 이동 → 학습률 매개변수로 속도 조절
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_)
#알코올도수, 당도, pH
#[0.15872278 0.68010884 0.16116839]
subsample 매개변수
- 트리 훈련에 사용할 훈련 세트 비율 정할 때 사용
- 기본값 : 1.0 → 전체 훈련 세트 사용
1보다 작을 때 → 훈련 세트의 일부 사용
정리
- 장점 : 그레이디언트 부스팅이 랜덤 포레스트보다 성능 높음
- 단점 : 순서대로 트리를 추가하기 때문에 훈련 속도가 느리다. → 그레이디언트 부스팅의 회귀 버전 사용 (GradientBoostingRegressor)
히스토그램 기반 그레이디언트 부스팅 (Histogram-based Gradient Boosting) HistGradientBoostingClassifier
- 입력 특성을 256개의 구간으로 나누고 그 구간 중에서 하나를 떼어 놓고 누락된 값을 위해 사용한다.
- 장점 : 노드를 분할할 때 최적의 분할을 매우 빠르게 찾을 수 있다. 입력에 누락된 특성이 있어도 따로 전처리할 필요가 없다.
#max_iter : 트리 개수 지정(부스팅 반복 횟수 지정)
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() 함수 사용
- 특성을 하나씩 램덤하게 섞어서 모델의 성능이 변화하는지 관찰하여 어떤 특성이 중요한지 계산한다.
- 반환하는 객체 : 특성 중요도(importances), 평균(importances_mean), 표준 편차(importances_std)
#히스토그램 기반 그레이디언트 부스팅 모델 훈련하고 훈련 세트에서 특성 중요도를 계산한다.
#n_repeats : 랜덤하게 섞을 횟수 지정
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)
#알코올 도수, 당도, pH
#[0.05969231 0.20238462 0.049 ]
#테스트 세트의 성능 확인
hgb.score(test_input, test_target) #0.8723076923076923
히스토그램 기반 그레이디언트 부스팅 회귀 버전 → HistGradientBoostingRegressor 클래스
그레이디언트 부스팅 알고리즘 구현한 라이브러리
- XGBoost (cross_validate() 함수도 사용 가능)
- LightGBM
#tree_method 매개변수 : hist로 지정하면 히스토그램 기반 그레이디언트 부스팅 사용 가능
#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.9558403027491312 0.8782000074035686
#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
정리 [앙상블 학습을 통한 성능 향상]
- 앙상블 : 정형 데이터에서 뛰어난 성능을 내는 머신러닝 알고리즘 (결정 트리 기반)
- 대표적인 앙상블 학습 : 사이킷런 (랜덤 포레스트, 엑스트라 트리, 그레이디언트 부스팅, 히스토그램 기반 그레이디언트 부스팅)
랜덤 포레스트 : 부트스트랩 샘플을 만들어 전체 특성 중 일부를 랜덤하게 선택하여 결정 트리를 만듦
엑스트라 트리 : 결정 트리의 노드를 랜덤하게 분할
그레이디언트 부스팅 : 얕은 결정 트리 연속 추가하여 손실 함수 최소화
히스토그램 기반 그레이디언트 부스팅 : 훈련 데이터를 256개 정수 구간으로 나누어 빠르고 높은 성능을 냄 - 그 외 라이브러리 : XGBoost, LightGBM
→ 앙상블 학습과 그리드 서치, 랜덤 서치를 사용한 하이퍼파라미터 튜닝을 사용하면 최고 수준의 성능을 내는 머신러닝 모델을 얻을수 있다.
'Study > PBL' 카테고리의 다른 글
[혼공머신] Ch07 (0) | 2024.07.24 |
---|---|
[혼공머신] Ch06 (1) | 2024.07.24 |
[혼공머신] Ch04 (0) | 2024.07.18 |
[혼공머신] Ch03 (0) | 2024.07.17 |
[혼공머신] Ch01, 2 (1) | 2024.07.12 |