LG DX DATA SCHOOL

01/19 결정트리부터 ...

getfeelingsfrom 2026. 1. 19. 16:19
# min_samples_leaf=6 으로 트리 생성 조건을 제약한 Decision Boundary 시각화
dt_clf = DecisionTreeClassifier(min_samples_leaf=6, random_state=156).fit(X_features, y_labels)
visualize_boundary(dt_clf, X_features, y_labels)

결정트리 

최적의 분리 규칙 기준 : 최적 분할의 결정은 불순도 감소량으 가장 크게 하는 분할 

 

 

이산형 목표변수 = decision트리 = 분류형 

연속형 목표변수 = regression 트리 = 회귀형 

 

불순도 측정 

의사 결정 트리 학습에서 각 노드에서 분기하기 위한 최적의 질문은 정보이득이라는 값이 최대가 되도록 만들어주는 것이 핵심 ! 

어느 특정 노드에서 m개의 자식 노드로 분기되는 경우 정보이득 = 

부모노드 불순도와 자식 노드 불순도의 차이를 크게 만들어주는게 규칙 = 값이 클수록 좋은 규칙 

 

 

결정트리에서 **불순도(Impurity)**란
👉 한 노드 안에 서로 다른 클래스가 얼마나 섞여 있는지를 나타내는 지표

이 그림의 x축은

  • p(y=1) : 클래스 1의 비율

✔ p = 0 또는 p = 1 인 경우

  • 한 클래스만 존재
  • 예: 전부 0이거나, 전부 1
  • 완전히 순수한 상태 (Pure node)

-> 이때는 불순도가 0
-> 더 이상 나눌 필요가 없음

 

 

✔ p = 0.5 (반반 섞인 경우)

  • 클래스 0과 1이 정확히 50:50
  • 어떤 클래스로 예측해야 할지 가장 애매한 상태

-> 불순도(스트레스 지수)가 최대
-> 결정트리가 가장 “불안정한” 상태

데이터가 추가가 일어나면 변화가 크게 나타난다 

 

 

결정트리 구현 

graphviz구성

from sklearn.tree import export_graphviz

# export_graphviz()의 호출 결과로 out_file로 지정된 tree.dot 파일을 생성함. 
export_graphviz(dt_clf, out_file="tree.dot", class_names=iris_data.target_names , \
feature_names = iris_data.feature_names, impurity=True, filled=True)

import graphviz

# 위에서 생성된 tree.dot 파일을 Graphviz 읽어서 Jupyter Notebook상에서 시각화 
with open("tree.dot") as f:
    dot_graph = f.read()
graphviz.Source(dot_graph)

지니계수가 0이라면 완벽 분류

 

import seaborn as sns
import numpy as np
%matplotlib inline

# feature importance 추출 
print("Feature importances:\n{0}".format(np.round(dt_clf.feature_importances_, 3)))

# feature별 importance 매핑
for name, value in zip(iris_data.feature_names , dt_clf.feature_importances_):
    print('{0} : {1:.3f}'.format(name, value))

# feature importance를 column 별로 시각화 하기 
sns.barplot(x=dt_clf.feature_importances_ , y=iris_data.feature_names)

결정트리 모델이 어떤 feature를 얼마나 중요하게 사용했는지 확인 -> bar plot으로 시각화

학습된 결정트리 모델 dt_clf에 대해

  • feature_importances_ 값을 뽑아서
  • 각 변수 이름(iris feature names)과 매칭해서 출력하고
  • seaborn으로 막대 그래프를 그려서 중요도를 직관적으로 비교한다.

 

결정트리

from sklearn.datasets import make_classification
import matplotlib.pyplot as plt
%matplotlib inline

plt.title("3 Class values with 2 Features Sample data creation")

# 2차원 시각화를 위해서 feature는 2개, 결정값 클래스는 3가지 유형의 classification 샘플 데이터 생성. 
X_features, y_labels = make_classification(n_features=2, n_redundant=0, n_informative=2,
                             n_classes=3, n_clusters_per_class=1,random_state=0)

# plot 형태로 2개의 feature로 2차원 좌표 시각화, 각 클래스값은 다른 색깔로 표시됨. 
plt.scatter(X_features[:, 0], X_features[:, 1], marker='o', c=y_labels, s=25, cmap='rainbow', edgecolor='k')

 

 

이 함수는 결과로 2개를 반환

  • X_features: 입력 데이터(feature 행렬) → shape: (샘플 수, feature 수)
  • y_labels: 정답 라벨(class) → shape: (샘플 수,)

즉,

  • X_features는 2차원 좌표 데이터
  • y_labels는 각 점이 어떤 클래스인지 (0/1/2)

 

 n_features=2

  • feature(독립변수) 수를 2개로 생성
  • 그래서 X_features[:,0], X_features[:,1]로 2차원 scatter 가능

 n_informative=2

  • “실제로 분류에 의미 있는 feature” 수
  • 여기서는 두 개 feature 모두 유의미하다는 뜻

 n_redundant=0

  • “중복(선형결합) feature” 없음
  • 만약 redundant가 크면 불필요한 feature가 섞여서 해석이 복잡해짐

 n_classes=3

  • 클래스 수가 3개 (다중분류)
  • y_labels 값은 보통 0,1,2로 들어감

 n_clusters_per_class=1

  • 클래스 당 군집(cluster) 수
  • 클래스마다 1개 덩어리로 뭉쳐있는 형태가 됨
    • 예: class0은 한 군데, class1도 한 군데, class2도 한 군데

 random_state=0

  • 난수 고정 → 실행할 때마다 같은 데이터 나오게 함 (재현성)


과적합

ㅇ위로 올라갈 수록 중요성을 가짐

과적합 :

과거 학습 데이터에 대해 지나치게 잘 맞도록 학습된 모델이,

새로운(미래) 데이터에 대해서는 오히려 예측 성능이 크게 떨어지는 현상

  • 학습 데이터에서는 정답을 거의 맞히지만
  • 실제 우리가 예측하고 싶은 미래 데이터에서는 틀릴 확률이 커지는 문제다.

=> 일반화 필요 

 

결정트리 파라미터 
min_samples_split : 

  • 노드를 분할하기 위한 최소한의 샘플 데이터 수로 과적합을 제어하는데 사용됨
  • 디폴트는 2이고 작게 설정할수록 분할되는 노드가 많아져서 과적합 가능성 증가 

min_samples_leaf: 

  • 분할이 될 경우 왼쪽과 오른쪽의 브랜치 노드에서 가져야 할 최소한의 샘플 데이터 수
  • 큰 값으로 설정될수록, 분할될 경우 왼쪽과 오른쪽의 브랜치 노드에서 가져야 할 최소한의 샘플 데이터 수 조건을 만족시키기가 어려우므로 노드 분할을 상대적으로 덜 수행함
  • min_samples_split와 유사하게 과적합 제어 용도
  • 비대칭적 데이터의 경우 특정 클래스의 데이터가 극도로 작을 수 있으므로 이 경우 작게 설정 필요 

 

import numpy as np

# Classifier의 Decision Boundary를 시각화 하는 함수
def visualize_boundary(model, X, y):
    fig,ax = plt.subplots()
    
    # 학습 데이타 scatter plot으로 나타내기
    ax.scatter(X[:, 0], X[:, 1], c=y, s=25, cmap='rainbow', edgecolor='k',
               clim=(y.min(), y.max()), zorder=3)
    ax.axis('tight')
    ax.axis('off')
    xlim_start , xlim_end = ax.get_xlim()
    ylim_start , ylim_end = ax.get_ylim()
    
    # 호출 파라미터로 들어온 training 데이타로 model 학습 . 
    model.fit(X, y)
    # meshgrid 형태인 모든 좌표값으로 예측 수행. 
    xx, yy = np.meshgrid(np.linspace(xlim_start,xlim_end, num=200),np.linspace(ylim_start,ylim_end, num=200))
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
    
    # contourf() 를 이용하여 class boundary 를 visualization 수행. 
    n_classes = len(np.unique(y))
    contours = ax.contourf(xx, yy, Z, alpha=0.3,
                           levels=np.arange(n_classes + 1) - 0.5,
                           cmap='rainbow', clim=(y.min(), y.max()),
                           zorder=1)

 

from sklearn.tree import DecisionTreeClassifier

# 특정한 트리 생성 제약없는 결정 트리의 Decsion Boundary 시각화.
dt_clf = DecisionTreeClassifier(random_state=156).fit(X_features, y_labels)
visualize_boundary(dt_clf, X_features, y_labels)

 

  • meshgrid를 이용해 2차원 입력 공간을 촘촘한 격자로 샘플링
  • 각 격자점에 대해 모델의 예측 클래스를 계산하여 contourf로 영역을 색칠함으로써 분류기의 decision boundary를 시각화
  • 결정트리는 축 기준(threshold) 분기 구조로 인해 경계가 직사각형/계단 형태

 

# min_samples_leaf=6 으로 트리 생성 조건을 제약한 Decision Boundary 시각화
dt_clf = DecisionTreeClassifier(min_samples_leaf=6, random_state=156).fit(X_features, y_labels)
visualize_boundary(dt_clf, X_features, y_labels)

 

  • 결정트리 모델을 만들 때 min_samples_leaf=6 조건을 넣어서
  • leaf node(말단 노드)에 들어가는 샘플 수가 최소 6개 이상이 되도록 제한함
  • decision boundary를 시각화
  • 그 결과 Decision Boundary가 단순화되고 작은 영역의 과도한 분할이 감소하여 과적합 위험이 완화되고 일반화 성능이 향상

 

 

==> 과적합해결 

 

def get_new_feature_name_df(old_feature_name_df):
    feature_dup_df = pd.DataFrame(data=old_feature_name_df.groupby('column_name').cumcount(),
                                  columns=['dup_cnt'])
    feature_dup_df = feature_dup_df.reset_index()
    new_feature_name_df = pd.merge(old_feature_name_df.reset_index(), feature_dup_df, how='outer')
    new_feature_name_df['column_name'] = new_feature_name_df[['column_name', 'dup_cnt']].apply(lambda x : x[0]+'_'+str(x[1]) 
                                                                                         if x[1] >0 else x[0] ,  axis=1)
    new_feature_name_df = new_feature_name_df.drop(['index'], axis=1)
    return new_feature_name_df

 

중복제거하는 전처리 작업 실행 

📌 이 처리가 중요한 이유

머신러닝 모델은 컬럼명을 기준으로 가중치(weight)를 학습

  • 컬럼명이 중복되면
    • 어떤 feature의 가중치인지 구분 불가
    • 학습 결과 해석 불가능
  • 특히:
    • 회귀 계수 해석
    • Feature Importance 분석
    • SHAP, Permutation Importance

=> 모델 해석 단계에서 치명적 문제

 

가중치

 

특정 시점의 여러 입력값을 하나의 출력으로 결합하기 위한 중요도 계수

 

  • 여러 센서 값(x, y, z)을
  • 하나의 평균 또는 가중합으로 표현 가능

✔ 센서 데이터 예시

  • 가속도 센서: x, y, z
  • 단일 시점 데이터 → 평균값 or 가중 평균으로 요약 가능
  • 정적 분석, 지도학습에 적합 

✔ 강화학습과의 차이

  • 강화학습(RL)은
    • 실시간으로 상태(state)를 받아
    • 행동(action)을 결정
    • 보상(reward)을 통해 학습
  • 즉,
    • 실시간 센서 입력
    • 연속적인 상태 변화
    • 높은 연산량
    • 강력한 하드웨어 요구

 


 

 

앙상블 기법 

(p.210)

  • 강력한 하나의 모델을 사용하는 대신 보다 약한 모델 여러 개를 조합하여 더 강력한 예측 모형 생성
  • 현실 세계로 예를 들면, 어려운문제를 해결하는데 한 명의 전문가보다 여러 명의 집단지성을 이용하여 문제를 해결하는 방법 

 

앙상블 학습을 통한 분류는 여러 개의 분류기를 생성하고 그 예측을 결합함으로써 보다 정확한 최종 예측을 도출하는 기법 

 

유형

  • 보팅 
    • 여러 개의 분류기가 투표를 통해 최종 예측 결과를 결정 
    • 서로 다른 알고리즘 분류기를 결합
  • 배깅
    • 여러 개의 분류기가 투표를 통해 최종 예측 결과를 결정 
    • 각각의 분류기가 모두 같은 유형의 알고리즘 기반 
    • 데이터 샘플링을 서로 다르게 가져가면서 학습 수행 
    • 랜덤포레스트 알고리즘
  • 부스팅
  • 스태깅 '

 

랜덤포레스트 

배깅과 부스팅보다 더 많은 무작위성을 주어 약한 학습기들을 선형결합하여 최종학습시킴

//추가필요

 

'LG DX DATA SCHOOL' 카테고리의 다른 글

0122 시계열 분석  (1) 2026.01.22
01/20  (1) 2026.01.21
01/16 사이킷런으로 시작하는 머신러닝  (0) 2026.01.16
01/14  (1) 2026.01.15
01/13 파이썬 머신러닝 시작  (0) 2026.01.13