ГлавнаяБлогEliminationSearchCV: быстрый перебор гиперпараметров
Python

EliminationSearchCV: быстрый перебор гиперпараметров

Узнайте, как EliminationSearchCV ускоряет перебор гиперпараметров до 152 раз, отсеивая плохие значения на ранних этапах. Попробуйте прямо сейчас!

Al
Редакция Algolitalgolit.ru
10 мин чтения26 июня 2026 г.

Почему GridSearchCV — это боль

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

Проблема GridSearchCV

GridSearchCV — это грубая сила по дизайну. Для сетки с k параметрами и n значениями каждый, она оценивает nᵏ × cv_folds конфигураций — каждую, независимо от того, насколько плохо значение проявляет себя в начале.

Вот что это означает на практике:

  • Тупиковые значения никогда не отбрасываются — плохой learning_rate=0.5 переоценивается в каждой последующей комбинации
  • Нет обучения на ранних результатах — поиск относится к раунду 1 и раунду 1000 как к одинаково неинформированным
  • Экспоненциальный рост стоимости — добавление одного нового параметра с 4 значениями может учетверить общее время обучения

Последний пункт — убийца. Ваша сетка не должна быть огромной, чтобы это было больно — она просто должна расти.

Идея: исключать вместо перебора

Что, если вместо оценки всего сразу мы будем тестировать значения параметров раундами — и отбрасывать плохие до того, как они размножатся?

Вот как работает EliminationSearchCV:

  • Раунд 1: Тестируем каждое значение параметра изолированно. Оцениваем их по каждому параметру и исключаем худшие.
  • Раунд 2: Тестируем выжившие пары. Ранжируем все комбинации глобально, оставляем лучшую долю.
  • Раунд 3+: Повторяем с тройками, затем полными комбинациями, пока не останется один победитель.

Плохие значения отсекаются рано. Они никогда не успевают размножиться в тысячи бесполезных комбинаций.

Конкретный пример

Настроим LogisticRegression с 4 параметрами:

param_grid = {
    'C': [0.001, 0.01, 0.1, 1, 10, 100],      # 6 значений
    'penalty': ['l1', 'l2'],                    # 2 значения
    'solver': ['liblinear', 'saga'],            # 2 значения
    'max_iter': [1000, 2000]                    # 2 значения
}
# GridSearchCV: 6 × 2 × 2 × 2 = 48 комбинаций × 5 фолдов = 240 обучений

С EliminationSearchCV и elimination_rate=0.8 (оставляем лучшие 20%):

  • Раунд 1 — одиночные параметры: 12 комбинаций → остаётся C:[1], penalty:['l1'], solver:['liblinear'], max_iter:[1000]
  • Раунд 2 — пары: 6 комбинаций (уже по 1 значению)
  • Раунд 3 — тройки: 4 комбинации
  • Раунд 4 — полные: 1 комбинация

Итого: 23 обучения против 240 у GridSearchCV. Те же лучшие параметры. Доля работы.

Замена в одну строку

API намеренно идентичен GridSearchCV:

from EliminationSearchCV import EliminationSearchCV

# Было:
# search = GridSearchCV(model, param_grid, cv=5)

# Стало — просто меняем имя класса
search = EliminationSearchCV(
    estimator=model,
    param_grid=param_grid,
    scoring='accuracy',
    cv=5,
    elimination_rate=0.8  # исключаем худшие 80% в каждом раунде
)
search.fit(X_train, y_train)

# Тот же интерфейс, что и у GridSearchCV
print(search.best_params_)
# → {'C': 1, 'penalty': 'l1', 'solver': 'liblinear', 'max_iter': 1000}
print(search.best_score_)
# → 0.9248

# Уже переобучен на всей обучающей выборке — готов к предсказанию
search.best_estimator_.predict(X_test)

Чем я горжусь: обработка недопустимых комбинаций

Sklearn может выдавать ошибки для несовместимых комбинаций — например, penalty='l1' с solver='lbfgs'. GridSearchCV на них падает. Приходится вручную фильтровать. EliminationSearchCV перехватывает любое исключение во время fit(), оценивает такую комбинацию в 0.0 и позволяет логике исключения обработать её естественно. Недопустимые комбинации просто умирают в раунде 1. Никакой специальной обработки от вас не требуется.

Результаты бенчмарков

Протестировано на 5 моделях и 3 датасетах (cv=2, elimination_rate=0.8, 10000 сэмплов):

  • DecisionTree: ускорение 152x, разница в точности -0.0008
  • RandomForest: ускорение 36x, разница -0.0002
  • GradientBoosting: ускорение 35x, разница -0.0194
  • KNeighbors: ускорение 11x, разница -0.0004
  • LogisticRegression: ускорение 4x, разница -0.0004

Полные сетки — вот где это сияет. Потеря точности минимальна — менее 0.02 на всех моделях, часто ноль.

Честное предостережение: Лёгкие сетки (маленькие пространства поиска) с этим подходом медленнее. Накладные расходы на исключение не окупаются, если комбинаций мало. Если ваша сетка маленькая, оставайтесь с GridSearchCV.

Архитектура: как это устроено

Библиотека состоит из двух файлов:

src/EliminationSearchCV/
├── EliminationSearchCV.py   # Основной класс: fit(), логика исключения, оценка
└── Utils.py                 # Вспомогательные функции: создание фолдов, генерация комбинаций, метрики

Поток внутри fit():

EliminationSearchCV.fit(X, y)
    │
    ├─▶ Utils.create_cv_data_sets()         — StratifiedKFold/KFold разбиения
    │
    └─▶ [Для каждого раунда i = 1 … n_params]
             │
             ├─▶ generate_param_combinations_with_limit(grid, limit=i)
             │
             ├─▶ _score_candidates(candidates)
             │         — оценка метрики по фолдам
             │
             └─▶ _eliminate_low_scoring_values(candidates, scores)
                       ├─▶ _eliminate_single_param_values()   — Раунд 1
                       └─▶ _eliminate_multi_param_values()    — Раунды 2+

Ключевое дизайнерское решение: в раунде 1 значения каждого параметра оцениваются и сравниваются изолированно — так значения C конкурируют только с другими значениями C, а не с penalty. Это предотвращает взаимное влияние параметров, находящихся на совершенно разных шкалах. В последующих раундах все комбинации ранжируются глобально, и выживает доля (1 - elimination_rate).

Что работает, а что ещё нет

Поддерживается:

  • fit(), best_params_, best_score_, best_estimator_
  • Раунд 1: изолированная оценка и исключение по параметрам
  • Раунды 2+: глобальное ранжирование комбинаций
  • StratifiedKFold / KFold кросс-валидация
  • Обработка недопустимых комбинаций
  • Метрики: accuracy, precision, recall, f1, roc_auc

В планах:

  • cv_results_ (разбивка оценок по фолдам)
  • n_jobs параллельное вычисление через joblib
  • verbose логирование
  • Полный набор тестов pytest
  • Совместимость с BaseEstimator из scikit-learn

Попробуйте

pip install elimination-search-cv

Требования: Python ≥ 3.8. scikit-learn и numpy устанавливаются автоматически.

GitHub: https://github.com/thisal-d/elimination-search-cv

Честное предупреждение

Это экспериментальный подход. Качество результатов сильно зависит от датасета и модели. Я активно тестирую его, и результаты пока многообещающие — но я бы не назвал его production-ready. Мне искренне нужна обратная связь о граничных случаях, где он даёт сбои. Если вы попробуете его на сетке, где он даёт явно неверные результаты или ведёт себя неожиданно, пожалуйста, откройте issue. Это будет полезнее похвалы.

Если вам было интересно, ⭐ на репозитории очень помогает — это поддерживает мотивацию продолжать разработку.

#гиперпараметры#оптимизация#scikit-learn#машинное обучение
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →