저는 어차피 머신러닝을 공부할꺼 제대로 공부해보자 다짐하고 WIL이지만 블로그 글처럼 써보려고 합니다….(스압 주의^^)

2.3.5 결정트리

  • 개념
    결정 트리(decision tree) : 특정 기준(질문)에 따라 데이터를 구분하는 모델을 결정 트리 모델
    노드 : 질문이나 정답을 담은 네모 상자
    ▪ 리프(leaf) : 마지막 노드
    ▪ 루프 노드(root node) : 맨 위의 노드, 전체 데이터 셋과 처음 분류 기준이 포함
    ▪ 순수 노드(pure node) : 한 개의 타깃 값(하나의 클래스나 하나의 회귀 분석 결과)로 이루어진 리프 노드
    에지(edge) : 질문의 답과 다음 질문을 연결
    테스트 : 결정 트리에 있는 질문들 (테스트 세트와 혼동하지 않기)



결정트리 만들기



STEP1. 모든 테스트에서 타깃 값에 대해 가장 많은 정보를 가진 특성을 이용해 테스트(x[1]<=0.06)한다.


STEP2. 각 노드에 대해 데이터를 잘 구분할 수 있는 테스트를 추가한다. 이 때 결정 트리의 리프에 한 개의 타깃 값(여기서는 클래스)을 가질 때 즉, 순수 노드가 될 때까지 반복한다.


STEP3. 최종 분할 트리의 모습!


STEP4. 새로운 데이터 포인트에 대해 예측
◽ 분류
: 주어진 데이터 포인트가 특성을 분할한 영역 중 어디에 놓이는가?
: 영역의 타깃 값 중 다수(순수 노드라면 하나)인 것을 예측 결과로 한다

◽ 회귀
: 각 노드의 테스트 결과에 따라 트리를 탐색하고 찾은 리프 노드의 훈련 데이터 평균값을 예측 결과로 한다


결정트리의 복잡도 제어하기

1) 문제점

  • 모든 리프 노드가 순수 노드가 될 때까지 진행하면 모델이 복잡해지고 훈련 데이터에 과대적합 되어 새로운 데이터에 일반화 되지 않는다.
  • 결정경계가 클래스의 포인트들에서 멀리 떨어진 이상치에 민감.(클래스 0으로 결정된 영역이 클래스 1에 속한 포인트들로 둘러쌓인 이상치까지 판별하고 있음)


2) 해결방법

  • 사전 가지치기
    트리 생성을 일찍 중단하는 전략
    트리의 최대 깊이리프의 최대 개수를 제한
    노드가 분할하기 위한 포인트의 최소 개수를 지정
  • 사후 가지치기(가치지기)
    트리를 만든 후 데이터 포인트가 적은 노드 삭제 혹은 병합


참고
scikit-learn에서 결정트리는 DecisionTreeRegressor와 DecisionTreeClassifier에 구현
sckit-learn은 사전 가지치기만 지원


3) 실습- 유방암 데이터셋을 이용하여 사전 가지치기의 효과를 확인해보자!

from sklearn.tree import DecisionTreeClassifier # 결정 트리 가져오기 

cancer = load_breast_cancer()   

# 훈련 세트와 테스트 세트로 나누기
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, stratify=cancer.target, random_state=42)

#기본값 설정으로 완전한 트리 모델 생성
tree = DecisionTreeClassifier(random_state=0) #random_state=0으로 하여 트리를 같은 조건으로 비교
tree.fit(X_train, y_train) # 훈련시킨다! 

print("훈련 세트 정확도: {:.3f}".format(tree.score(X_train, y_train)))
print("테스트 세트 정확도: {:.3f}".format(tree.score(X_test, y_test)))
훈련 세트 정확도: 1.000
테스트 세트 정확도: 0.937

Result
모든 리프 노드가 순수 노드이기 때문에 훈련 세트의 정확도는 100%
테스트 세트의 정확도는 선형 모델에서의 정확도보다 낮다



# 결정 트리의 깊이 제한으로 과대 적합 줄이기 
tree = DecisionTreeClassifier(max_depth=4, random_state=0) #max_depth로 제한 -> 연속된 질문 최대 4개로 제한 
tree.fit(X_train, y_train)

print("훈련 세트 정확도: {:.3f}".format(tree.score(X_train, y_train)))
print("테스트 세트 정확도: {:.3f}".format(tree.score(X_test, y_test)))
훈련 세트 정확도: 0.988
테스트 세트 정확도: 0.951

Result
훈련 세트의 정확도를 떨어뜨리지만 테스트 세트의 성능은 개선!



결정 트리 시각화

1) 시각화

  • tree 모듈의 export_graphviz 함수를 이용함
  • 그래프 저장용 텍스트 파일 포맷인 .dot 파일 생성
  • export_graphviz 함수에 filled 매개변수를 True로 지정하면 노드의 클래스가 구분되도록 칠해준다

2) 실습

 from sklearn.tree import export_graphviz 
 # 결정 트리, 생성되는 파일이름, 클래스이름, 특성 이름, filled는 노드에 색이 칠해짐
export_graphviz(tree, out_file="tree.dot", class_names=["악성", "양성"],
                feature_names=cancer.feature_names, impurity=False, filled=True)
 import graphviz # graphviz 모듈을 사용해 시각화!

with open("tree.dot") as f:
    dot_graph = f.read()
display(graphviz.Source(dot_graph))
 # plot_tree() 함수를 사용하면 .dot 파일을 만들지 않고 바로 트리를 그릴 수 있음!
 from sklearn.tree import plot_tree

plot_tree(tree, class_names=["악성", "양성"], feature_names=cancer.feature_names,
         impurity=False, filled=True, rounded=True, fontsize=4)
plt.show()



트리의 특성 중요도

1) 특성 중요도(feature importance)

  • 트리를 만드는 결정에 각 결정에 각 특성이 얼마나 중요한지 평가
  • 0과 1 사이의 양수(0: 전혀 사용되지 않았다 / 1: 완벽하게 타깃 클래스를 예측)
  • 트리가 너무 커서 전체 트리를 살펴보는게 어려울 때 확인
print("특성 중요도:\n", tree.feature_importances_)
특성 중요도:
 [0.    0.    0.    0.    0.    0.    0.    0.    0.    0.    0.01  0.048
 0.    0.    0.002 0.    0.    0.    0.    0.    0.727 0.046 0.    0.
 0.014 0.    0.018 0.122 0.012 0.   ]



이를 시각화 해보면

def plot_feature_importances_cancer(model):
    n_features = cancer.data.shape[1]
    plt.barh(np.arange(n_features), model.feature_importances_, align='center')
    plt.yticks(np.arange(n_features), cancer.feature_names)
    plt.xlabel("특성 중요도")
    plt.ylabel("특성")
    plt.ylim(-1, n_features)

plot_feature_importances_cancer(tree)

image

분석
worst radius(첫 번째 노드에서 사용한 특성)가 가장 중요한 특성임
-> 첫 번째 노드에서 두 클래스를 잘 나누고 있다.

2) 한계

  • 특성 중요도는 해당 특성이 어떤 클래스를 지지, 결정하는지 알 수 없다.(이건 트리를 봐야 함) EX) worst radius가 중요하다고 알려주지만 높은 반지름이 양성(클래스0)인지 악성(클래스1)인지 의미할 수 없다.
    cf) 특성과 클래스 사이의 관계는 단순하지 않고 복잡할 수 있음

  • 회귀 결정 트리, 즉 DecisionTreeRegressor는 외삽(훈련 데이터의 범위 밖의 새로운 데이터 포인트)에 대해 예측할 수 없음

분석
선형 모델은 테스트 데이터를 꽤 정확히 예측하지만 트리 모델은 훈련 데이터를 완벽하게 예측한다. 그러나 모델이 가진 데이터 범위 밖으로 나가면 마지막 포인트를 이용해 예측..


장단점과 매개변수

👍장점
첫째, 만들어진 모델을 쉽게 시각화 가능
둘째, 데이터 스케일에 구애받지 않아 결정 트리에서 특성의 정규화나 표준화 같은 전처리 과정이 필요 없음(이진 특성과 연속적인 특성이 혼합되어도 잘 작동)

👎단점
사전 가지치기를 사용해도 과대적합되는 경향이 있어 일반화 성능이 좋지 않다.


보충

엔트로피와 불순도에 대해서 추후.. 작성하도록 하겠음..


2.3.6 결정 트리의 앙상블

앙상블 = 머신러닝 모델을 연결하여 더 강력한 모델을 만드는 기법

다음은 분류와 회귀 문제의 다양한 데이터셋에서 효과적인 두 앙상블 모델

랜덤 포레스트

1) 랜덤 🌲포레스트🌲?

  • 결정 트리가 훈련 데이터에 과대적합된다는 단점을 해결!
  • 조금씩 다른 여러 결정 트리의 묶음
  • 서로 다른 방향으로 과대적합된 트리를 많이 만들고 그 결과를 평균냄 -> 모델의 예측 성능이 유지되면서 과대적합이 줄어듬
  • 핵심 포인트!! 각 트리가 고유하게 만들어지도록 무작위한 선택을 함!!


2) 랜덤 포레스트 구축

STEP1) 생성할 트리의 개수 정하기

  • RandomForestRegressor나 RandomForestClassifier의 n_estimators 매개변수

STEP2) 부트스트랩 샘플 생성

  • 트리를 만들 때 사용하는 데이터 포인트를 무작위로 선택
  • n_samples 개의 데이터 포인트 중에서 무작위로 테이터를 n_samples 횟수만큼 반복 추출(한 샘플이 여러 번 중복 추출될 수 있음)
  • 데이터 셋은 원래 데이터셋 크기와 같지만 모든 데이터 포인트가 들어있지 않다!(중복,, 누락,,)

STEP3) 분할 테스트에서 특성을 무작위로 선택

  • max_features 매개변수로 고를 특성의 개수를 조정
    ❌주의❌
     ◽ max_features = n_feautres로 설정하면 트리의 각 분기에서 모든 특성을 고려하므로 특성 선택에 무작위성이 들어가지 않는다   
     ◽ max_features = 1로 설정하면 트리의 분기는 테스트할 특성을 고를 필요가 없고 그냥 무작위로 선택한 특성을 활용    
     ◽ max_features 값을 크게 하면 랜덤 포레스트의 트리는 매우 비슷해지고 가장 두드러진 특성을 이용해 데이터에 맞춰질 것임!    
     ◽ max_features를 작게 하면 랜덤 포레스트 트리들은 많이 달라지고 각 트리는 깊이가 깊어진다(해당 특성에 데이터를 맞추기 위해서)   
    
  • 각 노드에서 후보 특성을 무작위로 선택하고 이 후보들 중에서 최선의 테스트(타깃 값에 많은 데이터를 갖는 특성을 선택)를 찾는다.
  • 후보 특성을 고르는 것은 매 노드마다 반복되므로 트리의 각 노드는 다른 후보 특성들을 사용하여 테스트를 만든다.


3) 회귀 VS 분류
알고리즘이 모델에 있는 모든 트리의 예측을 만들고…

◽ 분류 : 약한 투표 전략을 사용 = 각 트리들이 예측한 확률을 평균내어 가장 높은 확률을 가진 클래스를 선정
◽회귀 : 예측들을 평균하여 최종 예측을 만듬


4) 실습
트리 5개로 구성된 랜덤 포레스트 모델을 만들어 보자!!

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_moons

X, y = make_moons(n_samples=100, noise=0.25, random_state=3) 
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)

forest = RandomForestClassifier(n_estimators=5, random_state=2) 
#트리를 5개 생성
forest.fit(X_train, y_train)
# 랜덤 포레스트 안에 만들어진 트리는 extimators_ 속성에 저장
fig, axes = plt.subplots(2, 3, figsize=(20, 10))
for i, (ax, tree) in enumerate(zip(axes.ravel(), forest.estimators_)):
    ax.set_title("트리 {}".format(i))
    mglearn.plots.plot_tree_partition(X, y, tree, ax=ax)
    
mglearn.plots.plot_2d_separator(forest, X, fill=True, ax=axes[-1, -1], alpha=.4)
axes[-1, -1].set_title("랜덤 포레스트")
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.show() 

image

5) 매개변수

  • random_state
    지정하거나 지정하지 않으면 전혀 다른 모델이 만들어진다.
    랜덤 포레스트 트리가 많을 수록 random_state 값의 변화에 따른 변동이 적다
    같은 결과를 만들고 싶으면 random_state 값을 고정
  • n_estimators
    클수록 좋다! 더 많은 트리를 평균하면 과대적합을 줄여 안정적인 모델을 만듬!
    단, 더 많은 메모리와 긴 훈련 시간으로 이어짐
  • max_features
    각 트리가 얼마나 무작위가 될지를 결정
    일반적으로 기본값(auto)을 쓰는 것이 좋다
    분류 max_features=sqrt(n_features) / 회귀 max_features=n_features


그레이디언트 부스팅 회귀 트리

1) 그레이디언트 부스팅 회귀 트리?

  • 이전 트리의 예측과 타깃 값 사이의 오차를 줄이는 방향으로 순차적으로 새로운 트리를 추가하는 알고리즘
    ❗손실 함수를 정의 & 경사 하강법을 사용하여 다음에 추가될 트리가 예측해야 할 값을 보정❗
  • 무작위성이 없으며 대신 강력한 사전 가지치기가 사용
  • 보통 하나에서 다섯 정도의 깊지 않은 트리를 사용 -> 메모리를 적게 사용하고 예측도 빠름
  • 얕은 트리 같은 간단한 모델(약한 학습기)을 많이 연결 -> 데이터 일부에 대해서 예측을 수행
    -> 트리가 많이 추가될수록 성능이 좋아짐
  • 회귀와 분류 모두 사용 가능


2) 실습

from sklearn.ensemble import GradientBoostingClassifier

gbrt = GradientBoostingClassifier(random_state=0, max_depth=1) # 트리의 최대 깊이를 줄여 사전 가지치기를 강하게 함
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.991
테스트 세트 정확도: 0.972
gbrt = GradientBoostingClassifier(random_state=0, learning_rate=0.01) # 학습률을 낮춘다
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.988
테스트 세트 정확도: 0.965

분석
모델의 복잡도를 감소시키므로 훈련 세트의 정확도가 낮아짐
이 예시에서는 학습률을 낮추는 것이 테스트 세트의 성능을 조금밖에 개선 못함 반면, 트리의 최대 깊이를 낮추는 것은 모델 성능 향상에 크게 기여!!


3) 매개변수

  • learning_rate
    이전 트리의 오차를 얼마나 강하게 보정할 것인지 보정 정도를 조절
    학습률이 크면 트리는 보정을 강하게 하여 복잡한 모델을 만든다
    학습률을 낮추면 비슷한 복잡도의 모델을 만들기 위해 더 많은 트리를 추가해야 한다
  • n_estimators
    트리의 개수를 지정
    앙상블에 트리가 많이 추가되어 모델의 복잡도가 커지고 훈련세트의 오차를 바로잡을 기회가 많아짐
    estimators의 값이 클수록 모델이 복잡해지고 과대적합될 가능성이 있음!!!

    Tip! 가용한 시간과 메모리 한도에서 n_estimators를 맞추고 적절한 learning_rate를 찾는다

  • n_iter_no_change, validation_fraction
    조기 종료를 위한 매개변수
    훈련 데이터에서 validation_fraction(기본값 0.1) 비율만큼 검증 데이터로 사용 -> n_iter_no_change 반복 동안 검증 점수가 향상되지 않으면 훈련 종료
    n_iter_no_change 기본값이 None이면 조기 종료를 사용하지 않는다.


2.3.7 배깅, 엑스트라 트리, 에이다부스트

sckit-learn이 제공하는 다른 앙상블 알고리즘들!

배깅

1) 배깅(Bootstrap aggregating)

  • 중복을 허용한 랜덤 샘플링으로 만든 훈련 세트를 사용하여 분류기에 각기 다르게 학습
  • 분류기가 predict_proba() 메서드를 지원 하면 -> 확률값을 평균하여 예측 수행
    지원하지 않으면 -> 가장 빈도가 높은 클래스 레이블이 예측 결과가 됨

2) 실습

from sklearn.linear_model import LogisticRegression #로지스틱 회귀 모델을 100개 훈련해보겠음!
from sklearn.ensemble import BaggingClassifier
bagging = BaggingClassifier(LogisticRegression(), n_estimators=100, 
                            oob_score=True, n_jobs=-1, random_state=42)
# 로지스틱 회귀 객체를 기반 분류기(LogisticRegression())로 전달, 훈련할 분류기 개수는 100개(n_estimators=100)
# oob_score=True : 부트스트래핑에 포함되지 않은 샘플을 기반으로 훈련된 모델 평가, 테스트 세트 성능 짐작 가능!
bagging.fit(Xc_train, yc_train)
print("훈련 세트 정확도: {:.3f}".format(bagging.score(Xc_train, yc_train)))
print("테스트 세트 정확도: {:.3f}".format(bagging.score(Xc_test, yc_test)))
print("OOB 샘플의 정확도: {:.3f}".format(bagging.oob_score_))
훈련 세트 정확도: 0.953
테스트 세트 정확도: 0.951
OOB 샘플의 정확도: 0.946

엑스트라 트리

1) 엑스트라 트리(Extra-Trees)

  • 랜덤 포레스트와 비슷하지만 후보 특성을 무작위로 분할한 다음 최적의 분할을 찾는다.
  • DecisionTreeClassifier(splitter=’random’)을 사용하고 부트스트랩 샘플링은 적용하지 않는다. (splitter=’random’은 무작위로 분할한 후보 노드 중 최선의 분할)
  • 다른 방식으로 모델에 무작위성을 주입!! 예측방식은 랜덤 포레스트와 동일하게 각 트리가 만든 확률값을 평균한다.

2) 엑스트라 트리 VS 랜덤 포레스트
image
[출처: 끙정의 데이터 사이언스]

랜덤 포레스트는 주어진 Feature에 대한 Partition을 계산하고 비교해서 최선의 Feature를 선택하여 Node를 분할
엑스트라 트리는 Feature 중에서 아무거나 고른 후에 Feature에 대해서만 최적의 분할을 찾아 Node를 분할

에이다부스트

1) 에이다부스트(Adaptive Boosting)

  • 훈련된 각 모델은 성능에 따라 가중치가 부여!
  • 그레이디언트 부스팅처럼 약한 학습기를 사용
  • 이전의 모델이 잘못 분류한 샘플에 가중치를 높여서 다음 모델을 훈련(그레디언트 부스팅과 차이)
  • 예측할 때는 모델이 예측한 레이블을 기준으로 모델의 가중치를 합산하여 가장 높은 값을 가진 레이블을 선택!
  • AdaBoostClassifier의 기본값 = DecisionTreeClassifier(max_depth=1)를 사용
    AdaBoostRegressor의 기본값 = DecisionTreeRegressor(max_depth=3)을 사용
  • base_estimator 매개변수로 다른 모델을 지정할 수 있음!

시간 관계상 늦게 필기를 할 것 같습니다.. 곧 업데이트 할 것이니 기다려주세요~ㅠㅠ

2.3.8 커널 서포트 벡터 머신

선형 모델과 비선형 특성

커널 기법

SVM 이해하기

SVM 매개변수 튜닝

SVM을 위한 데이터 전처리

장단점과 매개변수

2.3.9신경망(딥러닝)

신경망 모델

신경망 튜닝

장단점과 매개변수

신경망의 복잡도 추정

2.4 분류 예측의 불확실성 추정

2.4.1 결정함수

2.4.2 예측 확률

2.4.3 다중 분류에서의 불확실성