๋ถ๋ฅ ๋ชจ๋ธ
ํ์ต ๋ชฉํ
์ด ๋ ์ํผ๋ฅผ ์๋ฃํ๋ฉด ๋ค์์ ํ ์ ์์ต๋๋ค:
- ๋ก์ง์คํฑ ํ๊ท๋ก ์ดํ ์์ธก
- ๊ฒฐ์ ํธ๋ฆฌ์ ๋๋ค ํฌ๋ ์คํธ ๊ตฌํ
- 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. ๋ก์ง์คํฑ ํ๊ท
์ด๋ก
๋ก์ง์คํฑ ํ๊ท๋ ์๊ทธ๋ชจ์ด๋ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ฅ ์ ์์ธกํ๋ ์ ํ ๋ชจ๋ธ์ ๋๋ค.
์ฅ์ :
- ํด์ ๊ฐ๋ฅ์ฑ ๋์ (๊ณ์ = ์ํฅ๋ ฅ)
- ๊ณผ์ ํฉ ์ํ ๋ฎ์
- ํ์ต ์๋ ๋น ๋ฆ
๊ตฌํ
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.0673214. ๊ฒฐ์ ํธ๋ฆฌ
์ด๋ก
๊ฒฐ์ ํธ๋ฆฌ๋ ํผ์ฒ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถํ ํ์ฌ ์์ธกํฉ๋๋ค.
์ฅ์ :
- ํด์ ๊ฐ๋ฅ (ํธ๋ฆฌ ์๊ฐํ)
- ์ค์ผ์ผ๋ง ๋ถํ์
- ๋น์ ํ ๊ด๊ณ ํ์ต
๋จ์ :
- ๊ณผ์ ํฉ ๊ฒฝํฅ
- ๋ถ์์ ์ฑ (๋ฐ์ดํฐ ๋ณํ์ ๋ฏผ๊ฐ)
๊ตฌํ
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. ๋๋ค ํฌ๋ ์คํธ
์ด๋ก
๋๋ค ํฌ๋ ์คํธ๋ ์ฌ๋ฌ ๊ฒฐ์ ํธ๋ฆฌ๋ฅผ ์์๋ธํ์ฌ ์์ธกํฉ๋๋ค.
์๋ ์๋ฆฌ:
- ๋ถํธ์คํธ๋ฉ ์ํ๋ง์ผ๋ก ์ฌ๋ฌ ๋ฐ์ดํฐ์ ์์ฑ
- ๊ฐ ๋ฐ์ดํฐ์ ์ผ๋ก ๊ฒฐ์ ํธ๋ฆฌ ํ์ต
- ๋ชจ๋ ํธ๋ฆฌ์ ์์ธก์ ํฌํ(๋ค์๊ฒฐ)
๊ตฌํ
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()
ํผ์ฒ ์ค์๋ ๋ถ์ ๊ฒฐ๊ณผ, 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 2007. ๋ชจ๋ธ ํ๊ฐ
ํผ๋ ํ๋ ฌ (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()
ํผ๋ ํ๋ ฌ์์ ๋๊ฐ์ (์ข์โ์ฐํ)์ ์ฌ๋ฐ๋ฅธ ์์ธก์ ๋๋ค. 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 ๊ณก์ ์ด ์ข์๋จ์ ๊ฐ๊น์ธ์๋ก ์ข์ ๋ชจ๋ธ์ ๋๋ค. 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.7388. ํด๋์ค ๋ถ๊ท ํ ์ฒ๋ฆฌ
๋ฌธ์
์ดํ ์์ธก์์ ์ดํ ๊ณ ๊ฐ์ ๋ณดํต 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 |
| AUC | 0.82 |
์ ๋ต ๋ณด๊ธฐ
์ฌํ์จ(Recall)์ ์ฐ์ ํด์ผ ํฉ๋๋ค.
- ์ฌํ์จ 0.45 = ์ค์ ์ดํ ๊ณ ๊ฐ ์ค 45%๋ง ํ์ง
- 55%์ ์ดํ ๊ณ ๊ฐ์ ๋์นจ (False Negative)
- ์ดํ ๋ฐฉ์ง ์บ ํ์ธ์ ํจ๊ณผ๊ฐ ์ ํ๋จ
๊ฐ์ ๋ฐฉ๋ฒ:
- ์๊ณ๊ฐ์ 0.5์์ 0.3์ผ๋ก ๋ฎ์ถค
- class_weight=โbalancedโ ์ฌ์ฉ
- SMOTE๋ก ์ค๋ฒ์ํ๋ง
๋น์ฆ๋์ค ๊ด์ ์์ ์ดํ ๊ณ ๊ฐ์ ๋์น๋ ๋น์ฉ > ๋น์ดํ ๊ณ ๊ฐ์๊ฒ ์บ ํ์ธ ๋น์ฉ
ํด์ฆ 2: ๋ชจ๋ธ ์ ํ
๋ฌธ์
๋ค์ ์ํฉ์์ ์ด๋ค ๋ชจ๋ธ์ ์ ํํด์ผ ํ ๊น์?
- ๋ชจ๋ธ ํด์์ด ์ค์ํ๊ณ , ์ด๋ค ํผ์ฒ๊ฐ ์ดํ์ ์ํฅ์ ๋ฏธ์น๋์ง ์ค๋ช ํด์ผ ํจ
- ๋ฐ์ดํฐ๊ฐ 1,000๊ฑด ๋ฏธ๋ง์ผ๋ก ์ ์
์ ๋ต ๋ณด๊ธฐ
๋ก์ง์คํฑ ํ๊ท๋ฅผ ์ ํํฉ๋๋ค.
์ด์ :
-
ํด์ ๊ฐ๋ฅ์ฑ: ๊ณ์๊ฐ ๊ฐ ํผ์ฒ์ ์ํฅ๋ ฅ์ ์ง์ ๋ณด์ฌ์ค
- ์์ ๊ณ์: ์ดํ ํ๋ฅ ์ฆ๊ฐ
- ์์ ๊ณ์: ์ดํ ํ๋ฅ ๊ฐ์
-
๋ฐ์ดํฐ ํฌ๊ธฐ: ๋จ์ ๋ชจ๋ธ์ด ์ ์ ๋ฐ์ดํฐ์์ ๋ ์์ ์
- XGBoost๋ ๋ฐ์ดํฐ๊ฐ ๋ง์์ผ ์ฅ์ ๋ฐํ
- ๊ณผ์ ํฉ ์ํ์ด ๋ฎ์
-
๋น์ฆ๋์ค ์ค๋ช : ๊ฒฝ์์ง์๊ฒ โtotal_spent๊ฐ 100 ์ฆ๊ฐํ๋ฉด ์ดํ ํ๋ฅ ์ด 5% ๊ฐ์โ๋ผ๊ณ ์ค๋ช ๊ฐ๋ฅ
์ ๋ฆฌ
๋ชจ๋ธ ์ ํ ๊ฐ์ด๋
| ์ํฉ | ์ถ์ฒ ๋ชจ๋ธ |
|---|---|
| ํด์ ํ์, ๋ฐ์ดํฐ ์ ์ | ๋ก์ง์คํฑ ํ๊ท |
| ๋น์ ํ ๊ด๊ณ, ํด์ ํ์ | ๊ฒฐ์ ํธ๋ฆฌ |
| ๋์ ์ฑ๋ฅ, ๋์ฉ๋ ๋ฐ์ดํฐ | XGBoost |
| ๊ท ํ์กํ ์ฑ๋ฅ | ๋๋ค ํฌ๋ ์คํธ |
ํ๊ฐ ์งํ ์ ํ
| ์ํฉ | ์ฐ์ ์งํ |
|---|---|
| False Positive ๋น์ฉ ๋์ (์คํธ ํํฐ) | ์ ๋ฐ๋ |
| False Negative ๋น์ฉ ๋์ (์ดํ ์์ธก) | ์ฌํ์จ |
| ๊ท ํ์กํ ํ๊ฐ | F1 Score |
| ์ ์ฒด์ ์ธ ๋ถ๋ฅ ๋ฅ๋ ฅ | AUC-ROC |
๋ค์ ๋จ๊ณ
๋ถ๋ฅ ๋ชจ๋ธ์ ๋ง์คํฐํ์ต๋๋ค! ๋ค์์ผ๋ก ํ๊ท ์์ธก์์ CLV ์์ธก, ๋งค์ถ ์์ธก ๋ฑ ์ฐ์๊ฐ์ ์์ธกํ๋ ๊ธฐ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.