1. 개요
- 무작위로 선택된 수천명의 사람에게 복잡한 질문을 하고 대답을 모은다고 가정하면 이렇게 모은 답이 전문가의 답보다 나을 가능성이 높은데, 이를 대중의 지혜 혹은 집단지성이라고 한다.
- 하나의 좋은 예측기를 이용하는 것보다 일반적인 여러 예측기를 이용해서 예측을 하면 더 좋은 결과를 만들 수 있다는 것을 앙상블 기법이라고 한다.
- Decision Tree는 전체 데이터를 이용해서 하나의 트리를 생성해서 결과를 예측하지만, Random Forest는 훈련 세트로부터 무작위로 각기 다른 서브 세트를 이용해서 여러개의 트리 분류기를 만들고 예측할 때 가장 많은 선택을 받은 클래스나 평균 이용
- 머신러닝에서 가장 좋은 모델은 앙상블을 이용하는 모델
2. 투표기반 분류기
- 분류기 여러개를 가지고 훈련을 한 후 투표를 해서 다수결의 원칙으로 분류하는 방식
- law of large numbers(큰 수의 법칙)
- 동전을 던졌을 때 앞면이 나올 확률이 51%이고 뒷면이 나올 확률이 49%인 경우 일반적으로 1000번을 던진다면 앞면이 510번 뒷면이 490번 나올 것이다.
- 이런 경우 앞면이 다수가 될 가능성은 확률적으로 75% 정도 된다.
- 이를 10000번으로 확장하면 확률은 97%가 된다.
- 앙상블 기법을 이용할 때 동일한 알고리즘을 사용하는 분류기를 여러 개 만들어도 되고 서로 다른 알고리즘의 분류기를 여러개 만들어도 됨
- 동일한 알고리즘을 사용하는 분류기를 여러개 만들어서 사용할 때는 훈련 데이터가 달라야 한다.
2-1) 직접 투표 방식
- 분류를 할 때 실제 분류된 클래스를 가지고 선정
2-2) 간접 투표 방식
- 분류를 할 때 클래스 별 확률 가지고 선정
- 이 방식을 사용할 때는 모든 분류기가 predict_proba 함수 사용 가능
- 이 방식의 성능이 직접 투표 방식보다 높음
2-3) API
- sklearn.ensemble.VotingClassifier 클래스
- 매개변수로는 estimators 가 있는데 여기에 list로 이름과 분류기를 튜플의 형태로 묶어서 전달하고 voting 매개변수에 hard와 soft를 설정해서 직접 투표 방식인지 간접 투표 방식인지 설정
2-4) 투표 기반 분류기 (클래스를 가지고 선정)
# 직접 투표 방식
# 데이터 생성
from sklearn.model_selection import train_test_split #훈련 데이터와 테스트 데이터 분할
from sklearn.datasets import make_moons #샘플 데이터 생성
X, y = make_moons(n_samples=500, noise=0.30, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
#개별 분류기
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
#모델 생성 및 훈련
log_clf = LogisticRegression(solver="lbfgs", random_state=42)
rnd_clf = RandomForestClassifier(n_estimators=100, random_state=42)
svm_clf = SVC(gamma="scale", random_state=42)
#직접 투표 기반 분류기
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(
estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
voting='hard')
#평가 지표 확인
from sklearn.metrics import accuracy_score
for clf in (log_clf, rnd_clf, svm_clf, voting_clf):
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(clf.__class__.__name__, accuracy_score(y_test, y_pred))
LogisticRegression 0.864 RandomForestClassifier 0.896 SVC 0.896 VotingClassifier 0.912 |
# 간접 투표 방식
- 확률을 가지고 분류할 때는 모든 예측기가 predict_proba()를 호출할 수 있어야 한다.
- SVM은 기본적으로 predict_proba()를 가지고는 있지만 사용을 못한다.
- 인스턴스를 만들 때 probability=True를 추가해 주어야 확률을 계산한다.
# log_clf = LogisticRegression(solver="lbfgs", random_state=42)
# rnd_clf = RandomForestClassifier(n_estimators=100, random_state=42)
svm_clf = SVC(gamma="scale", probability=True, random_state=42)
voting_clf = VotingClassifier(
estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
voting='soft')
from sklearn.metrics import accuracy_score
for clf in (log_clf, rnd_clf, svm_clf, voting_clf):
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(clf.__class__.__name__, accuracy_score(y_test, y_pred))
LogisticRegression 0.864 RandomForestClassifier 0.896 SVC 0.896 VotingClassifier 0.92 |
- 성능 조금 더 향상
직접 간접
log_clf 2 (0.49, 0.51)
svm_clf 2 (0.49, 0.51)
rnd_clf 1 (0.60, 0.40)
간접 투표 기반이 더 효율이 좋다.
100%는 아닐테니까 오류가 발생하고, 다른 모델에서 공통으로 발생할 가능성은 크지 않으니까 여러개를 돌리면 효율이 좋아질 것이라는게 투표기반 모델의 가정.
2-5) 배깅과 페이스팅
- 동일한 알고리즘을 사용하고 훈련 세트에 서브 세트를 무작위로 구성해서 예측기를 각기 다르게 학습시키는 것
- bagging(bootstrap aggregating): 훈련 세트에서 중복을 허용하고 샘플링하는 방식
- pasting: 훈련 세트에서 중복을 허용하지 않고 샘플링하는 방식
- 하나의 샘플링 데이터는 여러개의 예측기에 사용 가능한데
bagging은 하나의 예측기에 동일한 샘플이 포함될 수 있다.
pasting은 하나의 예측기 안에는 동일한 샘플이 포함될 수 없다. - 모든 예측기가 훈련을 마치면 모든 예측을 모아서 새로운 샘플에 대한 예측을 생성하는데, 이때 수집 함수는 분류일 때 최빈값이고 회귀일 때 평균을 계산
- 개별 예측기는 원본 데이터 전체로 훈련한 것보다 편향이 심하지만 수집 함수를 통과하면 편향과 분산이 모두 감소
- 예측기들은 동시에 다른 CPU 코어나 컴퓨터에서 병렬로 학습 가능
- API: BaggingClassifier
- 기본적으로 bagging을 사용. bootstrap 매개변수를 False로 설정하면 페이스팅 수행
- 샘플이 많으면 페이스팅 사용, 샘플의 개수가 적으면 bagging 사용
- n_jobs 매개변수: sklearn이 훈련과 예측에 사용할 CPU 코어 수를 설정 (-1을 설정하면 모든 코어 사용)
- n_estimators 매개변수: 예측기의 개수 설정
- max_sample 매개변수: 샘플의 최대 개수를 지정
- predict_proba 함수를 가진 경우에는 분류에서 간접 투표 방식 사용 가능
# DecisionTree와 DecisionTree의 Bagging 정확도 확인
- 하나가 나은가 여러개가 나은가
- 전체 데이터를 하나의 훈련데이터로
- 전체 데이터를 쪼개서 트리를 여러개로
from sklearn.metrics import accuracy_score
from sklearn.tree import DecisionTreeClassifier
tree_clf = DecisionTreeClassifier(random_state=42)
tree_clf.fit(X_train, y_train)
y_pred_tree = tree_clf.predict(X_test)
print(accuracy_score(y_test, y_pred_tree))
0.856 #정확도 |
from sklearn.ensemble import BaggingClassifier
bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators=500, #결정 트리 500개 생성
max_samples=100, bootstrap=True, random_state=42) #각 트리의 샘플 데이터 수 최대 100개, 복원 추출
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)
from sklearn.metrics import accuracy_score
print(accuracy_score(y_test, y_pred))
0.904 #정확도 |
- 앙상블의 예측이 결정 트리 하나의 예측보다 일반화가 잘 됨
- 앙상블은 비슷한 편향을 가지지만 더 작은 분산을 만듦
- 부트스트래핑은 각 예측기가 학습하는 서브 세트에 다양성을 증가시키므로 배깅이 페이스팅보다 편향이 조금 더 높지만 다양성을 추가하게 되면 예측기들의 상관관계를 줄이므로 앙상블의 분산을 감소
- 일반적으로는 배깅이 페이스팅보다 더 좋은 성능을 나타내지만 되도록이면 교차검증으로 모두 평가하는게 좋다.
- oob 평가
- 배깅에서 어떤 샘플은 한 예측기를 위해서 여러번 샘플링 되고, 어떤 데이터는 전혀 선택되지 않을 수 있다.
- 전체 데이터에서 63% 정도만 사용된다.
- 선택되지 않은 37%를 oob(out-of-bag) 샘플이라고 한다.
- 예측기마다 데이터가 다르다.
- 별도로 테스트 데이터 생성 없이 훈련에 사용하지 않은 37%의 데이터를 이용해서 검증 작업을 수행할 수 있다.
- 앙상블의 평가는 각 예측기의 oob 평가를 평균해서 얻는다.
- BaggingClassifier를 만들 때 oob_score = True로 설정하면 훈련이 끝난 후 자동으로 oob 평가 수행
- 그 결과를 oob_score_ 에 저장
bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators=500, max_samples=100,
bootstrap=True, oob_score=True, random_state=42)
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_
0.9253333333333333 |
- BaggingClassifier는 특성 샘플링도 지원
- max_features, bootstrap_features라는 매개변수 이용해서 특성 샘플링 지원
- 최대 사용하는 피처의 개수와 피처의 중복 허용 여부를 설정
- 각 예측기는 무작위로 선택한 입력 특성의 일부분으로 훈련된다.
- 이미지와 같은 고차원의 데이터 세트에서 이용
- 훈련 특성과 샘플을 모두 샘플링하는 것을 Random Patches Method라고 한다.
- 샘플을 모두 사용하고 특성만 샘플링하는 것은 Random Subspaces Method라고 한다.
- 특성 샘플링을 하게 되면 더 다양한 예측기가 만들어지므로 편향을 늘리는 대신 분산을 낮춘다.
3. RandomForest
- 같은 알고리즘으로 여러 개의 분류기를 만들어서 보팅으로 최종 결정하는 배깅의 대표적인 알고리즘
- 알고리즘은 DecisionTree를 이용
- bootstrap 샘플을 생성해서 샘플 데이터 각각에 결정 트리를 적용한 뒤 학습 결과를 취합하는 방식으로 작동
- 각각의 DecisionTree들은 전체 특성 중 일부만 학습
- random_state 값에 따라 예측이 서로 다른 모델이 만들어지는 경우가 있는데 트리의 개수를 늘리면 변동이 적어진다.
- 각 트리가 별개로 학습되므로 n_jobs를 이용해서 동시에 학습하도록 할 수 있다.
3-1) 장점
- 단일 트리의 단점 보완
- 매개변수 튜닝을 하지 않아도 잘 작동하고 데이터의 스케일을 맞출 필요가 없다.
- 매우 큰 데이터 세트에도 잘 작동
- 여러 개의 CPU 코어를 사용하는 것이 가능
3-2) 단점
- 대량의 데이터에서 수행하면 시간은 많이 걸린다.
- 차원이 매우 높고 (특성의 개수가 많음) 희소한 데이터(유사한 데이터가 별로 없는) 일수록 잘 작동하지 않는다.
- 이런 경우 선형 모델을 사용하는 것이 적합
- 희소한 데이터는 리프 노드에 있는 샘플의 개수가 적은 경우
3-3) 배깅과 랜덤 포레스트 비교
- 랜덤 포레스트는 Decision Tree의 배깅
- 배깅에서 부트 스트래핑을 사용하고 특성 샘플링을 이용하는 것이 랜덤 포레스트
- 부트 스트래핑은 하나의 훈련 세트에서 일부분의 데이터를 추출하는데 복원 추출 이용해서 수행
- 특성 샘플링은 비율을 설정해서 특성을 전부 이용하지 않고 일부분만을 이용해서 학습하는 방식
- 랜덤 포레스트에서는 특성의 비율을 기본적으로 제곱근만큼 사용
#배깅을 이용한 random forest 만들기
bag_clf = BaggingClassifier(
DecisionTreeClassifier(max_features="sqrt", max_leaf_nodes=16),
n_estimators=500, random_state=42)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)
#random forest 분류기
from sklearn.ensemble import RandomForestClassifier
rnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, random_state=42)
rnd_clf.fit(X_train, y_train)
y_pred_rf = rnd_clf.predict(X_test)
print(np.sum(y_pred == y_pred_rf) / len(y_pred))
1.0 |
- 2개 모두 일치하면 1.0
- RandomForest 알고리즘은 트리의 노드를 분할할 때 전체 특성 중에서 최선의 특성을 찾는 것이 아니라 무작위로 선택한 특성 후보 중에서 최적의 특성을 찾는 식으로 무작위성 주입
- 이러한 방식은 트리의 다양성을 높여서 편향을 손해보는 대신에 분산을 낮추는 방식으로 더 좋은 모델을 만들어간다.
3-4) 특성 중요도
- 트리 모델은 특성의 상대적 중요도를 측정하기 쉬움
- sklearn은 어떤 특성을 사용한 노드가 평균적으로 불순도를 얼마나 감소시켰는지 확인해서 특성의 중요도 판단
- 가중치의 평균이며 노드의 가중치는 연관된 훈련 샘플의 수와 같음
- 훈련 샘플의 수를 전체 합이 1이 되도록 결과값을 정규화한 후 feature_importances_에 저장
from sklearn.datasets import load_iris
iris = load_iris()
#data라는 속성에 피처 4가지 있음
#target이라는 속성의 꽃의 종류에 해당하는 범주형 데이터 3가지
#featre_names 속성에 피처 이름이 저장되어 있고 class_names에 클래스 이름 저장
rnd_clf = RandomForestClassifier(n_estimators=500, random_state=42)
rnd_clf.fit(iris["data"], iris["target"])
for name, score in zip(iris["feature_names"], rnd_clf.feature_importances_):
print(name, score)
sepal length (cm) 0.11249225099876378 sepal width (cm) 0.02311928828251033 petal length (cm) 0.4410304643639577 petal width (cm) 0.42335799635476823 |
- 이미지에서 특성의 중요도 확인해보면 이미지의 어느 부분이 다르게 보이는지 확인 가능
- MNIST 데이터에서 분류할 때 가장 크게 다른 부분은 가운데 부분
- 특성의 중요도를 파악한 후 전체 피처를 전부 사용하지 않고 일부분만 사용해도 거의 동일한 정확도를 만들어낼 수 있다.
#특성 중요도 시각화 - MNIST
from sklearn.datasets import fetch_openml
#28*28 이미지 가져오기
mnist = fetch_openml('mnist_784', version=1) # 28*28=784, 피처의 개수
mnist.target = mnist.target.astype(np.uint8) #정수변환
#랜덤포레스트 분류기
rnd_clf = RandomForestClassifier(n_estimators=100, random_state=42)
rnd_clf.fit(mnist["data"], mnist["target"])
#중요도를 시각화 하기 위한 함수
def plot_digit(data):
image = data.reshape(28, 28)
plt.imshow(image, cmap = mpl.cm.hot,
interpolation="nearest")
plt.axis("off")
plot_digit(rnd_clf.feature_importances_)
cbar = plt.colorbar(ticks=[rnd_clf.feature_importances_.min(), rnd_clf.feature_importances_.max()])
cbar.ax.set_yticklabels(['Not important', 'Very important'])
save_fig("mnist_feature_importance_plot")
plt.show()
- 이미지의 외곽 부분은 분류에 미치는 효과가 거의 없고 중앙 부분의 데이터가 이미지 분류에 영향을 많이 주므로 이 이미지는 가운데 부분만 잘라서 학습을 해도 무방
- 이런 경우 이미지를 자르면 피처의 개수가 줄어들기 때문에 나중에 학습할 때 학습 시간이 줄어들고 예측할 때 일반화 가능성이 높아진다.
3-5) 하이퍼 파라미터 튜닝
- RandomForest는 DecisionTree처럼 하이퍼 파라미터가 다양하다.
- GridSearchCV나 RandomSearchCV를 이용해서 최적의 파라미터를 찾는 작업이 중요
- 샘플의 개수가 많거나 피처의 개수가 많으면 시간이 오래 걸린다.
- 따라서 n_jobs를 -1로 설정해주는 것이 좋다.
# 하이퍼 파라미터 튜닝을 위한 파라미터 생성
- 기본은 dictionary(문자열 - 하이퍼 파라미터 이름과 리스트 - 값들로 구성)
- dictionary 내부의 list들은 전부 곱한 만큼의 조합
- 이러한 dictionary들을 list로 묶으면 별개로 동작
# GridSearchCV를 이용해 랜덤 포레스트의 하이퍼 파라미터를 튜닝
from sklearn.model_selection import GridSearchCV
params = {
'n_estimators':[100, 200, 300],
'max_depth' : [4, 6, 8, 10],
'min_samples_leaf' : [4, 6, 10],
'min_samples_split' : [8, 16, 20]
}
# RandomForestClassifier 모델 생성
rf_clf = RandomForestClassifier(random_state=42, n_jobs=-1)
#GridSearchCV 인스턴스 생성
grid_cv = GridSearchCV(rf_clf , param_grid=params , cv=3, n_jobs=-1 )
grid_cv.fit(X_train , y_train)
print('최적 하이퍼 파라미터:\n', grid_cv.best_params_)
rn_clf = grid_cv.best_estimator_
print('최적 분류기:\n', rn_clf)
최적 하이퍼 파라미터: {'max_depth': 6, 'min_samples_leaf': 4, 'min_samples_split': 8, 'n_estimators': 100} 최적 분류기: RandomForestClassifier(max_depth=6, min_samples_leaf=4, min_samples_split=8, n_jobs=-1, random_state=42) |
3-6) Extra Tree
- RandomForest에서는 트리를 만들 때 무작위로 특성의 서브 세트를 만들어서 분할에 이용하는데
분할을 하기 위해서 어떤 특성을 이용해서 분할하는 것이 효과적인지 판단하는 작업을 거치게 된다.
- 오래걸림
- 최적의 특성을 찾는 것이 아니고 일단 아무 특성이나 선택해서 분할하고 그 중에서 최상의 분할을 선택하는 방식의 트리
- 무작위성을 추가하는 방식
- RandomForest에 비해서 훈련 속도가 빠름
- 성능은 어떤 것이 좋을지 알 수 없기 때문에 모델을 만들어서 비교해야 한다.
- API: sklearn.ExtraTreesClaassifier
- 사용법은 RandomForest와 동일
from sklearn.ensemble import ExtraTreesClassifier
iris = load_iris()
rnd_clf = RandomForestClassifier(n_estimators=500, random_state=42)
rnd_clf.fit(iris["data"], iris["target"])
for name, score in zip(iris["feature_names"], rnd_clf.feature_importances_):
print(name, score)
sepal length (cm) 0.11249225099876375 sepal width (cm) 0.02311928828251033 petal length (cm) 0.4410304643639577 petal width (cm) 0.4233579963547682 |
4. Boosting
- 원래 이름은 hypothesis boosting
- 약한 학습기를 여러개 연결해서 강한 학습기를 만드는 앙상블 방법
- Bagging이나 Pasting은 여러 개의 학습기를 가지고 예측한 후 그 결과를 합산해서 예측
- 앞의 모델을 보완해 나가면서 예측기를 학습
- 종류는 여러가지
4-1) Ada Boost
- 이전 모델이 과소 적합했던 훈련 샘플의 가중치를 더 높이는 것으로 이 방식을 이용하면 새로 만들어진 예측기는 학습하기 어려운 샘플에 점점 더 맞춰지게 된다.
- 먼저 알고리즘의 기반이 되는 첫번째 분류기를 훈련 세트로 훈련시키고 예측을 만들고 그 다음에 알고리즘이 잘못 분류한 훈련 샘플의 가중치를 상대적으로 높인다.두번째 분류기는 업데이트된 가중치를 사용해서 훈련 세트에서 다시 훈련하고 다시 예측을 만들고 그 다음에 가중치를 업데이트 하는 방식
- 투표 방식이 아닌 것임
- API: sklearn. AdaBoostClassifier
- 기반 알고리즘이 predict_proba 함수를 제공한다면 클래스 확률을 기반으로 분류가 가능
- algorithm 매개변수에 SAMME.R 값 설정
- 확률을 이용하는 방식이 예측 값을 이용하는 방식보다 성능이 우수할 가능성이 높다.
# moons 데이터에 AdaBoost 적용
X, y = make_moons(n_samples=500, noise=0.30, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
from sklearn.ensemble import AdaBoostClassifier
ada_clf = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=1), n_estimators=200,
algorithm="SAMME.R", learning_rate=0.5, random_state=42)
ada_clf.fit(X_train, y_train)
plot_decision_boundary(ada_clf, X, y)
- learning_rate(학습률)는 훈련을 할 때 스텝의 크기로 이 값이 크면 최적화에 실패할 가능성이 높아지고, 낮으면 최적화에는 성공할 가능성이 높지만 훈련 속도가 느려지고 과대적합 가능성이 높아진다.
- 따라서 학습률은 그리드서치로 최적값을 찾을 필요가 있다.
4-2) Gradient Boosting
- 여러 개의 결정 트리를 묶어서 강력한 모델을 만드는 앙상블 기법
- 회귀와 분류 모두 가능
- RandomForest도 여러 개의 결정 트리를 이용하는 것은 같지만, Gradient Boosting은 이전 트리의 오차를 보완하는 방식으로 순차적으로 트리 생성
- 무작위성이 없다. 이전에 오분류한 샘플에 가중치를 부여한다. 대신 강력한 사전 가지 치기를 사용한다.
- 보통 1~5 정도 깊이의 트리를 사용하기 때문에 메모리를 적게 사용하고 에측도 빠르다.
- RandomForest 보다 매개변수 설정에 조금 더 민감하지만 잘 조정하면 더 높은 정확도를 제공한다.
- AdaBoost 가중치 업데이트를 경사 하강법을 이용해서 조정한다.
- 예측 속도는 빠르지만 훈련 속도(수행 시간)는 느리고 하이퍼 파라미터 튜닝도 좀더 어렵다.
랜덤 포레스트는 각각 훈련하기 때문에 병렬이 된다. n_jobs여럿 지정 가능.
그런데 부스팅은 순차적으로 가야하기 때문에 병렬이 불가하다.
GB의 장점은 메모리가 적고 예측도 빠르고 성능이 좋지만 훈련 시간이 문제임.
- API: sklean. GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingClassifier
import time
# GBM 수행 시간 측정을 위함. 시작 시간 설정.
start_time = time.time()
gb_clf = GradientBoostingClassifier(random_state=0)
gb_clf.fit(X_train , y_train)
gb_pred = gb_clf.predict(X_test)
gb_accuracy = accuracy_score(y_test, gb_pred)
print('GBM 정확도: {0:.4f}'.format(gb_accuracy))
print("GBM 수행 시간: {0:.1f} 초 ".format(time.time() - start_time))
GBM 정확도: 0.8880 GBM 수행 시간: 0.2 초 |
- 하이퍼 파라미터
- max_depth: 트리의 깊이
- max_features: 사용하는 피처의 비율
- n_estimators: 학습기의 개수로 기본은 100
- learning_rate: 학습률. 기본은 0.1이며 0~1사이로 설정 가능
- subsample: 샘플링 비율. 기본 값은 1인데 과대적합 가능성이 보이면 숫자를 낮추면 된다.기본은 모든 데이터로 학습
from sklearn.model_selection import GridSearchCV
params = {
'n_estimators':[100, 500],
'learning_rate' : [ 0.05, 0.1]
}
grid_cv = GridSearchCV(gb_clf , param_grid=params , cv=2 ,verbose=1)
grid_cv.fit(X_train , y_train)
print('최적 하이퍼 파라미터:\n', grid_cv.best_params_)
print('최고 예측 정확도: {0:.4f}'.format(grid_cv.best_score_))
- cv의 숫자는 교차검증에 사용할 fold의 개수로 실제 사용할 때는 조금 더 큰값 사용
- verbose는 훈련 가정에서 로그 출력 여부
Fitting 2 folds for each of 4 candidates, totalling 8 fits 최적 하이퍼 파라미터: {'learning_rate': 0.05, 'n_estimators': 100} 최고 예측 정확도: 0.8960 |
- 타이타닉
- deck 열에는 결측치가 많다.
embarked와 embark_town은 동일한 의미를 가진 컬럼
# 전처리
# load_dataset 함수를 사용하여 데이터프레임으로 변환
df = sns.load_dataset('titanic')
# IPython 디스플레이 설정 - 출력할 열의 개수 한도 늘리기
pd.set_option('display.max_columns', 15)
# NaN값이 많은 deck 열을 삭제, embarked와 내용이 겹치는 embark_town 열을 삭제
rdf = df.drop(['deck', 'embark_town'], axis=1)
# age 열에 나이 데이터가 없는 모든 행을 삭제 - age 열(891개 중 177개의 NaN 값)
rdf = rdf.dropna(subset=['age'], how='any', axis=0)
# embarked 열의 NaN값을 승선도시 중에서 최빈값으로 치환하기
# 각 범주의 개수를 구한 후 가장 큰 값의 인덱스를 가져오기
most_freq = rdf['embarked'].value_counts(dropna=True).idxmax()
# 치환
rdf['embarked'].fillna(most_freq, inplace=True)
#피처 선택
# 타겟은 survived
ndf = rdf[['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'embarked']]
# 원핫인코딩 - 범주형 데이터를 모형이 인식할 수 있도록 숫자형으로 변환
onehot_sex = pd.get_dummies(ndf['sex'])
ndf = pd.concat([ndf, onehot_sex], axis=1)
onehot_embarked = pd.get_dummies(ndf['embarked'], prefix='town')
ndf = pd.concat([ndf, onehot_embarked], axis=1)
ndf.drop(['sex', 'embarked'], axis=1, inplace=True)
# 속성(변수) 선택
X=ndf[['pclass', 'age', 'sibsp', 'parch', 'female', 'male',
'town_C', 'town_Q', 'town_S']] #독립 변수 X
# X=ndf.drop(['survived'], axis=1)
y=ndf['survived'] #종속 변수 Y
#피처의 스케일 확인
X.describe() #숫자 컬럼의 기술 통계량 확인
- 숫자 컬럼의 값들의 범위가 차이가 많이 나면 scaling을 수행하는 것이 좋다.
# 스케일링
# 설명 변수 데이터를 정규화(normalization)
from sklearn import preprocessing
X = preprocessing.StandardScaler().fit(X).transform(X)
# 훈련
# train data 와 test data로 구분(7:3 비율)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)
# 평가지표
gbrt = GradientBoostingClassifier(random_state=0)
gbrt.fit(X_train, y_train)
print("훈련 세트 정확도: {:.3f}".format(gbrt.score(X_train, y_train)))
print("테스트 세트 정확도: {:.3f}".format(gbrt.score(X_test, y_test)))
훈련 세트 정확도: 0.894 테스트 세트 정확도: 0.795 |
- 과대적합 발생: 훈련 데이터는 높고 테스트는 오히려 떨어졌다.
- n_estimators를 조정한다. 100개 정도가 적당
- max_depth도 높아지면 과대적합 가능
# 하이퍼 파라미터 변경
gbrt = GradientBoostingClassifier(n_estimators=100, max_depth=1, random_state=42)
gbrt.fit(X_train, y_train)
print("훈련 세트 정확도: {:.3f}".format(gbrt.score(X_train, y_train)))
print("테스트 세트 정확도: {:.3f}".format(gbrt.score(X_test, y_test)))
훈련 세트 정확도: 0.810 테스트 세트 정확도: 0.805 |
# 피처의 중요도 파악
# 특성 중요도 확인
print('pclass', 'age', 'sibsp', 'parch', 'female', 'male',
'town_C', 'town_Q', 'town_S')
print(gbrt.feature_importances_)
pclass age sibsp parch female male town_C town_Q town_S [0.22911763 0.11503979 0.02325801 0.01771038 0.2245367 0.38466583 0.00567166 0. 0. ] |
#별 연관이 없는 애들은 빼고 다시 훈련
X = ndf.drop(['survived', 'town_C', 'town_Q', 'town_S'], axis=1)
X = preprocessing.StandardScaler().fit(X).transform(X)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)
gbrt = GradientBoostingClassifier(n_estimators=100, max_depth=1, random_state=42)
gbrt.fit(X_train, y_train)
print("훈련 세트 정확도: {:.3f}".format(gbrt.score(X_train, y_train)))
print("테스트 세트 정확도: {:.3f}".format(gbrt.score(X_test, y_test)))
훈련 세트 정확도: 0.816 테스트 세트 정확도: 0.805 |
- 특징
- 일반적으로 성능이 우수하지만 처음부터 이 모델을 사용하는 것을 권장하지는 않는데 이유는 훈련 시간이 길기 때문
- 랜덤 포레스트나 선형 모델을 이용해서 훈련하고 평가한 후 성능이 나쁘거나 경진대회에서 마지막 성능까지 뽑아내고자 할 때 이용
- 트리 모델이므로 희소한 고차원 데이터에는 잘 작동하지 않음
- 가장 중요한 파라미터는 n_estimators, learning_rate, max_depth
gbrt = GradientBoostingClassifier(n_estimators=100, learning_rate=0.2, max_depth=2, random_state=42)
gbrt.fit(X_train, y_train)
print("훈련 세트 정확도: {:.3f}".format(gbrt.score(X_train, y_train)))
print("테스트 세트 정확도: {:.3f}".format(gbrt.score(X_test, y_test)))
훈련 세트 정확도: 0.880 테스트 세트 정확도: 0.823 |
- 회귀에 적용
- 분류에서는 잘못 분류된 데이터를 다음 학습기에서 다시 예측하는 형태로 구현
- 회귀에서는 첫번째 결정 트리 모델을 가지고 학습한 후 잔차를 구하여 다음 결정 트리 모델이 잔차를 타겟으로 해서 다시 학습을 수행하고 잔차를 구한 후 이 결과를 가지고 다음 결정 트리 모델이 훈련을 하는 방식으로 구현
- 예측값은 모든 결정 트리 모델의 예측을 더하면 됨
- GradientBoostingRegressor 제공
#샘플 데이터 생성
np.random.seed(42)
X = np.random.rand(100, 1) - 0.5
y = 3*X[:, 0]**2 + 0.05 * np.random.randn(100)
print(X[0])
print(y[0])
[-0.12545988] 0.05157289874841034 |
#첫번째 트리로 훈련
from sklearn.tree import DecisionTreeRegressor
#의사 결정 나무를 이용한 회귀
tree_reg1 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg1.fit(X, y)
print(y[0])
print(tree_reg1.predict(X[0].reshape(1, 1)))
0.05157289874841034 [0.12356613] |
#첫번째 예측기에서 생긴 잔여 오차에 두번째 DecisionTreeRegresssor를 훈련
y2 = y - tree_reg1.predict(X)
#잔차를 타겟으로 훈련
tree_reg2 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg2.fit(X, y2)
print(tree_reg2.predict(X[0].reshape(1, 1)))
[-0.09039794] |
#두번째 예측기에서 생긴 잔여 오차에 세번째 DecisionTreeRegresssor를 훈련
y3 = y2 - tree_reg2.predict(X)
tree_reg3 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg3.fit(X, y3)
print(tree_reg3.predict(X[0].reshape(1, 1)))
[0.00704347] |
#이제 세 개의 트리를 포함하는 앙상블 모델이 생겼습니다.
#새로운 샘플에 대한 예측을 만들려면 모든 트리의 예측을 더하면 됩니다.
X_new = np.array([[0.8]])
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))
print(y_pred)
[0.75026781] |
#이제 세 개의 트리를 포함하는 앙상블 모델이 생겼습니다.
#새로운 샘플에 대한 예측을 만들려면 모든 트리의 예측을 더하면 됩니다.
X_new = np.array([[0.05]])
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))
print(y_pred)
[0.04021166] |
- API 활용
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=100, learning_rate=0.05, random_state=42)
gbrt.fit(X, y)
print(gbrt.predict(X_new))
[0.00710809] |
- 확률적 그라디언트 부스팅
- 훈련 샘플의 비율을 설정하는 것
- subsample 매개변수에 훈련 샘플의 비율 설정
- 편향이 높아지는 대신 분산이 낮아지고 훈련 속도가 빨라진다.
4-3) Histogram-based Gradient Boosting
- sklearn에서 제공하는 다른형태의 Gradient Boosting
- 입력 특성을 구간으로 나누어서 정수로 대체하는 형식
- 구간의 개수는 max_bins 속성으로 설정 가능한데 기본값은 255이고 그 이상의 값은 안됨
- 구간 분할을 사용하면 학습 알고리즘이 평가해야 하는 임계값의 개수가 줄어들게 되고 정수로 작업을 수행하기 때문에 속도가 빠르다.
- 대규모 데이터 셋에서 빠르게 훈련할 때 사용
- API: HistGradientBoostingRegressor, HistGradientBoostingClassifier
- 사용 방법 거의 동일
- 일반 그라디언트 부스팅과의 차이점
- 샘플의 개수가 1000개가 넘으면 자동 조기종료 기능 활성화
- 조기종료 기능은 훈련을 하다가 현재 점수보다 그다지 좋아지지 않으면 훈련 종료
- n_estimators 매개변수 대신에 max_iter로 변경
- 조정할 수 있는 파라미터도 줄어듬
from sklearn.ensemble import HistGradientBoostingClassifier
hgbrt = HistGradientBoostingClassifier(max_iter=100, max_depth=3, random_state=42)
hgbrt.fit(X_train, y_train)
print("훈련 세트 정확도: {:.3f}".format(hgbrt.score(X_train, y_train)))
print("테스트 세트 정확도: {:.3f}".format(hgbrt.score(X_test, y_test)))
훈련 세트 정확도: 0.957 테스트 세트 정확도: 0.888 |
5. XG Boosting (eXtra Gradient Boosting)
- https://github.com/dmlc/xgboost
- 트리 기반의 앙상블 학습
- 케글 경연 대회에서 상위를 차지한 경우에 이 모델 많이 사용
- 분류에 있어서 다른머신러닝 알고리즘보다 뛰어난 예측 성능
- Gradient Boosting에 기반하지만 느린 훈련속도와 과적합 규제에 대한 부분 보완
- 병렬 학습 가능
- 자체 내장된 교차 검증 수행
- 결측값도 자체 처리
5-1) 하이퍼 파라미터
- 일반 파라미터: 일반적으로 실행 시 스레드의 개수 silent 모드 (로그 출력x) 등의 선택을 위한 파라미터로 기본 값을 거의 변경하지 않음
- 부스터 파라미터: 트리 최적화, 규제 등의 파라미터
- 학습 태스크 파라미터: 학습 수행 시의 객체 함수나 평가를 위한 지표 등을 설정
#설치
pip install xgboost
5-2) 훈련과 검증에 사용하는 데이터
- API: DMatrix
- data 매개변수에 피처, label 매개변수에 타겟 설정
- 위스콘신 유방암 데이터
- X-ray 촬영한 사진을 수치화한 데이터
- 사진을 직접 이용하는게 아니라 사진에서 필요한 데이터를 추출해서 수치화해서 사용하는 경우가 많다.
- 이러한 라벨링 작업에 Open CV를 많이 활용한다.
- 타겟이 0이면 악성(malignant), 1이면 양성(benign)
# 데이터 가져오기
import xgboost as xgb
from xgboost import plot_importance
from sklearn.datasets import load_breast_cancer
dataset = load_breast_cancer()
X_features= dataset.data
y_label = dataset.target
cancer_df = pd.DataFrame(data=X_features, columns=dataset.feature_names)
cancer_df['target']= y_label
cancer_df.head(3)
#레이블 분포 확인 - 층화 추출이나 오버나 언더샘플링 여부를 판단하기 위해서 수행함
print(dataset.target_names)
print(cancer_df['target'].value_counts())
['malignant' 'benign'] target 1 357 0 212 Name: count, dtype: int64 |
#훈련 데이터와 테스트 데이터 분리
# 전체 데이터 중 80%는 학습용 데이터, 20%는 테스트용 데이터 추출
X_train, X_test, y_train, y_test=train_test_split(X_features, y_label,
test_size=0.2, random_state=156 )
print(X_train.shape , X_test.shape)
(455, 30) (114, 30) |
#xgboost가 사용할 수 있는 형태로 데이터 변경
dtrain = xgb.DMatrix(data=X_train , label=y_train)
dtest = xgb.DMatrix(data=X_test , label=y_test)
#하이퍼 파라미터 생성
params = { 'max_depth':3,
'eta': 0.1, #학습률
'objective':'binary:logistic', #0하고 1이니까 이진분류
'eval_metric':'logloss' #평가지표
}
num_rounds = 500 #예측횟수
#모델 생성 후 훈련
# train 데이터 셋은 ‘train’ , evaluation(test) 데이터 셋은 ‘eval’ 로 명기합니다.
wlist = [(dtrain,'train'),(dtest,'eval') ]
# 하이퍼 파라미터와 early stopping 파라미터를 train( ) 함수의 파라미터로 전달
xgb_model = xgb.train(params = params , dtrain=dtrain , num_boost_round=num_rounds ,early_stopping_rounds=100, evals=wlist )
- early_stopping_rounds - 조기 종료 기능 설정
- 일반 그라디언트 부스팅은 예측기의 개수를 설정하면 무조건 예측기의 개수만큼 훈련 (무조건 500번)
중간에 조기종료를 하려면 매 학습시마다 점수를 가져와서 점수가 더 이상 좋아지지 않으면 종료되도록 알고리즘 구현
- 더이상 해봐야 이거보다 좋아지지 않는다.
# 예측을 수행하면 1로 판정할 확률 리턴
- 예측 값을 출력할 때는 클래스로 변환해서 출력해야 함
# predict( ) 수행 결과값을 10개만 표시, 예측 확률 값으로 표시됨
print(np.round(pred_probs[:10],3)) #소수 셋째짜리까지
[0.904 0.004 0.908 0.267 0.992 1. 1. 0.999 0.994 0. ] |
# 예측 확률이 0.5 보다 크면 1 , 그렇지 않으면 0 으로 예측값 결정하여 List 객체인 preds에 저장
preds = [ 1 if x > 0.5 else 0 for x in pred_probs ]
print('예측값 10개만 표시:',preds[:10])
예측값 10개만 표시: [1, 0, 1, 0, 1, 1, 1, 1, 1, 0] |
#범주형 평가 지표 출력
- 정확도, 정밀도, 재현율, 정밀도와 재현율의 조화 평균(f1-score), roc_auc
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.metrics import precision_score, recall_score
from sklearn.metrics import f1_score, roc_auc_score
#오차 행렬 출력
confusion = confusion_matrix( y_test, preds)
print('오차행렬:\n', confusion)
#정확도: 전체 데이터에서 맞게 분류한 것의 비율
accuracy = accuracy_score(y_test , preds)
print('정확도:', accuracy)
#정밀도: 검색된 문서들 중 관련있는 문서들의 비율 (True로 판정한 것 중 실제 True인 것의 비율)
precision = precision_score(y_test , preds)
print('정밀도:', precision)
#재현율: 관련된 문서들 중 검색된 비율 (실제 True인 것 중 True로 판정한 것의 비율)
recall = recall_score(y_test , preds)
print('재현율:', recall)
#recall과 precision의 조화 평균 (f1_score)
f1 = f1_score(y_test,preds)
print('f1_score:', f1)
#roc_auc score - area under curve. 그래프 곡선 아래 면적
#1에 가까울수록 좋은 성능
roc_auc = roc_auc_score(y_test, pred_probs)
print('roc_auc:', roc_auc)
오차행렬: [[59 2] [12 52]] 정확도: 0.888 정밀도: 0.9629629629629629 재현율: 0.8125 f1_score: 0.8813559322033898 roc_auc: 0.9723360655737704 |
# 피처의 중요도 출력
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 10))
plot_importance(xgb_model, ax=ax)
5-3) Light GBM
- XGBoost는 매우 뛰어난 부스팅이지만 여전히 학습 시간이 길다.
- GridSearchCV를 이용해서 하이퍼 파라미터 튜닝을 하다보면 수행 시간이 너무 오래 결러셔 많은 파라미터를 튜닝하기에 어려움을 겪게 된다.
- XGBoost보다 학습에 걸리는 시간이 훨씬 적고 메모리 사용량도 상대적으로 적으면서도 비슷한 성능 발휘
- 10000건 이하의 데이터 세트에 적용하면 과대적합이 발생할 가능성이 높음
- 다른 부스팅 알고리즘은 균형 트리를 만들려고 하는 경향이 있는데 이유는 과대적합을 방지하기 위해서인데 이것 때문에 훈련 시간이 오래 걸림
- Light GBM은 균형 잡히 트리를 만들려고 하지 않고 리프 중심 트리 분할을 수행
- 불균형 트리를 만들어서 사용한다.
- 데이터가 아주 많다면 리프에 배치되는 샘플의 개수도 많아질 가능성이 높기 때문에 과대 적합이 잘 발생하지 않는다.
- 설치
pip install lightgbm
- 4.x 버전이 설치가 된다.
- 조기종료가 콜백의 형태
- 로그 출력 옵션 없어짐
pip install lightgbm ==3.3.2
- 조기 종료가 파라미터 형태
- 로그 출력 옵션 있음
- 하이퍼 파라미터
- xbboost와 거의 유사
- xgboost와 다른 점은 파이썬 래퍼를 사용하면 데이터를 이전처럼 numpy의 ndarray로 지정
- 위스콘신 유방암
from lightgbm import LGBMClassifier
import lightgbm
dataset = load_breast_cancer()
ftr = dataset.data
target = dataset.target
# 전체 데이터 중 80%는 학습용 데이터, 20%는 테스트용 데이터 추출
X_train, X_test, y_train, y_test=train_test_split(ftr, target, test_size=0.2, random_state=156 )
print(X_train.shape, X_test.shape)
# 검증에 사용할 데이터 생성
evals = [(X_test, y_test)]
(455, 30) (114, 30) |
# 모델 생성 훈련
lgbm_wrapper = LGBMClassifier(n_estimators=1000)
lgbm_wrapper.fit(X_train, y_train, early_stopping_rounds=100, eval_metric="logloss",
eval_set=evals)
preds = lgbm_wrapper.predict(X_test)
#검증 이하생략
오차행렬: [[33 4] [ 1 76]] 정확도: 0.956140350877193 정밀도: 0.95 재현율: 0.987012987012987 f1_score: 0.9681528662420381 roc_auc: 0.995085995085995 |
#피처의 중요도 시각화
# plot_importance( )를 이용하여 feature 중요도 시각화
from lightgbm import plot_importance
fig, ax = plt.subplots(figsize=(10, 12))
plot_importance(lgbm_wrapper, ax=ax)
6. Stacking
- 부스팅이나 랜덤 포레스트를 예측기의 예측을 취합해서 무언가 동작(투표, 확률, 평균 등)을 수행하는데 예측을 취합하는 모델을 훈련시키려고 하는 방식
- 개별 알고리즘의 예측 결과 데이터 세트를 최종적인 메타 데이터를 만들고 이 데이터를 가지고 별도의 ML 알고리즘으로 최종 학습을 수행하고 테스트 데이터를 기반으로 다시 최종 예측을 수행하는 방식
- 개별 알고리즘의 예측 결과를 가지고 훈련해서 최종 결과를 만들어내는 예측기 Blender 생성
- 블랜더는 홀드 아웃 세트를 이용해서 학습
- 스태킹에서는 두 종류의 모델이 필요한데 하나는 개별적인 기반 모델이고 다른 하나는 기반 모델의 예측 데이터를 학습 데이터로 만들어서 학습하는 최종 메타 모델
- API: sklearn 스태킹 지원 안함. 직접 구현하거나 오픈소스 시용
6-1) 위스콘신 유방암 데이터를 이용해서 스태킹 구현
- 개별 학습기: KNN, RandomForest, AdaBoost, DecisionTree
- 최종 학습기: LogisticRegression
# 데이터 가져오기
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_train , X_test , y_train , y_test = train_test_split(X_data , y_label , test_size=0.2 , random_state=0)
# 예측기 생성
# 개별 ML 모델을 위한 Classifier 생성.
knn_clf = KNeighborsClassifier(n_neighbors=4)
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0)
dt_clf = DecisionTreeClassifier()
ada_clf = AdaBoostClassifier(n_estimators=100)
# 최종 Stacking 모델을 위한 Classifier생성.
lr_final = LogisticRegression(C=10)
# 개별 모델 학습
# 개별 모델들을 학습.
knn_clf.fit(X_train, y_train)
rf_clf.fit(X_train , y_train)
dt_clf.fit(X_train , y_train)
ada_clf.fit(X_train, y_train)
# 개별 학습기 가지고 예측한 후 정확도 확인
# 학습된 개별 모델들이 각자 반환하는 예측 데이터 셋을 생성하고 개별 모델의 정확도 측정.
knn_pred = knn_clf.predict(X_test)
rf_pred = rf_clf.predict(X_test)
dt_pred = dt_clf.predict(X_test)
ada_pred = ada_clf.predict(X_test)
print('KNN 정확도: {0:.4f}'.format(accuracy_score(y_test, knn_pred)))
print('Random Forest 정확도: {0:.4f}'.format(accuracy_score(y_test, rf_pred)))
print('Decision Tree 정확도: {0:.4f}'.format(accuracy_score(y_test, dt_pred)))
print('AdaBoost 정확도: {0:.4f}'.format(accuracy_score(y_test, ada_pred)))
KNN 정확도: 0.9211 랜덤 포레스트 정확도: 0.9649 결정 트리 정확도: 0.9123 에이다부스트 정확도: 0.9561 |
pred = np.array([knn_pred, rf_pred, dt_pred, ada_pred])
print(pred.shape)
# transpose를 이용해 행과 열의 위치 교환. 컬럼 레벨로 각 알고리즘의 예측 결과를 피처로 만듦.
pred = np.transpose(pred)
print(pred.shape)
(4, 114) (114, 4) |
#최종 모델의 정확도 확인
lr_final.fit(pred, y_test)
final = lr_final.predict(pred)
print('최종 메타 모델의 예측 정확도: {0:.4f}'.format(accuracy_score(y_test , final)))
최종 메타 모델의 예측 정확도: 0.9737 |
'Python' 카테고리의 다른 글
[Python] 차원 축소 (0) | 2024.03.12 |
---|---|
[Python] 지도학습 연습 _ 범주형 데이터 이진분류 (0) | 2024.03.07 |
[Python] 회귀 - 비선형 회귀 (0) | 2024.03.07 |
[Python] 분류 (0) | 2024.03.07 |
[Python] 머신러닝 (0) | 2024.03.07 |