[ML기초세션]_5주차
세션 전 학습 내용
교재: p.220 ~ p.283
[5-1] 결정 트리
핵심 키워드: 결정 트리, 불순도, 정보 이득, 가지치기, 특성 중요도
로지스틱 회귀 모델로의 와인 분류 문제 접근
1
2
3
4
5
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
wine.head()
만약 누락된 값이 있다면 그 데이터를 버리거나, 평균값을 채우는 등 전처리 과정이 필요
- describe(): 열에 대한 간략한 통계를 출력
특성들의 스케일이 다르기에, 표준화 필요
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)
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를 사용해 로지스틱 회귀 모델 훈련
1
2
3
4
5
6
7
8
9
10
11
12
13
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))
#점수가 낮으므로 언더피팅 예상
#coef, intercept 출력
print(lr.coef_, lr.intercept_)
우리는 이 모델이 왜 저런 계수 값을 학습했는지 정확히 이해할 수 없다. 대부분의 ML 모델은 이렇게 학습의 결과를 설명하기 어렵다.
결정 트리
결정 트리 모델은 이유를 설명하기 쉽다.
DT에서 random_state는 굳이 필요하지 않다. 예제에서는 설명을 위해 추가함
1
2
3
4
5
6
7
8
9
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))
#오버피팅된 결과 출력
- plot_tree(): DT를 이해하기 쉬운 트리 그림으로 출력하는 사이킷런의 함수
1
2
3
4
5
6
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()
- max_depth 매개변수: 트리의 최대 깊이 제한
- filled 매개변수: 클래스에 맞게 노드 색칠하기
- feature_names 매개변수: 특성 이름 전달
1
2
3
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
불순도
지니 불순도(gini impurity): DecisionTreeClassifier 클래스의 criterion 매개변수 기본값
정보 이득(information gain): 부모 노드와 자식 노드 사이의 불순도 차이
엔트로피 불순도: criterion = ‘entropy’ 지정하여 사용가능. 밑이 2인 로그를 사용하여 곱한다.
결정트리 알고리즘은 불순도 기준을 사용해 정보 이득이 최대가 되도록 노드를 분할한다. 노드를 순수하게 나눌수록 정보 이득이 커진다. 새로운 샘플에 대해 예측할 때는 마지막에 도달한 노드의 클래스 비율을 보고 예측한다.
가지치기
- max_depth 지정 가지치기
1
2
3
4
5
6
7
8
9
10
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.845 / 0.842
plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
DT의 장점: 특성값의 스케일이 계산에 영향을 미치지 않기 때문에 표준화 전처리 과정이 필요 없다.
feature_importances_ 속성: 특성 중요도 저장, 각 노드의 정보 이득과 전체 샘플에 대한 비율을 곱한 후 특성별로 더하여 계산한다. 특성 중요도를 활용하여 DT의 특성 선택에 활용할 수 있다!
1
2
3
print(dt.feature_importances_)
# [0.12345626(alchohol) 0.86862934(sugar) 0.0079144(pH) ]
결정 트리는 비전문가에게도 설명하기 쉬운 모델을 만든다. 뿐만 아니라 결정 트리는 많은 앙상블 학습 알고리즘의 기반이 된다. 앙상블 학습은 신경망과 함께 가장 높은 성능을 내기에 인기가 높은 알고리즘이다.
핵심 패키지와 함수
pandas
- info(): df의 요약 정보 출력. index, column type, null이 아닌 값의 개수, 메모리 사용량 제공. verbose 매개변수를 False로 바꾸면 각 열에 대한 정보는 출력하지 않는다.
- describe(): df 열의 통계 값을 제공한다. 수치형일 경우 min, max, avg, std, 사분위값 등이 출력. 문자열 같은 객체 타입의 열은 가장 자주 등장한 값과 횟수 출력. percentiles 매개변수에서는 백분위수를 지정한다.
scikit-learn
- DecisionTreeClassifier: 결정 트리 분류 클래스
- cirterion 매개변수: 불순도를 지정하며, 기본값은 지니 불순도이고 엔트로피 불순도를 사용할 수 있다.
- splitter 매개변수: 노드 분할 전략 선택. 기본값은 ‘best’로 정보 이득이 최대가 되도록 분할한다. ‘random’이면 임의로 노드를 분할한다.
- max_depth: 트리가 성장할 최대 깊이를 지정한다. 기본값은 None으로 리프노드가 순수하거나 min_samples_split보다 샘플 개수가 적을 때까지 성장한다.
- min_samples_split: 노드를 나누기 위한 최소 샘플 개수다. 기본값은 2.
- max_features 매개변수: 최적의 분할을 위해 탐색할 특성의 개수를 지정. 기본값은 None으로 모든 특성 사용.
- plot_tree(): DT모델 시각화. 첫 번째 매개변수로 결정 트리 모델 객체를 전달한다.
- max_depth 매개변수: 나타낼 트리 깊이 지정. 기본값은 None.
- feature_names 매개변수: 특성의 이름 지정
- filled 매개변수: 타깃값에 따라 노드 안에 색을 채운다.
[5-2] 교차 검증과 그리드 서치
핵심 키워드: 검증 세트, 교차 검증, 그리드 서치, 랜덤 서치
지금까지 우리는 훈련 세트에서 모델을 훈련하고 테스트 세트에서 모델을 평가했다. 그런데 테스트 세트를 사용해 자꾸 성능을 확인하다 보면 점점 테스트 세트에 맞추게 되는 셈이다. 테스트 세트로 일반화 성능을 올바르게 예측하려면 가능한 한 테스트 세트를 사용하지 말아야 한다. 모델을 만들고 나서 마지막 딱 한 번만 사용하는 것이 좋다.
검증 세트
테스트 세트를 사용하지 않고 모델이 과대적합인지 과소적합인지 판단하는 가장 간단한 방법은 훈련 세트를 또 나누어 검증세트를 만드는 것이다.
보통은 20~30%를 테스트 세트와 훈련 세트로 떼어 놓지만 훈련 데이터가 아주 많다면 단 몇 %만 떼어 놓아도 전체 데이터를 대표하는 데 문제가 없다.
- 훈련 세트에서 모델을 훈련하고 검증 세트로 모델을 평가한다. 이런 식으로 테스트 하고 싶은 매개변수를 바꿔가며 가장 좋은 모델을 고른다.
- 그다음 이 매개변수를 사용해 훈련 세트와 검증 세트를 합쳐 전체 훈련 데이터에서 모델을 다시 훈련한다.
- 마지막에 테스트 세트에서 최종 점수를 평가한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
# training set, test set spliting
train_input, test_input, train_target, test_target = train_test_split(
data, target, test_size=0.2, random_state=42)
# training set에서 validation set 만들기. test_size도 20% 재설정
sub_input, val_input, sub_target, val_target = train_test_split(
train_input, train_target, test_size=0.2, random_state=42)
교차 검증
- 검증세트를 떼어 내어 평가하는 과정을 여러 번 반복한다.
- 그 다음 이 점수를 평균하여 최종 검증 점수를 얻는다.
k-폴드 교차 검증(k-fold cross validation): 훈련 세트를 k개로 나누어 교차 검증을 수행하는 것
보통 5-폴드 교차 검증이나 10-폴드 교차 검증을 많이 사용한다. 이렇게 하면 데이터의 80-90%까지 훈련에 사용할 수 있다. 검증 세트가 줄어들지만 각 폴드에서 계산한 검증 점수를 평균하기 때문에 안정된 점수로 생각할 수 있다.
- cross_validate() 함수: 평가할 모델 객체를 첫 번째 매개변수로 전달하고, 그다음 검증 세트를 떼어 내지 않은 훈련 세트 전체를 함수에 전달한다.
cross_val_scrore() 함수: cross_validate() 함수의 결과 중에서 test_score 값만 반환한다.
1
2
3
4
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
print(scores)
이 함수는 fit_time, score_time, test_score 키를 가진 딕셔너리를 반환한다. 처음 2개의 키는 각각 모델을 훈련하는 시간과 검증하는 시간을 의미한다. 각 키마다 5개의 숫자가 담겨 있다. cross_validate() 함수는 기본적으로 5-폴드 교차 검증을 수행하는데, cv매개변수에서 폴드 수를 바꿀 수도 있다.
교차 검증의 최종 점수는 test_score 키에 담긴 5개의 점수를 평균하여 얻을 수 있다. 이름은 test_score지만 검증 폴드의 점수임을 혼동하지 말 것!
1
2
3
import numpy as np
print(np.mean(scores['test_score']))
교차 검증을 수행하면 입력한 모델에서 얻을 수 있는 최상의 검증 점수를 가능해 볼 수 있다.
한 가지 주의할 점은 cross_validate()는 훈련 세트를 섞고 폴드를 나누지 않는다. 만약 교차 검증 시 훈련 세트를 섞으려면 분할기(splitter)를 지정해야 한다.
사이킷런의 분할기는 교차 검증에서 폴드를 어떻게 나눌지 결정해 준다. cross_validate() 함수는 기본적으로 회귀 모델일 경우 KFold 분할기를 사용하고 분류 모델일 경우 타깃 클래스를 골고루 나누기 위해 StratifiedKFold를 사용한다.
1
2
3
4
5
6
7
8
9
10
11
from sklearn.model_selection import StratifiedKFold
scores = cross_validate(dt, train_input, train_target, cv=StratifiedKFold())
print(np.mean(scores['test_score']))
#0.8553..
#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.8574...
KFold 클래스도 동일한 방식으로 사용할 수 있다.
이제 결정 트리의 매개변수 값을 바꿔가며 가장 좋은 성능이 나오는 모델을 찾아보자.
하이퍼파라미터 튜닝
모델이 학습할 수 없어 사용자가 지정해야만 하는 파라미터를 하이퍼파라미터라고 한다. 사이킷런과 같은 ML 라이브러리를 사용할 때 이런 하이퍼파라미터는 모두 클래스나 메서드의 매개변수로 표현된다.
- 하이퍼파라미터 튜닝 과정:
- 라이브러리가 제공하는 기본값을 그대로 사용해 모델을 훈련한다.
- 검증 세트의 점수나 교차 검증을 통해 매개변수를 조금씩 바꿔본다.
사람의 개입 없이 하이퍼파라미터 튜닝을 자동으로 수행하는 기술을 ‘AutoML’이라고 부른다.
매개변수가 많아지면 최적의 매개변수 조합을 찾는 일이 점점 더 복잡해진다. 파이썬의 for문으로 직접 구현할 수도 있겠지만 사이킷런에서 제공하는 그리드 서치를 사용해보자.
- GridSearchCV 클래스: 하이퍼파라미터 탐색과 교차 검증을 한 번에 수행한다. 즉, 따로 cross_validate() 함수를 호출할 필요가 없다.
기본 매개변수를 사용한 DT모델에서 min_impurity_decrease 매개변수의 최적값을 찾아보자.
1
2
3
4
5
6
7
8
from sklearn.model_selection import GridSearchCV
params = {'min_impurity_decrease': [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}
# GridSearchCV클래스에 탐색 대상 모델과 params 변수를 전달하여 그리드 서치 객체를 만든다.
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)
GridSearchCV의 cv 매개변수 기본값은 5이므로 min_impurity_decrease 값마다 5-폴드 교차 검증을 수행한다. 결국 5*5=25개의 모델을 훈련한다. 많은 모델을 훈련하기에 GridSearchCV 클래스의 n_jobs 매개변수에서 병렬 실행에 사용할 CPU 코어 수를 지정하는 것이 좋다. 기본값은 1인데, -1로 지정하면 시스템에 있는 모든 코어를 사용한다.
편리하게도 사이킷런의 그리드 서치는 훈련이 끝나면 25개의 모델 중 검증 점수가 가장 높은 모델의 매개변수 조합으로 전체 훈련 세트에서 자동으로 다시 모델을 훈련한다. 이 모델은 gs객체의 best_estimator_ 속성에 저장되어 있다.
1
2
3
4
5
6
7
dt = gs.best_estimator_
print(dt.score(train_input, train_target))
# 0.9615162593804117
# 최적의 매개변수
print(gs.best_params_)
# {'min_impurity_decrease': 0.0001}
각 매개변수에서 수행한 교차 검증의 평균 점수는 cv_results_ 속성의 ‘mean_test_score’ 키에 저장되어 있다. 5번의 교차 검증으로 얻은 점수를 출력해보자.
1
2
print(gs.cv_results_['mean_test_score'])
#[0.86819297 0.86453617 0.86492226 0.86780891 0.86761605]
넘파이의 argmax() 함수를 사용하면 가장 큰 값의 인덱스를 추출할 수 있다. 이 인덱스를 사용해 params 키에 저장된 매개변수를 출력할 수 있다. 이 값이 최상의 검증 점수를 만든 매개변수 조합이다. 앞에서 출력한 gs.best_params_와 동일한지 확인해보자.
1
2
3
best_index = np.argmax(gs.cv_results_['mean_test_score'])
print(gs.cv_results_['params'][best_index])
# {'min_impurity_decrease': 0.0001}
과정 정리
- 탐색할 매개변수 지정
- 훈련 세트에서 그리드 서치를 수행하여 최상의 평균 검증 점수가 나오는 매개변수 조합 찾기
- 그리드 서치는 최상의 매개변수에서 검증 세트를 포함한 훈련 세트를 사용하여 최종 모델 훈련
이번엔 min_impurity_decrease, max_depth, min_samples_split의 최적 조합을 찾아보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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*10 = 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
랜덤 서치
매개변수의 값이 수치일 때 값의 범위나 간격을 미리 정하기 어려울 수 있다. 또 너무 많은 매개변수 조건이 있어 그리드 서치 수행 시간이 오래 걸릴 수 있다. 이럴 때 랜덤 서치를 사용한다.
랜덤서치에는 매개변수 값의 목록을 전달하는 것이 아니라 매개변수를 샘플링할 수 있는 확률 분포 객체를 전달한다.
싸이파이(scipy)는 파이썬의 핵심 과학 라이브러리 중 하나로 적분, 보간, 선형 대수, 확률 등을 포함한 수치 계산 전용 라이브러리다. 사이킷런은 넘파이와 싸이파이 기능을 많이 사용한다.
- uniform, radint 클래스: 주어진 범위에서 고르게 값을 뽑는다. 즉, 균등 분포에서 샘플링한다. randint는 정숫값을 뽑고, uniform은 실숫값을 뽑는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from scipy.stats import uniform, randint
# 0~10 사이의 범위를 갖는 randint 객체를 만들고 10개의 숫자를 샘플링해보자.
rgen = randint(0, 10)
rgen.rvs(10)
# array([4, 7, 6, 8, 9, 3, 8, 3, 1, 4])
# 1000개 샘플링
np.unique(rgen.rvs(1000), return_counts=True)
# (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
# array([116, 105, 95, 100, 84, 90, 97, 95, 107, 111]))
# uniform 사용하여 10개 실수 추출
ugen = uniform(0, 1)
ugen.rvs(10)
# array([0.07156624, 0.51330724, 0.78244744, 0.14237963, 0.05055468,
# 0.13124955, 0.15801332, 0.99110938, 0.08459786, 0.92447632])
이제 매개변수의 딕셔너리를 만들어보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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_inter 매개변수에 저장
# 그리드 서치보다 훨씬 교차 검증 수는 줄이면서 넓은 영역을 효과적으로 탐색할 수 있다.
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
핵심 패키지와 함수
scikit-learn
- cross_validate(): 교차 검증을 수행하는 함수
- 첫 번째 매개변수에 교차 검증을 수행할 모델 객체 전달, 두 번째와 세 번째 매개변수에 특성과 타깃 전달
- scoring 매개변수에 검증에 사용할 평가 지표를 지정할 수 있다. 기본적으로 분류 모델은 정확도를 의미하는 ‘accuracy’, 회귀 모델은 결정계수를 의미하는 ‘r2’가 된다.’
- cv 매개변수에 교차 검증 폴드 수나 splitter 객체를 지정할 수 있으며 기본값은 5다. 회귀일 때는 KFold 클래스를 사용하고, 분류일 때는 StratifiedKFold 클래스를 사용하여 5-폴드 교차 검증을 수행한다.
- GridSearchCV: 교차 검증으로 하이퍼파라미터 탐색 수행. 최상의 모델을 찾은 후 훈련 세트 전체를 사용해 최종 모델 훈련까지 수행.
- 첫 번째 매개변수로 그리드 서치를 수행할 모델 객체를 전달한다. 두 번째 매개변수에는 탐색할 모델의 매개변수와 값을 전달한다.
- scoring, cv, n_jobs, return_train_score 매개변수는 cross_validate() 함수와 동일하다.
- RandomizedSearchCV: 교차 검증으로 랜덤한 하이퍼파라미터 탐색을 수행한다. 최상의 모델을 찾은 후 훈련 세트 전체를 사용해 최종 모델을 훈련한다.
- 첫 번째 매개변수로 그리드 서치를 수행할 모델 객체를 전달한다. 두 번째 매개변수에는 탐색할 모델의 매개변수와 확률 분포 객체를 전달한다.
- scoring, cv, n_jobs, return_train_score 매개변수는 cross_validate() 함수와 동일하다.
[5-3] 트리의 앙상블
핵심 키워드: 앙상블 학습, 랜덤 포레스트, 엑스트라 트리, 그레이디언트 부스팅
정형 데이터(structured data): 어떤 구조로 되어 있는 데이터, CSV, DB, Excel에 저장하기 용이, 프로그래머가 다루는 대부분의 데이터
비정형 데이터(unstructured data): DB나 Excel로 표현하기 어려운 텍스트 데이터, 사진, 음악 등이 해당한다.
앙상블 학습(ensemble learning): 대부분 결정 트리를 기반으로 만들어진, 정형 데이터를 다루는데 가장 뛰어난 성과를 내는 알고리즘이다. 더 좋은 예측 결과를 만들기 위해 여러 개의 모델을 훈련하는 머신러닝 알고리즘이다.
랜덤 포레스트
- 랜덤 포레스트(random forest): 안정적인 성능 덕분에 널리 사용되고 있는 앙상블 학습의 대표 주자다. 이름에서 유추할 수 있듯이, 결정 트리를 랜덤하게 만들어 결정 트리의 숲을 만든다. 그리고 각 결정 트리의 예측을 사용해 최종 예측을 만든다.
- 부트스트랩(bootstrap): 데이터 세트에서 중복을 허용하여 데이터를 샘플링하는 방식.
랜덤 포레스트에서는 부트스트랩 방식을 사용하여 부트스트랩 샘플을 추출한다.
또한 노드를 분할할 때, 전체 특성 중에서 일부 특성을 무작위로 고른 뒤 최선의 분할을 찾는다.
분류모델인 RandomForestClassifier는 기본적으로 전체 특성 개수의 제곱근만큼의 특성을 선택한다. 4개의 특성이 있다면 노드마다 2개를 랜덤하게 선택하여 사용. 반면 회귀 모델인 RandomForestRegressor는 전체 특성을 사용한다.
사이킷런의 랜덤 포레스트는 기본적으로 100개의 결정 트리를 이렇게 훈련한다.
그 다음에, 분류일 때는 각 트리의 클래스별 확률을 평균하여 가장 높은 확률을 가진 클래스를 예측으로 삼는다. 회귀일 때는 단순히 각 트리의 예측을 평균한다.
랜덤 포레스트는 랜덤하게 선택한 샘플과 특성을 사용하기 때문에 훈련 세트에 과대적합되는 것을 막아주고 검증 세트와 테스트 세트에서 안정적인 성능을 얻을 수 있다.
1
2
3
4
5
6
7
8
9
10
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 지정 시 검증 점수 + 훈련 점수 반환, 기본값은 False
1
2
3
4
5
6
7
8
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가 제공하는 중요한 매개변수를 모두 제공한다. 또한, 결정 트리의 큰 장점 중 하나인 특성 중요도를 계산한다. 랜덤 포레스트의 특성 중요도는 각 결정 트리의 특성 중요도를 취합한 것이다.
1
2
3
4
rf.fit(train_input, train_target)
print(rf.feature_importances_)
# [0.23167441 0.50039841 0.26792718]
랜덤 포레스트에는 자체적으로 모델을 평가하는 점수를 얻을 수 있는 기능도 있다. 부트스트랩 샘플에 포함되지 않는 샘플인 OOB(out of bag) 샘플이 검증 세트의 역할을 수행하는 것.
1
2
3
4
5
6
rf = RandomForestClassifier(oob_score=True, n_jobs=-1, random_state=42)
rf.fit(train_input, train_target)
print(rf.oob_score_)
# 0.8934000384837406
엑스트라 트리
엑스트라 트리는 랜덤 포레스트와 매우 비슷하게 동작한다. 기본적으로 100개의 결정 트리를 훈련하고 전체 특성 중에 일부 특성을 랜덤하게 선택하여 노드를 분할하는 데 사용한다. 랜덤 포레스트와의 차이점은 부트스트랩 샘플을 사용하지 않는다는 것이다.
결정 트리를 만들 때 전체 훈련 세트를 사용하고, 대신 노드를 분할할 때 가장 좋은 분할을 찾는 것이 아니라 무작위로 분할한다.
하나의 결정 트리에서 특성을 무작위로 분할한다면 성능은 낮아지겠지만 많은 트리를 앙상블하기 때문에 과대적합을 막고 검증 세트의 점수를 높이는 효과가 있다.
1
2
3
4
5
6
7
8
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
일반적으로 엑스트라 트리가 랜덤 포레스트에 비해 무작위성이 좀 더 커서 더 많은 결정 트리를 훈련해야 하지만, 랜덤하게 노드를 분할하기에 빠른 계산 속도가 엑스트라 트리의 장점이다.
엑스트라 트리도 랜덤 포레스트와 마찬가지로 특성 중요도를 제공한다.
1
2
3
4
et.fit(train_input, train_target)
print(et.feature_importances_)
# [0.20183568 0.52242907 0.27573525]
엑스트라 트리의 회귀 버전은 ExtraTreesRegressor 클래스다.
그레이디언트 부스팅
그레이디언트 부스팅(gradient boosting): 깊이가 얕은 결정 트리를 사용하여 이전 트리의 오차를 보완하는 방식으로 앙상블 하는 방법
GradientBoostingClassifier: 깊이가 3인 결정 트리 100개 사용. 깊이가 얕은 트리를 사용하기에 과대적합에 강하고 일반적으로 높은 일반화 성능 기대 가능. 결정 트리를 계속 추가하면서 산을 내려온다.
1
2
3
4
5
6
7
8
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
그레이디언트 부스팅은 결정 트리의 개수를 늘려도 과대적합에 매우 강하다. 학습률을 증가시키고 트리의 개수를 늘리면 더 성능이 향상될 수 있다.
1
2
3
4
5
6
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
결정 트리 개수를 500개로 늘렸지만 과대적합을 잘 억제하고 있는 것을 볼 수 있다.
1
2
3
4
gb.fit(train_input, train_target)
print(gb.feature_importances_)
# [0.15872278 0.68010884 0.16116839]
subsample 매개변수: 기본값은 1.0(전체 훈련 세트 사용), <1(세트의 일부만 사용)
일반적으로 그레이디언트 부스팅이 랜덤 포레스트보다 조금 더 높은 성능을 얻을 수 있다. 하지만 순서대로 트리를 추가하기에 훈련 속도가 느리다. (GradientBoostingClassifier에는 n_jobs 매개변수가 없다)
그레이디언트 부스팅의 속도와 성능을 더욱 개선한 것이 히스토그램 기반 그레이디언트 부스팅이다.
히스토그램 기반 그레이디언트 부스팅
Histogram-based Gradient Boosting은
- 정형 데이터를 다루는 머신러닝 알고리즘 중 가장 인기가 높다.
- 이 방법은 먼저 입력 특성을 256개 구간으로 나누기에 최적의 분할을 매우 빠르게 찾을 수 있다.
- 입력에 누락된 특성이 있더라도 전처리할 필요 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 사이킷런 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)
# [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]
# 상대적으로 더욱 다양한 특성을 골고루 평가한다고 예상할 수 있다.
#####
hgb.score(test_input, test_target)
# 0.8723076923076923
앙상블 모델은 확실히 단일 결정 트리보다 좋은 결과를 얻을 수 있다.
사이킷런 외에도 여러 라이브러리에서 히스토그램 기반 그레이디언트 부스팅 알고리즘을 구현할 수 있다.
- 가장 대표적인 XGBoost 라이브러리
코랩에서 사용가능, 사이킷런의 cross_valiidate() 사용 가능, 다양한 부스팅 알고리즘 지원. tree_method 매개변수를 ‘hist’로 지정하면 히스토그램 기반 그레이디언트 부스팅 사용 가능.
1
2
3
4
5
6
7
8
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: 빠르고 최신 기술을 많이 적용하고 있다. 코랩에도 이미 설치됨
1
2
3
4
5
6
7
8
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
앙상블 학습은 정형 데이터에서 가장 뛰어난 성능을 내는 머신러닝 알고리즘 중 하나로, 더 좋은 예측 결과를 만들기 위해 여러 개의 모델을 훈련하는 머신러닝 알고리즘이다. 사이킷런의 대표적인 앙상블 학습으로는 랜덤 포레스트, 엑스트라 트리, 그레이디언트 부스팅, 히스토그램 기반 그레이디언트 부스팅이 있다.
- 랜덤 포레스트: 가장 대표적인 앙상블 학습 알고리즘, 성능이 좋고 안정적. DT를 훈련하기 위해 부트스트랩 샘플을 만들고 전체 특성 중 일부를 랜덤하게 선택해 결정 트리를 만든다.
- 엑스트라 트리: 랜덤 포레스트와 비슷하지만 부트스트랩을 사용하지 않고 노드를 랜덤으로 분할한다. 랜덤 포레스트보다 훈련 속도는 빠르지만 일반적으로 더 많은 트리가 필요하다.
- 그레이디언트 부스팅: 깊이가 얕은 트리를 연속적으로 추가하여 손실 함수를 최소화하는 앙상블 방법이다. 성능이 뛰어나지만 병렬로 훈련할 수 없기에 랜덤 포레스트나 엑스트라 트리보다 훈련 속도가 조금 느리다. 그레이디언트 부스팅에서 학습률 매개변수를 조정하여 모델의 복잡도를 제어할 수 있다.
- 히스토그램 기반 그레이디언트 부스팅 알고리즘: 가장 뛰어난 앙상블 학습. 훈련 데이터를 256개의 구간으로 변환하여 사용하기에 노드 분할 속도가 매우 빠르다. XGBoost와 LightGBM에도 설치되어 있음.
핵심 패키지와 함수
scikit-learn
- RandomForestClassifier: 랜덤 포레스트 분류 클래스.
- n_estimators 매개변수: 앙상블 구성 트리의 개수를 지정한다.
- criterion 매개변수: 불순도 지정, 기본값은 ‘gini’고, ‘entropy’를 선택하여 엔트로피 불순도를 사용할 수 있다.
- max_depth: 트리 성장 최대 깊이 지정. 기본값은 None으로, 지정하면 리프 노드가 순수하거나 min_samples_split보다 샘플 개수가 적을 때까지 성장한다.
- max_samples_split: 노드를 나누기 위한 최소 샘플 개수고 기본값은 2다.
- max_features: 최적의 분할을 위해 탐색할 특성의 개수를 지정한다. 기본값은 auto로 특성 개수의 제곱근이다.
- bootstrap 매개변수: 부트스트랩 샘플의 사용 유무를 지정. 기본값은 True.
- oob_score: OOB 샘플을 사용하여 훈련한 모델을 평가할지 지정. 기본값은 False.
- n_jobs: 병렬 실행에 사용할 CPU 코어 수를 지정.
- ExtraTreesClassifier: 엑스트라 트리 분류 클래스
- n_estimators, criterion, max_depth, min_samples_split, max_features: 랜덤 포레스트와 동일
- bootstrap: 부트스트랩 샘플 사용 유무 지정. 기본값은 False
- oob_score: OOB샘플을 사용하여 훈련한 모델을 평가할지 지정한다. 기본값은 False.
- n_jobs: 병렬 실행에 사용할 CPU 코어 수 지정.
- GradientBoostingClassifier: 그레이디언트 부스팅 분류 클래스
- loss: 손실함수 지정. 기본값은 ‘deviance’로, 로지스틱 손실 함수 의미.
- learning_rate: 트리가 앙상블에 기여하는 정도를 조절, 기본값은 0.1
- n_estimators: 부스팅 단계를 수행하는 트리의 개수, 기본값은 100
- subsample: 사용할 훈련 세트의 샘플 비율 지정, 기본값은 1.0
- max_depth: 개별 회귀 트리의 최대 깊이. 기본값은 3.
- HistGradientBoostingClassifier: 히스토그램 기반 그레이디언트 부스팅 분류 클래스.
- learning_rate: 학습률 또는 감쇠율이라 한다. 기본값은 0.1이며 1.0이면 감쇠 x
- max_iter: 부스팅 단계를 수행하는 트리의 개수다. 기본값은 100
- max_bins: 입력 데이터를 나눌 구간의 개수. 기본값은 255이며 최대치다. 여기에 1개의 구간이 누락된 값을 위해 추가된다.
발표 내용 by 이찬
- 앙상블 학습의 종류
- 부스팅 계열 모델 : CatBoost, LightGBM
- CatBoost, LightGBM의 성능비교
- 해당 결과를 통해 알 수 있는 사실
Ensemble Learning
머신러닝을 위한 다양한 학습 알고리즘들을 결합하여 학습시키는 것으로, 예측력의 보완은 물론, 각각의 알고리즘을 단독으로 사용할 경우 나타나는 단점들을 보완하여 데이터에 맞는 최적의 모델로 일반화하는 방법
weak classifier 들을 결합하여 strong classifier 를 만들어서 decision tree 에서 overfitting 되는 문제가 덜 발생하는 장점
Voting, Bagging, Boosting, Stacking
1. Voting
- 각각의 여러 모델(예: 결정 트리, 로지스틱 회귀, SVM 등)들을 독립적으로 학습시킨 후, 그 결과를 결합하여 예측을 수행
- 각 모델의 예측 결과를 투표하여 가장 많이 선택된 클래스(또는 예측값)를 최종 예측으로 사용하는 Hard Voting
- 각 모델이 반환한 클래스 확률의 평균을 구해, 가장 높은 평균 확률을 가진 클래스를 최종 예측으로 사용하는 Soft Voting
2. Bagging
- 샘플을 여러번 뽑아서(bootstrap) 각 모델을 학습시킨 다음 결과물을 집계(aggregation) 하는 방법
- 학습데이터가 충분하지 않더라도 충분한 학습효과를 낼 수 있는 장점
- 높은 bias의 underfitting 문제나 높은 variance의 overfitting 문제에 도움이 됨
- ex) random forest
Boting과 Bagging 둘 다 결국에는 각각의 모델들의 예측값을 voting 방식으로 결정하는데 데이터 처리 방식과 목표에서 차이가 존재
데이터 처리 방식
- Voting: 데이터셋은 동일하며, 각각 다른 알고리즘을 이용한 분류기의 예측을 결합
- Bagging: 데이터셋을 복원 추출하여 여러 서브데이터셋을 만들고, 각 서브데이터셋에 대해 서로 같은 알고리즘을 이용한 모델을 독립적으로 학습
목표
- Voting : 모델의 다양성을 활용하여 최종 예측의 정확성을 높이는 것이 목표입니다.
- Bagging: 모델의 분산을 줄이고 과적합을 방지하여, 예측의 안정성과 성능을 개선하는 것이 목표입니다.
3. Boosting
- 배깅에서 각각의 모델이 독립적으로 학습하는 반면, 부스팅은 이전 모델의 학습이 다음 모델의 학습에 영향을 줌
- 이전 모델의 학습 결과에 따라 오답에 대해서는 높은 가중치 부여
- 정답에 대해서는 낮은 가중치 부여
- 잘못 분류된 데이터에 집중해서 새로운 분류 규칙을 만드는 과정을 반복
- ex) Gradient Boost, XGBoost, LightGBM, AdaBoost
- 배깅은 병렬학습이고 부스팅은 순차학습이기에, 부스팅이 배깅보다 일반적으로 시간이 오래 걸림
- 배깅 : 특정영역에서 정확도 낮음
- 부스팅 : 이상치, 결측치에 취약
부스팅 계열 모델들은 불균형 데이터에 대한 적응력이 높음
- 오차 보정: 각 단계에서 이전 모델이 잘못 예측한 샘플에 더 많은 가중치를 부여 ==> 모델이 불균형 데이터셋에서 희귀한 클래스( ex) 공장에서 제조된 불량인 제품)의 특징에 더 집중하여 학습할 수 있음. => 비정상 클래스에 대한 예측 성능을 점진적으로 개선 + 클래스 불균형 문제를 완화 가능
4. Stacking
- 여러 가지 다른 모델을 학습시키고, 이 모델들을 베이스 모델이라고 정의
- 각각의 베이스 모델은 데이터의 서로 다른 측면을 학습함
- 개별 모델이 예측 결과를 다시 meta dataset 으로 사용하여 최종 모델인 Meta Learner 에서 학습
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# stacking 에시 코드
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
cancer_data = load_breast_cancer()
X_data = cancer_data.data
y_label = cancer_data.target
X_training , X_testing , y_training , y_testing = train_test_split(X_data , y_label , test_size=0.2 , random_state=0)
# 개별 ML 모델을 위한 Classifier 생성.
knn_clf = KNeighborsClassifier(n_neighbors=4) #K최근접이웃
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0)#랜덤포레스트
dt_clf = DecisionTreeClassifier() #결정트리
ada_clf = AdaBoostClassifier(n_estimators=100) #아다부스트
# 개별 모델들을 학습.
knn_clf.fit(X_training, y_training)
rf_clf.fit(X_training , y_training)
dt_clf.fit(X_training , y_training)
ada_clf.fit(X_training, y_training)
# 학습된 개별 모델들이 각자 반환하는 예측 데이터 셋을 생성하고 개별 모델의 정확도 측정.
knn_pred = knn_clf.predict(X_testing)
rf_pred = rf_clf.predict(X_testing)
dt_pred = dt_clf.predict(X_testing)
ada_pred = ada_clf.predict(X_testing)
# 시험데이터로 예측한 4가지 모델의 결과를 합침
pred = np.array([knn_pred, rf_pred, dt_pred, ada_pred])
print(pred.shape)
# transpose를 이용해 행과 열의 위치 교환. 컬럼 레벨로 각 알고리즘의 예측 결과를 피처로 만듦.
pred = np.transpose(pred)
print(pred.shape)
# 최종 분류기 모델 생성
lr_final = LogisticRegression(C=10)
# 최종 분류기 학습 및 예측
lr_final.fit(pred, y_testing)
final = lr_final.predict(pred)
Boosting 계열 모델들
Light GBM
기존 효율성 정확도의 trade-off를 해결했다는 것입니다. 그래서 기존 알고리즘의 정확도를 유지하면서 훨씬 좋은 효율성을 가지는 것이 장점
GBDT(Gradient Boosting Decision Tree)는 효율성, 정확도, 해석 측면에서 좋은 알고리즘이다.
하지만 Information Gain 기반으로 split 할 때 모든 feature와 data를 접근해야 하기 때문에 계산이 효율적이지 않고 복잡하다.
계산의 복잡도는 feature의 수와 data의 수에 비례하기 때문에 고차원 빅데이터에 특히 어려움을 가질 수 있다.
이러한 부분을 해결하기 위해서 lightgbm은 GOSS(Gradient-based One-Side Sampling)과 EFB(Exclusive Feature Bundling)을 새로 적용한 GBDT를 활용한다.
1. GOSS(Gradient-based One-Side Sampling)
- Gradient를 데이터 인스턴스를 줄이는 sampling에 유용한 정보로 활용하여 데이터 인스턴스를 줄이는 것과 정확도 사이의 균형을 맞춘 기법
- gradient가 큰 경우는 훈련이 덜 되었으며 작은 경우는 훈련이 충분히 된 경우
- Gradient가 큰 경우 information gain에 더 많이 영향을 줄 것이기 때문에 큰 gradient의 인스턴스는 유지하고 작은 gradient의 인스턴스를 일부분 제거
- 작은 gradient 인스턴스의 일부분을 제거했기 때문에 Information Gain을 계산할 때 작은 gradient에 대해 상수를 곱해 보완한다.
2. EFB(Exclusive Feature Bundling)
고차원 데이터는 sparse한 경우가 많으며 특히 sparse feature 공간에서 많은 featrue들은 상호배타적이다. 즉, 0이 아닌 값을 동시에 갖는 경우가 거의 없음
EFB는 상호배타적인 feature들을 하나의 feature로 묶어 정확도의 감소없이 훈련속도를 높여주는 방법
- 어떤 feature들을 bundle할 지에 대해 정하기 위해서 graph-coloring 문제로 치환
- graph-coloring 문제로 풀기 위해서 G = (V,E)로 정의하고 G의 row가 각 feature가 될 수 있게 V개의 feautre를 이용한다.
- 각 feature을 꼭지점으로 정의하고 feature 간 상호배타적이지 않으면 feature 간 edge를 추가하는 graph-coloring 문제로 만들고 최적 근사값을 위해 Greedy 알고리즘을 활용한다.
- 완전히 상호배타적인 feature들을 bundle하는 것이 아닌 조금의 conflict를 허용하여 bundle한다면 효율성을 높일 수 있으며 이 충돌비율을 조정하여 균형을 잡을 수 있다.
- feature들을 bundle할 때 기존 feature를 식별할 수 있는 것이 중요하며 이를 histogram-based 알고리즘으로 접근한다.
- histogram-based 알고리즘에서 서로 다른 bins에 배타적인 feature를 저장하여 bundle할 수 있다.
=> 상호배타적인 sparse한 feature를 bundle하여 graph-coloring 문제로 치환하여 적은 수의 dense feature로 만들고 계산의 효율성을 높이는 방식
Leaf-wise Tree Growth 방식으로 Level-wise Tree Growth 구조인 XGBoost보다 속도가 빠르다
- delta loss가 가장 큰 값을 선택하여 다시 subtree로 분할 => 성능을 가장 낮추는(loss가 가장 큰) node를 선택해 loss를 줄여나가는 방식
- tree를 균형적으로 만들기 위한 추가 연산이 필요 없기에 상대적으로 속도가 빠르다.
CatBoost
- CatBoost의 “Cat”은 “Categorical”의 약어로, 범주형 데이터(categorical data)에 특히 강력한 성능을 발휘하는 그레이디언트 부스팅(Gradient Boosting) 알고리즘을 의미합니다. CatBoost는 범주형 데이터 처리에 특화된 알고리즘으로, 범주형 피처를 효과적으로 처리할 수 있는 여러 가지 기능을 제공합니다.
- Gradient Boosting은 매우 강력하지만 여전히 통계적 문제에 직면하고 있다.
특히 gradient boosting은 매 boosting round마다 train의 target data에 의존하여 잔차를 구하고 학습하기 때문에 f(x_test) x_test의 분포에서 f(x_train) x_train 분포로 변화를 만든다. - 결국 예측 결과 변화, prediction shift를 만들고 하나의 target leakage 문제이다.
- 또한, 기존 알고리즘들은 범주형 변수의 처리 문제가 있다.
- 이런 두가지 문제점을 ordering priciple과 새로운 범주형 변수 처리 방법으로 해결한다.
1. 계산을 줄이고 target leakage를 최소화하는 Ordered TS
- 범주형 변수는 이산적인 값을 가지며 서로 비교할 수 없는 변수이다.
- 범주형 변수 처리의 대표적인 기법은 One-hot encoding이다.
- One-hot encoding은 각 범주의 특성들에 대해 이진변수로 만들기 때문에 feature의 수가 급격하게 증가하는 문제점이 있을 수 있다.
- Catboost에서는 oredering priciple을 적용한 Ordered TS를 제안한다.
- 현 시점을 기준으로 과거 데이터로만 TS(Target Statistic)를 추정하기 위해서 무작위 순열, 즉 인공적인 시간을 도입하여 TS 추정치를 observed history에서만 구하는 방식으로 categorical feature를 처리
- 하지만 하나의 무작위 순열로만 TS를 추정한다면 과거의 TS추정치는 그 이후의 추정치보다 분산이 높을 것이다.
- 이를 보정하기 위해 각 단계마다 다른 무작위 순열을 활용한다.
2. feature combination
- 범주형 변수의 조합으로 새로운 변수 조합을 만들어 낼 수 있습니다. Greedy 방식으로 이러한 조합들을 만들며 트리를 분할할 때 이전에 사용된 데이터에서 조합을 찾아내고 TS로 변환하는 방식입니다.
1
# !pip install catboost
LighGBM과 CatBoost의 특징 => 두 모델을 동일한 데이터로 학습 시킨 후 모델의 성능을 비교
1
2
3
4
import warnings
# 경고 필터링 설정
warnings.filterwarnings("ignore", category=UserWarning, module='lightgbm')
warnings.filterwarnings("ignore", message="No further splits with positive gain", module='lightgbm')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# Adult Income 데이터셋을 사용한 CatBoost와 LightGBM 간의 성능을 비교
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import lightgbm as lgb
from catboost import CatBoostClassifier
# 데이터 로드
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data'
column_names = [
'age', 'workclass', 'fnlwgt', 'education', 'education-num', 'marital-status',
'occupation', 'relationship', 'race', 'sex', 'hours-per-week', 'native-country', 'income'
]
data = pd.read_csv(url, header=None, names=column_names, na_values=' ?', skipinitialspace=True)
# 범주형 데이터 처리
cat_features = ['workclass', 'education', 'marital-status', 'occupation', 'relationship', 'race', 'sex', 'native-country']
# 결측치 처리
data.dropna(inplace=True)
# 타겟 변수와 피처 변수 분리
X = data.drop('income', axis=1)
y = data['income'].apply(lambda x: 1 if x == '>50K' else 0)
# 범주형 변수 인코딩
X_encoded = pd.get_dummies(X, columns=cat_features, drop_first=True) # drop_first=True를 통해 더미 변수를 생성합니다.
# 데이터 분할
X_train, X_test, y_train, y_test = train_test_split(X_encoded, y, test_size=0.3, random_state=42)
# CatBoost 모델 정의 및 학습
catboost_model = CatBoostClassifier(
learning_rate=0.1,
depth=6,
iterations=100,
cat_features=[X_encoded.columns.get_loc(col) for col in cat_features if col in X_encoded.columns], # 인코딩된 범주형 변수의 인덱스
verbose=0
)
catboost_model.fit(X_train, y_train)
# LightGBM 모델 정의 및 학습
lgbm_model = lgb.LGBMClassifier(
learning_rate=0.1,
num_leaves=31,
n_estimators=100
)
lgbm_model.fit(X_train, y_train)
# 모델 예측
catboost_predictions = catboost_model.predict(X_test)
lgbm_predictions = lgbm_model.predict(X_test)
# 정확도 평가
catboost_accuracy = accuracy_score(y_test, catboost_predictions)
lgbm_accuracy = accuracy_score(y_test, lgbm_predictions)
print(f"CatBoost Accuracy: {catboost_accuracy:.4f}")
print(f"LightGBM Accuracy: {lgbm_accuracy:.4f}")
결과값
- CatBoost Accuracy: 0.8490
- LightGBM Accuracy: 0.8611
결과값 분석
- 일반적으로 성능은 CatBoost, LightGBM, XGBoost 순으로 좋다고 알려져 있지만, 위의 데이터 셋과 같이 학습에 사용되는 범주형 변수의 개수가 수치형 변수보다 매우 적은 데이터셋에서는 CatBoost를 사용한다고 해서 XGBoost보다 성능이 무조건 더 높다라는 보장은 없음을 알 수 있다.
No Free Lunch Theorem (NFL Theorem)
- 개별적인 데이터들을 경험해보기 전까지는 해당 데이터에 더 잘 맞을 것이라고 보장할 수 있는 모델은 존재하지 않는다.
- 모든 데이터에서 가장 좋은 성능을 내는 알고리즘은 없으며, 복잡한 최신 알고리즘보다 단순한 트리 모델이 어떤 데이터의 특성을 더 잘 나타내는 경우도 있으니, 해당 데이터에 적합한 다양한 모델들을 평가해보고 비교해서 적절한 모델을 선택하는 것이 중요하다.
- 이때, 데이터의 복잡도, 데이터의 크기, 피쳐에서 범주형 변수의 수, 해당 데이터의 성격 등의 종합적인 특징들을 바탕으로 적합한 모델을 선택해야 하는 것이 중요하다.