현재 나는 시계열 데이터로 만성질환을 예측하는 프로젝트에 참여하고 있다. 건강 관련 사용할 수 있는 수많은 변수들이 있는데 통계적 기법,머신러닝 기법을 섞어서 feature extraction 을 진행해보고자 의사결정나무계열을 사용하게 되었다. (아직 참 많이 부족하기 때문에 미래는 모르겠고 그저 내게 주어진 연구 과제 하나하나에서 최대한 끌어낼 수 있는 만큼 지식도 경험도 얻어가고 싶은 생각 뿐,,)

스크린샷 2021-06-12 오후 3 56 23

김성범 교수님 유튜브 강의와 statquest 를 참고하여서 공부하였습니다

의사결정나무

의사 결정 나무의 각 노드에 제시된 분류 기준을 따라 내려가다보면 특정 끝 노드에 다다른다. 그 끝 노드의 집단의 대표 값이 최종 예측값이 된다. (예측 모델의 경우) , 분류 모델의 경우 끝 노드의 집단으로 해당 인풋 값이 분류된다.

스크린샷 2021-06-12 오후 4 10 45

분류모델이라고 하면 대략적으로 이런 느낌이라고 볼 수 있다.

예측 모델의 경우 인풋 값이 질문을 타고 타고 내려가서 맨 마지막에 다다른 끝 노드의 대표 값을 Cm 이라고 한다면

Cm ; 데이터를 m개로 분할하였을 때 (=m개의 끝 노드로 분할하였을 때) m번 끝 노드의 대표 값

비용함수는 min(예측값-Cm*I(x∈Rm))^2 이라고 할 수 있을 것이다.

Cm*I(x∈Rm) 에서 이때 인디케이터 함수는 해당 인풋 값이 특정 노드에 해당할 때만 그 대표값을 고려해주고 나머지는 다 0으로 만들어버린다. 시그마도 붙여야하는데 귀찮아서 생략하였다 ㅠ

이 비용함수를 최소화시키려면 각 끝 노드들의 대표값은 각 분할에 속해있는 y값들의 평균이 되어야한다.

분류 모델의 경우에는 위 디즈니를 예시로 든다면 다양한 디즈니 캐릭터들이 같은 끝 노드에 섞여있을 것이다. (디즈니 공주 vs 디즈니 빌런) 이 때 해당 끝 노드에 빌런 캐릭터가 더 많다면 해당 끝 노드는 빌런 클래스로 분류된다.

분류모델의 비용함수의 경우 크로스엔트로피나, 지니 계수,misclassification rate 을 활용할 수 있다.

스크린샷 2021-06-12 오후 4 37 38

classification error vs entropy

스크린샷 2021-06-12 오후 4 44 43

왼쪽 영역에서는 28 vs 42 -> 42가 더 많으므로 28개는 잘못 분류되었다.

오른쪽 영역에서는 12 vs 38 -> 38 개가 더 많으므로 12개는 잘못 분류되었다.

스크린샷 2021-06-12 오후 4 56 17

Information Gain ; 특정 (분할변수,분할점)을 사용하였을 때 엔트로피가 얼마나 변하는지 (클 수록 좋은 것 !)

원래 엔트로피 - 특정 변수를 사용했을 때의 엔트로피

; 특정 (분할변수,분할점)을 사용했을 때의 엔트로피가 원래 엔트로피와 거의 비슷하다 = 해당 변수는 유용하지 않다

이번에는 중간 노드까지 엔트로피를 계산해준다면 (빨간색 부분이 중간 노드) 맨 위 노드의 엔트로피와 값이 차이가 난다. 즉 아직 엔트로피가 감소할 여지가 있다. 따라서 주황색 노드까지 분류를 해야지만 자식 노드의 불순도가 0이 된다.

분할변수와 분할점

그러면 끝 노드까지 내려가는 분할변수와 분할점은 어떻게 결정할까?

중간 노드가 ‘변수 X1 이 2 이상입니까?’ 였다면

분할변수는 X1 이 되고 분할점은 2가 될 것이다. multivariate 이라면 분할변수가 될 수 있는 다양한 변수가 존재하고, 분할점으로 선택할 수 있는 숫자 또한 옵션이 많다.

결론은 다 계산해보고 비교해보는 것이라고 한다. 😲 (어차피 내가 안 하고 컴퓨터가 하니까 키키키키킼)

예를 들어, 세 변수가

스크린샷 2021-06-12 오후 4 22 20

이렇게 주어진 dataset 이 총 2개 존재한다고 하면

분할점, 분할변수를 설정할 수 있는 가지 수가 이렇게 총 6개가 나온다. (x1,2) (x1,3) (x2,5) (x2,6) (x3,1) (x3,7)

그리디 서치의 개념 !

랜덤포레스트

이제 나무가 여러 개 모인 포레스트이다 ! 🎄🌲🌳🌴

나무 하나로는 한계가 많다. 윗 가지에서 이미 분할점이나 분할변수 설정이 잘못 되었을 경우 계속 밑 노드까지 오류가 전파될 뿐더러, 트리를 깊게 쌓으면 해당 training 데이터 셋에만 커스토마이징된 트리로 overfitting 되기 쉽기 때문이다.

Bagging (Bootstrap + Aggregating)

  • Bootstrap

사용할 트리 개수만큼 training set 을 만들어준다.

이 때 training set 은 기존 training set 을 부트스트랩하는 방식 (;복원추출) 으로 만들어지기 때문에 처음 training set 이 m 개라면 두번째 training set 도 개수는 m 개가 동일하지만 중복 데이터가 있을 수도 있고 한번도 뽑히지 않은 데이터도 있을 수 있다.

  • Aggregating

1) 첫 번째 방법은 각 트리가 예측한 라벨 값을 모두 종합하여 과반 수가 선택한 라벨을 선택하는 것이다. (예측 모델의 경우 각 트리가 내뱉은 예측값의 평균값을 최종 예측 값으로 한다)

2) 두 번째 방법은 각 트리의 성능까지 고려하였을 때 과반 수가 선택한 라벨을 선택하는 것이다.

Random subspace

그냥 트리를 만들다가는 매우 비슷하게 생긴 트리들만 여러 개 만들어져서 랜덤포레스트가 아무 의미가 없을 수도 있다. 🌳🌳🌳

따라서 의사결정나무의 분기점을 탐색할 때, 원래 변수의 수보다 적은 수의 변수를 임의로 선택하여 해당 변수들만을 고려 대상으로 하는 것이 random subspace 이다.

랜덤포레스트의 중요 변수 선택

사실 이게 내가 공부하는 이유이므로 !

  • Permutation importance : 단점 ) 시간이 오래 걸릴 수 있음

Out of bag ; 부트스트랩 방식이므로 아예 어떠한 training set 에도 뽑히지 않은 (어떠한 트리의 훈련에도 사용되지 않은) 데이터 집합

1) 각 트리의 out of bag 에러를 계산한다.

2) 특정 변수 (;feature importance 를 알아보고자 하는 변수) 의 값을 임의로 뒤섞은 데이터 집합에 대해(permutation) out of bag error 을 계산한다.

3) 각 모델의 1) 에러와 2) 에러의 차이를 구하여 그 차이들의 평균과 분산을 구한다.

그리고 최종 변수의 중요도는 차이들의 평균 / 차이들의 표준편차 가 된다.

이 때 차이들의 표준편차로 나눔으로써 차이들의 분포 자체가 매우 랜덤할 수 있는데 일종의 스케일링 기능을 하게 된다.

from sklearn.ensemble import RandomForestClassifier
from eli5.sklearn import PermutationImportance

model=RandomForestClassifier().fit(X_train,y_train)
perm=PermutationImportance(model,scoring="accuracy",random_state=22).fit(X_val,y_val)
eli5.show_weights(perm,top=20,feature_names=X_val.columns.tolist())
  • randomforest 의 default feature_importances 메소드

    출처 : Gini importance

    랜덤 포레스트에서 디폴드로 제공하는 feature_importances 메소드의 경우 지니 불순도 개념을 사용한다고 한다.

    스크린샷 2021-06-12 오후 10 14 33

각 분류된 집단들이 얼마나 순혈인지를 알 수 있게 해주는 것이다. (classification 문제에서 잘 분류할 수록 비슷한 아이템들만 해당 집단에 속해야하므로)

스크린샷 2021-06-12 오후 10 14 33

스크린샷 2021-06-12 오후 10 17 14

스크린샷 2021-06-12 오후 10 17 23

스크린샷 2021-06-12 오후 10 17 32

스크린샷 2021-06-12 오후 10 17 40

스크린샷 2021-06-12 오후 10 18 12

permutation importance vs feature_importances

랜덤포레스트의 디폴트 feature_importances 는 training set 만을 바탕으로 순위가 결정된다는 점, 그리고 지니 불순도를 이용하는 원리 자체에서 과적합에 취약하다는 점에서 한계가 있다. 가령 training data 에서 특이하게도 미운 오리 한마리가 전체 백조 사이에 들어가있는 현상이 발견된다면 그 한마리를 건져내기 위해서 다양한 분기가 필요할 것이다. 해당 feature 의 순위가 높게 나온다고 해서, 좋은 feature 라고 단정지을 수 없는 것이다.

스크린샷 2021-06-12 오후 10 23 16

XG boost

xg boost 도 다양한 의사결정나무들을 활용하는 것은 맞다. 🎄🌲🌳🌴 다만 방식이 조금 다르다.

첫 번째 🎄 output + learning rate * 두 번째 🌲output + learning rate * 세 번째 🌳 output … 이런 식으로 트리 하나를 늘릴 때마다 점점 최종 y 값에 도달하도록 순차적으로 학습된다.

분할점과 분할변수의 경우 그리디로 구해준다는 점은 랜덤 포레스트와 유사하다.

  1. 부모 노드의 Similarity score 구하기
  2. 분할점과 분할변수 정하기
  3. 해당 분할점과 분할 변수를 기준으로 오른쪽 자식, 왼쪽 자식 나누기
  4. 오른쪽 자식의 Similarity score 구하기
  5. 왼쪽 자식의 Similarity score 구하기
  6. Gain = 오른쪽 자식의 Similarity score + 왼쪽 자식의 Similarity score - 부모 노드의 Similarity score
  7. Pruning 하이퍼파라미터 값인 감마보다 Gain 이 작다면 pruning 하기
  8. pruning 이 끝났다면 최종 도출된 tree 에 각 데이터가 속한 끝 노드를 기준으로 Output value 구하기
  9. 첫 번째 🎄 output + learning rate * 두 번째 🌲output + learning rate * 세 번째 🌳 output … 이런 식으로 해당 인풋 값의 아웃풋 값에 근접해지도록 1~8번 과정을 반복하기

의 과정으로 진행된다.

IMG_62B2708ED374-1

그리고 Regression,Classification 각각의 모델에서 similarity score, output value 를 구하는 방식은 위와 같다.

Why ?

왜 이런 공식이 도출됐는지 묻는다면 한 절반만 이해한 것 같다.

IMG_990E3CCD4FFF-1

결국 뒤에 람다 있는 regularization 부분을 제외한다면 결국 이 문제를 푸는 핵심은 “그래서 loss function 을 최소화시키는 output value 가 무엇이 되게 계속 트리를 만들어가야 하는데?” 이다

그런데 이를 최적화시키기는 무척이나 어려우므로 테일러 급수를 활용해서 위 빨간색 네모에서 아래 빨간색 네모와 같이 근사식으로 바꾸어주었다고 한다.

너무 복잡해보이므로 이미 정의된 기호를 바탕으로 첫번째 1차 미분은 g 로, 2차 미분은 헤시안의 정의에 의해 h 로 표기하면 좀 더 알아보기 쉬워진다.

IMG_9691621B1678-1

이제 좀 더 전형적인 optimization 문제로 바뀌었는데, 이 말인 즉슨 위 식을 Output value 에 대해 미분한 값이 0이 되게 하는 Output value 를 찾으면 된다 !

IMG_D234E9C8F6C2-1

그리고 regression 의 loss function (; 1/2 * (예측값 - y)^2 ) & classification 의 loss function (; cross entropy loss) 각각에서 이를 계산해보면 위의 공식이 그대로 도출된다고 한다. 좀 납득이 가는게 classification 의 loss 가 크로스 엔트로피이니까 막 미분하면 (p) (1-p) ~~ 이런 식으로 나오는 것 같다

그리고 Similarity function 이 크면 클수록 Gain 도 커지므로 Similarity function 은 크면 좋다. 그 이유도 바로 나오는데 이 Similarity function 이 바로 Loss function 을 뒤집은 것이기 때문이다 !

IMG_6E45D09D0591-1

XG Boost 의 중요 변수 선택

출처 : Feature Selection - XGBoost

디폴트 feature importance 의 경우 구하는 총 세 가지 방법이 있다고 한다. (기본은 Gain 으로 설정되어있다.)

  1. Gain/ Total gain

    앞서 언급한 지니 불순도 방식과 유사

  2. Cover/ Total Cover

    해당 변수가 분류에 관여한 샘플의 평균 수 / 총 합

  3. Weight

    해당 변수가 노드 분기에 사용된 횟수

그리고 XG Boost 도 랜덤 포레스트와 마찬가지로 사이킷런의 permutation importance 도 이용할 수 있다 !

정리하자면

정확도 : feature importance < permutation importance (하지만 permutation importance 도 절대적 지표라고 할 수 X )

시간 : feature importance > permutation importance

코드로 Feature importance 를 구할 수 있는 여러 모델들

  • linear regression

  • logistic regression

  • decision tree

  • random forest

  • XGboost

    그리고 사이킷런에서 이를 종합한 SelectFromModel 을 제공한다.

    The threshold value to use for feature selection. Features whose importance is greater or equal are kept while the others are discarded.

    RFselector = SelectFromModel(estimator=RandomForestClassifier()).fit(X, y)
    GBMselector = SelectFromModel(estimator=GradientBoostingClassifier()).fit(X, y)
    LRselector = SelectFromModel(estimator=LogisticRegression()).fit(X, y)
      
    columns = data.columns
    

이게 제일 합리적일 것 같다는 생각이 든다