Гайд · TNWS AI

Как выбрать порог по predict_proba у MLPClassifier

4 мин—

Получение вероятностей MLPClassifier и выбор порога по validation, а не test: форма predict_proba, классы, precision, recall и проверяемый пример.

Задача и применимость

Актуальный запрос: «как изменить порог классификации MLPClassifier predict_proba». Отделить обучение нейросети от правила принятия решения. predict_proba возвращает вероятности в порядке classes_; для бинарной задачи обычно берут столбец класса 1, но индекс нужно найти, а не предположить. Порог 0,5 является стандартным правилом predict, однако бизнес-задача может требовать другой компромисс precision и recall. Порог выбирают на validation, после чего один раз оценивают на test.

Материал относится к scikit-learn 1.8.0 и табличным числовым данным. MLPClassifier — полносвязный многослойный перцептрон для обучения с учителем, а не готовая языковая или vision-модель. Все вычисления ниже выполняются локально на синтетическом наборе make_classification: интернет, ключи API и внешние персональные данные не нужны. Значения метрик в рабочем проекте будут другими, поэтому фиксированные пороги примера являются проверкой кода, а не обещанием качества.

Пошаговое решение

  1. Разделите данные на train, validation и test.
  2. Обучите Pipeline только на train.
  3. Получите вероятность положительного класса на validation.
  4. Переберите заранее заданные пороги, выберите по F1 и примените к test один раз.

Готовый проверяемый шаблон

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import f1_score

X, y = make_classification(n_samples=700, n_features=8, n_informative=6, random_state=42)
X_train, X_tmp, y_train, y_tmp = train_test_split(X, y, test_size=.4, stratify=y, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_tmp, y_tmp, test_size=.5, stratify=y_tmp, random_state=42)
model = make_pipeline(StandardScaler(), MLPClassifier((16,), solver="lbfgs", max_iter=500, random_state=42))
model.fit(X_train, y_train)
idx = list(model[-1].classes_).index(1)
val_p = model.predict_proba(X_val)[:, idx]
thresholds = np.arange(.2, .81, .05)
best = max(thresholds, key=lambda t: f1_score(y_val, val_p >= t))
test_p = model.predict_proba(X_test)[:, idx]
score = f1_score(y_test, test_p >= best)
assert np.allclose(model.predict_proba(X_test).sum(axis=1), 1)
print(round(float(best), 2), round(score, 3))

Вход и ожидаемый результат

Вход разделён на три непересекающиеся части. Перебираются пороги от 0,20 до 0,80 с шагом 0,05. Ожидается выбранный порог из этого списка, вероятности формы (n_test,2) с суммой около единицы и конечный F1 на test.

Как проверить результат

Проверьте classes_, форму и диапазон probabilities. Сохраните правило выбора порога и не оптимизируйте его повторно на test. Для редкого положительного класса публикуйте precision, recall, support и confusion matrix вместе с F1.

Перенос на рабочую нейросеть

Заменяя синтетические данные рабочими, сначала составьте контракт: одна строка соответствует одному объекту, столбцы имеют стабильный порядок и единицы измерения, target формируется без информации из будущего. Категориальные поля кодируются отдельным преобразованием, пропуски обрабатываются до StandardScaler совместимым способом. Объединяйте preprocessing и estimator в Pipeline или ColumnTransformer, чтобы одинаковая логика применялась при fit, validation и predict. Версионируйте список признаков, код подготовки, разбиение и сам estimator как единый выпуск.

Критерии приёмки

  1. Train, validation и test не пересекаются, а random_state и алгоритм разбиения записаны.
  2. Preprocessing обучается только на train и входит в сохраняемый Pipeline.
  3. Форма, dtype и порядок признаков проверяются до predict.
  4. Метрика считается на независимой выборке и сопровождается support.
  5. Повторный запуск в закреплённом окружении воспроизводит контрольные predictions.
  6. Негативный тест с лишним или отсутствующим признаком завершается явной ошибкой, а не молчаливой перестановкой.

Регрессионный протокол

Сохраните маленький обезличенный regression-набор и ожидаемые классы либо вероятности. После изменения параметров сравнивайте не только одну итоговую метрику, но и confusion matrix, время обучения, число итераций и распределение вероятностей. Меняйте один фактор за эксперимент. Гиперпараметры выбирайте на validation или внутри кросс-валидации; test открывайте только для финальной оценки выбранной процедуры. Если обучающих данных мало, публикуйте разброс по нескольким разбиениям, а не лучший результат.

Диагностика расхождений

Если качество резко изменилось, проверяйте по порядку: набор и target, индексы split, порядок столбцов, статистики preprocessing, версии зависимостей, random_state и только затем внутренние параметры нейросети. Убедитесь, что признаки не содержат идентификатор ответа или данные из будущего. При расхождении после загрузки сравните predictions на одном сохранённом массиве, classes_, число входных признаков и параметры scaler. Не лечите неизвестную причину увеличением сети: это усложняет диагностику и часто усиливает переобучение.

Контроль после внедрения

Для эксплуатации добавьте журнал версии модели и схемы входа, но не записывайте в него исходные персональные признаки. До выкладки задайте допустимые границы latency, доли ошибок и целевой метрики. После выкладки сравнивайте распределения признаков и прогнозов с контрольным периодом. Если входная схема изменилась или качество вышло за порог, переключайтесь на предыдущий проверенный артефакт; автоматическое дообучение без независимой оценки не является безопасным исправлением.

Типичные ошибки и чего не делать

  • Не берите второй столбец без проверки classes_.
  • Не выбирайте порог на test.
  • Не называйте predict_proba калиброванной вероятностью без отдельной проверки калибровки.

Не подбирайте архитектуру и пороги по тестовой выборке. Не передавайте в модель персональные или чувствительные поля без правового основания и необходимости. Не считайте высокую метрику на синтетическом примере доказательством готовности к эксплуатации. Для важных решений добавьте человеческую проверку, мониторинг drift и безопасный сценарий отказа.

FAQ

Почему не всегда 0,5?

Цена ложных положительных и отрицательных решений может различаться.

Где выбирать порог?

На отдельной validation выборке.

Что означает порядок столбцов?

Он соответствует classes_ обученного classifier.

Официальные источники

Частые вопросы

Почему не всегда 0,5?

Цена ложных положительных и отрицательных решений может различаться.

Где выбирать порог?

На отдельной validation выборке.

Что означает порядок столбцов?

Он соответствует `classes_` обученного classifier.

Читайте также

Комментарии

Пока тихо. Скажите первое слово