← 모델 목록
수업 실습 2026-08-30 · tabular-classification
트리 계열 (결정 트리)

DecisionTreeClassifier (scikit-learn)

결정 트리 구독 이탈 예측 (ML 미니대회 분류 트랙)
모델
피처 조건으로 데이터를 나눠 가며 분류하는 단일 트리 모델. max_depth 4, class_weight balanced
사용 목적
수업 ML 미니대회 분류 트랙(60일 내 구독 이탈)에서 배깅·부스팅과 비교할 첫 기준 모델로 사용
코드
mnmms/mldl
가중치
없음
val_auc70.8%
f129.1%
recall70.2%
학습 시간없음
추론 CPU / GPU측정 안 함

데이터

  • 대회 제공 train 90,000 · 양성 10.71%
  • 입력 29개 → 전처리 후 53열
  • train 72,000 · valid 18,000 (stratify, seed 42)

학습

  • max_depth 4 · class_weight balanced
  • CPU · scikit-learn 1.9.0
  • 학습 시간 [미확인]

결과

  • valid ROC-AUC 0.708 (4개 모델 중 최저)
  • F1 0.291 · recall 0.70 · precision 0.18

배운 점

  • balanced 가중치로 이탈을 많이 잡는 대신 오탐이 많음
  • 대회 지표는 F1이 아니라 ROC-AUC
개요용도 의도한 용도: 미니대회에서 결정 트리 / 배깅 / 부스팅을 같은 검증셋으로 비교하는 출발점 대상 사용자: 해당 없음 (학…

용도

  • 의도한 용도: 미니대회에서 결정 트리 / 배깅 / 부스팅을 같은 검증셋으로 비교하는 출발점
  • 대상 사용자: 해당 없음 (학습용)
  • 의도하지 않은 용도: 실제 구독 이탈 예측 (합성 데이터)

모델 정보

항목 값
베이스 모델 직접 구현 (처음부터 학습)
크기 · 변형 DecisionTreeClassifier(max_depth=4, random_state=42, class_weight='balanced')
리비전 scikit-learn 1.9.0 (uv.lock 기준)
아키텍처 깊이 4 이하 결정 트리
학습 방식 처음부터 학습
프롬프트 · 에이전트 구성 해당 없음
데이터확보 방식 공개 데이터셋 그대로 (수업 대회 제공 합성 데이터) 출처 mini-competition/participant/cl…
항목 값
확보 방식 공개 데이터셋 그대로 (수업 대회 제공 합성 데이터)
출처 mini-competition/participant/classification/train.csv, test.csv (README: 구조적 결측, 비선형 상호작용, 시간 분포 이동, test에만 있는 희귀 범주를 포함한 합성 데이터)
직접 수집 내용 해당 없음
이용 조건 확인 해당 없음 (수업 자료)
규모 train 90,000 (2024-01 ~ 2025-12) / test 25,000 (2026-01 ~ 04, 정답 비공개)
분할 train을 무작위 8:2로 나눔: fit 72,000 / valid 18,000 (stratify, 시드 42)
레이블·클래스 분포 이탈(churned_60d=1) 10.71%. valid 이탈 1,927명
전처리 결측 5열 조사(결측 여부와 이탈률 차이 없음 → 무작위 결측으로 판단). payment_method는 'missing'으로 채운 뒤 효과 크기(Cramér's V 0.015)가 거의 0이라 제거. 숫자 4열은 train 중앙값. 숫자 22열 StandardScaler, 범주 7열 OneHot → 53열
개인정보·민감정보 처리 해당 없음 (합성 데이터)
학습하이퍼파라미터 max_depth=4 , class_weight='balanced' 에폭 / 스텝 해당 없음 학습 시간 [미확인…
항목 값
하이퍼파라미터 max_depth=4, class_weight='balanced'
에폭 / 스텝 해당 없음
학습 시간 [미확인] (노트북에 시간 측정이 없음)
하드웨어 CPU (scikit-learn). 장비 이름 [미확인]
프레임워크 scikit-learn 1.9.0, pandas 3.0.5
시드 · 재현 방법 train_test_split(test_size=0.2, random_state=42, stratify=y_train), 모델 random_state=42
결과valid 18,000명 기준, 노트북 출력입니다. ROC-AUC (확률 기준) 0.708 valid 18,000 랜덤 포레스…

valid 18,000명 기준, 노트북 출력입니다.

지표 값 테스트셋 비교 대상
ROC-AUC (확률 기준) 0.708 valid 18,000 랜덤 포레스트 0.761, CatBoost 0.771
F1 (이탈) 0.291 valid 18,000 랜덤 포레스트 0.223, CatBoost 0.340
recall (이탈) 0.702 valid 18,000
precision (이탈) 0.183 valid 18,000
accuracy 0.634 valid 18,000

혼동행렬 (행: 실제, 열: 예측): [[10053, 6020], [575, 1352]]

추론 성능 (CPU / GPU)

측정 안 함.

장비 장비 이름 배치 크기 지연 시간 (샘플당, p50 / p95) 처리량 (샘플/초) 메모리 사용
CPU [미확인] - 측정 안 함 측정 안 함 측정 안 함
GPU 해당 없음 (scikit-learn은 CPU 전용) - - - -
한계 · 배운 점한계 알려진 실패 유형: 이탈로 예측한 7,372명 중 1,352명만 실제 이탈 (precision 0.18) 편향 / 공정성…

한계

  • 알려진 실패 유형: 이탈로 예측한 7,372명 중 1,352명만 실제 이탈 (precision 0.18)
  • 편향 / 공정성 이슈: 해당 없음 (합성 데이터)
  • 성능이 떨어지는 입력 조건: [미확인]

배운 점

  • 이틀 전 과제에서 랜덤 포레스트가 이탈을 하나도 못 맞힌 경험 때문에 처음부터 class_weight='balanced'를 넣고, 결과가 나오면 혼동행렬부터 확인 (노트북 주석)
  • 노트북 주석: precision 18%가 "정확도-재현율 트레이드오프의 실체"
  • 대회 평가지표는 ROC-AUC라 predict_proba로 다시 비교함 (노트북 "⚠️ 대회 평가지표는 F1이 아니라 ROC-AUC" 셀)
사용 방법코드 셀 3개
In [1]결측 채우기·전처리
def fill_missing(df, ref):
    df = df.copy()
    df['payment_method'] = df['payment_method'].fillna('missing')
    for col in ['avg_watch_hours_30d', 'completion_rate_30d', 'buffering_ratio_30d', 'nps_score']:
        df[col] = df[col].fillna(ref[col].median())
    return df

train_proc = fill_missing(train_df, ref=train_df)
test_proc = fill_missing(test_df, ref=train_df)

drop_cols = ['customer_id', 'snapshot_date', 'churned_60d', 'payment_method']
cat_cols = ['plan_type', 'acquisition_channel', 'region', 'device_os',
            'contract_type', 'support_tier', 'usage_segment']
feature_cols = [c for c in train_proc.columns if c not in drop_cols]
num_cols = [c for c in feature_cols if c not in cat_cols]

preprocessor = ColumnTransformer([
    ('num', StandardScaler(), num_cols),
    ('cat', OneHotEncoder(handle_unknown='ignore'), cat_cols),
])
X_train_scaled = preprocessor.fit_transform(train_proc[feature_cols])
print('전처리 후 X_train shape:', X_train_scaled.shape)
Out[1] 출력 보기
전처리 후 X_train shape: (90000, 53)
In [2]검증셋 나누기·학습
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

X_train, X_val, y_train, y_val = train_test_split(X_train_scaled, y_train, test_size=0.2, random_state=42, stratify=y_train)

model_dt = DecisionTreeClassifier(max_depth=4, random_state=42, class_weight='balanced')
model_dt.fit(X_train, y_train)
pred = model_dt.predict(X_val)
print(confusion_matrix(y_val, pred))
print('F1 Score :', f1_score(y_val, pred))
Out[2] 출력 보기
[[10053  6020]
 [  575  1352]]
F1 Score : 0.29078395526400685
In [3]ROC-AUC (확률 기준)
from sklearn.metrics import roc_auc_score

proba = model_dt.predict_proba(X_val)[:, 1]
roc_auc_score(y_val, proba)
Out[3] 출력 보기
0.707975

같은 과제·데이터셋 비교 tabular-classification · 수업 미니대회: 60-day Subscription Churn (합성) · 4개

굵은 이름이 이 모델입니다. 주황 테두리가 그룹 안 최고값입니다.

val_auc 높을수록 좋음 · 축 일부 생략