Условие
Бизнес-задача: предсказать, у кого из клиентов банка появится потребность взять отпуск ради эстетики (target ∈ {0, 1}). Дано:
train.csv: колонкиcustomer,<hashed_factor1>, …,<hashed_factor20>,target.test.csv: то же безtarget.
Формат вывода
answers.csv с колонками customer, target. Метрика — ROC-AUC ≥ 0.7.
Решение
Подход
Стандартный пайплайн бинарной классификации: разведка → препроцессинг → модель → оценка.
1. EDA (разведочный анализ)
import pandas as pd
import numpy as np
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")
# Базовое
print(train.shape, test.shape)
print(train.dtypes)
print(train['target'].value_counts(normalize=True)) # дисбаланс?
print(train.isna().sum()) # пропуски
print(train.describe()) # масштабы фичейХешированные фичи hashed_factor1..20 — обычно категориальные (строки/числа). Понимаем:
- сколько уникальных значений в каждой;
- есть ли пересечение train/test по уровням;
- зависимость target от каждой фичи через
groupby('factor')['target'].mean().
2. Препроцессинг
features = [c for c in train.columns if c.startswith("hashed")]
# Если фичи категориальные с большим числом уровней — TargetEncoding.
from category_encoders import TargetEncoder
te = TargetEncoder(cols=features, smoothing=20)
X_train = te.fit_transform(train[features], train['target'])
X_test = te.transform(test[features])
# Альтернатива: для CatBoost/LightGBM — нативная поддержка категориальных.3. Модель
Для табличных данных с категориальными фичами GBDT (CatBoost / LightGBM / XGBoost) почти всегда лучшее решение.
from catboost import CatBoostClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import roc_auc_score
X = train[features].astype(str) # чтобы CatBoost понял как категориальные
y = train['target']
cat_features = list(range(X.shape[1]))
model = CatBoostClassifier(
iterations=1000,
learning_rate=0.05,
depth=6,
cat_features=cat_features,
eval_metric='AUC',
random_seed=42,
verbose=100,
)
X_tr, X_va, y_tr, y_va = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
model.fit(X_tr, y_tr, eval_set=(X_va, y_va), early_stopping_rounds=50)
print("Val AUC:", roc_auc_score(y_va, model.predict_proba(X_va)[:, 1]))4. Кросс-валидация
from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
aucs = []
test_preds = np.zeros(len(test))
for fold, (tr_idx, va_idx) in enumerate(skf.split(X, y)):
m = CatBoostClassifier(iterations=1000, learning_rate=0.05, depth=6,
cat_features=cat_features, eval_metric='AUC',
random_seed=42, verbose=False)
m.fit(X.iloc[tr_idx], y.iloc[tr_idx],
eval_set=(X.iloc[va_idx], y.iloc[va_idx]),
early_stopping_rounds=50)
val_pred = m.predict_proba(X.iloc[va_idx])[:, 1]
aucs.append(roc_auc_score(y.iloc[va_idx], val_pred))
test_preds += m.predict_proba(test[features].astype(str))[:, 1] / skf.n_splits
print(f"Fold {fold} AUC: {aucs[-1]:.4f}")
print("Mean CV AUC:", np.mean(aucs))5. Сабмит
submission = pd.DataFrame({
"customer": test["customer"],
"target": test_preds,
})
submission.to_csv("answers.csv", index=False)6. Если AUC < 0.7
- Feature engineering: пары фич (counts, ratios, target-encoded).
- Stacking: 2-3 модели разного типа (CatBoost, LightGBM, LogReg на TargetEncoded), затем мета-модель.
- Балансировка классов: если сильный дисбаланс,
class_weightsили undersampling. - Hyperparam tuning: Optuna / RandomSearch.
Подводные камни
- Утечка таргета. При TargetEncoding обязательно делать
out-of-foldэнкодинг внутри CV, иначе утечка раздуёт AUC на валидации. predictvspredict_proba. Для AUC нужен скор/вероятность, а не класс.- Категориальные через
astype(str)для CatBoost. Иначе CatBoost будет пытаться использовать как числовые. - Случайные сиды. Фиксируйте seed для воспроизводимости.
- Дисбаланс классов. Если 1% позитивов — AUC будет работать, но
accuracyнет; используйте AUC и/или PR-AUC. - Стратификация.
StratifiedKFoldобязательна при дисбалансе. - Тестовые уровни не в train. TargetEncoding должен корректно обрабатывать unseen — обычно через
default = global mean. - Калибровка вероятностей. Для AUC некритично (порядок важен), но если бизнес требует «вероятность 70%», нужен
CalibratedClassifierCV.
Эталонный ответ
CatBoost / LightGBM с категориальными фичами + 5-fold StratifiedKFold + усреднение test-предсказаний по фолдам. Базовый pipeline даёт AUC 0.75-0.85 на табличке такого формата. При проблемах — TargetEncoding (out-of-fold), feature engineering и stacking.