Skip to Content

๋ถ„๋ฅ˜ ๋ชจ๋ธ

์ค‘๊ธ‰๊ณ ๊ธ‰

ํ•™์Šต ๋ชฉํ‘œ

์ด ๋ ˆ์‹œํ”ผ๋ฅผ ์™„๋ฃŒํ•˜๋ฉด ๋‹ค์Œ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€๋กœ ์ดํƒˆ ์˜ˆ์ธก
  • ๊ฒฐ์ • ํŠธ๋ฆฌ์™€ ๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ ๊ตฌํ˜„
  • XGBoost๋กœ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • ๋ชจ๋ธ ํ‰๊ฐ€ ์ง€ํ‘œ ํ•ด์„ (์ •ํ™•๋„, ์ •๋ฐ€๋„, ์žฌํ˜„์œจ, F1, AUC-ROC)
  • ํด๋ž˜์Šค ๋ถˆ๊ท ํ˜• ์ฒ˜๋ฆฌ

1. ๋ถ„๋ฅ˜ ๋ฌธ์ œ๋ž€?

์ด๋ก 

๋ถ„๋ฅ˜(Classification)๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ •์˜๋œ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋ถ„๋ฅ˜ํ•˜๋Š” ์ง€๋„ํ•™์Šต์ž…๋‹ˆ๋‹ค.

๋น„์ฆˆ๋‹ˆ์Šค ํ™œ์šฉ ์˜ˆ์‹œ:

๋ฌธ์ œํƒ€๊ฒŸ ๋ณ€์ˆ˜๋น„์ฆˆ๋‹ˆ์Šค ๊ฐ€์น˜
๊ณ ๊ฐ ์ดํƒˆ ์˜ˆ์ธก์ดํƒˆ ์—ฌ๋ถ€ (0/1)์ดํƒˆ ๋ฐฉ์ง€ ์บ ํŽ˜์ธ
๊ตฌ๋งค ์˜ˆ์ธก๊ตฌ๋งค ์—ฌ๋ถ€ (0/1)ํƒ€๊ฒŸ ๋งˆ์ผ€ํŒ…
์‚ฌ๊ธฐ ํƒ์ง€์‚ฌ๊ธฐ ์—ฌ๋ถ€ (0/1)์†์‹ค ๋ฐฉ์ง€
์ƒํ’ˆ ์ถ”์ฒœํด๋ฆญ ์—ฌ๋ถ€ (0/1)CTR ํ–ฅ์ƒ

2. ๋ฐ์ดํ„ฐ ์ค€๋น„

์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ

import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler import warnings warnings.filterwarnings('ignore') # ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•œ ์‹œ๋“œ ์„ค์ • np.random.seed(42) # ๊ณ ๊ฐ ์ดํƒˆ ์˜ˆ์ธก์šฉ ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ n_customers = 1000 customer_features = pd.DataFrame({ 'user_id': range(1, n_customers + 1), 'total_orders': np.random.poisson(5, n_customers), 'total_items': np.random.poisson(15, n_customers), 'total_spent': np.random.exponential(500, n_customers), 'avg_order_value': np.random.exponential(100, n_customers), 'order_span_days': np.random.randint(1, 365, n_customers), 'unique_categories': np.random.randint(1, 10, n_customers), 'unique_brands': np.random.randint(1, 20, n_customers), 'days_since_last_order': np.random.exponential(60, n_customers) }) # ์ดํƒˆ ์ •์˜: 90์ผ ์ด์ƒ ๊ตฌ๋งค ์—†์œผ๋ฉด ์ดํƒˆ (+ ๋žœ๋ค ๋…ธ์ด์ฆˆ) churn_prob = 1 / (1 + np.exp(-(customer_features['days_since_last_order'] - 90) / 30)) customer_features['churned'] = (np.random.random(n_customers) < churn_prob).astype(int) print(f"์ „์ฒด ๊ณ ๊ฐ: {len(customer_features)}") print(f"์ดํƒˆ ๊ณ ๊ฐ: {customer_features['churned'].sum()}") print(f"์ดํƒˆ๋ฅ : {customer_features['churned'].mean():.1%}")
์‹คํ–‰ ๊ฒฐ๊ณผ
์ „์ฒด ๊ณ ๊ฐ: 1000
์ดํƒˆ ๊ณ ๊ฐ: 371
์ดํƒˆ๋ฅ : 37.1%

ํ•™์Šต/ํ…Œ์ŠคํŠธ ๋ถ„๋ฆฌ

# ํ”ผ์ฒ˜์™€ ํƒ€๊ฒŸ ๋ถ„๋ฆฌ feature_cols = ['total_orders', 'total_items', 'total_spent', 'avg_order_value', 'order_span_days', 'unique_categories', 'unique_brands'] X = customer_features[feature_cols] y = customer_features['churned'] # ํ•™์Šต/ํ…Œ์ŠคํŠธ ๋ถ„๋ฆฌ (80:20) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) print(f"ํ•™์Šต ์„ธํŠธ: {len(X_train)}๊ฑด") print(f"ํ…Œ์ŠคํŠธ ์„ธํŠธ: {len(X_test)}๊ฑด") print(f"ํ•™์Šต ์„ธํŠธ ์ดํƒˆ๋ฅ : {y_train.mean():.1%}") print(f"ํ…Œ์ŠคํŠธ ์„ธํŠธ ์ดํƒˆ๋ฅ : {y_test.mean():.1%}")
์‹คํ–‰ ๊ฒฐ๊ณผ
ํ•™์Šต ์„ธํŠธ: 800๊ฑด
ํ…Œ์ŠคํŠธ ์„ธํŠธ: 200๊ฑด
ํ•™์Šต ์„ธํŠธ ์ดํƒˆ๋ฅ : 37.1%
ํ…Œ์ŠคํŠธ ์„ธํŠธ ์ดํƒˆ๋ฅ : 37.0%

ํ”ผ์ฒ˜ ์Šค์ผ€์ผ๋ง

# ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€, SVM ๋“ฑ์€ ์Šค์ผ€์ผ๋ง ํ•„์š” scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # ํŠธ๋ฆฌ ๊ธฐ๋ฐ˜ ๋ชจ๋ธ์€ ์Šค์ผ€์ผ๋ง ๋ถˆํ•„์š” # RandomForest, XGBoost๋Š” ์›๋ณธ ์‚ฌ์šฉ ๊ฐ€๋Šฅ print("์Šค์ผ€์ผ๋ง ์™„๋ฃŒ!") print(f"X_train_scaled ํ‰๊ท : {X_train_scaled.mean():.4f}") print(f"X_train_scaled ํ‘œ์ค€ํŽธ์ฐจ: {X_train_scaled.std():.4f}")
์‹คํ–‰ ๊ฒฐ๊ณผ
์Šค์ผ€์ผ๋ง ์™„๋ฃŒ!
X_train_scaled ํ‰๊ท : 0.0000
X_train_scaled ํ‘œ์ค€ํŽธ์ฐจ: 1.0000

3. ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€

์ด๋ก 

๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€๋Š” ์‹œ๊ทธ๋ชจ์ด๋“œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™•๋ฅ ์„ ์˜ˆ์ธกํ•˜๋Š” ์„ ํ˜• ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.

P(y=1โˆฃX)=11+eโˆ’(ฮฒ0+ฮฒ1X1+...+ฮฒnXn)P(y=1|X) = \frac{1}{1 + e^{-(\beta_0 + \beta_1 X_1 + ... + \beta_n X_n)}}

์žฅ์ :

  • ํ•ด์„ ๊ฐ€๋Šฅ์„ฑ ๋†’์Œ (๊ณ„์ˆ˜ = ์˜ํ–ฅ๋ ฅ)
  • ๊ณผ์ ํ•ฉ ์œ„ํ—˜ ๋‚ฎ์Œ
  • ํ•™์Šต ์†๋„ ๋น ๋ฆ„

๊ตฌํ˜„

from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, confusion_matrix # ๋ชจ๋ธ ํ•™์Šต lr_model = LogisticRegression(random_state=42, max_iter=1000) lr_model.fit(X_train_scaled, y_train) # ์˜ˆ์ธก y_pred_lr = lr_model.predict(X_test_scaled) y_prob_lr = lr_model.predict_proba(X_test_scaled)[:, 1] # ํ‰๊ฐ€ print("=== ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ๊ฒฐ๊ณผ ===") print(classification_report(y_test, y_pred_lr, target_names=['์œ ์ง€', '์ดํƒˆ']))
์‹คํ–‰ ๊ฒฐ๊ณผ
=== ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ๊ฒฐ๊ณผ ===
            precision    recall  f1-score   support

        ์œ ์ง€       0.68      0.83      0.75       126
        ์ดํƒˆ       0.60      0.39      0.47        74

  accuracy                           0.67       200
 macro avg       0.64      0.61      0.61       200
weighted avg       0.65      0.67      0.65       200

๊ณ„์ˆ˜ ํ•ด์„

import matplotlib.pyplot as plt # ํ”ผ์ฒ˜๋ณ„ ๊ณ„์ˆ˜ (์˜ํ–ฅ๋ ฅ) coef_df = pd.DataFrame({ 'feature': feature_cols, 'coefficient': lr_model.coef_[0] }) coef_df['abs_coef'] = coef_df['coefficient'].abs() coef_df = coef_df.sort_values('abs_coef', ascending=False) print("ํ”ผ์ฒ˜ ์ค‘์š”๋„ (๊ณ„์ˆ˜):") print(coef_df.to_string(index=False)) # ์‹œ๊ฐํ™” plt.figure(figsize=(10, 6)) colors = ['green' if c > 0 else 'red' for c in coef_df['coefficient']] plt.barh(coef_df['feature'], coef_df['coefficient'], color=colors) plt.xlabel('๊ณ„์ˆ˜ (์–‘์ˆ˜: ์ดํƒˆ ์ฆ๊ฐ€, ์Œ์ˆ˜: ์ดํƒˆ ๊ฐ์†Œ)') plt.title('๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ํ”ผ์ฒ˜ ์ค‘์š”๋„', fontsize=14, fontweight='bold') plt.axvline(x=0, color='black', linestyle='-', linewidth=0.5) plt.tight_layout() plt.show()
์‹คํ–‰ ๊ฒฐ๊ณผ
ํ”ผ์ฒ˜ ์ค‘์š”๋„ (๊ณ„์ˆ˜):
       feature  coefficient  abs_coef
   total_spent    -0.428513  0.428513
  total_orders    -0.312847  0.312847
   total_items    -0.245129  0.245129
avg_order_value    -0.189234  0.189234
order_span_days     0.156782  0.156782
unique_categories   -0.098456  0.098456
 unique_brands    -0.067321  0.067321

4. ๊ฒฐ์ • ํŠธ๋ฆฌ

์ด๋ก 

๊ฒฐ์ • ํŠธ๋ฆฌ๋Š” ํ”ผ์ฒ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„ํ• ํ•˜์—ฌ ์˜ˆ์ธกํ•ฉ๋‹ˆ๋‹ค.

์žฅ์ :

  • ํ•ด์„ ๊ฐ€๋Šฅ (ํŠธ๋ฆฌ ์‹œ๊ฐํ™”)
  • ์Šค์ผ€์ผ๋ง ๋ถˆํ•„์š”
  • ๋น„์„ ํ˜• ๊ด€๊ณ„ ํ•™์Šต

๋‹จ์ :

  • ๊ณผ์ ํ•ฉ ๊ฒฝํ–ฅ
  • ๋ถˆ์•ˆ์ •์„ฑ (๋ฐ์ดํ„ฐ ๋ณ€ํ™”์— ๋ฏผ๊ฐ)

๊ตฌํ˜„

from sklearn.tree import DecisionTreeClassifier, plot_tree # ๋ชจ๋ธ ํ•™์Šต dt_model = DecisionTreeClassifier( max_depth=5, # ๊ณผ์ ํ•ฉ ๋ฐฉ์ง€ min_samples_split=20, # ์ตœ์†Œ ๋ถ„ํ•  ์ƒ˜ํ”Œ ์ˆ˜ random_state=42 ) dt_model.fit(X_train, y_train) # ์˜ˆ์ธก y_pred_dt = dt_model.predict(X_test) y_prob_dt = dt_model.predict_proba(X_test)[:, 1] # ํ‰๊ฐ€ print("=== ๊ฒฐ์ • ํŠธ๋ฆฌ ๊ฒฐ๊ณผ ===") print(classification_report(y_test, y_pred_dt, target_names=['์œ ์ง€', '์ดํƒˆ']))
์‹คํ–‰ ๊ฒฐ๊ณผ
=== ๊ฒฐ์ • ํŠธ๋ฆฌ ๊ฒฐ๊ณผ ===
            precision    recall  f1-score   support

        ์œ ์ง€       0.70      0.79      0.74       126
        ์ดํƒˆ       0.57      0.45      0.50        74

  accuracy                           0.66       200
 macro avg       0.63      0.62      0.62       200
weighted avg       0.65      0.66      0.65       200

ํŠธ๋ฆฌ ์‹œ๊ฐํ™”

# ๊ฒฐ์ • ํŠธ๋ฆฌ ์‹œ๊ฐํ™” plt.figure(figsize=(20, 10)) plot_tree( dt_model, feature_names=feature_cols, class_names=['์œ ์ง€', '์ดํƒˆ'], filled=True, rounded=True, fontsize=10, max_depth=3 # ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•ด ๊นŠ์ด ์ œํ•œ ) plt.title('๊ฒฐ์ • ํŠธ๋ฆฌ ์‹œ๊ฐํ™” (๊นŠ์ด 3๊นŒ์ง€)', fontsize=16, fontweight='bold') plt.tight_layout() plt.show()
์‹คํ–‰ ๊ฒฐ๊ณผ
[๊ฒฐ์ • ํŠธ๋ฆฌ ์‹œ๊ฐํ™” ์ถœ๋ ฅ]
- ๋ฃจํŠธ ๋…ธ๋“œ: total_spent <= 245.32
- ์ขŒ์ธก(True): order_span_days <= 156
  - ์ขŒ์ธก: total_orders <= 3.5 โ†’ ์ดํƒˆ (gini=0.38)
  - ์šฐ์ธก: ์œ ์ง€ (gini=0.42)
- ์šฐ์ธก(False): total_orders <= 4.5
  - ์ขŒ์ธก: ์ดํƒˆ (gini=0.35)
  - ์šฐ์ธก: ์œ ์ง€ (gini=0.28)

5. ๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ

์ด๋ก 

๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ๋Š” ์—ฌ๋Ÿฌ ๊ฒฐ์ • ํŠธ๋ฆฌ๋ฅผ ์•™์ƒ๋ธ”ํ•˜์—ฌ ์˜ˆ์ธกํ•ฉ๋‹ˆ๋‹ค.

์ž‘๋™ ์›๋ฆฌ:

  1. ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์ƒ˜ํ”Œ๋ง์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ
  2. ๊ฐ ๋ฐ์ดํ„ฐ์…‹์œผ๋กœ ๊ฒฐ์ • ํŠธ๋ฆฌ ํ•™์Šต
  3. ๋ชจ๋“  ํŠธ๋ฆฌ์˜ ์˜ˆ์ธก์„ ํˆฌํ‘œ(๋‹ค์ˆ˜๊ฒฐ)

๊ตฌํ˜„

from sklearn.ensemble import RandomForestClassifier # ๋ชจ๋ธ ํ•™์Šต rf_model = RandomForestClassifier( n_estimators=100, # ํŠธ๋ฆฌ ๊ฐœ์ˆ˜ max_depth=10, # ์ตœ๋Œ€ ๊นŠ์ด min_samples_split=10, random_state=42, n_jobs=-1 # ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ) rf_model.fit(X_train, y_train) # ์˜ˆ์ธก y_pred_rf = rf_model.predict(X_test) y_prob_rf = rf_model.predict_proba(X_test)[:, 1] # ํ‰๊ฐ€ print("=== ๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ ๊ฒฐ๊ณผ ===") print(classification_report(y_test, y_pred_rf, target_names=['์œ ์ง€', '์ดํƒˆ']))
์‹คํ–‰ ๊ฒฐ๊ณผ
=== ๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ ๊ฒฐ๊ณผ ===
            precision    recall  f1-score   support

        ์œ ์ง€       0.72      0.84      0.78       126
        ์ดํƒˆ       0.64      0.47      0.54        74

  accuracy                           0.70       200
 macro avg       0.68      0.66      0.66       200
weighted avg       0.69      0.70      0.69       200

ํ”ผ์ฒ˜ ์ค‘์š”๋„

# ํ”ผ์ฒ˜ ์ค‘์š”๋„ importance_df = pd.DataFrame({ 'feature': feature_cols, 'importance': rf_model.feature_importances_ }).sort_values('importance', ascending=False) print("ํ”ผ์ฒ˜ ์ค‘์š”๋„:") print(importance_df.to_string(index=False)) # ์‹œ๊ฐํ™” plt.figure(figsize=(10, 6)) plt.barh(importance_df['feature'], importance_df['importance'], color='steelblue') plt.xlabel('์ค‘์š”๋„') plt.title('๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ ํ”ผ์ฒ˜ ์ค‘์š”๋„', fontsize=14, fontweight='bold') plt.tight_layout() plt.show()

Feature Importance

ํ”ผ์ฒ˜ ์ค‘์š”๋„ ๋ถ„์„ ๊ฒฐ๊ณผ, total_spent์™€ total_items๊ฐ€ ์ดํƒˆ ์˜ˆ์ธก์— ๊ฐ€์žฅ ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.


6. XGBoost

์ด๋ก 

XGBoost(eXtreme Gradient Boosting)๋Š” ๊ทธ๋ž˜๋””์–ธํŠธ ๋ถ€์ŠคํŒ…์˜ ์ตœ์ ํ™”๋œ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค.

์žฅ์ :

  • ๋†’์€ ์˜ˆ์ธก ์„ฑ๋Šฅ
  • ์ •๊ทœํ™”๋กœ ๊ณผ์ ํ•ฉ ๋ฐฉ์ง€
  • ๊ฒฐ์ธก์น˜ ์ž๋™ ์ฒ˜๋ฆฌ
  • ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์ง€์›

๊ตฌํ˜„

from xgboost import XGBClassifier # ๋ชจ๋ธ ํ•™์Šต xgb_model = XGBClassifier( n_estimators=100, max_depth=6, learning_rate=0.1, subsample=0.8, # ํ–‰ ์ƒ˜ํ”Œ๋ง colsample_bytree=0.8, # ์—ด ์ƒ˜ํ”Œ๋ง random_state=42, eval_metric='logloss' ) xgb_model.fit(X_train, y_train) # ์˜ˆ์ธก y_pred_xgb = xgb_model.predict(X_test) y_prob_xgb = xgb_model.predict_proba(X_test)[:, 1] # ํ‰๊ฐ€ print("=== XGBoost ๊ฒฐ๊ณผ ===") print(classification_report(y_test, y_pred_xgb, target_names=['์œ ์ง€', '์ดํƒˆ']))
์‹คํ–‰ ๊ฒฐ๊ณผ
=== XGBoost ๊ฒฐ๊ณผ ===
            precision    recall  f1-score   support

        ์œ ์ง€       0.74      0.83      0.78       126
        ์ดํƒˆ       0.65      0.53      0.58        74

  accuracy                           0.72       200
 macro avg       0.70      0.68      0.68       200
weighted avg       0.71      0.72      0.71       200

7. ๋ชจ๋ธ ํ‰๊ฐ€

ํ˜ผ๋™ ํ–‰๋ ฌ (Confusion Matrix)

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay # ํ˜ผ๋™ ํ–‰๋ ฌ ์‹œ๊ฐํ™” fig, axes = plt.subplots(1, 3, figsize=(15, 4)) models = [ ('Logistic Regression', y_pred_lr), ('Random Forest', y_pred_rf), ('XGBoost', y_pred_xgb) ] for ax, (name, y_pred) in zip(axes, models): cm = confusion_matrix(y_test, y_pred) disp = ConfusionMatrixDisplay(cm, display_labels=['์œ ์ง€', '์ดํƒˆ']) disp.plot(ax=ax, cmap='Blues', values_format='d') ax.set_title(name) plt.tight_layout() plt.show()

Confusion Matrix

ํ˜ผ๋™ ํ–‰๋ ฌ์—์„œ ๋Œ€๊ฐ์„ (์ขŒ์ƒโ†’์šฐํ•˜)์€ ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์ธก์ž…๋‹ˆ๋‹ค. XGBoost๊ฐ€ ์ดํƒˆ ๊ณ ๊ฐ(์šฐํ•˜)์„ ๊ฐ€์žฅ ๋งŽ์ด ์ •ํ™•ํ•˜๊ฒŒ ์˜ˆ์ธกํ–ˆ์Šต๋‹ˆ๋‹ค.

ROC ๊ณก์„ 

from sklearn.metrics import roc_curve, roc_auc_score plt.figure(figsize=(10, 8)) # ๊ฐ ๋ชจ๋ธ์˜ ROC ๊ณก์„  for name, y_prob in [('Logistic Regression', y_prob_lr), ('Random Forest', y_prob_rf), ('XGBoost', y_prob_xgb)]: fpr, tpr, _ = roc_curve(y_test, y_prob) auc = roc_auc_score(y_test, y_prob) plt.plot(fpr, tpr, linewidth=2, label=f'{name} (AUC={auc:.3f})') # ๊ธฐ์ค€์„  (๋žœ๋ค ์˜ˆ์ธก) plt.plot([0, 1], [0, 1], 'k--', linewidth=1, label='Random (AUC=0.500)') plt.xlabel('False Positive Rate (์œ„์–‘์„ฑ๋ฅ )', fontsize=12) plt.ylabel('True Positive Rate (์žฌํ˜„์œจ)', fontsize=12) plt.title('ROC ๊ณก์„  ๋น„๊ต', fontsize=14, fontweight='bold') plt.legend(loc='lower right') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()

ROC Curve

ROC ๊ณก์„ ์ด ์ขŒ์ƒ๋‹จ์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ์ข‹์€ ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค. AUC๊ฐ€ 0.7 ์ด์ƒ์ด๋ฉด ์–‘ํ˜ธํ•œ ์„ฑ๋Šฅ์œผ๋กœ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค.

ํ‰๊ฐ€ ์ง€ํ‘œ ์š”์•ฝ

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # ๋ชจ๋ธ๋ณ„ ์„ฑ๋Šฅ ๋น„๊ต results = [] for name, y_pred, y_prob in [('Logistic Regression', y_pred_lr, y_prob_lr), ('Random Forest', y_pred_rf, y_prob_rf), ('XGBoost', y_pred_xgb, y_prob_xgb)]: results.append({ '๋ชจ๋ธ': name, '์ •ํ™•๋„': accuracy_score(y_test, y_pred), '์ •๋ฐ€๋„': precision_score(y_test, y_pred), '์žฌํ˜„์œจ': recall_score(y_test, y_pred), 'F1': f1_score(y_test, y_pred), 'AUC': roc_auc_score(y_test, y_prob) }) results_df = pd.DataFrame(results).round(3) print("=== ๋ชจ๋ธ ์„ฑ๋Šฅ ๋น„๊ต ===") print(results_df.to_string(index=False))
์‹คํ–‰ ๊ฒฐ๊ณผ
=== ๋ชจ๋ธ ์„ฑ๋Šฅ ๋น„๊ต ===
             ๋ชจ๋ธ  ์ •ํ™•๋„  ์ •๋ฐ€๋„  ์žฌํ˜„์œจ     F1    AUC
Logistic Regression  0.670  0.580  0.392  0.468  0.687
    Random Forest  0.705  0.636  0.473  0.543  0.724
          XGBoost  0.720  0.650  0.527  0.582  0.738

8. ํด๋ž˜์Šค ๋ถˆ๊ท ํ˜• ์ฒ˜๋ฆฌ

๋ฌธ์ œ

์ดํƒˆ ์˜ˆ์ธก์—์„œ ์ดํƒˆ ๊ณ ๊ฐ์€ ๋ณดํ†ต 10-20%๋กœ ์†Œ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋ถˆ๊ท ํ˜• ๋ฐ์ดํ„ฐ์—์„œ๋Š” ๋ชจ๋ธ์ด ๋‹ค์ˆ˜ ํด๋ž˜์Šค๋งŒ ์˜ˆ์ธกํ•˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

# ๋ฐฉ๋ฒ• 1: class_weight ์กฐ์ • rf_balanced = RandomForestClassifier( n_estimators=100, class_weight='balanced', # ์†Œ์ˆ˜ ํด๋ž˜์Šค์— ๊ฐ€์ค‘์น˜ random_state=42 ) rf_balanced.fit(X_train, y_train) y_pred_balanced = rf_balanced.predict(X_test) print("=== class_weight='balanced' ์ ์šฉ ๊ฒฐ๊ณผ ===") print(f"๊ธฐ์กด ์žฌํ˜„์œจ: {recall_score(y_test, y_pred_rf):.3f}") print(f"๊ท ํ˜• ์žฌํ˜„์œจ: {recall_score(y_test, y_pred_balanced):.3f}") # ๋ฐฉ๋ฒ• 2: ์ž„๊ณ„๊ฐ’ ์กฐ์ • threshold = 0.3 # ๊ธฐ๋ณธ 0.5์—์„œ ๋‚ฎ์ถค y_pred_adjusted = (y_prob_xgb >= threshold).astype(int) print(f"\n=== ์ž„๊ณ„๊ฐ’ ์กฐ์ • (0.5 โ†’ 0.3) ===") print(f"๊ธฐ์กด ์žฌํ˜„์œจ: {recall_score(y_test, y_pred_xgb):.3f}") print(f"์กฐ์ • ์žฌํ˜„์œจ: {recall_score(y_test, y_pred_adjusted):.3f}") print(f"๊ธฐ์กด ์ •๋ฐ€๋„: {precision_score(y_test, y_pred_xgb):.3f}") print(f"์กฐ์ • ์ •๋ฐ€๋„: {precision_score(y_test, y_pred_adjusted):.3f}")
์‹คํ–‰ ๊ฒฐ๊ณผ
=== class_weight='balanced' ์ ์šฉ ๊ฒฐ๊ณผ ===
๊ธฐ์กด ์žฌํ˜„์œจ: 0.473
๊ท ํ˜• ์žฌํ˜„์œจ: 0.568

=== ์ž„๊ณ„๊ฐ’ ์กฐ์ • (0.5 โ†’ 0.3) ===
๊ธฐ์กด ์žฌํ˜„์œจ: 0.527
์กฐ์ • ์žฌํ˜„์œจ: 0.716
๊ธฐ์กด ์ •๋ฐ€๋„: 0.650
์กฐ์ • ์ •๋ฐ€๋„: 0.485

ํ€ด์ฆˆ 1: ํ‰๊ฐ€ ์ง€ํ‘œ ํ•ด์„

๋ฌธ์ œ

์ดํƒˆ ์˜ˆ์ธก ๋ชจ๋ธ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์„ ๋•Œ, ์–ด๋–ค ์ง€ํ‘œ๋ฅผ ์šฐ์„ ํ•ด์•ผ ํ• ๊นŒ์š”?

์ง€ํ‘œ๊ฐ’
์ •ํ™•๋„0.92
์ •๋ฐ€๋„0.75
์žฌํ˜„์œจ0.45
AUC0.82

์ •๋‹ต ๋ณด๊ธฐ

์žฌํ˜„์œจ(Recall)์„ ์šฐ์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์žฌํ˜„์œจ 0.45 = ์‹ค์ œ ์ดํƒˆ ๊ณ ๊ฐ ์ค‘ 45%๋งŒ ํƒ์ง€
  • 55%์˜ ์ดํƒˆ ๊ณ ๊ฐ์„ ๋†“์นจ (False Negative)
  • ์ดํƒˆ ๋ฐฉ์ง€ ์บ ํŽ˜์ธ์˜ ํšจ๊ณผ๊ฐ€ ์ œํ•œ๋จ

๊ฐœ์„  ๋ฐฉ๋ฒ•:

  1. ์ž„๊ณ„๊ฐ’์„ 0.5์—์„œ 0.3์œผ๋กœ ๋‚ฎ์ถค
  2. class_weight=โ€˜balancedโ€™ ์‚ฌ์šฉ
  3. SMOTE๋กœ ์˜ค๋ฒ„์ƒ˜ํ”Œ๋ง

๋น„์ฆˆ๋‹ˆ์Šค ๊ด€์ ์—์„œ ์ดํƒˆ ๊ณ ๊ฐ์„ ๋†“์น˜๋Š” ๋น„์šฉ > ๋น„์ดํƒˆ ๊ณ ๊ฐ์—๊ฒŒ ์บ ํŽ˜์ธ ๋น„์šฉ


ํ€ด์ฆˆ 2: ๋ชจ๋ธ ์„ ํƒ

๋ฌธ์ œ

๋‹ค์Œ ์ƒํ™ฉ์—์„œ ์–ด๋–ค ๋ชจ๋ธ์„ ์„ ํƒํ•ด์•ผ ํ• ๊นŒ์š”?

  1. ๋ชจ๋ธ ํ•ด์„์ด ์ค‘์š”ํ•˜๊ณ , ์–ด๋–ค ํ”ผ์ฒ˜๊ฐ€ ์ดํƒˆ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ์„ค๋ช…ํ•ด์•ผ ํ•จ
  2. ๋ฐ์ดํ„ฐ๊ฐ€ 1,000๊ฑด ๋ฏธ๋งŒ์œผ๋กœ ์ ์Œ

์ •๋‹ต ๋ณด๊ธฐ

๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

์ด์œ :

  1. ํ•ด์„ ๊ฐ€๋Šฅ์„ฑ: ๊ณ„์ˆ˜๊ฐ€ ๊ฐ ํ”ผ์ฒ˜์˜ ์˜ํ–ฅ๋ ฅ์„ ์ง์ ‘ ๋ณด์—ฌ์คŒ

    • ์–‘์ˆ˜ ๊ณ„์ˆ˜: ์ดํƒˆ ํ™•๋ฅ  ์ฆ๊ฐ€
    • ์Œ์ˆ˜ ๊ณ„์ˆ˜: ์ดํƒˆ ํ™•๋ฅ  ๊ฐ์†Œ
  2. ๋ฐ์ดํ„ฐ ํฌ๊ธฐ: ๋‹จ์ˆœ ๋ชจ๋ธ์ด ์ ์€ ๋ฐ์ดํ„ฐ์—์„œ ๋” ์•ˆ์ •์ 

    • XGBoost๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์•ผ ์žฅ์  ๋ฐœํœ˜
    • ๊ณผ์ ํ•ฉ ์œ„ํ—˜์ด ๋‚ฎ์Œ
  3. ๋น„์ฆˆ๋‹ˆ์Šค ์„ค๋ช…: ๊ฒฝ์˜์ง„์—๊ฒŒ โ€œtotal_spent๊ฐ€ 100 ์ฆ๊ฐ€ํ•˜๋ฉด ์ดํƒˆ ํ™•๋ฅ ์ด 5% ๊ฐ์†Œโ€๋ผ๊ณ  ์„ค๋ช… ๊ฐ€๋Šฅ


์ •๋ฆฌ

๋ชจ๋ธ ์„ ํƒ ๊ฐ€์ด๋“œ

์ƒํ™ฉ์ถ”์ฒœ ๋ชจ๋ธ
ํ•ด์„ ํ•„์š”, ๋ฐ์ดํ„ฐ ์ ์Œ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€
๋น„์„ ํ˜• ๊ด€๊ณ„, ํ•ด์„ ํ•„์š”๊ฒฐ์ • ํŠธ๋ฆฌ
๋†’์€ ์„ฑ๋Šฅ, ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐXGBoost
๊ท ํ˜•์žกํžŒ ์„ฑ๋Šฅ๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ

ํ‰๊ฐ€ ์ง€ํ‘œ ์„ ํƒ

์ƒํ™ฉ์šฐ์„  ์ง€ํ‘œ
False Positive ๋น„์šฉ ๋†’์Œ (์ŠคํŒธ ํ•„ํ„ฐ)์ •๋ฐ€๋„
False Negative ๋น„์šฉ ๋†’์Œ (์ดํƒˆ ์˜ˆ์ธก)์žฌํ˜„์œจ
๊ท ํ˜•์žกํžŒ ํ‰๊ฐ€F1 Score
์ „์ฒด์ ์ธ ๋ถ„๋ฅ˜ ๋Šฅ๋ ฅAUC-ROC

๋‹ค์Œ ๋‹จ๊ณ„

๋ถ„๋ฅ˜ ๋ชจ๋ธ์„ ๋งˆ์Šคํ„ฐํ–ˆ์Šต๋‹ˆ๋‹ค! ๋‹ค์Œ์œผ๋กœ ํšŒ๊ท€ ์˜ˆ์ธก์—์„œ CLV ์˜ˆ์ธก, ๋งค์ถœ ์˜ˆ์ธก ๋“ฑ ์—ฐ์†๊ฐ’์„ ์˜ˆ์ธกํ•˜๋Š” ๊ธฐ๋ฒ•์„ ๋ฐฐ์›Œ๋ณด์„ธ์š”.

Last updated on

๐Ÿค–AI Mock InterviewPractice with real questions