Как определить критерии остановки для forward/backward selection?

Ответ

Я использовал комбинацию критериев, чтобы остановить отбор признаков до переобучения. Вот мои практические правила:

Основные критерии остановки:

  1. Отсутствие значимого улучшения на валидации. Это главный критерий. Я останавливаюсь, когда добавление/удаление очередного признака не даёт прироста целевой метрики (например, AUC-ROC или F1-score) на отдельном валидационном наборе больше, чем на заданный порог (например, threshold = 0.001).

    # Упрощённая логика внутри цикла отбора
    current_score = evaluate_model(X_train[:, selected], y_train, X_val[:, selected], y_val)
    
    if len(history_scores) > 1:
        improvement = current_score - max(history_scores)
        if improvement < IMPROVEMENT_THRESHOLD:
            print(f"Остановка. Улучшение {improvement:.4f} < порога {IMPROVEMENT_THRESHOLD}")
            break
    history_scores.append(current_score)
  2. Минимизация информационного критерия (AIC/BIC). Для линейных моделей я часто останавливался, когда значение AIC переставало уменьшаться. Это автоматически балансирует качество и сложность модели.

  3. Контроль переобучения через кросс-валидацию. Я запускал StratifiedKFold на каждом шаге. Если средняя метрика на валидационных фолдах начинала снижаться, а на тренировочных — расти, это явный сигнал к остановке.

  4. Статистическая значимость (p-value). При использовании statsmodels для линейной/логистической регрессии я добавлял только признаки с p-value < 0.05 и удалял те, у которых p-value становился выше 0.1.

Практический совет: Я никогда не полагался на один критерий. Например, в задаче предсказания оттока я сочетал контроль AUC на валидации с мониторингом AIC. Как только оба критерия переставали улучшаться два шага подряд — процесс останавливался.

Ответ 18+ 🔞

Блин, слушай, вот эта тема с отбором признаков — это вообще отдельная песня, ёпта. Я там столько раз наступал на грабли, что уже, блядь, лысину на лбу отбил. Но в итоге выработал себе чёткие правила, чтобы не переобучаться, как последний идиот.

На чём я обычно тормозу:

  1. Когда на валидации нихуя не улучшается. Это святое, блядь. Если после очередного круга добавления или удаления какой-нибудь фичи метрика на отложенной выборке (ну, там AUC-ROC) подпрыгивает меньше, чем на какую-то заранее выбранную погрешность (скажем, на 0.001), то всё — стоп машина. Зачем дальше-то мучать данные?

    # Примерно так это внутри выглядит
    current_score = evaluate_model(X_train[:, selected], y_train, X_val[:, selected], y_val)
    
    if len(history_scores) > 1:
        improvement = current_score - max(history_scores)
        if improvement < IMPROVEMENT_THRESHOLD:
            print(f"Всё, приехали. Прирост {improvement:.4f} — это даже не чих, а так, пшик.")
            break
    history_scores.append(current_score)
  2. Информационные критерии (AIC/BIC). Для всяких линейных моделей — просто красота. Как только этот самый AIC перестаёт падать и начинает ёрзать на месте, я понимаю — дальше только усложнять модель без толку. Баланс между качеством и сложностью, ёбать копать!

  3. Кросс-валидация, чтобы не бздеть. Запускаю на каждом шаге StratifiedKFold. И если вижу, что на тренировочных фолдах метрика лезет вверх, а на валидационных — уже ползёт вниз, это прям красная лампочка: «Чувак, ты уже переобучаешься, остановись, ебушки-воробушки!».

  4. Статистическая значимость, мать её. Когда через statsmodels работаю, то добавляю только те признаки, у которых p-value меньше 0.05. А если у уже сидящего в модели признака p-value вдруг вылез за 0.1 — вышвыриваю его без сожаления. Мертвый груз, хуй с горы.

Главный лайфхак, ёпта: Никогда не доверяй одному критерию. Один может соврать. Я, например, в одной задаче по оттоку клиентов одновременно следил и за AUC на валидации, и за AIC. И если оба этих упрямых осла два шага подряд упирались и не хотели улучшаться — всё, я сдавался. Процесс завершён, модель готова, можно идти пить чай.