сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Классический ML

Признаки и препроцессинг данных

Зачем это спрашивают

Есть правило, которое интервьюеры любят повторять: признаки важнее модели. Одна сильная фича даёт больше, чем смена алгоритма и неделя тюнинга. Поэтому спрашивают про кодирование категорий, пропуски и агрегаты - и очень внимательно слушают, прозвучит ли слово «утечка».

Это тема, где теорию не спрячешь за библиотекой: ответы сразу выдают, готовил ли ты данные руками.

// Типовые формулировки: «как закодируешь категорию с десятью тысячами значений?», «что делать с пропусками?», «как считать агрегаты по истории пользователя?».

Категории: от простого способа к опасному

One-hot - самый честный вариант: на каждое значение категории заводим свой столбец с нулём или единицей. Пока значений десятки, лучше ничего не надо. Но представь категорию «город» с десятью тысячами значений - получится десять тысяч столбцов. Линейные модели это ещё переживут за счёт разреженных матриц, а деревьям станет совсем плохо.

Тогда берут target encoding: заменяют категорию средним значением целевой переменной по ней. Приём мощный и ровно настолько же опасный, потому что без защиты категория буквально узнаёт собственный ответ. Защит нужно две. Сглаживание к общему среднему - чтобы категория, встретившаяся дважды, не считалась знатоком. И расчёт out-of-fold - чтобы среднее для конкретной строки считалось без учёта её самой.

// Бустинги умеют это сами: CatBoost делает такое кодирование из коробки и следит за порядком, чтобы объект не подглядывал в свой ответ. Часто это проще и безопаснее, чем городить руками.

кардинальность
сколько разных значений у категории; от неё и зависит выбор способа кодирования
target encoding
заменить категорию средним таргета по ней; только со сглаживанием и out-of-fold

Пропуски и кривые распределения

Пропуск - чаще сигнал, чем дыра. Человек не указал доход не случайно, и сам факт умолчания может значить больше, чем значение. Поэтому рабочий приём такой: добавь отдельный признак «здесь было пусто», а само значение заполни медианой или константой. Деревья извлекают пользу из факта пропуска сами, линейным моделям нужна аккуратная подстановка.

Числовые признаки для линейных моделей просят внимания. Масштабирование обязательно, иначе признак в миллионах задавит признак в единицах. Логарифм выпрямляет тяжёлые хвосты - доходы, чеки, длительности. Разбиение на корзины и сплайны добавляют нелинейность там, где прямой не хватает. Деревьям всё это безразлично: им важен только порядок значений, а не сами значения.

// Удалять строки с пропусками - почти всегда проигрыш. Теряешь объём и вносишь перекос, если пропуски неслучайны. А они редко случайны.

История и агрегаты: окно смотрит только назад

Главный источник сигнала в табличных задачах - агрегаты по истории, а вовсе не исходные поля. Средний чек пользователя, число заказов за последние тридцать дней, сколько дней прошло с последнего визита. Именно они обычно и вытягивают качество.

И здесь железное правило, нарушение которого стоит дороже всего: агрегат считается по данным строго до момента предсказания. Окно смотрит назад и никогда вперёд.

Календарь разворачивают в циклические признаки через синус и косинус часа или дня недели - чтобы для модели «23:00» и «01:00» оказались соседями, а не крайностями шкалы. Лаги и скользящие средние переносят прошлые значения целевой переменной в признаки текущего момента.

// Агрегат «за всё время», посчитанный одним запросом по всей таблице, включает в себя и день предсказания. Это утечка будущего - самая тихая и самая частая из всех.

лаг-признак
значение из прошлого (вчера, неделю назад), приделанное к текущему моменту

Как отвечать: «Как закодируешь категориальную фичу с 10 000 значений?»

One-hot на такой кардинальности взорвёт размерность, поэтому беру target encoding: заменяю категорию средним таргета, сглаживаю к общему среднему, чтобы редкие значения не переоценивались, и считаю всё out-of-fold - иначе категория узнает собственный ответ, и метрика на валидации будет просто враньём. Совсем редкие значения слил бы в отдельную группу «прочее». А если модель CatBoost, отдам категорию как есть: он делает ровно это из коробки и аккуратнее, чем я руками.

Названы обе защиты, а не только сам приём. Именно на пропущенном out-of-fold собеседование по этой теме обычно и заканчивается.

На чём валят

  • Target encoding без out-of-fold - самая частая утечка на собесе: категория знает собственный таргет.
  • One-hot на тысячах значений: взрыв размерности, и особенно тяжело деревьям.
  • Агрегат «за всё время», включающий день предсказания: утечка будущего прямо в признак.
  • Удалять строки с пропусками вместо подстановки: минус объём данных и перекос, если пропуски неслучайны.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 15, остальные разбираются в тренажёре.

  1. #feature_engineering1 / 5
    В признаке «доход» 30% пропусков. Чем плоха бездумная замена всех пропусков нулём?
    A)Ноль — осмысленное значение дохода: модель перепутает «неизвестно» и «нет дохода», а статистики признака исказятся
    B)Модель с нулями обучится, но заметно медленнее
    C)Ничем: деревья и бустинг нечувствительны к способу заполнения — сплиты сами отделят нули от настоящих значений дохода
    D)Заполнять пропуски запрещено — такие строки удаляют
    показать ответ и разбор
    +A)Ноль — осмысленное значение дохода: модель перепутает «неизвестно» и «нет дохода», а статистики признака исказятся

    // разбор: Замена должна отличать «значения нет» от «значение равно нулю». Треть выборки с нулевым доходом сдвигает среднее, ломает линейные зависимости и создаёт ложный кластер. Практика: медиана/мода + бинарный индикатор пропуска; для деревьев индикатор часто ценнее самой замены — пропуск бывает информативен.

  2. #feature_engineering2 / 5
    Для линейной модели категорию «город» закодировали числами: Москва = 1, Питер = 2, Казань = 3. В чём проблема?
    A)Линейные модели не принимают целочисленные признаки
    B)Label encoding создаёт слишком много колонок
    C)Проблемы нет: кодировка компактнее one-hot, а ложный порядок городов модель всё равно проигнорирует
    D)Модель выучит ложный порядок и «расстояния» между городами — нужен one-hot или таргет-кодировка
    показать ответ и разбор
    +D)Модель выучит ложный порядок и «расстояния» между городами — нужен one-hot или таргет-кодировка

    // разбор: Число навязывает порядок и метрику: для линейной модели «Казань = 3» означает «втрое больше Москвы» по этой оси — бессмыслица для номинальной категории. One-hot делает категории независимыми; при высокой кардинальности — target encoding со сглаживанием. Деревьям label encoding вредит меньше: они режут порогами, хотя и им проще с осмысленной кодировкой.

  3. #feature_engineering3 / 5
    Зачем логарифмируют сильно скошенные признаки (доход, цены, площади) перед линейной моделью?
    A)Логарифм убирает пропуски и выбросы
    B)Сжимает тяжёлый хвост: зависимость становится ближе к линейной, а экстремальные значения меньше тянут решение
    C)Логарифм обязателен для сходимости градиентного спуска
    D)Чтобы распределение признака стало строго нормальным: без этого оценки МНК теряют состоятельность и доверять им не получится
    показать ответ и разбор
    +B)Сжимает тяжёлый хвост: зависимость становится ближе к линейной, а экстремальные значения меньше тянут решение

    // разбор: Для лог-нормальных величин типа дохода эффект обычно мультипликативный: «+10%», а не «+10 000 ₽». Логарифм превращает умножение в сложение — линейная модель начинает описывать реальность, выбросы прижимаются. МНК нормальности признаков не требует (предположения — про остатки), а нули лечат через log1p.

  4. #feature_engineering4 / 5
    Час события (0–23) подали в линейную модель как обычное число. Чем это плохо и как правильно?
    A)Ничем: час — обычное число, модель разберётся
    B)Корректный способ — one-hot на 24 бинарные колонки, чтобы каждый час был независим
    C)23:00 и 00:00 окажутся «максимально далеко» друг от друга; цикличность кодируют парой sin/cos
    D)Часы надо перевести в минуты — растёт точность признака
    показать ответ и разбор
    +C)23:00 и 00:00 окажутся «максимально далеко» друг от друга; цикличность кодируют парой sin/cos

    // разбор: Время циклично: полночь соседствует с 23:00, а по числовой оси между ними 23 «часа» расстояния. Пара sin(2πh/24), cos(2πh/24) кладёт часы на окружность — соседние часы близки, линейная модель ловит плавные суточные паттерны. One-hot тоже рабочий (но 24 колонки и нет непрерывности), деревьям же часто хватает сырого часа — они режут диапазоны порогами.

  5. #feature_engineering5 / 5
    В признаке есть экстремальные выбросы. Какой скейлер устойчивее и почему?
    A)StandardScaler: среднее и стандартное отклонение устойчивы к выбросам
    B)MinMaxScaler: выбросы аккуратно сожмутся в границы [0, 1]
    C)Никакой: выбросы удаляют до масштабирования
    D)RobustScaler по медиане и IQR: квантили почти не двигаются от экстремальных значений
    показать ответ и разбор
    +D)RobustScaler по медиане и IQR: квантили почти не двигаются от экстремальных значений

    // разбор: Среднее и std сами утекают за выбросами, а в MinMax один выброс задаёт границу диапазона — и вся остальная масса сжимается в узкую полоску. Медиана и межквартильный размах меняются от пары экстремумов пренебрежимо — поэтому RobustScaler. Альтернативы: winsorization (обрезка хвостов по квантилям) или лог-преобразование.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.