본문 바로가기
Study/혼자 공부하는 머신러닝 + 딥러닝

[혼자 공부하는 머신러닝 + 딥러닝] 결정 트리

by kimdapadata 2024. 10. 31.

 

만약 캔으로 된 와인이 있다고 가정을 해봅시다.

이 와인은 레드 와인, 화이트 와인 두 가지 종류가 있습니다.

하지만 종류를 구별할 수 있는 표시가 없다고 가정 했을 때 우리는 어떻게 분류를 할 수 있을까요?

 

 

로지스틱 회귀로 와인 분류하기


우선 우리가 배웠던 로지스틱 회귀로 와인을 분류해봅시다.

 

와인 데이터셋을 보자면

wine data는 kaggle에서 가져왔다.

 

열은 알코올 도수(alcohol), 당도(sugar), pH, class(타깃값, 0 : 레드와인, 1 : 화이트와인)으로 이루어져 있습니다.

 

 

 

추가적으로 info() 메서드와 describe() 메서드를 사용하여 데이터의 정보를 좀 더 살펴보겠습니다.

 

결측값은 없고, 통계값을 살펴보면 각 열의 스케일이 다른 것을 알 수 있습니다.

 

 

 

그렇기 때문에 훈련 세트와 테스트 세트로 나눈 후, StandardScaler 클래스를 사용해 표준화 시켜야 합니다.

20% 정도만 테스트 세트로 나눴다.
StandardScaler 클래스를 사용하여 표준화

 

 

 

그 후 로지스틱 회귀 모델을 훈련하면

 

조금은 낮은 점수를 얻을 수 있습니다.


 

설명하기 쉬운 모델과 어려운 모델


자, 위의 결과를 만약에 설명한다고 했을 때 과연 쉽게 설명을 할 수 있을까요?

정답은 "아닙니다" 입니다.

 

 

이 모델을 설명하기 위해 계수와 절편을 출력해보겠습니다.

로지스틱 회귀 모델의 계수와 절편

 

이를 사용하여 설명을 하자면,

"알코올 도수 값에 0.51270274를 곱하고 당도에 1.6733911을 곱하고, pH값에 -0.68767781을 곱한 다음
모두 더합니다.

그 후, 마지막으로 1.81777902를 더한 값이 0보다 크면 화이트 와인, 작으면 레드 와인입니다.
현재 정확도는 77%이고......"

모델을 잘 모르는 사람이 보았을 때는 이해하기 어려운 설명입니다.

 

 

그들이 원하는 설명은

"자, 알코올 도수가 X보다 커? 그러면 레드 와인, 아니면 화이트 와인"

일 것입니다.

 

이렇게 이해하기 쉽게 설명하려면 어떤 모델을 사용하는게 좋을까요?

 

 

 

 

결정 트리


 

결정 트리 모델은 아래와 같이 스무고개와 같습니다.

 

 

이렇게 데이터를 잘 나눌 수 있는 질문을 찾는다면 계속 질문을 추가해서 분류 정확도를 높일 수 있습니다.

 

 

 

이제 사이킷런의 DecisionTreeClassifier 클래스를 사용해 결정 트리 모델을 훈련해보겠습니다.

더보기

여기서 잠깐!

random_state를 지정하는 이유는 무작위성을 없애려고 한 것이기 때문에
실전에서는 필요하지 않습니다.

 

보면 훈련 세트에 대한 점수가 엄청 높은 걸 알 수 있습니다.

테스트 성능은 그에 비해 조금 낮은데, 이는 과대적합된 모델이라고 볼 수 있습니다.

 

한번 이 모델을 plot_tree() 함수를 사용해서 결정 트리를 이해하기 쉬운 트리 그림으로 출력해 보겠습니다.

plot_tree()를 사용하여 그림으로 표현

 

결정 트리는 위에서부터 아래로 거꾸로 읽고,
맨 위의 노드를 루트 노드(Root Node), 맨 아래 끝에 달린 노드를 리프 노드(Leaf Node)라고 합니다.

 

 

너무 복잡하니 트리의 깊이를 제한해서 출력해보겠습니다.

max_depth 매개변수를 1로 주면 루트 노드를 제외하고 하나의 노드를 더 확장해서 그립니다.

그리고 filled 매개변수에서 클래스에 맞게 노드의 색을 칠할 수 있습니다.

또한 feature_names 매개변수에는 특성의 이름을 전달할 수 있습니다.

 

한번 그려보겠습니다.

 

아까보다 훨씬 읽기 편해졌습니다.

기본적으로 그림이 담고 있는 정보도 살펴보겠습니다.

 

 

루트 노드(Root Node)

 

우선 루트 노드를 살펴보면 당도(Sugar)가 -0.239 이하인지 물어보고 있습니다.

만약 어떤 데이터의 당도가 -0.239 이하면 왼쪽 가지로 가고, 그렇지 않으면 오른쪽 가지로 이동합니다.

즉 왼쪽이 Yes, 오른쪽이 No입니다.

루트 노드의 총 샘플 수는 5,197개(1,258 + 3,939)이고

음성 클래스(레드 와인)은 1,258개이고 양성 클래스(화이트 와인)은 3,939개입니다.

 

 

왼쪽 리프 노드와 오른쪽 리프 노드

 

왼쪽 노드와 오른쪽 노드를 살펴보면 색의 차이가 있는데,
이는 plot_tree() 함수에서 filled=True로 지정할 시 클래스마다 색깔을 부여합니다.

그리고 어떤 클래스의 비율에 따라 색깔의 차이가 생깁니다.

왼쪽 노드의 색을 보면 양성 클래스(화이트 와인)의 비율이 크게 줄었기 때문에 처음보다 색깔이 연해졌습니다.

그리고 오른쪽 노드의 색을 보면 음성 클래스(레드 와인)의 비율이 크게 줄었기 때문에 색깔이 진해졌습니다.

 

 

결정트리의 예측 방법은 간단합니다.

리프 노드에서 가장 많은 클래스가 예측 클래스가 됩니다.

만약 위의 결정 트리의 성장이 멈췄을 경우 왼쪽 노드, 오른쪽 노드에 도달한 데이터는 모두 양성 클래스로 예측됩니다.

왜냐하면 두 노드 모두 양성 클래스의 개수가 많기 때문입니다.

 

 

 

 

불순도 


박스에 자세히 보시면 gini라는 것이 있습니다.

gini는 지니 불순도를 의미하는데 DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값이 'gini'입니다.

criterion 매개변수의 용도는 노드에서 데이터를 분할할 기준을 정하는 것입니다.

 

앞에서 그린 트리에서 루트 노드는 어떻게 당도 -0.239를 기준으로 왼쪽과 오른쪽 노드로 나눴을까요?

바로 criterion 매개변수에 지정한 지니 불순도를 사용했기 때문입니다.

 

그럼 지니 불순도는 어떻게 계산하는지 알아봅시다.

지니 불순도는 클래스의 비율을 제곱해서 더한 다음 1에서 빼면 됩니다.

 

\(\textit{지니 불순도} = 1 - (\textit{음성 클래스 비율}^{2} + \textit{양성 클래스 비율}^{2})\)

 

 

 

그럼 이를 통해서 지니지수를 계산해보겠습니다.

 

루트 노드는   총 5,197개의 샘플이 있고 그 중에 1,258개가 음성 클래스, 3,939개가 양성 클래스입니다.

따라서 다음과 같이 지니지수가 나옵니다.

 

\( 1 - \left( \left( \frac{1258}{5197} \right)^{2} + \left( \frac{3939}{5197} \right)^{2} \right) = 0.367 \)

 

 

만약 어떤 노드의 클래스의 비율이 정확히 1/2씩이라면 지니 불순도는 0.5가 되는데 이는 좋지 않은 값입니다.

그리고 노드에 하나의 클래스만 있다면 지니 불순도는 0이 되어 가장 작은 값은 가지는데, 이런 노드를 순수 노드라고 합니다.

 

 

 

결정 트리 모델은 부모 노드(Parent Node)와 자식 노드(Child Node)의 불순도 차이가 가능한 크도록 트리를 성장시킵니다. 

부모 노드와 자식 노드의 불순도 차이를 계산하는 공식은 다음과 같습니다.

 

부모의 불순도 - (왼쪽 노드 샘플 수 / 부모의 샘플 수 ) * 왼쪽 노드 불순도 - (오른쪽 노드 샘플 수 / 부모의 샘플 수 ) * 오른쪽 노드 불순도

 

이런 부모와 자식 노드 사이의 불순도 차이를 정보 이득이라고 부릅니다.

그리고 결정트리는 노드를 나눌 때, 정보 이득이 최대가 되도록 데이터를 나눕니다.

 

 

더보기

여기서 잠깐!

사이킷런에는 또 다른 불순도 기준이 존재합니다.

DecisionTreeClassfier 클래스에서 criterion = 'entropy'를 지정하여 엔트로피 불순도를 사용할 수 있습니다.

공식은 다음과 같습니다.

 

\(
- \text{음성 클래스 비율} \times \log_2(\text{음성 클래스 비율}) - \text{양성 클래스 비율} \times \log_2(\text{양성 클래스 비율})
\)

 

결과를 정리하면

(1) 결정트리는 불순도 기준을 사용해 정보 이득이 최대가 되도록 노드를 분할한다.

(2) 노드를 순수하게 나눌수록 정보 이득이 커진다.

(3) 새로운 샘플에 대해 예측할 때에는 노드의 질문에 따라 트리를 이동한다

(4) 마지막에 도달한 노드의 클래스 비율을 보고 예측을 만든다.

 

 

 

 

가지치기


 

결정 트리는 가지치기를 해서 어느 정도에서 멈춰야 합니다.

가장 간단한 방법은 자라날 수 있는 트리의 최대 깊이를 지정하는 것입니다.

DecisionTreeClassifier 클래스의 max_depth 매개변수를 3으로 지정하면 루트 노드 아래로 최대 3개의 노드까지만
성장합니다.

 

max_depth = 3

 

이를 plot_tree() 함수로 그려보겠습니다.

plot_tree() 그래프

 

여기서 sugar <= -0.854로 되어 있는데 음수로 된 당도는 뭔가 이상합니다.

이를 누군가한테 설명해야 한다면, 상대방은 이해가 될까요?

앞서 불순도를 기준으로 샘플을 나누는 것이 결정 트리 알고리즘이라고 했습니다.

불순도 공식을 보면 클래스별 비율을 가지고 계산하는 것을 알 수 있습니다.

그렇다면 특성값의 스케일이 계산에 영향을 미칠까요?

정답은 "아니요" 입니다.

 

따라서 결정 트리 알고리즘은 표준화 전처리를 할 필요가 없다는 장점이 있습니다.

 

 

위에서는 train_scaled, test_scaled처럼 전처리 된 파일을 사용했는데, 이번엔 전처리 전의 파일을 사용하여 훈련 시켜보겠습니다.

 

전처리 전 파일

 

보면 값이 동일한 것을 알 수 있습니다.

 

 

 

마지막으로 결정 트리는 어떤 특성이 가장 유용한지 나타내는 특성 중요도를 계산해 줍니다.

이 트리의 루트 노드와 깊이 1에서 sugar를 사용했기 때문에 sugar가 가장 유용한 특성일 것입니다.

특성 중요도는 결정 트리 모델의 feature_importances_ 속성에 저장되어 있습니다.

alcohol, sugar, pH

 

역시 sugar가 0.87정도로 가장 높습니다.

그리고 이 3개의 값을 더하면 1이 됩니다.

특성 중요도는 각 노드의 정보 이득과 전체 샘플에 대한 비율을 곱한 후 특성별로 더하여 계산합니다.

 

 

 

출처 : 혼자 공부하는 머신러닝 + 딥러닝

https://product.kyobobook.co.kr/detail/S000001810330

 

다음 장에서는 교차 검증과 그리드 서치에 대해서 알아보겠습니다.