Data Science

Feature Engineering и предобработка данных

2012 год. Kaggle. Heritage Health Prize - 3 миллиона за предсказание госпитализаций. Победившая команда тратит 90% времени не на модели, не на нейросети - на признаки. Та же нейросеть: 62% на сырых данных, 94% на инженерных. Это не трюк. Это feature engineering.

  • **Netflix**: признак 'смотрел ли пользователь трейлер до выхода фильма' поднимает точность рекомендаций на 8% - лучше, чем любое изменение архитектуры
  • **Fraud detection в банках**: признак 'отклонение текущей транзакции от медианы последних 30 транзакций пользователя' - один из топ-3 по importance в большинстве антифрод систем
  • **Churn prediction**: признак 'количество дней с момента последней активности' работает лучше, чем сотни демографических переменных - потому что содержит сигнал, недоступный без временного контекста

Encoding и scaling: язык, на котором говорят модели

**2012 год. Kaggle. Heritage Health Prize - 3 миллиона долларов за предсказание госпитализаций.** Победившая команда после финала раскрыла структуру работы: 90% времени ушло на конструирование признаков. Не на выбор архитектуры, не на гиперпараметры - на признаки. Та же нейросеть на сырых данных давала 62% accuracy. На инженерных - 94%. Разрыв не в алгоритме.

Модель не видит 'категорию' или 'город'. Она видит числа. Feature engineering - это перевод реального мира в числовое пространство, которое модель может оптимизировать.

Encoding категориальных переменных

Допустим, есть колонка `color` со значениями `red`, `green`, `blue`. Присвоить 0, 1, 2 - плохая идея: модель решит, что `blue` = 2 * `red`, то есть вдвое больше красного. Порядка нет - не нужно его выдумывать.

One-Hot взрывает размерность: если `city` имеет 500 уникальных значений - получается 500 колонок. Для высокой кардинальности применяют **Target Encoding**: заменяют категорию средним значением целевой переменной по ней. В LightGBM и CatBoost это встроено из коробки.

Scaling: почему логистическая регрессия ненавидит сырые данные

Признак `age` лежит в диапазоне 0-100. Признак `salary` - в диапазоне 20,000-200,000. Без масштабирования градиентный спуск будет делать огромные шаги по `salary` и крошечные по `age`. Функция потерь - вытянутый эллипс вместо круга. Скорость сходимости падает в десятки раз.

Scaler нужно fit делать только на train-данных. fit_transform на всём датасете - это утечка данных: модель косвенно 'видит' тест через статистики (mean, std). Результаты валидации оказываются завышены, в проде - разочарование.

Колонка `temperature` имеет выброс: 99% значений в диапазоне -10 до +40, но есть несколько значений 200+. Какой scaler выбрать?

StandardScaler сдвинет всё в сторону из-за выбросов (mean и std чувствительны). MinMaxScaler сожмёт все нормальные значения в крошечный диапазон, потому что max будет 200+. RobustScaler использует median и IQR - устойчивые к выбросам статистики. Вариант с деревьями технически верен для некоторых алгоритмов, но вопрос про выбор scaler, а не алгоритма.

Пропущенные значения: не удалять, а понимать

Пропущенные значения: не удалять, а понимать

Строчка `df.dropna()` - самая опасная строка в data science. Пациент умер до следующего измерения? Это не шум - это сигнал. Клиент не заполнил поле 'доход'? Возможно, именно богатые его пропускают. Удалить пропуски значит выбросить информацию о паттерне пропуска.

Теория различает три типа пропусков. **MCAR** (Missing Completely At Random) - пропуск не зависит ни от чего, датчик случайно отказал. **MAR** (Missing At Random) - пропуск зависит от других наблюдаемых переменных, но не от самого пропущенного значения. **MNAR** (Missing Not At Random) - пропуск зависит от самого значения: богатые не сообщают доход, смертельно больные не приходят на обследование.

Правило большого пальца: если пропусков меньше 5% - simple imputation. От 5% до 20% - KNN или MICE. Больше 20% - создать бинарный флаг пропуска и заполнить медианой. Больше 50% - колонка под вопросом, нужен домен-анализ: почему так много пропусков?

В production-системах часто добавляют бинарный признак `feature_X_missing = 1/0` рядом с заполненным `feature_X`. LightGBM и XGBoost умеют нативно работать с NaN и сами находят оптимальное направление для пропусков при сплите - это встроенный учёт MNAR без явного кода.

Есть колонка 'loan_amount' в банковских данных. Анализ показывает: строки с пропусками имеют в среднем вдвое ниже доход. Это какой тип пропуска?

Пропуск зависит от observable переменной `income`, но сам по себе `loan_amount` не определяет, будет ли он пропущен - это MAR. MNAR был бы, если сама сумма кредита определяла пропуск (например, очень большие суммы скрывают). Тип пропуска критически важен: при MNAR simple imputation создаёт систематическое смещение.

Feature selection и feature importance: отрезать лишнее

Feature selection и feature importance: отрезать лишнее

После добавления дополнительных признаков через One-Hot и взаимодействий между переменными датасет может вырасти с 20 до 500 колонок. **Curse of dimensionality**: в высоких измерениях расстояния теряют смысл, k-NN деградирует, линейные модели переобучаются, обучение замедляется экспоненциально. Нужен отбор.

Методы делятся на три класса. **Filter methods** оценивают признаки независимо от модели - быстро, но не учитывают взаимодействия. **Wrapper methods** обучают модель на подмножествах признаков - точно, но дорого. **Embedded methods** встраивают отбор в обучение самой модели (L1 регуляризация, feature importance деревьев).

LightGBM feature importance считается двумя способами: **split** (сколько раз признак используется для сплита) и **gain** (суммарное улучшение метрики от всех сплитов по признаку). Gain надёжнее: признак с большим gain реально снижает ошибку, а не просто часто выбирается.

SHAP (SHapley Additive exPlanations) - это теория игр, применённая к ML. Shapley value каждого признака = его средний вклад по всем возможным коалициям признаков. В production это объяснимость: банк обязан объяснить клиенту, почему отказал в кредите. SHAP даёт это автоматически.

Отбор - это половина истории. Создание признаков - другая половина: - **Datetime**: `hour`, `day_of_week`, `is_weekend`, `days_since_signup` - **Взаимодействия**: `price * quantity = revenue` (то, что модель сама вряд ли найдёт) - **Агрегаты**: `user_avg_purchase_last_30d`, `category_item_count` - **Трансформации**: `log(income)` для скошенных распределений, `sqrt(distance)` - **Лаги**: в временных рядах `sales_lag_1d`, `sales_lag_7d`, `sales_rolling_mean_7d`

Феликс Санчес, занявший 1-е место в Heritage Health Prize, создал более 600 признаков из базовых 63 полей - данных о госпитализациях. Ключевые: 'количество посещений врача за последний год', 'было ли экстренное обращение за 30 дней', 'тренд изменения количества медикаментов'. Ни один из этих признаков не был в исходных данных.

Больше признаков = лучше модель

Нерелевантные признаки добавляют шум и усложняют обучение. Curse of dimensionality делает пространство всё более разреженным

В высоких измерениях каждая новая точка данных становится всё дальше от остальных. Деревья тратят сплиты на шумовые признаки. L2-расстояния перестают работать для k-NN. Оптимальный набор признаков почти всегда меньше доступного.

LightGBM показывает feature importance: признак A имеет split=500, gain=0.1. Признак B: split=50, gain=0.9. Какой признак реально важнее для качества модели?

Gain = суммарное снижение функции потерь по всем сплитам с этим признаком. Split = просто счётчик. Признак B при редком использовании даёт огромный gain - каждый его сплит резко снижает ошибку. Признак A используется часто, но каждый раз почти не помогает. Gain надёжнее для оценки реального вклада.

Ключевые идеи

  • **Encoding**: One-Hot для номинальных, Ordinal только при реальном порядке, Target Encoding при высокой кардинальности - с защитой от leakage
  • **Scaling**: StandardScaler для линейных моделей/нейросетей, RobustScaler при выбросах, деревья не нуждаются в масштабировании
  • **Imputation**: тип пропуска (MCAR/MAR/MNAR) определяет стратегию; создавать бинарный флаг пропуска как отдельный признак
  • **Feature importance**: Gain важнее Split в деревьях; SHAP даёт честное распределение вклада для коррелированных признаков
  • **Инженерия признаков**: создание новых признаков (взаимодействия, агрегаты, лаги) часто даёт больший прирост, чем смена алгоритма

Связанные темы

Feature engineering связывает предобработку данных с моделированием:

  • Data Preprocessing — Базовая предобработка как фундамент для инженерии признаков
  • Model Evaluation — Feature importance и отбор признаков требуют метрик оценки
  • PCA и снижение размерности — Следующий шаг после feature selection - трансформация пространства

Вопросы для размышления

  • Почему Target Encoding нужно делать только на train-данных? Что произойдёт, если fit на всём датасете?
  • В каком случае пропущенное значение само по себе является информативным признаком, и как это использовать в модели?
  • Feature importance показывает, что признак 'user_id' стоит на первом месте. Что это значит для модели и что делать?

Связанные уроки

  • ml-04-data-preprocessing — Feature engineering - это следующий уровень поверх базовой предобработки из ML-04
  • ml-05-evaluation — Feature importance и отбор признаков требуют метрик оценки модели
  • ml-01-intro — Понимание типов задач ML необходимо для осмысленного выбора признаков
  • ds-04 — EDA предшествует feature engineering - сначала понять данные, потом трансформировать
  • ml-19-pca — PCA - следующий шаг после scaling и отбора признаков для снижения размерности
  • stat-31-eda
Feature Engineering и предобработка данных

0

1

Войти